ref: 58b48c44b9f17095ebeccf1b8550fe137bd1f465
parent: 7c273d147f0805abbf5753cb32800de176e21884
author: laamaa <jonne.kokkonen@gmail.com>
date: Tue Sep 9 10:53:11 EDT 2025
centralize inline font handling, config ui tweaks
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,7 +15,7 @@
endif ()
-file(GLOB m8c_SRC "src/*.h" "src/*.c" "src/backends/*.h" "src/backends/*.c" "src/fonts/*.h")
+file(GLOB m8c_SRC "src/*.h" "src/*.c" "src/backends/*.h" "src/backends/*.c" "src/fonts/*.h" "src/fonts/*.c")
set(MACOS_CONTENTS "${CMAKE_CURRENT_SOURCE_DIR}/package/macos/m8c.app/Contents") set(MACOS_ENTITLEMENTS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/package/macos/Entitlements.plist")--- a/src/SDL2_inprint.h
+++ b/src/SDL2_inprint.h
@@ -4,10 +4,10 @@
#ifndef SDL2_inprint_h
#define SDL2_inprint_h
-#include "fonts/inline_font.h"
+#include "fonts/fonts.h"
#include <SDL3/SDL.h>
-extern void inline_font_initialize(struct inline_font *font);
+extern void inline_font_initialize(const struct inline_font *font);
extern void inline_font_close(void);
extern void inline_font_set_renderer(SDL_Renderer *renderer);
@@ -17,6 +17,6 @@
extern void inprint(SDL_Renderer *dst, const char *str, Uint32 x, Uint32 y, Uint32 fgcolor,
Uint32 bgcolor);
-extern SDL_Texture *get_inline_font(void);
+const struct inline_font *inline_font_get_current(void);
#endif /* SDL2_inprint_h */
--- a/src/fonts/font1.h
+++ b/src/fonts/font1.h
@@ -1,7 +1,7 @@
#ifndef FONT1_H_
#define FONT1_H_
-#include "inline_font.h"
+#include "fonts.h"
struct inline_font font_v1_small = {470,
--- a/src/fonts/font2.h
+++ b/src/fonts/font2.h
@@ -1,6 +1,7 @@
#ifndef FONT2_H_
#define FONT2_H_
-#include "inline_font.h"
+
+#include "fonts.h"
struct inline_font font_v1_large = {752,
--- a/src/fonts/font3.h
+++ b/src/fonts/font3.h
@@ -1,7 +1,7 @@
#ifndef FONT3_H_
#define FONT3_H_
-#include "inline_font.h"
+#include "fonts.h"
struct inline_font font_v2_small = {846,
--- a/src/fonts/font4.h
+++ b/src/fonts/font4.h
@@ -1,7 +1,7 @@
#ifndef FONT4_H_
#define FONT4_H_
-#include "inline_font.h"
+#include "fonts.h"
struct inline_font font_v2_large = {940,
--- a/src/fonts/font5.h
+++ b/src/fonts/font5.h
@@ -1,7 +1,7 @@
#ifndef FONT5_H_
#define FONT5_H_
-#include "inline_font.h"
+#include "fonts.h"
struct inline_font font_v2_huge = {1128,
--- /dev/null
+++ b/src/fonts/fonts.c
@@ -1,0 +1,29 @@
+// fonts.c
+#include "fonts.h"
+#include "font1.h"
+#include "font2.h"
+#include "font3.h"
+#include "font4.h"
+#include "font5.h"
+
+#include <stddef.h>
+
+static struct inline_font *fonts_storage[] = {+ &font_v1_small, &font_v1_large, &font_v2_small, &font_v2_large, &font_v2_huge
+};
+
+#define FONT_COUNT (sizeof(fonts_storage) / sizeof(fonts_storage[0]))
+
+size_t fonts_count(void) {+ return FONT_COUNT;
+}
+
+const struct inline_font *fonts_get(const size_t index) {+ return (index < FONT_COUNT) ? (const struct inline_font *)fonts_storage[index] : NULL;
+}
+
+const struct inline_font *const *fonts_all(size_t *count) {+ if (count) *count = FONT_COUNT;
+ // Cast to a read-only view so callers can’t mutate the array or elements
+ return (const struct inline_font *const *)fonts_storage;
+}
\ No newline at end of file
--- /dev/null
+++ b/src/fonts/fonts.h
@@ -1,0 +1,29 @@
+// fonts.h
+#ifndef FONTS_H
+#define FONTS_H
+
+#include <stddef.h>
+
+struct inline_font {+ const int width;
+ const int height;
+ const int glyph_x;
+ const int glyph_y;
+ const int screen_offset_x;
+ const int screen_offset_y;
+ const int text_offset_y;
+ const int waveform_max_height;
+ const long image_size;
+ const unsigned char image_data[];
+};
+
+// Number of available fonts
+size_t fonts_count(void);
+
+// Get a font by index (returns NULL if index is out of range)
+const struct inline_font *fonts_get(size_t index);
+
+// Get the whole font table (read-only). If count != NULL, it receives the length.
+const struct inline_font *const *fonts_all(size_t *count);
+
+#endif // FONTS_H
\ No newline at end of file
--- a/src/fonts/inline_font.h
+++ /dev/null
@@ -1,17 +1,0 @@
-#ifndef INLINE_FONT_H_
-#define INLINE_FONT_H_
-
-struct inline_font {- const int width;
- const int height;
- const int glyph_x;
- const int glyph_y;
- const int screen_offset_x;
- const int screen_offset_y;
- const int text_offset_y;
- const int waveform_max_height;
- const long image_size;
- const unsigned char image_data[];
-};
-
-#endif
--- a/src/inprint2.c
+++ b/src/inprint2.c
@@ -2,7 +2,7 @@
// https://github.com/driedfruit/SDL_inprint Released into public domain.
// Modified to support multiple fonts & adding a background to text.
-#include "fonts/inline_font.h"
+#include "fonts/fonts.h"
#include <SDL3/SDL.h>
#define CHARACTERS_PER_ROW 94
@@ -14,18 +14,18 @@
static SDL_Renderer *selected_renderer = NULL;
static SDL_Texture *inline_font = NULL;
static SDL_Texture *selected_font = NULL;
-static struct inline_font *selected_inline_font;
+static const struct inline_font *selected_inline_font;
static Uint16 selected_font_w, selected_font_h;
-void inline_font_initialize(struct inline_font *font) {+void inline_font_initialize(const struct inline_font *font) { if (inline_font != NULL) {return;
}
- selected_font_w = font->width;
- selected_font_h = font->height;
selected_inline_font = font;
+ selected_font_w = selected_inline_font->width;
+ selected_font_h = selected_inline_font->height;
SDL_IOStream *font_bmp =
SDL_IOFromConstMem(selected_inline_font->image_data, selected_inline_font->image_size);
@@ -132,4 +132,7 @@
d_rect.x += (float)selected_inline_font->glyph_x + 1;
}
}
-SDL_Texture *get_inline_font(void) { return selected_font; }+
+const struct inline_font *inline_font_get_current(void) {+ return selected_inline_font;
+}
--- a/src/render.c
+++ b/src/render.c
@@ -11,12 +11,7 @@
#include "fx_cube.h"
#include "settings.h"
-#include "fonts/font1.h"
-#include "fonts/font2.h"
-#include "fonts/font3.h"
-#include "fonts/font4.h"
-#include "fonts/font5.h"
-#include "fonts/inline_font.h"
+#include "fonts/fonts.h"
#include <stdlib.h>
@@ -58,9 +53,6 @@
static int screensaver_initialized = 0;
-struct inline_font *fonts[5] = {&font_v1_small, &font_v1_large, &font_v2_small, &font_v2_large,- &font_v2_huge};
-
uint8_t fullscreen = 0;
static uint8_t dirty = 0;
@@ -144,10 +136,10 @@
setup_hd_texture_scaling();
}
-static void change_font(struct inline_font *font) {+static void change_font(const unsigned int index) {inline_font_close();
inline_font_set_renderer(rend);
- inline_font_initialize(font);
+ inline_font_initialize(fonts_get(index));
}
// Append a formatted line to the circular log buffer
@@ -229,13 +221,14 @@
// Use a small font to fit more lines
const int prev_font_mode = font_mode;
inline_font_close();
- inline_font_initialize(fonts[0]);
+ const struct inline_font *font_small = fonts_get(0);
+ inline_font_initialize(font_small);
- const int line_height = fonts[0]->glyph_y + 1;
+ const int line_height = font_small->glyph_y + 1;
const int margin_x = 2;
const int margin_y = 1;
const int usable_width = texture_width - (margin_x * 2);
- const int cols = SDL_max(1, usable_width / (fonts[0]->glyph_x + 1));
+ const int cols = SDL_max(1, usable_width / (font_small->glyph_x + 1));
// Compute how many text rows fit
const int max_rows = (texture_height - margin_y * 2) / line_height;
@@ -301,7 +294,7 @@
// Restore previous font
inline_font_close();
- inline_font_initialize(fonts[prev_font_mode]);
+ inline_font_initialize(fonts_get(prev_font_mode));
SDL_SetRenderTarget(rend, prev_target);
}
@@ -372,11 +365,12 @@
return;
font_mode = mode;
- screen_offset_y = fonts[mode]->screen_offset_y;
- text_offset_y = fonts[mode]->text_offset_y;
- waveform_max_height = fonts[mode]->waveform_max_height;
+ const struct inline_font *new_font = fonts_get(mode);
+ screen_offset_y = new_font->screen_offset_y;
+ text_offset_y = new_font->text_offset_y;
+ waveform_max_height = new_font->waveform_max_height;
- change_font(fonts[mode]);
+ change_font(mode);
SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Font mode %i, Screen offset %i", mode, screen_offset_y);
}
@@ -522,8 +516,8 @@
void display_keyjazz_overlay(const uint8_t show, const uint8_t base_octave,
const uint8_t velocity) {- const Uint16 overlay_offset_x = texture_width - (fonts[font_mode]->glyph_x * 7 + 1);
- const Uint16 overlay_offset_y = texture_height - (fonts[font_mode]->glyph_y + 1);
+ const Uint16 overlay_offset_x = texture_width - (fonts_get(font_mode)->glyph_x * 7 + 1);
+ const Uint16 overlay_offset_y = texture_height - (fonts_get(font_mode)->glyph_y + 1);
const Uint32 bg_color =
global_background_color.r << 16 | global_background_color.g << 8 | global_background_color.b;
@@ -531,7 +525,7 @@
char overlay_text[7];
SDL_snprintf(overlay_text, sizeof(overlay_text), "%02X %u", velocity, base_octave);
inprint(rend, overlay_text, overlay_offset_x, overlay_offset_y, 0xC8C8C8, bg_color);
- inprint(rend, "*", overlay_offset_x + (fonts[font_mode]->glyph_x * 5 + 5), overlay_offset_y,
+ inprint(rend, "*", overlay_offset_x + (fonts_get(font_mode)->glyph_x * 5 + 5), overlay_offset_y,
0xFF0000, bg_color);
} else {inprint(rend, " ", overlay_offset_x, overlay_offset_y, 0xC8C8C8, bg_color);
@@ -752,7 +746,7 @@
global_background_color.g = 0;
global_background_color.b = 0;
fx_cube_init(rend, (SDL_Color){255, 255, 255, 255}, texture_width, texture_height,- fonts[font_mode]->glyph_x);
+ fonts_get(font_mode)->glyph_x);
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Screensaver initialized");
screensaver_initialized = 1;
return 1;
--- a/src/settings.c
+++ b/src/settings.c
@@ -3,18 +3,21 @@
#include "SDL2_inprint.h"
#include "common.h"
#include <SDL3/SDL.h>
+#include "fonts/fonts.h"
// Internal state
static int g_settings_open = 0;
static int g_selected_index = 0;
static int g_needs_redraw = 1;
+static int g_config_saved = 0;
static SDL_Texture *g_settings_texture = NULL;
+
typedef enum capture_mode_t {- CAPTURE_NONE,
- CAPTURE_KEY,
- CAPTURE_BUTTON,
- CAPTURE_AXIS
+ CAPTURE_NONE,
+ CAPTURE_KEY,
+ CAPTURE_BUTTON,
+ CAPTURE_AXIS
} capture_mode_t;
static capture_mode_t g_capture_mode = CAPTURE_NONE;
@@ -21,398 +24,536 @@
static void *g_capture_target = NULL; // points to conf field
typedef enum item_type_t {- ITEM_HEADER,
- ITEM_TOGGLE_BOOL,
- ITEM_SAVE,
- ITEM_CLOSE,
- ITEM_SUBMENU,
- ITEM_BIND_KEY,
- ITEM_BIND_BUTTON,
- ITEM_BIND_AXIS,
- ITEM_INT_ADJ
+ ITEM_HEADER,
+ ITEM_TOGGLE_BOOL,
+ ITEM_SAVE,
+ ITEM_CLOSE,
+ ITEM_SUBMENU,
+ ITEM_BIND_KEY,
+ ITEM_BIND_BUTTON,
+ ITEM_BIND_AXIS,
+ ITEM_INT_ADJ
} item_type_t;
typedef struct setting_item_s {- const char *label;
- item_type_t type;
- void *target;
- int step;
- int min_val;
- int max_val;
+ const char *label;
+ item_type_t type;
+ void *target;
+ int step;
+ int min_val;
+ int max_val;
} setting_item_s;
typedef enum settings_view_t { VIEW_ROOT, VIEW_KEYS, VIEW_GAMEPAD, VIEW_ANALOG } settings_view_t;static settings_view_t g_view = VIEW_ROOT;
+extern SDL_Gamepad *game_controllers[4];
-static void add_item(setting_item_s *items, int *count, const char *label, item_type_t type, void *target,
- int step, int min_val, int max_val) {- items[*count] = (setting_item_s){label, type, target, step, min_val, max_val};- (*count)++;
+static void add_item(setting_item_s *items, int *count, const char *label, item_type_t type,
+ void *target, int step, int min_val, int max_val) {+ items[*count] = (setting_item_s){label, type, target, step, min_val, max_val};+ (*count)++;
}
static void build_menu(const config_params_s *conf, setting_item_s *items, int *count) {- *count = 0;
- switch (g_view) {- case VIEW_ROOT:
- add_item(items, count, "Graphics", ITEM_HEADER, NULL, 0, 0, 0);
- add_item(items, count, "Integer scaling", ITEM_TOGGLE_BOOL, (void *)&conf->integer_scaling, 0, 0, 0);
- add_item(items, count, "Fullscreen", ITEM_TOGGLE_BOOL, (void *)&conf->init_fullscreen, 0, 0, 0);
-
- add_item(items, count, "Audio", ITEM_HEADER, NULL, 0, 0, 0);
- add_item(items, count, "Audio enabled", ITEM_TOGGLE_BOOL, (void *)&conf->audio_enabled, 0, 0, 0);
-
- add_item(items, count, "Bindings", ITEM_HEADER, NULL, 0, 0, 0);
- add_item(items, count, "Keyboard bindings >", ITEM_SUBMENU, NULL, 0, 0, 0);
- add_item(items, count, "Gamepad bindings >", ITEM_SUBMENU, NULL, 0, 0, 0);
- add_item(items, count, "Analog settings >", ITEM_SUBMENU, NULL, 0, 0, 0);
-
- add_item(items, count, "Save", ITEM_SAVE, NULL, 0, 0, 0);
- add_item(items, count, "Close", ITEM_CLOSE, NULL, 0, 0, 0);
- break;
- case VIEW_KEYS:
- add_item(items, count, "Keyboard bindings", ITEM_HEADER, NULL, 0, 0, 0);
- add_item(items, count, "Up", ITEM_BIND_KEY, (void *)&conf->key_up, 0, 0, 0);
- add_item(items, count, "Left", ITEM_BIND_KEY, (void *)&conf->key_left, 0, 0, 0);
- add_item(items, count, "Down", ITEM_BIND_KEY, (void *)&conf->key_down, 0, 0, 0);
- add_item(items, count, "Right", ITEM_BIND_KEY, (void *)&conf->key_right, 0, 0, 0);
- add_item(items, count, "Select", ITEM_BIND_KEY, (void *)&conf->key_select, 0, 0, 0);
- add_item(items, count, "Start", ITEM_BIND_KEY, (void *)&conf->key_start, 0, 0, 0);
- add_item(items, count, "Opt", ITEM_BIND_KEY, (void *)&conf->key_opt, 0, 0, 0);
- add_item(items, count, "Edit", ITEM_BIND_KEY, (void *)&conf->key_edit, 0, 0, 0);
- add_item(items, count, "Delete", ITEM_BIND_KEY, (void *)&conf->key_delete, 0, 0, 0);
- add_item(items, count, "Reset", ITEM_BIND_KEY, (void *)&conf->key_reset, 0, 0, 0);
- add_item(items, count, "Toggle audio", ITEM_BIND_KEY, (void *)&conf->key_toggle_audio, 0, 0, 0);
- add_item(items, count, "Toggle log", ITEM_BIND_KEY, (void *)&conf->key_toggle_log, 0, 0, 0);
- add_item(items, count, "Back", ITEM_CLOSE, NULL, 0, 0, 0);
- break;
- case VIEW_GAMEPAD:
- add_item(items, count, "Gamepad bindings", ITEM_HEADER, NULL, 0, 0, 0);
- add_item(items, count, "Up", ITEM_BIND_BUTTON, (void *)&conf->gamepad_up, 0, 0, 0);
- add_item(items, count, "Left", ITEM_BIND_BUTTON, (void *)&conf->gamepad_left, 0, 0, 0);
- add_item(items, count, "Down", ITEM_BIND_BUTTON, (void *)&conf->gamepad_down, 0, 0, 0);
- add_item(items, count, "Right", ITEM_BIND_BUTTON, (void *)&conf->gamepad_right, 0, 0, 0);
- add_item(items, count, "Select", ITEM_BIND_BUTTON, (void *)&conf->gamepad_select, 0, 0, 0);
- add_item(items, count, "Start", ITEM_BIND_BUTTON, (void *)&conf->gamepad_start, 0, 0, 0);
- add_item(items, count, "Opt", ITEM_BIND_BUTTON, (void *)&conf->gamepad_opt, 0, 0, 0);
- add_item(items, count, "Edit", ITEM_BIND_BUTTON, (void *)&conf->gamepad_edit, 0, 0, 0);
- add_item(items, count, "Quit", ITEM_BIND_BUTTON, (void *)&conf->gamepad_quit, 0, 0, 0);
- add_item(items, count, "Reset", ITEM_BIND_BUTTON, (void *)&conf->gamepad_reset, 0, 0, 0);
- add_item(items, count, "Back", ITEM_CLOSE, NULL, 0, 0, 0);
- break;
- case VIEW_ANALOG:
- add_item(items, count, "Analog settings", ITEM_HEADER, NULL, 0, 0, 0);
- add_item(items, count, "Analog threshold", ITEM_INT_ADJ, (void *)&conf->gamepad_analog_threshold, 1000, 1000, 32767);
- add_item(items, count, "Axis Up/Down", ITEM_BIND_AXIS, (void *)&conf->gamepad_analog_axis_updown, 0, 0, 0);
- add_item(items, count, "Axis Left/Right", ITEM_BIND_AXIS, (void *)&conf->gamepad_analog_axis_leftright, 0, 0, 0);
- add_item(items, count, "Axis Select", ITEM_BIND_AXIS, (void *)&conf->gamepad_analog_axis_select, 0, 0, 0);
- add_item(items, count, "Axis Start", ITEM_BIND_AXIS, (void *)&conf->gamepad_analog_axis_start, 0, 0, 0);
- add_item(items, count, "Axis Opt", ITEM_BIND_AXIS, (void *)&conf->gamepad_analog_axis_opt, 0, 0, 0);
- add_item(items, count, "Axis Edit", ITEM_BIND_AXIS, (void *)&conf->gamepad_analog_axis_edit, 0, 0, 0);
- add_item(items, count, "Back", ITEM_CLOSE, NULL, 0, 0, 0);
- break;
- }
+ *count = 0;
+ switch (g_view) {+ case VIEW_ROOT:
+ add_item(items, count, "Graphics ", ITEM_HEADER, NULL, 0, 0, 0);
+ add_item(items, count, "Integer scaling", ITEM_TOGGLE_BOOL, (void *)&conf->integer_scaling, 0,
+ 0, 0);
+ add_item(items, count, "Fullscreen ", ITEM_TOGGLE_BOOL, (void *)&conf->init_fullscreen, 0,
+ 0, 0);
+ add_item(items, count, "", ITEM_HEADER, NULL, 0, 0, 0);
+ add_item(items, count, "Audio ", ITEM_HEADER, NULL, 0, 0, 0);
+ add_item(items, count, "Audio enabled ", ITEM_TOGGLE_BOOL, (void *)&conf->audio_enabled, 0, 0,
+ 0);
+ add_item(items, count, "", ITEM_HEADER, NULL, 0, 0, 0);
+ add_item(items, count, "Bindings ", ITEM_HEADER, NULL, 0, 0, 0);
+ add_item(items, count, "Keyboard bindings >", ITEM_SUBMENU, NULL, 0, 0, 0);
+ add_item(items, count, "Gamepad bindings >", ITEM_SUBMENU, NULL, 0, 0, 0);
+ add_item(items, count, "Gamepad analog axis >", ITEM_SUBMENU, NULL, 0, 0, 0);
+ add_item(items, count, "", ITEM_HEADER, NULL, 0, 0, 0);
+ add_item(items, count, "Save", ITEM_SAVE, NULL, 0, 0, 0);
+ add_item(items, count, "Close", ITEM_CLOSE, NULL, 0, 0, 0);
+ break;
+ case VIEW_KEYS:
+ add_item(items, count, "Keyboard bindings", ITEM_HEADER, NULL, 0, 0, 0);
+ add_item(items, count, "Up ", ITEM_BIND_KEY, (void *)&conf->key_up, 0, 0, 0);
+ add_item(items, count, "Left ", ITEM_BIND_KEY, (void *)&conf->key_left, 0, 0, 0);
+ add_item(items, count, "Down ", ITEM_BIND_KEY, (void *)&conf->key_down, 0, 0, 0);
+ add_item(items, count, "Right ", ITEM_BIND_KEY, (void *)&conf->key_right, 0, 0, 0);
+ add_item(items, count, "Select ", ITEM_BIND_KEY, (void *)&conf->key_select, 0, 0, 0);
+ add_item(items, count, "Start ", ITEM_BIND_KEY, (void *)&conf->key_start, 0, 0, 0);
+ add_item(items, count, "Opt ", ITEM_BIND_KEY, (void *)&conf->key_opt, 0, 0, 0);
+ add_item(items, count, "Edit ", ITEM_BIND_KEY, (void *)&conf->key_edit, 0, 0, 0);
+ add_item(items, count, "Delete ", ITEM_BIND_KEY, (void *)&conf->key_delete, 0, 0, 0);
+ add_item(items, count, "Reset ", ITEM_BIND_KEY, (void *)&conf->key_reset, 0, 0, 0);
+ add_item(items, count, "Toggle audio", ITEM_BIND_KEY, (void *)&conf->key_toggle_audio, 0, 0, 0);
+ add_item(items, count, "Toggle log ", ITEM_BIND_KEY, (void *)&conf->key_toggle_log, 0, 0, 0);
+ add_item(items, count, "", ITEM_HEADER, NULL, 0, 0, 0);
+ add_item(items, count, "Back", ITEM_CLOSE, NULL, 0, 0, 0);
+ break;
+ case VIEW_GAMEPAD:
+ add_item(items, count, "Gamepad bindings", ITEM_HEADER, NULL, 0, 0, 0);
+ add_item(items, count, "Up ", ITEM_BIND_BUTTON, (void *)&conf->gamepad_up, 0, 0, 0);
+ add_item(items, count, "Left ", ITEM_BIND_BUTTON, (void *)&conf->gamepad_left, 0, 0, 0);
+ add_item(items, count, "Down ", ITEM_BIND_BUTTON, (void *)&conf->gamepad_down, 0, 0, 0);
+ add_item(items, count, "Right ", ITEM_BIND_BUTTON, (void *)&conf->gamepad_right, 0, 0, 0);
+ add_item(items, count, "Select", ITEM_BIND_BUTTON, (void *)&conf->gamepad_select, 0, 0, 0);
+ add_item(items, count, "Start ", ITEM_BIND_BUTTON, (void *)&conf->gamepad_start, 0, 0, 0);
+ add_item(items, count, "Opt ", ITEM_BIND_BUTTON, (void *)&conf->gamepad_opt, 0, 0, 0);
+ add_item(items, count, "Edit ", ITEM_BIND_BUTTON, (void *)&conf->gamepad_edit, 0, 0, 0);
+ add_item(items, count, "Quit ", ITEM_BIND_BUTTON, (void *)&conf->gamepad_quit, 0, 0, 0);
+ add_item(items, count, "Reset ", ITEM_BIND_BUTTON, (void *)&conf->gamepad_reset, 0, 0, 0);
+ add_item(items, count, "", ITEM_HEADER, NULL, 0, 0, 0);
+ add_item(items, count, "Back", ITEM_CLOSE, NULL, 0, 0, 0);
+ break;
+ case VIEW_ANALOG:
+ add_item(items, count, "Gamepad analog axis", ITEM_HEADER, NULL, 0, 0, 0);
+ add_item(items, count, "Deadzone", ITEM_INT_ADJ, (void *)&conf->gamepad_analog_threshold, 1000,
+ 1000, 32767);
+ add_item(items, count, "Axis Up/Down ", ITEM_BIND_AXIS,
+ (void *)&conf->gamepad_analog_axis_updown, 0, 0, 0);
+ add_item(items, count, "Axis Left/Right", ITEM_BIND_AXIS,
+ (void *)&conf->gamepad_analog_axis_leftright, 0, 0, 0);
+ add_item(items, count, "Axis Select ", ITEM_BIND_AXIS,
+ (void *)&conf->gamepad_analog_axis_select, 0, 0, 0);
+ add_item(items, count, "Axis Start ", ITEM_BIND_AXIS,
+ (void *)&conf->gamepad_analog_axis_start, 0, 0, 0);
+ add_item(items, count, "Axis Opt ", ITEM_BIND_AXIS,
+ (void *)&conf->gamepad_analog_axis_opt, 0, 0, 0);
+ add_item(items, count, "Axis Edit ", ITEM_BIND_AXIS,
+ (void *)&conf->gamepad_analog_axis_edit, 0, 0, 0);
+ add_item(items, count, "", ITEM_HEADER, NULL, 0, 0, 0);
+ add_item(items, count, "Back", ITEM_CLOSE, NULL, 0, 0, 0);
+ break;
+ }
}
static void settings_destroy_texture(SDL_Renderer *rend) {- (void)rend;
- if (g_settings_texture != NULL) {- SDL_DestroyTexture(g_settings_texture);
- g_settings_texture = NULL;
- }
+ (void)rend;
+ if (g_settings_texture != NULL) {+ SDL_DestroyTexture(g_settings_texture);
+ g_settings_texture = NULL;
+ }
}
void settings_toggle_open(void) {- g_settings_open = !g_settings_open;
- g_selected_index = 1; // first actionable item
- g_capture_mode = CAPTURE_NONE;
- g_capture_target = NULL;
- g_needs_redraw = 1;
+ g_settings_open = !g_settings_open;
+ g_selected_index = 1; // first actionable item
+ g_capture_mode = CAPTURE_NONE;
+ g_capture_target = NULL;
+ g_needs_redraw = 1;
}
bool settings_is_open(void) { return g_settings_open != 0; }-static void settings_move(int delta) {- if (!g_settings_open)
- return;
- // We will clamp after building the menu in render
- g_selected_index += delta;
- if (g_selected_index < 1)
- g_selected_index = 1;
- g_needs_redraw = 1;
+static void settings_move(const config_params_s *conf, int delta) {+ if (!g_settings_open)
+ return;
+
+ setting_item_s items[64];
+ int count = 0;
+ build_menu(conf, items, &count);
+
+ int idx = g_selected_index + delta;
+
+ // Clamp within bounds
+ if (idx < 0)
+ idx = 0;
+ if (idx >= count)
+ idx = count - 1;
+
+ // Skip headers in the direction of movement
+ const int step = (delta >= 0) ? 1 : -1;
+ while (idx >= 0 && idx < count && items[idx].type == ITEM_HEADER) {+ idx += step;
+ }
+
+ // Ensure we don't select before first actionable item
+ if (idx < 1)
+ idx = 1;
+
+ // Final safety clamp
+ if (idx >= count)
+ idx = count - 1;
+
+ g_selected_index = idx;
+ g_needs_redraw = 1;
}
static void settings_activate(struct app_context *ctx, const setting_item_s *items, int count) {- if (g_selected_index < 1 || g_selected_index >= count)
- return;
- const setting_item_s *it = &items[g_selected_index];
- config_params_s *conf = &ctx->conf;
+ if (g_selected_index < 1 || g_selected_index >= count)
+ return;
+ const setting_item_s *it = &items[g_selected_index];
+ config_params_s *conf = &ctx->conf;
- switch (it->type) {- case ITEM_TOGGLE_BOOL: {- unsigned int *val = (unsigned int *)it->target;
- *val = *val ? 0 : 1;
- if (it->target == &conf->init_fullscreen) {- extern void toggle_fullscreen(void);
- toggle_fullscreen();
- }
- g_needs_redraw = 1;
- break;
- }
- case ITEM_SAVE: {- extern void write_config(const config_params_s *conf);
- write_config(conf);
- g_needs_redraw = 1;
- break;
- }
- case ITEM_CLOSE:
- settings_toggle_open();
- break;
- case ITEM_BIND_KEY:
- g_capture_mode = CAPTURE_KEY;
- g_capture_target = it->target;
- g_needs_redraw = 1;
- break;
- case ITEM_BIND_BUTTON:
- g_capture_mode = CAPTURE_BUTTON;
- g_capture_target = it->target;
- g_needs_redraw = 1;
- break;
- case ITEM_BIND_AXIS:
- g_capture_mode = CAPTURE_AXIS;
- g_capture_target = it->target;
- g_needs_redraw = 1;
- break;
- case ITEM_INT_ADJ:
- case ITEM_HEADER:
- break;
- }
+ switch (it->type) {+ case ITEM_TOGGLE_BOOL: {+ unsigned int *val = it->target;
+ *val = *val ? 0 : 1;
+ if (it->target == &conf->init_fullscreen) {+ extern void toggle_fullscreen(void);
+ toggle_fullscreen();
+ }
+ if (it->target == &conf->integer_scaling) {+ extern void renderer_fix_texture_scaling_after_window_resize(config_params_s * config);
+ renderer_fix_texture_scaling_after_window_resize(conf);
+ }
+ g_needs_redraw = 1;
+ break;
+ }
+ case ITEM_SAVE: {+ write_config(conf);
+ g_needs_redraw = 1;
+ g_config_saved = 1;
+ break;
+ }
+ case ITEM_CLOSE:
+ settings_toggle_open();
+ break;
+ case ITEM_BIND_KEY:
+ g_capture_mode = CAPTURE_KEY;
+ g_capture_target = it->target;
+ g_needs_redraw = 1;
+ break;
+ case ITEM_BIND_BUTTON:
+ g_capture_mode = CAPTURE_BUTTON;
+ g_capture_target = it->target;
+ g_needs_redraw = 1;
+ break;
+ case ITEM_BIND_AXIS:
+ g_capture_mode = CAPTURE_AXIS;
+ g_capture_target = it->target;
+ g_needs_redraw = 1;
+ break;
+ case ITEM_INT_ADJ:
+ case ITEM_HEADER:
+ break;
+ default:
+ break;
+ }
}
void settings_handle_event(struct app_context *ctx, const SDL_Event *e) {- if (!g_settings_open)
- return;
+ if (!g_settings_open)
+ return;
- if (e->type == SDL_EVENT_KEY_DOWN) {- if (e->key.key == SDLK_ESCAPE || e->key.key == SDLK_F1) {- if (g_capture_mode != CAPTURE_NONE) {- g_capture_mode = CAPTURE_NONE;
- g_capture_target = NULL;
- g_needs_redraw = 1;
- return;
- }
- if (g_view != VIEW_ROOT) {- g_view = VIEW_ROOT;
- g_selected_index = 1;
- g_needs_redraw = 1;
- return;
- }
- settings_toggle_open();
- return;
- }
- // Capture key remap
- if (g_capture_mode == CAPTURE_KEY) {- if (g_capture_target != NULL) {- unsigned int *dst = (unsigned int *)g_capture_target;
- *dst = e->key.scancode;
- }
- g_capture_mode = CAPTURE_NONE;
- g_capture_target = NULL;
- g_needs_redraw = 1;
- return;
- }
- if (e->key.key == SDLK_UP) {- settings_move(-1);
- return;
- }
- if (e->key.key == SDLK_DOWN) {- settings_move(1);
- return;
- }
- if (e->key.key == SDLK_LEFT || e->key.key == SDLK_RIGHT) {- setting_item_s items[64];
- int count = 0;
- build_menu(&ctx->conf, items, &count);
- if (g_selected_index > 0 && g_selected_index < count) {- setting_item_s *it = &items[g_selected_index];
- if (it->type == ITEM_INT_ADJ && it->target != NULL) {- int *val = (int *)it->target;
- int delta = (e->key.key == SDLK_LEFT ? -it->step : it->step);
- int newv = *val + delta;
- if (newv < it->min_val) newv = it->min_val;
- if (newv > it->max_val) newv = it->max_val;
- *val = newv;
- g_needs_redraw = 1;
- return;
- }
- }
- }
- if (e->key.key == SDLK_RETURN || e->key.key == SDLK_SPACE) {- setting_item_s items[64];
- int count = 0;
- build_menu(&ctx->conf, items, &count);
- // Handle entering submenus from root based on item type
- if (g_view == VIEW_ROOT) {- const setting_item_s *it = &items[g_selected_index];
- if (it->type == ITEM_SUBMENU && it->label && SDL_strstr(it->label, "Keyboard bindings") == it->label) {- g_view = VIEW_KEYS;
- g_selected_index = 1;
- g_needs_redraw = 1;
- return;
- }
- if (it->type == ITEM_SUBMENU && it->label && SDL_strstr(it->label, "Gamepad bindings") == it->label) {- g_view = VIEW_GAMEPAD;
- g_selected_index = 1;
- g_needs_redraw = 1;
- return;
- }
- if (it->type == ITEM_SUBMENU && it->label && SDL_strstr(it->label, "Analog settings") == it->label) {- g_view = VIEW_ANALOG;
- g_selected_index = 1;
- g_needs_redraw = 1;
- return;
- }
- }
- // Back item in submenus
- if (g_view != VIEW_ROOT) {- const setting_item_s *it = &items[g_selected_index];
- if (it->type == ITEM_CLOSE && it->label && SDL_strcmp(it->label, "Back") == 0) {- g_view = VIEW_ROOT;
- g_selected_index = 1;
- g_needs_redraw = 1;
- return;
- }
- }
- settings_activate(ctx, items, count);
- return;
- }
- }
+ if (e->type == SDL_EVENT_KEY_DOWN) {+ if (e->key.key == SDLK_ESCAPE || e->key.key == SDLK_F1) {+ if (g_capture_mode != CAPTURE_NONE) {+ g_capture_mode = CAPTURE_NONE;
+ g_capture_target = NULL;
+ g_needs_redraw = 1;
+ return;
+ }
+ if (g_view != VIEW_ROOT) {+ g_view = VIEW_ROOT;
+ g_selected_index = 1;
+ g_needs_redraw = 1;
+ return;
+ }
+ settings_toggle_open();
+ return;
+ }
+ // Capture key remap
+ if (g_capture_mode == CAPTURE_KEY) {+ if (g_capture_target != NULL) {+ unsigned int *dst = g_capture_target;
+ *dst = e->key.scancode;
+ }
+ g_capture_mode = CAPTURE_NONE;
+ g_capture_target = NULL;
+ g_needs_redraw = 1;
+ return;
+ }
+ if (e->key.key == SDLK_UP) {+ settings_move(&ctx->conf, -1);
+ return;
+ }
+ if (e->key.key == SDLK_DOWN) {+ settings_move(&ctx->conf, 1);
+ return;
+ }
+ if (e->key.key == SDLK_LEFT || e->key.key == SDLK_RIGHT) {+ setting_item_s items[64];
+ int count = 0;
+ build_menu(&ctx->conf, items, &count);
+ if (g_selected_index > 0 && g_selected_index < count) {+ setting_item_s *it = &items[g_selected_index];
+ if (it->type == ITEM_INT_ADJ && it->target != NULL) {+ int *val = (int *)it->target;
+ int delta = (e->key.key == SDLK_LEFT ? -it->step : it->step);
+ int newv = *val + delta;
+ if (newv < it->min_val)
+ newv = it->min_val;
+ if (newv > it->max_val)
+ newv = it->max_val;
+ *val = newv;
+ g_needs_redraw = 1;
+ return;
+ }
+ }
+ }
+ if (e->key.key == SDLK_RETURN || e->key.key == SDLK_SPACE) {+ setting_item_s items[64];
+ int count = 0;
+ build_menu(&ctx->conf, items, &count);
+ // Handle entering submenus from root based on item type
+ if (g_view == VIEW_ROOT) {+ const setting_item_s *it = &items[g_selected_index];
+ if (it->type == ITEM_SUBMENU && it->label &&
+ SDL_strstr(it->label, "Keyboard bindings") == it->label) {+ g_view = VIEW_KEYS;
+ g_selected_index = 1;
+ g_needs_redraw = 1;
+ return;
+ }
+ if (it->type == ITEM_SUBMENU && it->label &&
+ SDL_strstr(it->label, "Gamepad bindings") == it->label) {+ g_view = VIEW_GAMEPAD;
+ g_selected_index = 1;
+ g_needs_redraw = 1;
+ return;
+ }
+ if (it->type == ITEM_SUBMENU && it->label &&
+ SDL_strstr(it->label, "Gamepad analog axis") == it->label) {+ g_view = VIEW_ANALOG;
+ g_selected_index = 1;
+ g_needs_redraw = 1;
+ return;
+ }
+ }
+ // Back item in submenus
+ if (g_view != VIEW_ROOT) {+ const setting_item_s *it = &items[g_selected_index];
+ if (it->type == ITEM_CLOSE && it->label && SDL_strcmp(it->label, "Back") == 0) {+ g_view = VIEW_ROOT;
+ g_selected_index = 1;
+ g_needs_redraw = 1;
+ return;
+ }
+ }
+ settings_activate(ctx, items, count);
+ return;
+ }
+ }
- // Capture gamepad button
- if (g_capture_mode == CAPTURE_BUTTON && e->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {- if (g_capture_target != NULL) {- int *dst = (int *)g_capture_target;
- *dst = e->gbutton.button;
- }
- g_capture_mode = CAPTURE_NONE;
- g_capture_target = NULL;
- g_needs_redraw = 1;
- return;
- }
- // Capture axis on significant motion
- if (g_capture_mode == CAPTURE_AXIS && e->type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {- if (SDL_abs(e->gaxis.value) > 16000) {- if (g_capture_target != NULL) {- int *dst = (int *)g_capture_target;
- *dst = e->gaxis.axis;
- }
- g_capture_mode = CAPTURE_NONE;
- g_capture_target = NULL;
- g_needs_redraw = 1;
- return;
- }
- }
+ // Capture gamepad button
+ if (g_capture_mode == CAPTURE_BUTTON && e->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {+ if (g_capture_target != NULL) {+ int *dst = (int *)g_capture_target;
+ *dst = e->gbutton.button;
+ }
+ g_capture_mode = CAPTURE_NONE;
+ g_capture_target = NULL;
+ g_needs_redraw = 1;
+ return;
+ }
+ // Capture axis on significant motion
+ if (g_capture_mode == CAPTURE_AXIS && e->type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {+ if (SDL_abs(e->gaxis.value) > 16000) {+ if (g_capture_target != NULL) {+ int *dst = (int *)g_capture_target;
+ *dst = e->gaxis.axis;
+ }
+ g_capture_mode = CAPTURE_NONE;
+ g_capture_target = NULL;
+ g_needs_redraw = 1;
+ return;
+ }
+ }
}
void settings_render_overlay(SDL_Renderer *rend, const config_params_s *conf, int texture_w,
- int texture_h) {- if (!g_settings_open)
- return;
+ int texture_h) {+ if (!g_settings_open)
+ return;
- if (g_settings_texture == NULL) {- g_settings_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888,
- SDL_TEXTUREACCESS_TARGET, texture_w, texture_h);
- if (g_settings_texture == NULL) {- return;
- }
- SDL_SetTextureBlendMode(g_settings_texture, SDL_BLENDMODE_BLEND);
- SDL_SetTextureScaleMode(g_settings_texture, SDL_SCALEMODE_NEAREST);
- g_needs_redraw = 1;
- }
+ const struct inline_font *previous_font = inline_font_get_current();
+ if (previous_font->glyph_x != fonts_get(0)->glyph_x) {+ // Switch to small font if not active already
+ inline_font_close();
+ inline_font_initialize(fonts_get(0));
+ }
- if (!g_needs_redraw)
- goto composite;
- g_needs_redraw = 0;
+ if (g_settings_texture == NULL) {+ g_settings_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET,
+ texture_w, texture_h);
+ if (g_settings_texture == NULL) {+ inline_font_close();
+ inline_font_initialize(previous_font);
+ return;
+ }
+ SDL_SetTextureBlendMode(g_settings_texture, SDL_BLENDMODE_BLEND);
+ SDL_SetTextureScaleMode(g_settings_texture, SDL_SCALEMODE_NEAREST);
+ g_needs_redraw = 1;
+ }
- SDL_Texture *prev = SDL_GetRenderTarget(rend);
- SDL_SetRenderTarget(rend, g_settings_texture);
+ if (!g_needs_redraw)
+ goto composite;
+ g_needs_redraw = 0;
- // Background
- SDL_SetRenderDrawColor(rend, 0, 0, 0, 200);
- SDL_RenderClear(rend);
+ SDL_Texture *prev = SDL_GetRenderTarget(rend);
+ SDL_SetRenderTarget(rend, g_settings_texture);
- // Title and items
- const Uint32 fg = 0xFFFFFF;
- const Uint32 bg = 0x000000;
- int x = 8;
- int y = 8;
+ // Background
+ SDL_SetRenderDrawColor(rend, 0, 0, 0, 240);
+ SDL_RenderClear(rend);
- inprint(rend, "Settings", x, y, 0xFFFF00, bg);
- y += 14;
+ // Title and items
+ const Uint32 fg = 0xFFFFFF;
+ const Uint32 bg = 0xFFFFFF;
+ const Uint32 selected_item_fg = 0x00FFFF;
+ const Uint32 selected_item_bg = 0x00FFFF;
+ const Uint32 title = 0xFF0000;
+ const Uint32 section_header = 0xAAAAFF;
+ const int margin_x_unselected = 2;
+ const int margin_x_selected = 2;
+ int x = 8;
+ int y = 8;
- setting_item_s items[64];
- int count = 0;
- build_menu(conf, items, &count);
- if (g_selected_index >= count) g_selected_index = count - 1;
+ inprint(rend, "M8C Config", x, y, title, title);
+ y += 24;
- if (g_capture_mode == CAPTURE_KEY) {- inprint(rend, "Press a key... (Esc to cancel)", x, y, 0x00FFFF, bg);
- y += 12;
- } else if (g_capture_mode == CAPTURE_BUTTON) {- inprint(rend, "Press a gamepad button...", x, y, 0x00FFFF, bg);
- y += 12;
- } else if (g_capture_mode == CAPTURE_AXIS) {- inprint(rend, "Move a gamepad axis...", x, y, 0x00FFFF, bg);
- y += 12;
- }
+ setting_item_s items[64];
+ int count = 0;
+ build_menu(conf, items, &count);
+ if (g_selected_index >= count)
+ g_selected_index = count - 1;
- for (int i = 0; i < count; i++) {- const int selected = (i == g_selected_index);
- const setting_item_s *it = &items[i];
- if (it->type == ITEM_HEADER) {- inprint(rend, it->label, x, y, 0xAAAAFF, bg);
- y += 12;
- continue;
- }
- if (it->type == ITEM_TOGGLE_BOOL) {- const unsigned int *val = (const unsigned int *)it->target;
- char line[160];
- SDL_snprintf(line, sizeof line, "%s [%s]", it->label, (*val ? "On" : "Off"));
- inprint(rend, line, x + (selected ? 0 : 2), y, selected ? 0x00FF00 : fg, bg);
- y += 12;
- continue;
- }
- if (it->type == ITEM_BIND_KEY || it->type == ITEM_BIND_BUTTON || it->type == ITEM_BIND_AXIS) {- int v = *(int *)it->target;
- char line[160];
- SDL_snprintf(line, sizeof line, "%s: %d", it->label, v);
- inprint(rend, line, x + (selected ? 0 : 2), y, selected ? 0x00FF00 : fg, bg);
- y += 12;
- continue;
- }
- if (it->type == ITEM_INT_ADJ) {- char line[160];
- SDL_snprintf(line, sizeof line, "%s: %d (Left/Right)", it->label, *(int *)it->target);
- inprint(rend, line, x + (selected ? 0 : 2), y, selected ? 0x00FF00 : fg, bg);
- y += 12;
- continue;
- }
- if (it->type == ITEM_SAVE || it->type == ITEM_CLOSE) {- char line[160];
- SDL_snprintf(line, sizeof line, "%s", it->label);
- inprint(rend, line, x + (selected ? 0 : 2), y, selected ? 0x00FF00 : fg, bg);
- y += 12;
- continue;
- }
- }
+ if (g_capture_mode == CAPTURE_KEY) {+ inprint(rend, "Press a key... (Esc to cancel)", x, y, selected_item_fg, selected_item_fg);
+ } else if (g_capture_mode == CAPTURE_BUTTON) {+ inprint(rend, "Press a gamepad button...", x, y, selected_item_fg, selected_item_fg);
+ } else if (g_capture_mode == CAPTURE_AXIS) {+ inprint(rend, "Move a gamepad axis...", x, y, selected_item_fg, selected_item_fg);
+ }
+ if (g_config_saved) {+ inprint(rend, "Configuration saved", x, y, selected_item_fg, selected_item_fg);
+ g_config_saved = 0;
+ }
+ y += 12;
- SDL_SetRenderTarget(rend, prev);
-composite:
- SDL_RenderTexture(rend, g_settings_texture, NULL, NULL);
-}
+ for (int i = 0; i < count; i++) {+ const int selected = (i == g_selected_index);
+ const setting_item_s *it = &items[i];
+ if (it->type == ITEM_HEADER) {+ inprint(rend, it->label, x, y, section_header, section_header);
+ y += 12;
+ continue;
+ }
+ if (it->type == ITEM_TOGGLE_BOOL) {+ const unsigned int *val = (const unsigned int *)it->target;
+ char line[160];
+ SDL_snprintf(line, sizeof line, "%s [%s]", it->label, (*val ? "On" : "Off"));
+ inprint(rend, line, x + (selected ? margin_x_selected : margin_x_unselected), y,
+ selected ? selected_item_fg : fg, selected ? selected_item_bg : bg);
+ y += 12;
+ continue;
+ }
+ if (it->type == ITEM_BIND_KEY) {+ int sc = *(int *)it->target;
+ const char *name = SDL_GetScancodeName((SDL_Scancode)sc);
+ if (name == NULL || name[0] == '\0') {+ name = "Unknown";
+ }
+ char line[160];
+ SDL_snprintf(line, sizeof line, "%s: %s", it->label, name);
+ inprint(rend, line, x + (selected ? margin_x_selected : margin_x_unselected), y,
+ selected ? selected_item_fg : fg, selected ? selected_item_bg : bg);
+ y += 12;
+ continue;
+ }
+ if (it->type == ITEM_BIND_BUTTON) {+ int v = *(int *)it->target;
+ const char *name = NULL;
-// Handle renderer/size resets: drop the cached texture to recreate at new size on next frame
-void settings_on_texture_size_change(SDL_Renderer *rend) { settings_destroy_texture(rend); g_needs_redraw = 1; }+ // Pick the first active controller if available
+ SDL_Gamepad *pad = NULL;
+ for (int gi = 0; gi < 4; gi++) {+ if (game_controllers[gi] != NULL) {+ pad = game_controllers[gi];
+ break;
+ }
+ }
+ if (pad) {+ // Use controller-specific face button labels when possible
+ SDL_GamepadButtonLabel lbl = SDL_GetGamepadButtonLabel(pad, (SDL_GamepadButton)v);
+ if (lbl != SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN) {+ name = SDL_GetGamepadStringForButton(v);
+ }
+ }
+ // Generic fallback by standardized button name
+ if (name == NULL || name[0] == '\0') {+ name = SDL_GetGamepadStringForButton((SDL_GamepadButton)v);
+ }
+ char line[160];
+ if (name && name[0] != '\0') {+ SDL_snprintf(line, sizeof line, "%s: %s", it->label, name);
+ } else {+ SDL_snprintf(line, sizeof line, "%s: %d", it->label, v);
+ }
+ inprint(rend, line, x + (selected ? margin_x_selected : margin_x_unselected), y,
+ selected ? selected_item_fg : fg, selected ? selected_item_bg : bg);
+ y += 12;
+ continue;
+ }
+ if (it->type == ITEM_BIND_AXIS) {+ int v = *(int *)it->target;
+ const char *name = SDL_GetGamepadStringForAxis((SDL_GamepadAxis)v);
+ char line[160];
+ if (name && name[0] != '\0') {+ SDL_snprintf(line, sizeof line, "%s: %s", it->label, name);
+ } else {+ SDL_snprintf(line, sizeof line, "%s: %d", it->label, v);
+ }
+ inprint(rend, line, x + (selected ? margin_x_selected : margin_x_unselected), y,
+ selected ? selected_item_fg : fg, selected ? selected_item_bg : bg);
+ y += 12;
+ continue;
+ }
+ if (it->type == ITEM_INT_ADJ) {+ char line[160];
+ SDL_snprintf(line, sizeof line, "%s: %d (Left/Right)", it->label, *(int *)it->target);
+ inprint(rend, line, x + (selected ? margin_x_selected : margin_x_unselected), y,
+ selected ? selected_item_fg : fg, selected ? selected_item_bg : bg);
+ y += 12;
+ continue;
+ }
+ if (it->type == ITEM_SAVE || it->type == ITEM_CLOSE) {+ char line[160];
+ SDL_snprintf(line, sizeof line, "%s", it->label);
+ inprint(rend, line, x + (selected ? margin_x_selected : margin_x_unselected), y,
+ selected ? selected_item_fg : fg, selected ? selected_item_bg : bg);
+ y += 12;
+ continue;
+ }
+ if (it->type == ITEM_SUBMENU) {+ char line[160];
+ SDL_snprintf(line, sizeof line, "%s", it->label);
+ inprint(rend, line, x + (selected ? margin_x_selected : margin_x_unselected), y,
+ selected ? selected_item_fg : fg, selected ? selected_item_bg : bg);
+ y += 12;
+ }
+ }
+
+ SDL_SetRenderTarget(rend, prev);
+
+composite:
+ SDL_RenderTexture(rend, g_settings_texture, NULL, NULL);
+ if (previous_font->glyph_x != fonts_get(0)->glyph_x) {+ inline_font_close();
+ inline_font_initialize(previous_font);
+ }
+}
+
+// Handle renderer/size resets: drop the cached texture to recreate at new size on next frame
+void settings_on_texture_size_change(SDL_Renderer *rend) {+ settings_destroy_texture(rend);
+ g_needs_redraw = 1;
+}
--- a/src/settings.h
+++ b/src/settings.h
@@ -25,6 +25,11 @@
// Notify settings overlay that logical render size changed; drops cached texture
void settings_on_texture_size_change(SDL_Renderer *rend);
+// Set the font (SDL_Texture*) to be used while rendering the settings menu.
+// Pass NULL to use the current global font.
+void settings_set_font(SDL_Texture *font);
+
+
#endif // SETTINGS_H_
--
⑨