diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 54 | ||||
-rw-r--r-- | README.funkey-s.md | 58 | ||||
-rw-r--r-- | cheat.c | 9 | ||||
-rw-r--r-- | core.c | 25 | ||||
-rw-r--r-- | core.h | 3 | ||||
-rw-r--r-- | funkey/fk_instant_play.c | 90 | ||||
-rw-r--r-- | funkey/fk_instant_play.h | 50 | ||||
-rw-r--r-- | funkey/fk_menu.c | 1689 | ||||
-rw-r--r-- | funkey/fk_menu.h | 170 | ||||
-rw-r--r-- | main.c | 136 | ||||
-rw-r--r-- | main.h | 13 | ||||
-rw-r--r-- | menu.c | 41 | ||||
-rw-r--r-- | options.c | 5 | ||||
-rw-r--r-- | overrides.c | 2 | ||||
-rw-r--r-- | overrides/gpsp.h | 2 | ||||
-rw-r--r-- | overrides/mame2000.h | 2 | ||||
-rw-r--r-- | overrides/pcsx_rearmed.h | 2 | ||||
-rw-r--r-- | overrides/picodrive.h | 2 | ||||
-rw-r--r-- | overrides/snes9x2002.h | 2 | ||||
-rw-r--r-- | overrides/snes9x2005.h | 7 | ||||
-rw-r--r-- | overrides/snes9x2005_plus.h | 75 | ||||
-rw-r--r-- | patches/libpicofe/0002-small-screen.patch | 13 | ||||
-rw-r--r-- | patches/snes9x2005/1001-funkey-s-support.patch | 93 | ||||
-rw-r--r-- | patches/snes9x2005_plus/1001-funkey-s-support.patch | 93 | ||||
-rw-r--r-- | plat.h | 2 | ||||
-rw-r--r-- | plat_funkey.c | 115 | ||||
-rw-r--r-- | scale.c | 46 | ||||
-rw-r--r-- | scale.h | 12 |
29 files changed, 2751 insertions, 61 deletions
@@ -21,3 +21,4 @@ picoarch /smsplus-gx /snes9x2002 /snes9x2005 +/snes9x2005_plus @@ -1,5 +1,6 @@ # Global definitions platform ?= unix +core_platform ?= $(platform) CC = $(CROSS_COMPILE)gcc SYSROOT = $(shell $(CC) --print-sysroot) @@ -19,7 +20,12 @@ LDFLAGS = -lc -ldl -lgcc -lm -lSDL -lasound -lpng -lz -Wl,--gc-sections -flto # Unpolished or slow cores that build # EXTRA_CORES += fbalpha2012 # EXTRA_CORES += mame2003_plus -CORES = beetle-pce-fast bluemsx fceumm fmsx gambatte gme gpsp mame2000 pcsx_rearmed picodrive quicknes smsplus-gx snes9x2002 snes9x2005 $(EXTRA_CORES) + +CORES = beetle-pce-fast bluemsx fceumm fmsx gambatte gme gpsp mame2000 pcsx_rearmed picodrive quicknes smsplus-gx snes9x2002 snes9x2005 $(EXTRA_CORES) + +ifeq ($(platform), funkey-s) +CORES := $(CORES) snes9x2005_plus +endif beetle-pce-fast_REPO = https://github.com/libretro/beetle-pce-fast-libretro beetle-pce-fast_CORE = mednafen_pce_fast_libretro.so @@ -53,11 +59,20 @@ smsplus-gx_CORE = smsplus_libretro.so snes9x2005_REPO = https://git.crowdedwood.com/snes9x2005 snes9x2005_REVISION = performance +snes9x2005_plus_REPO = https://git.crowdedwood.com/snes9x2005 +snes9x2005_plus_REVISION = performance +snes9x2005_plus_FLAGS = USE_BLARGG_APU=1 + ifeq ($(platform), trimui) OBJS += plat_trimui.o CFLAGS += -mcpu=arm926ej-s -mtune=arm926ej-s -fno-PIC -DCONTENT_DIR='"/mnt/SDCARD/Roms"' LDFLAGS += -fno-PIC - +else ifeq ($(platform), funkey-s) + OBJS += plat_funkey.o funkey/fk_menu.o funkey/fk_instant_play.o + CFLAGS += -DCONTENT_DIR='"/mnt"' -DFUNKEY_S + LDFLAGS += -fPIC + LDFLAGS += -lSDL_image -lSDL_ttf # For fk_menu + core_platform = classic_armv7_a7 else ifeq ($(platform), unix) OBJS += plat_linux.o LDFLAGS += -fPIE @@ -105,12 +120,13 @@ print-%: all: $(BIN) cores libpicofe/.patched: - cd libpicofe && git apply -p1 < ../patches/libpicofe/0001-key-combos.patch && touch .patched + cd libpicofe && git apply -p1 < ../patches/libpicofe/0001-key-combos.patch && git apply -p1 < ../patches/libpicofe/0002-small-screen.patch && touch .patched clean-libpicofe: - test ! -f libpicofe/.patched || (cd libpicofe && git apply -p1 -R < ../patches/libpicofe/0001-key-combos.patch && rm .patched) + test ! -f libpicofe/.patched || (cd libpicofe && git apply -p1 -R < ../patches/libpicofe/0002-small-screen.patch && git apply -p1 -R < ../patches/libpicofe/0001-key-combos.patch && rm .patched) plat_trimui.o: plat_sdl.c +plat_funkey.o: plat_sdl.c plat_linux.o: plat_sdl.c $(BIN): libpicofe/.patched $(OBJS) @@ -122,7 +138,7 @@ $1_REPO ?= https://github.com/libretro/$(1)/ $1_BUILD_PATH ?= $(1) -$1_MAKE = make $(and $($1_MAKEFILE),-f $($1_MAKEFILE)) platform=$(platform) $(and $(DEBUG),DEBUG=$(DEBUG)) $(and $(PROFILE),PROFILE=$(PROFILE)) $($(1)_FLAGS) +$1_MAKE = make $(and $($1_MAKEFILE),-f $($1_MAKEFILE)) platform=$(core_platform) $(and $(DEBUG),DEBUG=$(DEBUG)) $(and $(PROFILE),PROFILE=$(PROFILE)) $($(1)_FLAGS) $(1): git clone $(if $($1_REVISION),,--depth 1) --recursive $$($(1)_REPO) $(1) @@ -338,3 +354,31 @@ picoarch.zip: cd pkg && zip -r ../picoarch.zip * endif # platform=trimui + +ifeq ($(platform), funkey-s) + +define picoarch_DESKTOP +[Desktop Entry] +Name=picoarch +Comment=Small screen libretro frontend +Exec=picoarch +Icon= +Terminal=false +Type=Application +StartupNotify=true +Categories=emulators; +endef + +picoarch.opk: $(BIN) cores + mkdir -p ".opkdata" + $(file >default.funkey-s.desktop,$(picoarch_DESKTOP)) + mv default.funkey-s.desktop ".opkdata" + cp $(BIN) $(SOFILES) ".opkdata" + cd .opkdata && mksquashfs * ../picoarch.opk -all-root -no-xattrs -noappend -no-exports + rm -r .opkdata + +picoarch-funkey-s.zip: picoarch.opk + rm -f picoarch-funkey-s.zip + zip picoarch-funkey-s.zip README.funkey-s.md picoarch.opk + +endif # platform=funkey-s diff --git a/README.funkey-s.md b/README.funkey-s.md new file mode 100644 index 0000000..0513f5a --- /dev/null +++ b/README.funkey-s.md @@ -0,0 +1,58 @@ +# picoarch - a libretro frontend designed for small screens and low power + +picoarch runs libretro cores (emulators) for various systems with low overhead and UI designed for small screen, low-powered devices like the Trimui Model S (PowKiddy A66) and Funkey S. + +It supports: + +- **Arcade** (mame2000) +- **Colecovision** (blueMSX, smsplus) +- **Game Boy / Game Boy Color** (gambatte) +- **Game Boy Advance** (gpsp) +- **Game Gear** (picodrive, smsplus) +- **Genesis** (picodrive) +- **MSX** (fMSX, blueMSX) +- **NES** (quicknes, fceumm) +- **Sega Master System** (picodrive, smsplus) +- **Super NES** (snes9x2002, snes9x2005, snes9x2005_plus) +- **PCE / TurboGrafx-16** (beetle-pce-fast) +- **PlayStation** (pcsx_rearmed) +- more to come + +picoarch can also play game music (gme). + +All emulators have: + +- FunKey menu +- fast-forward +- soft scaling options +- per-game config +- screenshots + +Most have: +- FunKey Instant Play (close / reopen device, requires core with save states) +- autosave and resume (requires core with save states) +- cheat support +- IPS/BPS softpatching +- auto-frameskip for smooth audio + +## Install + +Mount USB and copy picoarch.opk into Emulators directory. + +## Notes / extra features + +### BIOS + +Some emulators require bios files. bios files are placed into `/mnt/FunKey/.picoarch/system`. This directory is created after first launch. + +The libretro documentation specifies which bios is required for each core. For example, needed fMSX bios files are listed here: <https://docs.libretro.com/library/fmsx/> + +### Cheats + +Cheats use RetroArch .cht file format. Many cheat files are here <https://github.com/libretro/libretro-database/tree/master/cht> + +Cheat file name needs to match ROM name, and go underneath save directory. For example, `/Apps/.picoarch-gambatte/cheats/Super Mario Land (World).cht`. When a cheat file is detected, a "cheats" menu item will appear in advanced menu. Not all cheats work with all cores, may want to clean up files to just the cheats you want. + +### IPS / BPS soft-patching + +Many cores can apply patches when loading. For example, loading `/roms/game.gba` will apply patches named `/roms/game.ips`, `/roms/game.ips1`, `/roms/game.IPS2`, `/roms/game.bps`, etc. Patching is temporary, original files are unmodified. Patches are loaded in case-insensitive alphabetical order. Note that `.ips12` loads before `.ips2`, but after `.ips02`. @@ -6,9 +6,14 @@ #include "main.h" #include "util.h" +#define MAX_LINE_LEN SCREEN_WIDTH / 6 + +#if SCREEN_WIDTH == 240 +#define MAX_DESC_LEN 22 +#else #define MAX_DESC_LEN 27 -#define MAX_LINE_LEN 52 -#define MAX_LINES 3 +#endif +#define MAX_LINES 4 static size_t parse_count(FILE *file) { size_t count = 0; @@ -42,9 +42,11 @@ static int core_load_game_info(struct content *content, struct retro_game_info * return content_load_game_info(content, game_info, info.need_fullpath); } -void config_file_name(char *buf, size_t len, int is_game) +void config_file_name(char *buf, size_t len, config_type config_type) { - if (is_game && content) { + if (config_type == CONFIG_TYPE_AUTO) { + snprintf(buf, len, "%s%s", config_dir, "picoarch-auto.cfg"); + } else if (config_type == CONFIG_TYPE_GAME && content) { content_based_name(content, buf, len, save_dir, NULL, ".cfg"); } else { snprintf(buf, len, "%s%s", config_dir, "picoarch.cfg"); @@ -115,12 +117,18 @@ bool state_allowed(void) { } void state_file_name(char *name, size_t size, int slot) { - char extension[5] = {0}; + char extension[6] = {0}; - snprintf(extension, 5, ".st%d", slot); + snprintf(extension, 6, ".st%d", slot); content_based_name(content, name, MAX_PATH, save_dir, NULL, extension); } +bool state_exists(int slot) { + char fname[MAX_PATH]; + state_file_name(fname, sizeof(fname), slot); + return access(fname, F_OK) == 0; +} + int state_read(void) { char filename[MAX_PATH]; FILE *state_file = NULL; @@ -287,6 +295,15 @@ static void set_directories(const char *core_name) { #ifdef MINUI strncpy(system_dir, save_dir, MAX_PATH-1); #else + +#ifdef FUNKEY_S + if (home != NULL) { + snprintf(system_dir, MAX_PATH, "%s/.picoarch", home); + mkdir(system_dir, 0755); + snprintf(system_dir, MAX_PATH, "%s/.picoarch/system", home); + mkdir(system_dir, 0755); + } else +#endif if (getcwd(cwd, MAX_PATH)) { snprintf(system_dir, MAX_PATH, "%s/system", cwd); mkdir(system_dir, 0755); @@ -43,7 +43,7 @@ extern unsigned audio_buffer_size_override; extern int state_slot; extern int resume_slot; -void config_file_name(char *buf, size_t len, int is_game); +void config_file_name(char *buf, size_t len, config_type config_type); void save_relative_path(char *buf, size_t len, const char *basename); void sram_read(void); @@ -51,6 +51,7 @@ void sram_write(void); bool state_allowed(void); void state_file_name(char *name, size_t size, int slot); +bool state_exists(int slot); int state_read(void); int state_write(void); int state_resume(void); diff --git a/funkey/fk_instant_play.c b/funkey/fk_instant_play.c new file mode 100644 index 0000000..eeacae8 --- /dev/null +++ b/funkey/fk_instant_play.c @@ -0,0 +1,90 @@ +/* + FK - FunKey retro gaming console library + Copyright (C) 2020-2021 Vincent Buso + Copyright (C) 2020-2021 Michel Stempin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Vincent Buso + vincent.buso@funkey-project.com + Michel Stempin + michel.stempin@funkey-project.com +*/ + +/** + * @file FK_Instant_Play.c + * This is the Instant Play API for the FunKey retro gaming console library + */ + +#include <stdio.h> +#include <unistd.h> +#include "fk_menu.h" +#include "fk_instant_play.h" +#include "core.h" + +#ifndef SHELL_CMD_POWERDOWN +#define SHELL_CMD_POWERDOWN "shutdown_funkey" +#define SHELL_CMD_SCHEDULE_POWERDOWN "sched_shutdown" +#define SHELL_CMD_CANCEL_SCHED_POWERDOWN "cancel_sched_powerdown" +#endif + +static char *prog_name; + +/* Handler for SIGUSR1, caused by closing the console */ +static void handle_sigusr1(int signal) +{ + FILE *fp; + + printf("Caught signal USR1(%d)\n", signal); + + /* Exit menu if it was launched */ + FK_EndMenu(); + + /* Send command to cancel any previously scheduled powerdown */ + fp = popen(SHELL_CMD_CANCEL_SCHED_POWERDOWN, "r"); + if (fp == NULL) + { + /* Countdown is still ticking, so better do nothing + than start writing and get interrupted! + */ + printf("Failed to cancel scheduled shutdown\n"); + exit(0); + } + pclose(fp); + + state_slot = AUTOSAVE_SLOT; + if(!state_write()) { + printf("Save failed"); + state_slot = 0; + } + + save_config(CONFIG_TYPE_AUTO); + + /* Perform Instant Play save and shutdown */ + execlp(SHELL_CMD_INSTANT_PLAY, SHELL_CMD_INSTANT_PLAY, "save", prog_name, core_path, content->path, NULL); + + /* Should not be reached */ + printf("Failed to perform shutdown\n"); + + /* Exit application */ + exit(0); +} + +void FK_InitInstantPlay(int argc, char **argv) +{ + prog_name = argv[0]; + signal(SIGUSR1, handle_sigusr1); +} + diff --git a/funkey/fk_instant_play.h b/funkey/fk_instant_play.h new file mode 100644 index 0000000..b3e3845 --- /dev/null +++ b/funkey/fk_instant_play.h @@ -0,0 +1,50 @@ +/* + FK - FunKey retro gaming console library + Copyright (C) 2020-2021 Vincent Buso + Copyright (C) 2020-2021 Michel Stempin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Vincent Buso + vincent.buso@funkey-project.com + Michel Stempin + michel.stempin@funkey-project.com +*/ + +/** + * @file FK_Instant_Play.h + * This is the Instant Play API for the FunKey retro gaming console library + */ + +#ifndef _FK_instant_play_h +#define _FK_instant_play_h + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +#include <signal.h> + +#define AUTOSAVE_SLOT 99 + +extern void FK_InitInstantPlay(int argc, char **argv); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif + +#endif /* _FK_instant_play_h */ diff --git a/funkey/fk_menu.c b/funkey/fk_menu.c new file mode 100644 index 0000000..8323d21 --- /dev/null +++ b/funkey/fk_menu.c @@ -0,0 +1,1689 @@ +/* + FK - FunKey retro gaming console library + Copyright (C) 2020-2021 Vincent Buso + Copyright (C) 2020-2021 Michel Stempin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Vincent Buso + vincent.buso@funkey-project.com + Michel Stempin + michel.stempin@funkey-project.com +*/ + +/** + * @file FK_menu.c + * This is the menu API for the FunKey retro gaming console library + */ + +#include "fk_menu.h" +#include "main.h" +#include "core.h" +#include "scale.h" +#include "plat.h" + +/// -------------- DEFINES -------------- + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +//#define MENU_DEBUG +#define MENU_ERROR + +#ifdef MENU_DEBUG +#define MENU_DEBUG_PRINTF(...) printf(__VA_ARGS__); +#else +#define MENU_DEBUG_PRINTF(...) +#endif //MENU_DEBUG + +#ifdef MENU_ERROR +#define MENU_ERROR_PRINTF(...) printf(__VA_ARGS__); +#else +#define MENU_ERROR_PRINTF(...) +#endif //MENU_ERROR + +#define SCREEN_HORIZONTAL_SIZE 240 //RES_HW_SCREEN_HORIZONTAL +#define SCREEN_VERTICAL_SIZE 240 //RES_HW_SCREEN_VERTICAL + +#define SCROLL_SPEED_PX 30 +#define FPS_MENU 60 +#define ARROWS_PADDING 8 + +#define MENU_ZONE_WIDTH SCREEN_HORIZONTAL_SIZE +#define MENU_ZONE_HEIGHT SCREEN_VERTICAL_SIZE +#define MENU_BG_SQUARE_WIDTH 180 +#define MENU_BG_SQUARE_HEIGHT 140 + +#define MENU_FONT_NAME_TITLE "/usr/games/menu_resources/OpenSans-Bold.ttf" +#define MENU_FONT_SIZE_TITLE 22 +#define MENU_FONT_NAME_INFO "/usr/games/menu_resources/OpenSans-Bold.ttf" +#define MENU_FONT_SIZE_INFO 16 +#define MENU_FONT_NAME_SMALL_INFO "/usr/games/menu_resources/OpenSans-Regular.ttf" +#define MENU_FONT_SIZE_SMALL_INFO 13 +#define MENU_PNG_BG_PATH "/usr/games/menu_resources/zone_bg.png" +#define MENU_PNG_ARROW_TOP_PATH "/usr/games/menu_resources/arrow_top.png" +#define MENU_PNG_ARROW_BOTTOM_PATH "/usr/games/menu_resources/arrow_bottom.png" + +#define GRAY_MAIN_R 85 +#define GRAY_MAIN_G 85 +#define GRAY_MAIN_B 85 +#define WHITE_MAIN_R 236 +#define WHITE_MAIN_G 236 +#define WHITE_MAIN_B 236 + +#define MAX_SAVE_SLOTS 9 + +#define MAXPATHLEN 512 + + +/// -------------- STATIC VARIABLES -------------- +static SDL_Surface * background_screen = NULL; +static int backup_key_repeat_delay=0; +static int backup_key_repeat_interval=0; +static TTF_Font *menu_title_font = NULL; +static TTF_Font *menu_info_font = NULL; +static TTF_Font *menu_small_info_font = NULL; +static SDL_Surface *img_arrow_top = NULL; +static SDL_Surface *img_arrow_bottom = NULL; +static SDL_Surface ** menu_zone_surfaces = NULL; +static int *idx_menus = NULL; +static int nb_menu_zones = 0; +static int menuItem=0; +static int stop_menu_loop = 0; + +static SDL_Color text_color = {GRAY_MAIN_R, GRAY_MAIN_G, GRAY_MAIN_B}; +static int padding_y_from_center_menu_zone = 18; +static uint16_t width_progress_bar = 100; +static uint16_t height_progress_bar = 20; + +#ifdef HAS_MENU_VOLUME +static uint16_t x_volume_bar = 0; +static uint16_t y_volume_bar = 0; +static int volume_percentage = 0; +#endif +#ifdef HAS_MENU_BRIGHTNESS +static uint16_t x_brightness_bar = 0; +static uint16_t y_brightness_bar = 0; +static int brightness_percentage = 0; +#endif + +#ifdef HAS_MENU_ASPECT_RATIO +#undef X +#define X(a, b) b, +static const char *aspect_ratio_name[] = {ASPECT_RATIOS}; +static int menu_aspect_ratio = ASPECT_RATIOS_TYPE_STRETCHED; +/* static int aspect_ratio_factor_percent = 50; + * static int aspect_ratio_factor_step = 10; */ +#endif + +#ifdef HAS_MENU_THEME +static Configuration *config = NULL; +static int indexChooseLayout = 0; +#endif + +#undef X +#define X(a, b) b, +const char *resume_options_str[] = {RESUME_OPTIONS}; + +/* #if defined(HAS_MENU_SAVE) || defined (HAS_MENU_LOAD) + * static int savestate_slot = 0; + * #endif */ + +#ifdef HAS_MENU_USB +/// USB stuff +static int usb_data_connected = 0; +static int usb_sharing = 0; +#endif + +#ifdef HAS_MENU_RO_RW +static int read_write = 0; +#endif + +/// -------------- FUNCTIONS IMPLEMENTATION -------------- + +#if defined(HAS_MENU_VOLUME) || defined(HAS_MENU_BRIGHTNESS) +static void draw_progress_bar(SDL_Surface * surface, uint16_t x, uint16_t y, uint16_t width, + uint16_t height, uint8_t percentage, uint16_t nb_bars) +{ + /// ------ Init Variables ------ + uint16_t line_width = 1; //px + uint16_t padding_bars_ratio = 3; + uint16_t nb_full_bars = 0; + + /// ------ Check values ------ + percentage = (percentage > 100)?100:percentage; + x = (x > (surface->w-1))?(surface->w-1):x; + y = (y > surface->h-1)?(surface->h-1):y; + width = (width < line_width*2+1)?(line_width*2+1):width; + width = (width > surface->w-x-1)?(surface->w-x-1):width; + height = (height < line_width*2+1)?(line_width*2+1):height; + height = (height > surface->h-y-1)?(surface->h-y-1):height; + uint16_t nb_bars_max = ( width * padding_bars_ratio / (line_width*2+1) + 1 ) / (padding_bars_ratio+1); + nb_bars = (nb_bars > nb_bars_max)?nb_bars_max:nb_bars; + uint16_t bar_width = (width / nb_bars)*padding_bars_ratio/(padding_bars_ratio+1)+1; + uint16_t bar_padding_x = bar_width/padding_bars_ratio; + nb_full_bars = nb_bars*percentage/100; + + /// ------ draw full bars ------ + for (int i = 0; i < nb_full_bars; ++i) + { + /// ---- draw one bar ---- + //MENU_DEBUG_PRINTF("Drawing filled bar %d\n", i); + SDL_Rect rect = {x+ i*(bar_width +bar_padding_x), + y, bar_width, height}; + SDL_FillRect(surface, &rect, SDL_MapRGB(surface->format, GRAY_MAIN_R, GRAY_MAIN_G, GRAY_MAIN_B)); + } + + /// ------ draw full bars ------ + for (int i = 0; i < (nb_bars-nb_full_bars); ++i) + { + /// ---- draw one bar ---- + //MENU_DEBUG_PRINTF("Drawing empty bar %d\n", i); + SDL_Rect rect = {x+ i*(bar_width +bar_padding_x) + nb_full_bars*(bar_width +bar_padding_x), + y, bar_width, height}; + SDL_FillRect(surface, &rect, SDL_MapRGB(surface->format, GRAY_MAIN_R, GRAY_MAIN_G, GRAY_MAIN_B)); + + SDL_Rect rect2 = {x+ i*(bar_width +bar_padding_x) + line_width + nb_full_bars*(bar_width +bar_padding_x), + y + line_width, bar_width - line_width*2, height - line_width*2}; + SDL_FillRect(surface, &rect2, SDL_MapRGB(surface->format, WHITE_MAIN_R, WHITE_MAIN_R, WHITE_MAIN_R)); + } + + +} +#endif + +#if defined(HAS_MENU_ASPECT_RATIO) +static void read_aspect_ratio(void) +{ + switch (scale_size) { + case SCALE_SIZE_FULL: + menu_aspect_ratio = ASPECT_RATIOS_TYPE_STRETCHED; + break; + case SCALE_SIZE_ASPECT: + menu_aspect_ratio = ASPECT_RATIOS_TYPE_SCALED; + break; + case SCALE_SIZE_CROP: + menu_aspect_ratio = ASPECT_RATIOS_TYPE_CROPPED; + break; + case SCALE_SIZE_NONE: + menu_aspect_ratio = ASPECT_RATIOS_TYPE_NONE; + break; + } +} + +static void update_aspect_ratio(void) +{ + switch (menu_aspect_ratio) { + case ASPECT_RATIOS_TYPE_STRETCHED: + scale_size = SCALE_SIZE_FULL; + scale_filter = SCALE_FILTER_SMOOTH; + break; + case ASPECT_RATIOS_TYPE_SCALED: + scale_size = SCALE_SIZE_ASPECT; + scale_filter = SCALE_FILTER_SHARP; + break; + case ASPECT_RATIOS_TYPE_CROPPED: + scale_size = SCALE_SIZE_CROP; + scale_filter = SCALE_FILTER_SMOOTH; + break; + case ASPECT_RATIOS_TYPE_NONE: + scale_size = SCALE_SIZE_NONE; + scale_filter = SCALE_FILTER_NEAREST; + break; + } + scale_update_scaler(); +} +#endif + +static void add_menu_zone(ENUM_MENU_TYPE menu_type) +{ + /// ------ Increase nb of menu zones ------- + nb_menu_zones++; + + /// ------ Realoc idx Menus array ------- + if(!idx_menus){ + idx_menus = (int*) malloc(nb_menu_zones*sizeof(int)); + menu_zone_surfaces = (SDL_Surface**) malloc(nb_menu_zones*sizeof(SDL_Surface*)); + } + else{ + int *temp = (int*) realloc(idx_menus, nb_menu_zones*sizeof(int)); + idx_menus = temp; + menu_zone_surfaces = (SDL_Surface**) realloc(menu_zone_surfaces, nb_menu_zones*sizeof(SDL_Surface*)); + } + idx_menus[nb_menu_zones-1] = menu_type; + + /// ------ Reinit menu surface with height increased ------- + menu_zone_surfaces[nb_menu_zones-1] = IMG_Load(MENU_PNG_BG_PATH); + if(!menu_zone_surfaces[nb_menu_zones-1]) { + MENU_ERROR_PRINTF("ERROR IMG_Load: %s\n", IMG_GetError()); + } + + /// --------- Init Common Variables -------- + SDL_Surface *text_surface = NULL; + SDL_Surface *surface = menu_zone_surfaces[nb_menu_zones-1]; + SDL_Rect text_pos; + + /// --------- Add new zone --------- + switch(menu_type){ +#ifdef HAS_MENU_VOLUME + case MENU_TYPE_VOLUME: + MENU_DEBUG_PRINTF("Init MENU_TYPE_VOLUME\n"); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "VOLUME", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + + x_volume_bar = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - width_progress_bar)/2; + y_volume_bar = surface->h - MENU_ZONE_HEIGHT/2 - height_progress_bar/2 + padding_y_from_center_menu_zone; + draw_progress_bar(surface, x_volume_bar, y_volume_bar, + width_progress_bar, height_progress_bar, 0, 100/STEP_CHANGE_VOLUME); + break; +#endif +#ifdef HAS_MENU_BRIGHTNESS + case MENU_TYPE_BRIGHTNESS: + MENU_DEBUG_PRINTF("Init MENU_TYPE_BRIGHTNESS\n"); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "BRIGHTNESS", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + + x_brightness_bar = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - width_progress_bar)/2; + y_brightness_bar = surface->h - MENU_ZONE_HEIGHT/2 - height_progress_bar/2 + padding_y_from_center_menu_zone; + draw_progress_bar(surface, x_brightness_bar, y_brightness_bar, + width_progress_bar, height_progress_bar, 0, 100/STEP_CHANGE_BRIGHTNESS); + break; +#endif +#ifdef HAS_MENU_SAVE + case MENU_TYPE_SAVE: + MENU_DEBUG_PRINTF("Init MENU_TYPE_SAVE\n"); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "SAVE", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone*2; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + break; +#endif +#ifdef HAS_MENU_LOAD + case MENU_TYPE_LOAD: + MENU_DEBUG_PRINTF("Init MENU_TYPE_LOAD\n"); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "LOAD", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone*2; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + break; +#endif +#ifdef HAS_MENU_ASPECT_RATIO + case MENU_TYPE_ASPECT_RATIO: + MENU_DEBUG_PRINTF("Init MENU_TYPE_ASPECT_RATIO\n"); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "ASPECT RATIO", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + break; +#endif +#ifdef HAS_MENU_USB + case MENU_TYPE_USB: + MENU_DEBUG_PRINTF("Init MENU_TYPE_USB\n"); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "USB", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + break; +#endif +#ifdef HAS_MENU_THEME + case MENU_TYPE_THEME: + MENU_DEBUG_PRINTF("Init MENU_TYPE_THEME\n"); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "SET THEME", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone*2; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + break; +#endif +#ifdef HAS_MENU_LAUNCHER + case MENU_TYPE_LAUNCHER: + MENU_DEBUG_PRINTF("Init MENU_TYPE_LAUNCHER\n"); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "SET LAUNCHER", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone*2; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "GMENU2X", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + break; +#endif +#ifdef HAS_MENU_RO_RW + case MENU_TYPE_RO_RW: + MENU_DEBUG_PRINTF("Init MENU_TYPE_RO_RW\n"); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "SET SYSTEM:", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 - padding_y_from_center_menu_zone*2; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + break; +#endif +#ifdef HAS_MENU_ADVANCED + case MENU_TYPE_ADVANCED: + MENU_DEBUG_PRINTF("Init MENU_TYPE_ADVANCED\n"); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "ADVANCED", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + break; +#endif +#ifdef HAS_MENU_EXIT + case MENU_TYPE_EXIT: + MENU_DEBUG_PRINTF("Init MENU_TYPE_EXIT\n"); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "EXIT GAME", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + break; +#endif +#ifdef HAS_MENU_POWERDOWN + case MENU_TYPE_POWERDOWN: + MENU_DEBUG_PRINTF("Init MENU_TYPE_POWERDOWN\n"); + /// ------ Text ------ + text_surface = TTF_RenderText_Blended(menu_title_font, "POWERDOWN", text_color); + text_pos.x = (surface->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = surface->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, surface, &text_pos); + break; +#endif + default: + MENU_DEBUG_PRINTF("Warning - In add_menu_zone, unknown MENU_TYPE: %d\n", menu_type); + break; + } + + /// ------ Free Surfaces ------- + SDL_FreeSurface(text_surface); +} + +static void init_menu_zones(void) +{ +#ifdef HAS_MENU_VOLUME + add_menu_zone(MENU_TYPE_VOLUME); +#endif +#ifdef HAS_MENU_BRIGHTNESS + add_menu_zone(MENU_TYPE_BRIGHTNESS); +#endif +#ifdef HAS_MENU_SAVE + if (state_allowed()) { + add_menu_zone(MENU_TYPE_SAVE); + } +#endif +#ifdef HAS_MENU_LOAD + if (state_allowed()) { + add_menu_zone(MENU_TYPE_LOAD); + } +#endif +#ifdef HAS_MENU_ASPECT_RATIO + add_menu_zone(MENU_TYPE_ASPECT_RATIO); +#endif +#ifdef HAS_MENU_RO_RW + add_menu_zone(MENU_TYPE_RO_RW); +#endif +#ifdef HAS_MENU_ADVANCED + add_menu_zone(MENU_TYPE_ADVANCED); +#endif +#ifdef HAS_MENU_EXIT + add_menu_zone(MENU_TYPE_EXIT); +#endif +#ifdef HAS_MENU_USB + add_menu_zone(MENU_TYPE_USB); +#endif +#ifdef HAS_MENU_THEME + add_menu_zone(MENU_TYPE_THEME); +#endif +#ifdef HAS_MENU_LAUNCHER + add_menu_zone(MENU_TYPE_LAUNCHER); +#endif +#ifdef HAS_MENU_POWERDOWN + add_menu_zone(MENU_TYPE_POWERDOWN); +#endif +} + + +#ifdef HAS_MENU_THEME +void FK_InitMenu(Configuration &c) + #else +void FK_InitMenu(void) +#endif +{ + MENU_DEBUG_PRINTF("Init Menu\n"); + /// ----- Loading the fonts ----- + menu_title_font = TTF_OpenFont(MENU_FONT_NAME_TITLE, MENU_FONT_SIZE_TITLE); + if(!menu_title_font){ + MENU_ERROR_PRINTF("ERROR in init_menu_SDL: Could not open menu font %s, %s\n", MENU_FONT_NAME_TITLE, SDL_GetError()); + } + menu_info_font = TTF_OpenFont(MENU_FONT_NAME_INFO, MENU_FONT_SIZE_INFO); + if(!menu_info_font){ + MENU_ERROR_PRINTF("ERROR in init_menu_SDL: Could not open menu font %s, %s\n", MENU_FONT_NAME_INFO, SDL_GetError()); + } + menu_small_info_font = TTF_OpenFont(MENU_FONT_NAME_SMALL_INFO, MENU_FONT_SIZE_SMALL_INFO); + if(!menu_small_info_font){ + MENU_ERROR_PRINTF("ERROR in init_menu_SDL: Could not open menu font %s, %s\n", MENU_FONT_NAME_SMALL_INFO, SDL_GetError()); + } + + /// ------ Load arrows imgs ------- + img_arrow_top = IMG_Load(MENU_PNG_ARROW_TOP_PATH); + if(!img_arrow_top) { + MENU_ERROR_PRINTF("ERROR IMG_Load: %s\n", IMG_GetError()); + } + img_arrow_bottom = IMG_Load(MENU_PNG_ARROW_BOTTOM_PATH); + if(!img_arrow_bottom) { + MENU_ERROR_PRINTF("ERROR IMG_Load: %s\n", IMG_GetError()); + } + +#ifdef HAS_MENU_THEME + /// ------ Save config pointer ------ + config = &c; +#endif +#ifdef HAS_MENU_RO_RW + /// ----- Shell cmd ---- + if (system(SHELL_CMD_RO) < 0) { + MENU_ERROR_PRINTF("Failed to run command %s\n", SHELL_CMD_RO); + } +#endif + + /// ------ Init menu zones ------ + init_menu_zones(); + + return; +} + + +void FK_EndMenu(void) +{ + MENU_DEBUG_PRINTF("End Menu \n"); + /// ------ Close font ------- + TTF_CloseFont(menu_title_font); + TTF_CloseFont(menu_info_font); + TTF_CloseFont(menu_small_info_font); + + /// ------ Free Surfaces ------- + for(int i=0; i < nb_menu_zones; i++){ + if(menu_zone_surfaces[i] != NULL){ + SDL_FreeSurface(menu_zone_surfaces[i]); + } + } + SDL_FreeSurface(img_arrow_top); + SDL_FreeSurface(img_arrow_bottom); + + /// ------ Free Menu memory and reset vars ----- + if(idx_menus){ + free(idx_menus); + } + idx_menus=NULL; + nb_menu_zones = 0; + +#ifdef HAS_MENU_RO_RW + /// ----- Shell cmd ---- + if (system(SHELL_CMD_RO) < 0) { + MENU_ERROR_PRINTF("Failed to run command %s\n", SHELL_CMD_RO); + } +#endif + + return; +} + +void FK_StopMenu(void) +{ + stop_menu_loop = 1; +} + + +static void init_menu_system_values(void) +{ +#if defined(HAS_MENU_VOLUME) || defined(HAS_MENU_BRIGHTNESS) || defined(HAS_MENU_USB) || defined(HAS_MENU_RO_RW) + FILE *fp; + char res[100]; +#endif + +#ifdef HAS_MENU_VOLUME + /// ------- Get system volume percentage -------- + fp = popen(SHELL_CMD_VOLUME_GET, "r"); + if (fp == NULL) { + MENU_ERROR_PRINTF("Failed to run command %s\n", SHELL_CMD_VOLUME_GET ); + volume_percentage = 50; ///wrong value: setting default to 50 + } + else{ + fgets(res, sizeof(res)-1, fp); + pclose(fp); + + /// Check if Volume is a number (at least the first char) + if(res[0] < '0' || res[0] > '9'){ + MENU_ERROR_PRINTF("Wrong return value: %s for volume cmd: %s\n",res, SHELL_CMD_VOLUME_GET); + volume_percentage = 50; ///wrong value: setting default to 50 + } + else{ + volume_percentage = atoi(res); + MENU_DEBUG_PRINTF("System volume = %d%%\n", volume_percentage); + } + } +#endif +#ifdef HAS_MENU_BRIGHTNESS + /// ------- Get system brightness percentage ------- + fp = popen(SHELL_CMD_BRIGHTNESS_GET, "r"); + if (fp == NULL) { + MENU_ERROR_PRINTF("Failed to run command %s\n", SHELL_CMD_BRIGHTNESS_GET ); + brightness_percentage = 50; ///wrong value: setting default to 50 + } + else{ + fgets(res, sizeof(res)-1, fp); + pclose(fp); + + /// Check if brightness is a number (at least the first char) + if(res[0] < '0' || res[0] > '9'){ + MENU_ERROR_PRINTF("Wrong return value: %s for volume cmd: %s\n",res, SHELL_CMD_BRIGHTNESS_GET); + brightness_percentage = 50; ///wrong value: setting default to 50 + } + else{ + brightness_percentage = atoi(res); + MENU_DEBUG_PRINTF("System brightness = %d%%\n", brightness_percentage); + } + } +#endif +#ifdef HAS_MENU_USB + /// ------- Get USB Value ------- + usb_data_connected = Utils::executeRawPath(SHELL_CMD_USB_DATA_CONNECTED); + usb_sharing = Utils::executeRawPath(SHELL_CMD_USB_CHECK_IS_SHARING); + + /** Sanity check if usb not connected */ + if(!usb_data_connected){ + usb_sharing = 0; + + if(idx_menus[menuItem] == MENU_TYPE_USB){ + menuItem = 0; + } + } + + /** Sanity check if currently in USB sharing (should not happen) */ + if(usb_sharing){ + + /// Force USB menu to launch + for(int cur_idx=0; cur_idx < nb_menu_zones; cur_idx++){ + if(idx_menus[cur_idx] == MENU_TYPE_USB){ + menuItem = cur_idx; + printf("USB mounted, setting menu item to %d\n", menuItem); + break; + } + } + } +#endif +} + +static void menu_screen_refresh(SDL_Surface *screen, int menuItem, int prevItem, int scroll, uint8_t menu_confirmation, uint8_t menu_action) +{ + /// --------- Vars --------- +#ifdef HAS_MENU_USB + int print_arrows = (scroll || usb_sharing)?0:1; +#else + int print_arrows = 1; +#endif + + /// --------- Clear HW screen ---------- + if(SDL_BlitSurface(background_screen, NULL, screen, NULL)){ + MENU_ERROR_PRINTF("ERROR Could not Clear screen: %s\n", SDL_GetError()); + } + + /// --------- Setup Blit Window ---------- + SDL_Rect menu_blit_window; + menu_blit_window.x = 0; + menu_blit_window.w = SCREEN_HORIZONTAL_SIZE; + + /// --------- Blit prev menu Zone going away ---------- + menu_blit_window.y = scroll; + menu_blit_window.h = SCREEN_VERTICAL_SIZE; + if(SDL_BlitSurface(menu_zone_surfaces[prevItem], &menu_blit_window, screen, NULL)){ + MENU_ERROR_PRINTF("ERROR Could not Blit surface on screen: %s\n", SDL_GetError()); + } + + /// --------- Blit new menu Zone going in (only during animations) ---------- + if(scroll>0){ + menu_blit_window.y = SCREEN_VERTICAL_SIZE-scroll; + menu_blit_window.h = SCREEN_VERTICAL_SIZE; + if(SDL_BlitSurface(menu_zone_surfaces[menuItem], NULL, screen, &menu_blit_window)){ + MENU_ERROR_PRINTF("ERROR Could not Blit surface on screen: %s\n", SDL_GetError()); + } + } + else if(scroll<0){ + menu_blit_window.y = SCREEN_VERTICAL_SIZE+scroll; + menu_blit_window.h = SCREEN_VERTICAL_SIZE; + if(SDL_BlitSurface(menu_zone_surfaces[menuItem], &menu_blit_window, screen, NULL)){ + MENU_ERROR_PRINTF("ERROR Could not Blit surface on screen: %s\n", SDL_GetError()); + } + } + /// --------- No Scroll ? Blitting menu-specific info + else{ + SDL_Surface * text_surface = NULL; + char text_tmp[100]; + SDL_Rect text_pos; +#ifdef HAS_MENU_THEME + char *curLayoutName; + bool dots=false; + int max_chars = 15; +#endif + + switch(idx_menus[menuItem]){ +#ifdef HAS_MENU_VOLUME + case MENU_TYPE_VOLUME: + draw_progress_bar(screen, x_volume_bar, y_volume_bar, + width_progress_bar, height_progress_bar, volume_percentage, 100/STEP_CHANGE_VOLUME); + break; +#endif +#ifdef HAS_MENU_BRIGHTNESS + case MENU_TYPE_BRIGHTNESS: + draw_progress_bar(screen, x_volume_bar, y_volume_bar, + width_progress_bar, height_progress_bar, brightness_percentage, 100/STEP_CHANGE_BRIGHTNESS); + break; +#endif +#ifdef HAS_MENU_SAVE + case MENU_TYPE_SAVE: + /// ---- Write slot ----- + sprintf(text_tmp, "IN SLOT < %d >", state_slot+1); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + + if(menu_action){ + sprintf(text_tmp, "Saving..."); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + } + else{ + if(menu_confirmation){ + sprintf(text_tmp, "Are you sure?"); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + } + else{ + if (state_exists(state_slot)) { + text_surface = TTF_RenderText_Blended(menu_info_font, "Used", text_color); + } else { + text_surface = TTF_RenderText_Blended(menu_info_font, "Free", text_color); + } + } + } + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + break; +#endif +#ifdef HAS_MENU_LOAD + case MENU_TYPE_LOAD: + /// ---- Write slot ----- + sprintf(text_tmp, "FROM SLOT < %d >", state_slot+1); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + + if(menu_action){ + sprintf(text_tmp, "Loading..."); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + } + else{ + if(menu_confirmation){ + sprintf(text_tmp, "Are you sure?"); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + } + else{ + if (state_exists(state_slot)) { + text_surface = TTF_RenderText_Blended(menu_info_font, "Used", text_color); + } else { + text_surface = TTF_RenderText_Blended(menu_info_font, "Free", text_color); + } + } + } + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + break; +#endif +#ifdef HAS_MENU_ASPECT_RATIO + case MENU_TYPE_ASPECT_RATIO: + sprintf(text_tmp, "< %s >", aspect_ratio_name[menu_aspect_ratio]); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + break; +#endif +#ifdef HAS_MENU_USB + case MENU_TYPE_USB: + /// ---- Write slot ----- + sprintf(text_tmp, "%s USB", usb_sharing?"EJECT":"MOUNT"); + text_surface = TTF_RenderText_Blended(menu_title_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + + if(menu_action){ + sprintf(text_tmp, "in progress ..."); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + } + else if(menu_confirmation){ + sprintf(text_tmp, "Are you sure?"); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + } + else{ + ///Nothing + } + break; +#endif +#ifdef HAS_MENU_THEME + case MENU_TYPE_THEME: + /// ---- Write current chosen theme ----- + curLayoutName = (char*)Utils::getFileName(config->layouts_.at(indexChooseLayout)).c_str(); + + // no more than max_chars chars in name to fit screen + if(strlen(curLayoutName) > max_chars){ + curLayoutName[max_chars-2] = 0; + dots = true; + } + sprintf(text_tmp, "< %s%s >", curLayoutName, dots?"...":"" ); + + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + + if(menu_action){ + sprintf(text_tmp, "In progress..."); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + } + else if(menu_confirmation){ + sprintf(text_tmp, "Are you sure?"); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + } + break; +#endif +#ifdef HAS_MENU_LAUNCHER + case MENU_TYPE_LAUNCHER: + if(menu_action){ + sprintf(text_tmp, "In progress..."); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + } + else if(menu_confirmation){ + sprintf(text_tmp, "Are you sure?"); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + } + break; +#endif +#ifdef HAS_MENU_EXIT + case MENU_TYPE_EXIT: +#endif +#ifdef HAS_MENU_POWERDOWN + case MENU_TYPE_POWERDOWN: +#endif +#if defined(HAS_MENU_EXIT) || defined(HAS_MENU_POWERDOWN) + if(menu_action){ + sprintf(text_tmp, "Shutting down..."); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + } + else{ + if(menu_confirmation){ + sprintf(text_tmp, "Are you sure?"); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + } + } + break; +#endif +#ifdef HAS_MENU_RO_RW + case MENU_TYPE_RO_RW: + sprintf(text_tmp, "%s", read_write?"READ-ONLY":"READ-WRITE"); + text_surface = TTF_RenderText_Blended(menu_title_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + + if(menu_action){ + sprintf(text_tmp, "in progress ..."); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + } + else if(menu_confirmation){ + sprintf(text_tmp, "Are you sure?"); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, screen, &text_pos); + } + else{ + ///Nothing + } + break; +#endif + default: + break; + } + + /// ------ Free Surfaces ------- + if(text_surface) + SDL_FreeSurface(text_surface); + } + + /// --------- Print arrows -------- + if(print_arrows){ + /// Top arrow + SDL_Rect pos_arrow_top; + pos_arrow_top.x = (screen->w - img_arrow_top->w)/2; + pos_arrow_top.y = (screen->h - MENU_BG_SQUARE_HEIGHT)/4 - img_arrow_top->h/2; + SDL_BlitSurface(img_arrow_top, NULL, screen, &pos_arrow_top); + + /// Bottom arrow + SDL_Rect pos_arrow_bottom; + pos_arrow_bottom.x = (screen->w - img_arrow_bottom->w)/2; + pos_arrow_bottom.y = screen->h - + (screen->h - MENU_BG_SQUARE_HEIGHT)/4 - img_arrow_bottom->h/2; + SDL_BlitSurface(img_arrow_bottom, NULL, screen, &pos_arrow_bottom); + } + + /// --------- Flip Screen ---------- + SDL_Flip(screen); +} + + +int FK_RunMenu(SDL_Surface *screen) +{ + MENU_DEBUG_PRINTF("Run Menu\n"); + + SDL_Event event; + uint32_t prev_ms = SDL_GetTicks(); + uint32_t cur_ms = SDL_GetTicks(); + int scroll=0; + int start_scroll=0; + uint8_t screen_refresh = 1; + char shell_cmd[100]; + uint8_t menu_confirmation = 0; + stop_menu_loop = 0; +#ifdef HAS_MENU_THEME + indexChooseLayout = config->currentLayoutIdx_; +#endif + int returnCode = MENU_RETURN_OK; + + /// ------ Load default keymap ------ + system(SHELL_CMD_KEYMAP_DEFAULT); + + /// ------ Get System values ------- + init_menu_system_values(); + int prevItem=menuItem; + + /// Save prev key repeat params and set new Key repeat + SDL_GetKeyRepeat(&backup_key_repeat_delay, &backup_key_repeat_interval); + if(SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL)){ + MENU_ERROR_PRINTF("ERROR with SDL_EnableKeyRepeat: %s\n", SDL_GetError()); + } + +#if defined(HAS_MENU_SAVE) || defined(HAS_MENU_LOAD) + /// Get save slot from game + state_slot = (state_slot%MAX_SAVE_SLOTS); // security +#endif + +#if defined(HAS_MENU_ASPECT_RATIO) + read_aspect_ratio(); +#endif + + /// ------ Backup currently displayed app screen ------- + background_screen = SDL_CreateRGBSurface(SDL_SWSURFACE, + screen->w, screen->h, 32, 0, 0, 0, 0); + if(background_screen == NULL){ + MENU_ERROR_PRINTF("ERROR Could not create background_screen: %s\n", SDL_GetError()); + return MENU_RETURN_ERROR; + } + if(SDL_BlitSurface(screen, NULL, background_screen, NULL)){ + MENU_ERROR_PRINTF("ERROR Could not copy screen: %s\n", SDL_GetError()); + } + + /// -------- Main loop --------- + while (!stop_menu_loop) + { + /// -------- Handle Keyboard Events --------- + if(!scroll){ + while (SDL_PollEvent(&event)) + switch(event.type) + { + case SDL_QUIT: + stop_menu_loop = 1; + returnCode = MENU_RETURN_EXIT; + break; + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case SDLK_b: + if(menu_confirmation){ + /// ------ Reset menu confirmation ------ + menu_confirmation = 0; + /// ------ Refresh screen ------ + screen_refresh = 1; + } + /*else{ + stop_menu_loop = 1; + }*/ + break; + + case SDLK_q: + case SDLK_ESCAPE: + /// ------ Check if no action ------ +#ifdef HAS_MENU_USB + if(usb_sharing){ + break; + } +#endif + stop_menu_loop = 1; + break; + + case SDLK_d: + case SDLK_DOWN: + MENU_DEBUG_PRINTF("DOWN\n"); + /// ------ Check if no action ------ +#ifdef HAS_MENU_USB + if(usb_sharing){ + break; + } +#endif + /// ------ Start scrolling to new menu ------- + menuItem++; + if (menuItem>=nb_menu_zones) menuItem=0; + +#ifdef HAS_MENU_USB + /// Skip if usb menu if usb not connected + if(idx_menus[menuItem] == MENU_TYPE_USB && !usb_data_connected){ + menuItem++; + if (menuItem>=nb_menu_zones) menuItem=0; + } +#endif + start_scroll=1; + + /// ------ Reset menu confirmation ------ + menu_confirmation = 0; + + /// ------ Refresh screen ------ + screen_refresh = 1; + break; + + case SDLK_u: + case SDLK_UP: + MENU_DEBUG_PRINTF("UP\n"); + /// ------ Check if no action ------ +#ifdef HAS_MENU_USB + if(usb_sharing){ + break; + } +#endif + /// ------ Start scrolling to new menu ------- + menuItem--; + if (menuItem<0) menuItem=nb_menu_zones-1; + +#ifdef HAS_MENU_USB + /// Skip if usb menu if usb not connected + if(idx_menus[menuItem] == MENU_TYPE_USB && !usb_data_connected){ + menuItem--; + if (menuItem<0) menuItem=nb_menu_zones-1; + } +#endif + start_scroll=-1; + + /// ------ Reset menu confirmation ------ + menu_confirmation = 0; + + /// ------ Refresh screen ------ + screen_refresh = 1; + break; + + case SDLK_l: + case SDLK_LEFT: + //MENU_DEBUG_PRINTF("LEFT\n"); +#ifdef HAS_MENU_VOLUME + if(idx_menus[menuItem] == MENU_TYPE_VOLUME){ + MENU_DEBUG_PRINTF("Volume DOWN\n"); + /// ----- Compute new value ----- + volume_percentage = (volume_percentage < STEP_CHANGE_VOLUME)? + 0:(volume_percentage-STEP_CHANGE_VOLUME); + + /// ----- Shell cmd ---- + sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage); + system(shell_cmd); + + /// ------ Refresh screen ------ + screen_refresh = 1; + } else +#endif +#ifdef HAS_MENU_BRIGHTNESS + if(idx_menus[menuItem] == MENU_TYPE_BRIGHTNESS){ + MENU_DEBUG_PRINTF("Brightness DOWN\n"); + /// ----- Compute new value ----- + brightness_percentage = (brightness_percentage < STEP_CHANGE_BRIGHTNESS)? + 0:(brightness_percentage-STEP_CHANGE_BRIGHTNESS); + + /// ----- Shell cmd ---- + sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage); + system(shell_cmd); + + /// ------ Refresh screen ------ + screen_refresh = 1; + } else +#endif +#ifdef HAS_MENU_LOAD + if(idx_menus[menuItem] == MENU_TYPE_SAVE){ + MENU_DEBUG_PRINTF("Save Slot DOWN\n"); + state_slot = (!state_slot)?(MAX_SAVE_SLOTS-1):(state_slot-1); + /// ------ Refresh screen ------ + screen_refresh = 1; + } else +#endif +#ifdef HAS_MENU_LOAD + if(idx_menus[menuItem] == MENU_TYPE_LOAD){ + MENU_DEBUG_PRINTF("Load Slot DOWN\n"); + //idx_load_slot = (!idx_load_slot)?(MAX_SAVE_SLOTS-1):(idx_load_slot-1); + state_slot = (!state_slot)?(MAX_SAVE_SLOTS-1):(state_slot-1); + /// ------ Refresh screen ------ + screen_refresh = 1; + } else +#endif +#ifdef HAS_MENU_ASPECT_RATIO + if(idx_menus[menuItem] == MENU_TYPE_ASPECT_RATIO){ + MENU_DEBUG_PRINTF("Aspect Ratio DOWN\n"); + menu_aspect_ratio = (!menu_aspect_ratio)?(NB_ASPECT_RATIOS_TYPES-1):(menu_aspect_ratio-1); + update_aspect_ratio(); + + /// ------ Refresh screen ------ + screen_refresh = 1; + } else +#endif +#ifdef HAS_MENU_THEME + if(idx_menus[menuItem] == MENU_TYPE_THEME){ + MENU_DEBUG_PRINTF("Theme previous\n"); + indexChooseLayout = (!indexChooseLayout)?(config->layouts_.size()-1):(indexChooseLayout-1); + /// ------ Refresh screen ------ + screen_refresh = 1; + } else +#endif + {} + break; + + case SDLK_r: + case SDLK_RIGHT: + //MENU_DEBUG_PRINTF("RIGHT\n"); +#ifdef HAS_MENU_VOLUME + if(idx_menus[menuItem] == MENU_TYPE_VOLUME){ + MENU_DEBUG_PRINTF("Volume UP\n"); + /// ----- Compute new value ----- + volume_percentage = (volume_percentage > 100 - STEP_CHANGE_VOLUME)? + 100:(volume_percentage+STEP_CHANGE_VOLUME); + + /// ----- Shell cmd ---- + sprintf(shell_cmd, "%s %d", SHELL_CMD_VOLUME_SET, volume_percentage); + system(shell_cmd); + + /// ------ Refresh screen ------ + screen_refresh = 1; + } else +#endif +#ifdef HAS_MENU_BRIGHTNESS + if(idx_menus[menuItem] == MENU_TYPE_BRIGHTNESS){ + MENU_DEBUG_PRINTF("Brightness UP\n"); + /// ----- Compute new value ----- + brightness_percentage = (brightness_percentage > 100 - STEP_CHANGE_BRIGHTNESS)? + 100:(brightness_percentage+STEP_CHANGE_BRIGHTNESS); + + /// ----- Shell cmd ---- + sprintf(shell_cmd, "%s %d", SHELL_CMD_BRIGHTNESS_SET, brightness_percentage); + system(shell_cmd); + + /// ------ Refresh screen ------ + screen_refresh = 1; + } else +#endif +#ifdef HAS_MENU_SAVE + if(idx_menus[menuItem] == MENU_TYPE_SAVE){ + MENU_DEBUG_PRINTF("Save Slot UP\n"); + state_slot = (state_slot+1)%MAX_SAVE_SLOTS; + /// ------ Refresh screen ------ + screen_refresh = 1; + } else +#endif +#ifdef HAS_MENU_LOAD + if(idx_menus[menuItem] == MENU_TYPE_LOAD){ + MENU_DEBUG_PRINTF("Load Slot UP\n"); + //idx_load_slot = (idx_load_slot+1)%MAX_SAVE_SLOTS; + state_slot = (state_slot+1)%MAX_SAVE_SLOTS; + /// ------ Refresh screen ------ + screen_refresh = 1; + } else +#endif +#ifdef HAS_MENU_ASPECT_RATIO + if(idx_menus[menuItem] == MENU_TYPE_ASPECT_RATIO){ + MENU_DEBUG_PRINTF("Aspect Ratio UP\n"); + menu_aspect_ratio = (menu_aspect_ratio+1)%NB_ASPECT_RATIOS_TYPES; + update_aspect_ratio(); + /// ------ Refresh screen ------ + screen_refresh = 1; + } else +#endif +#ifdef HAS_MENU_THEME + if(idx_menus[menuItem] == MENU_TYPE_THEME){ + MENU_DEBUG_PRINTF("Theme previous\n"); + indexChooseLayout = (indexChooseLayout+1)%config->layouts_.size(); + /// ------ Refresh screen ------ + screen_refresh = 1; + } else +#endif + {} + break; + + case SDLK_a: + case SDLK_RETURN: +#ifdef HAS_MENU_SAVE + if(idx_menus[menuItem] == MENU_TYPE_SAVE){ + if(menu_confirmation){ + MENU_DEBUG_PRINTF("Saving in slot %d\n", state_slot); + /// ------ Refresh Screen ------- + menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1); + + /// ------ Save game ------ + state_write(); + stop_menu_loop = 1; + } + else{ + MENU_DEBUG_PRINTF("Save game - asking confirmation\n"); + menu_confirmation = 1; + /// ------ Refresh screen ------ + screen_refresh = 1; + } + } else +#endif +#ifdef HAS_MENU_LOAD + if(idx_menus[menuItem] == MENU_TYPE_LOAD){ + if(menu_confirmation){ + MENU_DEBUG_PRINTF("Loading in slot %d\n", state_slot); + /// ------ Refresh Screen ------- + menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1); + + /// ------ Load game ------ + state_read(); + stop_menu_loop = 1; + } + else{ + MENU_DEBUG_PRINTF("Save game - asking confirmation\n"); + menu_confirmation = 1; + /// ------ Refresh screen ------ + screen_refresh = 1; + } + } else +#endif +#ifdef HAS_MENU_USB + if(idx_menus[menuItem] == MENU_TYPE_USB){ + MENU_DEBUG_PRINTF("USB %s\n", usb_sharing?"unmount":"mount"); + if(menu_confirmation){ + MENU_DEBUG_PRINTF("%s USB - confirmed\n", usb_sharing?"Unmount":"Mount"); + /// ----- Refresh screen ---- + menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1); + + /// ----- Shell cmd ---- + /*system(usb_sharing?SHELL_CMD_USB_UNMOUNT:SHELL_CMD_USB_MOUNT);*/ + bool res = Utils::executeRawPath(usb_sharing?SHELL_CMD_USB_UNMOUNT:SHELL_CMD_USB_MOUNT); + if (!res) { + MENU_ERROR_PRINTF("Failed to run command %s\n", shell_cmd); + } + else{ + usb_sharing = !usb_sharing; + } + + /// ------ Refresh screen ------ + menu_confirmation = 0; + screen_refresh = 1; + } + else{ + MENU_DEBUG_PRINTF("%s USB - asking confirmation\n", usb_sharing?"Unmount":"Mount"); + menu_confirmation = 1; + screen_refresh = 1; + } + } else +#endif +#ifdef HAS_MENU_THEME + if(idx_menus[menuItem] == MENU_TYPE_THEME){ + if(menu_confirmation){ + MENU_DEBUG_PRINTF("Theme change - confirmed\n"); + + /// ------ Refresh Screen ------- + menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1); + + /// ----- Write new theme and restart RetroFe ---- + config->exportCurrentLayout(Utils::combinePath(Configuration::absolutePath, "layout.conf"), + Utils::getFileName(config->layouts_.at(indexChooseLayout))); + stop_menu_loop = 1; + returnCode = MENU_RETURN_EXIT; + } + else{ + MENU_DEBUG_PRINTF("Theme change - asking confirmation\n"); + menu_confirmation = 1; + /// ------ Refresh screen ------ + screen_refresh = 1; + } + } else +#endif +#ifdef HAS_MENU_LAUNCHER + if(idx_menus[menuItem] == MENU_TYPE_LAUNCHER){ + if(menu_confirmation){ + MENU_DEBUG_PRINTF("Lancher change - confirmed\n"); + + /// ------ Refresh Screen ------- + menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1); + + /// ----- Shell cmd ---- + MENU_DEBUG_PRINTF("Running command: %s\n", SHELL_CMD_SET_LAUNCHER_GMENU2X); + Utils::executeRawPath(SHELL_CMD_SET_LAUNCHER_GMENU2X); + + stop_menu_loop = 1; + returnCode = MENU_RETURN_EXIT; + } + else{ + MENU_DEBUG_PRINTF("Launcher change - asking confirmation\n"); + menu_confirmation = 1; + /// ------ Refresh screen ------ + screen_refresh = 1; + } + } else +#endif +#ifdef HAS_MENU_ADVANCED + if(idx_menus[menuItem] == MENU_TYPE_ADVANCED){ + MENU_DEBUG_PRINTF("Advanced menu\n"); + + SDL_Event sdlevent; + do { + SDL_WaitEvent(&sdlevent); + } while (sdlevent.type != SDL_KEYUP || sdlevent.key.keysym.sym != event.key.keysym.sym); + + stop_menu_loop = 1; + returnCode = MENU_RETURN_MENU; + } else +#endif +#ifdef HAS_MENU_EXIT + if(idx_menus[menuItem] == MENU_TYPE_EXIT){ + MENU_DEBUG_PRINTF("Exit game\n"); + if(menu_confirmation){ + MENU_DEBUG_PRINTF("Exit game - confirmed\n"); + /// ----- The game should be saved here ---- + + /// ----- Exit game and back to launcher ---- + stop_menu_loop = 1; + returnCode = MENU_RETURN_EXIT; + } + else{ + MENU_DEBUG_PRINTF("Exit game - asking confirmation\n"); + menu_confirmation = 1; + /// ------ Refresh screen ------ + screen_refresh = 1; + } + } else +#endif +#ifdef HAS_MENU_POWERDOWN + if(idx_menus[menuItem] == MENU_TYPE_POWERDOWN){ + if(menu_confirmation){ + MENU_DEBUG_PRINTF("Powerdown - confirmed\n"); + + /// ------ Refresh Screen ------- + menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1); + + /// ----- Shell cmd ---- + system(SHELL_CMD_POWERDOWN); + return MENU_RETURN_EXIT; + } + else{ + MENU_DEBUG_PRINTF("Powerdown - asking confirmation\n"); + menu_confirmation = 1; + /// ------ Refresh screen ------ + screen_refresh = 1; + } + } else +#endif +#ifdef HAS_MENU_RO_RW + if(idx_menus[menuItem] == MENU_TYPE_RO_RW){ + MENU_DEBUG_PRINTF("%s\n", read_write?"RO":"RW"); + if(menu_confirmation){ + MENU_DEBUG_PRINTF("SYSTEM %s - confirmed\n", read_write?"RO":"RW"); + /// ----- Refresh screen ---- + menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 1); + + /// ----- Shell cmd ---- + if (system(read_write?SHELL_CMD_RO:SHELL_CMD_RW) < 0) { + MENU_ERROR_PRINTF("Failed to run command %s\n", shell_cmd); + } + else{ + read_write = !read_write; + } + + /// ------ Refresh screen ------ + menu_confirmation = 0; + screen_refresh = 1; + } + else{ + MENU_DEBUG_PRINTF("SYSTEM %s - asking confirmation\n", read_write?"RW":"RO"); + menu_confirmation = 1; + screen_refresh = 1; + } + } else +#endif + {} + break; + + default: + //MENU_DEBUG_PRINTF("Keydown: %d\n", event.key.keysym.sym); + break; + } + break; + } + } + + /// --------- Handle Scroll effect --------- + if ((scroll>0) || (start_scroll>0)){ + scroll+=MIN(SCROLL_SPEED_PX, MENU_ZONE_HEIGHT-scroll); + start_scroll = 0; + screen_refresh = 1; + } + else if ((scroll<0) || (start_scroll<0)){ + scroll-=MIN(SCROLL_SPEED_PX, MENU_ZONE_HEIGHT+scroll); + start_scroll = 0; + screen_refresh = 1; + } + if (scroll>=MENU_ZONE_HEIGHT || scroll<=-MENU_ZONE_HEIGHT) { + prevItem=menuItem; + scroll=0; + screen_refresh = 1; + } + + /// --------- Handle FPS --------- + cur_ms = SDL_GetTicks(); + if(cur_ms-prev_ms < 1000/FPS_MENU){ + SDL_Delay(1000/FPS_MENU - (cur_ms-prev_ms)); + } + prev_ms = SDL_GetTicks(); + + + /// --------- Refresh screen + if(screen_refresh){ + menu_screen_refresh(screen, menuItem, prevItem, scroll, menu_confirmation, 0); + } + + /// --------- reset screen refresh --------- + screen_refresh = 0; + } + + /// ------ Restore last keymap ------ + system(SHELL_CMD_KEYMAP_RESUME); + + /// ------ Reset prev key repeat params ------- + if(SDL_EnableKeyRepeat(backup_key_repeat_delay, backup_key_repeat_interval)){ + MENU_ERROR_PRINTF("ERROR with SDL_EnableKeyRepeat: %s\n", SDL_GetError()); + } + + /// --------- Clear HW screen ---------- + if(SDL_BlitSurface(background_screen, NULL, screen, NULL)){ + MENU_ERROR_PRINTF("ERROR Could not Clear screen: %s\n", SDL_GetError()); + } + + /// --------- Flip Screen ---------- + SDL_Flip(screen); + + if(background_screen != NULL){ + SDL_FreeSurface(background_screen); + background_screen = NULL; + } + MENU_DEBUG_PRINTF("Leave Menu\n"); + plat_video_menu_leave(); + return returnCode; +} + +#ifdef HAS_MENU_ASPECT_RATIO +void FK_NextAspectRatio(void) +{ + char shell_cmd[100]; + FILE *fp; + + read_aspect_ratio(); + menu_aspect_ratio = (menu_aspect_ratio+1)%NB_ASPECT_RATIOS_TYPES; + update_aspect_ratio(); + scale_update_scaler(); + plat_video_menu_leave(); + snprintf(shell_cmd, 100, "%s %d \" DISPLAY MODE: %s\"", + SHELL_CMD_NOTIF_SET, NOTIF_SECONDS_DISP, aspect_ratio_name[menu_aspect_ratio]); + fp = popen(shell_cmd, "r"); + if (fp == NULL) { + printf("Failed to run command %s\n", shell_cmd); + } else { + pclose(fp); + } +} +#endif + +/****************************/ +/* Quick Resume Menu */ +/****************************/ +int FK_RunResumeMenu(SDL_Surface *hw_screen) +{ + MENU_DEBUG_PRINTF("Init resume menu\n"); + + /* Decare vars */ + SDL_Surface *text_surface = NULL; + char text_tmp[40]; + SDL_Rect text_pos; + SDL_Event event; + uint32_t prev_ms = SDL_GetTicks(); + uint32_t cur_ms = SDL_GetTicks(); + stop_menu_loop = 0; + uint8_t screen_refresh = 1; + uint8_t menu_confirmation = 0; + int option_idx=RESUME_YES; + + /* Save prev key repeat params and set new Key repeat */ + SDL_GetKeyRepeat(&backup_key_repeat_delay, &backup_key_repeat_interval); + if(SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL)){ + MENU_ERROR_PRINTF("ERROR with SDL_EnableKeyRepeat: %s\n", SDL_GetError()); + } + + /* Load BG */ + SDL_Surface *img_square_bg = IMG_Load(MENU_PNG_BG_PATH); + if(!img_square_bg) { + MENU_ERROR_PRINTF("ERROR IMG_Load: %s\n", IMG_GetError()); + } + SDL_Surface *bg_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, hw_screen->w, hw_screen->h, 16, 0, 0, 0, 0); + SDL_BlitSurface(img_square_bg, NULL, bg_surface, NULL); + SDL_FreeSurface(img_square_bg); + + + /* Print top arrow */ + SDL_Rect pos_arrow_top; + pos_arrow_top.x = (bg_surface->w - img_arrow_top->w)/2; + pos_arrow_top.y = (bg_surface->h - MENU_BG_SQUARE_HEIGHT)/4 - img_arrow_top->h/2; + SDL_BlitSurface(img_arrow_top, NULL, bg_surface, &pos_arrow_top); + + /* Print bottom arrow */ + SDL_Rect pos_arrow_bottom; + pos_arrow_bottom.x = (bg_surface->w - img_arrow_bottom->w)/2; + pos_arrow_bottom.y = bg_surface->h - + (bg_surface->h - MENU_BG_SQUARE_HEIGHT)/4 - img_arrow_bottom->h/2; + SDL_BlitSurface(img_arrow_bottom, NULL, bg_surface, &pos_arrow_bottom); + + if (text_surface) + SDL_FreeSurface(text_surface); + + /* Main loop */ + while (!stop_menu_loop) + { + /* Handle keyboard events */ + while (SDL_PollEvent(&event)) + switch(event.type) + { + case SDL_QUIT: + stop_menu_loop = 1; + break; + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case SDLK_b: + if(menu_confirmation){ + /// ------ Reset menu confirmation ------ + menu_confirmation = 0; + + /// ------ Refresh screen ------ + screen_refresh = 1; + } + /*else{ + stop_menu_loop = 1; + }*/ + break; + + case SDLK_q: + case SDLK_ESCAPE: + /*stop_menu_loop = 1;*/ + break; + + case SDLK_u: + case SDLK_UP: + MENU_DEBUG_PRINTF("Option UP\n"); + option_idx = (!option_idx)?(NB_RESUME_OPTIONS-1):(option_idx-1); + + /// ------ Reset menu confirmation ------ + menu_confirmation = 0; + + /// ------ Refresh screen ------ + screen_refresh = 1; + break; + + case SDLK_d: + case SDLK_DOWN: + MENU_DEBUG_PRINTF("Option DOWN\n"); + option_idx = (option_idx+1)%NB_RESUME_OPTIONS; + + /// ------ Reset menu confirmation ------ + menu_confirmation = 0; + + /// ------ Refresh screen ------ + screen_refresh = 1; + break; + + case SDLK_a: + case SDLK_RETURN: + MENU_DEBUG_PRINTF("Pressed A\n"); + if(menu_confirmation){ + MENU_DEBUG_PRINTF("Confirmed\n"); + + /// ----- exit menu ---- + stop_menu_loop = 1; + } + else{ + MENU_DEBUG_PRINTF("Asking confirmation\n"); + menu_confirmation = 1; + + /// ------ Refresh screen ------ + screen_refresh = 1; + } + break; + + default: + //MENU_DEBUG_PRINTF("Keydown: %d\n", event.key.keysym.sym); + break; + } + break; + } + + /* Handle FPS */ + cur_ms = SDL_GetTicks(); + if(cur_ms-prev_ms < 1000/FPS_MENU){ + SDL_Delay(1000/FPS_MENU - (cur_ms-prev_ms)); + } + prev_ms = SDL_GetTicks(); + + /* Refresh screen */ + if(screen_refresh){ + /* Clear and draw BG */ + SDL_FillRect(hw_screen, NULL, 0); + if(SDL_BlitSurface(bg_surface, NULL, hw_screen, NULL)){ + MENU_ERROR_PRINTF("ERROR Could not draw background: %s\n", SDL_GetError()); + } + + /* Draw resume or reset option */ + text_surface = TTF_RenderText_Blended(menu_title_font, resume_options_str[option_idx], text_color); + text_pos.x = (hw_screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = hw_screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2; + SDL_BlitSurface(text_surface, NULL, hw_screen, &text_pos); + + /* Draw confirmation */ + if(menu_confirmation){ + sprintf(text_tmp, "Are you sure ?"); + text_surface = TTF_RenderText_Blended(menu_info_font, text_tmp, text_color); + text_pos.x = (hw_screen->w - MENU_ZONE_WIDTH)/2 + (MENU_ZONE_WIDTH - text_surface->w)/2; + text_pos.y = hw_screen->h - MENU_ZONE_HEIGHT/2 - text_surface->h/2 + 2*padding_y_from_center_menu_zone; + SDL_BlitSurface(text_surface, NULL, hw_screen, &text_pos); + } + + /* Flip Screen */ + SDL_Flip(hw_screen); + } + + /* reset screen refresh */ + screen_refresh = 0; + } + + /* Free SDL Surfaces */ + if(bg_surface) + SDL_FreeSurface(bg_surface); + if(text_surface) + SDL_FreeSurface(text_surface); + + /* Reset prev key repeat params */ + if(SDL_EnableKeyRepeat(backup_key_repeat_delay, backup_key_repeat_interval)){ + MENU_ERROR_PRINTF("ERROR with SDL_EnableKeyRepeat: %s\n", SDL_GetError()); + } + + return option_idx; +} diff --git a/funkey/fk_menu.h b/funkey/fk_menu.h new file mode 100644 index 0000000..36bc460 --- /dev/null +++ b/funkey/fk_menu.h @@ -0,0 +1,170 @@ +/* + FK - FunKey retro gaming console library + Copyright (C) 2020-2021 Vincent Buso + Copyright (C) 2020-2021 Michel Stempin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Vincent Buso + vincent.buso@funkey-project.com + Michel Stempin + michel.stempin@funkey-project.com +*/ + +/** + * @file FK_menu.h + * This is the menu API for the FunKey retro gaming console library + */ + +#ifndef _FK_menu_h +#define _FK_menu_h + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +#include <SDL/SDL.h> +#include <SDL/SDL_ttf.h> +#include <SDL/SDL_image.h> + +#define HAS_MENU_VOLUME +#define HAS_MENU_BRIGHTNESS +#define HAS_MENU_SAVE +#define HAS_MENU_LOAD +#define HAS_MENU_ASPECT_RATIO +//#define HAS_MENU_USB +//#define HAS_MENU_THEME +//#define HAS_MENU_LAUNCHER +#define HAS_MENU_ADVANCED +#define HAS_MENU_EXIT +//#define HAS_MENU_POWERDOWN +//#define HAS_MENU_RO_RW + +typedef enum{ + MENU_TYPE_VOLUME, + MENU_TYPE_BRIGHTNESS, + MENU_TYPE_SAVE, + MENU_TYPE_LOAD, + MENU_TYPE_ASPECT_RATIO, + MENU_TYPE_USB, + MENU_TYPE_THEME, + MENU_TYPE_LAUNCHER, + MENU_TYPE_ADVANCED, + MENU_TYPE_EXIT, + MENU_TYPE_POWERDOWN, + MENU_TYPE_RO_RW, + NB_MENU_TYPES, +} ENUM_MENU_TYPE; + +typedef enum{ + MENU_RETURN_OK, + MENU_RETURN_MENU, + MENU_RETURN_EXIT, + MENU_RETURN_ERROR, + NB_MENU_RETURN_CODES, +} ENUM_MENU_RETURN_CODES; + +#ifdef HAS_MENU_ASPECT_RATIO +///------ Definition of the different aspect ratios +#define ASPECT_RATIOS \ + /* X(ASPECT_RATIOS_TYPE_MANUAL, "MANUAL ZOOM") */ \ + X(ASPECT_RATIOS_TYPE_STRETCHED, "STRETCHED") \ + X(ASPECT_RATIOS_TYPE_CROPPED, "CROPPED") \ + X(ASPECT_RATIOS_TYPE_SCALED, "SCALED") \ + X(ASPECT_RATIOS_TYPE_NONE, "NONE") \ + X(NB_ASPECT_RATIOS_TYPES, "") + +////------ Enumeration of the different aspect ratios ------ +#undef X +#define X(a, b) a, +typedef enum {ASPECT_RATIOS} ENUM_ASPECT_RATIOS_TYPES; +#endif + +///------ Definition of the different resume options +#define RESUME_OPTIONS \ + X(RESUME_YES, "RESUME GAME") \ + X(RESUME_NO, "NEW GAME") \ + X(NB_RESUME_OPTIONS, "") + +////------ Enumeration of the different resume options ------ +#undef X +#define X(a, b) a, +typedef enum {RESUME_OPTIONS} ENUM_RESUME_OPTIONS; + +////------ Defines to be shared ------- +#ifdef HAS_MENU_VOLUME +#define STEP_CHANGE_VOLUME 10 +#endif +#ifdef HAS_MENU_BRIGHTNESS +#define STEP_CHANGE_BRIGHTNESS 10 +#endif +#define NOTIF_SECONDS_DISP 2 + +////------ Menu commands ------- +#ifdef HAS_MENU_VOLUME +#define SHELL_CMD_VOLUME_GET "volume get" +#define SHELL_CMD_VOLUME_SET "volume set" +#endif +#ifdef HAS_MENU_BRIGHTNESS +#define SHELL_CMD_BRIGHTNESS_GET "brightness get" +#define SHELL_CMD_BRIGHTNESS_SET "brightness set" +#endif +#ifdef HAS_MENU_USB +#define SHELL_CMD_USB_DATA_CONNECTED "is_usb_data_connected" +#define SHELL_CMD_USB_MOUNT "share start" +#define SHELL_CMD_USB_UNMOUNT "share stop" +#define SHELL_CMD_USB_CHECK_IS_SHARING "share is_sharing" +#endif +#ifdef HAS_MENU_POWERDOWN +#define SHELL_CMD_POWERDOWN "shutdown_funkey" +#define SHELL_CMD_SCHEDULE_POWERDOWN "sched_shutdown" +#define SHELL_CMD_CANCEL_SCHED_POWERDOWN "cancel_sched_powerdown" +#endif +#ifdef HAS_MENU_LAUNCHER +#define SHELL_CMD_SET_LAUNCHER_GMENU2X "set_launcher gmenu2x" +#define SHELL_CMD_SET_LAUNCHER_RETROFE "set_launcher retrofe" +#endif +#ifdef HAS_MENU_RO_RW +#define SHELL_CMD_RO "ro" +#define SHELL_CMD_RW "rw" +#endif + +#define SHELL_CMD_INSTANT_PLAY "instant_play" +#define SHELL_CMD_NOTIF_SET "notif set" +#define SHELL_CMD_NOTIF_CLEAR "notif clear" +#define SHELL_CMD_KEYMAP_DEFAULT "keymap default" +#define SHELL_CMD_KEYMAP_RESUME "keymap resume" + +#ifdef HAS_MENU_THEME +extern void FK_InitMenu(Configuration &c); +#else +extern void FK_InitMenu(void); +#endif +extern void FK_EndMenu(void); +extern int FK_RunMenu(SDL_Surface *screen); +extern int FK_RunResumeMenu(SDL_Surface *screen); +extern void FK_StopMenu(void); + +#ifdef HAS_MENU_ASPECT_RATIO +extern void FK_NextAspectRatio(void); +#endif + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif + +#endif /* _FK_menu_h */ @@ -22,6 +22,12 @@ void* mmenu = NULL; char save_template_path[MAX_PATH]; #endif +#ifdef FUNKEY_S +#include "funkey/fk_menu.h" +#include "funkey/fk_instant_play.h" +static bool instant_play = false; +#endif + bool should_quit = false; unsigned current_audio_buffer_size; char core_name[MAX_PATH]; @@ -204,6 +210,24 @@ void set_defaults(void) audio_buffer_size = 5; scale_size = SCALE_SIZE_NONE; scale_filter = SCALE_FILTER_NEAREST; + + /* Sets better defaults for small screen */ + if (SCREEN_WIDTH == 240) { + scale_size = SCALE_SIZE_CROP; + scale_filter = SCALE_FILTER_SMOOTH; + + if (!strcmp(core_name, "gambatte")) { + scale_size = SCALE_SIZE_ASPECT; + scale_filter = SCALE_FILTER_SHARP; + } + + if (!strcmp(core_name, "pcsx_rearmed") || + !strcmp(core_name, "picodrive")) { + scale_size = SCALE_SIZE_FULL; + scale_filter = SCALE_FILTER_SMOOTH; + } + } + scale_update_scaler(); if (current_audio_buffer_size < audio_buffer_size) @@ -218,12 +242,12 @@ void set_defaults(void) options_update_changed(); } -int save_config(int is_game) +int save_config(config_type config_type) { char config_filename[MAX_PATH]; FILE *config_file; - config_file_name(config_filename, MAX_PATH, is_game); + config_file_name(config_filename, MAX_PATH, config_type); config_file = fopen(config_filename, "wb"); if (!config_file) { fprintf(stderr, "Could not write config to %s\n", config_filename); @@ -235,7 +259,7 @@ int save_config(int is_game) fclose(config_file); - if (is_game) + if (config_type == CONFIG_TYPE_GAME) config_override = 1; return 0; @@ -245,15 +269,25 @@ static void alloc_config_buffer(char **config_ptr) { char config_filename[MAX_PATH]; FILE *config_file; size_t length; + int config_auto = 0; config_override = 0; - config_file_name(config_filename, MAX_PATH, 1); + config_file_name(config_filename, MAX_PATH, CONFIG_TYPE_AUTO); config_file = fopen(config_filename, "rb"); if (config_file) { - config_override = 1; + config_auto = 1; +#ifdef FUNKEY_S + instant_play = true; +#endif } else { - config_file_name(config_filename, MAX_PATH, 0); + config_file_name(config_filename, MAX_PATH, CONFIG_TYPE_GAME); config_file = fopen(config_filename, "rb"); + if (config_file) { + config_override = 1; + } else { + config_file_name(config_filename, MAX_PATH, CONFIG_TYPE_CORE); + config_file = fopen(config_filename, "rb"); + } } if (!config_file) @@ -269,6 +303,13 @@ static void alloc_config_buffer(char **config_ptr) { fread(*config_ptr, 1, length, config_file); fclose(config_file); + + if (config_auto) { + config_file_name(config_filename, MAX_PATH, CONFIG_TYPE_GAME); + if (access(config_filename, F_OK) == 0) { + config_override = 1; + } + } } void load_config(void) @@ -307,13 +348,13 @@ void load_config_keys(void) } } -int remove_config(int is_game) { +int remove_config(config_type config_type) { char config_filename[MAX_PATH]; int ret; - config_file_name(config_filename, MAX_PATH, is_game); + config_file_name(config_filename, MAX_PATH, config_type); ret = remove(config_filename); - if (ret == 0) + if (ret == 0 && config_type == CONFIG_TYPE_GAME) config_override = 0; return ret; @@ -331,7 +372,7 @@ void handle_emu_action(emu_action action) case EACTION_MENU: toggle_fast_forward(1); /* Force FF off */ sram_write(); -#ifdef MMENU +#if defined(MMENU) if (mmenu && content && content->path) { ShowMenu_t ShowMenu = (ShowMenu_t)dlsym(mmenu, "ShowMenu"); SDL_Surface *screen = SDL_GetVideoSurface(); @@ -361,10 +402,27 @@ void handle_emu_action(emu_action action) SDL_PushEvent(&sdlevent); } else { -#endif menu_loop(); -#ifdef MMENU } +#elif defined(FUNKEY_S) + { + SDL_Surface *screen = SDL_GetVideoSurface(); + int return_code = FK_RunMenu(screen); + + if (return_code == MENU_RETURN_MENU) { + menu_loop(); + } else if (return_code == MENU_RETURN_EXIT) { + should_quit = 1; + } + + // release that menu key + SDL_Event sdlevent; + sdlevent.type = SDL_KEYUP; + sdlevent.key.keysym.sym = SDLK_q; + SDL_PushEvent(&sdlevent); + } +#else + menu_loop(); #endif break; case EACTION_TOGGLE_HUD: @@ -384,6 +442,11 @@ void handle_emu_action(emu_action action) case EACTION_SCREENSHOT: screenshot(); break; +#ifdef FUNKEY_S + case EACTION_NEXT_SCALER: + FK_NextAspectRatio(); + break; +#endif case EACTION_QUIT: should_quit = 1; break; @@ -508,6 +571,10 @@ static void adjust_audio(void) { int main(int argc, char **argv) { char content_path[MAX_PATH]; +#ifdef FUNKEY_S + char autosave_path[MAX_PATH]; +#endif + if (argc > 1) { if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { @@ -568,9 +635,42 @@ int main(int argc, char **argv) { if (ResumeSlot) resume_slot = ResumeSlot(); } #endif +#ifdef FUNKEY_S + if (IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF | IMG_INIT_WEBP) == 0) { + PA_ERROR("Error initializing SDL_Image\n"); + quit(-1); + } + + if (TTF_Init() == -1) { + PA_ERROR("Error initializing SDL_ttf\n"); + quit(-1); + } + FK_InitMenu(); + + state_file_name(autosave_path, MAX_PATH, AUTOSAVE_SLOT); + if (access(autosave_path, F_OK) == 0) { + if (instant_play) { + resume_slot = AUTOSAVE_SLOT; + } else { + SDL_Surface *screen = SDL_GetVideoSurface(); + int resume = FK_RunResumeMenu(screen); + if (resume == RESUME_YES) { + resume_slot = AUTOSAVE_SLOT; + } + } + + instant_play = false; + } +#endif show_startup_message(); state_resume(); +#ifdef FUNKEY_S + remove(autosave_path); + remove_config(CONFIG_TYPE_AUTO); + FK_InitInstantPlay(argc, argv); +#endif + do { count_fps(); adjust_audio(); @@ -584,6 +684,18 @@ int main(int argc, char **argv) { int quit(int code) { menu_finish(); + +#ifdef FUNKEY_S + if (current_core.initialized && state_allowed()) { + state_slot = AUTOSAVE_SLOT; + state_write(); + } + + FK_EndMenu(); + TTF_Quit(); + IMG_Quit(); +#endif + core_unload(); plat_finish(); exit(code); @@ -15,9 +15,18 @@ typedef enum { EACTION_SAVE_STATE, EACTION_LOAD_STATE, EACTION_SCREENSHOT, +#ifdef FUNKEY_S + EACTION_NEXT_SCALER, +#endif EACTION_QUIT, } emu_action; +typedef enum { + CONFIG_TYPE_CORE = 0, + CONFIG_TYPE_GAME, + CONFIG_TYPE_AUTO, +} config_type; + extern bool should_quit; extern unsigned current_audio_buffer_size; extern char core_name[MAX_PATH]; @@ -42,10 +51,10 @@ extern char save_template_path[MAX_PATH]; int screenshot(void); void set_defaults(void); -int save_config(int is_game); +int save_config(config_type config_type); void load_config(void); void load_config_keys(void); -int remove_config(int is_game); +int remove_config(config_type config_type); void handle_emu_action(emu_action action); void pa_log(enum retro_log_level level, const char *fmt, ...); @@ -74,17 +74,7 @@ me_bind_action emuctrl_actions[] = static int emu_check_save_file(int slot, int *time) { - char fname[MAX_PATH]; - struct stat status; - int ret; - - state_file_name(fname, sizeof(fname), slot); - - ret = stat(fname, &status); - if (ret != 0) - return 0; - - return 1; + return state_exists(slot); } static int emu_save_load_game(int load, int unused) @@ -123,7 +113,7 @@ static int mh_restore_defaults(int id, int keys) static int mh_savecfg(int id, int keys) { - if (save_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0) + if (save_config(id == MA_OPT_SAVECFG_GAME ? CONFIG_TYPE_GAME : CONFIG_TYPE_CORE) == 0) menu_update_msg("config saved"); else menu_update_msg("failed to write config"); @@ -484,6 +474,8 @@ static const char h_restore_def[] = "Switches back to default settings"; static const char h_show_fps[] = "Shows frames and vsyncs per second"; static const char h_show_cpu[] = "Shows CPU usage"; + +#if (SCREEN_WIDTH >= 320) static const char h_enable_drc[] = "Dynamically adjusts audio rate for smoother video"; static const char h_audio_buffer_size[] = @@ -500,15 +492,36 @@ static const char h_scale_filter[] = "When stretching, how missing pixels are filled.\n" "Nearest copies the last pixel. Sharp keeps pixels\n" "aligned where possible. Smooth adds a blur effect."; +#else +static const char h_enable_drc[] = + "Dynamically adjusts audio rate for\n" + "smoother video."; + +static const char h_audio_buffer_size[] = + "The audio buffer size, in frames.\n" + "Higher values reduce the risk of audio\n" + "crackling at the cost of delayed sound."; +static const char h_scale_size[] = + "How much to stretch the screen when\n" + "scaling. Native does no stretching.\n" + "Aspect uses the correct aspect ratio.\n" + "Full uses the whole screen."; + +static const char h_scale_filter[] = + "When stretching, how missing pixels\n" + "are filled. Nearest copies the last\n" + "pixel. Sharp tries to keep pixels\n" + "aligned. Smooth adds a blur effect."; +#endif -static const char *men_scale_size[] = { "Native", "Aspect", "Full", NULL}; +static const char *men_scale_size[] = { "Native", "Aspect", "Full", "Crop", NULL}; static const char *men_scale_filter[] = { "Nearest", "Sharp", "Smooth", NULL}; static menu_entry e_menu_video_options[] = { mee_onoff_h ("Show FPS", 0, show_fps, 1, h_show_fps), - mee_onoff_h ("Show CPU %", 0, show_cpu, 1, h_show_cpu), + mee_onoff_h ("Show CPU %%", 0, show_cpu, 1, h_show_cpu), mee_enum_h ("Screen size", 0, scale_size, men_scale_size, h_scale_size), mee_enum_h ("Filter", 0, scale_filter, men_scale_filter, h_scale_filter), mee_range_h ("Audio buffer", 0, audio_buffer_size, 1, 15, h_audio_buffer_size), @@ -5,6 +5,7 @@ #include "options.h" #include "overrides.h" #include "util.h" +#include "scale.h" int show_fps; int show_cpu; @@ -19,8 +20,8 @@ enum scale_filter scale_filter; struct core_options core_options; #define MAX_DESC_LEN 20 -#define MAX_LINE_LEN 52 -#define MAX_LINES 3 +#define MAX_LINE_LEN SCREEN_WIDTH / 6 +#define MAX_LINES 4 static int options_default_index(const struct core_option_entry* entry, const char *default_value) { const char *value; diff --git a/overrides.c b/overrides.c index bbadb62..576328a 100644 --- a/overrides.c +++ b/overrides.c @@ -13,6 +13,7 @@ #include "overrides/smsplus.h" #include "overrides/snes9x2002.h" #include "overrides/snes9x2005.h" +#include "overrides/snes9x2005_plus.h" #include "util.h" static const struct core_override overrides[] = { @@ -30,6 +31,7 @@ static const struct core_override overrides[] = { smsplus_overrides, snes9x2002_overrides, snes9x2005_overrides, + snes9x2005_plus_overrides, }; static const struct core_override *override; diff --git a/overrides/gpsp.h b/overrides/gpsp.h index 44a35e0..86fc866 100644 --- a/overrides/gpsp.h +++ b/overrides/gpsp.h @@ -29,7 +29,7 @@ static const struct core_override_option gpsp_core_option_overrides[] = { }, { .key = "gpsp_frameskip_threshold", - .desc = "FS Threshold (%)", + .desc = "FS Threshold (%%)", .info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.", }, { diff --git a/overrides/mame2000.h b/overrides/mame2000.h index 9d306c0..81af3fc 100644 --- a/overrides/mame2000.h +++ b/overrides/mame2000.h @@ -11,7 +11,7 @@ static const struct core_override_option mame2000_core_option_overrides[] = { { .key = "mame2000-frameskip_threshold", .info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.", - .retro_var_value = "FS Threshold (%); 30|40|50|60", + .retro_var_value = "FS Threshold (%%); 30|40|50|60", }, { .key = "mame2000-frameskip_interval", diff --git a/overrides/pcsx_rearmed.h b/overrides/pcsx_rearmed.h index e68724e..b35cc2d 100644 --- a/overrides/pcsx_rearmed.h +++ b/overrides/pcsx_rearmed.h @@ -12,7 +12,7 @@ static const struct core_override_option pcsx_rearmed_core_option_overrides[] = }, { .key = "pcsx_rearmed_frameskip_threshold", - .desc = "FS Threshold (%)", + .desc = "FS Threshold (%%)", .info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.", }, { diff --git a/overrides/picodrive.h b/overrides/picodrive.h index aa1dc30..f0c9ca0 100644 --- a/overrides/picodrive.h +++ b/overrides/picodrive.h @@ -43,7 +43,7 @@ static const struct core_override_option picodrive_core_option_overrides[] = { }, { .key = "picodrive_frameskip_threshold", - .desc = "FS Threshold (%)", + .desc = "FS Threshold (%%)", .info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.", }, { diff --git a/overrides/snes9x2002.h b/overrides/snes9x2002.h index fb54780..4cb3b5d 100644 --- a/overrides/snes9x2002.h +++ b/overrides/snes9x2002.h @@ -8,7 +8,7 @@ static const struct core_override_option snes9x2002_core_option_overrides[] = { }, { .key = "snes9x2002_frameskip_threshold", - .desc = "FS Threshold (%)", + .desc = "FS Threshold (%%)", .info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.", }, { diff --git a/overrides/snes9x2005.h b/overrides/snes9x2005.h index 237a89a..b9c04a5 100644 --- a/overrides/snes9x2005.h +++ b/overrides/snes9x2005.h @@ -3,6 +3,7 @@ static const struct core_override_option snes9x2005_core_option_overrides[] = { { .key = "snes9x_2005_region", + .desc = "Region", .info = "'PAL' is 50hz, 'NTSC' is 60hz. Games will run faster or slower than normal if the incorrect region is selected.", }, { @@ -12,7 +13,7 @@ static const struct core_override_option snes9x2005_core_option_overrides[] = { }, { .key = "snes9x_2005_frameskip_threshold", - .desc = "FS Threshold (%)", + .desc = "FS Threshold (%%)", .info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.", }, { @@ -33,7 +34,11 @@ static const struct core_override_option snes9x2005_core_option_overrides[] = { }, { .key = "snes9x_2005_dynarec", +#ifdef FUNKEY_S + .default_value = "disabled", +#else .default_value = "enabled", +#endif }, { NULL } }; diff --git a/overrides/snes9x2005_plus.h b/overrides/snes9x2005_plus.h new file mode 100644 index 0000000..2720b45 --- /dev/null +++ b/overrides/snes9x2005_plus.h @@ -0,0 +1,75 @@ +#include "overrides.h" + +static const struct core_override_option snes9x2005_plus_core_option_overrides[] = { + { + .key = "snes9x_2005_region", + .desc = "Region", + .info = "'PAL' is 50hz, 'NTSC' is 60hz. Games will run faster or slower than normal if the incorrect region is selected.", + }, + { + .key = "snes9x_2005_frameskip", + .info = "Skip frames to avoid audio crackling. Improves performance at the expense of visual smoothness.", + .default_value = "auto" + }, + { + .key = "snes9x_2005_frameskip_threshold", + .desc = "FS Threshold (%%)", + .info = "When 'Frameskip' is set to 'Threshold', sets how low the audio buffer can get before frames will be skipped.", + }, + { + .key = "snes9x_2005_frameskip_interval", + .desc = "FS Interval", + .info = "The maximum number of frames that can be skipped before a new frame is rendered.", + .default_value = "3" + }, + { + .key = "snes9x_2005_overclock_cycles", + .desc = "Overclock (Restart)", + .info = "Alleviate normal SNES slowdown. Compatible keeps as much compatibility as possible. Max will reduce more slowdown but break more games.", + }, + { + .key = "snes9x_2005_reduce_sprite_flicker", + .desc = "Reduce Flicker", + .info = "Raises sprite limit to reduce flickering in games.", + }, + { + .key = "snes9x_2005_dynarec", +#ifdef FUNKEY_S + .default_value = "disabled", +#else + .default_value = "enabled", +#endif + }, + { NULL } +}; + +me_bind_action snes9x2005_plus_ctrl_actions[] = +{ + { "UP ", 1 << RETRO_DEVICE_ID_JOYPAD_UP}, + { "DOWN ", 1 << RETRO_DEVICE_ID_JOYPAD_DOWN }, + { "LEFT ", 1 << RETRO_DEVICE_ID_JOYPAD_LEFT }, + { "RIGHT ", 1 << RETRO_DEVICE_ID_JOYPAD_RIGHT }, + { "A BUTTON ", 1 << RETRO_DEVICE_ID_JOYPAD_A }, + { "B BUTTON ", 1 << RETRO_DEVICE_ID_JOYPAD_B }, + { "X BUTTON ", 1 << RETRO_DEVICE_ID_JOYPAD_X }, + { "Y BUTTON ", 1 << RETRO_DEVICE_ID_JOYPAD_Y }, + { "START ", 1 << RETRO_DEVICE_ID_JOYPAD_START }, + { "SELECT ", 1 << RETRO_DEVICE_ID_JOYPAD_SELECT }, + { "L BUTTON ", 1 << RETRO_DEVICE_ID_JOYPAD_L }, + { "R BUTTON ", 1 << RETRO_DEVICE_ID_JOYPAD_R }, + { NULL, 0 } +}; + +const struct core_override_fast_forward snes9x2005_plus_fast_forward = { + .type_key = "snes9x_2005_frameskip", + .type_value = "auto", + .interval_key = "snes9x_2005_frameskip_interval" +}; + +#define snes9x2005_plus_overrides { \ + .core_name = "snes9x2005_plus", \ + .fast_forward = &snes9x2005_plus_fast_forward, \ + .actions = snes9x2005_plus_ctrl_actions, \ + .action_size = array_size(snes9x2005_plus_ctrl_actions), \ + .options = snes9x2005_plus_core_option_overrides \ +} diff --git a/patches/libpicofe/0002-small-screen.patch b/patches/libpicofe/0002-small-screen.patch new file mode 100644 index 0000000..2a5d828 --- /dev/null +++ b/patches/libpicofe/0002-small-screen.patch @@ -0,0 +1,13 @@ +diff --git a/menu.c b/menu.c +index e91f84a..3c98f1f 100644 +--- a/menu.c ++++ b/menu.c +@@ -518,7 +518,7 @@ static void me_draw(const menu_entry *entries, int sel, void (*draw_more)(void)) + {
+ const menu_entry *ent, *ent_sel = entries;
+ int x, y, w = 0, h = 0;
+- int offs, col2_offs = 27 * me_mfont_w;
++ int offs, col2_offs = (g_menuscreen_w >= 320 ? 27 : 0) * me_mfont_w;
+ int vi_sel_ln = 0;
+ const char *name;
+ int i, n;
diff --git a/patches/snes9x2005/1001-funkey-s-support.patch b/patches/snes9x2005/1001-funkey-s-support.patch new file mode 100644 index 0000000..1825917 --- /dev/null +++ b/patches/snes9x2005/1001-funkey-s-support.patch @@ -0,0 +1,93 @@ +diff --git a/Makefile b/Makefile +index 23b8979..0a09e85 100644 +--- a/Makefile ++++ b/Makefile +@@ -74,13 +74,46 @@ ifeq ($(platform), unix) + fpic := -fPIC + SHARED := -shared -Wl,--no-undefined -Wl,--version-script=link.T + CFLAGS += -fno-builtin -fno-exceptions -ffunction-sections +-# ARM ++# (armv7 a7, hard point, neon based) ### ++# NESC, SNESC, C64 mini ++else ifeq ($(platform), classic_armv7_a7) ++ TARGET := $(TARGET_NAME)_libretro.so ++ fpic := -fPIC ++ SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined ++ CFLAGS += -Ofast \ ++ -flto=4 -fwhole-program -fuse-linker-plugin \ ++ -fdata-sections -ffunction-sections -Wl,--gc-sections \ ++ -fno-stack-protector -fno-ident -fomit-frame-pointer \ ++ -falign-functions=1 -falign-jumps=1 -falign-loops=1 \ ++ -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-unroll-loops \ ++ -fmerge-all-constants -fno-math-errno \ ++ -marm -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard ++ CC = $(CROSS_COMPILE)gcc ++ AR = $(CROSS_COMPILE)ar ++ CXXFLAGS += $(CFLAGS) ++ CPPFLAGS += $(CFLAGS) ++ ASFLAGS += $(CFLAGS) ++ HAVE_NEON = 1 ++ ARCH = arm ++ BUILTIN_GPU = neon ++ USE_DYNAREC = 1 ++ ifeq ($(shell echo `$(CC) -dumpversion` "< 4.9" | bc -l), 1) ++ CFLAGS += -march=armv7-a ++ else ++ CFLAGS += -march=armv7ve ++ # If gcc is 5.0 or later ++ ifeq ($(shell echo `$(CC) -dumpversion` ">= 5" | bc -l), 1) ++ LDFLAGS += -static-libgcc -static-libstdc++ ++ endif ++ endif ++####################################### ++# generic ARM + else ifneq (,$(findstring armv,$(platform))) + TARGET := $(TARGET_NAME)_libretro.so + fpic := -fPIC + SHARED := -shared -Wl,--no-undefined -Wl,--version-script=link.T +- CC = gcc +- CXX = g++ ++ CC = $(CROSS_COMPILE)gcc ++ CXX = $(CROSS_COMPILE)g++ + PLATFORM_DEFINES += -marm + ifneq (,$(findstring softfloat,$(platform))) + PLATFORM_DEFINES += -mfloat-abi=softfp +@@ -306,38 +339,6 @@ else + endif + OPTIMIZE += -Ofast -DNDEBUG=1 + +-# (armv7 a7, hard point, neon based) ### +-# NESC, SNESC, C64 mini +-else ifeq ($(platform), classic_armv7_a7) +- TARGET := $(TARGET_NAME)_libretro.so +- fpic := -fPIC +- SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined +- CFLAGS += -Ofast \ +- -flto=4 -fwhole-program -fuse-linker-plugin \ +- -fdata-sections -ffunction-sections -Wl,--gc-sections \ +- -fno-stack-protector -fno-ident -fomit-frame-pointer \ +- -falign-functions=1 -falign-jumps=1 -falign-loops=1 \ +- -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-unroll-loops \ +- -fmerge-all-constants -fno-math-errno \ +- -marm -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard +- CXXFLAGS += $(CFLAGS) +- CPPFLAGS += $(CFLAGS) +- ASFLAGS += $(CFLAGS) +- HAVE_NEON = 1 +- ARCH = arm +- BUILTIN_GPU = neon +- USE_DYNAREC = 1 +- ifeq ($(shell echo `$(CC) -dumpversion` "< 4.9" | bc -l), 1) +- CFLAGS += -march=armv7-a +- else +- CFLAGS += -march=armv7ve +- # If gcc is 5.0 or later +- ifeq ($(shell echo `$(CC) -dumpversion` ">= 5" | bc -l), 1) +- LDFLAGS += -static-libgcc -static-libstdc++ +- endif +- endif +-####################################### +- + # Windows MSVC 2010 x64 + else ifeq ($(platform), windows_msvc2010_x64) + CC = cl.exe diff --git a/patches/snes9x2005_plus/1001-funkey-s-support.patch b/patches/snes9x2005_plus/1001-funkey-s-support.patch new file mode 100644 index 0000000..1825917 --- /dev/null +++ b/patches/snes9x2005_plus/1001-funkey-s-support.patch @@ -0,0 +1,93 @@ +diff --git a/Makefile b/Makefile +index 23b8979..0a09e85 100644 +--- a/Makefile ++++ b/Makefile +@@ -74,13 +74,46 @@ ifeq ($(platform), unix) + fpic := -fPIC + SHARED := -shared -Wl,--no-undefined -Wl,--version-script=link.T + CFLAGS += -fno-builtin -fno-exceptions -ffunction-sections +-# ARM ++# (armv7 a7, hard point, neon based) ### ++# NESC, SNESC, C64 mini ++else ifeq ($(platform), classic_armv7_a7) ++ TARGET := $(TARGET_NAME)_libretro.so ++ fpic := -fPIC ++ SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined ++ CFLAGS += -Ofast \ ++ -flto=4 -fwhole-program -fuse-linker-plugin \ ++ -fdata-sections -ffunction-sections -Wl,--gc-sections \ ++ -fno-stack-protector -fno-ident -fomit-frame-pointer \ ++ -falign-functions=1 -falign-jumps=1 -falign-loops=1 \ ++ -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-unroll-loops \ ++ -fmerge-all-constants -fno-math-errno \ ++ -marm -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard ++ CC = $(CROSS_COMPILE)gcc ++ AR = $(CROSS_COMPILE)ar ++ CXXFLAGS += $(CFLAGS) ++ CPPFLAGS += $(CFLAGS) ++ ASFLAGS += $(CFLAGS) ++ HAVE_NEON = 1 ++ ARCH = arm ++ BUILTIN_GPU = neon ++ USE_DYNAREC = 1 ++ ifeq ($(shell echo `$(CC) -dumpversion` "< 4.9" | bc -l), 1) ++ CFLAGS += -march=armv7-a ++ else ++ CFLAGS += -march=armv7ve ++ # If gcc is 5.0 or later ++ ifeq ($(shell echo `$(CC) -dumpversion` ">= 5" | bc -l), 1) ++ LDFLAGS += -static-libgcc -static-libstdc++ ++ endif ++ endif ++####################################### ++# generic ARM + else ifneq (,$(findstring armv,$(platform))) + TARGET := $(TARGET_NAME)_libretro.so + fpic := -fPIC + SHARED := -shared -Wl,--no-undefined -Wl,--version-script=link.T +- CC = gcc +- CXX = g++ ++ CC = $(CROSS_COMPILE)gcc ++ CXX = $(CROSS_COMPILE)g++ + PLATFORM_DEFINES += -marm + ifneq (,$(findstring softfloat,$(platform))) + PLATFORM_DEFINES += -mfloat-abi=softfp +@@ -306,38 +339,6 @@ else + endif + OPTIMIZE += -Ofast -DNDEBUG=1 + +-# (armv7 a7, hard point, neon based) ### +-# NESC, SNESC, C64 mini +-else ifeq ($(platform), classic_armv7_a7) +- TARGET := $(TARGET_NAME)_libretro.so +- fpic := -fPIC +- SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined +- CFLAGS += -Ofast \ +- -flto=4 -fwhole-program -fuse-linker-plugin \ +- -fdata-sections -ffunction-sections -Wl,--gc-sections \ +- -fno-stack-protector -fno-ident -fomit-frame-pointer \ +- -falign-functions=1 -falign-jumps=1 -falign-loops=1 \ +- -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-unroll-loops \ +- -fmerge-all-constants -fno-math-errno \ +- -marm -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard +- CXXFLAGS += $(CFLAGS) +- CPPFLAGS += $(CFLAGS) +- ASFLAGS += $(CFLAGS) +- HAVE_NEON = 1 +- ARCH = arm +- BUILTIN_GPU = neon +- USE_DYNAREC = 1 +- ifeq ($(shell echo `$(CC) -dumpversion` "< 4.9" | bc -l), 1) +- CFLAGS += -march=armv7-a +- else +- CFLAGS += -march=armv7ve +- # If gcc is 5.0 or later +- ifeq ($(shell echo `$(CC) -dumpversion` ">= 5" | bc -l), 1) +- LDFLAGS += -static-libgcc -static-libstdc++ +- endif +- endif +-####################################### +- + # Windows MSVC 2010 x64 + else ifeq ($(platform), windows_msvc2010_x64) + CC = cl.exe @@ -8,8 +8,6 @@ struct audio_frame { int16_t right; }; -#define HUD_LEN 41 - int plat_init(void); int plat_reinit(void); void plat_finish(void); diff --git a/plat_funkey.c b/plat_funkey.c new file mode 100644 index 0000000..3447994 --- /dev/null +++ b/plat_funkey.c @@ -0,0 +1,115 @@ +#include <SDL/SDL.h> +#include "libretro.h" +#include "libpicofe/plat.h" +#include "libpicofe/input.h" +#include "libpicofe/in_sdl.h" +#include "main.h" +#include "util.h" + +#define MAX_SAMPLE_RATE 48000 + +static const struct in_default_bind in_sdl_defbinds[] = { + { SDLK_u, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_UP }, + { SDLK_d, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_DOWN }, + { SDLK_l, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_LEFT }, + { SDLK_r, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_RIGHT }, + { SDLK_b, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_B }, + { SDLK_a, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_A }, + { SDLK_x, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_X }, + { SDLK_y, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_Y }, + { SDLK_s, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_START }, + { SDLK_k, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_SELECT }, + { SDLK_m, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_L }, + { SDLK_n, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_R }, + { SDLK_v, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_L2 }, + { SDLK_o, IN_BINDTYPE_PLAYER12, RETRO_DEVICE_ID_JOYPAD_R2 }, + { SDLK_q, IN_BINDTYPE_EMU, EACTION_MENU }, + { SDLK_h, IN_BINDTYPE_EMU, EACTION_NEXT_SCALER }, + { 0, 0, 0 } +}; + +const struct menu_keymap in_sdl_key_map[] = +{ + { SDLK_u, PBTN_UP }, + { SDLK_d, PBTN_DOWN }, + { SDLK_l, PBTN_LEFT }, + { SDLK_r, PBTN_RIGHT }, + { SDLK_a, PBTN_MOK }, + { SDLK_b, PBTN_MBACK }, + { SDLK_x, PBTN_MA2 }, + { SDLK_y, PBTN_MA3 }, + { SDLK_m, PBTN_L }, + { SDLK_n, PBTN_R }, + { SDLK_q, PBTN_MENU }, +}; + +const struct menu_keymap in_sdl_joy_map[] = +{ + { SDLK_u, PBTN_UP }, + { SDLK_d, PBTN_DOWN }, + { SDLK_l, PBTN_LEFT }, + { SDLK_r, PBTN_RIGHT }, + { SDLK_WORLD_0, PBTN_MOK }, + { SDLK_WORLD_1, PBTN_MBACK }, + { SDLK_WORLD_2, PBTN_MA2 }, + { SDLK_WORLD_3, PBTN_MA3 }, +}; + +static const char * const in_sdl_key_names[SDLK_LAST] = { + [SDLK_u] = "UP", + [SDLK_d] = "DOWN", + [SDLK_l] = "LEFT", + [SDLK_r] = "RIGHT", + [SDLK_x] = "X", + [SDLK_b] = "B", + [SDLK_a] = "A", + [SDLK_y] = "Y", + [SDLK_s] = "START", + [SDLK_k] = "FN", + [SDLK_m] = "L", + [SDLK_n] = "R", + [SDLK_v] = "FN+L", + [SDLK_o] = "FN+R", + [SDLK_1] = "MENU+UP", + [SDLK_2] = "MENU+DOWN", + [SDLK_3] = "MENU+LEFT", + [SDLK_4] = "MENU+RIGHT", + [SDLK_5] = "MENU+B", + [SDLK_6] = "MENU+A", + [SDLK_7] = "MENU+X", + [SDLK_8] = "MENU+Y", + [SDLK_9] = "MENU+START", + [SDLK_0] = "MENU+SELECT", + [SDLK_TAB] = "MENU+L", + [SDLK_BACKSLASH] = "MENU+R", + [SDLK_q] = "MENU", +}; + +static const struct mod_keymap in_sdl_mod_keymap[] = { + { SDLK_u, SDLK_1 }, + { SDLK_d, SDLK_2 }, + { SDLK_l, SDLK_3 }, + { SDLK_r, SDLK_4 }, + { SDLK_b, SDLK_5 }, + { SDLK_a, SDLK_6 }, + { SDLK_x, SDLK_7 }, + { SDLK_y, SDLK_8 }, + { SDLK_s, SDLK_9 }, + { SDLK_k, SDLK_0 }, + { SDLK_TAB, SDLK_TAB }, /* mod+L1 = L2 */ + { SDLK_BACKSPACE, SDLK_BACKSLASH }, /* mod+R1 = R2 */ +}; + +static const struct in_pdata in_sdl_platform_data = { + .defbinds = in_sdl_defbinds, + .key_map = in_sdl_key_map, + .kmap_size = array_size(in_sdl_key_map), + .joy_map = in_sdl_joy_map, + .jmap_size = array_size(in_sdl_joy_map), + .key_names = in_sdl_key_names, + /* .mod_key = SDLK_ESCAPE, + * .mod_keymap = in_sdl_mod_keymap, + * .modmap_size = array_size(in_sdl_mod_keymap), */ +}; + +#include "plat_sdl.c" @@ -104,6 +104,29 @@ static void scale_1x(unsigned w, unsigned h, size_t pitch, const void *src, void } } +static void scale_crop(unsigned w, unsigned h, size_t pitch, const void *src, void *dst) { + int dst_y = ((SCREEN_HEIGHT - (short)h) / 2); + int dst_x = ((SCREEN_WIDTH - (short)w) * SCREEN_BPP / 2); + + if (dst_y < 0) { + src += -dst_y * pitch; + dst_y = 0; + h = SCREEN_HEIGHT; + } + + if (dst_x < 0) { + src += -dst_x; + dst_x = 0; + w = SCREEN_WIDTH; + } + + dst += dst_y * SCREEN_PITCH + dst_x; + + for (unsigned y = 0; y < h; y++) { + memcpy(dst + y * SCREEN_PITCH, src + y * pitch, w * SCREEN_BPP); + } +} + static void scale_nearest(unsigned w, unsigned h, size_t pitch, const void *src, void *dst) { int dy = -dst_h; unsigned lines = h; @@ -416,7 +439,10 @@ static void scale_select_scaler(unsigned w, unsigned h, size_t pitch) { blend_args.blend_line = NULL; } - if (scale_size == SCALE_SIZE_FULL) { + if (scale_size == SCALE_SIZE_CROP) { + scaler = scale_crop; + return; + } if (scale_size == SCALE_SIZE_FULL) { dst_w = SCREEN_WIDTH; dst_h = SCREEN_HEIGHT; dst_offs = 0; @@ -447,8 +473,8 @@ static void scale_select_scaler(unsigned w, unsigned h, size_t pitch) { if (!scaler && w == 160 && h == 144) { if (scale_size == SCALE_SIZE_ASPECT && scale_filter == SCALE_FILTER_SHARP) { - unsigned dst_x = ((320 - 240) * SCREEN_BPP / 2); - unsigned dst_y = ((240 - 216) / 2); + unsigned dst_x = ((SCREEN_WIDTH - 240) * SCREEN_BPP / 2); + unsigned dst_y = ((SCREEN_HEIGHT - 216) / 2); dst_offs = dst_y * SCREEN_PITCH + dst_x; scaler = scale_sharp_160x144_240x216; @@ -456,18 +482,16 @@ static void scale_select_scaler(unsigned w, unsigned h, size_t pitch) { } } - if (!scaler && w == 240 && h == 160) { - if (scale_filter == SCALE_FILTER_SHARP) { + if (SCREEN_WIDTH == 320 && scale_filter == SCALE_FILTER_SHARP) { + if (!scaler && w == 240 && h == 160) { scaler = scale_sharp_240x160_320xXXX; return; } - } - if (!scaler && - w == 256 && - (current_aspect_ratio == 4.0f / 3.0f || scale_size == SCALE_SIZE_FULL)) - { - if (scale_filter == SCALE_FILTER_SHARP) { + if (!scaler && + w == 256 && + (current_aspect_ratio == 4.0f / 3.0f || scale_size == SCALE_SIZE_FULL)) + { scaler = scale_sharp_256xXXX_320xXXX; return; } @@ -1,7 +1,18 @@ #ifndef __SCALE_H__ #define __SCALE_H__ +#ifdef FUNKEY_S +#define SCREEN_WIDTH 240 +#else #define SCREEN_WIDTH 320 +#endif + +#if SCREEN_WIDTH == 240 +#define HUD_LEN 31 +#else +#define HUD_LEN 41 +#endif + #define SCREEN_HEIGHT 240 #define SCREEN_BPP 2 #define SCREEN_PITCH (SCREEN_BPP * SCREEN_WIDTH) @@ -10,6 +21,7 @@ enum scale_size { SCALE_SIZE_NONE, SCALE_SIZE_ASPECT, SCALE_SIZE_FULL, + SCALE_SIZE_CROP, }; enum scale_filter { |