/** * rarch_init_system_av_info: * * Initialize system A/V information by calling the libretro core's * get_system_av_info function. **/ void rarch_init_system_av_info(void) { struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); core.retro_get_system_av_info(av_info); event_command(EVENT_CMD_SET_FRAME_LIMIT); }
/** * 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; }
/** * video_viewport_set_config: * * Sets viewport to config aspect ratio. **/ void video_viewport_set_config(void) { settings_t *settings = config_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); if (settings->video.aspect_ratio < 0.0f) { struct retro_game_geometry *geom = &av_info->geometry; if (geom && geom->aspect_ratio > 0.0f && settings->video.aspect_ratio_auto) aspectratio_lut[ASPECT_RATIO_CONFIG].value = geom->aspect_ratio; else { unsigned base_width = geom->base_width; unsigned base_height = geom->base_height; /* Get around division by zero errors */ if (base_width == 0) base_width = 1; if (base_height == 0) base_height = 1; aspectratio_lut[ASPECT_RATIO_CONFIG].value = (float)base_width / base_height; /* 1:1 PAR. */ } } else aspectratio_lut[ASPECT_RATIO_CONFIG].value = settings->video.aspect_ratio; }
static void psp_set_aspect_ratio(void *data, unsigned aspectratio_index) { psp1_video_t *psp = (psp1_video_t*)data; struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); switch (aspectratio_index) { case ASPECT_RATIO_SQUARE: video_viewport_set_square_pixel( av_info->geometry.base_width, av_info->geometry.base_height); break; case ASPECT_RATIO_CORE: video_viewport_set_core(); break; case ASPECT_RATIO_CONFIG: video_viewport_set_config(); break; default: break; } video_driver_set_aspect_ratio_value(aspectratio_lut[aspectratio_index].value); psp->keep_aspect = true; psp->should_resize = true; }
static void sdl2_poke_set_aspect_ratio(void *data, unsigned aspectratio_index) { sdl2_video_t *vid = (sdl2_video_t*)data; const struct retro_system_av_info *av_info = (const struct retro_system_av_info*)video_viewport_get_system_av_info(); switch (aspectratio_index) { case ASPECT_RATIO_SQUARE: video_viewport_set_square_pixel(av_info->geometry.base_width, av_info->geometry.base_height); break; case ASPECT_RATIO_CORE: video_viewport_set_core(); break; case ASPECT_RATIO_CONFIG: video_viewport_set_config(); break; default: break; } video_driver_set_aspect_ratio_value( aspectratio_lut[aspectratio_index].value); vid->video.force_aspect = true; vid->should_resize = true; }
/** * rarch_limit_frame_time: * * Limit frame time if fast forward ratio throttle is enabled. **/ static void rarch_limit_frame_time(settings_t *settings, runloop_t *runloop) { retro_time_t target = 0; retro_time_t to_sleep_ms = 0; retro_time_t current = rarch_get_time_usec(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); double effective_fps = av_info->timing.fps * settings->fastforward_ratio; double mft_f = 1000000.0f / effective_fps; runloop->frames.limit.minimum_time = (retro_time_t) roundf(mft_f); target = runloop->frames.limit.last_time + runloop->frames.limit.minimum_time; to_sleep_ms = (target - current) / 1000; if (to_sleep_ms <= 0) { runloop->frames.limit.last_time = rarch_get_time_usec(); return; } rarch_sleep((unsigned int)to_sleep_ms); /* Combat jitter a bit. */ runloop->frames.limit.last_time += runloop->frames.limit.minimum_time; }
int setting_uint_action_left_custom_viewport_height( void *data, bool wraparound) { video_viewport_t vp; struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); video_viewport_t *custom = video_viewport_get_custom(); settings_t *settings = config_get_ptr(); struct retro_game_geometry *geom = (struct retro_game_geometry*) &av_info->geometry; if (!settings || !av_info) return -1; video_driver_get_viewport_info(&vp); if (custom->height <= 1) custom->height = 1; else if (settings->bools.video_scale_integer) { if (custom->height > geom->base_height) custom->height -= geom->base_height; } else custom->height -= 1; aspectratio_lut[ASPECT_RATIO_CUSTOM].value = (float)custom->width / custom->height; return 0; }
/** * rarch_init_system_av_info: * * Initialize system A/V information by calling the libretro core's * get_system_av_info function. **/ void rarch_init_system_av_info(void) { runloop_t *runloop = rarch_main_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); pretro_get_system_av_info(av_info); runloop->frames.limit.last_time = rarch_get_time_usec(); }
void rarch_main_set_frame_limit_last_time(void) { settings_t *settings = config_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); float fastforward_ratio = settings->fastforward_ratio; if (fastforward_ratio == 0.0f) fastforward_ratio = 1.0f; frame_limit_last_time = rarch_get_time_usec(); frame_limit_minimum_time = (retro_time_t)roundf(1000000.0f / (av_info->timing.fps * fastforward_ratio)); }
bool core_load(unsigned poll_type_behavior) { current_core.poll_type = poll_type_behavior; if (!core_verify_api_version()) return false; if (!core_init_libretro_cbs(&retro_ctx)) return false; core_get_system_av_info(video_viewport_get_system_av_info()); return true; }
bool core_load(void) { settings_t *settings = config_get_ptr(); core_poll_type = settings->input.poll_type_behavior; if (!core_verify_api_version()) return false; if (!core_init_libretro_cbs(&retro_ctx)) return false; core_get_system_av_info(video_viewport_get_system_av_info()); runloop_ctl(RUNLOOP_CTL_SET_FRAME_LIMIT, NULL); return true; }
/** * video_viewport_set_core: * * Sets viewport to aspect ratio set by core. **/ void video_viewport_set_core(void) { struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); struct retro_game_geometry *geom = &av_info->geometry; if (!geom || geom->base_width <= 0.0f || geom->base_height <= 0.0f) return; /* Fallback to 1:1 pixel ratio if none provided */ if (geom->aspect_ratio > 0.0f) aspectratio_lut[ASPECT_RATIO_CORE].value = geom->aspect_ratio; else aspectratio_lut[ASPECT_RATIO_CORE].value = (float)geom->base_width / geom->base_height; }
/** * driver_update_system_av_info: * @info : 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). **/ 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(); driver_t *driver = driver_get_ptr(); memcpy(av_info, info, sizeof(*av_info)); event_command(EVENT_CMD_REINIT); /* Cannot continue recording with different parameters. * Take the easiest route out and just restart the recording. */ if (driver->recording_data) { rarch_main_msg_queue_push_new( MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT, 2, 180, false); event_command(EVENT_CMD_RECORD_DEINIT); event_command(EVENT_CMD_RECORD_INIT); } 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; }
void video_monitor_adjust_system_rates(void) { float timing_skew; const struct retro_system_timing *info = NULL; struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); settings_t *settings = config_get_ptr(); rarch_system_info_t *system = rarch_system_info_get_ptr(); if (!system) return; system->force_nonblock = false; if (av_info) info = (const struct retro_system_timing*)&av_info->timing; if (!info || info->fps <= 0.0) return; timing_skew = fabs(1.0f - info->fps / settings->video.refresh_rate); /* We don't want to adjust pitch too much. If we have extreme cases, * just don't readjust at all. */ if (timing_skew <= settings->audio.max_timing_skew) return; RARCH_LOG("Timings deviate too much. Will not adjust. (Display = %.2f Hz, Game = %.2f Hz)\n", settings->video.refresh_rate, (float)info->fps); if (info->fps <= settings->video.refresh_rate) return; /* We won't be able to do VSync reliably when game FPS > monitor FPS. */ system->force_nonblock = true; RARCH_LOG("Game FPS > Monitor FPS. Cannot rely on VSync.\n"); }
static void init_video_filter(enum retro_pixel_format colfmt) { unsigned width, height, pow2_x, pow2_y, maxsize; struct retro_game_geometry *geom = NULL; settings_t *settings = config_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); deinit_video_filter(); if (!*settings->video.softfilter_plugin) return; /* Deprecated format. Gets pre-converted. */ if (colfmt == RETRO_PIXEL_FORMAT_0RGB1555) colfmt = RETRO_PIXEL_FORMAT_RGB565; if (video_state.hw_render_callback.context_type) { RARCH_WARN("Cannot use CPU filters when hardware rendering is used.\n"); return; } geom = av_info ? (struct retro_game_geometry*)&av_info->geometry : NULL; width = geom->max_width; height = geom->max_height; video_state.filter.filter = rarch_softfilter_new( settings->video.softfilter_plugin, RARCH_SOFTFILTER_THREADS_AUTO, colfmt, width, height); if (!video_state.filter.filter) { RARCH_ERR("Failed to load filter.\n"); return; } rarch_softfilter_get_max_output_size(video_state.filter.filter, &width, &height); pow2_x = next_pow2(width); pow2_y = next_pow2(height); maxsize = max(pow2_x, pow2_y); video_state.filter.scale = maxsize / RARCH_SCALE_BASE; video_state.filter.out_rgb32 = rarch_softfilter_get_output_format( video_state.filter.filter) == RETRO_PIXEL_FORMAT_XRGB8888; video_state.filter.out_bpp = video_state.filter.out_rgb32 ? sizeof(uint32_t) : sizeof(uint16_t); /* TODO: Aligned output. */ video_state.filter.buffer = malloc(width * height * video_state.filter.out_bpp); if (!video_state.filter.buffer) goto error; return; error: RARCH_ERR("Softfilter initialization failed.\n"); deinit_video_filter(); }
/** * 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; }
bool rarch_main_ctl(enum rarch_main_ctl_state state, void *data) { driver_t *driver = driver_get_ptr(); settings_t *settings = config_get_ptr(); global_t *global = global_get_ptr(); switch (state) { case RARCH_MAIN_CTL_CHECK_STATE: { event_cmd_state_t *cmd = (event_cmd_state_t*)data; if (!cmd || main_is_idle) return false; if (cmd->screenshot_pressed) event_command(EVENT_CMD_TAKE_SCREENSHOT); if (cmd->mute_pressed) event_command(EVENT_CMD_AUDIO_MUTE_TOGGLE); if (cmd->osk_pressed) driver->keyboard_linefeed_enable = !driver->keyboard_linefeed_enable; if (cmd->volume_up_pressed) event_command(EVENT_CMD_VOLUME_UP); else if (cmd->volume_down_pressed) event_command(EVENT_CMD_VOLUME_DOWN); #ifdef HAVE_NETPLAY if (driver->netplay_data) { if (cmd->netplay_flip_pressed) event_command(EVENT_CMD_NETPLAY_FLIP_PLAYERS); if (cmd->fullscreen_toggle) event_command(EVENT_CMD_FULLSCREEN_TOGGLE); break; } #endif check_pause(driver, settings, cmd->pause_pressed, cmd->frameadvance_pressed); if (!rarch_main_ctl(RARCH_MAIN_CTL_CHECK_PAUSE_STATE, cmd)) return false; check_fast_forward_button(driver, cmd->fastforward_pressed, cmd->hold_pressed, cmd->old_hold_pressed); check_stateslots(settings, cmd->state_slot_increase, cmd->state_slot_decrease); if (cmd->save_state_pressed) event_command(EVENT_CMD_SAVE_STATE); else if (cmd->load_state_pressed) event_command(EVENT_CMD_LOAD_STATE); check_rewind(settings, global, cmd->rewind_pressed); rarch_main_ctl(RARCH_MAIN_CTL_CHECK_SLOWMOTION, &cmd->slowmotion_pressed); if (cmd->movie_record) rarch_main_ctl(RARCH_MAIN_CTL_CHECK_MOVIE, NULL); check_shader_dir(global, cmd->shader_next_pressed, cmd->shader_prev_pressed); if (cmd->disk_eject_pressed) event_command(EVENT_CMD_DISK_EJECT_TOGGLE); else if (cmd->disk_next_pressed) event_command(EVENT_CMD_DISK_NEXT); else if (cmd->disk_prev_pressed) event_command(EVENT_CMD_DISK_PREV); if (cmd->reset_pressed) event_command(EVENT_CMD_RESET); if (global->cheat) { if (cmd->cheat_index_plus_pressed) cheat_manager_index_next(global->cheat); else if (cmd->cheat_index_minus_pressed) cheat_manager_index_prev(global->cheat); else if (cmd->cheat_toggle_pressed) cheat_manager_toggle(global->cheat); } } break; case RARCH_MAIN_CTL_CHECK_PAUSE_STATE: { bool check_is_oneshot; event_cmd_state_t *cmd = (event_cmd_state_t*)data; if (!cmd) return false; check_is_oneshot = cmd->frameadvance_pressed || cmd->rewind_pressed; if (!main_is_paused) return true; if (cmd->fullscreen_toggle) { event_command(EVENT_CMD_FULLSCREEN_TOGGLE); video_driver_cached_frame(); } if (!check_is_oneshot) return false; } break; case RARCH_MAIN_CTL_CHECK_SLOWMOTION: { bool *ptr = (bool*)data; if (!ptr) return false; main_is_slowmotion = *ptr; if (!main_is_slowmotion) return false; if (settings->video.black_frame_insertion) video_driver_cached_frame(); if (state_manager_frame_is_reversed()) rarch_main_msg_queue_push_new(MSG_SLOW_MOTION_REWIND, 0, 30, true); else rarch_main_msg_queue_push_new(MSG_SLOW_MOTION, 0, 30, true); } break; case RARCH_MAIN_CTL_CHECK_MOVIE: if (global->bsv.movie_playback) return rarch_main_ctl(RARCH_MAIN_CTL_CHECK_MOVIE_PLAYBACK, NULL); if (!global->bsv.movie) return rarch_main_ctl(RARCH_MAIN_CTL_CHECK_MOVIE_INIT, NULL); return rarch_main_ctl(RARCH_MAIN_CTL_CHECK_MOVIE_RECORD, NULL); case RARCH_MAIN_CTL_CHECK_MOVIE_RECORD: if (!global->bsv.movie) return false; rarch_main_msg_queue_push_new( MSG_MOVIE_RECORD_STOPPED, 2, 180, true); RARCH_LOG("%s\n", msg_hash_to_str(MSG_MOVIE_RECORD_STOPPED)); event_command(EVENT_CMD_BSV_MOVIE_DEINIT); break; case RARCH_MAIN_CTL_CHECK_MOVIE_INIT: if (global->bsv.movie) return false; { char path[PATH_MAX_LENGTH], msg[PATH_MAX_LENGTH]; settings->rewind_granularity = 1; if (settings->state_slot > 0) snprintf(path, sizeof(path), "%s%d", global->bsv.movie_path, settings->state_slot); else strlcpy(path, global->bsv.movie_path, sizeof(path)); strlcat(path, ".bsv", sizeof(path)); snprintf(msg, sizeof(msg), "%s \"%s\".", msg_hash_to_str(MSG_STARTING_MOVIE_RECORD_TO), path); global->bsv.movie = bsv_movie_init(path, RARCH_MOVIE_RECORD); if (!global->bsv.movie) return false; else if (global->bsv.movie) { rarch_main_msg_queue_push(msg, 1, 180, true); RARCH_LOG("%s \"%s\".\n", msg_hash_to_str(MSG_STARTING_MOVIE_RECORD_TO), path); } else { rarch_main_msg_queue_push_new( MSG_FAILED_TO_START_MOVIE_RECORD, 1, 180, true); RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_START_MOVIE_RECORD)); } } break; case RARCH_MAIN_CTL_CHECK_MOVIE_PLAYBACK: if (!global->bsv.movie_end) return false; rarch_main_msg_queue_push_new( MSG_MOVIE_PLAYBACK_ENDED, 1, 180, false); RARCH_LOG("%s\n", msg_hash_to_str(MSG_MOVIE_PLAYBACK_ENDED)); event_command(EVENT_CMD_BSV_MOVIE_DEINIT); global->bsv.movie_end = false; global->bsv.movie_playback = false; break; case RARCH_MAIN_CTL_STATE_FREE: main_is_idle = false; main_is_paused = false; main_is_slowmotion = false; frame_limit_last_time = 0.0; main_max_frames = 0; break; case RARCH_MAIN_CTL_GLOBAL_FREE: event_command(EVENT_CMD_TEMPORARY_CONTENT_DEINIT); event_command(EVENT_CMD_SUBSYSTEM_FULLPATHS_DEINIT); event_command(EVENT_CMD_RECORD_DEINIT); event_command(EVENT_CMD_LOG_FILE_DEINIT); memset(&g_extern, 0, sizeof(g_extern)); break; case RARCH_MAIN_CTL_CLEAR_STATE: driver_clear_state(); rarch_main_ctl(RARCH_MAIN_CTL_STATE_FREE, NULL); rarch_main_ctl(RARCH_MAIN_CTL_GLOBAL_FREE, NULL); break; case RARCH_MAIN_CTL_SET_MAX_FRAMES: { unsigned *ptr = (unsigned*)data; if (!ptr) return false; main_max_frames = *ptr; } break; case RARCH_MAIN_CTL_SET_FRAME_LIMIT_LAST_TIME: { struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); float fastforward_ratio = settings->fastforward_ratio; if (fastforward_ratio == 0.0f) fastforward_ratio = 1.0f; frame_limit_last_time = retro_get_time_usec(); frame_limit_minimum_time = (retro_time_t)roundf(1000000.0f / (av_info->timing.fps * fastforward_ratio)); } break; case RARCH_MAIN_CTL_IS_IDLE: { bool *ptr = (bool*)data; if (!ptr) return false; *ptr = main_is_idle; } break; case RARCH_MAIN_CTL_SET_IDLE: { bool *ptr = (bool*)data; if (!ptr) return false; main_is_idle = *ptr; } break; case RARCH_MAIN_CTL_IS_SLOWMOTION: { bool *ptr = (bool*)data; if (!ptr) return false; *ptr = main_is_slowmotion; } break; case RARCH_MAIN_CTL_SET_SLOWMOTION: { bool *ptr = (bool*)data; if (!ptr) return false; main_is_slowmotion = *ptr; } break; case RARCH_MAIN_CTL_SET_PAUSED: { bool *ptr = (bool*)data; if (!ptr) return false; main_is_paused = *ptr; } break; case RARCH_MAIN_CTL_IS_PAUSED: { bool *ptr = (bool*)data; if (!ptr) return false; *ptr = main_is_paused; } break; default: return false; } return true; }
static int action_iterate_menu_viewport(char *s, size_t len, const char *label, unsigned action, uint32_t hash) { int stride_x = 1, stride_y = 1; menu_displaylist_info_t info = {0}; struct retro_game_geometry *geom = NULL; const char *base_msg = NULL; unsigned type = 0; video_viewport_t *custom = video_viewport_get_custom(); menu_display_t *disp = menu_display_get_ptr(); menu_navigation_t *nav = menu_navigation_get_ptr(); menu_list_t *menu_list = menu_list_get_ptr(); settings_t *settings = config_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); if (!menu_list) return -1; menu_list_get_last_stack(menu_list, NULL, NULL, &type, NULL); geom = (struct retro_game_geometry*)&av_info->geometry; if (settings->video.scale_integer) { stride_x = geom->base_width; stride_y = geom->base_height; } switch (action) { case MENU_ACTION_UP: if (type == MENU_SETTINGS_CUSTOM_VIEWPORT) { custom->y -= stride_y; custom->height += stride_y; } else if (custom->height >= (unsigned)stride_y) custom->height -= stride_y; event_command(EVENT_CMD_VIDEO_APPLY_STATE_CHANGES); break; case MENU_ACTION_DOWN: if (type == MENU_SETTINGS_CUSTOM_VIEWPORT) { custom->y += stride_y; if (custom->height >= (unsigned)stride_y) custom->height -= stride_y; } else custom->height += stride_y; event_command(EVENT_CMD_VIDEO_APPLY_STATE_CHANGES); break; case MENU_ACTION_LEFT: if (type == MENU_SETTINGS_CUSTOM_VIEWPORT) { custom->x -= stride_x; custom->width += stride_x; } else if (custom->width >= (unsigned)stride_x) custom->width -= stride_x; event_command(EVENT_CMD_VIDEO_APPLY_STATE_CHANGES); break; case MENU_ACTION_RIGHT: if (type == MENU_SETTINGS_CUSTOM_VIEWPORT) { custom->x += stride_x; if (custom->width >= (unsigned)stride_x) custom->width -= stride_x; } else custom->width += stride_x; event_command(EVENT_CMD_VIDEO_APPLY_STATE_CHANGES); break; case MENU_ACTION_CANCEL: menu_entry_go_back(); if (hash == MENU_LABEL_CUSTOM_VIEWPORT_2) { info.list = menu_list->menu_stack; info.type = MENU_SETTINGS_CUSTOM_VIEWPORT; info.directory_ptr = nav->selection_ptr; menu_displaylist_push_list(&info, DISPLAYLIST_INFO); } break; case MENU_ACTION_OK: menu_list_flush_stack(menu_list, NULL, 49); if (type == MENU_SETTINGS_CUSTOM_VIEWPORT && !settings->video.scale_integer) { info.list = menu_list->menu_stack; strlcpy(info.label, menu_hash_to_str(MENU_LABEL_CUSTOM_VIEWPORT_2), sizeof(info.label)); info.type = 0; info.directory_ptr = nav->selection_ptr; menu_displaylist_push_list(&info, DISPLAYLIST_INFO); } break; case MENU_ACTION_START: if (!settings->video.scale_integer) { video_viewport_t vp; video_driver_viewport_info(&vp); if (type == MENU_SETTINGS_CUSTOM_VIEWPORT) { custom->width += custom->x; custom->height += custom->y; custom->x = 0; custom->y = 0; } else { custom->width = vp.full_width - custom->x; custom->height = vp.full_height - custom->y; } event_command(EVENT_CMD_VIDEO_APPLY_STATE_CHANGES); } break; case MENU_ACTION_MESSAGE: if (disp) disp->msg_force = true; break; default: break; } menu_list_get_last_stack(menu_list, NULL, &label, &type, NULL); if (settings->video.scale_integer) { custom->x = 0; custom->y = 0; custom->width = ((custom->width + geom->base_width - 1) / geom->base_width) * geom->base_width; custom->height = ((custom->height + geom->base_height - 1) / geom->base_height) * geom->base_height; base_msg = "Set scale"; snprintf(s, len, "%s (%4ux%4u, %u x %u scale)", base_msg, custom->width, custom->height, custom->width / geom->base_width, custom->height / geom->base_height); } else { if (type == MENU_SETTINGS_CUSTOM_VIEWPORT) base_msg = menu_hash_to_str(MENU_LABEL_VALUE_CUSTOM_VIEWPORT_1); else if (hash == MENU_LABEL_CUSTOM_VIEWPORT_2) base_msg = menu_hash_to_str(MENU_LABEL_VALUE_CUSTOM_VIEWPORT_2); snprintf(s, len, "%s (%d, %d : %4ux%4u)", base_msg, custom->x, custom->y, custom->width, custom->height); } if (!custom->width) custom->width = stride_x; if (!custom->height) custom->height = stride_y; aspectratio_lut[ASPECT_RATIO_CUSTOM].value = (float)custom->width / custom->height; event_command(EVENT_CMD_VIDEO_APPLY_STATE_CHANGES); return 0; }
/** * recording_init: * * Initializes recording. * * Returns: true (1) if successful, otherwise false (0). **/ bool recording_init(void) { char recording_file[PATH_MAX_LENGTH] = {0}; struct ffemu_params params = {0}; global_t *global = global_get_ptr(); driver_t *driver = driver_get_ptr(); settings_t *settings = config_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); const struct retro_hw_render_callback *hw_render = (const struct retro_hw_render_callback*)video_driver_callback(); if (!global->record.enable) return false; if (global->libretro_dummy) { RARCH_WARN(RETRO_LOG_INIT_RECORDING_SKIPPED); return false; } if (!settings->video.gpu_record && hw_render->context_type) { RARCH_WARN("Libretro core is hardware rendered. Must use post-shaded recording as well.\n"); return false; } RARCH_LOG("Custom timing given: FPS: %.4f, Sample rate: %.4f\n", (float)av_info->timing.fps, (float)av_info->timing.sample_rate); strlcpy(recording_file, global->record.path, sizeof(recording_file)); if (global->record.use_output_dir) fill_pathname_join(recording_file, global->record.output_dir, global->record.path, sizeof(recording_file)); params.out_width = av_info->geometry.base_width; params.out_height = av_info->geometry.base_height; params.fb_width = av_info->geometry.max_width; params.fb_height = av_info->geometry.max_height; params.channels = 2; params.filename = recording_file; params.fps = av_info->timing.fps; params.samplerate = av_info->timing.sample_rate; params.pix_fmt = (video_driver_get_pixel_format() == RETRO_PIXEL_FORMAT_XRGB8888) ? FFEMU_PIX_ARGB8888 : FFEMU_PIX_RGB565; params.config = NULL; if (*global->record.config) params.config = global->record.config; if (settings->video.gpu_record && driver->video->read_viewport) { struct video_viewport vp = {0}; video_driver_viewport_info(&vp); if (!vp.width || !vp.height) { RARCH_ERR("Failed to get viewport information from video driver. " "Cannot start recording ...\n"); return false; } params.out_width = vp.width; params.out_height = vp.height; params.fb_width = next_pow2(vp.width); params.fb_height = next_pow2(vp.height); if (settings->video.force_aspect && (video_driver_get_aspect_ratio() > 0.0f)) params.aspect_ratio = video_driver_get_aspect_ratio(); else params.aspect_ratio = (float)vp.width / vp.height; params.pix_fmt = FFEMU_PIX_BGR24; global->record.gpu_width = vp.width; global->record.gpu_height = vp.height; RARCH_LOG("Detected viewport of %u x %u\n", vp.width, vp.height); global->record.gpu_buffer = (uint8_t*)malloc(vp.width * vp.height * 3); if (!global->record.gpu_buffer) { RARCH_ERR("Failed to allocate GPU record buffer.\n"); return false; } } else { if (global->record.width || global->record.height) { params.out_width = global->record.width; params.out_height = global->record.height; } if (settings->video.force_aspect && (video_driver_get_aspect_ratio() > 0.0f)) params.aspect_ratio = video_driver_get_aspect_ratio(); else params.aspect_ratio = (float)params.out_width / params.out_height; if (settings->video.post_filter_record && video_driver_frame_filter_alive()) { unsigned max_width = 0; unsigned max_height = 0; if (video_driver_frame_filter_is_32bit()) params.pix_fmt = FFEMU_PIX_ARGB8888; else params.pix_fmt = FFEMU_PIX_RGB565; rarch_softfilter_get_max_output_size( video_driver_frame_filter_get_ptr(), &max_width, &max_height); params.fb_width = next_pow2(max_width); params.fb_height = next_pow2(max_height); } } RARCH_LOG("Recording to %s @ %ux%u. (FB size: %ux%u pix_fmt: %u)\n", global->record.path, params.out_width, params.out_height, params.fb_width, params.fb_height, (unsigned)params.pix_fmt); if (!record_driver_init_first(&driver->recording, &driver->recording_data, ¶ms)) { RARCH_ERR(RETRO_LOG_INIT_RECORDING_FAILED); event_command(EVENT_CMD_GPU_RECORD_DEINIT); return false; } return true; }
/** * 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; }
void init_video(void) { unsigned max_dim, scale, width, height; video_viewport_t *custom_vp = NULL; const input_driver_t *tmp = NULL; const struct retro_game_geometry *geom = NULL; video_info_t video = {0}; static uint16_t dummy_pixels[32] = {0}; driver_t *driver = driver_get_ptr(); global_t *global = global_get_ptr(); settings_t *settings = config_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); init_video_filter(video_state.pix_fmt); event_command(EVENT_CMD_SHADER_DIR_INIT); if (av_info) geom = (const struct retro_game_geometry*)&av_info->geometry; max_dim = max(geom->max_width, geom->max_height); scale = next_pow2(max_dim) / RARCH_SCALE_BASE; scale = max(scale, 1); if (video_state.filter.filter) scale = video_state.filter.scale; /* Update core-dependent aspect ratio values. */ video_viewport_set_square_pixel(geom->base_width, geom->base_height); video_viewport_set_core(); video_viewport_set_config(); /* Update CUSTOM viewport. */ custom_vp = video_viewport_get_custom(); if (settings->video.aspect_ratio_idx == ASPECT_RATIO_CUSTOM) { float default_aspect = aspectratio_lut[ASPECT_RATIO_CORE].value; aspectratio_lut[ASPECT_RATIO_CUSTOM].value = (custom_vp->width && custom_vp->height) ? (float)custom_vp->width / custom_vp->height : default_aspect; } video_driver_set_aspect_ratio_value( aspectratio_lut[settings->video.aspect_ratio_idx].value); if (settings->video.fullscreen) { width = settings->video.fullscreen_x; height = settings->video.fullscreen_y; } else { if (settings->video.force_aspect) { /* Do rounding here to simplify integer scale correctness. */ unsigned base_width = roundf(geom->base_height * video_driver_get_aspect_ratio()); width = roundf(base_width * settings->video.scale); } else width = roundf(geom->base_width * settings->video.scale); height = roundf(geom->base_height * settings->video.scale); } if (width && height) RARCH_LOG("Video @ %ux%u\n", width, height); else RARCH_LOG("Video @ fullscreen\n"); driver->display_type = RARCH_DISPLAY_NONE; driver->video_display = 0; driver->video_window = 0; if (!init_video_pixel_converter(RARCH_SCALE_BASE * scale)) { RARCH_ERR("Failed to initialize pixel converter.\n"); rarch_fail(1, "init_video()"); } video.width = width; video.height = height; video.fullscreen = settings->video.fullscreen; video.vsync = settings->video.vsync && !global->system.force_nonblock; video.force_aspect = settings->video.force_aspect; #ifdef GEKKO video.viwidth = settings->video.viwidth; video.vfilter = settings->video.vfilter; #endif video.smooth = settings->video.smooth; video.input_scale = scale; video.rgb32 = video_state.filter.filter ? video_state.filter.out_rgb32 : (video_state.pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888); tmp = (const input_driver_t*)driver->input; /* Need to grab the "real" video driver interface on a reinit. */ find_video_driver(); #ifdef HAVE_THREADS if (settings->video.threaded && !video_state.hw_render_callback.context_type) { /* Can't do hardware rendering with threaded driver currently. */ RARCH_LOG("Starting threaded video driver ...\n"); if (!rarch_threaded_video_init(&driver->video, &driver->video_data, &driver->input, &driver->input_data, driver->video, &video)) { RARCH_ERR("Cannot open threaded video driver ... Exiting ...\n"); rarch_fail(1, "init_video()"); } } else #endif driver->video_data = driver->video->init(&video, &driver->input, &driver->input_data); if (!driver->video_data) { RARCH_ERR("Cannot open video driver ... Exiting ...\n"); rarch_fail(1, "init_video()"); } driver->video_poke = NULL; if (driver->video->poke_interface) driver->video->poke_interface(driver->video_data, &driver->video_poke); if (driver->video->viewport_info && (!custom_vp->width || !custom_vp->height)) { /* Force custom viewport to have sane parameters. */ custom_vp->width = width; custom_vp->height = height; video_driver_viewport_info(custom_vp); } video_driver_set_rotation( (settings->video.rotation + global->system.rotation) % 4); video_driver_suppress_screensaver(settings->ui.suspend_screensaver_enable); if (!driver->input) init_video_input(tmp); event_command(EVENT_CMD_OVERLAY_DEINIT); event_command(EVENT_CMD_OVERLAY_INIT); video_driver_cached_frame_set(&dummy_pixels, 4, 4, 8); #if defined(PSP) video_driver_set_texture_frame(&dummy_pixels, false, 1, 1, 1.0f); #endif }
/** * video_viewport_get_scaled_integer: * @vp : Viewport handle * @width : Width. * @height : Height. * @aspect_ratio : Aspect ratio (in float). * @keep_aspect : Preserve aspect ratio? * * Gets viewport scaling dimensions based on * scaled integer aspect ratio. **/ void video_viewport_get_scaled_integer(struct video_viewport *vp, unsigned width, unsigned height, float aspect_ratio, bool keep_aspect) { int padding_x = 0, padding_y = 0; settings_t *settings = config_get_ptr(); if (!vp) return; if (settings->video.aspect_ratio_idx == ASPECT_RATIO_CUSTOM) { struct video_viewport *custom = video_viewport_get_custom(); if (custom) { padding_x = width - custom->width; padding_y = height - custom->height; width = custom->width; height = custom->height; } } else { unsigned base_width; /* Use system reported sizes as these define the * geometry for the "normal" case. */ struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); unsigned base_height = av_info ? av_info->geometry.base_height : 0; if (base_height == 0) base_height = 1; /* Account for non-square pixels. * This is sort of contradictory with the goal of integer scale, * but it is desirable in some cases. * * If square pixels are used, base_height will be equal to * system->av_info.base_height. */ base_width = (unsigned)roundf(base_height * aspect_ratio); /* Make sure that we don't get 0x scale ... */ if (width >= base_width && height >= base_height) { if (keep_aspect) { /* X/Y scale must be same. */ unsigned max_scale = min(width / base_width, height / base_height); padding_x = width - base_width * max_scale; padding_y = height - base_height * max_scale; } else { /* X/Y can be independent, each scaled as much as possible. */ padding_x = width % base_width; padding_y = height % base_height; } } width -= padding_x; height -= padding_y; } vp->width = width; vp->height = height; vp->x = padding_x / 2; vp->y = padding_y / 2; }
bool core_ctl(enum core_ctl_state state, void *data) { static bool has_set_input_descriptors = false; static struct retro_callbacks retro_ctx; switch (state) { case CORE_CTL_RETRO_CHEAT_SET: { retro_ctx_cheat_info_t *info = (retro_ctx_cheat_info_t*)data; core.retro_cheat_set(info->index, info->enabled, info->code); } break; case CORE_CTL_RETRO_CHEAT_RESET: core.retro_cheat_reset(); break; case CORE_CTL_RETRO_API_VERSION: { retro_ctx_api_info_t *api = (retro_ctx_api_info_t*)data; api->version = core.retro_api_version(); } break; case CORE_CTL_SET_POLL_TYPE: { unsigned *poll_type = (unsigned*)data; core_poll_type = *poll_type; } break; case CORE_CTL_RETRO_SYMBOLS_INIT: { enum rarch_core_type *core_type = (enum rarch_core_type*)data; if (!core_type) return false; init_libretro_sym(*core_type, &core); } break; case CORE_CTL_RETRO_SET_CONTROLLER_PORT_DEVICE: { retro_ctx_controller_info_t *pad = (retro_ctx_controller_info_t*)data; if (!pad) return false; core.retro_set_controller_port_device(pad->port, pad->device); } break; case CORE_CTL_RETRO_GET_MEMORY: { retro_ctx_memory_info_t *info = (retro_ctx_memory_info_t*)data; if (!info) return false; info->size = core.retro_get_memory_size(info->id); info->data = core.retro_get_memory_data(info->id); } break; case CORE_CTL_RETRO_LOAD_GAME: { retro_ctx_load_content_info_t *load_info = (retro_ctx_load_content_info_t*)data; if (!load_info) return false; if (load_info->special) return core.retro_load_game_special(load_info->special->id, load_info->info, load_info->content->size); return core.retro_load_game(*load_info->content->elems[0].data ? load_info->info : NULL); } case CORE_CTL_RETRO_GET_SYSTEM_INFO: { struct retro_system_info *system = (struct retro_system_info*)data; if (!system) return false; core.retro_get_system_info(system); } break; case CORE_CTL_RETRO_UNSERIALIZE: { retro_ctx_serialize_info_t *info = (retro_ctx_serialize_info_t*)data; if (!info) return false; if (!core.retro_unserialize(info->data_const, info->size)) return false; } break; case CORE_CTL_RETRO_SERIALIZE: { retro_ctx_serialize_info_t *info = (retro_ctx_serialize_info_t*)data; if (!info) return false; if (!core.retro_serialize(info->data, info->size)) return false; } break; case CORE_CTL_RETRO_SERIALIZE_SIZE: { retro_ctx_size_info_t *info = (retro_ctx_size_info_t *)data; if (!info) return false; info->size = core.retro_serialize_size(); } break; case CORE_CTL_RETRO_CTX_FRAME_CB: { retro_ctx_frame_info_t *info = (retro_ctx_frame_info_t*)data; if (!info || !retro_ctx.frame_cb) return false; retro_ctx.frame_cb( info->data, info->width, info->height, info->pitch); } break; case CORE_CTL_RETRO_CTX_POLL_CB: if (!retro_ctx.poll_cb) return false; retro_ctx.poll_cb(); break; case CORE_CTL_RETRO_SET_ENVIRONMENT: { retro_ctx_environ_info_t *info = (retro_ctx_environ_info_t*)data; if (!info) return false; core.retro_set_environment(info->env); } break; case CORE_CTL_RETRO_GET_SYSTEM_AV_INFO: { struct retro_system_av_info *av_info = (struct retro_system_av_info*)data; if (!av_info) return false; core.retro_get_system_av_info(av_info); } break; case CORE_CTL_RETRO_RESET: core.retro_reset(); break; case CORE_CTL_RETRO_INIT: core.retro_init(); break; case CORE_CTL_RETRO_DEINIT: core.retro_deinit(); uninit_libretro_sym(&core); break; case CORE_CTL_RETRO_UNLOAD_GAME: video_driver_ctl(RARCH_DISPLAY_CTL_DEINIT_HW_CONTEXT, NULL); audio_driver_ctl(RARCH_AUDIO_CTL_STOP, NULL); core.retro_unload_game(); break; case CORE_CTL_RETRO_RUN: switch (core_poll_type) { case POLL_TYPE_EARLY: input_poll(); break; case POLL_TYPE_LATE: core_input_polled = false; break; } if (core.retro_run) core.retro_run(); if (core_poll_type == POLL_TYPE_LATE && !core_input_polled) input_poll(); break; case CORE_CTL_SET_CBS: return retro_set_default_callbacks(data); case CORE_CTL_SET_CBS_REWIND: retro_set_rewind_callbacks(); break; case CORE_CTL_INIT: { settings_t *settings = config_get_ptr(); core_poll_type = settings->input.poll_type_behavior; if (!core_ctl(CORE_CTL_VERIFY_API_VERSION, NULL)) return false; if (!retro_init_libretro_cbs(&retro_ctx)) return false; core_ctl(CORE_CTL_RETRO_GET_SYSTEM_AV_INFO, video_viewport_get_system_av_info()); runloop_ctl(RUNLOOP_CTL_SET_FRAME_LIMIT, NULL); } break; case CORE_CTL_DEINIT: return retro_uninit_libretro_cbs(&retro_ctx); case CORE_CTL_VERIFY_API_VERSION: { unsigned api_version = core.retro_api_version(); RARCH_LOG("Version of libretro API: %u\n", api_version); RARCH_LOG("Compiled against API: %u\n", RETRO_API_VERSION); if (api_version != RETRO_API_VERSION) { RARCH_WARN("%s\n", msg_hash_to_str(MSG_LIBRETRO_ABI_BREAK)); return false; } } break; case CORE_CTL_HAS_SET_INPUT_DESCRIPTORS: return has_set_input_descriptors; case CORE_CTL_SET_INPUT_DESCRIPTORS: has_set_input_descriptors = true; break; case CORE_CTL_UNSET_INPUT_DESCRIPTORS: has_set_input_descriptors = false; break; case CORE_CTL_NONE: default: break; } return true; }
static int httpserver_handle_basic_info(struct mg_connection* conn, void* cbdata) { 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", }; unsigned p, q, r; retro_ctx_api_info_t api; retro_ctx_region_info_t region; retro_ctx_memory_info_t sram; retro_ctx_memory_info_t rtc; retro_ctx_memory_info_t sysram; retro_ctx_memory_info_t vram; char core_path[PATH_MAX_LENGTH] = {0}; const char* pixel_format = NULL; const struct retro_subsystem_info* subsys = NULL; const struct retro_subsystem_rom_info* rom = NULL; const struct retro_subsystem_memory_info* mem = NULL; const struct retro_controller_description* ctrl = NULL; const char* comma = NULL; const struct core_option* opts = NULL; const struct retro_system_av_info* av_info = NULL; const core_option_manager_t* core_opts = NULL; const struct mg_request_info * req = mg_get_request_info(conn); const settings_t * settings = config_get_ptr(); rarch_system_info_t *system = runloop_get_system_info(); if (string_is_empty(system->info.library_name)) return httpserver_error(conn, 500, "Core not initialized in %s", __FUNCTION__); if (!core_is_game_loaded()) return httpserver_error(conn, 500, "Game not loaded in %s", __FUNCTION__); json_string_encode(core_path, sizeof(core_path), config_get_active_core_path()); core_api_version(&api); core_get_region(®ion); switch (video_driver_get_pixel_format()) { case RETRO_PIXEL_FORMAT_0RGB1555: pixel_format = "RETRO_PIXEL_FORMAT_0RGB1555"; break; case RETRO_PIXEL_FORMAT_XRGB8888: pixel_format = "RETRO_PIXEL_FORMAT_XRGB8888"; break; case RETRO_PIXEL_FORMAT_RGB565: pixel_format = "RETRO_PIXEL_FORMAT_RGB565"; break; default: pixel_format = "?"; break; } sram.id = RETRO_MEMORY_SAVE_RAM; core_get_memory(&sram); rtc.id = RETRO_MEMORY_RTC; core_get_memory(&rtc); sysram.id = RETRO_MEMORY_SYSTEM_RAM; core_get_memory(&sysram); vram.id = RETRO_MEMORY_VIDEO_RAM; core_get_memory(&vram); mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n" "{" "\"corePath\":\"%s\"," "\"apiVersion\":%u," "\"systemInfo\":" "{" "\"libraryName\":\"%s\"," "\"libraryVersion\":\"%s\"," "\"validExtensions\":\"%s\"," "\"needsFullpath\":%s," "\"blockExtract\":%s" "}," "\"region\":\"%s\"," "\"pixelFormat\":\"%s\"," "\"rotation\":%u," "\"performaceLevel\":%u," "\"supportsNoGame\":%s," #ifdef HAVE_CHEEVOS "\"frontendSupportsAchievements\":true," "\"coreSupportsAchievements\":%s," #else "\"frontendSupportsAchievements\":false," "\"coreSupportsAchievements\":null," #endif "\"saveRam\":{\"pointer\":\"%" PRIXPTR "\",\"size\":" STRING_REP_UINT64 "}," "\"rtcRam\":{\"pointer\":\"%" PRIXPTR "\",\"size\":" STRING_REP_UINT64 "}," "\"systemRam\":{\"pointer\":\"%" PRIXPTR "\",\"size\":" STRING_REP_UINT64 "}," "\"videoRam\":{\"pointer\":\"%" PRIXPTR "\",\"size\":" STRING_REP_UINT64 "},", core_path, api.version, system->info.library_name, system->info.library_version, system->info.valid_extensions, system->info.need_fullpath ? "true" : "false", system->info.block_extract ? "true" : "false", region.region ? "RETRO_REGION_PAL" : "RETRO_REGION_NTSC", pixel_format, system->rotation, system->performance_level, content_does_not_need_content() ? "true" : "false", #ifdef HAVE_CHEEVOS cheevos_get_support_cheevos() ? "true" : "false", #endif (uintptr_t)sram.data, sram.size, (uintptr_t)rtc.data, rtc.size, (uintptr_t)sysram.data, sysram.size, (uintptr_t)vram.data, vram.size ); mg_printf(conn, "\"subSystems\":["); subsys = system->subsystem.data; for (p = 0; p < system->subsystem.size; p++, subsys++) { mg_printf(conn, "%s{\"id\":%u,\"description\":\"%s\",\"identifier\":\"%s\",\"roms\":[", p == 0 ? "" : ",", subsys->id, subsys->desc, subsys->ident); rom = subsys->roms; for (q = 0; q < subsys->num_roms; q++, rom++) { mg_printf(conn, "%s{" "\"description\":\"%s\"," "\"extensions\":\"%s\"," "\"needsFullpath\":%s," "\"blockExtract\":%s," "\"required\":%s," "\"memory\":[", q == 0 ? "" : ",", rom->desc, rom->valid_extensions, rom->need_fullpath ? "true" : "false", rom->block_extract ? "true" : "false", rom->required ? "true" : "false" ); mem = rom->memory; comma = ""; for (r = 0; r < rom->num_memory; r++, mem++) { mg_printf(conn, "%s{\"extension\":\"%s\",\"type\":%u}", comma, mem->extension, mem->type); comma = ","; } mg_printf(conn, "]}"); } mg_printf(conn, "]}"); } av_info = video_viewport_get_system_av_info(); mg_printf(conn, "],\"avInfo\":{" "\"geometry\":{" "\"baseWidth\":%u," "\"baseHeight\":%u," "\"maxWidth\":%u," "\"maxHeight\":%u," "\"aspectRatio\":%f" "}," "\"timing\":{" "\"fps\":%f," "\"sampleRate\":%f" "}" "},", av_info->geometry.base_width, av_info->geometry.base_height, av_info->geometry.max_width, av_info->geometry.max_height, av_info->geometry.aspect_ratio, av_info->timing.fps, av_info->timing.sample_rate ); mg_printf(conn, "\"ports\":["); comma = ""; for (p = 0; p < system->ports.size; p++) { ctrl = system->ports.data[p].types; for (q = 0; q < system->ports.data[p].num_types; q++, ctrl++) { mg_printf(conn, "%s{\"id\":%u,\"description\":\"%s\"}", comma, ctrl->id, ctrl->desc); comma = ","; } } mg_printf(conn, "],\"inputDescriptors\":["); comma = ""; if (core_has_set_input_descriptor()) { for (p = 0; p < settings->input.max_users; p++) { for (q = 0; q < RARCH_FIRST_CUSTOM_BIND; q++) { const char* description = system->input_desc_btn[p][q]; if (description) { mg_printf(conn, "%s{\"player\":%u,\"button\":\"%s\",\"description\":\"%s\"}", comma, p + 1, libretro_btn_desc[q], description ); comma = ","; } } } } mg_printf(conn, "],\"coreOptions\":["); rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, (void*)&core_opts); opts = core_opts->opts; for (p = 0; p < core_opts->size; p++, opts++) { mg_printf(conn, "%s{\"key\":\"%s\",\"description\":\"%s\",\"values\":[", p == 0 ? "" : ",", opts->key, opts->desc); comma = ""; for (q = 0; q < opts->vals->size; q++) { mg_printf(conn, "%s\"%s\"", comma, opts->vals->elems[q].data); comma = ","; } mg_printf(conn, "]}"); } mg_printf(conn, "]}"); return 1; }
/** * recording_init: * * Initializes recording. * * Returns: true (1) if successful, otherwise false (0). **/ bool recording_init(void) { char recording_file[PATH_MAX_LENGTH] = {0}; struct ffemu_params params = {0}; global_t *global = global_get_ptr(); settings_t *settings = config_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); const struct retro_hw_render_callback *hw_render = (const struct retro_hw_render_callback*)video_driver_callback(); bool *recording_enabled = recording_is_enabled(); if (!*recording_enabled) return false; if (global->inited.core.type == CORE_TYPE_DUMMY) { RARCH_WARN("%s\n", msg_hash_to_str(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED)); return false; } if (!settings->video.gpu_record && hw_render->context_type) { RARCH_WARN("%s.\n", msg_hash_to_str(MSG_HW_RENDERED_MUST_USE_POSTSHADED_RECORDING)); return false; } RARCH_LOG("%s: FPS: %.4f, Sample rate: %.4f\n", msg_hash_to_str(MSG_CUSTOM_TIMING_GIVEN), (float)av_info->timing.fps, (float)av_info->timing.sample_rate); strlcpy(recording_file, global->record.path, sizeof(recording_file)); if (global->record.use_output_dir) fill_pathname_join(recording_file, global->record.output_dir, global->record.path, sizeof(recording_file)); params.out_width = av_info->geometry.base_width; params.out_height = av_info->geometry.base_height; params.fb_width = av_info->geometry.max_width; params.fb_height = av_info->geometry.max_height; params.channels = 2; params.filename = recording_file; params.fps = av_info->timing.fps; params.samplerate = av_info->timing.sample_rate; params.pix_fmt = (video_driver_get_pixel_format() == RETRO_PIXEL_FORMAT_XRGB8888) ? FFEMU_PIX_ARGB8888 : FFEMU_PIX_RGB565; params.config = NULL; if (*global->record.config) params.config = global->record.config; if (video_driver_ctl(RARCH_DISPLAY_CTL_SUPPORTS_RECORDING, NULL)) { unsigned gpu_size; struct video_viewport vp = {0}; video_driver_viewport_info(&vp); if (!vp.width || !vp.height) { RARCH_ERR("Failed to get viewport information from video driver. " "Cannot start recording ...\n"); return false; } params.out_width = vp.width; params.out_height = vp.height; params.fb_width = next_pow2(vp.width); params.fb_height = next_pow2(vp.height); if (settings->video.force_aspect && (video_driver_get_aspect_ratio() > 0.0f)) params.aspect_ratio = video_driver_get_aspect_ratio(); else params.aspect_ratio = (float)vp.width / vp.height; params.pix_fmt = FFEMU_PIX_BGR24; global->record.gpu_width = vp.width; global->record.gpu_height = vp.height; RARCH_LOG("%s %u x %u\n", msg_hash_to_str(MSG_DETECTED_VIEWPORT_OF), vp.width, vp.height); gpu_size = vp.width * vp.height * 3; if (!video_driver_ctl(RARCH_DISPLAY_CTL_GPU_RECORD_INIT, &gpu_size)) return false; } else { if (global->record.width || global->record.height) { params.out_width = global->record.width; params.out_height = global->record.height; } if (settings->video.force_aspect && (video_driver_get_aspect_ratio() > 0.0f)) params.aspect_ratio = video_driver_get_aspect_ratio(); else params.aspect_ratio = (float)params.out_width / params.out_height; if (settings->video.post_filter_record && video_driver_ctl(RARCH_DISPLAY_CTL_FRAME_FILTER_ALIVE, NULL)) { unsigned max_width = 0; unsigned max_height = 0; params.pix_fmt = FFEMU_PIX_RGB565; if (video_driver_ctl(RARCH_DISPLAY_CTL_FRAME_FILTER_IS_32BIT, NULL)) params.pix_fmt = FFEMU_PIX_ARGB8888; rarch_softfilter_get_max_output_size( video_driver_frame_filter_get_ptr(), &max_width, &max_height); params.fb_width = next_pow2(max_width); params.fb_height = next_pow2(max_height); } } RARCH_LOG("%s %s @ %ux%u. (FB size: %ux%u pix_fmt: %u)\n", msg_hash_to_str(MSG_RECORDING_TO), global->record.path, params.out_width, params.out_height, params.fb_width, params.fb_height, (unsigned)params.pix_fmt); if (!record_driver_init_first(&recording_driver, &recording_data, ¶ms)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_START_RECORDING)); event_command(EVENT_CMD_GPU_RECORD_DEINIT); 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; }