/** * driver_update_system_av_info: * @data : pointer to new A/V info * * Update the system Audio/Video information. * Will reinitialize audio/video drivers. * Used by RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO. * * Returns: true (1) if successful, otherwise false (0). **/ static bool driver_update_system_av_info(const struct retro_system_av_info *info) { struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); settings_t *settings = config_get_ptr(); memcpy(av_info, info, sizeof(*av_info)); command_event(CMD_EVENT_REINIT, NULL); /* Cannot continue recording with different parameters. * Take the easiest route out and just restart the recording. */ if (recording_driver_get_data_ptr()) { runloop_msg_queue_push( msg_hash_to_str(MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT), 2, 180, false); command_event(CMD_EVENT_RECORD_DEINIT, NULL); command_event(CMD_EVENT_RECORD_INIT, NULL); } /* Hide mouse cursor in fullscreen after * a RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO call. */ if (settings->bools.video_fullscreen) video_driver_hide_mouse(); return true; }
/** * driver_update_system_av_info: * @data : pointer to new A/V info * * Update the system Audio/Video information. * Will reinitialize audio/video drivers. * Used by RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO. * * Returns: true (1) if successful, otherwise false (0). **/ static bool driver_update_system_av_info(const struct retro_system_av_info *info) { struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); memcpy(av_info, info, sizeof(*av_info)); command_event(CMD_EVENT_REINIT, NULL); /* Cannot continue recording with different parameters. * Take the easiest route out and just restart the recording. */ if (recording_driver_get_data_ptr()) { runloop_msg_queue_push( msg_hash_to_str(MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT), 2, 180, false); command_event(CMD_EVENT_RECORD_DEINIT, NULL); command_event(CMD_EVENT_RECORD_INIT, NULL); } return true; }
/** * runloop_iterate: * * Run Libretro core in RetroArch for one frame. * * Returns: 0 on success, 1 if we have to wait until button input in order * to wake up the loop, -1 if we forcibly quit out of the RetroArch iteration loop. **/ int runloop_iterate(unsigned *sleep_ms) { unsigned i; event_cmd_state_t cmd; event_cmd_state_t *cmd_ptr = &cmd; retro_time_t current, target, to_sleep_ms; static retro_usec_t frame_time_last = 0; static retro_time_t frame_limit_minimum_time = 0.0; static retro_time_t frame_limit_last_time = 0.0; static retro_input_t last_input = 0; settings_t *settings = config_get_ptr(); global_t *global = global_get_ptr(); rarch_system_info_t *system = NULL; cmd.state[1] = last_input; cmd.state[0] = input_keys_pressed(); last_input = cmd.state[0]; runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system); if (runloop_ctl(RUNLOOP_CTL_IS_FRAME_TIME_LAST, NULL)) { frame_time_last = 0; runloop_ctl(RUNLOOP_CTL_UNSET_FRAME_TIME_LAST, NULL); } if (runloop_ctl(RUNLOOP_CTL_SHOULD_SET_FRAME_LIMIT, NULL)) { struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); float fastforward_ratio = (settings->fastforward_ratio == 0.0f) ? 1.0f : settings->fastforward_ratio; frame_limit_last_time = retro_get_time_usec(); frame_limit_minimum_time = (retro_time_t)roundf(1000000.0f / (av_info->timing.fps * fastforward_ratio)); runloop_ctl(RUNLOOP_CTL_UNSET_FRAME_LIMIT, NULL); } if (input_driver_ctl(RARCH_INPUT_CTL_IS_FLUSHING_INPUT, NULL)) { input_driver_ctl(RARCH_INPUT_CTL_UNSET_FLUSHING_INPUT, NULL); if (cmd.state[0]) { cmd.state[0] = 0; /* If core was paused before entering menu, evoke * pause toggle to wake it up. */ if (runloop_ctl(RUNLOOP_CTL_IS_PAUSED, NULL)) BIT64_SET(cmd.state[0], RARCH_PAUSE_TOGGLE); input_driver_ctl(RARCH_INPUT_CTL_SET_FLUSHING_INPUT, NULL); } } if (system->frame_time.callback) { /* Updates frame timing if frame timing callback is in use by the core. * Limits frame time if fast forward ratio throttle is enabled. */ bool is_slowmotion; retro_time_t current = retro_get_time_usec(); retro_time_t delta = current - frame_time_last; bool is_locked_fps = (runloop_ctl(RUNLOOP_CTL_IS_PAUSED, NULL) || input_driver_ctl(RARCH_INPUT_CTL_IS_NONBLOCK_STATE, NULL)) | !!recording_driver_get_data_ptr(); runloop_ctl(RUNLOOP_CTL_IS_SLOWMOTION, &is_slowmotion); if (!frame_time_last || is_locked_fps) delta = system->frame_time.reference; if (!is_locked_fps && is_slowmotion) delta /= settings->slowmotion_ratio; frame_time_last = current; if (is_locked_fps) frame_time_last = 0; system->frame_time.callback(delta); } cmd.state[2] = cmd.state[0] & ~cmd.state[1]; /* trigger */ if (runloop_cmd_triggered(cmd_ptr, RARCH_OVERLAY_NEXT)) event_command(EVENT_CMD_OVERLAY_NEXT); if (runloop_cmd_triggered(cmd_ptr, RARCH_FULLSCREEN_TOGGLE_KEY)) { bool fullscreen_toggled = !runloop_ctl(RUNLOOP_CTL_IS_PAUSED, NULL); #ifdef HAVE_MENU fullscreen_toggled = fullscreen_toggled || menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL); #endif if (fullscreen_toggled) event_command(EVENT_CMD_FULLSCREEN_TOGGLE); } if (runloop_cmd_triggered(cmd_ptr, RARCH_GRAB_MOUSE_TOGGLE)) event_command(EVENT_CMD_GRAB_MOUSE_TOGGLE); #ifdef HAVE_MENU if (runloop_cmd_menu_press(cmd_ptr) || (global->inited.core.type == CORE_TYPE_DUMMY)) { if (menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL)) { if (global->inited.main && (global->inited.core.type != CORE_TYPE_DUMMY)) rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL); } else rarch_ctl(RARCH_CTL_MENU_RUNNING, NULL); } #endif #ifdef HAVE_OVERLAY runloop_iterate_linefeed_overlay(settings); #endif if (runloop_iterate_time_to_exit(runloop_cmd_press(cmd_ptr, RARCH_QUIT_KEY)) != 1) { frame_limit_last_time = 0.0; return -1; } #ifdef HAVE_MENU if (menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL)) { bool focused = runloop_ctl(RUNLOOP_CTL_CHECK_FOCUS, NULL) && !ui_companion_is_on_foreground(); bool is_idle = runloop_ctl(RUNLOOP_CTL_IS_IDLE, NULL); if (menu_driver_iterate((enum menu_action)menu_input_frame_retropad(cmd.state[0], cmd.state[2])) == -1) rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL); if (focused || !is_idle) menu_driver_ctl(RARCH_MENU_CTL_RENDER, NULL); if (!focused || is_idle) { *sleep_ms = 10; return 1; } goto end; } #endif if (!runloop_ctl(RUNLOOP_CTL_CHECK_STATE, &cmd)) { /* RetroArch has been paused. */ retro_ctx.poll_cb(); *sleep_ms = 10; return 1; } #if defined(HAVE_THREADS) lock_autosave(); #endif #ifdef HAVE_NETPLAY netplay_driver_ctl(RARCH_NETPLAY_CTL_PRE_FRAME, NULL); #endif if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) bsv_movie_ctl(BSV_MOVIE_CTL_SET_FRAME_START, NULL); if (system->camera_callback.caps) driver_camera_poll(); /* Update binds for analog dpad modes. */ for (i = 0; i < settings->input.max_users; i++) { if (!settings->input.analog_dpad_mode[i]) continue; input_push_analog_dpad(settings->input.binds[i], settings->input.analog_dpad_mode[i]); input_push_analog_dpad(settings->input.autoconf_binds[i], settings->input.analog_dpad_mode[i]); } if ((settings->video.frame_delay > 0) && !input_driver_ctl(RARCH_INPUT_CTL_IS_NONBLOCK_STATE, NULL)) retro_sleep(settings->video.frame_delay); /* Run libretro for one frame. */ core.retro_run(); #ifdef HAVE_CHEEVOS /* Test the achievements. */ cheevos_test(); #endif for (i = 0; i < settings->input.max_users; i++) { if (!settings->input.analog_dpad_mode[i]) continue; input_pop_analog_dpad(settings->input.binds[i]); input_pop_analog_dpad(settings->input.autoconf_binds[i]); } if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) bsv_movie_ctl(BSV_MOVIE_CTL_SET_FRAME_END, NULL); #ifdef HAVE_NETPLAY netplay_driver_ctl(RARCH_NETPLAY_CTL_POST_FRAME, NULL); #endif #if defined(HAVE_THREADS) unlock_autosave(); #endif #ifdef HAVE_MENU end: #endif if (!settings->fastforward_ratio) return 0; current = retro_get_time_usec(); target = frame_limit_last_time + frame_limit_minimum_time; to_sleep_ms = (target - current) / 1000; if (to_sleep_ms > 0) { *sleep_ms = (unsigned)to_sleep_ms; /* Combat jitter a bit. */ frame_limit_last_time += frame_limit_minimum_time; return 1; } frame_limit_last_time = retro_get_time_usec(); return 0; }
/** * runloop_iterate: * * Run Libretro core in RetroArch for one frame. * * Returns: 0 on success, 1 if we have to wait until * button input in order to wake up the loop, * -1 if we forcibly quit out of the RetroArch iteration loop. **/ int runloop_iterate(unsigned *sleep_ms) { unsigned i; event_cmd_state_t cmd; retro_time_t current, target, to_sleep_ms; static retro_input_t last_input = {0}; event_cmd_state_t *cmd_ptr = &cmd; static retro_time_t frame_limit_minimum_time = 0.0; static retro_time_t frame_limit_last_time = 0.0; settings_t *settings = config_get_ptr(); cmd.state[1] = last_input; cmd.state[0] = input_keys_pressed(); last_input = cmd.state[0]; runloop_ctl(RUNLOOP_CTL_UNSET_FRAME_TIME_LAST, NULL); if (runloop_ctl(RUNLOOP_CTL_SHOULD_SET_FRAME_LIMIT, NULL)) { struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); float fastforward_ratio = (settings->fastforward_ratio == 0.0f) ? 1.0f : settings->fastforward_ratio; frame_limit_last_time = cpu_features_get_time_usec(); frame_limit_minimum_time = (retro_time_t)roundf(1000000.0f / (av_info->timing.fps * fastforward_ratio)); runloop_ctl(RUNLOOP_CTL_UNSET_FRAME_LIMIT, NULL); } if (input_driver_is_flushing_input()) { input_driver_unset_flushing_input(); if (cmd.state[0].state) { cmd.state[0].state = 0; /* If core was paused before entering menu, evoke * pause toggle to wake it up. */ if (runloop_ctl(RUNLOOP_CTL_IS_PAUSED, NULL)) BIT64_SET(cmd.state[0].state, RARCH_PAUSE_TOGGLE); input_driver_set_flushing_input(); } } if (runloop_frame_time.callback) { /* Updates frame timing if frame timing callback is in use by the core. * Limits frame time if fast forward ratio throttle is enabled. */ retro_time_t current = cpu_features_get_time_usec(); retro_time_t delta = current - runloop_frame_time_last; bool is_locked_fps = (runloop_ctl(RUNLOOP_CTL_IS_PAUSED, NULL) || input_driver_is_nonblock_state()) | !!recording_driver_get_data_ptr(); if (!runloop_frame_time_last || is_locked_fps) delta = runloop_frame_time.reference; if (!is_locked_fps && runloop_ctl(RUNLOOP_CTL_IS_SLOWMOTION, NULL)) delta /= settings->slowmotion_ratio; runloop_frame_time_last = current; if (is_locked_fps) runloop_frame_time_last = 0; runloop_frame_time.callback(delta); } cmd.state[2].state = cmd.state[0].state & ~cmd.state[1].state; /* trigger */ if (runloop_cmd_triggered(cmd_ptr, RARCH_OVERLAY_NEXT)) command_event(CMD_EVENT_OVERLAY_NEXT, NULL); if (runloop_cmd_triggered(cmd_ptr, RARCH_FULLSCREEN_TOGGLE_KEY)) { bool fullscreen_toggled = !runloop_ctl(RUNLOOP_CTL_IS_PAUSED, NULL); #ifdef HAVE_MENU fullscreen_toggled = fullscreen_toggled || menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL); #endif if (fullscreen_toggled) command_event(CMD_EVENT_FULLSCREEN_TOGGLE, NULL); } if (runloop_cmd_triggered(cmd_ptr, RARCH_GRAB_MOUSE_TOGGLE)) command_event(CMD_EVENT_GRAB_MOUSE_TOGGLE, NULL); #ifdef HAVE_MENU if (runloop_cmd_menu_press(cmd_ptr) || rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)) { if (menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL)) { if (rarch_ctl(RARCH_CTL_IS_INITED, NULL) && !rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)) rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL); } else rarch_ctl(RARCH_CTL_MENU_RUNNING, NULL); } #endif #ifdef HAVE_OVERLAY runloop_iterate_linefeed_overlay(settings); #endif if (runloop_iterate_time_to_exit( runloop_cmd_press(cmd_ptr, RARCH_QUIT_KEY)) != 1) { frame_limit_last_time = 0.0; return -1; } #ifdef HAVE_MENU if (menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL)) { int ret = runloop_iterate_menu((enum menu_action) menu_input_frame_retropad(cmd.state[0], cmd.state[2]), sleep_ms); if (ret == -1) goto end; return ret; } #endif if (!runloop_check_state(&cmd, &runloop_shader_dir)) { /* RetroArch has been paused. */ core_poll(); *sleep_ms = 10; return 1; } #if defined(HAVE_THREADS) autosave_lock(); #endif #ifdef HAVE_NETPLAY netplay_driver_ctl(RARCH_NETPLAY_CTL_PRE_FRAME, NULL); #endif if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) bsv_movie_ctl(BSV_MOVIE_CTL_SET_FRAME_START, NULL); camera_driver_ctl(RARCH_CAMERA_CTL_POLL, NULL); /* Update binds for analog dpad modes. */ for (i = 0; i < settings->input.max_users; i++) { if (!settings->input.analog_dpad_mode[i]) continue; input_push_analog_dpad(settings->input.binds[i], settings->input.analog_dpad_mode[i]); input_push_analog_dpad(settings->input.autoconf_binds[i], settings->input.analog_dpad_mode[i]); } if ((settings->video.frame_delay > 0) && !input_driver_is_nonblock_state()) retro_sleep(settings->video.frame_delay); core_run(); #ifdef HAVE_CHEEVOS cheevos_test(); #endif for (i = 0; i < settings->input.max_users; i++) { if (!settings->input.analog_dpad_mode[i]) continue; input_pop_analog_dpad(settings->input.binds[i]); input_pop_analog_dpad(settings->input.autoconf_binds[i]); } if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) bsv_movie_ctl(BSV_MOVIE_CTL_SET_FRAME_END, NULL); #ifdef HAVE_NETPLAY netplay_driver_ctl(RARCH_NETPLAY_CTL_POST_FRAME, NULL); #endif #if defined(HAVE_THREADS) autosave_unlock(); #endif if (!settings->fastforward_ratio) return 0; #ifdef HAVE_MENU end: #endif current = cpu_features_get_time_usec(); target = frame_limit_last_time + frame_limit_minimum_time; to_sleep_ms = (target - current) / 1000; if (to_sleep_ms > 0) { *sleep_ms = (unsigned)to_sleep_ms; /* Combat jitter a bit. */ frame_limit_last_time += frame_limit_minimum_time; return 1; } frame_limit_last_time = cpu_features_get_time_usec(); return 0; }
/** * rarch_environment_cb: * @cmd : Identifier of command. * @data : Pointer to data. * * Environment callback function implementation. * * Returns: true (1) if environment callback command could * be performed, otherwise false (0). **/ bool rarch_environment_cb(unsigned cmd, void *data) { unsigned p; settings_t *settings = config_get_ptr(); global_t *global = global_get_ptr(); rarch_system_info_t *system = NULL; runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system); if (ignore_environment_cb) return false; switch (cmd) { case RETRO_ENVIRONMENT_GET_OVERSCAN: *(bool*)data = !settings->video.crop_overscan; RARCH_LOG("Environ GET_OVERSCAN: %u\n", (unsigned)!settings->video.crop_overscan); break; case RETRO_ENVIRONMENT_GET_CAN_DUPE: *(bool*)data = true; RARCH_LOG("Environ GET_CAN_DUPE: true\n"); break; case RETRO_ENVIRONMENT_GET_VARIABLE: if (!runloop_ctl(RUNLOOP_CTL_CORE_OPTIONS_GET, data)) { struct retro_variable *var = (struct retro_variable*)data; if (var) { RARCH_LOG("Environ GET_VARIABLE %s: not implemented.\n", var->key); var->value = NULL; } } break; case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: *(bool*)data = runloop_ctl(RUNLOOP_CTL_IS_CORE_OPTION_UPDATED, NULL); break; case RETRO_ENVIRONMENT_SET_VARIABLES: RARCH_LOG("Environ SET_VARIABLES.\n"); runloop_ctl(RUNLOOP_CTL_CORE_OPTIONS_DEINIT, NULL); runloop_ctl(RUNLOOP_CTL_CORE_OPTIONS_INIT, data); break; case RETRO_ENVIRONMENT_SET_MESSAGE: { const struct retro_message *msg = (const struct retro_message*)data; RARCH_LOG("Environ SET_MESSAGE: %s\n", msg->msg); runloop_msg_queue_push(msg->msg, 1, msg->frames, true); break; } case RETRO_ENVIRONMENT_SET_ROTATION: { unsigned rotation = *(const unsigned*)data; RARCH_LOG("Environ SET_ROTATION: %u\n", rotation); if (!settings->video.allow_rotate) break; system->rotation = rotation; if (!video_driver_set_rotation(rotation)) return false; break; } case RETRO_ENVIRONMENT_SHUTDOWN: RARCH_LOG("Environ SHUTDOWN.\n"); runloop_ctl(RUNLOOP_CTL_SET_SHUTDOWN, NULL); runloop_ctl(RUNLOOP_CTL_SET_CORE_SHUTDOWN, NULL); break; case RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL: system->performance_level = *(const unsigned*)data; RARCH_LOG("Environ PERFORMANCE_LEVEL: %u.\n", system->performance_level); break; case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY: if (string_is_empty(settings->system_directory)) { char *fullpath = NULL; runloop_ctl(RUNLOOP_CTL_GET_CONTENT_PATH, &fullpath); RARCH_WARN("SYSTEM DIR is empty, assume CONTENT DIR %s\n", fullpath); fill_pathname_basedir(global->dir.systemdir, fullpath, sizeof(global->dir.systemdir)); *(const char**)data = global->dir.systemdir; RARCH_LOG("Environ SYSTEM_DIRECTORY: \"%s\".\n", global->dir.systemdir); } else { *(const char**)data = settings->system_directory; RARCH_LOG("Environ SYSTEM_DIRECTORY: \"%s\".\n", settings->system_directory); } break; case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY: *(const char**)data = rarch_get_current_savefile_dir(); break; case RETRO_ENVIRONMENT_GET_USERNAME: *(const char**)data = *settings->username ? settings->username : NULL; RARCH_LOG("Environ GET_USERNAME: \"%s\".\n", settings->username); break; case RETRO_ENVIRONMENT_GET_LANGUAGE: *(unsigned *)data = settings->user_language; RARCH_LOG("Environ GET_LANGUAGE: \"%u\".\n", settings->user_language); break; case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT: { enum retro_pixel_format pix_fmt = *(const enum retro_pixel_format*)data; switch (pix_fmt) { case RETRO_PIXEL_FORMAT_0RGB1555: RARCH_LOG("Environ SET_PIXEL_FORMAT: 0RGB1555.\n"); break; case RETRO_PIXEL_FORMAT_RGB565: RARCH_LOG("Environ SET_PIXEL_FORMAT: RGB565.\n"); break; case RETRO_PIXEL_FORMAT_XRGB8888: RARCH_LOG("Environ SET_PIXEL_FORMAT: XRGB8888.\n"); break; default: return false; } video_driver_set_pixel_format(pix_fmt); break; } case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS: { unsigned retro_id, retro_port; const struct retro_input_descriptor *desc = NULL; static const char *libretro_btn_desc[] = { "B (bottom)", "Y (left)", "Select", "Start", "D-Pad Up", "D-Pad Down", "D-Pad Left", "D-Pad Right", "A (right)", "X (up)", "L", "R", "L2", "R2", "L3", "R3", }; memset(system->input_desc_btn, 0, sizeof(system->input_desc_btn)); desc = (const struct retro_input_descriptor*)data; for (; desc->description; desc++) { retro_port = desc->port; retro_id = desc->id; if (desc->port >= MAX_USERS) continue; /* Ignore all others for now. */ if (desc->device != RETRO_DEVICE_JOYPAD && desc->device != RETRO_DEVICE_ANALOG) continue; if (desc->id >= RARCH_FIRST_CUSTOM_BIND) continue; if (desc->device == RETRO_DEVICE_ANALOG) { switch (retro_id) { case RETRO_DEVICE_ID_ANALOG_X: switch (desc->index) { case RETRO_DEVICE_INDEX_ANALOG_LEFT: system->input_desc_btn[retro_port][RARCH_ANALOG_LEFT_X_PLUS] = desc->description; system->input_desc_btn[retro_port][RARCH_ANALOG_LEFT_X_MINUS] = desc->description; break; case RETRO_DEVICE_INDEX_ANALOG_RIGHT: system->input_desc_btn[retro_port][RARCH_ANALOG_RIGHT_X_PLUS] = desc->description; system->input_desc_btn[retro_port][RARCH_ANALOG_RIGHT_X_MINUS] = desc->description; break; } break; case RETRO_DEVICE_ID_ANALOG_Y: switch (desc->index) { case RETRO_DEVICE_INDEX_ANALOG_LEFT: system->input_desc_btn[retro_port][RARCH_ANALOG_LEFT_Y_PLUS] = desc->description; system->input_desc_btn[retro_port][RARCH_ANALOG_LEFT_Y_MINUS] = desc->description; break; case RETRO_DEVICE_INDEX_ANALOG_RIGHT: system->input_desc_btn[retro_port][RARCH_ANALOG_RIGHT_Y_PLUS] = desc->description; system->input_desc_btn[retro_port][RARCH_ANALOG_RIGHT_Y_MINUS] = desc->description; break; } break; } } else system->input_desc_btn[retro_port][retro_id] = desc->description; } RARCH_LOG("Environ SET_INPUT_DESCRIPTORS:\n"); for (p = 0; p < settings->input.max_users; p++) { for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++) { const char *description = system->input_desc_btn[p][retro_id]; if (!description) continue; RARCH_LOG("\tRetroPad, User %u, Button \"%s\" => \"%s\"\n", p + 1, libretro_btn_desc[retro_id], description); } } global->has_set.input_descriptors = true; break; } case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK: { retro_keyboard_event_t *frontend_key_event = NULL; retro_keyboard_event_t *key_event = NULL; const struct retro_keyboard_callback *info = (const struct retro_keyboard_callback*)data; runloop_ctl(RUNLOOP_CTL_FRONTEND_KEY_EVENT_GET, &frontend_key_event); runloop_ctl(RUNLOOP_CTL_KEY_EVENT_GET, &key_event); RARCH_LOG("Environ SET_KEYBOARD_CALLBACK.\n"); if (key_event) *key_event = info->callback; if (frontend_key_event) *frontend_key_event = *key_event; break; } case RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE: RARCH_LOG("Environ SET_DISK_CONTROL_INTERFACE.\n"); system->disk_control = *(const struct retro_disk_control_callback*)data; break; case RETRO_ENVIRONMENT_SET_HW_RENDER: case RETRO_ENVIRONMENT_SET_HW_RENDER | RETRO_ENVIRONMENT_EXPERIMENTAL: { struct retro_hw_render_callback *hw_render = video_driver_callback(); struct retro_hw_render_callback *cb = (struct retro_hw_render_callback*)data; RARCH_LOG("Environ SET_HW_RENDER.\n"); switch (cb->context_type) { case RETRO_HW_CONTEXT_NONE: RARCH_LOG("Requesting no HW context.\n"); break; #if defined(HAVE_OPENGLES2) case RETRO_HW_CONTEXT_OPENGLES2: #if defined(HAVE_OPENGLES3) case RETRO_HW_CONTEXT_OPENGLES3: #endif RARCH_LOG("Requesting OpenGLES%u context.\n", cb->context_type == RETRO_HW_CONTEXT_OPENGLES2 ? 2 : 3); break; #if defined(HAVE_OPENGLES3) case RETRO_HW_CONTEXT_OPENGLES_VERSION: RARCH_LOG("Requesting OpenGLES%u.%u context.\n", cb->version_major, cb->version_minor); break; #endif case RETRO_HW_CONTEXT_OPENGL: case RETRO_HW_CONTEXT_OPENGL_CORE: RARCH_ERR("Requesting OpenGL context, but RetroArch is compiled against OpenGLES2. Cannot use HW context.\n"); return false; #elif defined(HAVE_OPENGL) case RETRO_HW_CONTEXT_OPENGLES2: case RETRO_HW_CONTEXT_OPENGLES3: RARCH_ERR("Requesting OpenGLES%u context, but RetroArch is compiled against OpenGL. Cannot use HW context.\n", cb->context_type == RETRO_HW_CONTEXT_OPENGLES2 ? 2 : 3); return false; case RETRO_HW_CONTEXT_OPENGLES_VERSION: RARCH_ERR("Requesting OpenGLES%u.%u context, but RetroArch is compiled against OpenGL. Cannot use HW context.\n", cb->version_major, cb->version_minor); return false; case RETRO_HW_CONTEXT_OPENGL: RARCH_LOG("Requesting OpenGL context.\n"); break; case RETRO_HW_CONTEXT_OPENGL_CORE: RARCH_LOG("Requesting core OpenGL context (%u.%u).\n", cb->version_major, cb->version_minor); break; #endif default: RARCH_LOG("Requesting unknown context.\n"); return false; } cb->get_current_framebuffer = video_driver_get_current_framebuffer; cb->get_proc_address = video_driver_get_proc_address; if (cmd & RETRO_ENVIRONMENT_EXPERIMENTAL) /* Old ABI. Don't copy garbage. */ memcpy(hw_render, cb, offsetof(struct retro_hw_render_callback, stencil)); else memcpy(hw_render, cb, sizeof(*cb)); break; } case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME: { bool state = *(const bool*)data; RARCH_LOG("Environ SET_SUPPORT_NO_GAME: %s.\n", state ? "yes" : "no"); system->no_content = state; break; } case RETRO_ENVIRONMENT_GET_LIBRETRO_PATH: { const char **path = (const char**)data; *path = NULL; #ifdef HAVE_DYNAMIC *path = settings->libretro; #endif break; } /* FIXME - PS3 audio driver needs to be fixed so that threaded * audio works correctly (audio is already on a thread for PS3 * audio driver so that's probably the problem) */ #if defined(HAVE_THREADS) && !defined(__CELLOS_LV2__) case RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK: { const struct retro_audio_callback *info = (const struct retro_audio_callback*)data; RARCH_LOG("Environ SET_AUDIO_CALLBACK.\n"); if (recording_driver_get_data_ptr()) /* A/V sync is a must. */ return false; #ifdef HAVE_NETPLAY if (global->netplay.enable) return false; #endif audio_driver_set_callback(info); break; } #endif case RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK: { const struct retro_frame_time_callback *info = (const struct retro_frame_time_callback*)data; RARCH_LOG("Environ SET_FRAME_TIME_CALLBACK.\n"); #ifdef HAVE_NETPLAY /* retro_run() will be called in very strange and * mysterious ways, have to disable it. */ if (global->netplay.enable) return false; #endif system->frame_time = *info; break; } case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE: { struct retro_rumble_interface *iface = (struct retro_rumble_interface*)data; RARCH_LOG("Environ GET_RUMBLE_INTERFACE.\n"); iface->set_rumble_state = input_driver_set_rumble_state; break; } case RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES: { uint64_t *mask = (uint64_t*)data; RARCH_LOG("Environ GET_INPUT_DEVICE_CAPABILITIES.\n"); if (input_driver_ctl(RARCH_INPUT_CTL_HAS_CAPABILITIES, NULL)) *mask = input_driver_get_capabilities(); else return false; break; } case RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE: { struct retro_sensor_interface *iface = (struct retro_sensor_interface*)data; RARCH_LOG("Environ GET_SENSOR_INTERFACE.\n"); iface->set_sensor_state = input_sensor_set_state; iface->get_sensor_input = input_sensor_get_input; break; } case RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE: { struct retro_camera_callback *cb = (struct retro_camera_callback*)data; RARCH_LOG("Environ GET_CAMERA_INTERFACE.\n"); cb->start = driver_camera_start; cb->stop = driver_camera_stop; system->camera_callback = *cb; if (cb->caps != 0) camera_driver_ctl(RARCH_CAMERA_CTL_SET_ACTIVE, NULL); else camera_driver_ctl(RARCH_CAMERA_CTL_UNSET_ACTIVE, NULL); break; } case RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE: { struct retro_location_callback *cb = (struct retro_location_callback*)data; RARCH_LOG("Environ GET_LOCATION_INTERFACE.\n"); cb->start = driver_location_start; cb->stop = driver_location_stop; cb->get_position = driver_location_get_position; cb->set_interval = driver_location_set_interval; system->location_callback = *cb; location_driver_ctl(RARCH_LOCATION_CTL_UNSET_ACTIVE, NULL); break; } case RETRO_ENVIRONMENT_GET_LOG_INTERFACE: { struct retro_log_callback *cb = (struct retro_log_callback*)data; RARCH_LOG("Environ GET_LOG_INTERFACE.\n"); cb->log = rarch_log_libretro; break; } case RETRO_ENVIRONMENT_GET_PERF_INTERFACE: { struct retro_perf_callback *cb = (struct retro_perf_callback*)data; RARCH_LOG("Environ GET_PERF_INTERFACE.\n"); cb->get_time_usec = retro_get_time_usec; cb->get_cpu_features = retro_get_cpu_features; cb->get_perf_counter = retro_get_perf_counter; cb->perf_register = retro_perf_register; /* libretro specific path. */ cb->perf_start = retro_perf_start; cb->perf_stop = retro_perf_stop; cb->perf_log = retro_perf_log; /* libretro specific path. */ break; } case RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY: { const char **dir = (const char**)data; *dir = *settings->core_assets_directory ? settings->core_assets_directory : NULL; RARCH_LOG("Environ CORE_ASSETS_DIRECTORY: \"%s\".\n", settings->core_assets_directory); break; } case RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO: { RARCH_LOG("Environ SET_SYSTEM_AV_INFO.\n"); return driver_ctl(RARCH_DRIVER_CTL_UPDATE_SYSTEM_AV_INFO, (void**)&data); } case RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO: { unsigned i, j; const struct retro_subsystem_info *info = (const struct retro_subsystem_info*)data; RARCH_LOG("Environ SET_SUBSYSTEM_INFO.\n"); for (i = 0; info[i].ident; i++) { RARCH_LOG("Special game type: %s\n", info[i].desc); RARCH_LOG(" Ident: %s\n", info[i].ident); RARCH_LOG(" ID: %u\n", info[i].id); RARCH_LOG(" Content:\n"); for (j = 0; j < info[i].num_roms; j++) { RARCH_LOG(" %s (%s)\n", info[i].roms[j].desc, info[i].roms[j].required ? "required" : "optional"); } } free(system->special); system->special = (struct retro_subsystem_info*) calloc(i, sizeof(*system->special)); if (!system->special) return false; memcpy(system->special, info, i * sizeof(*system->special)); system->num_special = i; break; } case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: { unsigned i, j; const struct retro_controller_info *info = (const struct retro_controller_info*)data; RARCH_LOG("Environ SET_CONTROLLER_INFO.\n"); for (i = 0; info[i].types; i++) { RARCH_LOG("Controller port: %u\n", i + 1); for (j = 0; j < info[i].num_types; j++) RARCH_LOG(" %s (ID: %u)\n", info[i].types[j].desc, info[i].types[j].id); } free(system->ports); system->ports = (struct retro_controller_info*) calloc(i, sizeof(*system->ports)); if (!system->ports) return false; memcpy(system->ports, info, i * sizeof(*system->ports)); system->num_ports = i; break; } case RETRO_ENVIRONMENT_SET_GEOMETRY: { struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); const struct retro_game_geometry *in_geom = (const struct retro_game_geometry*)data; struct retro_game_geometry *geom = av_info ? (struct retro_game_geometry*)&av_info->geometry : NULL; if (!geom) return false; RARCH_LOG("Environ SET_GEOMETRY.\n"); /* Can potentially be called every frame, * don't do anything unless required. */ if (geom->base_width != in_geom->base_width || geom->base_height != in_geom->base_height || geom->aspect_ratio != in_geom->aspect_ratio) { geom->base_width = in_geom->base_width; geom->base_height = in_geom->base_height; geom->aspect_ratio = in_geom->aspect_ratio; RARCH_LOG("SET_GEOMETRY: %ux%u, aspect: %.3f.\n", geom->base_width, geom->base_height, geom->aspect_ratio); /* Forces recomputation of aspect ratios if * using core-dependent aspect ratios. */ event_cmd_ctl(EVENT_CMD_VIDEO_SET_ASPECT_RATIO, NULL); /* TODO: Figure out what to do, if anything, with recording. */ } break; } case RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER: return video_driver_get_current_software_framebuffer((struct retro_framebuffer*)data); /* Private extensions for internal use, not part of libretro API. */ case RETRO_ENVIRONMENT_EXEC: { RARCH_LOG("Environ (Private) EXEC.\n"); char *fullpath = NULL; runloop_ctl(RUNLOOP_CTL_GET_CONTENT_PATH, &fullpath); if (fullpath != data) { runloop_ctl(RUNLOOP_CTL_CLEAR_CONTENT_PATH, NULL); if (data) runloop_ctl(RUNLOOP_CTL_SET_CONTENT_PATH, data); } #if defined(RARCH_CONSOLE) frontend_driver_set_fork(true, true, false); #elif defined(HAVE_DYNAMIC) rarch_ctl(RARCH_CTL_LOAD_CONTENT, NULL); #endif } break; default: RARCH_LOG("Environ UNSUPPORTED (#%u).\n", cmd); return false; } return true; }
/** * runloop_iterate: * * Run Libretro core in RetroArch for one frame. * * Returns: 0 on success, 1 if we have to wait until * button input in order to wake up the loop, * -1 if we forcibly quit out of the RetroArch iteration loop. **/ int runloop_iterate(unsigned *sleep_ms) { unsigned i; retro_time_t current, target, to_sleep_ms; static uint64_t last_input = 0; enum runloop_state runloop_status = RUNLOOP_STATE_NONE; static retro_time_t frame_limit_minimum_time = 0.0; static retro_time_t frame_limit_last_time = 0.0; settings_t *settings = config_get_ptr(); uint64_t current_input = menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL) ? input_menu_keys_pressed() : input_keys_pressed(); uint64_t old_input = last_input; last_input = current_input; if (runloop_frame_time_last_enable) { runloop_frame_time_last = 0; runloop_frame_time_last_enable = false; } if (runloop_set_frame_limit) { struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); float fastforward_ratio = (settings->fastforward_ratio == 0.0f) ? 1.0f : settings->fastforward_ratio; frame_limit_last_time = cpu_features_get_time_usec(); frame_limit_minimum_time = (retro_time_t)roundf(1000000.0f / (av_info->timing.fps * fastforward_ratio)); runloop_set_frame_limit = false; } if (runloop_frame_time.callback) { /* Updates frame timing if frame timing callback is in use by the core. * Limits frame time if fast forward ratio throttle is enabled. */ retro_time_t current = cpu_features_get_time_usec(); retro_time_t delta = current - runloop_frame_time_last; bool is_locked_fps = (runloop_paused || input_driver_is_nonblock_state()) | !!recording_driver_get_data_ptr(); if (!runloop_frame_time_last || is_locked_fps) delta = runloop_frame_time.reference; if (!is_locked_fps && runloop_slowmotion) delta /= settings->slowmotion_ratio; runloop_frame_time_last = current; if (is_locked_fps) runloop_frame_time_last = 0; runloop_frame_time.callback(delta); } runloop_status = runloop_check_state(settings, current_input, old_input, sleep_ms); switch (runloop_status) { case RUNLOOP_STATE_QUIT: frame_limit_last_time = 0.0; command_event(CMD_EVENT_QUIT, NULL); return -1; case RUNLOOP_STATE_SLEEP: case RUNLOOP_STATE_END: case RUNLOOP_STATE_MENU_ITERATE: core_poll(); #ifdef HAVE_NETWORKING /* FIXME: This is an ugly way to tell Netplay this... */ netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL); #endif if (runloop_status == RUNLOOP_STATE_SLEEP) *sleep_ms = 10; if (runloop_status == RUNLOOP_STATE_END) goto end; if (runloop_status == RUNLOOP_STATE_MENU_ITERATE) return 0; return 1; case RUNLOOP_STATE_ITERATE: case RUNLOOP_STATE_NONE: default: break; } autosave_lock(); if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) bsv_movie_ctl(BSV_MOVIE_CTL_SET_FRAME_START, NULL); camera_driver_ctl(RARCH_CAMERA_CTL_POLL, NULL); /* Update binds for analog dpad modes. */ for (i = 0; i < settings->input.max_users; i++) { if (!settings->input.analog_dpad_mode[i]) continue; input_push_analog_dpad(settings->input.binds[i], settings->input.analog_dpad_mode[i]); input_push_analog_dpad(settings->input.autoconf_binds[i], settings->input.analog_dpad_mode[i]); } if ((settings->video.frame_delay > 0) && !input_driver_is_nonblock_state()) retro_sleep(settings->video.frame_delay); core_run(); #ifdef HAVE_CHEEVOS cheevos_test(); #endif for (i = 0; i < settings->input.max_users; i++) { if (!settings->input.analog_dpad_mode[i]) continue; input_pop_analog_dpad(settings->input.binds[i]); input_pop_analog_dpad(settings->input.autoconf_binds[i]); } if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) bsv_movie_ctl(BSV_MOVIE_CTL_SET_FRAME_END, NULL); autosave_unlock(); if (!settings->fastforward_ratio) return 0; end: current = cpu_features_get_time_usec(); target = frame_limit_last_time + frame_limit_minimum_time; to_sleep_ms = (target - current) / 1000; if (to_sleep_ms > 0) { *sleep_ms = (unsigned)to_sleep_ms; /* Combat jitter a bit. */ frame_limit_last_time += frame_limit_minimum_time; return 1; } frame_limit_last_time = cpu_features_get_time_usec(); return 0; }