shithub: m8c

Download patch

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_
 
 
--