diff --git a/Makefile b/Makefile index 644de6e901..c62fd6b45d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ include config.mk +HAVE_LAKKA = 1 +DEFINES += -DHAVE_LAKKA TARGET = retroarch tools/retroarch-joyconfig tools/retrolaunch/retrolaunch @@ -67,7 +69,6 @@ endif DEFINES = -DHAVE_CONFIG_H -DHAVE_SCREENSHOTS -DRARCH_INTERNAL -DHAVE_CC_RESAMPLER -#HAVE_LAKKA = 1 ifeq ($(GLOBAL_CONFIG_DIR),) GLOBAL_CONFIG_DIR = /etc @@ -101,8 +102,9 @@ ifeq ($(HAVE_RGUI), 1) DEFINES += -DHAVE_MENU HAVE_MENU_COMMON = 1 ifeq ($(HAVE_LAKKA), 1) - OBJ += frontend/menu/disp/lakka.o + OBJ += frontend/menu/disp/png_texture_load.o frontend/menu/disp/tween.o frontend/menu/backend/menu_lakka_backend.o frontend/menu/disp/lakka.o DEFINES += -DHAVE_LAKKA + LIBS += -lpng endif endif diff --git a/driver.c b/driver.c index a62e622edb..29d53a5702 100644 --- a/driver.c +++ b/driver.c @@ -1681,12 +1681,13 @@ static const menu_ctx_driver_t *menu_ctx_drivers[] = { #if defined(HAVE_RMENU_XUI) &menu_ctx_rmenu_xui, #endif -#if defined(HAVE_RGUI) - &menu_ctx_rgui, -#endif #if defined(HAVE_LAKKA) &menu_ctx_lakka, #endif +#if defined(HAVE_RGUI) + &menu_ctx_rgui, +#endif + NULL // zero length array is not valid }; diff --git a/driver.h b/driver.h index 0ac93028dd..ec5a1e94fb 100644 --- a/driver.h +++ b/driver.h @@ -685,6 +685,7 @@ extern const menu_ctx_driver_t menu_ctx_rgui; extern const menu_ctx_driver_t menu_ctx_lakka; extern const menu_ctx_driver_backend_t menu_ctx_backend_common; +extern const menu_ctx_driver_backend_t menu_ctx_backend_lakka; #ifdef HAVE_FILTERS_BUILTIN extern const struct softfilter_implementation *blargg_ntsc_snes_rf_get_implementation(softfilter_simd_mask_t simd); diff --git a/frontend/menu/backend/menu_lakka_backend.c b/frontend/menu/backend/menu_lakka_backend.c new file mode 100644 index 0000000000..3bc75dcf91 --- /dev/null +++ b/frontend/menu/backend/menu_lakka_backend.c @@ -0,0 +1,4293 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2014 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include "menu_common_backend.h" +#include "../menu_navigation.h" +#include "../menu_input_line_cb.h" + +#include "../../../gfx/gfx_common.h" +#include "../../../driver.h" +#include "../../../file_ext.h" +#include "../../../input/input_common.h" +#include "../../../config.def.h" +#include "../../../input/keyboard_line.h" + +#ifdef HAVE_CONFIG_H +#include "../../../config.h" +#endif + +#if defined(__CELLOS_LV2__) +#include + +#if (CELL_SDK_VERSION > 0x340000) +#include +#endif +#endif + +static void menu_lakka_entries_init(void *data, unsigned menu_type) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + unsigned i, last; + char tmp[256]; + switch (menu_type) + { +#ifdef HAVE_SHADER_MANAGER + case RGUI_SETTINGS_SHADER_OPTIONS: + file_list_clear(rgui->selection_buf); + file_list_push(rgui->selection_buf, "Apply Shader Changes", + RGUI_SETTINGS_SHADER_APPLY, 0); + file_list_push(rgui->selection_buf, "Default Filter", RGUI_SETTINGS_SHADER_FILTER, 0); + file_list_push(rgui->selection_buf, "Load Shader Preset", + RGUI_SETTINGS_SHADER_PRESET, 0); + file_list_push(rgui->selection_buf, "Save As Shader Preset", + RGUI_SETTINGS_SHADER_PRESET_SAVE, 0); + file_list_push(rgui->selection_buf, "Shader Passes", + RGUI_SETTINGS_SHADER_PASSES, 0); + + for (i = 0; i < rgui->shader.passes; i++) + { + char buf[64]; + + snprintf(buf, sizeof(buf), "Shader #%u", i); + file_list_push(rgui->selection_buf, buf, + RGUI_SETTINGS_SHADER_0 + 3 * i, 0); + + snprintf(buf, sizeof(buf), "Shader #%u Filter", i); + file_list_push(rgui->selection_buf, buf, + RGUI_SETTINGS_SHADER_0_FILTER + 3 * i, 0); + + snprintf(buf, sizeof(buf), "Shader #%u Scale", i); + file_list_push(rgui->selection_buf, buf, + RGUI_SETTINGS_SHADER_0_SCALE + 3 * i, 0); + } + break; +#endif + case RGUI_SETTINGS_GENERAL_OPTIONS: + file_list_clear(rgui->selection_buf); + file_list_push(rgui->selection_buf, "Configuration Save On Exit", RGUI_SETTINGS_CONFIG_SAVE_ON_EXIT, 0); + file_list_push(rgui->selection_buf, "Configuration Per-Core", RGUI_SETTINGS_PER_CORE_CONFIG, 0); +#ifdef HAVE_SCREENSHOTS + file_list_push(rgui->selection_buf, "GPU Screenshots", RGUI_SETTINGS_GPU_SCREENSHOT, 0); +#endif + file_list_push(rgui->selection_buf, "Load Dummy On Core Shutdown", RGUI_SETTINGS_LOAD_DUMMY_ON_CORE_SHUTDOWN, 0); + file_list_push(rgui->selection_buf, "Show Framerate", RGUI_SETTINGS_DEBUG_TEXT, 0); + file_list_push(rgui->selection_buf, "Rewind", RGUI_SETTINGS_REWIND_ENABLE, 0); + file_list_push(rgui->selection_buf, "Rewind Granularity", RGUI_SETTINGS_REWIND_GRANULARITY, 0); + file_list_push(rgui->selection_buf, "SRAM Block Overwrite", RGUI_SETTINGS_BLOCK_SRAM_OVERWRITE, 0); +#if defined(HAVE_THREADS) + file_list_push(rgui->selection_buf, "SRAM Autosave", RGUI_SETTINGS_SRAM_AUTOSAVE, 0); +#endif + file_list_push(rgui->selection_buf, "Window Compositing", RGUI_SETTINGS_WINDOW_COMPOSITING_ENABLE, 0); + file_list_push(rgui->selection_buf, "Window Unfocus Pause", RGUI_SETTINGS_PAUSE_IF_WINDOW_FOCUS_LOST, 0); + file_list_push(rgui->selection_buf, "Savestate Autosave On Exit", RGUI_SETTINGS_SAVESTATE_AUTO_SAVE, 0); + file_list_push(rgui->selection_buf, "Savestate Autoload", RGUI_SETTINGS_SAVESTATE_AUTO_LOAD, 0); + break; + case RGUI_SETTINGS_VIDEO_OPTIONS: + file_list_clear(rgui->selection_buf); +#if defined(GEKKO) || defined(__CELLOS_LV2__) + file_list_push(rgui->selection_buf, "Screen Resolution", RGUI_SETTINGS_VIDEO_RESOLUTION, 0); +#endif + file_list_push(rgui->selection_buf, "Soft Filter", RGUI_SETTINGS_VIDEO_SOFTFILTER, 0); +#if defined(__CELLOS_LV2__) + file_list_push(rgui->selection_buf, "PAL60 Mode", RGUI_SETTINGS_VIDEO_PAL60, 0); +#endif +#ifndef HAVE_SHADER_MANAGER + file_list_push(rgui->selection_buf, "Default Filter", RGUI_SETTINGS_VIDEO_FILTER, 0); +#endif +#ifdef HW_RVL + file_list_push(rgui->selection_buf, "VI Trap filtering", RGUI_SETTINGS_VIDEO_SOFT_FILTER, 0); +#endif +#if defined(HW_RVL) || defined(_XBOX360) + file_list_push(rgui->selection_buf, "Gamma", RGUI_SETTINGS_VIDEO_GAMMA, 0); +#endif +#ifdef _XBOX1 + file_list_push(rgui->selection_buf, "Soft filtering", RGUI_SETTINGS_SOFT_DISPLAY_FILTER, 0); + file_list_push(rgui->selection_buf, "Flicker filtering", RGUI_SETTINGS_FLICKER_FILTER, 0); +#endif + file_list_push(rgui->selection_buf, "Integer Scale", RGUI_SETTINGS_VIDEO_INTEGER_SCALE, 0); + file_list_push(rgui->selection_buf, "Aspect Ratio", RGUI_SETTINGS_VIDEO_ASPECT_RATIO, 0); + file_list_push(rgui->selection_buf, "Custom Ratio", RGUI_SETTINGS_CUSTOM_VIEWPORT, 0); +#if !defined(RARCH_CONSOLE) && !defined(RARCH_MOBILE) + file_list_push(rgui->selection_buf, "Toggle Fullscreen", RGUI_SETTINGS_TOGGLE_FULLSCREEN, 0); +#endif + file_list_push(rgui->selection_buf, "Rotation", RGUI_SETTINGS_VIDEO_ROTATION, 0); + file_list_push(rgui->selection_buf, "VSync", RGUI_SETTINGS_VIDEO_VSYNC, 0); + file_list_push(rgui->selection_buf, "Hard GPU Sync", RGUI_SETTINGS_VIDEO_HARD_SYNC, 0); + file_list_push(rgui->selection_buf, "Hard GPU Sync Frames", RGUI_SETTINGS_VIDEO_HARD_SYNC_FRAMES, 0); +#if !defined(RARCH_MOBILE) + file_list_push(rgui->selection_buf, "Black Frame Insertion", RGUI_SETTINGS_VIDEO_BLACK_FRAME_INSERTION, 0); +#endif + file_list_push(rgui->selection_buf, "VSync Swap Interval", RGUI_SETTINGS_VIDEO_SWAP_INTERVAL, 0); +#if defined(HAVE_THREADS) && !defined(GEKKO) + file_list_push(rgui->selection_buf, "Threaded Driver", RGUI_SETTINGS_VIDEO_THREADED, 0); +#endif +#if !defined(RARCH_CONSOLE) && !defined(RARCH_MOBILE) + file_list_push(rgui->selection_buf, "Windowed Scale (X)", RGUI_SETTINGS_VIDEO_WINDOW_SCALE_X, 0); + file_list_push(rgui->selection_buf, "Windowed Scale (Y)", RGUI_SETTINGS_VIDEO_WINDOW_SCALE_Y, 0); +#endif + file_list_push(rgui->selection_buf, "Crop Overscan (reload)", RGUI_SETTINGS_VIDEO_CROP_OVERSCAN, 0); + file_list_push(rgui->selection_buf, "Monitor Index", RGUI_SETTINGS_VIDEO_MONITOR_INDEX, 0); + file_list_push(rgui->selection_buf, "Estimated Monitor FPS", RGUI_SETTINGS_VIDEO_REFRESH_RATE_AUTO, 0); + break; + case RGUI_SETTINGS_FONT_OPTIONS: + file_list_clear(rgui->selection_buf); + file_list_push(rgui->selection_buf, "OSD Font Enable", RGUI_SETTINGS_FONT_ENABLE, 0); + file_list_push(rgui->selection_buf, "OSD Font Scale to Window", RGUI_SETTINGS_FONT_SCALE, 0); + file_list_push(rgui->selection_buf, "OSD Font Size", RGUI_SETTINGS_FONT_SIZE, 0); + break; + case RGUI_SETTINGS_CORE_OPTIONS: + file_list_clear(rgui->selection_buf); + + if (g_extern.system.core_options) + { + size_t i, opts; + + opts = core_option_size(g_extern.system.core_options); + for (i = 0; i < opts; i++) + file_list_push(rgui->selection_buf, + core_option_get_desc(g_extern.system.core_options, i), RGUI_SETTINGS_CORE_OPTION_START + i, 0); + } + else + file_list_push(rgui->selection_buf, "No options available.", RGUI_SETTINGS_CORE_OPTION_NONE, 0); + break; + case RGUI_SETTINGS_CORE_INFO: + file_list_clear(rgui->selection_buf); + if (rgui->core_info_current.data) + { + snprintf(tmp, sizeof(tmp), "Core name: %s", + rgui->core_info_current.display_name ? rgui->core_info_current.display_name : ""); + file_list_push(rgui->selection_buf, tmp, RGUI_SETTINGS_CORE_INFO_NONE, 0); + + if (rgui->core_info_current.authors_list) + { + strlcpy(tmp, "Authors: ", sizeof(tmp)); + string_list_join_concat(tmp, sizeof(tmp), rgui->core_info_current.authors_list, ", "); + file_list_push(rgui->selection_buf, tmp, RGUI_SETTINGS_CORE_INFO_NONE, 0); + } + + if (rgui->core_info_current.permissions_list) + { + strlcpy(tmp, "Permissions: ", sizeof(tmp)); + string_list_join_concat(tmp, sizeof(tmp), rgui->core_info_current.permissions_list, ", "); + file_list_push(rgui->selection_buf, tmp, RGUI_SETTINGS_CORE_INFO_NONE, 0); + } + + if (rgui->core_info_current.supported_extensions_list) + { + strlcpy(tmp, "Supported extensions: ", sizeof(tmp)); + string_list_join_concat(tmp, sizeof(tmp), rgui->core_info_current.supported_extensions_list, ", "); + file_list_push(rgui->selection_buf, tmp, RGUI_SETTINGS_CORE_INFO_NONE, 0); + } + + if (rgui->core_info_current.firmware_count > 0) + { + core_info_list_update_missing_firmware(rgui->core_info, rgui->core_info_current.path, + g_settings.system_directory); + + file_list_push(rgui->selection_buf, "Firmware: ", RGUI_SETTINGS_CORE_INFO_NONE, 0); + for (i = 0; i < rgui->core_info_current.firmware_count; i++) + { + if (rgui->core_info_current.firmware[i].desc) + { + snprintf(tmp, sizeof(tmp), " name: %s", + rgui->core_info_current.firmware[i].desc ? rgui->core_info_current.firmware[i].desc : ""); + file_list_push(rgui->selection_buf, tmp, RGUI_SETTINGS_CORE_INFO_NONE, 0); + + snprintf(tmp, sizeof(tmp), " status: %s, %s", + rgui->core_info_current.firmware[i].missing ? "missing" : "present", + rgui->core_info_current.firmware[i].optional ? "optional" : "required"); + file_list_push(rgui->selection_buf, tmp, RGUI_SETTINGS_CORE_INFO_NONE, 0); + } + } + } + + if (rgui->core_info_current.notes) + { + snprintf(tmp, sizeof(tmp), "Core notes: "); + file_list_push(rgui->selection_buf, tmp, RGUI_SETTINGS_CORE_INFO_NONE, 0); + + for (i = 0; i < rgui->core_info_current.note_list->size; i++) + { + snprintf(tmp, sizeof(tmp), " %s", rgui->core_info_current.note_list->elems[i].data); + file_list_push(rgui->selection_buf, tmp, RGUI_SETTINGS_CORE_INFO_NONE, 0); + } + } + } + else + file_list_push(rgui->selection_buf, "No information available.", RGUI_SETTINGS_CORE_OPTION_NONE, 0); + break; + case RGUI_SETTINGS_OPTIONS: + file_list_clear(rgui->selection_buf); + file_list_push(rgui->selection_buf, "General Options", RGUI_SETTINGS_GENERAL_OPTIONS, 0); + file_list_push(rgui->selection_buf, "Video Options", RGUI_SETTINGS_VIDEO_OPTIONS, 0); +#ifdef HAVE_SHADER_MANAGER + file_list_push(rgui->selection_buf, "Shader Options", RGUI_SETTINGS_SHADER_OPTIONS, 0); +#endif + file_list_push(rgui->selection_buf, "Font Options", RGUI_SETTINGS_FONT_OPTIONS, 0); + file_list_push(rgui->selection_buf, "Audio Options", RGUI_SETTINGS_AUDIO_OPTIONS, 0); + file_list_push(rgui->selection_buf, "Input Options", RGUI_SETTINGS_INPUT_OPTIONS, 0); +#ifdef HAVE_OVERLAY + file_list_push(rgui->selection_buf, "Overlay Options", RGUI_SETTINGS_OVERLAY_OPTIONS, 0); +#endif +#ifdef HAVE_NETPLAY + file_list_push(rgui->selection_buf, "Netplay Options", RGUI_SETTINGS_NETPLAY_OPTIONS, 0); +#endif + file_list_push(rgui->selection_buf, "Path Options", RGUI_SETTINGS_PATH_OPTIONS, 0); + if (g_extern.main_is_init && !g_extern.libretro_dummy) + { + if (g_extern.system.disk_control.get_num_images) + file_list_push(rgui->selection_buf, "Disk Options", RGUI_SETTINGS_DISK_OPTIONS, 0); + } + file_list_push(rgui->selection_buf, "Privacy Options", RGUI_SETTINGS_PRIVACY_OPTIONS, 0); + break; + case RGUI_SETTINGS_PRIVACY_OPTIONS: + file_list_clear(rgui->selection_buf); +#ifdef HAVE_CAMERA + file_list_push(rgui->selection_buf, "Allow Camera", RGUI_SETTINGS_PRIVACY_CAMERA_ALLOW, 0); +#endif +#ifdef HAVE_LOCATION + file_list_push(rgui->selection_buf, "Allow Location Services", RGUI_SETTINGS_PRIVACY_LOCATION_ALLOW, 0); +#endif + break; + case RGUI_SETTINGS_DISK_OPTIONS: + file_list_clear(rgui->selection_buf); + file_list_push(rgui->selection_buf, "Disk Index", RGUI_SETTINGS_DISK_INDEX, 0); + file_list_push(rgui->selection_buf, "Disk Image Append", RGUI_SETTINGS_DISK_APPEND, 0); + break; + case RGUI_SETTINGS_OVERLAY_OPTIONS: + file_list_clear(rgui->selection_buf); + file_list_push(rgui->selection_buf, "Overlay Preset", RGUI_SETTINGS_OVERLAY_PRESET, 0); + file_list_push(rgui->selection_buf, "Overlay Opacity", RGUI_SETTINGS_OVERLAY_OPACITY, 0); + file_list_push(rgui->selection_buf, "Overlay Scale", RGUI_SETTINGS_OVERLAY_SCALE, 0); + break; +#ifdef HAVE_NETPLAY + case RGUI_SETTINGS_NETPLAY_OPTIONS: + file_list_clear(rgui->selection_buf); + file_list_push(rgui->selection_buf, "Netplay Enable", RGUI_SETTINGS_NETPLAY_ENABLE, 0); + file_list_push(rgui->selection_buf, "Netplay Mode", RGUI_SETTINGS_NETPLAY_MODE, 0); + file_list_push(rgui->selection_buf, "Spectator Mode Enable", RGUI_SETTINGS_NETPLAY_SPECTATOR_MODE_ENABLE, 0); + file_list_push(rgui->selection_buf, "Host IP Address", RGUI_SETTINGS_NETPLAY_HOST_IP_ADDRESS, 0); + file_list_push(rgui->selection_buf, "TCP/UDP Port", RGUI_SETTINGS_NETPLAY_TCP_UDP_PORT, 0); + file_list_push(rgui->selection_buf, "Delay Frames", RGUI_SETTINGS_NETPLAY_DELAY_FRAMES, 0); + file_list_push(rgui->selection_buf, "Nickname", RGUI_SETTINGS_NETPLAY_NICKNAME, 0); + break; +#endif + case RGUI_SETTINGS_PATH_OPTIONS: + file_list_clear(rgui->selection_buf); + file_list_push(rgui->selection_buf, "Content Directory", RGUI_BROWSER_DIR_PATH, 0); +#ifdef HAVE_DYNAMIC + file_list_push(rgui->selection_buf, "Config Directory", RGUI_CONFIG_DIR_PATH, 0); +#endif + file_list_push(rgui->selection_buf, "Core Directory", RGUI_LIBRETRO_DIR_PATH, 0); + file_list_push(rgui->selection_buf, "Core Info Directory", RGUI_LIBRETRO_INFO_DIR_PATH, 0); +#ifdef HAVE_DYLIB + file_list_push(rgui->selection_buf, "Video Filter Directory", RGUI_FILTER_DIR_PATH, 0); + file_list_push(rgui->selection_buf, "DSP Filter Directory", RGUI_DSP_FILTER_DIR_PATH, 0); +#endif +#ifdef HAVE_SHADER_MANAGER + file_list_push(rgui->selection_buf, "Shader Directory", RGUI_SHADER_DIR_PATH, 0); +#endif + file_list_push(rgui->selection_buf, "Savestate Directory", RGUI_SAVESTATE_DIR_PATH, 0); + file_list_push(rgui->selection_buf, "Savefile Directory", RGUI_SAVEFILE_DIR_PATH, 0); +#ifdef HAVE_OVERLAY + file_list_push(rgui->selection_buf, "Overlay Directory", RGUI_OVERLAY_DIR_PATH, 0); +#endif + file_list_push(rgui->selection_buf, "System Directory", RGUI_SYSTEM_DIR_PATH, 0); +#ifdef HAVE_SCREENSHOTS + file_list_push(rgui->selection_buf, "Screenshot Directory", RGUI_SCREENSHOT_DIR_PATH, 0); +#endif + break; + case RGUI_SETTINGS_INPUT_OPTIONS: + file_list_clear(rgui->selection_buf); + file_list_push(rgui->selection_buf, "Player", RGUI_SETTINGS_BIND_PLAYER, 0); + file_list_push(rgui->selection_buf, "Device", RGUI_SETTINGS_BIND_DEVICE, 0); + file_list_push(rgui->selection_buf, "Device Type", RGUI_SETTINGS_BIND_DEVICE_TYPE, 0); + file_list_push(rgui->selection_buf, "Analog D-pad Mode", RGUI_SETTINGS_BIND_ANALOG_MODE, 0); + file_list_push(rgui->selection_buf, "Input Axis Threshold", RGUI_SETTINGS_INPUT_AXIS_THRESHOLD, 0); + file_list_push(rgui->selection_buf, "Autodetect enable", RGUI_SETTINGS_DEVICE_AUTODETECT_ENABLE, 0); + + file_list_push(rgui->selection_buf, "Bind Mode", RGUI_SETTINGS_CUSTOM_BIND_MODE, 0); + file_list_push(rgui->selection_buf, "Configure All (RetroPad)", RGUI_SETTINGS_CUSTOM_BIND_ALL, 0); + file_list_push(rgui->selection_buf, "Default All (RetroPad)", RGUI_SETTINGS_CUSTOM_BIND_DEFAULT_ALL, 0); +#ifdef HAVE_OSK + file_list_push(rgui->selection_buf, "Onscreen Keyboard Enable", RGUI_SETTINGS_ONSCREEN_KEYBOARD_ENABLE, 0); +#endif + last = (driver.input && driver.input->set_keybinds && !driver.input->get_joypad_driver) ? RGUI_SETTINGS_BIND_R3 : RGUI_SETTINGS_BIND_MENU_TOGGLE; + for (i = RGUI_SETTINGS_BIND_BEGIN; i <= last; i++) + file_list_push(rgui->selection_buf, input_config_bind_map[i - RGUI_SETTINGS_BIND_BEGIN].desc, i, 0); + break; + case RGUI_SETTINGS_AUDIO_OPTIONS: + file_list_clear(rgui->selection_buf); + file_list_push(rgui->selection_buf, "DSP Filter", RGUI_SETTINGS_AUDIO_DSP_FILTER, 0); + file_list_push(rgui->selection_buf, "Mute Audio", RGUI_SETTINGS_AUDIO_MUTE, 0); + file_list_push(rgui->selection_buf, "Rate Control Delta", RGUI_SETTINGS_AUDIO_CONTROL_RATE_DELTA, 0); +#ifdef __CELLOS_LV2__ + file_list_push(rgui->selection_buf, "System BGM Control", RGUI_SETTINGS_CUSTOM_BGM_CONTROL_ENABLE, 0); +#endif +#ifdef _XBOX1 + file_list_push(rgui->selection_buf, "Volume Effect", RGUI_SETTINGS_AUDIO_DSP_EFFECT, 0); +#endif + file_list_push(rgui->selection_buf, "Volume Level", RGUI_SETTINGS_AUDIO_VOLUME, 0); + break; + case RGUI_SETTINGS_DRIVERS: + file_list_clear(rgui->selection_buf); + file_list_push(rgui->selection_buf, "Video Driver", RGUI_SETTINGS_DRIVER_VIDEO, 0); + file_list_push(rgui->selection_buf, "Audio Driver", RGUI_SETTINGS_DRIVER_AUDIO, 0); + file_list_push(rgui->selection_buf, "Audio Device", RGUI_SETTINGS_DRIVER_AUDIO_DEVICE, 0); + file_list_push(rgui->selection_buf, "Audio Resampler", RGUI_SETTINGS_DRIVER_AUDIO_RESAMPLER, 0); + file_list_push(rgui->selection_buf, "Input Driver", RGUI_SETTINGS_DRIVER_INPUT, 0); +#ifdef HAVE_CAMERA + file_list_push(rgui->selection_buf, "Camera Driver", RGUI_SETTINGS_DRIVER_CAMERA, 0); +#endif +#ifdef HAVE_LOCATION + file_list_push(rgui->selection_buf, "Location Driver", RGUI_SETTINGS_DRIVER_LOCATION, 0); +#endif +#ifdef HAVE_MENU + file_list_push(rgui->selection_buf, "Menu Driver", RGUI_SETTINGS_DRIVER_MENU, 0); +#endif + break; + case RGUI_SETTINGS: + file_list_clear(rgui->selection_buf); + +#if defined(HAVE_DYNAMIC) || defined(HAVE_LIBRETRO_MANAGEMENT) + file_list_push(rgui->selection_buf, "Core", RGUI_SETTINGS_CORE, 0); +#endif + if (rgui->history) + file_list_push(rgui->selection_buf, "Load Content (History)", RGUI_SETTINGS_OPEN_HISTORY, 0); + + if (rgui->core_info && core_info_list_num_info_files(rgui->core_info)) + file_list_push(rgui->selection_buf, "Load Content (Detect Core)", RGUI_SETTINGS_OPEN_FILEBROWSER_DEFERRED_CORE, 0); + + if (rgui->info.library_name || g_extern.system.info.library_name) + { + char load_game_core_msg[64]; + snprintf(load_game_core_msg, sizeof(load_game_core_msg), "Load Content (%s)", + rgui->info.library_name ? rgui->info.library_name : g_extern.system.info.library_name); + file_list_push(rgui->selection_buf, load_game_core_msg, RGUI_SETTINGS_OPEN_FILEBROWSER, 0); + } + + file_list_push(rgui->selection_buf, "Core Options", RGUI_SETTINGS_CORE_OPTIONS, 0); + file_list_push(rgui->selection_buf, "Core Information", RGUI_SETTINGS_CORE_INFO, 0); + file_list_push(rgui->selection_buf, "Settings", RGUI_SETTINGS_OPTIONS, 0); + file_list_push(rgui->selection_buf, "Drivers", RGUI_SETTINGS_DRIVERS, 0); + + if (g_extern.main_is_init && !g_extern.libretro_dummy) + { + file_list_push(rgui->selection_buf, "Save State", RGUI_SETTINGS_SAVESTATE_SAVE, 0); + file_list_push(rgui->selection_buf, "Load State", RGUI_SETTINGS_SAVESTATE_LOAD, 0); +#ifdef HAVE_SCREENSHOTS + file_list_push(rgui->selection_buf, "Take Screenshot", RGUI_SETTINGS_SCREENSHOT, 0); +#endif + file_list_push(rgui->selection_buf, "Resume Content", RGUI_SETTINGS_RESUME_GAME, 0); + file_list_push(rgui->selection_buf, "Restart Content", RGUI_SETTINGS_RESTART_GAME, 0); + + } +#ifndef HAVE_DYNAMIC + file_list_push(rgui->selection_buf, "Restart RetroArch", RGUI_SETTINGS_RESTART_EMULATOR, 0); +#endif + file_list_push(rgui->selection_buf, "RetroArch Config", RGUI_SETTINGS_CONFIG, 0); + file_list_push(rgui->selection_buf, "Save New Config", RGUI_SETTINGS_SAVE_CONFIG, 0); + file_list_push(rgui->selection_buf, "Help", RGUI_START_SCREEN, 0); + file_list_push(rgui->selection_buf, "Quit RetroArch", RGUI_SETTINGS_QUIT_RARCH, 0); + break; + } + + if (driver.menu_ctx && driver.menu_ctx->populate_entries) + driver.menu_ctx->populate_entries(rgui, menu_type); + +} + +static int menu_start_screen_iterate(void *data, unsigned action) +{ + unsigned i; + char msg[1024]; + rgui_handle_t *rgui = (rgui_handle_t*)data; + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render) + driver.menu_ctx->render(rgui); + + char desc[6][64]; + static const unsigned binds[] = { + RETRO_DEVICE_ID_JOYPAD_UP, + RETRO_DEVICE_ID_JOYPAD_DOWN, + RETRO_DEVICE_ID_JOYPAD_A, + RETRO_DEVICE_ID_JOYPAD_B, + RARCH_MENU_TOGGLE, + RARCH_QUIT_KEY, + }; + + for (i = 0; i < ARRAY_SIZE(binds); i++) + { + if (driver.input && driver.input->set_keybinds) + { + struct platform_bind key_label; + strlcpy(key_label.desc, "Unknown", sizeof(key_label.desc)); + key_label.joykey = g_settings.input.binds[0][binds[i]].joykey; + driver.input->set_keybinds(&key_label, 0, 0, 0, 1ULL << KEYBINDS_ACTION_GET_BIND_LABEL); + strlcpy(desc[i], key_label.desc, sizeof(desc[i])); + } + else + { + const struct retro_keybind *bind = &g_settings.input.binds[0][binds[i]]; + input_get_bind_string(desc[i], bind, sizeof(desc[i])); + } + } + + snprintf(msg, sizeof(msg), + "-- Welcome to RetroArch / RGUI --\n" + " \n" // strtok_r doesn't split empty strings. + + "Basic RGUI controls:\n" + " Scroll (Up): %-20s\n" + " Scroll (Down): %-20s\n" + " Accept/OK: %-20s\n" + " Back: %-20s\n" + "Enter/Exit RGUI: %-20s\n" + " Exit RetroArch: %-20s\n" + " \n" + + "To run content:\n" + "Load a libretro core (Core).\n" + "Load a content file (Load Content). \n" + " \n" + + "See Path Options to set directories\n" + "for faster access to files.\n" + " \n" + + "Press Accept/OK to continue.", + desc[0], desc[1], desc[2], desc[3], desc[4], desc[5]); + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render_messagebox) + driver.menu_ctx->render_messagebox(rgui, msg); + + if (action == RGUI_ACTION_OK) + file_list_pop(rgui->menu_stack, &rgui->selection_ptr); + return 0; +} + +static unsigned menu_lakka_type_is(unsigned type) +{ + unsigned ret = 0; + bool type_found; + + type_found = + type == RGUI_SETTINGS || + type == RGUI_SETTINGS_GENERAL_OPTIONS || + type == RGUI_SETTINGS_CORE_OPTIONS || + type == RGUI_SETTINGS_CORE_INFO || + type == RGUI_SETTINGS_VIDEO_OPTIONS || + type == RGUI_SETTINGS_FONT_OPTIONS || + type == RGUI_SETTINGS_SHADER_OPTIONS || + type == RGUI_SETTINGS_AUDIO_OPTIONS || + type == RGUI_SETTINGS_DISK_OPTIONS || + type == RGUI_SETTINGS_PATH_OPTIONS || + type == RGUI_SETTINGS_PRIVACY_OPTIONS || + type == RGUI_SETTINGS_OVERLAY_OPTIONS || + type == RGUI_SETTINGS_NETPLAY_OPTIONS || + type == RGUI_SETTINGS_OPTIONS || + type == RGUI_SETTINGS_DRIVERS || + (type == RGUI_SETTINGS_INPUT_OPTIONS); + + if (type_found) + { + ret = RGUI_SETTINGS; + return ret; + } + + type_found = (type >= RGUI_SETTINGS_SHADER_0 && + type <= RGUI_SETTINGS_SHADER_LAST && + ((type - RGUI_SETTINGS_SHADER_0) % 3) == 0) || + type == RGUI_SETTINGS_SHADER_PRESET; + + if (type_found) + { + ret = RGUI_SETTINGS_SHADER_OPTIONS; + return ret; + } + + type_found = type == RGUI_BROWSER_DIR_PATH || + type == RGUI_SHADER_DIR_PATH || + type == RGUI_FILTER_DIR_PATH || + type == RGUI_DSP_FILTER_DIR_PATH || + type == RGUI_SAVESTATE_DIR_PATH || + type == RGUI_LIBRETRO_DIR_PATH || + type == RGUI_LIBRETRO_INFO_DIR_PATH || + type == RGUI_CONFIG_DIR_PATH || + type == RGUI_SAVEFILE_DIR_PATH || + type == RGUI_OVERLAY_DIR_PATH || + type == RGUI_SCREENSHOT_DIR_PATH || + type == RGUI_SYSTEM_DIR_PATH; + + if (type_found) + { + ret = RGUI_FILE_DIRECTORY; + return ret; + } + + return ret; +} + +static int menu_settings_iterate(void *data, unsigned action) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + rgui->frame_buf_pitch = rgui->width * 2; + unsigned type = 0; + const char *label = NULL; + if (action != RGUI_ACTION_REFRESH) + file_list_get_at_offset(rgui->selection_buf, rgui->selection_ptr, &label, &type); + + if (type == RGUI_SETTINGS_CORE) + label = rgui->libretro_dir; + else if (type == RGUI_SETTINGS_CONFIG) + label = g_settings.rgui_config_directory; + else if (type == RGUI_SETTINGS_DISK_APPEND) + label = g_settings.rgui_content_directory; + + const char *dir = NULL; + unsigned menu_type = 0; + file_list_get_last(rgui->menu_stack, &dir, &menu_type); + + if (rgui->need_refresh) + action = RGUI_ACTION_NOOP; + + switch (action) + { + case RGUI_ACTION_UP: + if (rgui->selection_ptr > 0) + menu_decrement_navigation(rgui); + else + menu_set_navigation(rgui, rgui->selection_buf->size - 1); + break; + + case RGUI_ACTION_DOWN: + if (rgui->selection_ptr + 1 < rgui->selection_buf->size) + menu_increment_navigation(rgui); + else + menu_clear_navigation(rgui); + break; + + case RGUI_ACTION_CANCEL: + if (rgui->menu_stack->size > 1) + { + file_list_pop(rgui->menu_stack, &rgui->selection_ptr); + rgui->need_refresh = true; + } + break; + + case RGUI_ACTION_LEFT: + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + case RGUI_ACTION_START: + if ((type == RGUI_SETTINGS_OPEN_FILEBROWSER || type == RGUI_SETTINGS_OPEN_FILEBROWSER_DEFERRED_CORE) + && action == RGUI_ACTION_OK) + { + rgui->defer_core = type == RGUI_SETTINGS_OPEN_FILEBROWSER_DEFERRED_CORE; + file_list_push(rgui->menu_stack, g_settings.rgui_content_directory, RGUI_FILE_DIRECTORY, rgui->selection_ptr); + menu_clear_navigation(rgui); + rgui->need_refresh = true; + } + else if ((type == RGUI_SETTINGS_OPEN_HISTORY || menu_lakka_type_is(type) == RGUI_FILE_DIRECTORY) && action == RGUI_ACTION_OK) + { + file_list_push(rgui->menu_stack, "", type, rgui->selection_ptr); + menu_clear_navigation(rgui); + rgui->need_refresh = true; + } + else if ((menu_lakka_type_is(type) == RGUI_SETTINGS || type == RGUI_SETTINGS_CORE || type == RGUI_SETTINGS_CONFIG || type == RGUI_SETTINGS_DISK_APPEND) && action == RGUI_ACTION_OK) + { + file_list_push(rgui->menu_stack, label, type, rgui->selection_ptr); + menu_clear_navigation(rgui); + rgui->need_refresh = true; + } + else if (type == RGUI_SETTINGS_CUSTOM_VIEWPORT && action == RGUI_ACTION_OK) + { + file_list_push(rgui->menu_stack, "", type, rgui->selection_ptr); + + // Start with something sane. + rarch_viewport_t *custom = &g_extern.console.screen.viewports.custom_vp; + + if (driver.video_data && driver.video && driver.video->viewport_info) + driver.video->viewport_info(driver.video_data, custom); + aspectratio_lut[ASPECT_RATIO_CUSTOM].value = (float)custom->width / custom->height; + + g_settings.video.aspect_ratio_idx = ASPECT_RATIO_CUSTOM; + if (driver.video_data && driver.video_poke && driver.video_poke->set_aspect_ratio) + driver.video_poke->set_aspect_ratio(driver.video_data, + g_settings.video.aspect_ratio_idx); + } + else + { + int ret = 0; + + if (driver.menu_ctx && driver.menu_ctx->backend && driver.menu_ctx->backend->setting_toggle) + ret = driver.menu_ctx->backend->setting_toggle(rgui, type, action, menu_type); + if (ret) + return ret; + } + break; + + case RGUI_ACTION_REFRESH: + menu_clear_navigation(rgui); + rgui->need_refresh = true; + break; + + case RGUI_ACTION_MESSAGE: + rgui->msg_force = true; + break; + + default: + break; + } + + file_list_get_last(rgui->menu_stack, &dir, &menu_type); + + if (rgui->need_refresh && !(menu_type == RGUI_FILE_DIRECTORY || + menu_lakka_type_is(menu_type) == RGUI_SETTINGS_SHADER_OPTIONS|| + menu_lakka_type_is(menu_type) == RGUI_FILE_DIRECTORY || + menu_type == RGUI_SETTINGS_VIDEO_SOFTFILTER || + menu_type == RGUI_SETTINGS_AUDIO_DSP_FILTER || + menu_type == RGUI_SETTINGS_OVERLAY_PRESET || + menu_type == RGUI_SETTINGS_CORE || + menu_type == RGUI_SETTINGS_CONFIG || + menu_type == RGUI_SETTINGS_DISK_APPEND || + menu_type == RGUI_SETTINGS_OPEN_HISTORY)) + { + rgui->need_refresh = false; + if ( + menu_type == RGUI_SETTINGS_INPUT_OPTIONS + || menu_type == RGUI_SETTINGS_PATH_OPTIONS + || menu_type == RGUI_SETTINGS_OVERLAY_OPTIONS + || menu_type == RGUI_SETTINGS_NETPLAY_OPTIONS + || menu_type == RGUI_SETTINGS_OPTIONS + || menu_type == RGUI_SETTINGS_DRIVERS + || menu_type == RGUI_SETTINGS_CORE_INFO + || menu_type == RGUI_SETTINGS_CORE_OPTIONS + || menu_type == RGUI_SETTINGS_AUDIO_OPTIONS + || menu_type == RGUI_SETTINGS_DISK_OPTIONS + || menu_type == RGUI_SETTINGS_PRIVACY_OPTIONS + || menu_type == RGUI_SETTINGS_GENERAL_OPTIONS + || menu_type == RGUI_SETTINGS_VIDEO_OPTIONS + || menu_type == RGUI_SETTINGS_FONT_OPTIONS + || menu_type == RGUI_SETTINGS_SHADER_OPTIONS + ) + menu_lakka_entries_init(rgui, menu_type); + else + menu_lakka_entries_init(rgui, RGUI_SETTINGS); + } + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render) + driver.menu_ctx->render(rgui); + + // Have to defer it so we let settings refresh. + if (rgui->push_start_screen) + { + rgui->push_start_screen = false; + file_list_push(rgui->menu_stack, "", RGUI_START_SCREEN, 0); + } + + return 0; +} + +static int menu_viewport_iterate(void *data, unsigned action) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + rarch_viewport_t *custom = &g_extern.console.screen.viewports.custom_vp; + + unsigned menu_type = 0; + file_list_get_last(rgui->menu_stack, NULL, &menu_type); + + struct retro_game_geometry *geom = &g_extern.system.av_info.geometry; + int stride_x = g_settings.video.scale_integer ? + geom->base_width : 1; + int stride_y = g_settings.video.scale_integer ? + geom->base_height : 1; + + switch (action) + { + case RGUI_ACTION_UP: + if (menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT) + { + custom->y -= stride_y; + custom->height += stride_y; + } + else if (custom->height >= (unsigned)stride_y) + custom->height -= stride_y; + + if (driver.video_data && driver.video_poke && driver.video_poke->apply_state_changes) + driver.video_poke->apply_state_changes(driver.video_data); + break; + + case RGUI_ACTION_DOWN: + if (menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT) + { + custom->y += stride_y; + if (custom->height >= (unsigned)stride_y) + custom->height -= stride_y; + } + else + custom->height += stride_y; + + if (driver.video_data && driver.video_poke && driver.video_poke->apply_state_changes) + driver.video_poke->apply_state_changes(driver.video_data); + break; + + case RGUI_ACTION_LEFT: + if (menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT) + { + custom->x -= stride_x; + custom->width += stride_x; + } + else if (custom->width >= (unsigned)stride_x) + custom->width -= stride_x; + + if (driver.video_data && driver.video_poke && driver.video_poke->apply_state_changes) + driver.video_poke->apply_state_changes(driver.video_data); + break; + + case RGUI_ACTION_RIGHT: + if (menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT) + { + custom->x += stride_x; + if (custom->width >= (unsigned)stride_x) + custom->width -= stride_x; + } + else + custom->width += stride_x; + + if (driver.video_data && driver.video_poke && driver.video_poke->apply_state_changes) + driver.video_poke->apply_state_changes(driver.video_data); + break; + + case RGUI_ACTION_CANCEL: + file_list_pop(rgui->menu_stack, &rgui->selection_ptr); + if (menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT_2) + { + file_list_push(rgui->menu_stack, "", + RGUI_SETTINGS_CUSTOM_VIEWPORT, + rgui->selection_ptr); + } + break; + + case RGUI_ACTION_OK: + file_list_pop(rgui->menu_stack, &rgui->selection_ptr); + if (menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT + && !g_settings.video.scale_integer) + { + file_list_push(rgui->menu_stack, "", + RGUI_SETTINGS_CUSTOM_VIEWPORT_2, + rgui->selection_ptr); + } + break; + + case RGUI_ACTION_START: + if (!g_settings.video.scale_integer) + { + rarch_viewport_t vp; + + if (driver.video_data && driver.video && driver.video->viewport_info) + driver.video->viewport_info(driver.video_data, &vp); + + if (menu_type == RGUI_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; + } + + if (driver.video_data && driver.video_poke && driver.video_poke->apply_state_changes) + driver.video_poke->apply_state_changes(driver.video_data); + } + break; + + case RGUI_ACTION_MESSAGE: + rgui->msg_force = true; + break; + + default: + break; + } + + file_list_get_last(rgui->menu_stack, NULL, &menu_type); + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render) + driver.menu_ctx->render(rgui); + + const char *base_msg = NULL; + char msg[64]; + + if (g_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(msg, sizeof(msg), "%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 (menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT) + base_msg = "Set Upper-Left Corner"; + else if (menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT_2) + base_msg = "Set Bottom-Right Corner"; + + snprintf(msg, sizeof(msg), "%s (%d, %d : %4ux%4u)", + base_msg, custom->x, custom->y, custom->width, custom->height); + } + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render_messagebox) + driver.menu_ctx->render_messagebox(rgui, msg); + + 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; + + if (driver.video_data && driver.video_poke && driver.video_poke->apply_state_changes) + driver.video_poke->apply_state_changes(driver.video_data); + + return 0; +} + +static void menu_parse_and_resolve(void *data, unsigned menu_type) +{ + const core_info_t *info = NULL; + const char *dir; + size_t i, list_size; + file_list_t *list; + rgui_handle_t *rgui; + + rgui = (rgui_handle_t*)data; + dir = NULL; + + file_list_clear(rgui->selection_buf); + + // parsing switch + switch (menu_type) + { + case RGUI_SETTINGS_OPEN_HISTORY: + /* History parse */ + list_size = rom_history_size(rgui->history); + + for (i = 0; i < list_size; i++) + { + const char *path, *core_path, *core_name; + char fill_buf[PATH_MAX]; + + path = NULL; + core_path = NULL; + core_name = NULL; + + rom_history_get_index(rgui->history, i, + &path, &core_path, &core_name); + + if (path) + { + char path_short[PATH_MAX]; + fill_pathname(path_short, path_basename(path), "", sizeof(path_short)); + + snprintf(fill_buf, sizeof(fill_buf), "%s (%s)", + path_short, core_name); + } + else + strlcpy(fill_buf, core_name, sizeof(fill_buf)); + + file_list_push(rgui->selection_buf, fill_buf, RGUI_FILE_PLAIN, 0); + } + break; + case RGUI_SETTINGS_DEFERRED_CORE: + break; + default: + { + /* Directory parse */ + file_list_get_last(rgui->menu_stack, &dir, &menu_type); + + if (!*dir) + { +#if defined(GEKKO) +#ifdef HW_RVL + file_list_push(rgui->selection_buf, "sd:/", menu_type, 0); + file_list_push(rgui->selection_buf, "usb:/", menu_type, 0); +#endif + file_list_push(rgui->selection_buf, "carda:/", menu_type, 0); + file_list_push(rgui->selection_buf, "cardb:/", menu_type, 0); +#elif defined(_XBOX1) + file_list_push(rgui->selection_buf, "C:", menu_type, 0); + file_list_push(rgui->selection_buf, "D:", menu_type, 0); + file_list_push(rgui->selection_buf, "E:", menu_type, 0); + file_list_push(rgui->selection_buf, "F:", menu_type, 0); + file_list_push(rgui->selection_buf, "G:", menu_type, 0); +#elif defined(_XBOX360) + file_list_push(rgui->selection_buf, "game:", menu_type, 0); +#elif defined(_WIN32) + unsigned drives = GetLogicalDrives(); + char drive[] = " :\\"; + for (i = 0; i < 32; i++) + { + drive[0] = 'A' + i; + if (drives & (1 << i)) + file_list_push(rgui->selection_buf, drive, menu_type, 0); + } +#elif defined(__CELLOS_LV2__) + file_list_push(rgui->selection_buf, "/app_home/", menu_type, 0); + file_list_push(rgui->selection_buf, "/dev_hdd0/", menu_type, 0); + file_list_push(rgui->selection_buf, "/dev_hdd1/", menu_type, 0); + file_list_push(rgui->selection_buf, "/host_root/", menu_type, 0); + file_list_push(rgui->selection_buf, "/dev_usb000/", menu_type, 0); + file_list_push(rgui->selection_buf, "/dev_usb001/", menu_type, 0); + file_list_push(rgui->selection_buf, "/dev_usb002/", menu_type, 0); + file_list_push(rgui->selection_buf, "/dev_usb003/", menu_type, 0); + file_list_push(rgui->selection_buf, "/dev_usb004/", menu_type, 0); + file_list_push(rgui->selection_buf, "/dev_usb005/", menu_type, 0); + file_list_push(rgui->selection_buf, "/dev_usb006/", menu_type, 0); +#elif defined(PSP) + file_list_push(rgui->selection_buf, "ms0:/", menu_type, 0); + file_list_push(rgui->selection_buf, "ef0:/", menu_type, 0); + file_list_push(rgui->selection_buf, "host0:/", menu_type, 0); +#else + file_list_push(rgui->selection_buf, "/", menu_type, 0); +#endif + return; + } +#if defined(GEKKO) && defined(HW_RVL) + LWP_MutexLock(gx_device_mutex); + int dev = gx_get_device_from_path(dir); + + if (dev != -1 && !gx_devices[dev].mounted && gx_devices[dev].interface->isInserted()) + fatMountSimple(gx_devices[dev].name, gx_devices[dev].interface); + + LWP_MutexUnlock(gx_device_mutex); +#endif + + const char *exts; + char ext_buf[1024]; + if (menu_type == RGUI_SETTINGS_CORE) + exts = EXT_EXECUTABLES; + else if (menu_type == RGUI_SETTINGS_CONFIG) + exts = "cfg"; + else if (menu_type == RGUI_SETTINGS_SHADER_PRESET) + exts = "cgp|glslp"; + else if (menu_lakka_type_is(menu_type) == RGUI_SETTINGS_SHADER_OPTIONS) + exts = "cg|glsl"; + else if (menu_type == RGUI_SETTINGS_VIDEO_SOFTFILTER) + exts = EXT_EXECUTABLES; + else if (menu_type == RGUI_SETTINGS_AUDIO_DSP_FILTER) + exts = EXT_EXECUTABLES; + else if (menu_type == RGUI_SETTINGS_OVERLAY_PRESET) + exts = "cfg"; + else if (menu_lakka_type_is(menu_type) == RGUI_FILE_DIRECTORY) + exts = ""; // we ignore files anyway + else if (rgui->defer_core) + exts = rgui->core_info ? core_info_list_get_all_extensions(rgui->core_info) : ""; + else if (rgui->info.valid_extensions) + { + exts = ext_buf; + if (*rgui->info.valid_extensions) + snprintf(ext_buf, sizeof(ext_buf), "%s|zip", rgui->info.valid_extensions); + else + *ext_buf = '\0'; + } + else + exts = g_extern.system.valid_extensions; + + struct string_list *list = dir_list_new(dir, exts, true); + if (!list) + return; + + dir_list_sort(list, true); + + if (menu_lakka_type_is(menu_type) == RGUI_FILE_DIRECTORY) + file_list_push(rgui->selection_buf, "", RGUI_FILE_USE_DIRECTORY, 0); + + for (i = 0; i < list->size; i++) + { + bool is_dir = list->elems[i].attr.b; + + if ((menu_lakka_type_is(menu_type) == RGUI_FILE_DIRECTORY) && !is_dir) + continue; + + // Need to preserve slash first time. + const char *path = list->elems[i].data; + if (*dir) + path = path_basename(path); + +#ifdef HAVE_LIBRETRO_MANAGEMENT + if (menu_type == RGUI_SETTINGS_CORE && (is_dir || strcasecmp(path, SALAMANDER_FILE) == 0)) + continue; +#endif + + // Push menu_type further down in the chain. + // Needed for shader manager currently. + file_list_push(rgui->selection_buf, path, + is_dir ? menu_type : RGUI_FILE_PLAIN, 0); + } + + if (driver.menu_ctx && driver.menu_ctx->backend->entries_init) + driver.menu_ctx->backend->entries_init(rgui, menu_type); + + string_list_free(list); + } + } + + // resolving switch + switch (menu_type) + { + case RGUI_SETTINGS_CORE: + dir = NULL; + list = (file_list_t*)rgui->selection_buf; + file_list_get_last(rgui->menu_stack, &dir, &menu_type); + list_size = list->size; + for (i = 0; i < list_size; i++) + { + const char *path; + unsigned type = 0; + file_list_get_at_offset(list, i, &path, &type); + if (type != RGUI_FILE_PLAIN) + continue; + + char core_path[PATH_MAX]; + fill_pathname_join(core_path, dir, path, sizeof(core_path)); + + char display_name[256]; + if (rgui->core_info && + core_info_list_get_display_name(rgui->core_info, + core_path, display_name, sizeof(display_name))) + file_list_set_alt_at_offset(list, i, display_name); + } + file_list_sort_on_alt(rgui->selection_buf); + break; + case RGUI_SETTINGS_DEFERRED_CORE: + core_info_list_get_supported_cores(rgui->core_info, rgui->deferred_path, &info, &list_size); + for (i = 0; i < list_size; i++) + { + file_list_push(rgui->selection_buf, info[i].path, RGUI_FILE_PLAIN, 0); + file_list_set_alt_at_offset(rgui->selection_buf, i, info[i].display_name); + } + file_list_sort_on_alt(rgui->selection_buf); + break; + default: + (void)0; + } + + rgui->scroll_indices_size = 0; + if (menu_type != RGUI_SETTINGS_OPEN_HISTORY) + menu_build_scroll_indices(rgui, rgui->selection_buf); + + // Before a refresh, we could have deleted a file on disk, causing + // selection_ptr to suddendly be out of range. Ensure it doesn't overflow. + if (rgui->selection_ptr >= rgui->selection_buf->size && rgui->selection_buf->size) + menu_set_navigation(rgui, rgui->selection_buf->size - 1); + else if (!rgui->selection_buf->size) + menu_clear_navigation(rgui); +} + +// This only makes sense for PC so far. +// Consoles use set_keybind callbacks instead. +static int menu_custom_bind_iterate(void *data, unsigned action) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + (void)action; // Have to ignore action here. Only bind that should work here is Quit RetroArch or something like that. + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render) + driver.menu_ctx->render(rgui); + + char msg[256]; + snprintf(msg, sizeof(msg), "[%s]\npress joypad\n(RETURN to skip)", input_config_bind_map[rgui->binds.begin - RGUI_SETTINGS_BIND_BEGIN].desc); + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render_messagebox) + driver.menu_ctx->render_messagebox(rgui, msg); + + struct rgui_bind_state binds = rgui->binds; + menu_poll_bind_state(&binds); + + if ((binds.skip && !rgui->binds.skip) || menu_poll_find_trigger(&rgui->binds, &binds)) + { + binds.begin++; + if (binds.begin <= binds.last) + binds.target++; + else + file_list_pop(rgui->menu_stack, &rgui->selection_ptr); + + // Avoid new binds triggering things right away. + rgui->trigger_state = 0; + rgui->old_input_state = -1ULL; + } + rgui->binds = binds; + return 0; +} + +static int menu_custom_bind_iterate_keyboard(void *data, unsigned action) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + (void)action; // Have to ignore action here. + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render) + driver.menu_ctx->render(rgui); + + int64_t current = rarch_get_time_usec(); + int timeout = (rgui->binds.timeout_end - current) / 1000000; + + char msg[256]; + snprintf(msg, sizeof(msg), "[%s]\npress keyboard\n(timeout %d seconds)", + input_config_bind_map[rgui->binds.begin - RGUI_SETTINGS_BIND_BEGIN].desc, timeout); + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render_messagebox) + driver.menu_ctx->render_messagebox(rgui, msg); + + bool timed_out = false; + if (timeout <= 0) + { + rgui->binds.begin++; + rgui->binds.target->key = RETROK_UNKNOWN; // Could be unsafe, but whatever. + rgui->binds.target++; + rgui->binds.timeout_end = rarch_get_time_usec() + RGUI_KEYBOARD_BIND_TIMEOUT_SECONDS * 1000000; + timed_out = true; + } + + // binds.begin is updated in keyboard_press callback. + if (rgui->binds.begin > rgui->binds.last) + { + file_list_pop(rgui->menu_stack, &rgui->selection_ptr); + + // Avoid new binds triggering things right away. + rgui->trigger_state = 0; + rgui->old_input_state = -1ULL; + + // We won't be getting any key events, so just cancel early. + if (timed_out) + input_keyboard_wait_keys_cancel(); + } + return 0; +} + +static int menu_lakka_iterate(void *data, unsigned action) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + + const char *dir = 0; + unsigned menu_type = 0; + file_list_get_last(rgui->menu_stack, &dir, &menu_type); + int ret = 0; + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->set_texture) + driver.menu_ctx->set_texture(rgui, false); + +#ifdef HAVE_OSK + // process pending osk init callback + if (g_extern.osk.cb_init) + { + if (g_extern.osk.cb_init(driver.osk_data)) + g_extern.osk.cb_init = NULL; + } + + // process pending osk callback + if (g_extern.osk.cb_callback) + { + if (g_extern.osk.cb_callback(driver.osk_data)) + g_extern.osk.cb_callback = NULL; + } +#endif + + if (menu_type == RGUI_START_SCREEN) + return menu_start_screen_iterate(rgui, action); + else if (menu_lakka_type_is(menu_type) == RGUI_SETTINGS) + return menu_settings_iterate(rgui, action); + else if (menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT || menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT_2) + return menu_viewport_iterate(rgui, action); + else if (menu_type == RGUI_SETTINGS_CUSTOM_BIND) + return menu_custom_bind_iterate(rgui, action); + else if (menu_type == RGUI_SETTINGS_CUSTOM_BIND_KEYBOARD) + return menu_custom_bind_iterate_keyboard(rgui, action); + + if (rgui->need_refresh && action != RGUI_ACTION_MESSAGE) + action = RGUI_ACTION_NOOP; + + unsigned scroll_speed = (max(rgui->scroll_accel, 2) - 2) / 4 + 1; + unsigned fast_scroll_speed = 4 + 4 * scroll_speed; + + switch (action) + { + case RGUI_ACTION_UP: + if (rgui->selection_ptr >= scroll_speed) + menu_set_navigation(rgui, rgui->selection_ptr - scroll_speed); + else + menu_set_navigation(rgui, rgui->selection_buf->size - 1); + break; + + case RGUI_ACTION_DOWN: + if (rgui->selection_ptr + scroll_speed < rgui->selection_buf->size) + menu_set_navigation(rgui, rgui->selection_ptr + scroll_speed); + else + menu_clear_navigation(rgui); + break; + + case RGUI_ACTION_LEFT: + if (rgui->selection_ptr > fast_scroll_speed) + menu_set_navigation(rgui, rgui->selection_ptr - fast_scroll_speed); + else + menu_clear_navigation(rgui); + break; + + case RGUI_ACTION_RIGHT: + if (rgui->selection_ptr + fast_scroll_speed < rgui->selection_buf->size) + menu_set_navigation(rgui, rgui->selection_ptr + fast_scroll_speed); + else + menu_set_navigation_last(rgui); + break; + + case RGUI_ACTION_SCROLL_UP: + menu_descend_alphabet(rgui, &rgui->selection_ptr); + break; + case RGUI_ACTION_SCROLL_DOWN: + menu_ascend_alphabet(rgui, &rgui->selection_ptr); + break; + + case RGUI_ACTION_CANCEL: + if (rgui->menu_stack->size > 1) + { + file_list_pop(rgui->menu_stack, &rgui->selection_ptr); + rgui->need_refresh = true; + } + break; + + case RGUI_ACTION_OK: + { + if (rgui->selection_buf->size == 0) + return 0; + + const char *path = 0; + unsigned type = 0; + file_list_get_at_offset(rgui->selection_buf, rgui->selection_ptr, &path, &type); + + if ( + menu_lakka_type_is(type) == RGUI_SETTINGS_SHADER_OPTIONS || + menu_lakka_type_is(type) == RGUI_FILE_DIRECTORY || + type == RGUI_SETTINGS_OVERLAY_PRESET || + type == RGUI_SETTINGS_VIDEO_SOFTFILTER || + type == RGUI_SETTINGS_AUDIO_DSP_FILTER || + type == RGUI_SETTINGS_CORE || + type == RGUI_SETTINGS_CONFIG || + type == RGUI_SETTINGS_DISK_APPEND || + type == RGUI_FILE_DIRECTORY) + { + char cat_path[PATH_MAX]; + fill_pathname_join(cat_path, dir, path, sizeof(cat_path)); + + file_list_push(rgui->menu_stack, cat_path, type, rgui->selection_ptr); + menu_clear_navigation(rgui); + rgui->need_refresh = true; + } + else + { +#ifdef HAVE_SHADER_MANAGER + if (menu_lakka_type_is(menu_type) == RGUI_SETTINGS_SHADER_OPTIONS) + { + if (menu_type == RGUI_SETTINGS_SHADER_PRESET) + { + char shader_path[PATH_MAX]; + fill_pathname_join(shader_path, dir, path, sizeof(shader_path)); + if (driver.menu_ctx && driver.menu_ctx->backend && driver.menu_ctx->backend->shader_manager_set_preset) + driver.menu_ctx->backend->shader_manager_set_preset(&rgui->shader, gfx_shader_parse_type(shader_path, RARCH_SHADER_NONE), + shader_path); + } + else + { + unsigned pass = (menu_type - RGUI_SETTINGS_SHADER_0) / 3; + fill_pathname_join(rgui->shader.pass[pass].source.cg, + dir, path, sizeof(rgui->shader.pass[pass].source.cg)); + } + + // Pop stack until we hit shader manager again. + menu_flush_stack_type(rgui, RGUI_SETTINGS_SHADER_OPTIONS); + } + else +#endif + if (menu_type == RGUI_SETTINGS_DEFERRED_CORE) + { + // FIXME: Add for consoles. + strlcpy(g_settings.libretro, path, sizeof(g_settings.libretro)); + strlcpy(g_extern.fullpath, rgui->deferred_path, sizeof(g_extern.fullpath)); + load_menu_game_new_core(); + rgui->msg_force = true; + ret = -1; + menu_flush_stack_type(rgui, RGUI_SETTINGS); + } + else if (menu_type == RGUI_SETTINGS_CORE) + { +#if defined(HAVE_DYNAMIC) + fill_pathname_join(g_settings.libretro, dir, path, sizeof(g_settings.libretro)); + menu_update_system_info(rgui, &rgui->load_no_rom); + + // No ROM needed for this core, load game immediately. + if (rgui->load_no_rom) + { + g_extern.lifecycle_state |= (1ULL << MODE_LOAD_GAME); + *g_extern.fullpath = '\0'; + rgui->msg_force = true; + ret = -1; + } + + // Core selection on non-console just updates directory listing. + // Will take affect on new ROM load. +#elif defined(RARCH_CONSOLE) + rarch_environment_cb(RETRO_ENVIRONMENT_SET_LIBRETRO_PATH, (void*)path); + +#if defined(GEKKO) && defined(HW_RVL) + fill_pathname_join(g_extern.fullpath, default_paths.core_dir, + SALAMANDER_FILE, sizeof(g_extern.fullpath)); +#else + fill_pathname_join(g_settings.libretro, dir, path, sizeof(g_settings.libretro)); +#endif + g_extern.lifecycle_state &= ~(1ULL << MODE_GAME); + g_extern.lifecycle_state |= (1ULL << MODE_EXITSPAWN); + ret = -1; +#endif + + menu_flush_stack_type(rgui, RGUI_SETTINGS); + } + else if (menu_type == RGUI_SETTINGS_CONFIG) + { + char config[PATH_MAX]; + fill_pathname_join(config, dir, path, sizeof(config)); + menu_flush_stack_type(rgui, RGUI_SETTINGS); + rgui->msg_force = true; + if (menu_replace_config(config)) + { + menu_clear_navigation(rgui); + ret = -1; + } + } +#ifdef HAVE_OVERLAY + else if (menu_type == RGUI_SETTINGS_OVERLAY_PRESET) + { + fill_pathname_join(g_settings.input.overlay, dir, path, sizeof(g_settings.input.overlay)); + + if (driver.overlay) + input_overlay_free(driver.overlay); + driver.overlay = input_overlay_new(g_settings.input.overlay); + if (!driver.overlay) + RARCH_ERR("Failed to load overlay.\n"); + + menu_flush_stack_type(rgui, RGUI_SETTINGS_OPTIONS); + } +#endif + else if (menu_type == RGUI_SETTINGS_DISK_APPEND) + { + char image[PATH_MAX]; + fill_pathname_join(image, dir, path, sizeof(image)); + rarch_disk_control_append_image(image); + + g_extern.lifecycle_state |= 1ULL << MODE_GAME; + + menu_flush_stack_type(rgui, RGUI_SETTINGS); + ret = -1; + } + else if (menu_type == RGUI_SETTINGS_OPEN_HISTORY) + { + load_menu_game_history(rgui->selection_ptr); + menu_flush_stack_type(rgui, RGUI_SETTINGS); + ret = -1; + } + else if (menu_type == RGUI_BROWSER_DIR_PATH) + { + strlcpy(g_settings.rgui_content_directory, dir, sizeof(g_settings.rgui_content_directory)); + menu_flush_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS); + } +#ifdef HAVE_SCREENSHOTS + else if (menu_type == RGUI_SCREENSHOT_DIR_PATH) + { + strlcpy(g_settings.screenshot_directory, dir, sizeof(g_settings.screenshot_directory)); + menu_flush_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS); + } +#endif + else if (menu_type == RGUI_SAVEFILE_DIR_PATH) + { + strlcpy(g_extern.savefile_dir, dir, sizeof(g_extern.savefile_dir)); + menu_flush_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS); + } +#ifdef HAVE_OVERLAY + else if (menu_type == RGUI_OVERLAY_DIR_PATH) + { + strlcpy(g_extern.overlay_dir, dir, sizeof(g_extern.overlay_dir)); + menu_flush_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS); + } +#endif + else if (menu_type == RGUI_SETTINGS_VIDEO_SOFTFILTER) + { + fill_pathname_join(g_settings.video.filter_path, dir, path, sizeof(g_settings.video.filter_path)); +#ifdef HAVE_DYLIB + rarch_set_fullscreen(g_settings.video.fullscreen); +#endif + menu_flush_stack_type(rgui, RGUI_SETTINGS_VIDEO_OPTIONS); + } + else if (menu_type == RGUI_SETTINGS_AUDIO_DSP_FILTER) + { +#ifdef HAVE_DYLIB + fill_pathname_join(g_settings.audio.dsp_plugin, dir, path, sizeof(g_settings.audio.dsp_plugin)); +#endif + rarch_deinit_dsp_filter(); + rarch_init_dsp_filter(); + menu_flush_stack_type(rgui, RGUI_SETTINGS_AUDIO_OPTIONS); + } + else if (menu_type == RGUI_SAVESTATE_DIR_PATH) + { + strlcpy(g_extern.savestate_dir, dir, sizeof(g_extern.savestate_dir)); + menu_flush_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS); + } + else if (menu_type == RGUI_LIBRETRO_DIR_PATH) + { + strlcpy(rgui->libretro_dir, dir, sizeof(rgui->libretro_dir)); + menu_init_core_info(rgui); + menu_flush_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS); + } +#ifdef HAVE_DYNAMIC + else if (menu_type == RGUI_CONFIG_DIR_PATH) + { + strlcpy(g_settings.rgui_config_directory, dir, sizeof(g_settings.rgui_config_directory)); + menu_flush_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS); + } +#endif + else if (menu_type == RGUI_LIBRETRO_INFO_DIR_PATH) + { + strlcpy(g_settings.libretro_info_path, dir, sizeof(g_settings.libretro_info_path)); + menu_init_core_info(rgui); + menu_flush_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS); + } + else if (menu_type == RGUI_SHADER_DIR_PATH) + { + strlcpy(g_settings.video.shader_dir, dir, sizeof(g_settings.video.shader_dir)); + menu_flush_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS); + } + else if (menu_type == RGUI_FILTER_DIR_PATH) + { + strlcpy(g_settings.video.filter_dir, dir, sizeof(g_settings.video.filter_dir)); + menu_flush_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS); + } + else if (menu_type == RGUI_DSP_FILTER_DIR_PATH) + { + strlcpy(g_settings.audio.filter_dir, dir, sizeof(g_settings.audio.filter_dir)); + menu_flush_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS); + } + else if (menu_type == RGUI_SYSTEM_DIR_PATH) + { + strlcpy(g_settings.system_directory, dir, sizeof(g_settings.system_directory)); + menu_flush_stack_type(rgui, RGUI_SETTINGS_PATH_OPTIONS); + } + else + { + if (rgui->defer_core) + { + fill_pathname_join(rgui->deferred_path, dir, path, sizeof(rgui->deferred_path)); + + const core_info_t *info = NULL; + size_t supported = 0; + if (rgui->core_info) + core_info_list_get_supported_cores(rgui->core_info, rgui->deferred_path, &info, &supported); + + if (supported == 1) // Can make a decision right now. + { + strlcpy(g_extern.fullpath, rgui->deferred_path, sizeof(g_extern.fullpath)); + strlcpy(g_settings.libretro, info->path, sizeof(g_settings.libretro)); + +#ifdef HAVE_DYNAMIC + menu_update_system_info(rgui, &rgui->load_no_rom); + g_extern.lifecycle_state |= (1ULL << MODE_LOAD_GAME); +#else + rarch_environment_cb(RETRO_ENVIRONMENT_SET_LIBRETRO_PATH, (void*)g_settings.libretro); + rarch_environment_cb(RETRO_ENVIRONMENT_EXEC, (void*)g_extern.fullpath); +#endif + + menu_flush_stack_type(rgui, RGUI_SETTINGS); + rgui->msg_force = true; + ret = -1; + } + else // Present a selection. + { + file_list_push(rgui->menu_stack, rgui->libretro_dir, RGUI_SETTINGS_DEFERRED_CORE, rgui->selection_ptr); + menu_clear_navigation(rgui); + rgui->need_refresh = true; + } + } + else + { + fill_pathname_join(g_extern.fullpath, dir, path, sizeof(g_extern.fullpath)); + g_extern.lifecycle_state |= (1ULL << MODE_LOAD_GAME); + + menu_flush_stack_type(rgui, RGUI_SETTINGS); + rgui->msg_force = true; + ret = -1; + } + } + } + break; + } + + case RGUI_ACTION_REFRESH: + menu_clear_navigation(rgui); + rgui->need_refresh = true; + break; + + case RGUI_ACTION_MESSAGE: + rgui->msg_force = true; + break; + + default: + break; + } + + + // refresh values in case the stack changed + file_list_get_last(rgui->menu_stack, &dir, &menu_type); + + if (rgui->need_refresh && (menu_type == RGUI_FILE_DIRECTORY || + menu_lakka_type_is(menu_type) == RGUI_SETTINGS_SHADER_OPTIONS || + menu_lakka_type_is(menu_type) == RGUI_FILE_DIRECTORY || + menu_type == RGUI_SETTINGS_OVERLAY_PRESET || + menu_type == RGUI_SETTINGS_VIDEO_SOFTFILTER || + menu_type == RGUI_SETTINGS_AUDIO_DSP_FILTER || + menu_type == RGUI_SETTINGS_DEFERRED_CORE || + menu_type == RGUI_SETTINGS_CORE || + menu_type == RGUI_SETTINGS_CONFIG || + menu_type == RGUI_SETTINGS_OPEN_HISTORY || + menu_type == RGUI_SETTINGS_DISK_APPEND)) + { + rgui->need_refresh = false; + menu_parse_and_resolve(rgui, menu_type); + } + + if (driver.menu_ctx && driver.menu_ctx->iterate) + driver.menu_ctx->iterate(rgui, action); + + if (driver.video_data && driver.menu_ctx && driver.menu_ctx->render) + driver.menu_ctx->render(rgui); + + return ret; +} + +static void menu_lakka_shader_manager_init(void *data) +{ + (void)data; + +#ifdef HAVE_SHADER_MANAGER + rgui_handle_t *rgui = (rgui_handle_t*)data; + memset(&rgui->shader, 0, sizeof(rgui->shader)); + config_file_t *conf = NULL; + + const char *config_path = NULL; + if (*g_extern.core_specific_config_path && g_settings.core_specific_config) + config_path = g_extern.core_specific_config_path; + else if (*g_extern.config_path) + config_path = g_extern.config_path; + + // In a multi-config setting, we can't have conflicts on rgui.cgp/rgui.glslp. + if (config_path) + { + fill_pathname_base(rgui->default_glslp, config_path, sizeof(rgui->default_glslp)); + path_remove_extension(rgui->default_glslp); + strlcat(rgui->default_glslp, ".glslp", sizeof(rgui->default_glslp)); + fill_pathname_base(rgui->default_cgp, config_path, sizeof(rgui->default_cgp)); + path_remove_extension(rgui->default_cgp); + strlcat(rgui->default_cgp, ".cgp", sizeof(rgui->default_cgp)); + } + else + { + strlcpy(rgui->default_glslp, "rgui.glslp", sizeof(rgui->default_glslp)); + strlcpy(rgui->default_cgp, "rgui.cgp", sizeof(rgui->default_cgp)); + } + + char cgp_path[PATH_MAX]; + + const char *ext = path_get_extension(g_settings.video.shader_path); + if (strcmp(ext, "glslp") == 0 || strcmp(ext, "cgp") == 0) + { + conf = config_file_new(g_settings.video.shader_path); + if (conf) + { + if (gfx_shader_read_conf_cgp(conf, &rgui->shader)) + gfx_shader_resolve_relative(&rgui->shader, g_settings.video.shader_path); + config_file_free(conf); + } + } + else if (strcmp(ext, "glsl") == 0 || strcmp(ext, "cg") == 0) + { + strlcpy(rgui->shader.pass[0].source.cg, g_settings.video.shader_path, + sizeof(rgui->shader.pass[0].source.cg)); + rgui->shader.passes = 1; + } + else + { + const char *shader_dir = *g_settings.video.shader_dir ? + g_settings.video.shader_dir : g_settings.system_directory; + + fill_pathname_join(cgp_path, shader_dir, "rgui.glslp", sizeof(cgp_path)); + conf = config_file_new(cgp_path); + + if (!conf) + { + fill_pathname_join(cgp_path, shader_dir, "rgui.cgp", sizeof(cgp_path)); + conf = config_file_new(cgp_path); + } + + if (conf) + { + if (gfx_shader_read_conf_cgp(conf, &rgui->shader)) + gfx_shader_resolve_relative(&rgui->shader, cgp_path); + config_file_free(conf); + } + } +#endif +} + +static void menu_lakka_shader_manager_set_preset(void *data, unsigned type, const char *path) +{ +#ifdef HAVE_SHADER_MANAGER + struct gfx_shader *shader = (struct gfx_shader*)data; + + RARCH_LOG("Setting RGUI shader: %s.\n", path ? path : "N/A (stock)"); + + if (video_set_shader_func((enum rarch_shader_type)type, path)) + { + // Makes sure that we use RGUI CGP shader on driver reinit. + // Only do this when the cgp actually works to avoid potential errors. + strlcpy(g_settings.video.shader_path, path ? path : "", + sizeof(g_settings.video.shader_path)); + g_settings.video.shader_enable = true; + + if (path && shader) + { + // Load stored CGP into RGUI menu on success. + // Used when a preset is directly loaded. + // No point in updating when the CGP was created from RGUI itself. + config_file_t *conf = config_file_new(path); + if (conf) + { + gfx_shader_read_conf_cgp(conf, shader); + gfx_shader_resolve_relative(shader, path); + config_file_free(conf); + } + + rgui->need_refresh = true; + } + } + else + { + RARCH_ERR("Setting RGUI CGP failed.\n"); + g_settings.video.shader_enable = false; + } +#endif +} + +static void menu_lakka_shader_manager_get_str(void *data, char *type_str, size_t type_str_size, unsigned type) +{ + (void)data; + (void)type_str; + (void)type_str_size; + (void)type; + +#ifdef HAVE_SHADER_MANAGER + struct gfx_shader *shader = (struct gfx_shader*)data; + if (type == RGUI_SETTINGS_SHADER_APPLY) + *type_str = '\0'; + else if (type == RGUI_SETTINGS_SHADER_PASSES) + snprintf(type_str, type_str_size, "%u", shader->passes); + else + { + unsigned pass = (type - RGUI_SETTINGS_SHADER_0) / 3; + switch ((type - RGUI_SETTINGS_SHADER_0) % 3) + { + case 0: + if (*shader->pass[pass].source.cg) + fill_pathname_base(type_str, + shader->pass[pass].source.cg, type_str_size); + else + strlcpy(type_str, "N/A", type_str_size); + break; + + case 1: + switch (shader->pass[pass].filter) + { + case RARCH_FILTER_LINEAR: + strlcpy(type_str, "Linear", type_str_size); + break; + + case RARCH_FILTER_NEAREST: + strlcpy(type_str, "Nearest", type_str_size); + break; + + case RARCH_FILTER_UNSPEC: + strlcpy(type_str, "Don't care", type_str_size); + break; + } + break; + + case 2: + { + unsigned scale = shader->pass[pass].fbo.scale_x; + if (!scale) + strlcpy(type_str, "Don't care", type_str_size); + else + snprintf(type_str, type_str_size, "%ux", scale); + break; + } + } + } +#endif +} + +static void menu_lakka_shader_manager_save_preset(void *data, const char *basename, bool apply) +{ +#ifdef HAVE_SHADER_MANAGER + char buffer[PATH_MAX]; + unsigned d, type; + rgui_handle_t *rgui; + + rgui = (rgui_handle_t*)data; + + if (driver.menu_ctx && driver.menu_ctx->backend && driver.menu_ctx->backend->shader_manager_get_type) + type = driver.menu_ctx->backend->shader_manager_get_type(&rgui->shader); + else + type = RARCH_SHADER_NONE; + + if (type == RARCH_SHADER_NONE) + return; + + const char *conf_path = NULL; + + if (basename) + { + strlcpy(buffer, basename, sizeof(buffer)); + // Append extension automatically as appropriate. + if (!strstr(basename, ".cgp") && !strstr(basename, ".glslp")) + { + if (type == RARCH_SHADER_GLSL) + strlcat(buffer, ".glslp", sizeof(buffer)); + else if (type == RARCH_SHADER_CG) + strlcat(buffer, ".cgp", sizeof(buffer)); + } + conf_path = buffer; + } + else + conf_path = type == RARCH_SHADER_GLSL ? rgui->default_glslp : rgui->default_cgp; + + char config_directory[PATH_MAX]; + if (*g_extern.config_path) + fill_pathname_basedir(config_directory, g_extern.config_path, sizeof(config_directory)); + else + *config_directory = '\0'; + + char cgp_path[PATH_MAX]; + const char *dirs[] = { + g_settings.video.shader_dir, + g_settings.rgui_config_directory, + config_directory, + }; + + config_file_t *conf = config_file_new(NULL); + if (!conf) + return; + gfx_shader_write_conf_cgp(conf, &rgui->shader); + + bool ret = false; + + for (d = 0; d < ARRAY_SIZE(dirs); d++) + { + if (!*dirs[d]) + continue; + + fill_pathname_join(cgp_path, dirs[d], conf_path, sizeof(cgp_path)); + if (config_file_write(conf, cgp_path)) + { + RARCH_LOG("Saved shader preset to %s.\n", cgp_path); + if (apply) + { + if (driver.menu_ctx && driver.menu_ctx->backend && driver.menu_ctx->backend->shader_manager_set_preset) + driver.menu_ctx->backend->shader_manager_set_preset(NULL, type, cgp_path); + } + ret = true; + break; + } + else + RARCH_LOG("Failed writing shader preset to %s.\n", cgp_path); + } + + config_file_free(conf); + if (!ret) + RARCH_ERR("Failed to save shader preset. Make sure config directory and/or shader dir are writable.\n"); +#endif +} + +static unsigned menu_lakka_shader_manager_get_type(void *data) +{ + unsigned i, type; + (void)data; + + i = 0; + type = 0; + + (void)i; + +#ifdef HAVE_SHADER_MANAGER + const struct gfx_shader *shader = (const struct gfx_shader*)data; + + // All shader types must be the same, or we cannot use it. + type = RARCH_SHADER_NONE; + + for (i = 0; i < shader->passes; i++) + { + enum rarch_shader_type pass_type = gfx_shader_parse_type(shader->pass[i].source.cg, + RARCH_SHADER_NONE); + + switch (pass_type) + { + case RARCH_SHADER_CG: + case RARCH_SHADER_GLSL: + if (type == RARCH_SHADER_NONE) + type = pass_type; + else if (type != pass_type) + return RARCH_SHADER_NONE; + break; + + default: + return RARCH_SHADER_NONE; + } + } +#endif + + return type; +} + +static int menu_lakka_shader_manager_setting_toggle(void *data, unsigned setting, unsigned action) +{ + (void)data; + (void)setting; + (void)action; + +#ifdef HAVE_SHADER_MANAGER + unsigned dist_shader, dist_filter, dist_scale; + rgui_handle_t *rgui; + + rgui = (rgui_handle_t*)data; + dist_shader = setting - RGUI_SETTINGS_SHADER_0; + dist_filter = setting - RGUI_SETTINGS_SHADER_0_FILTER; + dist_scale = setting - RGUI_SETTINGS_SHADER_0_SCALE; + + if (setting == RGUI_SETTINGS_SHADER_FILTER) + { + switch (action) + { + case RGUI_ACTION_START: + g_settings.video.smooth = true; + break; + + case RGUI_ACTION_LEFT: + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + g_settings.video.smooth = !g_settings.video.smooth; + break; + + default: + break; + } + } + else if ((setting == RGUI_SETTINGS_SHADER_APPLY || setting == RGUI_SETTINGS_SHADER_PASSES) && + (driver.menu_ctx && driver.menu_ctx->backend && driver.menu_ctx->backend->setting_set)) + driver.menu_ctx->backend->setting_set(rgui, setting, action); + else if ((dist_shader % 3) == 0 || setting == RGUI_SETTINGS_SHADER_PRESET) + { + dist_shader /= 3; + struct gfx_shader_pass *pass = setting == RGUI_SETTINGS_SHADER_PRESET ? + &rgui->shader.pass[dist_shader] : NULL; + switch (action) + { + case RGUI_ACTION_OK: + file_list_push(rgui->menu_stack, g_settings.video.shader_dir, setting, rgui->selection_ptr); + menu_clear_navigation(rgui); + rgui->need_refresh = true; + break; + + case RGUI_ACTION_START: + if (pass) + *pass->source.cg = '\0'; + break; + + default: + break; + } + } + else if ((dist_filter % 3) == 0) + { + dist_filter /= 3; + struct gfx_shader_pass *pass = &rgui->shader.pass[dist_filter]; + switch (action) + { + case RGUI_ACTION_START: + rgui->shader.pass[dist_filter].filter = RARCH_FILTER_UNSPEC; + break; + + case RGUI_ACTION_LEFT: + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + { + unsigned delta = action == RGUI_ACTION_LEFT ? 2 : 1; + pass->filter = (enum gfx_filter_type)((pass->filter + delta) % 3); + break; + } + + default: + break; + } + } + else if ((dist_scale % 3) == 0) + { + dist_scale /= 3; + struct gfx_shader_pass *pass = &rgui->shader.pass[dist_scale]; + switch (action) + { + case RGUI_ACTION_START: + pass->fbo.scale_x = pass->fbo.scale_y = 0; + pass->fbo.valid = false; + break; + + case RGUI_ACTION_LEFT: + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + { + unsigned current_scale = pass->fbo.scale_x; + unsigned delta = action == RGUI_ACTION_LEFT ? 5 : 1; + current_scale = (current_scale + delta) % 6; + pass->fbo.valid = current_scale; + pass->fbo.scale_x = pass->fbo.scale_y = current_scale; + break; + } + + default: + break; + } + } +#endif + + return 0; +} + +static int menu_lakka_setting_toggle(void *data, unsigned setting, unsigned action, unsigned menu_type) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; +#ifdef HAVE_SHADER_MANAGER + if ((setting >= RGUI_SETTINGS_SHADER_FILTER) && (setting <= RGUI_SETTINGS_SHADER_LAST)) + { + if (driver.menu_ctx && driver.menu_ctx->backend && driver.menu_ctx->backend->shader_manager_setting_toggle) + return driver.menu_ctx->backend->shader_manager_setting_toggle(rgui, setting, action); + else + return 0; + } +#endif + if ((setting >= RGUI_SETTINGS_CORE_OPTION_START) && + (driver.menu_ctx && driver.menu_ctx->backend && driver.menu_ctx->backend->core_setting_toggle) + ) + return driver.menu_ctx->backend->core_setting_toggle(setting, action); + + if (driver.menu_ctx && driver.menu_ctx->backend && driver.menu_ctx->backend->setting_set) + return driver.menu_ctx->backend->setting_set(rgui, setting, action); + + return 0; +} + +static int menu_lakka_core_setting_toggle(unsigned setting, unsigned action) +{ + unsigned index; + index = setting - RGUI_SETTINGS_CORE_OPTION_START; + + switch (action) + { + case RGUI_ACTION_LEFT: + core_option_prev(g_extern.system.core_options, index); + break; + + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + core_option_next(g_extern.system.core_options, index); + break; + + case RGUI_ACTION_START: + core_option_set_default(g_extern.system.core_options, index); + break; + + default: + break; + } + + return 0; +} + +#ifdef GEKKO +#define MAX_GAMMA_SETTING 2 + +static unsigned rgui_gx_resolutions[GX_RESOLUTIONS_LAST][2] = { + { 512, 192 }, + { 598, 200 }, + { 640, 200 }, + { 384, 224 }, + { 448, 224 }, + { 480, 224 }, + { 512, 224 }, + { 576, 224 }, + { 608, 224 }, + { 640, 224 }, + { 340, 232 }, + { 512, 232 }, + { 512, 236 }, + { 336, 240 }, + { 384, 240 }, + { 512, 240 }, + { 530, 240 }, + { 640, 240 }, + { 512, 384 }, + { 598, 400 }, + { 640, 400 }, + { 384, 448 }, + { 448, 448 }, + { 480, 448 }, + { 512, 448 }, + { 576, 448 }, + { 608, 448 }, + { 640, 448 }, + { 340, 464 }, + { 512, 464 }, + { 512, 472 }, + { 384, 480 }, + { 512, 480 }, + { 530, 480 }, + { 640, 480 }, +}; + +static unsigned rgui_current_gx_resolution = GX_RESOLUTIONS_640_480; +#else +#define MAX_GAMMA_SETTING 1 +#endif + + +#ifdef HAVE_OSK +static bool osk_callback_enter_audio_device(void *data) +{ + if (g_extern.lifecycle_state & (1ULL << MODE_OSK_ENTRY_SUCCESS) + && driver.osk && driver.osk->get_text_buf) + { + RARCH_LOG("OSK - Applying input data.\n"); + char tmp_str[256]; + wchar_t *text_buf = (wchar_t*)driver.osk->get_text_buf(driver.osk_data); + int num = wcstombs(tmp_str, text_buf, sizeof(tmp_str)); + tmp_str[num] = 0; + strlcpy(g_settings.audio.device, tmp_str, sizeof(g_settings.audio.device)); + goto do_exit; + } + else if (g_extern.lifecycle_state & (1ULL << MODE_OSK_ENTRY_FAIL)) + goto do_exit; + + return false; + +do_exit: + g_extern.lifecycle_state &= ~((1ULL << MODE_OSK_ENTRY_SUCCESS) | + (1ULL << MODE_OSK_ENTRY_FAIL)); + return true; +} + +static bool osk_callback_enter_audio_device_init(void *data) +{ + if (!driver.osk) + return false; + + if (driver.osk->write_initial_msg) + driver.osk->write_initial_msg(driver.osk_data, L"192.168.1.1"); + if (driver.osk->write_msg) + driver.osk->write_msg(driver.osk_data, L"Enter Audio Device / IP address for audio driver."); + if (driver.osk->start) + driver.osk->start(driver.osk_data); + + return true; +} + +static bool osk_callback_enter_filename(void *data) +{ + if (!driver.osk) + return false; + + if (g_extern.lifecycle_state & (1ULL << MODE_OSK_ENTRY_SUCCESS)) + { + RARCH_LOG("OSK - Applying input data.\n"); + char tmp_str[256]; + char filepath[PATH_MAX]; + int num = wcstombs(tmp_str, driver.osk->get_text_buf(driver.osk_data), sizeof(tmp_str)); + tmp_str[num] = 0; + + fill_pathname_join(filepath, g_settings.video.shader_dir, tmp_str, sizeof(filepath)); + strlcat(filepath, ".cgp", sizeof(filepath)); + RARCH_LOG("[osk_callback_enter_filename]: filepath is: %s.\n", filepath); + config_file_t *conf = config_file_new(NULL); + if (!conf) + return false; + gfx_shader_write_conf_cgp(conf, &rgui->shader); + config_file_write(conf, filepath); + config_file_free(conf); + goto do_exit; + } + else if (g_extern.lifecycle_state & (1ULL << MODE_OSK_ENTRY_FAIL)) + goto do_exit; + + return false; +do_exit: + g_extern.lifecycle_state &= ~((1ULL << MODE_OSK_ENTRY_SUCCESS) | + (1ULL << MODE_OSK_ENTRY_FAIL)); + return true; +} + +static bool osk_callback_enter_filename_init(void *data) +{ + if (!driver.osk) + return false; + + if (driver.osk->write_initial_msg) + driver.osk->write_initial_msg(driver.osk_data, L"Save Preset"); + if (driver.osk->write_msg) + driver.osk->write_msg(driver.osk_data, L"Enter filename for preset."); + if (driver.osk->start) + driver.osk->start(driver.osk_data); + + return true; +} + +#endif + +#ifndef RARCH_DEFAULT_PORT +#define RARCH_DEFAULT_PORT 55435 +#endif + +static int menu_lakka_setting_set(void *data, unsigned setting, unsigned action) +{ + rgui_handle_t *rgui = (rgui_handle_t*)data; + unsigned port = rgui->current_pad; + + switch (setting) + { + case RGUI_START_SCREEN: + if (action == RGUI_ACTION_OK) + file_list_push(rgui->menu_stack, "", RGUI_START_SCREEN, 0); + break; + case RGUI_SETTINGS_REWIND_ENABLE: + if (action == RGUI_ACTION_OK || + action == RGUI_ACTION_LEFT || + action == RGUI_ACTION_RIGHT) + { + g_settings.rewind_enable = !g_settings.rewind_enable; + if (g_settings.rewind_enable) + rarch_init_rewind(); + else + rarch_deinit_rewind(); + } + else if (action == RGUI_ACTION_START) + { + g_settings.rewind_enable = false; + rarch_deinit_rewind(); + } + break; +#ifdef HAVE_SCREENSHOTS + case RGUI_SETTINGS_GPU_SCREENSHOT: + if (action == RGUI_ACTION_OK || + action == RGUI_ACTION_LEFT || + action == RGUI_ACTION_RIGHT) + g_settings.video.gpu_screenshot = !g_settings.video.gpu_screenshot; + else if (action == RGUI_ACTION_START) + g_settings.video.gpu_screenshot = true; + break; +#endif + case RGUI_SETTINGS_REWIND_GRANULARITY: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_RIGHT) + g_settings.rewind_granularity++; + else if (action == RGUI_ACTION_LEFT) + { + if (g_settings.rewind_granularity > 1) + g_settings.rewind_granularity--; + } + else if (action == RGUI_ACTION_START) + g_settings.rewind_granularity = 1; + break; + case RGUI_SETTINGS_CONFIG_SAVE_ON_EXIT: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_RIGHT + || action == RGUI_ACTION_LEFT) + g_extern.config_save_on_exit = !g_extern.config_save_on_exit; + else if (action == RGUI_ACTION_START) + g_extern.config_save_on_exit = true; + break; + case RGUI_SETTINGS_SAVESTATE_AUTO_SAVE: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_RIGHT + || action == RGUI_ACTION_LEFT) + g_settings.savestate_auto_save = !g_settings.savestate_auto_save; + else if (action == RGUI_ACTION_START) + g_settings.savestate_auto_save = false; + break; + case RGUI_SETTINGS_SAVESTATE_AUTO_LOAD: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_RIGHT + || action == RGUI_ACTION_LEFT) + g_settings.savestate_auto_load = !g_settings.savestate_auto_load; + else if (action == RGUI_ACTION_START) + g_settings.savestate_auto_load = true; + break; + case RGUI_SETTINGS_BLOCK_SRAM_OVERWRITE: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_RIGHT + || action == RGUI_ACTION_LEFT) + g_settings.block_sram_overwrite = !g_settings.block_sram_overwrite; + else if (action == RGUI_ACTION_START) + g_settings.block_sram_overwrite = false; + break; + case RGUI_SETTINGS_PER_CORE_CONFIG: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_RIGHT + || action == RGUI_ACTION_LEFT) + g_settings.core_specific_config = !g_settings.core_specific_config; + else if (action == RGUI_ACTION_START) + g_settings.core_specific_config = default_core_specific_config; + break; +#if defined(HAVE_THREADS) + case RGUI_SETTINGS_SRAM_AUTOSAVE: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_RIGHT) + { + rarch_deinit_autosave(); + g_settings.autosave_interval += 10; + if (g_settings.autosave_interval) + rarch_init_autosave(); + } + else if (action == RGUI_ACTION_LEFT) + { + if (g_settings.autosave_interval) + { + rarch_deinit_autosave(); + g_settings.autosave_interval -= min(10, g_settings.autosave_interval); + if (g_settings.autosave_interval) + rarch_init_autosave(); + } + } + else if (action == RGUI_ACTION_START) + { + rarch_deinit_autosave(); + g_settings.autosave_interval = 0; + } + break; +#endif + case RGUI_SETTINGS_SAVESTATE_SAVE: + case RGUI_SETTINGS_SAVESTATE_LOAD: + if (action == RGUI_ACTION_OK) + { + if (setting == RGUI_SETTINGS_SAVESTATE_SAVE) + rarch_save_state(); + else + { + // Disallow savestate load when we absoluetely cannot change game state. +#ifdef HAVE_BSV_MOVIE + if (g_extern.bsv.movie) + break; +#endif +#ifdef HAVE_NETPLAY + if (g_extern.netplay) + break; +#endif + rarch_load_state(); + } + g_extern.lifecycle_state |= (1ULL << MODE_GAME); + return -1; + } + else if (action == RGUI_ACTION_START) + g_extern.state_slot = 0; + else if (action == RGUI_ACTION_LEFT) + { + // Slot -1 is (auto) slot. + if (g_extern.state_slot >= 0) + g_extern.state_slot--; + } + else if (action == RGUI_ACTION_RIGHT) + g_extern.state_slot++; + break; +#ifdef HAVE_SCREENSHOTS + case RGUI_SETTINGS_SCREENSHOT: + if (action == RGUI_ACTION_OK) + rarch_take_screenshot(); + break; +#endif + case RGUI_SETTINGS_RESTART_GAME: + if (action == RGUI_ACTION_OK) + { + rarch_game_reset(); + g_extern.lifecycle_state |= (1ULL << MODE_GAME); + return -1; + } + break; + case RGUI_SETTINGS_AUDIO_MUTE: + if (action == RGUI_ACTION_START) + g_extern.audio_data.mute = false; + else + g_extern.audio_data.mute = !g_extern.audio_data.mute; + break; + case RGUI_SETTINGS_AUDIO_CONTROL_RATE_DELTA: + if (action == RGUI_ACTION_START) + { + g_settings.audio.rate_control_delta = rate_control_delta; + g_settings.audio.rate_control = rate_control; + } + else if (action == RGUI_ACTION_LEFT) + { + if (g_settings.audio.rate_control_delta > 0.0) + g_settings.audio.rate_control_delta -= 0.001; + + if (g_settings.audio.rate_control_delta < 0.0005) + { + g_settings.audio.rate_control = false; + g_settings.audio.rate_control_delta = 0.0; + } + else + g_settings.audio.rate_control = true; + } + else if (action == RGUI_ACTION_RIGHT) + { + if (g_settings.audio.rate_control_delta < 0.2) + g_settings.audio.rate_control_delta += 0.001; + g_settings.audio.rate_control = true; + } + break; + case RGUI_SETTINGS_AUDIO_VOLUME: + { + float db_delta = 0.0f; + if (action == RGUI_ACTION_START) + { + g_extern.audio_data.volume_db = 0.0f; + g_extern.audio_data.volume_gain = 1.0f; + } + else if (action == RGUI_ACTION_LEFT) + db_delta -= 1.0f; + else if (action == RGUI_ACTION_RIGHT) + db_delta += 1.0f; + + if (db_delta != 0.0f) + { + g_extern.audio_data.volume_db += db_delta; + g_extern.audio_data.volume_db = max(g_extern.audio_data.volume_db, -80.0f); + g_extern.audio_data.volume_db = min(g_extern.audio_data.volume_db, 12.0f); + g_extern.audio_data.volume_gain = db_to_gain(g_extern.audio_data.volume_db); + } + break; + } + case RGUI_SETTINGS_DEBUG_TEXT: + if (action == RGUI_ACTION_START) + g_settings.fps_show = false; + else if (action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + g_settings.fps_show = !g_settings.fps_show; + break; + case RGUI_SETTINGS_DISK_INDEX: + { + const struct retro_disk_control_callback *control = &g_extern.system.disk_control; + + unsigned num_disks = control->get_num_images(); + unsigned current = control->get_image_index(); + + int step = 0; + if (action == RGUI_ACTION_RIGHT || action == RGUI_ACTION_OK) + step = 1; + else if (action == RGUI_ACTION_LEFT) + step = -1; + + if (step) + { + unsigned next_index = (current + num_disks + 1 + step) % (num_disks + 1); + rarch_disk_control_set_eject(true, false); + rarch_disk_control_set_index(next_index); + rarch_disk_control_set_eject(false, false); + } + + break; + } + case RGUI_SETTINGS_RESTART_EMULATOR: + if (action == RGUI_ACTION_OK) + { +#if defined(GEKKO) && defined(HW_RVL) + fill_pathname_join(g_extern.fullpath, default_paths.core_dir, SALAMANDER_FILE, + sizeof(g_extern.fullpath)); +#endif + g_extern.lifecycle_state &= ~(1ULL << MODE_GAME); + g_extern.lifecycle_state |= (1ULL << MODE_EXITSPAWN); + return -1; + } + break; + case RGUI_SETTINGS_RESUME_GAME: + if (action == RGUI_ACTION_OK) + { + g_extern.lifecycle_state |= (1ULL << MODE_GAME); + return -1; + } + break; + case RGUI_SETTINGS_QUIT_RARCH: + if (action == RGUI_ACTION_OK) + { + g_extern.lifecycle_state &= ~(1ULL << MODE_GAME); + return -1; + } + break; + case RGUI_SETTINGS_SAVE_CONFIG: + if (action == RGUI_ACTION_OK) + menu_save_new_config(); + break; +#ifdef HAVE_OVERLAY + case RGUI_SETTINGS_OVERLAY_PRESET: + switch (action) + { + case RGUI_ACTION_OK: + file_list_push(rgui->menu_stack, g_extern.overlay_dir, setting, rgui->selection_ptr); + menu_clear_navigation(rgui); + rgui->need_refresh = true; + break; + +#ifndef __QNX__ // FIXME: Why ifndef QNX? + case RGUI_ACTION_START: + if (driver.overlay) + input_overlay_free(driver.overlay); + driver.overlay = NULL; + *g_settings.input.overlay = '\0'; + break; +#endif + + default: + break; + } + break; +#endif + case RGUI_SETTINGS_VIDEO_SOFTFILTER: + switch (action) + { +#ifdef HAVE_FILTERS_BUILTIN + case RGUI_ACTION_LEFT: + if (g_settings.video.filter_idx > 0) + g_settings.video.filter_idx--; + break; + case RGUI_ACTION_RIGHT: + if ((g_settings.video.filter_idx + 1) != softfilter_get_last_idx()) + g_settings.video.filter_idx++; + break; +#endif + case RGUI_ACTION_OK: +#if defined(HAVE_FILTERS_BUILTIN) + rarch_set_fullscreen(g_settings.video.fullscreen); +#elif defined(HAVE_DYLIB) + file_list_push(rgui->menu_stack, g_settings.video.filter_dir, setting, rgui->selection_ptr); + menu_clear_navigation(rgui); +#endif + rgui->need_refresh = true; + break; + case RGUI_ACTION_START: +#if defined(HAVE_FILTERS_BUILTIN) + g_settings.video.filter_idx = 0; +#else + strlcpy(g_settings.video.filter_path, "", sizeof(g_settings.video.filter_path)); +#endif + rarch_set_fullscreen(g_settings.video.fullscreen); + break; + } + break; + case RGUI_SETTINGS_AUDIO_DSP_FILTER: + switch (action) + { +#ifdef HAVE_FILTERS_BUILTIN + case RGUI_ACTION_LEFT: + if (g_settings.audio.filter_idx > 0) + g_settings.audio.filter_idx--; + break; + case RGUI_ACTION_RIGHT: + if ((g_settings.audio.filter_idx + 1) != dspfilter_get_last_idx()) + g_settings.audio.filter_idx++; + break; +#endif + case RGUI_ACTION_OK: +#if defined(HAVE_FILTERS_BUILTIN) + rarch_deinit_dsp_filter(); + rarch_init_dsp_filter(); +#elif defined(HAVE_DYLIB) + file_list_push(rgui->menu_stack, g_settings.audio.filter_dir, setting, rgui->selection_ptr); + menu_clear_navigation(rgui); +#endif + rgui->need_refresh = true; + break; + case RGUI_ACTION_START: +#if defined(HAVE_FILTERS_BUILTIN) + g_settings.audio.filter_idx = 0; +#elif defined(HAVE_DYLIB) + strlcpy(g_settings.audio.dsp_plugin, "", sizeof(g_settings.audio.dsp_plugin)); +#endif + rarch_deinit_dsp_filter(); + rarch_init_dsp_filter(); + break; + } + break; +#ifdef HAVE_OVERLAY + case RGUI_SETTINGS_OVERLAY_OPACITY: + { + bool changed = true; + switch (action) + { + case RGUI_ACTION_LEFT: + g_settings.input.overlay_opacity -= 0.01f; + + if (g_settings.input.overlay_opacity < 0.0f) + g_settings.input.overlay_opacity = 0.0f; + break; + + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + g_settings.input.overlay_opacity += 0.01f; + + if (g_settings.input.overlay_opacity > 1.0f) + g_settings.input.overlay_opacity = 1.0f; + break; + + case RGUI_ACTION_START: + g_settings.input.overlay_opacity = 0.7f; + break; + + default: + changed = false; + break; + } + + if (changed && driver.overlay) + input_overlay_set_alpha_mod(driver.overlay, + g_settings.input.overlay_opacity); + break; + } + + case RGUI_SETTINGS_OVERLAY_SCALE: + { + bool changed = true; + switch (action) + { + case RGUI_ACTION_LEFT: + g_settings.input.overlay_scale -= 0.01f; + + if (g_settings.input.overlay_scale < 0.01f) // Avoid potential divide by zero. + g_settings.input.overlay_scale = 0.01f; + break; + + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + g_settings.input.overlay_scale += 0.01f; + + if (g_settings.input.overlay_scale > 2.0f) + g_settings.input.overlay_scale = 2.0f; + break; + + case RGUI_ACTION_START: + g_settings.input.overlay_scale = 1.0f; + break; + + default: + changed = false; + break; + } + + if (changed && driver.overlay) + input_overlay_set_scale_factor(driver.overlay, + g_settings.input.overlay_scale); + break; + } +#endif + // controllers + case RGUI_SETTINGS_BIND_PLAYER: + if (action == RGUI_ACTION_START) + rgui->current_pad = 0; + else if (action == RGUI_ACTION_LEFT) + { + if (rgui->current_pad != 0) + rgui->current_pad--; + } + else if (action == RGUI_ACTION_RIGHT) + { + if (rgui->current_pad < MAX_PLAYERS - 1) + rgui->current_pad++; + } +#ifdef HAVE_RGUI + if (port != rgui->current_pad) + rgui->need_refresh = true; +#endif + port = rgui->current_pad; + break; + case RGUI_SETTINGS_BIND_DEVICE: + // If set_keybinds is supported, we do it more fancy, and scroll through + // a list of supported devices directly. + if (driver.input->set_keybinds && driver.input->devices_size) + { + unsigned device_last = driver.input->devices_size(driver.input_data); + g_settings.input.device[port] += device_last; + if (action == RGUI_ACTION_START) + g_settings.input.device[port] = 0; + else if (action == RGUI_ACTION_LEFT) + g_settings.input.device[port]--; + else if (action == RGUI_ACTION_RIGHT) + g_settings.input.device[port]++; + + // device_last can be 0, avoid modulo. + if (g_settings.input.device[port] >= device_last) + g_settings.input.device[port] -= device_last; + // needs to be checked twice, in case we go right past the end of the list + if (g_settings.input.device[port] >= device_last) + g_settings.input.device[port] -= device_last; + + unsigned keybind_action = (1ULL << KEYBINDS_ACTION_SET_DEFAULT_BINDS); + + driver.input->set_keybinds(driver.input_data, g_settings.input.device[port], port, 0, + keybind_action); + } + else + { + // When only straight g_settings.input.joypad_map[] style + // mapping is supported. + int *p = &g_settings.input.joypad_map[port]; + if (action == RGUI_ACTION_START) + *p = port; + else if (action == RGUI_ACTION_LEFT) + (*p)--; + else if (action == RGUI_ACTION_RIGHT) + (*p)++; + + if (*p < -1) + *p = -1; + else if (*p >= MAX_PLAYERS) + *p = MAX_PLAYERS - 1; + } + break; + case RGUI_SETTINGS_BIND_ANALOG_MODE: + switch (action) + { + case RGUI_ACTION_START: + g_settings.input.analog_dpad_mode[port] = 0; + break; + + case RGUI_ACTION_OK: + case RGUI_ACTION_RIGHT: + g_settings.input.analog_dpad_mode[port] = (g_settings.input.analog_dpad_mode[port] + 1) % ANALOG_DPAD_LAST; + break; + + case RGUI_ACTION_LEFT: + g_settings.input.analog_dpad_mode[port] = (g_settings.input.analog_dpad_mode[port] + ANALOG_DPAD_LAST - 1) % ANALOG_DPAD_LAST; + break; + + default: + break; + } + break; + case RGUI_SETTINGS_INPUT_AXIS_THRESHOLD: + switch (action) + { + case RGUI_ACTION_START: + g_settings.input.axis_threshold = 0.5; + break; + + case RGUI_ACTION_OK: + case RGUI_ACTION_RIGHT: + g_settings.input.axis_threshold += 0.01; + break; + case RGUI_ACTION_LEFT: + g_settings.input.axis_threshold -= 0.01; + break; + + default: + break; + } + g_settings.input.axis_threshold = max(min(g_settings.input.axis_threshold, 0.95f), 0.05f); + break; + case RGUI_SETTINGS_BIND_DEVICE_TYPE: + { + unsigned current_device, current_index, i; + unsigned types = 0; + unsigned devices[128]; + + devices[types++] = RETRO_DEVICE_NONE; + devices[types++] = RETRO_DEVICE_JOYPAD; + // Only push RETRO_DEVICE_ANALOG as default if we use an older core which doesn't use SET_CONTROLLER_INFO. + if (!g_extern.system.num_ports) + devices[types++] = RETRO_DEVICE_ANALOG; + + const struct retro_controller_info *desc = port < g_extern.system.num_ports ? &g_extern.system.ports[port] : NULL; + if (desc) + { + for (i = 0; i < desc->num_types; i++) + { + unsigned id = desc->types[i].id; + if (types < ARRAY_SIZE(devices) && id != RETRO_DEVICE_NONE && id != RETRO_DEVICE_JOYPAD) + devices[types++] = id; + } + } + + current_device = g_settings.input.libretro_device[port]; + current_index = 0; + for (i = 0; i < types; i++) + { + if (current_device == devices[i]) + { + current_index = i; + break; + } + } + + bool updated = true; + switch (action) + { + case RGUI_ACTION_START: + current_device = RETRO_DEVICE_JOYPAD; + break; + + case RGUI_ACTION_LEFT: + current_device = devices[(current_index + types - 1) % types]; + break; + + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + current_device = devices[(current_index + 1) % types]; + break; + + default: + updated = false; + } + + if (updated) + { + g_settings.input.libretro_device[port] = current_device; + pretro_set_controller_port_device(port, current_device); + } + + break; + } + case RGUI_SETTINGS_DEVICE_AUTODETECT_ENABLE: + if (action == RGUI_ACTION_OK) + g_settings.input.autodetect_enable = !g_settings.input.autodetect_enable; + break; + case RGUI_SETTINGS_CUSTOM_BIND_MODE: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + rgui->bind_mode_keyboard = !rgui->bind_mode_keyboard; + break; + case RGUI_SETTINGS_CUSTOM_BIND_ALL: + if (action == RGUI_ACTION_OK) + { + if (rgui->bind_mode_keyboard) + { + rgui->binds.target = &g_settings.input.binds[port][0]; + rgui->binds.begin = RGUI_SETTINGS_BIND_BEGIN; + rgui->binds.last = RGUI_SETTINGS_BIND_LAST; + file_list_push(rgui->menu_stack, "", RGUI_SETTINGS_CUSTOM_BIND_KEYBOARD, rgui->selection_ptr); + rgui->binds.timeout_end = rarch_get_time_usec() + RGUI_KEYBOARD_BIND_TIMEOUT_SECONDS * 1000000; + input_keyboard_wait_keys(rgui, menu_custom_bind_keyboard_cb); + } + else + { + rgui->binds.target = &g_settings.input.binds[port][0]; + rgui->binds.begin = RGUI_SETTINGS_BIND_BEGIN; + rgui->binds.last = RGUI_SETTINGS_BIND_LAST; + file_list_push(rgui->menu_stack, "", RGUI_SETTINGS_CUSTOM_BIND, rgui->selection_ptr); + menu_poll_bind_get_rested_axes(&rgui->binds); + menu_poll_bind_state(&rgui->binds); + } + } + break; + case RGUI_SETTINGS_CUSTOM_BIND_DEFAULT_ALL: + if (action == RGUI_ACTION_OK) + { + unsigned i; + struct retro_keybind *target = &g_settings.input.binds[port][0]; + const struct retro_keybind *def_binds = port ? retro_keybinds_rest : retro_keybinds_1; + rgui->binds.begin = RGUI_SETTINGS_BIND_BEGIN; + rgui->binds.last = RGUI_SETTINGS_BIND_LAST; + for (i = RGUI_SETTINGS_BIND_BEGIN; i <= RGUI_SETTINGS_BIND_LAST; i++, target++) + { + if (rgui->bind_mode_keyboard) + target->key = def_binds[i - RGUI_SETTINGS_BIND_BEGIN].key; + else + { + target->joykey = NO_BTN; + target->joyaxis = AXIS_NONE; + } + } + } + break; + case RGUI_SETTINGS_BIND_UP: + case RGUI_SETTINGS_BIND_DOWN: + case RGUI_SETTINGS_BIND_LEFT: + case RGUI_SETTINGS_BIND_RIGHT: + case RGUI_SETTINGS_BIND_A: + case RGUI_SETTINGS_BIND_B: + case RGUI_SETTINGS_BIND_X: + case RGUI_SETTINGS_BIND_Y: + case RGUI_SETTINGS_BIND_START: + case RGUI_SETTINGS_BIND_SELECT: + case RGUI_SETTINGS_BIND_L: + case RGUI_SETTINGS_BIND_R: + case RGUI_SETTINGS_BIND_L2: + case RGUI_SETTINGS_BIND_R2: + case RGUI_SETTINGS_BIND_L3: + case RGUI_SETTINGS_BIND_R3: + case RGUI_SETTINGS_BIND_TURBO_ENABLE: + case RGUI_SETTINGS_BIND_ANALOG_LEFT_X_PLUS: + case RGUI_SETTINGS_BIND_ANALOG_LEFT_X_MINUS: + case RGUI_SETTINGS_BIND_ANALOG_LEFT_Y_PLUS: + case RGUI_SETTINGS_BIND_ANALOG_LEFT_Y_MINUS: + case RGUI_SETTINGS_BIND_ANALOG_RIGHT_X_PLUS: + case RGUI_SETTINGS_BIND_ANALOG_RIGHT_X_MINUS: + case RGUI_SETTINGS_BIND_ANALOG_RIGHT_Y_PLUS: + case RGUI_SETTINGS_BIND_ANALOG_RIGHT_Y_MINUS: + case RGUI_SETTINGS_BIND_FAST_FORWARD_KEY: + case RGUI_SETTINGS_BIND_FAST_FORWARD_HOLD_KEY: + case RGUI_SETTINGS_BIND_LOAD_STATE_KEY: + case RGUI_SETTINGS_BIND_SAVE_STATE_KEY: + case RGUI_SETTINGS_BIND_FULLSCREEN_TOGGLE_KEY: + case RGUI_SETTINGS_BIND_QUIT_KEY: + case RGUI_SETTINGS_BIND_STATE_SLOT_PLUS: + case RGUI_SETTINGS_BIND_STATE_SLOT_MINUS: + case RGUI_SETTINGS_BIND_REWIND: + case RGUI_SETTINGS_BIND_MOVIE_RECORD_TOGGLE: + case RGUI_SETTINGS_BIND_PAUSE_TOGGLE: + case RGUI_SETTINGS_BIND_FRAMEADVANCE: + case RGUI_SETTINGS_BIND_RESET: + case RGUI_SETTINGS_BIND_SHADER_NEXT: + case RGUI_SETTINGS_BIND_SHADER_PREV: + case RGUI_SETTINGS_BIND_CHEAT_INDEX_PLUS: + case RGUI_SETTINGS_BIND_CHEAT_INDEX_MINUS: + case RGUI_SETTINGS_BIND_CHEAT_TOGGLE: + case RGUI_SETTINGS_BIND_SCREENSHOT: + case RGUI_SETTINGS_BIND_DSP_CONFIG: + case RGUI_SETTINGS_BIND_MUTE: + case RGUI_SETTINGS_BIND_NETPLAY_FLIP: + case RGUI_SETTINGS_BIND_SLOWMOTION: + case RGUI_SETTINGS_BIND_ENABLE_HOTKEY: + case RGUI_SETTINGS_BIND_VOLUME_UP: + case RGUI_SETTINGS_BIND_VOLUME_DOWN: + case RGUI_SETTINGS_BIND_OVERLAY_NEXT: + case RGUI_SETTINGS_BIND_DISK_EJECT_TOGGLE: + case RGUI_SETTINGS_BIND_DISK_NEXT: + case RGUI_SETTINGS_BIND_GRAB_MOUSE_TOGGLE: + case RGUI_SETTINGS_BIND_MENU_TOGGLE: + if (driver.input->set_keybinds && !driver.input->get_joypad_driver) + { + unsigned keybind_action = KEYBINDS_ACTION_NONE; + + if (action == RGUI_ACTION_START) + keybind_action = (1ULL << KEYBINDS_ACTION_SET_DEFAULT_BIND); + + // FIXME: The array indices here look totally wrong ... Fixed it so it looks kind of sane for now. + if (keybind_action != KEYBINDS_ACTION_NONE) + driver.input->set_keybinds(driver.input_data, g_settings.input.device[port], port, + setting - RGUI_SETTINGS_BIND_BEGIN, keybind_action); + } + else + { + struct retro_keybind *bind = &g_settings.input.binds[port][setting - RGUI_SETTINGS_BIND_BEGIN]; + if (action == RGUI_ACTION_OK) + { + rgui->binds.begin = setting; + rgui->binds.last = setting; + rgui->binds.target = bind; + rgui->binds.player = port; + file_list_push(rgui->menu_stack, "", + rgui->bind_mode_keyboard ? RGUI_SETTINGS_CUSTOM_BIND_KEYBOARD : RGUI_SETTINGS_CUSTOM_BIND, rgui->selection_ptr); + + if (rgui->bind_mode_keyboard) + { + rgui->binds.timeout_end = rarch_get_time_usec() + RGUI_KEYBOARD_BIND_TIMEOUT_SECONDS * 1000000; + input_keyboard_wait_keys(rgui, menu_custom_bind_keyboard_cb); + } + else + { + menu_poll_bind_get_rested_axes(&rgui->binds); + menu_poll_bind_state(&rgui->binds); + } + } + else if (action == RGUI_ACTION_START) + { + if (rgui->bind_mode_keyboard) + { + const struct retro_keybind *def_binds = port ? retro_keybinds_rest : retro_keybinds_1; + bind->key = def_binds[setting - RGUI_SETTINGS_BIND_BEGIN].key; + } + else + { + bind->joykey = NO_BTN; + bind->joyaxis = AXIS_NONE; + } + } + } + break; + case RGUI_BROWSER_DIR_PATH: + if (action == RGUI_ACTION_START) + *g_settings.rgui_content_directory = '\0'; + break; +#ifdef HAVE_SCREENSHOTS + case RGUI_SCREENSHOT_DIR_PATH: + if (action == RGUI_ACTION_START) + *g_settings.screenshot_directory = '\0'; + break; +#endif + case RGUI_SAVEFILE_DIR_PATH: + if (action == RGUI_ACTION_START) + *g_extern.savefile_dir = '\0'; + break; +#ifdef HAVE_OVERLAY + case RGUI_OVERLAY_DIR_PATH: + if (action == RGUI_ACTION_START) + *g_extern.overlay_dir = '\0'; + break; +#endif + case RGUI_SAVESTATE_DIR_PATH: + if (action == RGUI_ACTION_START) + *g_extern.savestate_dir = '\0'; + break; + case RGUI_LIBRETRO_DIR_PATH: + if (action == RGUI_ACTION_START) + { + *rgui->libretro_dir = '\0'; + menu_init_core_info(rgui); + } + break; + case RGUI_LIBRETRO_INFO_DIR_PATH: + if (action == RGUI_ACTION_START) + { + *g_settings.libretro_info_path = '\0'; + menu_init_core_info(rgui); + } + break; + case RGUI_CONFIG_DIR_PATH: + if (action == RGUI_ACTION_START) + *g_settings.rgui_config_directory = '\0'; + break; + case RGUI_FILTER_DIR_PATH: + if (action == RGUI_ACTION_START) + *g_settings.video.filter_dir = '\0'; + break; + case RGUI_DSP_FILTER_DIR_PATH: + if (action == RGUI_ACTION_START) + *g_settings.audio.filter_dir = '\0'; + break; + case RGUI_SHADER_DIR_PATH: + if (action == RGUI_ACTION_START) + *g_settings.video.shader_dir = '\0'; + break; + case RGUI_SYSTEM_DIR_PATH: + if (action == RGUI_ACTION_START) + *g_settings.system_directory = '\0'; + break; + case RGUI_SETTINGS_VIDEO_ROTATION: + if (action == RGUI_ACTION_START) + { + g_settings.video.rotation = ORIENTATION_NORMAL; + video_set_rotation_func((g_settings.video.rotation + g_extern.system.rotation) % 4); + } + else if (action == RGUI_ACTION_LEFT) + { + if (g_settings.video.rotation > 0) + g_settings.video.rotation--; + video_set_rotation_func((g_settings.video.rotation + g_extern.system.rotation) % 4); + } + else if (action == RGUI_ACTION_RIGHT) + { + if (g_settings.video.rotation < LAST_ORIENTATION) + g_settings.video.rotation++; + video_set_rotation_func((g_settings.video.rotation + g_extern.system.rotation) % 4); + } + break; + + case RGUI_SETTINGS_VIDEO_FILTER: + if (action == RGUI_ACTION_START) + g_settings.video.smooth = video_smooth; + else + g_settings.video.smooth = !g_settings.video.smooth; + + if (driver.video_data && driver.video_poke && driver.video_poke->set_filtering) + driver.video_poke->set_filtering(driver.video_data, 1, g_settings.video.smooth); + break; + + case RGUI_SETTINGS_DRIVER_VIDEO: + if (action == RGUI_ACTION_LEFT) + find_prev_video_driver(); + else if (action == RGUI_ACTION_RIGHT) + find_next_video_driver(); + break; + case RGUI_SETTINGS_DRIVER_AUDIO: + if (action == RGUI_ACTION_LEFT) + find_prev_audio_driver(); + else if (action == RGUI_ACTION_RIGHT) + find_next_audio_driver(); + break; + case RGUI_SETTINGS_DRIVER_AUDIO_DEVICE: + if (action == RGUI_ACTION_OK) + { +#ifdef HAVE_OSK + if (g_settings.osk.enable) + { + g_extern.osk.cb_init = osk_callback_enter_audio_device_init; + g_extern.osk.cb_callback = osk_callback_enter_audio_device; + } + else +#endif + menu_key_start_line(rgui, "Audio Device Name / IP: ", audio_device_callback); + } + else if (action == RGUI_ACTION_START) + *g_settings.audio.device = '\0'; + break; + case RGUI_SETTINGS_DRIVER_AUDIO_RESAMPLER: + if (action == RGUI_ACTION_LEFT) + find_prev_resampler_driver(); + else if (action == RGUI_ACTION_RIGHT) + find_next_resampler_driver(); + break; + case RGUI_SETTINGS_DRIVER_INPUT: + if (action == RGUI_ACTION_LEFT) + find_prev_input_driver(); + else if (action == RGUI_ACTION_RIGHT) + find_next_input_driver(); + break; +#ifdef HAVE_CAMERA + case RGUI_SETTINGS_DRIVER_CAMERA: + if (action == RGUI_ACTION_LEFT) + find_prev_camera_driver(); + else if (action == RGUI_ACTION_RIGHT) + find_next_camera_driver(); + break; +#endif +#ifdef HAVE_LOCATION + case RGUI_SETTINGS_DRIVER_LOCATION: + if (action == RGUI_ACTION_LEFT) + find_prev_location_driver(); + else if (action == RGUI_ACTION_RIGHT) + find_next_location_driver(); + break; +#endif +#ifdef HAVE_MENU + case RGUI_SETTINGS_DRIVER_MENU: + if (action == RGUI_ACTION_LEFT) + find_prev_menu_driver(); + else if (action == RGUI_ACTION_RIGHT) + find_next_menu_driver(); + break; +#endif + case RGUI_SETTINGS_VIDEO_GAMMA: + if (action == RGUI_ACTION_START) + { + g_extern.console.screen.gamma_correction = 0; + if (driver.video_data && driver.video_poke && driver.video_poke->apply_state_changes) + driver.video_poke->apply_state_changes(driver.video_data); + } + else if (action == RGUI_ACTION_LEFT) + { + if (g_extern.console.screen.gamma_correction > 0) + { + g_extern.console.screen.gamma_correction--; + if (driver.video_data && driver.video_poke && driver.video_poke->apply_state_changes) + driver.video_poke->apply_state_changes(driver.video_data); + } + } + else if (action == RGUI_ACTION_RIGHT) + { + if (g_extern.console.screen.gamma_correction < MAX_GAMMA_SETTING) + { + g_extern.console.screen.gamma_correction++; + if (driver.video_data && driver.video_poke && driver.video_poke->apply_state_changes) + driver.video_poke->apply_state_changes(driver.video_data); + } + } + break; + + case RGUI_SETTINGS_VIDEO_INTEGER_SCALE: + if (action == RGUI_ACTION_START) + g_settings.video.scale_integer = scale_integer; + else if (action == RGUI_ACTION_LEFT || + action == RGUI_ACTION_RIGHT || + action == RGUI_ACTION_OK) + g_settings.video.scale_integer = !g_settings.video.scale_integer; + + if (driver.video_data && driver.video_poke && driver.video_poke->apply_state_changes) + driver.video_poke->apply_state_changes(driver.video_data); + break; + + case RGUI_SETTINGS_VIDEO_ASPECT_RATIO: + if (action == RGUI_ACTION_START) + g_settings.video.aspect_ratio_idx = aspect_ratio_idx; + else if (action == RGUI_ACTION_LEFT) + { + if (g_settings.video.aspect_ratio_idx > 0) + g_settings.video.aspect_ratio_idx--; + } + else if (action == RGUI_ACTION_RIGHT) + { + if (g_settings.video.aspect_ratio_idx < LAST_ASPECT_RATIO) + g_settings.video.aspect_ratio_idx++; + } + + if (driver.video_data && driver.video_poke && driver.video_poke->set_aspect_ratio) + driver.video_poke->set_aspect_ratio(driver.video_data, g_settings.video.aspect_ratio_idx); + break; + + case RGUI_SETTINGS_TOGGLE_FULLSCREEN: + if (action == RGUI_ACTION_OK) + rarch_set_fullscreen(!g_settings.video.fullscreen); + break; + +#if defined(GEKKO) + case RGUI_SETTINGS_VIDEO_RESOLUTION: + if (action == RGUI_ACTION_LEFT) + { + if (rgui_current_gx_resolution > 0) + { + rgui_current_gx_resolution--; + if (driver.video_data) + gx_set_video_mode(driver.video_data, rgui_gx_resolutions[rgui_current_gx_resolution][0], rgui_gx_resolutions[rgui_current_gx_resolution][1]); + } + } + else if (action == RGUI_ACTION_RIGHT) + { + if (rgui_current_gx_resolution < GX_RESOLUTIONS_LAST - 1) + { +#ifdef HW_RVL + if ((rgui_current_gx_resolution + 1) > GX_RESOLUTIONS_640_480) + if (CONF_GetVideo() != CONF_VIDEO_PAL) + return 0; +#endif + + rgui_current_gx_resolution++; + if (driver.video_data) + gx_set_video_mode(driver.video_data, rgui_gx_resolutions[rgui_current_gx_resolution][0], + rgui_gx_resolutions[rgui_current_gx_resolution][1]); + } + } + break; +#elif defined(__CELLOS_LV2__) + case RGUI_SETTINGS_VIDEO_RESOLUTION: + if (action == RGUI_ACTION_LEFT) + { + if (g_extern.console.screen.resolutions.current.idx) + { + g_extern.console.screen.resolutions.current.idx--; + g_extern.console.screen.resolutions.current.id = + g_extern.console.screen.resolutions.list[g_extern.console.screen.resolutions.current.idx]; + } + } + else if (action == RGUI_ACTION_RIGHT) + { + if (g_extern.console.screen.resolutions.current.idx + 1 < + g_extern.console.screen.resolutions.count) + { + g_extern.console.screen.resolutions.current.idx++; + g_extern.console.screen.resolutions.current.id = + g_extern.console.screen.resolutions.list[g_extern.console.screen.resolutions.current.idx]; + } + } + else if (action == RGUI_ACTION_OK) + { + if (g_extern.console.screen.resolutions.list[g_extern.console.screen.resolutions.current.idx] == CELL_VIDEO_OUT_RESOLUTION_576) + { + if (g_extern.console.screen.pal_enable) + g_extern.lifecycle_state |= (1ULL<< MODE_VIDEO_PAL_ENABLE); + } + else + { + g_extern.lifecycle_state &= ~(1ULL << MODE_VIDEO_PAL_ENABLE); + g_extern.lifecycle_state &= ~(1ULL << MODE_VIDEO_PAL_TEMPORAL_ENABLE); + } + + if (driver.video && driver.video->restart) + driver.video->restart(); + if (driver.menu_ctx && driver.menu_ctx->free_assets) + driver.menu_ctx->free_assets(rgui); + if (driver.menu_ctx && driver.menu_ctx->init_assets) + driver.menu_ctx->init_assets(rgui); + } + break; + case RGUI_SETTINGS_VIDEO_PAL60: + switch (action) + { + case RGUI_ACTION_LEFT: + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + if (g_extern.lifecycle_state & (1ULL << MODE_VIDEO_PAL_ENABLE)) + { + if (g_extern.lifecycle_state & (1ULL << MODE_VIDEO_PAL_TEMPORAL_ENABLE)) + g_extern.lifecycle_state &= ~(1ULL << MODE_VIDEO_PAL_TEMPORAL_ENABLE); + else + g_extern.lifecycle_state |= (1ULL << MODE_VIDEO_PAL_TEMPORAL_ENABLE); + + if (driver.video && driver.video->restart) + driver.video->restart(); + if (driver.menu_ctx && driver.menu_ctx->free_assets) + driver.menu_ctx->free_assets(rgui); + if (driver.menu_ctx && driver.menu_ctx->init_assets) + driver.menu_ctx->init_assets(rgui); + } + break; + case RGUI_ACTION_START: + if (g_extern.lifecycle_state & (1ULL << MODE_VIDEO_PAL_ENABLE)) + { + g_extern.lifecycle_state &= ~(1ULL << MODE_VIDEO_PAL_TEMPORAL_ENABLE); + + if (driver.video && driver.video->restart) + driver.video->restart(); + if (driver.menu_ctx && driver.menu_ctx->free_assets) + driver.menu_ctx->free_assets(rgui); + if (driver.menu_ctx && driver.menu_ctx->init_assets) + driver.menu_ctx->init_assets(rgui); + } + break; + } + break; +#endif +#ifdef HW_RVL + case RGUI_SETTINGS_VIDEO_SOFT_FILTER: + if (g_extern.lifecycle_state & (1ULL << MODE_VIDEO_SOFT_FILTER_ENABLE)) + g_extern.lifecycle_state &= ~(1ULL << MODE_VIDEO_SOFT_FILTER_ENABLE); + else + g_extern.lifecycle_state |= (1ULL << MODE_VIDEO_SOFT_FILTER_ENABLE); + + if (driver.video_data && driver.video_poke && driver.video_poke->apply_state_changes) + driver.video_poke->apply_state_changes(driver.video_data); + break; +#endif + + case RGUI_SETTINGS_VIDEO_VSYNC: + switch (action) + { + case RGUI_ACTION_START: + g_settings.video.vsync = true; + break; + + case RGUI_ACTION_LEFT: + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + g_settings.video.vsync = !g_settings.video.vsync; + break; + + default: + break; + } + break; + + case RGUI_SETTINGS_VIDEO_HARD_SYNC: + switch (action) + { + case RGUI_ACTION_START: + g_settings.video.hard_sync = false; + break; + + case RGUI_ACTION_LEFT: + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + g_settings.video.hard_sync = !g_settings.video.hard_sync; + break; + + default: + break; + } + break; + + case RGUI_SETTINGS_VIDEO_BLACK_FRAME_INSERTION: + switch (action) + { + case RGUI_ACTION_START: + g_settings.video.black_frame_insertion = false; + break; + + case RGUI_ACTION_LEFT: + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + g_settings.video.black_frame_insertion = !g_settings.video.black_frame_insertion; + break; + + default: + break; + } + break; + + case RGUI_SETTINGS_VIDEO_CROP_OVERSCAN: + switch (action) + { + case RGUI_ACTION_START: + g_settings.video.crop_overscan = true; + break; + + case RGUI_ACTION_LEFT: + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + g_settings.video.crop_overscan = !g_settings.video.crop_overscan; + break; + + default: + break; + } + break; + + case RGUI_SETTINGS_VIDEO_WINDOW_SCALE_X: + case RGUI_SETTINGS_VIDEO_WINDOW_SCALE_Y: + { + float *scale = setting == RGUI_SETTINGS_VIDEO_WINDOW_SCALE_X ? &g_settings.video.xscale : &g_settings.video.yscale; + float old_scale = *scale; + + switch (action) + { + case RGUI_ACTION_START: + *scale = 3.0f; + break; + + case RGUI_ACTION_LEFT: + *scale -= 1.0f; + break; + + case RGUI_ACTION_RIGHT: + *scale += 1.0f; + break; + + default: + break; + } + + *scale = roundf(*scale); + *scale = max(*scale, 1.0f); + + if (old_scale != *scale && !g_settings.video.fullscreen) + rarch_set_fullscreen(g_settings.video.fullscreen); // Reinit video driver. + + break; + } + +#ifdef HAVE_THREADS + case RGUI_SETTINGS_VIDEO_THREADED: + { + bool old = g_settings.video.threaded; + if (action == RGUI_ACTION_OK || + action == RGUI_ACTION_LEFT || + action == RGUI_ACTION_RIGHT) + g_settings.video.threaded = !g_settings.video.threaded; + else if (action == RGUI_ACTION_START) + g_settings.video.threaded = false; + + if (g_settings.video.threaded != old) + rarch_set_fullscreen(g_settings.video.fullscreen); // Reinit video driver. + break; + } +#endif + + case RGUI_SETTINGS_VIDEO_SWAP_INTERVAL: + { + unsigned old = g_settings.video.swap_interval; + switch (action) + { + case RGUI_ACTION_START: + g_settings.video.swap_interval = 1; + break; + + case RGUI_ACTION_LEFT: + g_settings.video.swap_interval--; + break; + + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + g_settings.video.swap_interval++; + break; + + default: + break; + } + + g_settings.video.swap_interval = min(g_settings.video.swap_interval, 4); + g_settings.video.swap_interval = max(g_settings.video.swap_interval, 1); + if (old != g_settings.video.swap_interval && driver.video && driver.video_data) + video_set_nonblock_state_func(false); // This will update the current swap interval. Since we're in RGUI now, always apply VSync. + + break; + } + + case RGUI_SETTINGS_VIDEO_HARD_SYNC_FRAMES: + switch (action) + { + case RGUI_ACTION_START: + g_settings.video.hard_sync_frames = 0; + break; + + case RGUI_ACTION_LEFT: + if (g_settings.video.hard_sync_frames > 0) + g_settings.video.hard_sync_frames--; + break; + + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + if (g_settings.video.hard_sync_frames < 3) + g_settings.video.hard_sync_frames++; + break; + + default: + break; + } + break; + + case RGUI_SETTINGS_VIDEO_MONITOR_INDEX: + switch (action) + { + case RGUI_ACTION_START: + g_settings.video.monitor_index = 0; + rarch_set_fullscreen(g_settings.video.fullscreen); + break; + + case RGUI_ACTION_OK: + case RGUI_ACTION_RIGHT: + g_settings.video.monitor_index++; + rarch_set_fullscreen(g_settings.video.fullscreen); + break; + + case RGUI_ACTION_LEFT: + if (g_settings.video.monitor_index) + { + g_settings.video.monitor_index--; + rarch_set_fullscreen(g_settings.video.fullscreen); + } + break; + + default: + break; + } + break; + + case RGUI_SETTINGS_VIDEO_REFRESH_RATE_AUTO: + switch (action) + { + case RGUI_ACTION_START: + g_extern.measure_data.frame_time_samples_count = 0; + break; + + case RGUI_ACTION_OK: + { + double refresh_rate = 0.0; + double deviation = 0.0; + unsigned sample_points = 0; + if (driver_monitor_fps_statistics(&refresh_rate, &deviation, &sample_points)) + { + driver_set_monitor_refresh_rate(refresh_rate); + // Incase refresh rate update forced non-block video. + video_set_nonblock_state_func(false); + } + break; + } + + default: + break; + } + break; +#ifdef HAVE_SHADER_MANAGER + case RGUI_SETTINGS_SHADER_PASSES: + switch (action) + { + case RGUI_ACTION_START: + rgui->shader.passes = 0; + break; + + case RGUI_ACTION_LEFT: + if (rgui->shader.passes) + { + rgui->shader.passes--; + rgui->need_refresh = true; + } + break; + + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + if (rgui->shader.passes < GFX_MAX_SHADERS) + { + rgui->shader.passes++; + rgui->need_refresh = true; + } + break; + + default: + break; + } + +#ifndef HAVE_RMENU + rgui->need_refresh = true; +#endif + break; + case RGUI_SETTINGS_SHADER_APPLY: + { + unsigned type = RARCH_SHADER_NONE; + + if (!driver.video || !driver.video->set_shader || action != RGUI_ACTION_OK) + return 0; + + RARCH_LOG("Applying shader ...\n"); + + if (driver.menu_ctx && driver.menu_ctx->backend && driver.menu_ctx->backend->shader_manager_get_type) + type = driver.menu_ctx->backend->shader_manager_get_type(&rgui->shader); + + if (rgui->shader.passes && type != RARCH_SHADER_NONE + && driver.menu_ctx && driver.menu_ctx->backend && + driver.menu_ctx->backend->shader_manager_save_preset) + driver.menu_ctx->backend->shader_manager_save_preset(rgui, NULL, true); + else + { + type = gfx_shader_parse_type("", DEFAULT_SHADER_TYPE); + if (type == RARCH_SHADER_NONE) + { +#if defined(HAVE_GLSL) + type = RARCH_SHADER_GLSL; +#elif defined(HAVE_CG) || defined(HAVE_HLSL) + type = RARCH_SHADER_CG; +#endif + } + if (driver.menu_ctx && driver.menu_ctx->backend && driver.menu_ctx->backend->shader_manager_set_preset) + driver.menu_ctx->backend->shader_manager_set_preset(NULL, type, NULL); + } + break; + } + case RGUI_SETTINGS_SHADER_PRESET_SAVE: + if (action == RGUI_ACTION_OK) + { +#ifdef HAVE_OSK + if (g_settings.osk.enable) + { + g_extern.osk.cb_init = osk_callback_enter_filename_init; + g_extern.osk.cb_callback = osk_callback_enter_filename; + } + else +#endif + menu_key_start_line(rgui, "Preset Filename: ", preset_filename_callback); + } + break; +#endif +#ifdef _XBOX1 + case RGUI_SETTINGS_FLICKER_FILTER: + switch (action) + { + case RGUI_ACTION_LEFT: + if (g_extern.console.screen.flicker_filter_index > 0) + g_extern.console.screen.flicker_filter_index--; + break; + case RGUI_ACTION_RIGHT: + if (g_extern.console.screen.flicker_filter_index < 5) + g_extern.console.screen.flicker_filter_index++; + break; + case RGUI_ACTION_START: + g_extern.console.screen.flicker_filter_index = 0; + break; + } + break; + case RGUI_SETTINGS_SOFT_DISPLAY_FILTER: + switch (action) + { + case RGUI_ACTION_LEFT: + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + if (g_extern.lifecycle_state & (1ULL << MODE_VIDEO_SOFT_FILTER_ENABLE)) + g_extern.lifecycle_state &= ~(1ULL << MODE_VIDEO_SOFT_FILTER_ENABLE); + else + g_extern.lifecycle_state |= (1ULL << MODE_VIDEO_SOFT_FILTER_ENABLE); + break; + case RGUI_ACTION_START: + g_extern.lifecycle_state |= (1ULL << MODE_VIDEO_SOFT_FILTER_ENABLE); + break; + } + break; +#endif + case RGUI_SETTINGS_CUSTOM_BGM_CONTROL_ENABLE: + switch (action) + { + case RGUI_ACTION_OK: +#if (CELL_SDK_VERSION > 0x340000) + if (g_extern.lifecycle_state & (1ULL << MODE_AUDIO_CUSTOM_BGM_ENABLE)) + g_extern.lifecycle_state &= ~(1ULL << MODE_AUDIO_CUSTOM_BGM_ENABLE); + else + g_extern.lifecycle_state |= (1ULL << MODE_AUDIO_CUSTOM_BGM_ENABLE); + if (g_extern.lifecycle_state & (1ULL << MODE_AUDIO_CUSTOM_BGM_ENABLE)) + cellSysutilEnableBgmPlayback(); + else + cellSysutilDisableBgmPlayback(); + +#endif + break; + case RGUI_ACTION_START: +#if (CELL_SDK_VERSION > 0x340000) + g_extern.lifecycle_state |= (1ULL << MODE_AUDIO_CUSTOM_BGM_ENABLE); +#endif + break; + } + break; + case RGUI_SETTINGS_PAUSE_IF_WINDOW_FOCUS_LOST: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + g_settings.pause_nonactive = !g_settings.pause_nonactive; + else if (action == RGUI_ACTION_START) + g_settings.pause_nonactive = false; + break; + case RGUI_SETTINGS_WINDOW_COMPOSITING_ENABLE: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + { + g_settings.video.disable_composition = !g_settings.video.disable_composition; + rarch_set_fullscreen(g_settings.video.fullscreen); + } + else if (action == RGUI_ACTION_START) + { + g_settings.video.disable_composition = false; + rarch_set_fullscreen(g_settings.video.fullscreen); + } + break; +#ifdef HAVE_NETPLAY + case RGUI_SETTINGS_NETPLAY_ENABLE: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + { + g_extern.netplay_enable = !g_extern.netplay_enable; + /* TODO/FIXME - toggle netplay on/off */ + } + else if (action == RGUI_ACTION_START) + { + g_extern.netplay_enable = false; + /* TODO/FIXME - toggle netplay on/off */ + } + break; + case RGUI_SETTINGS_NETPLAY_HOST_IP_ADDRESS: + if (action == RGUI_ACTION_OK) + menu_key_start_line(rgui, "IP Address: ", netplay_ipaddress_callback); + else if (action == RGUI_ACTION_START) + *g_extern.netplay_server = '\0'; + break; + case RGUI_SETTINGS_NETPLAY_DELAY_FRAMES: + if (action == RGUI_ACTION_LEFT) + { + if (g_extern.netplay_sync_frames != 0) + g_extern.netplay_sync_frames--; + } + else if (action == RGUI_ACTION_RIGHT) + g_extern.netplay_sync_frames++; + else if (action == RGUI_ACTION_START) + g_extern.netplay_sync_frames = 0; + break; + case RGUI_SETTINGS_NETPLAY_TCP_UDP_PORT: + if (action == RGUI_ACTION_OK) + menu_key_start_line(rgui, "TCP/UDP Port: ", netplay_port_callback); + else if (action == RGUI_ACTION_START) + g_extern.netplay_port = RARCH_DEFAULT_PORT; + break; + case RGUI_SETTINGS_NETPLAY_NICKNAME: + if (action == RGUI_ACTION_OK) + menu_key_start_line(rgui, "Nickname: ", netplay_nickname_callback); + else if (action == RGUI_ACTION_START) + *g_extern.netplay_nick = '\0'; + break; + case RGUI_SETTINGS_NETPLAY_MODE: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + g_extern.netplay_is_client = !g_extern.netplay_is_client; + else if (action == RGUI_ACTION_START) + g_extern.netplay_is_client = false; + break; + case RGUI_SETTINGS_NETPLAY_SPECTATOR_MODE_ENABLE: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + g_extern.netplay_is_spectate = !g_extern.netplay_is_spectate; + else if (action == RGUI_ACTION_START) + g_extern.netplay_is_spectate = false; + break; +#endif +#ifdef HAVE_OSK + case RGUI_SETTINGS_ONSCREEN_KEYBOARD_ENABLE: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + g_settings.osk.enable = !g_settings.osk.enable; + else if (action == RGUI_ACTION_START) + g_settings.osk.enable = false; + break; +#endif +#ifdef HAVE_CAMERA + case RGUI_SETTINGS_PRIVACY_CAMERA_ALLOW: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + g_settings.camera.allow = !g_settings.camera.allow; + else if (action == RGUI_ACTION_START) + g_settings.camera.allow = false; + break; +#endif +#ifdef HAVE_LOCATION + case RGUI_SETTINGS_PRIVACY_LOCATION_ALLOW: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + g_settings.location.allow = !g_settings.location.allow; + else if (action == RGUI_ACTION_START) + g_settings.location.allow = false; + break; +#endif + case RGUI_SETTINGS_FONT_ENABLE: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + g_settings.video.font_enable = !g_settings.video.font_enable; + else if (action == RGUI_ACTION_START) + g_settings.video.font_enable = true; + break; + case RGUI_SETTINGS_FONT_SCALE: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + g_settings.video.font_scale = !g_settings.video.font_scale; + else if (action == RGUI_ACTION_START) + g_settings.video.font_scale = true; + break; + case RGUI_SETTINGS_FONT_SIZE: + if (action == RGUI_ACTION_LEFT) + g_settings.video.font_size -= 1.0f; + else if (action == RGUI_ACTION_RIGHT) + g_settings.video.font_size += 1.0f; + else if (action == RGUI_ACTION_START) + g_settings.video.font_size = font_size; + g_settings.video.font_size = roundf(max(g_settings.video.font_size, 1.0f)); + break; + case RGUI_SETTINGS_LOAD_DUMMY_ON_CORE_SHUTDOWN: + if (action == RGUI_ACTION_OK || action == RGUI_ACTION_LEFT || action == RGUI_ACTION_RIGHT) + g_settings.load_dummy_on_core_shutdown = !g_settings.load_dummy_on_core_shutdown; + else if (action == RGUI_ACTION_START) + g_settings.load_dummy_on_core_shutdown = load_dummy_on_core_shutdown; + break; + default: + break; + } + + return 0; +} + +static void menu_lakka_setting_set_label(char *type_str, size_t type_str_size, unsigned *w, unsigned type) +{ + switch (type) + { + case RGUI_SETTINGS_VIDEO_ROTATION: + strlcpy(type_str, rotation_lut[g_settings.video.rotation], + type_str_size); + break; + case RGUI_SETTINGS_VIDEO_SOFT_FILTER: + snprintf(type_str, type_str_size, + (g_extern.lifecycle_state & (1ULL << MODE_VIDEO_SOFT_FILTER_ENABLE)) ? "ON" : "OFF"); + break; + case RGUI_SETTINGS_VIDEO_FILTER: + if (g_settings.video.smooth) + strlcpy(type_str, "Bilinear filtering", type_str_size); + else + strlcpy(type_str, "Point filtering", type_str_size); + break; + case RGUI_SETTINGS_VIDEO_GAMMA: + snprintf(type_str, type_str_size, "%d", g_extern.console.screen.gamma_correction); + break; + case RGUI_SETTINGS_VIDEO_VSYNC: + strlcpy(type_str, g_settings.video.vsync ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_VIDEO_HARD_SYNC: + strlcpy(type_str, g_settings.video.hard_sync ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_VIDEO_BLACK_FRAME_INSERTION: + strlcpy(type_str, g_settings.video.black_frame_insertion ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_VIDEO_SWAP_INTERVAL: + snprintf(type_str, type_str_size, "%u", g_settings.video.swap_interval); + break; + case RGUI_SETTINGS_VIDEO_THREADED: + strlcpy(type_str, g_settings.video.threaded ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_VIDEO_WINDOW_SCALE_X: + snprintf(type_str, type_str_size, "%.1fx", g_settings.video.xscale); + break; + case RGUI_SETTINGS_VIDEO_WINDOW_SCALE_Y: + snprintf(type_str, type_str_size, "%.1fx", g_settings.video.yscale); + break; + case RGUI_SETTINGS_VIDEO_CROP_OVERSCAN: + strlcpy(type_str, g_settings.video.crop_overscan ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_VIDEO_HARD_SYNC_FRAMES: + snprintf(type_str, type_str_size, "%u", g_settings.video.hard_sync_frames); + break; + case RGUI_SETTINGS_DRIVER_VIDEO: + strlcpy(type_str, g_settings.video.driver, type_str_size); + break; + case RGUI_SETTINGS_DRIVER_AUDIO: + strlcpy(type_str, g_settings.audio.driver, type_str_size); + break; + case RGUI_SETTINGS_DRIVER_AUDIO_DEVICE: + strlcpy(type_str, g_settings.audio.device, type_str_size); + break; + case RGUI_SETTINGS_DRIVER_AUDIO_RESAMPLER: + strlcpy(type_str, g_settings.audio.resampler, type_str_size); + break; + case RGUI_SETTINGS_DRIVER_INPUT: + strlcpy(type_str, g_settings.input.driver, type_str_size); + break; +#ifdef HAVE_CAMERA + case RGUI_SETTINGS_DRIVER_CAMERA: + strlcpy(type_str, g_settings.camera.driver, type_str_size); + break; +#endif +#ifdef HAVE_LOCATION + case RGUI_SETTINGS_DRIVER_LOCATION: + strlcpy(type_str, g_settings.location.driver, type_str_size); + break; +#endif +#ifdef HAVE_MENU + case RGUI_SETTINGS_DRIVER_MENU: + strlcpy(type_str, g_settings.menu.driver, type_str_size); + break; +#endif + case RGUI_SETTINGS_VIDEO_MONITOR_INDEX: + if (g_settings.video.monitor_index) + snprintf(type_str, type_str_size, "%u", g_settings.video.monitor_index); + else + strlcpy(type_str, "0 (Auto)", type_str_size); + break; + case RGUI_SETTINGS_VIDEO_REFRESH_RATE_AUTO: + { + double refresh_rate = 0.0; + double deviation = 0.0; + unsigned sample_points = 0; + if (driver_monitor_fps_statistics(&refresh_rate, &deviation, &sample_points)) + snprintf(type_str, type_str_size, "%.3f Hz (%.1f%% dev, %u samples)", refresh_rate, 100.0 * deviation, sample_points); + else + strlcpy(type_str, "N/A", type_str_size); + break; + } + case RGUI_SETTINGS_VIDEO_INTEGER_SCALE: + strlcpy(type_str, g_settings.video.scale_integer ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_VIDEO_ASPECT_RATIO: + strlcpy(type_str, aspectratio_lut[g_settings.video.aspect_ratio_idx].name, type_str_size); + break; +#if defined(GEKKO) + case RGUI_SETTINGS_VIDEO_RESOLUTION: + strlcpy(type_str, gx_get_video_mode(), type_str_size); + break; +#elif defined(__CELLOS_LV2__) + case RGUI_SETTINGS_VIDEO_RESOLUTION: + { + unsigned width = gfx_ctx_get_resolution_width(g_extern.console.screen.resolutions.list[g_extern.console.screen.resolutions.current.idx]); + unsigned height = gfx_ctx_get_resolution_height(g_extern.console.screen.resolutions.list[g_extern.console.screen.resolutions.current.idx]); + snprintf(type_str, type_str_size, "%dx%d", width, height); + } + break; + case RGUI_SETTINGS_VIDEO_PAL60: + if (g_extern.lifecycle_state & (1ULL << MODE_VIDEO_PAL_TEMPORAL_ENABLE)) + strlcpy(type_str, "ON", type_str_size); + else + strlcpy(type_str, "OFF", type_str_size); + break; +#endif + case RGUI_FILE_PLAIN: + strlcpy(type_str, "(FILE)", type_str_size); + *w = 6; + break; + case RGUI_FILE_DIRECTORY: + strlcpy(type_str, "(DIR)", type_str_size); + *w = 5; + break; + case RGUI_SETTINGS_REWIND_ENABLE: + strlcpy(type_str, g_settings.rewind_enable ? "ON" : "OFF", type_str_size); + break; +#ifdef HAVE_SCREENSHOTS + case RGUI_SETTINGS_GPU_SCREENSHOT: + strlcpy(type_str, g_settings.video.gpu_screenshot ? "ON" : "OFF", type_str_size); + break; +#endif + case RGUI_SETTINGS_REWIND_GRANULARITY: + snprintf(type_str, type_str_size, "%u", g_settings.rewind_granularity); + break; + case RGUI_SETTINGS_CONFIG_SAVE_ON_EXIT: + strlcpy(type_str, g_extern.config_save_on_exit ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_SAVESTATE_AUTO_SAVE: + strlcpy(type_str, g_settings.savestate_auto_save ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_SAVESTATE_AUTO_LOAD: + strlcpy(type_str, g_settings.savestate_auto_load ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_BLOCK_SRAM_OVERWRITE: + strlcpy(type_str, g_settings.block_sram_overwrite ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_PER_CORE_CONFIG: + strlcpy(type_str, g_settings.core_specific_config ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_SRAM_AUTOSAVE: + if (g_settings.autosave_interval) + snprintf(type_str, type_str_size, "%u seconds", g_settings.autosave_interval); + else + strlcpy(type_str, "OFF", type_str_size); + break; + case RGUI_SETTINGS_SAVESTATE_SAVE: + case RGUI_SETTINGS_SAVESTATE_LOAD: + if (g_extern.state_slot < 0) + strlcpy(type_str, "-1 (auto)", type_str_size); + else + snprintf(type_str, type_str_size, "%d", g_extern.state_slot); + break; + case RGUI_SETTINGS_AUDIO_MUTE: + strlcpy(type_str, g_extern.audio_data.mute ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_AUDIO_CONTROL_RATE_DELTA: + snprintf(type_str, type_str_size, "%.3f", g_settings.audio.rate_control_delta); + break; + case RGUI_SETTINGS_DEBUG_TEXT: + snprintf(type_str, type_str_size, (g_settings.fps_show) ? "ON" : "OFF"); + break; + case RGUI_BROWSER_DIR_PATH: + strlcpy(type_str, *g_settings.rgui_content_directory ? g_settings.rgui_content_directory : "", type_str_size); + break; +#ifdef HAVE_SCREENSHOTS + case RGUI_SCREENSHOT_DIR_PATH: + strlcpy(type_str, *g_settings.screenshot_directory ? g_settings.screenshot_directory : "", type_str_size); + break; +#endif + case RGUI_SAVEFILE_DIR_PATH: + strlcpy(type_str, *g_extern.savefile_dir ? g_extern.savefile_dir : "", type_str_size); + break; +#ifdef HAVE_OVERLAY + case RGUI_OVERLAY_DIR_PATH: + strlcpy(type_str, *g_extern.overlay_dir ? g_extern.overlay_dir : "", type_str_size); + break; +#endif + case RGUI_SAVESTATE_DIR_PATH: + strlcpy(type_str, *g_extern.savestate_dir ? g_extern.savestate_dir : "", type_str_size); + break; + case RGUI_LIBRETRO_DIR_PATH: + strlcpy(type_str, *rgui->libretro_dir ? rgui->libretro_dir : "", type_str_size); + break; + case RGUI_LIBRETRO_INFO_DIR_PATH: + strlcpy(type_str, *g_settings.libretro_info_path ? g_settings.libretro_info_path : "", type_str_size); + break; + case RGUI_CONFIG_DIR_PATH: + strlcpy(type_str, *g_settings.rgui_config_directory ? g_settings.rgui_config_directory : "", type_str_size); + break; + case RGUI_FILTER_DIR_PATH: + strlcpy(type_str, *g_settings.video.filter_dir ? g_settings.video.filter_dir : "", type_str_size); + break; + case RGUI_DSP_FILTER_DIR_PATH: + strlcpy(type_str, *g_settings.audio.filter_dir ? g_settings.audio.filter_dir : "", type_str_size); + break; + case RGUI_SHADER_DIR_PATH: + strlcpy(type_str, *g_settings.video.shader_dir ? g_settings.video.shader_dir : "", type_str_size); + break; + case RGUI_SYSTEM_DIR_PATH: + strlcpy(type_str, *g_settings.system_directory ? g_settings.system_directory : "", type_str_size); + break; + case RGUI_SETTINGS_DISK_INDEX: + { + const struct retro_disk_control_callback *control = &g_extern.system.disk_control; + unsigned images = control->get_num_images(); + unsigned current = control->get_image_index(); + if (current >= images) + strlcpy(type_str, "No Disk", type_str_size); + else + snprintf(type_str, type_str_size, "%u", current + 1); + break; + } + case RGUI_SETTINGS_CONFIG: + if (*g_extern.config_path) + fill_pathname_base(type_str, g_extern.config_path, type_str_size); + else + strlcpy(type_str, "", type_str_size); + break; + case RGUI_SETTINGS_OPEN_FILEBROWSER: + case RGUI_SETTINGS_OPEN_FILEBROWSER_DEFERRED_CORE: + case RGUI_SETTINGS_OPEN_HISTORY: + case RGUI_SETTINGS_CORE_OPTIONS: + case RGUI_SETTINGS_CORE_INFO: + case RGUI_SETTINGS_CUSTOM_VIEWPORT: + case RGUI_SETTINGS_TOGGLE_FULLSCREEN: + case RGUI_SETTINGS_VIDEO_OPTIONS: + case RGUI_SETTINGS_FONT_OPTIONS: + case RGUI_SETTINGS_AUDIO_OPTIONS: + case RGUI_SETTINGS_DISK_OPTIONS: +#ifdef HAVE_SHADER_MANAGER + case RGUI_SETTINGS_SHADER_OPTIONS: + case RGUI_SETTINGS_SHADER_PRESET: +#endif + case RGUI_SETTINGS_GENERAL_OPTIONS: + case RGUI_SETTINGS_SHADER_PRESET_SAVE: + case RGUI_SETTINGS_CORE: + case RGUI_SETTINGS_DISK_APPEND: + case RGUI_SETTINGS_INPUT_OPTIONS: + case RGUI_SETTINGS_PATH_OPTIONS: + case RGUI_SETTINGS_OVERLAY_OPTIONS: + case RGUI_SETTINGS_NETPLAY_OPTIONS: + case RGUI_SETTINGS_PRIVACY_OPTIONS: + case RGUI_SETTINGS_OPTIONS: + case RGUI_SETTINGS_DRIVERS: + case RGUI_SETTINGS_CUSTOM_BIND_ALL: + case RGUI_SETTINGS_CUSTOM_BIND_DEFAULT_ALL: + strlcpy(type_str, "...", type_str_size); + break; + case RGUI_SETTINGS_VIDEO_SOFTFILTER: + { + const char *filter_name = rarch_softfilter_get_name(g_extern.filter.filter); + strlcpy(type_str, filter_name ? filter_name : "N/A", type_str_size); + } + break; + case RGUI_SETTINGS_AUDIO_DSP_FILTER: + { + const char *filter_name = rarch_dspfilter_get_name((void*)g_extern.audio_data.dsp_plugin); + strlcpy(type_str, filter_name ? filter_name : "N/A", type_str_size); + } + break; +#ifdef HAVE_OVERLAY + case RGUI_SETTINGS_OVERLAY_PRESET: + strlcpy(type_str, path_basename(g_settings.input.overlay), type_str_size); + break; + case RGUI_SETTINGS_OVERLAY_OPACITY: + snprintf(type_str, type_str_size, "%.2f", g_settings.input.overlay_opacity); + break; + case RGUI_SETTINGS_OVERLAY_SCALE: + snprintf(type_str, type_str_size, "%.2f", g_settings.input.overlay_scale); + break; +#endif + case RGUI_SETTINGS_BIND_PLAYER: + snprintf(type_str, type_str_size, "#%d", rgui->current_pad + 1); + break; + case RGUI_SETTINGS_BIND_DEVICE: + { + int map = g_settings.input.joypad_map[rgui->current_pad]; + if (map >= 0 && map < MAX_PLAYERS) + { + const char *device_name = g_settings.input.device_names[map]; + if (*device_name) + strlcpy(type_str, device_name, type_str_size); + else + snprintf(type_str, type_str_size, "N/A (port #%u)", map); + } + else + strlcpy(type_str, "Disabled", type_str_size); + } + break; + case RGUI_SETTINGS_BIND_ANALOG_MODE: + { + static const char *modes[] = { + "None", + "Left Analog", + "Right Analog", + "Dual Analog", + }; + + strlcpy(type_str, modes[g_settings.input.analog_dpad_mode[rgui->current_pad] % ANALOG_DPAD_LAST], type_str_size); + } + break; + case RGUI_SETTINGS_INPUT_AXIS_THRESHOLD: + snprintf(type_str, type_str_size, "%.3f", g_settings.input.axis_threshold); + break; + case RGUI_SETTINGS_BIND_DEVICE_TYPE: + { + const struct retro_controller_description *desc; + desc = NULL; + if (rgui->current_pad < g_extern.system.num_ports) + { + desc = libretro_find_controller_description(&g_extern.system.ports[rgui->current_pad], + g_settings.input.libretro_device[rgui->current_pad]); + } + + const char *name = desc ? desc->desc : NULL; + if (!name) // Find generic name. + { + switch (g_settings.input.libretro_device[rgui->current_pad]) + { + case RETRO_DEVICE_NONE: + name = "None"; + break; + case RETRO_DEVICE_JOYPAD: + name = "Joypad"; + break; + case RETRO_DEVICE_ANALOG: + name = "Joypad w/ Analog"; + break; + default: + name = "Unknown"; + break; + } + } + + strlcpy(type_str, name, type_str_size); + } + break; + case RGUI_SETTINGS_DEVICE_AUTODETECT_ENABLE: + strlcpy(type_str, g_settings.input.autodetect_enable ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_CUSTOM_BIND_MODE: + strlcpy(type_str, rgui->bind_mode_keyboard ? "Keyboard" : "Joypad", type_str_size); + break; + case RGUI_SETTINGS_BIND_UP: + case RGUI_SETTINGS_BIND_DOWN: + case RGUI_SETTINGS_BIND_LEFT: + case RGUI_SETTINGS_BIND_RIGHT: + case RGUI_SETTINGS_BIND_A: + case RGUI_SETTINGS_BIND_B: + case RGUI_SETTINGS_BIND_X: + case RGUI_SETTINGS_BIND_Y: + case RGUI_SETTINGS_BIND_START: + case RGUI_SETTINGS_BIND_SELECT: + case RGUI_SETTINGS_BIND_L: + case RGUI_SETTINGS_BIND_R: + case RGUI_SETTINGS_BIND_L2: + case RGUI_SETTINGS_BIND_R2: + case RGUI_SETTINGS_BIND_L3: + case RGUI_SETTINGS_BIND_R3: + case RGUI_SETTINGS_BIND_TURBO_ENABLE: + case RGUI_SETTINGS_BIND_ANALOG_LEFT_X_PLUS: + case RGUI_SETTINGS_BIND_ANALOG_LEFT_X_MINUS: + case RGUI_SETTINGS_BIND_ANALOG_LEFT_Y_PLUS: + case RGUI_SETTINGS_BIND_ANALOG_LEFT_Y_MINUS: + case RGUI_SETTINGS_BIND_ANALOG_RIGHT_X_PLUS: + case RGUI_SETTINGS_BIND_ANALOG_RIGHT_X_MINUS: + case RGUI_SETTINGS_BIND_ANALOG_RIGHT_Y_PLUS: + case RGUI_SETTINGS_BIND_ANALOG_RIGHT_Y_MINUS: + case RGUI_SETTINGS_BIND_FAST_FORWARD_KEY: + case RGUI_SETTINGS_BIND_FAST_FORWARD_HOLD_KEY: + case RGUI_SETTINGS_BIND_LOAD_STATE_KEY: + case RGUI_SETTINGS_BIND_SAVE_STATE_KEY: + case RGUI_SETTINGS_BIND_FULLSCREEN_TOGGLE_KEY: + case RGUI_SETTINGS_BIND_QUIT_KEY: + case RGUI_SETTINGS_BIND_STATE_SLOT_PLUS: + case RGUI_SETTINGS_BIND_STATE_SLOT_MINUS: + case RGUI_SETTINGS_BIND_REWIND: + case RGUI_SETTINGS_BIND_MOVIE_RECORD_TOGGLE: + case RGUI_SETTINGS_BIND_PAUSE_TOGGLE: + case RGUI_SETTINGS_BIND_FRAMEADVANCE: + case RGUI_SETTINGS_BIND_RESET: + case RGUI_SETTINGS_BIND_SHADER_NEXT: + case RGUI_SETTINGS_BIND_SHADER_PREV: + case RGUI_SETTINGS_BIND_CHEAT_INDEX_PLUS: + case RGUI_SETTINGS_BIND_CHEAT_INDEX_MINUS: + case RGUI_SETTINGS_BIND_CHEAT_TOGGLE: + case RGUI_SETTINGS_BIND_SCREENSHOT: + case RGUI_SETTINGS_BIND_DSP_CONFIG: + case RGUI_SETTINGS_BIND_MUTE: + case RGUI_SETTINGS_BIND_NETPLAY_FLIP: + case RGUI_SETTINGS_BIND_SLOWMOTION: + case RGUI_SETTINGS_BIND_ENABLE_HOTKEY: + case RGUI_SETTINGS_BIND_VOLUME_UP: + case RGUI_SETTINGS_BIND_VOLUME_DOWN: + case RGUI_SETTINGS_BIND_OVERLAY_NEXT: + case RGUI_SETTINGS_BIND_DISK_EJECT_TOGGLE: + case RGUI_SETTINGS_BIND_DISK_NEXT: + case RGUI_SETTINGS_BIND_GRAB_MOUSE_TOGGLE: + case RGUI_SETTINGS_BIND_MENU_TOGGLE: + input_get_bind_string(type_str, &g_settings.input.binds[rgui->current_pad][type - RGUI_SETTINGS_BIND_BEGIN], type_str_size); + break; + case RGUI_SETTINGS_AUDIO_DSP_EFFECT: +#ifdef RARCH_CONSOLE + strlcpy(type_str, (g_extern.console.sound.volume_level) ? "Loud" : "Normal", type_str_size); + break; +#endif + case RGUI_SETTINGS_AUDIO_VOLUME: + snprintf(type_str, type_str_size, "%.1f dB", g_extern.audio_data.volume_db); + break; +#ifdef _XBOX1 + case RGUI_SETTINGS_FLICKER_FILTER: + snprintf(type_str, type_str_size, "%d", g_extern.console.screen.flicker_filter_index); + break; + case RGUI_SETTINGS_SOFT_DISPLAY_FILTER: + snprintf(type_str, type_str_size, + (g_extern.lifecycle_state & (1ULL << MODE_VIDEO_SOFT_FILTER_ENABLE)) ? "ON" : "OFF"); + break; +#endif + case RGUI_SETTINGS_CUSTOM_BGM_CONTROL_ENABLE: + strlcpy(type_str, (g_extern.lifecycle_state & (1ULL << MODE_AUDIO_CUSTOM_BGM_ENABLE)) ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_PAUSE_IF_WINDOW_FOCUS_LOST: + strlcpy(type_str, g_settings.pause_nonactive ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_WINDOW_COMPOSITING_ENABLE: + strlcpy(type_str, g_settings.video.disable_composition ? "OFF" : "ON", type_str_size); + break; +#ifdef HAVE_NETPLAY + case RGUI_SETTINGS_NETPLAY_ENABLE: + strlcpy(type_str, g_extern.netplay_enable ? "ON" : "OFF", type_str_size); + break; + case RGUI_SETTINGS_NETPLAY_HOST_IP_ADDRESS: + strlcpy(type_str, g_extern.netplay_server, type_str_size); + break; + case RGUI_SETTINGS_NETPLAY_DELAY_FRAMES: + snprintf(type_str, type_str_size, "%d", g_extern.netplay_sync_frames); + break; + case RGUI_SETTINGS_NETPLAY_TCP_UDP_PORT: + snprintf(type_str, type_str_size, "%d", g_extern.netplay_port ? g_extern.netplay_port : RARCH_DEFAULT_PORT); + break; + case RGUI_SETTINGS_NETPLAY_NICKNAME: + snprintf(type_str, type_str_size, "%s", g_extern.netplay_nick); + break; + case RGUI_SETTINGS_NETPLAY_MODE: + snprintf(type_str, type_str_size, g_extern.netplay_is_client ? "Client" : "Server"); + break; + case RGUI_SETTINGS_NETPLAY_SPECTATOR_MODE_ENABLE: + snprintf(type_str, type_str_size, g_extern.netplay_is_spectate ? "ON" : "OFF"); + break; +#endif +#ifdef HAVE_CAMERA + case RGUI_SETTINGS_PRIVACY_CAMERA_ALLOW: + snprintf(type_str, type_str_size, g_settings.camera.allow ? "ON" : "OFF"); + break; +#endif +#ifdef HAVE_LOCATION + case RGUI_SETTINGS_PRIVACY_LOCATION_ALLOW: + snprintf(type_str, type_str_size, g_settings.location.allow ? "ON" : "OFF"); + break; +#endif +#ifdef HAVE_OSK + case RGUI_SETTINGS_ONSCREEN_KEYBOARD_ENABLE: + snprintf(type_str, type_str_size, g_settings.osk.enable ? "ON" : "OFF"); + break; +#endif + case RGUI_SETTINGS_FONT_ENABLE: + snprintf(type_str, type_str_size, g_settings.video.font_enable ? "ON" : "OFF"); + break; + case RGUI_SETTINGS_FONT_SCALE: + snprintf(type_str, type_str_size, g_settings.video.font_scale ? "ON" : "OFF"); + break; + case RGUI_SETTINGS_FONT_SIZE: + snprintf(type_str, type_str_size, "%.1f", g_settings.video.font_size); + break; + case RGUI_SETTINGS_LOAD_DUMMY_ON_CORE_SHUTDOWN: + snprintf(type_str, type_str_size, g_settings.load_dummy_on_core_shutdown ? "ON" : "OFF"); + break; + default: + *type_str = '\0'; + *w = 0; + break; + } +} + +const menu_ctx_driver_backend_t menu_ctx_backend_lakka = { + menu_lakka_entries_init, + menu_lakka_iterate, + menu_lakka_shader_manager_init, + menu_lakka_shader_manager_get_str, + menu_lakka_shader_manager_set_preset, + menu_lakka_shader_manager_save_preset, + menu_lakka_shader_manager_get_type, + menu_lakka_shader_manager_setting_toggle, + menu_lakka_type_is, + menu_lakka_core_setting_toggle, + menu_lakka_setting_toggle, + menu_lakka_setting_set, + menu_lakka_setting_set_label, + "menu_lakka", +}; diff --git a/frontend/menu/disp/lakka.c b/frontend/menu/disp/lakka.c index 93d3e301f9..bdd6382a10 100644 --- a/frontend/menu/disp/lakka.c +++ b/frontend/menu/disp/lakka.c @@ -39,240 +39,478 @@ #include "../../../screenshot.h" #include "../../../gfx/fonts/bitmap.h" -GLuint texid = 0; -struct texture_image *menu_texture; -static bool textures_inited =false; +#include "lakka.h" +#include "tween.h" +#include "png_texture_load.h" -// thanks to https://github.com/DavidEGrayson/ahrs-visualizer/blob/master/png_texture.cpp -static GLuint png_texture_load(void *data, int * width, int * height) -{ - struct texture_image *menu_texture = (struct texture_image*)data; - // Generate the OpenGL texture object - GLuint texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +#define HSPACING 300 +#define VSPACING 75 +#define C_ACTIVE_ZOOM 1.0 +#define C_PASSIVE_ZOOM 0.5 +#define I_ACTIVE_ZOOM 0.75 +#define I_PASSIVE_ZOOM 0.35 +#define DELAY 0.02 - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, menu_texture->width, menu_texture->height, 0, GL_RGB, GL_UNSIGNED_BYTE, menu_texture->pixels); +const GLfloat background_color[] = { + 0.1, 0.74, 0.61, 1.00, + 0.1, 0.74, 0.61, 1.00, + 0.1, 0.74, 0.61, 1.00, + 0.1, 0.74, 0.61, 1.00, +}; - return texture; -} +menu_category* categories = NULL; -#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_HLSL) -#define HAVE_SHADER_MANAGER -#endif +int depth = 0; -static uint16_t menu_framebuf[400 * 240]; +GLuint settings_icon; +GLuint arrow_icon; +GLuint run_icon; +GLuint resume_icon; +GLuint savestate_icon; +GLuint loadstate_icon; +GLuint screenshot_icon; +GLuint reload_icon; -#define LAKKA_TERM_START_X 15 -#define LAKKA_TERM_START_Y 27 -#define LAKKA_TERM_WIDTH (((rgui->width - LAKKA_TERM_START_X - 15) / (FONT_WIDTH_STRIDE))) -#define LAKKA_TERM_HEIGHT (((rgui->height - LAKKA_TERM_START_Y - 15) / (FONT_HEIGHT_STRIDE)) - 1) +struct font_output_list run_label; +struct font_output_list resume_label; + +int num_categories = 0; + +int menu_active_category = 0; int dim = 192; -static void lakka_copy_glyph(uint8_t *glyph, const uint8_t *buf) +float all_categories_x = 0; + +rgui_handle_t *rgui; + +// Fonts +void *font; +const gl_font_renderer_t *font_ctx; +const font_renderer_driver_t *font_driver; +GLuint font_tex; +GLint max_font_size; +int font_tex_w, font_tex_h; +uint32_t *font_tex_buf; +char font_last_msg[256]; +int font_last_width, font_last_height; +GLfloat font_color[16]; +GLfloat font_color_dark[16]; +GLuint texture; + +// Move the categories left or right depending on the menu_active_category variable +void lakka_switch_categories() { - int y, x; - for (y = 0; y < FONT_HEIGHT; y++) + // translation + add_tween(DELAY, -menu_active_category * HSPACING, &all_categories_x, &inOutQuad); + + // alpha tweening + for (int i = 0; i < num_categories; i++) { - for (x = 0; x < FONT_WIDTH; x++) + float ca = (i == menu_active_category) ? 1.0 : 0.5; + float cz = (i == menu_active_category) ? C_ACTIVE_ZOOM : C_PASSIVE_ZOOM; + add_tween(DELAY, ca, &categories[i].alpha, &inOutQuad); + add_tween(DELAY, cz, &categories[i].zoom, &inOutQuad); + + for (int j = 0; j < categories[i].num_items; j++) { - uint32_t col = - ((uint32_t)buf[3 * (-y * 256 + x) + 0] << 0) | - ((uint32_t)buf[3 * (-y * 256 + x) + 1] << 8) | - ((uint32_t)buf[3 * (-y * 256 + x) + 2] << 16); - - uint8_t rem = 1 << ((x + y * FONT_WIDTH) & 7); - unsigned offset = (x + y * FONT_WIDTH) >> 3; - - if (col != 0xff) - glyph[offset] |= rem; + float ia = (i != menu_active_category ) ? 0 : + (j == categories[i].active_item) ? 1.0 : 0.5; + add_tween(DELAY, ia, &categories[i].items[j].alpha, &inOutQuad); } } } -static uint16_t gray_filler(unsigned x, unsigned y) +void lakka_switch_items() { - return 0x0008; -} - -static uint16_t green_filler(unsigned x, unsigned y) -{ - return 0xf008; -} - -static void fill_rect(uint16_t *buf, unsigned pitch, - unsigned x, unsigned y, - unsigned width, unsigned height, - uint16_t (*col)(unsigned x, unsigned y)) -{ - unsigned j, i; - for (j = y; j < y + height; j++) - for (i = x; i < x + width; i++) - buf[j * (pitch >> 1) + i] = col(i, j); -} - -static void blit_line(rgui_handle_t *rgui, - int x, int y, const char *message, bool green) -{ - int j, i; - while (*message) + for (int j = 0; j < categories[menu_active_category].num_items; j++) { - for (j = 0; j < FONT_HEIGHT; j++) - { - for (i = 0; i < FONT_WIDTH; i++) - { - uint8_t rem = 1 << ((i + j * FONT_WIDTH) & 7); - int offset = (i + j * FONT_WIDTH) >> 3; - bool col = (rgui->font[FONT_OFFSET((unsigned char)*message) + offset] & rem); + float ia = (j == categories[menu_active_category].active_item) ? 1.0 : 0.5; + float iz = (j == categories[menu_active_category].active_item) ? I_ACTIVE_ZOOM : I_PASSIVE_ZOOM; + float iy = (j == categories[menu_active_category].active_item) ? VSPACING*2.5 : + (j < categories[menu_active_category].active_item) ? VSPACING*(j-categories[menu_active_category].active_item - 1) : + VSPACING*(j-categories[menu_active_category].active_item + 3); - if (col) - { - rgui->frame_buf[(y + j) * (rgui->frame_buf_pitch >> 1) + (x + i)] = green ? -#ifdef GEKKO - (3 << 0) | (10 << 4) | (3 << 8) | (7 << 12) : 0x7FFF; -#else - (15 << 0) | (7 << 4) | (15 << 8) | (7 << 12) : 0xFFFF; -#endif + add_tween(DELAY, ia, &categories[menu_active_category].items[j].alpha, &inOutQuad); + add_tween(DELAY, iz, &categories[menu_active_category].items[j].zoom, &inOutQuad); + add_tween(DELAY, iy, &categories[menu_active_category].items[j].y, &inOutQuad); + } +} + +void lakka_switch_subitems () +{ + menu_item ai = categories[menu_active_category].items[categories[menu_active_category].active_item]; + + for (int k = 0; k < ai.num_subitems; k++) { + // Above items + if (k < ai.active_subitem) { + add_tween(DELAY, 0.5, &ai.subitems[k].alpha, &inOutQuad); + add_tween(DELAY, VSPACING*(k-ai.active_subitem + 2), &ai.subitems[k].y, &inOutQuad); + add_tween(DELAY, I_PASSIVE_ZOOM, &ai.subitems[k].zoom, &inOutQuad); + // Active item + } else if (k == ai.active_subitem) { + add_tween(DELAY, 1.0, &ai.subitems[k].alpha, &inOutQuad); + add_tween(DELAY, VSPACING*2.5, &ai.subitems[k].y, &inOutQuad); + add_tween(DELAY, I_ACTIVE_ZOOM, &ai.subitems[k].zoom, &inOutQuad); + // Under items + } else if (k > ai.active_subitem) { + add_tween(DELAY, 0.5, &ai.subitems[k].alpha, &inOutQuad); + add_tween(DELAY, VSPACING*(k-ai.active_subitem + 3), &ai.subitems[k].y, &inOutQuad); + add_tween(DELAY, I_PASSIVE_ZOOM, &ai.subitems[k].zoom, &inOutQuad); + } + } +} + +void lakka_reset_submenu() +{ + if (! (g_extern.main_is_init && !g_extern.libretro_dummy && strcmp(g_extern.fullpath, categories[menu_active_category].items[categories[menu_active_category].active_item].rom) == 0)) { // Keeps active submenu state (do we really want that?) + categories[menu_active_category].items[categories[menu_active_category].active_item].active_subitem = 0; + for (int i = 0; i < num_categories; i++) { + for (int j = 0; j < categories[i].num_items; j++) { + for (int k = 0; k < categories[i].items[j].num_subitems; k++) { + categories[i].items[j].subitems[k].alpha = 0; + categories[i].items[j].subitems[k].zoom = k == categories[i].items[j].active_subitem ? I_ACTIVE_ZOOM : I_PASSIVE_ZOOM; + categories[i].items[j].subitems[k].y = k == 0 ? VSPACING*2.5 : VSPACING*(3+k); } } } - - x += FONT_WIDTH_STRIDE; - message++; } } -static void init_font(rgui_handle_t *rgui, const uint8_t *font_bmp_buf) +void lakka_open_submenu () +{ + add_tween(DELAY, -HSPACING * (menu_active_category+1), &all_categories_x, &inOutQuad); + + // Reset contextual menu style + lakka_reset_submenu(); + + for (int i = 0; i < num_categories; i++) { + if (i == menu_active_category) { + add_tween(DELAY, 1.0, &categories[i].alpha, &inOutQuad); + for (int j = 0; j < categories[i].num_items; j++) { + if (j == categories[i].active_item) { + for (int k = 0; k < categories[i].items[j].num_subitems; k++) { + if (k == categories[i].items[j].active_subitem) { + add_tween(DELAY, 1.0, &categories[i].items[j].subitems[k].alpha, &inOutQuad); + add_tween(DELAY, I_ACTIVE_ZOOM, &categories[i].items[j].subitems[k].zoom, &inOutQuad); + } else { + add_tween(DELAY, 0.5, &categories[i].items[j].subitems[k].alpha, &inOutQuad); + add_tween(DELAY, I_PASSIVE_ZOOM, &categories[i].items[j].subitems[k].zoom, &inOutQuad); + } + } + } else { + add_tween(DELAY, 0, &categories[i].items[j].alpha, &inOutQuad); + } + } + } else { + add_tween(DELAY, 0, &categories[i].alpha, &inOutQuad); + } + } +} + +void lakka_close_submenu () +{ + add_tween(DELAY, -HSPACING * menu_active_category, &all_categories_x, &inOutQuad); + + for (int i = 0; i < num_categories; i++) { + if (i == menu_active_category) { + add_tween(DELAY, 1.0, &categories[i].alpha, &inOutQuad); + add_tween(DELAY, C_ACTIVE_ZOOM, &categories[i].zoom, &inOutQuad); + for (int j = 0; j < categories[i].num_items; j++) { + if (j == categories[i].active_item) { + add_tween(DELAY, 1.0, &categories[i].items[j].alpha, &inOutQuad); + for (int k = 0; k < categories[i].items[j].num_subitems; k++) { + add_tween(DELAY, 0, &categories[i].items[j].subitems[k].alpha, &inOutQuad); + } + } else { + add_tween(DELAY, 0.5, &categories[i].items[j].alpha, &inOutQuad); + } + } + } else { + add_tween(DELAY, 0.5, &categories[i].alpha, &inOutQuad); + add_tween(DELAY, C_PASSIVE_ZOOM, &categories[i].zoom, &inOutQuad); + for (int j = 0; j < categories[i].num_items; j++) { + add_tween(DELAY, 0, &categories[i].items[j].alpha, &inOutQuad); + } + } + } +} + +struct font_rect +{ + int x, y; + int width, height; + int pot_width, pot_height; +}; + +/* font rendering */ + +static bool init_font(void *data, const char *font_path, float font_size, unsigned win_width, unsigned win_height) +{ + size_t i, j; + (void)win_width; + (void)win_height; + + if (!g_settings.video.font_enable) + return false; + + (void)font_size; + gl_t *gl = (gl_t*)data; + + if (font_renderer_create_default(&font_driver, &font)) + { + glGenTextures(1, &font_tex); + glBindTexture(GL_TEXTURE_2D, font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, texture); + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_font_size); + } + else + { + RARCH_WARN("Couldn't init font renderer.\n"); + return false; + } + + for (i = 0; i < 4; i++) + { + font_color[4 * i + 0] = g_settings.video.msg_color_r; + font_color[4 * i + 1] = g_settings.video.msg_color_g; + font_color[4 * i + 2] = g_settings.video.msg_color_b; + font_color[4 * i + 3] = 1.0; + } + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 3; j++) + font_color_dark[4 * i + j] = 0.3 * font_color[4 * i + j]; + font_color_dark[4 * i + 3] = 1.0; + } + + return true; +} + +static void calculate_msg_geometry(const struct font_output *head, struct font_rect *rect) +{ + int x_min = head->off_x; + int x_max = head->off_x + head->width+10; + int y_min = head->off_y; + int y_max = head->off_y + head->height; + + while ((head = head->next)) + { + int left = head->off_x; + int right = head->off_x + head->width; + int bottom = head->off_y; + int top = head->off_y + head->height; + + if (left < x_min) + x_min = left; + if (right > x_max) + x_max = right; + + if (bottom < y_min) + y_min = bottom; + if (top > y_max) + y_max = top; + } + + rect->x = x_min; + rect->y = y_min; + rect->width = x_max - x_min; + rect->height = y_max - y_min; +} + +static void adjust_power_of_two(gl_t *gl, struct font_rect *geom) +{ + // Some systems really hate NPOT textures. + geom->pot_width = next_pow2(geom->width); + geom->pot_height = next_pow2(geom->height); + + if (geom->pot_width > max_font_size) + geom->pot_width = max_font_size; + if (geom->pot_height > max_font_size) + geom->pot_height = max_font_size; + + if ((geom->pot_width > font_tex_w) || (geom->pot_height > font_tex_h)) + { + font_tex_buf = (uint32_t*)realloc(font_tex_buf, + geom->pot_width * geom->pot_height * sizeof(uint32_t)); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, geom->pot_width, geom->pot_height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + font_tex_w = geom->pot_width; + font_tex_h = geom->pot_height; + } +} + +static void copy_glyph(const struct font_output *head, const struct font_rect *geom, uint32_t *buffer, unsigned width, unsigned height) +{ + int h, w; + // head has top-left oriented coords. + int x = head->off_x - geom->x; + int y = head->off_y - geom->y; + y = height - head->height - y - 1; + + const uint8_t *src = head->output; + int font_width = head->width + ((x < 0) ? x : 0); + int font_height = head->height + ((y < 0) ? y : 0); + + if (x < 0) + { + src += -x; + x = 0; + } + + if (y < 0) + { + src += -y * head->pitch; + y = 0; + } + + if (x + font_width > (int)width) + font_width = width - x; + + if (y + font_height > (int)height) + font_height = height - y; + + uint32_t *dst = buffer + y * width + x; + for (h = 0; h < font_height; h++, dst += width, src += head->pitch) + { + uint8_t *d = (uint8_t*)dst; + for (w = 0; w < font_width; w++) + { + *d++ = 0xff; + *d++ = 0xff; + *d++ = 0xff; + *d++ = src[w]; + } + } +} + +static void blit_fonts(gl_t *gl, const struct font_output *head, const struct font_rect *geom) +{ + memset(font_tex_buf, 0, font_tex_w * font_tex_h * sizeof(uint32_t)); + + while (head) + { + copy_glyph(head, geom, font_tex_buf, font_tex_w, font_tex_h); + head = head->next; + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, 8); + glTexSubImage2D(GL_TEXTURE_2D, + 0, 0, 0, font_tex_w, font_tex_h, + GL_RGBA, GL_UNSIGNED_BYTE, font_tex_buf); +} + +static void calculate_font_coords(gl_t *gl, + GLfloat font_vertex[8], GLfloat font_vertex_dark[8], GLfloat font_tex_coords[8], GLfloat scale, GLfloat pos_x, GLfloat pos_y) { unsigned i; - uint8_t *font = (uint8_t *) calloc(1, FONT_OFFSET(256)); - rgui->alloc_font = true; - for (i = 0; i < 256; i++) + GLfloat scale_factor = scale; + + GLfloat lx = pos_x; + GLfloat hx = (GLfloat)font_last_width * scale_factor / gl->vp.width + lx; + GLfloat ly = pos_y; + GLfloat hy = (GLfloat)font_last_height * scale_factor / gl->vp.height + ly; + + font_vertex[0] = lx; + font_vertex[2] = hx; + font_vertex[4] = lx; + font_vertex[6] = hx; + font_vertex[1] = hy; + font_vertex[3] = hy; + font_vertex[5] = ly; + font_vertex[7] = ly; + + GLfloat shift_x = 2.0f / gl->vp.width; + GLfloat shift_y = 2.0f / gl->vp.height; + for (i = 0; i < 4; i++) { - unsigned y = i / 16; - unsigned x = i % 16; - lakka_copy_glyph(&font[FONT_OFFSET(i)], - font_bmp_buf + 54 + 3 * (256 * (255 - 16 * y) + 16 * x)); + font_vertex_dark[2 * i + 0] = font_vertex[2 * i + 0] - shift_x; + font_vertex_dark[2 * i + 1] = font_vertex[2 * i + 1] - shift_y; } - rgui->font = font; + lx = 0.0f; + hx = (GLfloat)font_last_width / font_tex_w; + ly = 1.0f - (GLfloat)font_last_height / font_tex_h; + hy = 1.0f; + + font_tex_coords[0] = lx; + font_tex_coords[2] = hx; + font_tex_coords[4] = lx; + font_tex_coords[6] = hx; + font_tex_coords[1] = ly; + font_tex_coords[3] = ly; + font_tex_coords[5] = hy; + font_tex_coords[7] = hy; } -static bool rguidisp_init_font(void *data) +static void lakka_draw_text(void *data, struct font_output_list out, float x, float y, float scale, float alpha) { - rgui_handle_t *rgui = (rgui_handle_t*)data; - - const uint8_t *font_bmp_buf = NULL; - const uint8_t *font_bin_buf = bitmap_bin; - bool ret = true; - - if (font_bmp_buf) - init_font(rgui, font_bmp_buf); - else if (font_bin_buf) - rgui->font = font_bin_buf; - else - ret = false; - - return ret; -} - -static void lakka_render_background(rgui_handle_t *rgui) -{ - fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, - 0, 0, rgui->width, rgui->height, gray_filler); - - fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, - 5, 5, rgui->width - 10, 5, green_filler); - - fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, - 5, rgui->height - 10, rgui->width - 10, 5, green_filler); - - fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, - 5, 5, 5, rgui->height - 10, green_filler); - - fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, - rgui->width - 10, 5, 5, rgui->height - 10, green_filler); -} - -static void lakka_render_messagebox(void *data, const char *message) -{ - rgui_handle_t *rgui = (rgui_handle_t*)data; - size_t i; - - if (!message || !*message) + gl_t *gl = (gl_t*)data; + if (!font) return; - struct string_list *list = string_split(message, "\n"); - if (!list) - return; - if (list->elems == 0) + for (int i = 0; i < 4; i++) { - string_list_free(list); - return; + font_color[4 * i + 0] = 1.0; + font_color[4 * i + 1] = 1.0; + font_color[4 * i + 2] = 1.0; + font_color[4 * i + 3] = alpha; } - unsigned width = 0; - unsigned glyphs_width = 0; - for (i = 0; i < list->size; i++) - { - char *msg = list->elems[i].data; - unsigned msglen = strlen(msg); - if (msglen > LAKKA_TERM_WIDTH) - { - msg[LAKKA_TERM_WIDTH - 2] = '.'; - msg[LAKKA_TERM_WIDTH - 1] = '.'; - msg[LAKKA_TERM_WIDTH - 0] = '.'; - msg[LAKKA_TERM_WIDTH + 1] = '\0'; - msglen = LAKKA_TERM_WIDTH; - } + if (gl->shader) + gl->shader->use(gl, GL_SHADER_STOCK_BLEND); - unsigned line_width = msglen * FONT_WIDTH_STRIDE - 1 + 6 + 10; - width = max(width, line_width); - glyphs_width = max(glyphs_width, msglen); - } + gl_set_viewport(gl, gl->win_width, gl->win_height, true, false); - unsigned height = FONT_HEIGHT_STRIDE * list->size + 6 + 10; - int x = (rgui->width - width) / 2; - int y = (rgui->height - height) / 2; - - fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, - x + 5, y + 5, width - 10, height - 10, gray_filler); + glEnable(GL_BLEND); - fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, - x, y, width - 5, 5, green_filler); + GLfloat font_vertex[8]; + GLfloat font_vertex_dark[8]; + GLfloat font_tex_coords[8]; - fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, - x + width - 5, y, 5, height - 5, green_filler); + glBindTexture(GL_TEXTURE_2D, font_tex); - fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, - x + 5, y + height - 5, width - 5, 5, green_filler); + gl->coords.tex_coord = font_tex_coords; - fill_rect(rgui->frame_buf, rgui->frame_buf_pitch, - x, y + 5, 5, height - 5, green_filler); + struct font_output *head = out.head; - for (i = 0; i < list->size; i++) - { - const char *msg = list->elems[i].data; - int offset_x = FONT_WIDTH_STRIDE * (glyphs_width - strlen(msg)) / 2; - int offset_y = FONT_HEIGHT_STRIDE * i; - blit_line(rgui, x + 8 + offset_x, y + 8 + offset_y, msg, false); - } + struct font_rect geom; + calculate_msg_geometry(head, &geom); + adjust_power_of_two(gl, &geom); + blit_fonts(gl, head, &geom); - string_list_free(list); + //font_driver->free_output(font, &out); + + font_last_width = geom.width; + font_last_height = geom.height; + + calculate_font_coords(gl, font_vertex, font_vertex_dark, font_tex_coords, + scale, x / gl->win_width, (gl->win_height - y) / gl->win_height); + + gl->coords.vertex = font_vertex; + gl->coords.color = font_color; + gl_shader_set_coords(gl, &gl->coords, &gl->mvp); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Post - Go back to old rendering path. + gl->coords.vertex = gl->vertex_ptr; + gl->coords.tex_coord = gl->tex_coords; + gl->coords.color = gl->white_color_ptr; + glBindTexture(GL_TEXTURE_2D, texture); + + glDisable(GL_BLEND); + + struct gl_ortho ortho = {0, 1, 0, 1, -1, 1}; + gl_set_projection(gl, &ortho, true); } -const GLfloat background_color[] = { - 0, 200, 200, 0.75, - 0, 200, 200, 0.75, - 0, 200, 200, 0.75, - 0, 200, 200, 0.75, -}; - void lakka_draw_background(void *data) { gl_t *gl = (gl_t*)data; @@ -284,7 +522,7 @@ void lakka_draw_background(void *data) glBindTexture(GL_TEXTURE_2D, 0); if (gl->shader) - gl->shader->use(GL_SHADER_STOCK_BLEND); + gl->shader->use(gl, GL_SHADER_STOCK_BLEND); gl_shader_set_coords(gl, &gl->coords, &gl->mvp_no_rot); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); @@ -296,7 +534,7 @@ void lakka_draw_icon(void *data, GLuint texture, float x, float y, float alpha, { gl_t *gl = (gl_t*)data; - glViewport(x, gl->win_height - y, dim, dim); + glViewport(x, 900 - y, dim, dim); glEnable(GL_BLEND); @@ -307,13 +545,6 @@ void lakka_draw_icon(void *data, GLuint texture, float x, float y, float alpha, 1.0f, 1.0f, 1.0f, alpha, }; - static const GLfloat vertexes_flipped[] = { - 0, 1, - 1, 1, - 0, 0, - 1, 0 - }; - static const GLfloat vtest[] = { 0, 0, 1, 0, @@ -327,7 +558,7 @@ void lakka_draw_icon(void *data, GLuint texture, float x, float y, float alpha, glBindTexture(GL_TEXTURE_2D, texture); if (gl->shader) - gl->shader->use(GL_SHADER_STOCK_BLEND); + gl->shader->use(gl, GL_SHADER_STOCK_BLEND); math_matrix mymat; @@ -347,301 +578,143 @@ void lakka_draw_icon(void *data, GLuint texture, float x, float y, float alpha, gl->coords.vertex = gl->vertex_ptr; gl->coords.tex_coord = gl->tex_coords; gl->coords.color = gl->white_color_ptr; - - gl_set_viewport(gl, gl->win_width, gl->win_height, 0, 0); -} - -static void lakka_render(void *data) -{ - rgui_handle_t *rgui = (rgui_handle_t*)data; - - gl_t *gl = (gl_t*)driver.video_data; - - gl_set_viewport(gl, gl->win_width, gl->win_height, 0, 0); - - lakka_draw_background(gl); - - lakka_draw_icon(gl, texid, 0, 0, 1, 0, 1); - - gl_set_viewport(gl, gl->win_width, gl->win_height, false, false); - - if (rgui->need_refresh && - (g_extern.lifecycle_state & (1ULL << MODE_MENU)) - && !rgui->msg_force) - return; - - size_t begin = rgui->selection_ptr >= LAKKA_TERM_HEIGHT / 2 ? - rgui->selection_ptr - LAKKA_TERM_HEIGHT / 2 : 0; - size_t end = rgui->selection_ptr + LAKKA_TERM_HEIGHT <= rgui->selection_buf->size ? - rgui->selection_ptr + LAKKA_TERM_HEIGHT : rgui->selection_buf->size; - - // Do not scroll if all items are visible. - if (rgui->selection_buf->size <= LAKKA_TERM_HEIGHT) - begin = 0; - - if (end - begin > LAKKA_TERM_HEIGHT) - end = begin + LAKKA_TERM_HEIGHT; - - lakka_render_background(rgui); - - char title[256]; - const char *dir = NULL; - unsigned menu_type = 0; - unsigned menu_type_is = 0; - file_list_get_last(rgui->menu_stack, &dir, &menu_type); - - if (driver.menu_ctx && driver.menu_ctx->backend && driver.menu_ctx->backend->type_is) - menu_type_is = driver.menu_ctx->backend->type_is(menu_type); - - if (menu_type == RGUI_SETTINGS_CORE) - snprintf(title, sizeof(title), "CORE SELECTION %s", dir); - else if (menu_type == RGUI_SETTINGS_DEFERRED_CORE) - snprintf(title, sizeof(title), "DETECTED CORES %s", dir); - else if (menu_type == RGUI_SETTINGS_CONFIG) - snprintf(title, sizeof(title), "CONFIG %s", dir); - else if (menu_type == RGUI_SETTINGS_DISK_APPEND) - snprintf(title, sizeof(title), "DISK APPEND %s", dir); - else if (menu_type == RGUI_SETTINGS_VIDEO_OPTIONS) - strlcpy(title, "VIDEO OPTIONS", sizeof(title)); - else if (menu_type == RGUI_SETTINGS_DRIVERS) - strlcpy(title, "DRIVER OPTIONS", sizeof(title)); -#ifdef HAVE_SHADER_MANAGER - else if (menu_type == RGUI_SETTINGS_SHADER_OPTIONS) - strlcpy(title, "SHADER OPTIONS", sizeof(title)); -#endif - else if (menu_type == RGUI_SETTINGS_AUDIO_OPTIONS) - strlcpy(title, "AUDIO OPTIONS", sizeof(title)); - else if (menu_type == RGUI_SETTINGS_DISK_OPTIONS) - strlcpy(title, "DISK OPTIONS", sizeof(title)); - else if (menu_type == RGUI_SETTINGS_CORE_OPTIONS) - strlcpy(title, "CORE OPTIONS", sizeof(title)); -#ifdef HAVE_SHADER_MANAGER - else if (menu_type_is == RGUI_SETTINGS_SHADER_OPTIONS) - snprintf(title, sizeof(title), "SHADER %s", dir); -#endif - else if ((menu_type == RGUI_SETTINGS_INPUT_OPTIONS) || - (menu_type == RGUI_SETTINGS_PATH_OPTIONS) || - (menu_type == RGUI_SETTINGS_OPTIONS) || - (menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT || menu_type == RGUI_SETTINGS_CUSTOM_VIEWPORT_2) || - menu_type == RGUI_SETTINGS_CUSTOM_BIND || - menu_type == RGUI_START_SCREEN || - menu_type == RGUI_SETTINGS) - snprintf(title, sizeof(title), "MENU %s", dir); - else if (menu_type == RGUI_SETTINGS_OPEN_HISTORY) - strlcpy(title, "LOAD HISTORY", sizeof(title)); -#ifdef HAVE_OVERLAY - else if (menu_type == RGUI_SETTINGS_OVERLAY_PRESET) - snprintf(title, sizeof(title), "OVERLAY %s", dir); -#endif - else if (menu_type == RGUI_BROWSER_DIR_PATH) - snprintf(title, sizeof(title), "BROWSER DIR %s", dir); -#ifdef HAVE_SCREENSHOTS - else if (menu_type == RGUI_SCREENSHOT_DIR_PATH) - snprintf(title, sizeof(title), "SCREENSHOT DIR %s", dir); -#endif - else if (menu_type == RGUI_SHADER_DIR_PATH) - snprintf(title, sizeof(title), "SHADER DIR %s", dir); - else if (menu_type == RGUI_FILTER_DIR_PATH) - snprintf(title, sizeof(title), "FILTER DIR %s", dir); - else if (menu_type == RGUI_SAVESTATE_DIR_PATH) - snprintf(title, sizeof(title), "SAVESTATE DIR %s", dir); -#ifdef HAVE_DYNAMIC - else if (menu_type == RGUI_LIBRETRO_DIR_PATH) - snprintf(title, sizeof(title), "LIBRETRO DIR %s", dir); -#endif - else if (menu_type == RGUI_CONFIG_DIR_PATH) - snprintf(title, sizeof(title), "CONFIG DIR %s", dir); - else if (menu_type == RGUI_SAVEFILE_DIR_PATH) - snprintf(title, sizeof(title), "SAVEFILE DIR %s", dir); -#ifdef HAVE_OVERLAY - else if (menu_type == RGUI_OVERLAY_DIR_PATH) - snprintf(title, sizeof(title), "OVERLAY DIR %s", dir); -#endif - else if (menu_type == RGUI_SYSTEM_DIR_PATH) - snprintf(title, sizeof(title), "SYSTEM DIR %s", dir); - else - { - if (rgui->defer_core) - snprintf(title, sizeof(title), "CONTENT %s", dir); - else - { - const char *core_name = rgui->info.library_name; - if (!core_name) - core_name = g_extern.system.info.library_name; - if (!core_name) - core_name = "No Core"; - snprintf(title, sizeof(title), "CONTENT (%s) %s", core_name, dir); - } - } - - char title_buf[256]; - menu_ticker_line(title_buf, LAKKA_TERM_WIDTH - 3, g_extern.frame_count / 15, title, true); - blit_line(rgui, LAKKA_TERM_START_X + 15, 15, title_buf, true); - - char title_msg[64]; - const char *core_name = rgui->info.library_name; - if (!core_name) - core_name = g_extern.system.info.library_name; - if (!core_name) - core_name = "No Core"; - - const char *core_version = rgui->info.library_version; - if (!core_version) - core_version = g_extern.system.info.library_version; - if (!core_version) - core_version = ""; - - snprintf(title_msg, sizeof(title_msg), "%s - %s %s", PACKAGE_VERSION, core_name, core_version); - blit_line(rgui, LAKKA_TERM_START_X + 15, (LAKKA_TERM_HEIGHT * FONT_HEIGHT_STRIDE) + LAKKA_TERM_START_Y + 2, title_msg, true); - - unsigned x, y; - size_t i; - - x = LAKKA_TERM_START_X; - y = LAKKA_TERM_START_Y; - - for (i = begin; i < end; i++, y += FONT_HEIGHT_STRIDE) - { - const char *path = 0; - unsigned type = 0; - file_list_get_at_offset(rgui->selection_buf, i, &path, &type); - char message[256]; - char type_str[256]; - - unsigned w = 19; - if (menu_type == RGUI_SETTINGS_INPUT_OPTIONS || menu_type == RGUI_SETTINGS_CUSTOM_BIND) - w = 21; - else if (menu_type == RGUI_SETTINGS_PATH_OPTIONS) - w = 24; - -#ifdef HAVE_SHADER_MANAGER - if (type >= RGUI_SETTINGS_SHADER_FILTER && - type <= RGUI_SETTINGS_SHADER_LAST) - { - // HACK. Work around that we're using the menu_type as dir type to propagate state correctly. - if ((menu_type_is == RGUI_SETTINGS_SHADER_OPTIONS) - && (menu_type_is == RGUI_SETTINGS_SHADER_OPTIONS)) - { - type = RGUI_FILE_DIRECTORY; - strlcpy(type_str, "(DIR)", sizeof(type_str)); - w = 5; - } - else if (type == RGUI_SETTINGS_SHADER_OPTIONS || type == RGUI_SETTINGS_SHADER_PRESET) - strlcpy(type_str, "...", sizeof(type_str)); - else if (type == RGUI_SETTINGS_SHADER_FILTER) - snprintf(type_str, sizeof(type_str), "%s", - g_settings.video.smooth ? "Linear" : "Nearest"); - else - shader_manager_get_str(&rgui->shader, type_str, sizeof(type_str), type); - } - else -#endif - // Pretty-print libretro cores from menu. - if (menu_type == RGUI_SETTINGS_CORE || menu_type == RGUI_SETTINGS_DEFERRED_CORE) - { - if (type == RGUI_FILE_PLAIN) - { - strlcpy(type_str, "(CORE)", sizeof(type_str)); - file_list_get_alt_at_offset(rgui->selection_buf, i, &path); - w = 6; - } - else - { - strlcpy(type_str, "(DIR)", sizeof(type_str)); - type = RGUI_FILE_DIRECTORY; - w = 5; - } - } - else if (menu_type == RGUI_SETTINGS_CONFIG || -#ifdef HAVE_OVERLAY - menu_type == RGUI_SETTINGS_OVERLAY_PRESET || -#endif - menu_type == RGUI_SETTINGS_DISK_APPEND || - menu_type_is == RGUI_FILE_DIRECTORY) - { - if (type == RGUI_FILE_PLAIN) - { - strlcpy(type_str, "(FILE)", sizeof(type_str)); - w = 6; - } - else if (type == RGUI_FILE_USE_DIRECTORY) - { - *type_str = '\0'; - w = 0; - } - else - { - strlcpy(type_str, "(DIR)", sizeof(type_str)); - type = RGUI_FILE_DIRECTORY; - w = 5; - } - } - else if (menu_type == RGUI_SETTINGS_OPEN_HISTORY) - { - *type_str = '\0'; - w = 0; - } - else if (type >= RGUI_SETTINGS_CORE_OPTION_START) - strlcpy(type_str, - core_option_get_val(g_extern.system.core_options, type - RGUI_SETTINGS_CORE_OPTION_START), - sizeof(type_str)); - else if (driver.menu_ctx && driver.menu_ctx->backend && driver.menu_ctx->backend->setting_set_label) - driver.menu_ctx->backend->setting_set_label(type_str, sizeof(type_str), &w, type); - - char entry_title_buf[256]; - char type_str_buf[64]; - bool selected = i == rgui->selection_ptr; - - strlcpy(entry_title_buf, path, sizeof(entry_title_buf)); - strlcpy(type_str_buf, type_str, sizeof(type_str_buf)); - - if ((type == RGUI_FILE_PLAIN || type == RGUI_FILE_DIRECTORY)) - menu_ticker_line(entry_title_buf, LAKKA_TERM_WIDTH - (w + 1 + 2), g_extern.frame_count / 15, path, selected); - else - menu_ticker_line(type_str_buf, w, g_extern.frame_count / 15, type_str, selected); - - snprintf(message, sizeof(message), "%c %-*.*s %-*s", - selected ? '>' : ' ', - LAKKA_TERM_WIDTH - (w + 1 + 2), LAKKA_TERM_WIDTH - (w + 1 + 2), - entry_title_buf, - w, - type_str_buf); - - blit_line(rgui, x, y, message, selected); - } - -#ifdef GEKKO - const char *message_queue; - - if (rgui->msg_force) - { - message_queue = msg_queue_pull(g_extern.msg_queue); - rgui->msg_force = false; - } - else - message_queue = driver.current_msg; - - rgui_render_messagebox(rgui, message_queue); -#endif - - if (rgui->keyboard.display) - { - char msg[1024]; - const char *str = *rgui->keyboard.buffer; - if (!str) - str = ""; - snprintf(msg, sizeof(msg), "%s\n%s", rgui->keyboard.label, str); - lakka_render_messagebox(rgui, msg); - } } static void lakka_set_texture(void *data, bool enable) { rgui_handle_t *rgui = (rgui_handle_t*)data; - if (driver.video_data && driver.video_poke && driver.video_poke->set_texture_enable) - driver.video_poke->set_texture_frame(driver.video_data, menu_framebuf, - enable, rgui->width, rgui->height, 1.0f); + //if (driver.video_data && driver.video_poke && driver.video_poke->set_texture_enable) + //driver.video_poke->set_texture_frame(driver.video_data, menu_framebuf, + // enable, 1440, 900, 1.0f); +} + +void lakka_render(void *data) +{ + // Throttle in case VSync is broken (avoid 1000+ FPS RGUI). + /*retro_time_t time, delta, target_msec, sleep_msec; + time = rarch_get_time_usec(); + delta = (time - rgui->last_time) / 1000; + target_msec = 750 / g_settings.video.refresh_rate; // Try to sleep less, so we can hopefully rely on FPS logger. + sleep_msec = target_msec - delta; + if (sleep_msec > 0) + rarch_sleep((unsigned int)sleep_msec); + rgui->last_time = rarch_get_time_usec();*/ + + //update_tweens((float)delta/10000); + + gl_t *gl = (gl_t*)data; + + glViewport(0, 0, gl->win_width, gl->win_height); + + lakka_draw_background(gl); + + for(int i = 0; i < num_categories; i++) + { + // draw items + for(int j = 0; j < categories[i].num_items; j++) + { + lakka_draw_icon(gl, + categories[i].items[j].icon, + 156 + HSPACING*(i+1) + all_categories_x - dim/2.0, + 300 + categories[i].items[j].y + dim/2.0, + categories[i].items[j].alpha, + 0, + categories[i].items[j].zoom); + + if (i == menu_active_category && j == categories[i].active_item && depth == 1) // performance improvement + { + for(int k = 0; k < categories[i].items[j].num_subitems; k++) + { + if (k == 0 && g_extern.main_is_init && !g_extern.libretro_dummy && strcmp(g_extern.fullpath, categories[menu_active_category].items[categories[menu_active_category].active_item].rom) == 0) + { + lakka_draw_icon(gl, + resume_icon, + 156 + HSPACING*(i+2) + all_categories_x - dim/2.0, + 300 + categories[i].items[j].subitems[k].y + dim/2.0, + categories[i].items[j].subitems[k].alpha, + 0, + categories[i].items[j].subitems[k].zoom); + lakka_draw_text(gl, + resume_label, + 156 + HSPACING*(i+2) + all_categories_x + dim/2.0, + 300 + categories[i].items[j].subitems[k].y + 15, + 1, + categories[i].items[j].subitems[k].alpha); + } + else if (k == 0) + { + lakka_draw_icon(gl, + run_icon, + 156 + HSPACING*(i+2) + all_categories_x - dim/2.0, + 300 + categories[i].items[j].subitems[k].y + dim/2.0, + categories[i].items[j].subitems[k].alpha, + 0, + categories[i].items[j].subitems[k].zoom); + lakka_draw_text(gl, + run_label, + 156 + HSPACING*(i+2) + all_categories_x + dim/2.0, + 300 + categories[i].items[j].subitems[k].y + 15, + 1, + categories[i].items[j].subitems[k].alpha); + } else if (g_extern.main_is_init && !g_extern.libretro_dummy && strcmp(g_extern.fullpath, categories[menu_active_category].items[categories[menu_active_category].active_item].rom) == 0) + { + lakka_draw_icon(gl, + categories[i].items[j].subitems[k].icon, + 156 + HSPACING*(i+2) + all_categories_x - dim/2.0, + 300 + categories[i].items[j].subitems[k].y + dim/2.0, + categories[i].items[j].subitems[k].alpha, + 0, + categories[i].items[j].subitems[k].zoom); + /*if category.prefix ~= "settings" and (k == 2 or k == 3) and item.slot == -1 then + love.graphics.print(subitem.name .. " <" .. item.slot .. " (auto)>", 256 + (HSPACING*(i+1)) + all_categories.x, 300-15 + subitem.y) + elseif category.prefix ~= "settings" and (k == 2 or k == 3) then + love.graphics.print(subitem.name .. " <" .. item.slot .. ">", 256 + (HSPACING*(i+1)) + all_categories.x, 300-15 + subitem.y) + else*/ + lakka_draw_text(gl, + categories[i].items[j].subitems[k].out, + 156 + HSPACING*(i+2) + all_categories_x + dim/2.0, + 300 + categories[i].items[j].subitems[k].y + 15, + 1, + categories[i].items[j].subitems[k].alpha); + /*end*/ + } + } + } + + if (depth == 0) { + if (i == menu_active_category && j > categories[menu_active_category].active_item - 4 && j < categories[menu_active_category].active_item + 10) // performance improvement + lakka_draw_text(gl, + categories[i].items[j].out, + 156 + HSPACING*(i+1) + all_categories_x + dim/2.0, + 300 + categories[i].items[j].y + 15, + 1, + categories[i].items[j].alpha); + } else { + lakka_draw_icon(gl, + arrow_icon, + 156 + (HSPACING*(i+1)) + all_categories_x + 150 +-dim/2.0, + 300 + categories[i].items[j].y + dim/2.0, + categories[i].items[j].alpha, + 0, + categories[i].items[j].zoom); + } + } + + // draw category + lakka_draw_icon(gl, + categories[i].icon, + 156 + (HSPACING*(i+1)) + all_categories_x - dim/2.0, + 300 + dim/2.0, + categories[i].alpha, + 0, + categories[i].zoom); + } + + struct font_output_list msg = (depth == 0) ? categories[menu_active_category].out : categories[menu_active_category].items[categories[menu_active_category].active_item].out; + lakka_draw_text(gl, msg, 15.0, 40.0, 1, 1.0); + + gl_set_viewport(gl, gl->win_width, gl->win_height, false, false); } static void lakka_init_assets(void *data) @@ -651,47 +724,204 @@ static void lakka_init_assets(void *data) if (!rgui) return; - menu_texture = (struct texture_image*)calloc(1, sizeof(*menu_texture)); - texture_image_load("/home/squarepusher/1391666767568.png", menu_texture); + gl_t *gl = (gl_t*)data; - texid = png_texture_load(menu_texture, &dim, &dim); + settings_icon = png_texture_load("/usr/share/retroarch/settings.png", &dim, &dim); + arrow_icon = png_texture_load("/usr/share/retroarch/arrow.png", &dim, &dim); + run_icon = png_texture_load("/usr/share/retroarch/run.png", &dim, &dim); + resume_icon = png_texture_load("/usr/share/retroarch/resume.png", &dim, &dim); + savestate_icon = png_texture_load("/usr/share/retroarch/savestate.png", &dim, &dim); + loadstate_icon = png_texture_load("/usr/share/retroarch/loadstate.png", &dim, &dim); + screenshot_icon = png_texture_load("/usr/share/retroarch/screenshot.png", &dim, &dim); + reload_icon = png_texture_load("/usr/share/retroarch/reload.png", &dim, &dim); - lakka_set_texture(rgui, true); + font_driver->render_msg(font, "Run", &run_label); + font_driver->render_msg(font, "Resume", &resume_label); +} + +void lakka_init_settings() +{ + gl_t *gl = (gl_t*)driver.video_data; + + menu_category mcat; + mcat.name = "Settings"; + mcat.icon = settings_icon; + mcat.alpha = 1.0; + mcat.zoom = C_ACTIVE_ZOOM; + mcat.active_item = 0; + mcat.num_items = 0; + mcat.items = calloc(mcat.num_items, sizeof(menu_item)); + struct font_output_list out; + font_driver->render_msg(font, mcat.name, &out); + mcat.out = out; + categories[0] = mcat; +} + +char * str_replace ( const char *string, const char *substr, const char *replacement ){ + char *tok = NULL; + char *newstr = NULL; + char *oldstr = NULL; + char *head = NULL; + + /* if either substr or replacement is NULL, duplicate string a let caller handle it */ + if ( substr == NULL || replacement == NULL ) return strdup (string); + newstr = strdup (string); + head = newstr; + while ( (tok = strstr ( head, substr ))){ + oldstr = newstr; + newstr = malloc ( strlen ( oldstr ) - strlen ( substr ) + strlen ( replacement ) + 1 ); + /*failed to alloc mem, free old string and return NULL */ + if ( newstr == NULL ){ + free (oldstr); + return NULL; + } + memcpy ( newstr, oldstr, tok - oldstr ); + memcpy ( newstr + (tok - oldstr), replacement, strlen ( replacement ) ); + memcpy ( newstr + (tok - oldstr) + strlen( replacement ), tok + strlen ( substr ), strlen ( oldstr ) - strlen ( substr ) - ( tok - oldstr ) ); + memset ( newstr + strlen ( oldstr ) - strlen ( substr ) + strlen ( replacement ) , 0, 1 ); + /* move back head right after the last replacement */ + head = newstr + (tok - oldstr) + strlen( replacement ); + free (oldstr); + } + return newstr; +} + +void lakka_init_items(int i, menu_category *mcat, core_info_t corenfo, char* gametexturepath, char* path) +{ + gl_t *gl = (gl_t*)driver.video_data; + + struct string_list *list = dir_list_new(path, corenfo.supported_extensions, true); + dir_list_sort(list, true); + + int num_items = list ? list->size : 0; + + for (int j = 0; j < num_items; j++) { + if (list->elems[j].attr.b) // is a directory + { + lakka_init_items(i, mcat, corenfo, gametexturepath, list->elems[j].data); + } + else + { + int n = mcat->num_items; + mcat->num_items++; + mcat->items = realloc(mcat->items, mcat->num_items * sizeof(menu_item)); + + mcat->items[n].name = path_basename(list->elems[j].data); + mcat->items[n].rom = list->elems[j].data; + mcat->items[n].icon = png_texture_load(gametexturepath, &dim, &dim); + mcat->items[n].alpha = i != menu_active_category ? 0 : n ? 0.5 : 1; + mcat->items[n].zoom = n ? I_PASSIVE_ZOOM : I_ACTIVE_ZOOM; + mcat->items[n].y = n ? VSPACING*(3+n) : VSPACING*2.5; + mcat->items[n].active_subitem = 0; + mcat->items[n].num_subitems = 5; + mcat->items[n].subitems = calloc(mcat->items[n].num_subitems, sizeof(menu_subitem)); + + for (int k = 0; k < mcat->items[n].num_subitems; k++) { + switch (k) { + case 0: + mcat->items[n].subitems[k].name = "Run"; + mcat->items[n].subitems[k].icon = run_icon; + break; + case 1: + mcat->items[n].subitems[k].name = "Save State"; + mcat->items[n].subitems[k].icon = savestate_icon; + break; + case 2: + mcat->items[n].subitems[k].name = "Load State"; + mcat->items[n].subitems[k].icon = loadstate_icon; + break; + case 3: + mcat->items[n].subitems[k].name = "Take Screenshot"; + mcat->items[n].subitems[k].icon = screenshot_icon; + break; + case 4: + mcat->items[n].subitems[k].name = "Reload"; + mcat->items[n].subitems[k].icon = reload_icon; + break; + } + mcat->items[n].subitems[k].alpha = 0; + mcat->items[n].subitems[k].zoom = k == mcat->items[n].active_subitem ? I_ACTIVE_ZOOM : I_PASSIVE_ZOOM; + mcat->items[n].subitems[k].y = k == 0 ? VSPACING*2.5 : VSPACING*(3+k); + struct font_output_list out; + font_driver->render_msg(font, mcat->items[n].subitems[k].name, &out); + mcat->items[n].subitems[k].out = out; + } + + struct font_output_list out; + font_driver->render_msg(font, mcat->items[n].name, &out); + mcat->items[n].out = out; + } + } } static void *lakka_init(void) { - uint16_t *framebuf = menu_framebuf; - size_t framebuf_pitch; - rgui_handle_t *rgui = (rgui_handle_t*)calloc(1, sizeof(*rgui)); - rgui->frame_buf = framebuf; - rgui->width = 320; - rgui->height = 240; - framebuf_pitch = rgui->width * sizeof(uint16_t); + gl_t *gl = (gl_t*)driver.video_data; - rgui->frame_buf_pitch = framebuf_pitch; + init_font(gl, g_settings.video.font_path, 6, 1440, 900); - bool ret = rguidisp_init_font(rgui); + menu_init_core_info(rgui); - if (!ret) - { - RARCH_ERR("No font bitmap or binary, abort"); - /* TODO - should be refactored - perhaps don't do rarch_fail but instead - * exit program */ - g_extern.lifecycle_state &= ~((1ULL << MODE_MENU) | (1ULL << MODE_GAME)); - return NULL; + rgui->core_info = core_info_list_new("/usr/lib/libretro"); + + num_categories = rgui->core_info ? rgui->core_info->count + 1 : 1; + + lakka_init_assets(gl); + + categories = realloc(categories, num_categories * sizeof(menu_category)); + + lakka_init_settings(); + + for (int i = 0; i < num_categories-1; i++) { + core_info_t corenfo = rgui->core_info->list[i]; + + char core_id[256]; + strcpy(core_id, basename(corenfo.path)); + strcpy(core_id, str_replace(core_id, ".so", "")); + strcpy(core_id, str_replace(core_id, ".dll", "")); + strcpy(core_id, str_replace(core_id, ".dylib", "")); + strcpy(core_id, str_replace(core_id, "-libretro", "")); + strcpy(core_id, str_replace(core_id, "_libretro", "")); + strcpy(core_id, str_replace(core_id, "libretro-", "")); + strcpy(core_id, str_replace(core_id, "libretro_", "")); + + char texturepath[256]; + strcpy(texturepath, "/usr/share/retroarch/"); + strcat(texturepath, core_id); + strcat(texturepath, ".png"); + + char gametexturepath[256]; + strcpy(gametexturepath, "/usr/share/retroarch/"); + strcat(gametexturepath, core_id); + strcat(gametexturepath, "-game.png"); + + menu_category mcat; + mcat.name = corenfo.display_name; + mcat.libretro = corenfo.path; + mcat.icon = png_texture_load(texturepath, &dim, &dim); + mcat.alpha = 0.5; + mcat.zoom = C_PASSIVE_ZOOM; + mcat.active_item = 0; + mcat.num_items = 0; + mcat.items = calloc(mcat.num_items, sizeof(menu_item)); + struct font_output_list out; + font_driver->render_msg(font, mcat.name, &out); + mcat.out = out; + + lakka_init_items(i+1, &mcat, corenfo, gametexturepath, g_settings.content_directory); + + categories[i+1] = mcat; } - - lakka_init_assets(rgui); - return rgui; + rgui->last_time = rarch_get_time_usec(); + + return NULL; } static void lakka_free_assets(void *data) { - texture_image_free(menu_texture); } static void lakka_free(void *data) @@ -721,7 +951,7 @@ static int lakka_input_postprocess(void *data, uint64_t old_state) const menu_ctx_driver_t menu_ctx_lakka = { lakka_set_texture, - lakka_render_messagebox, + NULL, lakka_render, NULL, lakka_init, @@ -742,7 +972,6 @@ const menu_ctx_driver_t menu_ctx_lakka = { NULL, NULL, NULL, - NULL, - NULL, + &menu_ctx_backend_lakka, "lakka", }; diff --git a/frontend/menu/disp/lakka.h b/frontend/menu/disp/lakka.h new file mode 100644 index 0000000000..9df07c65a3 --- /dev/null +++ b/frontend/menu/disp/lakka.h @@ -0,0 +1,38 @@ +void lakka_render(void *data); + +typedef struct +{ + char* name; + GLuint icon; + float alpha; + float zoom; + float y; + struct font_output_list out; +} menu_subitem; + +typedef struct +{ + char* name; + char* rom; + GLuint icon; + float alpha; + float zoom; + float y; + int active_subitem; + int num_subitems; + menu_subitem *subitems; + struct font_output_list out; +} menu_item; + +typedef struct +{ + char* name; + char* libretro; + GLuint icon; + float alpha; + float zoom; + int active_item; + int num_items; + menu_item *items; + struct font_output_list out; +} menu_category; diff --git a/frontend/menu/disp/png_texture_load.c b/frontend/menu/disp/png_texture_load.c new file mode 100644 index 0000000000..69b0945566 --- /dev/null +++ b/frontend/menu/disp/png_texture_load.c @@ -0,0 +1,172 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * Copyright (C) 2011-2013 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "png_texture_load.h" + +// thanks to https://github.com/DavidEGrayson/ahrs-visualizer/blob/master/png_texture.cpp +GLuint png_texture_load(const char * file_name, int * width, int * height) +{ + png_byte header[8]; + + FILE *fp = fopen(file_name, "rb"); + if (fp == 0) + { + perror(file_name); + return 0; + } + + // read the header + fread(header, 1, 8, fp); + + if (png_sig_cmp(header, 0, 8)) + { + fprintf(stderr, "error: %s is not a PNG.\n", file_name); + fclose(fp); + return 0; + } + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + fprintf(stderr, "error: png_create_read_struct returned 0.\n"); + fclose(fp); + return 0; + } + + // create png info struct + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + fprintf(stderr, "error: png_create_info_struct returned 0.\n"); + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + fclose(fp); + return 0; + } + + // create png info struct + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + fprintf(stderr, "error: png_create_info_struct returned 0.\n"); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); + fclose(fp); + return 0; + } + + // the code in this if statement gets called if libpng encounters an error + if (setjmp(png_jmpbuf(png_ptr))) { + fprintf(stderr, "error from libpng\n"); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + return 0; + } + + // init png reading + png_init_io(png_ptr, fp); + + // let libpng know you already read the first 8 bytes + png_set_sig_bytes(png_ptr, 8); + + // read all the info up to the image data + png_read_info(png_ptr, info_ptr); + + // variables to pass to get info + int bit_depth, color_type; + png_uint_32 temp_width, temp_height; + + // get info about png + png_get_IHDR(png_ptr, info_ptr, &temp_width, &temp_height, &bit_depth, &color_type, + NULL, NULL, NULL); + + if (width){ *width = temp_width; } + if (height){ *height = temp_height; } + + //printf("%s: %lux%lu %d\n", file_name, temp_width, temp_height, color_type); + + if (bit_depth != 8) + { + fprintf(stderr, "%s: Unsupported bit depth %d. Must be 8.\n", file_name, bit_depth); + return 0; + } + + GLint format; + switch(color_type) + { + case PNG_COLOR_TYPE_RGB: + format = GL_RGB; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + format = GL_RGBA; + break; + default: + fprintf(stderr, "%s: Unknown libpng color type %d.\n", file_name, color_type); + return 0; + } + + // Update the png info struct. + png_read_update_info(png_ptr, info_ptr); + + // Row size in bytes. + int rowbytes = png_get_rowbytes(png_ptr, info_ptr); + + // glTexImage2d requires rows to be 4-byte aligned + rowbytes += 3 - ((rowbytes-1) % 4); + + // Allocate the image_data as a big block, to be given to opengl + png_byte * image_data = (png_byte *)malloc(rowbytes * temp_height * sizeof(png_byte)+15); + if (image_data == NULL) + { + fprintf(stderr, "error: could not allocate memory for PNG image data\n"); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + return 0; + } + + // row_pointers is for pointing to image_data for reading the png with libpng + png_byte ** row_pointers = (png_byte **)malloc(temp_height * sizeof(png_byte *)); + if (row_pointers == NULL) + { + fprintf(stderr, "error: could not allocate memory for PNG row pointers\n"); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + free(image_data); + fclose(fp); + return 0; + } + + // set the individual row_pointers to point at the correct offsets of image_data + for (unsigned int i = 0; i < temp_height; i++) + { + row_pointers[temp_height - 1 - i] = image_data + i * rowbytes; + } + + // read the png into image_data through row_pointers + png_read_image(png_ptr, row_pointers); + + // Generate the OpenGL texture object + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, format, temp_width, temp_height, 0, format, GL_UNSIGNED_BYTE, image_data); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // clean up + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + free(image_data); + free(row_pointers); + fclose(fp); + return texture; +} \ No newline at end of file diff --git a/frontend/menu/disp/png_texture_load.h b/frontend/menu/disp/png_texture_load.h new file mode 100644 index 0000000000..e06cb67102 --- /dev/null +++ b/frontend/menu/disp/png_texture_load.h @@ -0,0 +1,21 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * Copyright (C) 2011-2013 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "../../../gfx/gl_common.h" +#include "../../../gfx/gfx_common.h" +#include + +GLuint png_texture_load(const char *, int *, int *); \ No newline at end of file diff --git a/frontend/menu/disp/tween.c b/frontend/menu/disp/tween.c new file mode 100644 index 0000000000..380b680978 --- /dev/null +++ b/frontend/menu/disp/tween.c @@ -0,0 +1,75 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * Copyright (C) 2011-2013 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include "tween.h" + +tween* tweens = NULL; +int numtweens = 0; + +float inOutQuad(float t, float b, float c, float d) +{ + t = t / d * 2; + if (t < 1) + return c / 2 * pow(t, 2) + b; + return -c / 2 * ((t - 1) * (t - 3) - 1) + b; +} + +void add_tween(float duration, float target_value, float* subject, easingFunc easing) { + numtweens++; + tweens = realloc(tweens, numtweens * sizeof(tween)); + tweens[numtweens-1].alive = 1; + tweens[numtweens-1].duration = duration; + tweens[numtweens-1].running_since = 0; + tweens[numtweens-1].initial_value = *subject; + tweens[numtweens-1].target_value = target_value; + tweens[numtweens-1].subject = subject; + tweens[numtweens-1].easing = easing; +} + +void update_tweens(float dt) +{ + int active_tweens = 0; + for(int i = 0; i < numtweens; i++) + { + tweens[i] = update_tween(tweens[i], dt); + active_tweens += tweens[i].running_since < tweens[i].duration ? 1 : 0; + } + if (numtweens && !active_tweens) { + numtweens = 0; + } +} + +tween update_tween(tween tw, float dt) +{ + if (tw.running_since < tw.duration) { + tw.running_since += dt; + *(tw.subject) = tw.easing( + tw.running_since, + tw.initial_value, + tw.target_value - tw.initial_value, + tw.duration); + if (tw.running_since >= tw.duration) + *(tw.subject) = tw.target_value; + } + return tw; +} + +void free_tweens() +{ + free(tweens); +} diff --git a/frontend/menu/disp/tween.h b/frontend/menu/disp/tween.h new file mode 100644 index 0000000000..35446e9dd1 --- /dev/null +++ b/frontend/menu/disp/tween.h @@ -0,0 +1,37 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * Copyright (C) 2011-2013 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "math.h" + +typedef float (*easingFunc)(float, float, float, float); + +float inOutQuad(float, float, float, float); + +typedef struct +{ + int alive; + float duration; + float running_since; + float initial_value; + float target_value; + float* subject; + easingFunc easing; +} tween; + +tween update_tween(tween, float); +void update_tweens(float); +void add_tween(float, float, float*, easingFunc); +void free_tweens(); \ No newline at end of file diff --git a/gfx/gl.c b/gfx/gl.c index 881bd2bf19..35516cb1d1 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -49,6 +49,10 @@ #include "shader_glsl.h" #endif +#ifdef HAVE_LAKKA +#include "../frontend/menu/disp/lakka.h" +#endif + #include "shader_common.h" // Used for the last pass when rendering to the back buffer. @@ -1515,7 +1519,11 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei #if defined(HAVE_MENU) if (gl->rgui_texture_enable) - gl_draw_texture(gl); + #if defined(HAVE_LAKKA) + lakka_render(gl); + #else + gl_draw_texture(gl); + #endif #endif if (msg && gl->font_ctx) diff --git a/media/lakka/fceu-game.png b/media/lakka/fceu-game.png new file mode 100644 index 0000000000..f8ed60da39 Binary files /dev/null and b/media/lakka/fceu-game.png differ diff --git a/media/lakka/fceu.png b/media/lakka/fceu.png new file mode 100644 index 0000000000..15954e4bf0 Binary files /dev/null and b/media/lakka/fceu.png differ diff --git a/media/lakka/fceumm-game.png b/media/lakka/fceumm-game.png new file mode 100644 index 0000000000..f8ed60da39 Binary files /dev/null and b/media/lakka/fceumm-game.png differ diff --git a/media/lakka/fceumm.png b/media/lakka/fceumm.png new file mode 100644 index 0000000000..15954e4bf0 Binary files /dev/null and b/media/lakka/fceumm.png differ diff --git a/media/lakka/gambatte-game.png b/media/lakka/gambatte-game.png new file mode 100644 index 0000000000..5421aa9e99 Binary files /dev/null and b/media/lakka/gambatte-game.png differ diff --git a/media/lakka/gambatte.png b/media/lakka/gambatte.png new file mode 100644 index 0000000000..5cd19e4309 Binary files /dev/null and b/media/lakka/gambatte.png differ diff --git a/media/lakka/genplus-game.png b/media/lakka/genplus-game.png new file mode 100644 index 0000000000..c4e6b6cfa7 Binary files /dev/null and b/media/lakka/genplus-game.png differ diff --git a/media/lakka/genplus.png b/media/lakka/genplus.png new file mode 100644 index 0000000000..dc353be932 Binary files /dev/null and b/media/lakka/genplus.png differ diff --git a/media/lakka/imame4all-game.png b/media/lakka/imame4all-game.png new file mode 100644 index 0000000000..aaa7f41950 Binary files /dev/null and b/media/lakka/imame4all-game.png differ diff --git a/media/lakka/imame4all.png b/media/lakka/imame4all.png new file mode 100644 index 0000000000..ee9f8f52c4 Binary files /dev/null and b/media/lakka/imame4all.png differ diff --git a/media/lakka/loadstate.png b/media/lakka/loadstate.png new file mode 100644 index 0000000000..cb425a38b7 Binary files /dev/null and b/media/lakka/loadstate.png differ diff --git a/media/lakka/mame078-game.png b/media/lakka/mame078-game.png new file mode 100644 index 0000000000..aaa7f41950 Binary files /dev/null and b/media/lakka/mame078-game.png differ diff --git a/media/lakka/mame078.png b/media/lakka/mame078.png new file mode 100644 index 0000000000..ee9f8f52c4 Binary files /dev/null and b/media/lakka/mame078.png differ diff --git a/media/lakka/mednafen-psx-game.png b/media/lakka/mednafen-psx-game.png new file mode 100644 index 0000000000..6fe56c163c Binary files /dev/null and b/media/lakka/mednafen-psx-game.png differ diff --git a/media/lakka/mednafen-psx.png b/media/lakka/mednafen-psx.png new file mode 100644 index 0000000000..15c91b795a Binary files /dev/null and b/media/lakka/mednafen-psx.png differ diff --git a/media/lakka/nx.png b/media/lakka/nx.png new file mode 100644 index 0000000000..6af096a23c Binary files /dev/null and b/media/lakka/nx.png differ diff --git a/media/lakka/nxengine.png b/media/lakka/nxengine.png new file mode 100644 index 0000000000..6af096a23c Binary files /dev/null and b/media/lakka/nxengine.png differ diff --git a/media/lakka/picodrive-game.png b/media/lakka/picodrive-game.png new file mode 100644 index 0000000000..c4e6b6cfa7 Binary files /dev/null and b/media/lakka/picodrive-game.png differ diff --git a/media/lakka/picodrive.png b/media/lakka/picodrive.png new file mode 100644 index 0000000000..dc353be932 Binary files /dev/null and b/media/lakka/picodrive.png differ diff --git a/media/lakka/pocketsnes-game.png b/media/lakka/pocketsnes-game.png new file mode 100644 index 0000000000..cd5cc5c823 Binary files /dev/null and b/media/lakka/pocketsnes-game.png differ diff --git a/media/lakka/pocketsnes.png b/media/lakka/pocketsnes.png new file mode 100644 index 0000000000..4f801db647 Binary files /dev/null and b/media/lakka/pocketsnes.png differ diff --git a/media/lakka/reload.png b/media/lakka/reload.png new file mode 100644 index 0000000000..b9f556065e Binary files /dev/null and b/media/lakka/reload.png differ diff --git a/media/lakka/resume.png b/media/lakka/resume.png new file mode 100644 index 0000000000..398043479b Binary files /dev/null and b/media/lakka/resume.png differ diff --git a/media/lakka/run.png b/media/lakka/run.png new file mode 100644 index 0000000000..6b5177b877 Binary files /dev/null and b/media/lakka/run.png differ diff --git a/media/lakka/savestate.png b/media/lakka/savestate.png new file mode 100644 index 0000000000..e865e460cf Binary files /dev/null and b/media/lakka/savestate.png differ diff --git a/media/lakka/screenshot.png b/media/lakka/screenshot.png new file mode 100644 index 0000000000..a3de3ecca6 Binary files /dev/null and b/media/lakka/screenshot.png differ diff --git a/media/lakka/setting.png b/media/lakka/setting.png new file mode 100644 index 0000000000..6e581f27c3 Binary files /dev/null and b/media/lakka/setting.png differ diff --git a/media/lakka/settings.png b/media/lakka/settings.png new file mode 100644 index 0000000000..89a59f08e5 Binary files /dev/null and b/media/lakka/settings.png differ diff --git a/media/lakka/snes9x-next-game.png b/media/lakka/snes9x-next-game.png new file mode 100644 index 0000000000..cd5cc5c823 Binary files /dev/null and b/media/lakka/snes9x-next-game.png differ diff --git a/media/lakka/snes9x-next.png b/media/lakka/snes9x-next.png new file mode 100644 index 0000000000..4f801db647 Binary files /dev/null and b/media/lakka/snes9x-next.png differ diff --git a/qb/config.params.sh b/qb/config.params.sh index fc239642b5..d97b8f29c6 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -1,4 +1,5 @@ HAVE_RGUI=yes # Disable RGUI +HAVE_LAKKA=yes # Disable Lakka HAVE_DYNAMIC=yes # Disable dynamic loading of libretro library HAVE_SDL=auto # SDL support HAVE_UDEV=auto # Udev/Evdev gamepad support