mirror of https://github.com/bsnes-emu/bsnes.git
Added basic menu
This commit is contained in:
parent
7a024d2a69
commit
c66e9a06cf
1594
SDL/font.c
1594
SDL/font.c
File diff suppressed because it is too large
Load Diff
|
@ -3,7 +3,12 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
extern uint8_t font[];
|
extern uint8_t font[];
|
||||||
|
extern const uint8_t font_max;
|
||||||
#define GLYPH_HEIGHT 8
|
#define GLYPH_HEIGHT 8
|
||||||
#define GLYPH_WIDTH 6
|
#define GLYPH_WIDTH 6
|
||||||
|
#define SELECTION_STRING "\x7f"
|
||||||
|
#define CTRL_STRING "\x80\x81\x82"
|
||||||
|
#define SHIFT_STRING "\x83"
|
||||||
|
#define CMD_STRING "\x84\x85"
|
||||||
#endif /* font_h */
|
#endif /* font_h */
|
||||||
|
|
||||||
|
|
213
SDL/gui.c
213
SDL/gui.c
|
@ -12,39 +12,48 @@ SDL_Renderer *renderer = NULL;
|
||||||
SDL_Texture *texture = NULL;
|
SDL_Texture *texture = NULL;
|
||||||
SDL_PixelFormat *pixel_format = NULL;
|
SDL_PixelFormat *pixel_format = NULL;
|
||||||
enum scaling_mode scaling_mode = GB_SDL_SCALING_INTEGER_FACTOR;
|
enum scaling_mode scaling_mode = GB_SDL_SCALING_INTEGER_FACTOR;
|
||||||
|
enum pending_command pending_command;
|
||||||
|
unsigned command_parameter;
|
||||||
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#define MODIFIER_NAME "Cmd"
|
#define MODIFIER_NAME " " CMD_STRING
|
||||||
#else
|
#else
|
||||||
#define MODIFIER_NAME "Ctrl"
|
#define MODIFIER_NAME CTRL_STRING
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static const char help[] =
|
static const char *help[] ={
|
||||||
"Drop a GB or GBC ROM file to play.\n"
|
"Drop a GB or GBC ROM\n"
|
||||||
|
"file to play.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
||||||
"Controls:\n"
|
"Controls:\n"
|
||||||
" D-Pad: Arrow Keys\n"
|
" D-Pad: Arrow Keys\n"
|
||||||
" A: X\n"
|
" A: X\n"
|
||||||
" B: Z\n"
|
" B: Z\n"
|
||||||
" Start: Enter\n"
|
" Start: Enter\n"
|
||||||
" Select: Backspace\n"
|
" Select: Backspace\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" Turbo: Space\n"
|
||||||
|
" Menu: Escape\n",
|
||||||
"Keyboard Shortcuts: \n"
|
"Keyboard Shortcuts: \n"
|
||||||
" Restart: " MODIFIER_NAME "+R\n"
|
" Reset: " MODIFIER_NAME "+R\n"
|
||||||
" Pause: " MODIFIER_NAME "+P\n"
|
" Pause: " MODIFIER_NAME "+P\n"
|
||||||
" Turbo: Space\n"
|
" Toggle DMG/CGB: " MODIFIER_NAME "+T\n"
|
||||||
|
"\n"
|
||||||
|
" Save state: " MODIFIER_NAME "+(0-9)\n"
|
||||||
|
" Load state: " MODIFIER_NAME "+" SHIFT_STRING "+(0-9)\n"
|
||||||
|
"\n"
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
" Mute/Unmute: " MODIFIER_NAME "+Shift+M\n"
|
" Mute/Unmute: " MODIFIER_NAME "+" SHIFT_STRING "+M\n"
|
||||||
#else
|
#else
|
||||||
" Mute/Unmute: " MODIFIER_NAME "+M\n"
|
" Mute/Unmute: " MODIFIER_NAME "+M\n"
|
||||||
#endif
|
#endif
|
||||||
" Save state: " MODIFIER_NAME "+Number (0-9)\n"
|
" Cycle scaling modes: Tab"
|
||||||
" Load state: " MODIFIER_NAME "+Shift+Number (0-9)\n"
|
"\n"
|
||||||
" Cycle between DMG/CGB emulation: " MODIFIER_NAME "+T\n"
|
" Break Debugger: " CTRL_STRING "+C"
|
||||||
" Cycle scaling modes: Tab"
|
};
|
||||||
;
|
|
||||||
|
|
||||||
void cycle_scaling(void)
|
void cycle_scaling(void)
|
||||||
{
|
{
|
||||||
|
@ -85,15 +94,10 @@ void update_viewport(void)
|
||||||
SDL_RenderSetViewport(renderer, &rect);
|
SDL_RenderSetViewport(renderer, &rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_help(void)
|
|
||||||
{
|
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Help", help, window);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Does NOT check for bounds! */
|
/* Does NOT check for bounds! */
|
||||||
static void draw_char(uint32_t *buffer, unsigned char ch, uint32_t color)
|
static void draw_char(uint32_t *buffer, unsigned char ch, uint32_t color)
|
||||||
{
|
{
|
||||||
if (ch < ' ' || ch > '~') {
|
if (ch < ' ' || ch > font_max) {
|
||||||
ch = '?';
|
ch = '?';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,17 +114,7 @@ static void draw_char(uint32_t *buffer, unsigned char ch, uint32_t color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Does NOT check for bounds! */
|
static void draw_unbordered_text(uint32_t *buffer, unsigned x, unsigned y, const char *string, uint32_t color)
|
||||||
static void draw_bordered_char(uint32_t *buffer, unsigned char ch, uint32_t color, uint32_t border)
|
|
||||||
{
|
|
||||||
draw_char(buffer - 1, ch, border);
|
|
||||||
draw_char(buffer + 1, ch, border);
|
|
||||||
draw_char(buffer - 160, ch, border);
|
|
||||||
draw_char(buffer + 160, ch, border);
|
|
||||||
draw_char(buffer, ch, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void draw_text(uint32_t *buffer, unsigned x, unsigned y, const char *string, uint32_t color, uint32_t border)
|
|
||||||
{
|
{
|
||||||
unsigned orig_x = x;
|
unsigned orig_x = x;
|
||||||
while (*string) {
|
while (*string) {
|
||||||
|
@ -131,24 +125,75 @@ static void draw_text(uint32_t *buffer, unsigned x, unsigned y, const char *stri
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x == 0 || x > 160 - GLYPH_WIDTH - 1 || y == 0 || y > 144 - GLYPH_HEIGHT - 1) {
|
if (x > 160 - GLYPH_WIDTH || y == 0 || y > 144 - GLYPH_HEIGHT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_bordered_char(&buffer[x + 160 * y], *string, color, border);
|
draw_char(&buffer[x + 160 * y], *string, color);
|
||||||
x += GLYPH_WIDTH;
|
x += GLYPH_WIDTH;
|
||||||
string++;
|
string++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_text_centered(uint32_t *buffer, unsigned y, const char *string, uint32_t color, uint32_t border)
|
static void draw_text(uint32_t *buffer, unsigned x, unsigned y, const char *string, uint32_t color, uint32_t border)
|
||||||
{
|
{
|
||||||
draw_text(buffer, 160 / 2 - (unsigned) strlen(string) * GLYPH_WIDTH / 2, y, string, color, border);
|
draw_unbordered_text(buffer, x - 1, y, string, border);
|
||||||
|
draw_unbordered_text(buffer, x + 1, y, string, border);
|
||||||
|
draw_unbordered_text(buffer, x, y - 1, string, border);
|
||||||
|
draw_unbordered_text(buffer, x, y + 1, string, border);
|
||||||
|
draw_unbordered_text(buffer, x, y, string, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void draw_text_centered(uint32_t *buffer, unsigned y, const char *string, uint32_t color, uint32_t border, bool show_selection)
|
||||||
|
{
|
||||||
|
unsigned x = 160 / 2 - (unsigned) strlen(string) * GLYPH_WIDTH / 2;
|
||||||
|
draw_text(buffer, x, y, string, color, border);
|
||||||
|
if (show_selection) {
|
||||||
|
draw_text(buffer, x - GLYPH_WIDTH, y, SELECTION_STRING, color, border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct menu_item {
|
||||||
|
const char *string;
|
||||||
|
void (*handler)(void);
|
||||||
|
};
|
||||||
|
static const struct menu_item *current_menu = NULL;
|
||||||
|
static unsigned current_selection = 0;
|
||||||
|
|
||||||
|
static enum {
|
||||||
|
SHOWING_DROP_MESSAGE,
|
||||||
|
SHOWING_MENU,
|
||||||
|
SHOWING_HELP,
|
||||||
|
} gui_state;
|
||||||
|
|
||||||
|
static void item_exit(void)
|
||||||
|
{
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned current_help_page = 0;
|
||||||
|
static void item_help(void)
|
||||||
|
{
|
||||||
|
current_help_page = 0;
|
||||||
|
gui_state = SHOWING_HELP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct menu_item paused_menu[] = {
|
||||||
|
{"Resume", NULL},
|
||||||
|
{"Help", item_help},
|
||||||
|
{"Exit", item_exit},
|
||||||
|
{NULL,}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct menu_item nonpaused_menu[] = {
|
||||||
|
{"Help", item_help},
|
||||||
|
{"Exit", item_exit},
|
||||||
|
{NULL,}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
extern void set_filename(const char *new_filename, bool new_should_free);
|
extern void set_filename(const char *new_filename, bool new_should_free);
|
||||||
void run_gui(void)
|
void run_gui(bool is_running)
|
||||||
{
|
{
|
||||||
/* Draw the "Drop file" screen */
|
/* Draw the "Drop file" screen */
|
||||||
static SDL_Surface *converted_background = NULL;
|
static SDL_Surface *converted_background = NULL;
|
||||||
|
@ -165,18 +210,37 @@ void run_gui(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t pixels[160 * 144];
|
uint32_t pixels[160 * 144];
|
||||||
memcpy(pixels, converted_background->pixels, sizeof(pixels));
|
|
||||||
|
|
||||||
draw_text_centered(pixels, 116, "Drop a GB or GBC", gui_palette_native[3], gui_palette_native[0]);
|
|
||||||
draw_text_centered(pixels, 128, "file to play", gui_palette_native[3], gui_palette_native[0]);
|
|
||||||
|
|
||||||
SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t));
|
|
||||||
SDL_RenderClear(renderer);
|
|
||||||
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
|
||||||
SDL_RenderPresent(renderer);
|
|
||||||
|
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
|
gui_state = is_running? SHOWING_MENU : SHOWING_DROP_MESSAGE;
|
||||||
|
bool should_render = true;
|
||||||
|
current_menu = is_running? paused_menu : nonpaused_menu;
|
||||||
while (SDL_WaitEvent(&event)) {
|
while (SDL_WaitEvent(&event)) {
|
||||||
|
if (should_render) {
|
||||||
|
should_render = false;
|
||||||
|
memcpy(pixels, converted_background->pixels, sizeof(pixels));
|
||||||
|
|
||||||
|
switch (gui_state) {
|
||||||
|
case SHOWING_DROP_MESSAGE:
|
||||||
|
draw_text_centered(pixels, 116, "Drop a GB or GBC", gui_palette_native[3], gui_palette_native[0], false);
|
||||||
|
draw_text_centered(pixels, 128, "file to play", gui_palette_native[3], gui_palette_native[0], false);
|
||||||
|
break;
|
||||||
|
case SHOWING_MENU:
|
||||||
|
draw_text_centered(pixels, 16, "SameBoy", gui_palette_native[3], gui_palette_native[0], false);
|
||||||
|
unsigned i = 0;
|
||||||
|
for (const struct menu_item *item = current_menu; item->string; item++, i++) {
|
||||||
|
draw_text_centered(pixels, 12 * i + 40, item->string, gui_palette_native[3], gui_palette_native[0], i == current_selection);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SHOWING_HELP:
|
||||||
|
draw_text(pixels, 2, 2, help[current_help_page], gui_palette_native[3], gui_palette_native[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t));
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_QUIT: {
|
case SDL_QUIT: {
|
||||||
exit(0);
|
exit(0);
|
||||||
|
@ -192,21 +256,54 @@ void run_gui(void)
|
||||||
}
|
}
|
||||||
case SDL_DROPFILE: {
|
case SDL_DROPFILE: {
|
||||||
set_filename(event.drop.file, true);
|
set_filename(event.drop.file, true);
|
||||||
|
pending_command = GB_SDL_NEW_FILE_COMMAND;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
if (event.key.keysym.sym == SDLK_TAB) {
|
if (event.key.keysym.sym == SDLK_TAB) {
|
||||||
cycle_scaling();
|
cycle_scaling();
|
||||||
}
|
}
|
||||||
#ifndef __APPLE__
|
else if (event.key.keysym.sym == SDLK_ESCAPE) {
|
||||||
else if (event.key.keysym.sym == SDLK_F1) {
|
if (is_running) {
|
||||||
show_help();
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (gui_state == SHOWING_DROP_MESSAGE) {
|
||||||
|
gui_state = SHOWING_MENU;
|
||||||
|
}
|
||||||
|
else if (gui_state == SHOWING_MENU) {
|
||||||
|
gui_state = SHOWING_DROP_MESSAGE;
|
||||||
|
}
|
||||||
|
should_render = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
else if (event.key.keysym.sym == SDLK_QUESTION || (event.key.keysym.sym && (event.key.keysym.mod & KMOD_SHIFT))) {
|
if (gui_state == SHOWING_MENU) {
|
||||||
show_help();
|
if (event.key.keysym.sym == SDLK_DOWN && current_menu[current_selection + 1].string) {
|
||||||
|
current_selection++;
|
||||||
|
should_render = true;
|
||||||
|
}
|
||||||
|
else if (event.key.keysym.sym == SDLK_UP && current_selection) {
|
||||||
|
current_selection--;
|
||||||
|
should_render = true;
|
||||||
|
}
|
||||||
|
else if (event.key.keysym.sym == SDLK_RETURN) {
|
||||||
|
if (current_menu[current_selection].handler) {
|
||||||
|
current_menu[current_selection].handler();
|
||||||
|
should_render = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(gui_state == SHOWING_HELP) {
|
||||||
|
current_help_page++;
|
||||||
|
if (current_help_page == sizeof(help) / sizeof(help[0])) {
|
||||||
|
gui_state = SHOWING_MENU;
|
||||||
|
}
|
||||||
|
should_render = true;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
SDL/gui.h
15
SDL/gui.h
|
@ -17,9 +17,20 @@ enum scaling_mode {
|
||||||
|
|
||||||
extern enum scaling_mode scaling_mode;
|
extern enum scaling_mode scaling_mode;
|
||||||
|
|
||||||
|
enum pending_command {
|
||||||
|
GB_SDL_NO_COMMAND,
|
||||||
|
GB_SDL_SAVE_STATE_COMMAND,
|
||||||
|
GB_SDL_LOAD_STATE_COMMAND,
|
||||||
|
GB_SDL_RESET_COMMAND,
|
||||||
|
GB_SDL_NEW_FILE_COMMAND,
|
||||||
|
GB_SDL_TOGGLE_MODEL_COMMAND,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern enum pending_command pending_command;
|
||||||
|
extern unsigned command_parameter;
|
||||||
|
|
||||||
void update_viewport(void);
|
void update_viewport(void);
|
||||||
void cycle_scaling(void);
|
void cycle_scaling(void);
|
||||||
void show_help(void);
|
|
||||||
|
|
||||||
void run_gui(void);
|
void run_gui(bool is_running);
|
||||||
#endif
|
#endif
|
||||||
|
|
30
SDL/main.c
30
SDL/main.c
|
@ -71,17 +71,6 @@ static const char *end_capturing_logs(bool show_popup, bool should_exit)
|
||||||
return captured_log;
|
return captured_log;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum {
|
|
||||||
GB_SDL_NO_COMMAND,
|
|
||||||
GB_SDL_SAVE_STATE_COMMAND,
|
|
||||||
GB_SDL_LOAD_STATE_COMMAND,
|
|
||||||
GB_SDL_RESET_COMMAND,
|
|
||||||
GB_SDL_NEW_FILE_COMMAND,
|
|
||||||
GB_SDL_TOGGLE_MODEL_COMMAND,
|
|
||||||
} pending_command;
|
|
||||||
|
|
||||||
static unsigned command_parameter;
|
|
||||||
|
|
||||||
static void handle_events(GB_gameboy_t *gb)
|
static void handle_events(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
@ -111,6 +100,10 @@ static void handle_events(GB_gameboy_t *gb)
|
||||||
|
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
switch (event.key.keysym.sym) {
|
switch (event.key.keysym.sym) {
|
||||||
|
case SDLK_ESCAPE:
|
||||||
|
run_gui(true);
|
||||||
|
break;
|
||||||
|
|
||||||
case SDLK_c:
|
case SDLK_c:
|
||||||
if (event.type == SDL_KEYDOWN && (event.key.keysym.mod & KMOD_CTRL)) {
|
if (event.type == SDL_KEYDOWN && (event.key.keysym.mod & KMOD_CTRL)) {
|
||||||
GB_debugger_break(gb);
|
GB_debugger_break(gb);
|
||||||
|
@ -151,18 +144,6 @@ static void handle_events(GB_gameboy_t *gb)
|
||||||
case SDLK_TAB:
|
case SDLK_TAB:
|
||||||
cycle_scaling();
|
cycle_scaling();
|
||||||
break;
|
break;
|
||||||
#ifndef __APPLE__
|
|
||||||
case SDLK_F1:
|
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Help", help, window);
|
|
||||||
break;
|
|
||||||
#else
|
|
||||||
case SDLK_SLASH:
|
|
||||||
if (!(event.key.keysym.sym && (event.key.keysym.mod & KMOD_SHIFT))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SDLK_QUESTION:
|
|
||||||
show_help();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* Save states */
|
/* Save states */
|
||||||
|
@ -292,6 +273,7 @@ static bool handle_pending_command(void)
|
||||||
|
|
||||||
static void run(void)
|
static void run(void)
|
||||||
{
|
{
|
||||||
|
pending_command = GB_SDL_NO_COMMAND;
|
||||||
restart:
|
restart:
|
||||||
if (GB_is_inited(&gb)) {
|
if (GB_is_inited(&gb)) {
|
||||||
GB_switch_model_and_reset(&gb, !dmg);
|
GB_switch_model_and_reset(&gb, !dmg);
|
||||||
|
@ -420,7 +402,7 @@ usage:
|
||||||
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
|
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
|
||||||
|
|
||||||
if (filename == NULL) {
|
if (filename == NULL) {
|
||||||
run_gui();
|
run_gui(false);
|
||||||
}
|
}
|
||||||
run(); // Never returns
|
run(); // Never returns
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue