From 9ac9695d736ccd28063f95e72d771eb7768eb131 Mon Sep 17 00:00:00 2001 From: Nicholas Sidwell Date: Fri, 2 Jan 2026 11:00:08 -0800 Subject: [PATCH] feat: add support to persist window position and size --- src/config.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/config.h | 13 ++++++++++ src/events.c | 19 +++++++++++++++ src/main.c | 6 +++-- src/render.c | 15 +++++++++++- src/settings.c | 9 ++++--- 6 files changed, 121 insertions(+), 6 deletions(-) diff --git a/src/config.c b/src/config.c index 6498057..041de98 100644 --- a/src/config.c +++ b/src/config.c @@ -6,6 +6,7 @@ #include #include #include +#include #include /* Case-insensitive string compare from ini.h library */ @@ -30,6 +31,14 @@ config_params_s config_initialize(char *filename) { c.init_fullscreen = 0; // default fullscreen state at load c.integer_scaling = 0; // use integer scaling for the user interface + + // remember window size and position on next start + c.persist_window_position_and_size = 0; + c.window.height = 480; + c.window.width = 640; + c.window.x = 0; + c.window.y = 0; + c.wait_packets = 256; // amount of empty command queue reads before assuming device disconnected c.audio_enabled = 0; // route M8 audio to default output c.audio_buffer_size = 0; // requested audio buffer size in samples: 0 = let SDL decide @@ -80,6 +89,29 @@ config_params_s config_initialize(char *filename) { return c; } +void update_and_write_window_position_and_size_config( + config_params_s *conf, const window_position_and_size_s *window_position_and_size) { + // Moved event + if (INT_MIN == window_position_and_size->height && INT_MIN == window_position_and_size->width) { + SDL_Log("x%d\n", window_position_and_size->x); + SDL_Log("y%d\n", window_position_and_size->y); + + conf->window.x = window_position_and_size->x; + conf->window.y = window_position_and_size->y; + } + + // Resized event + if (INT_MIN == window_position_and_size->x && INT_MIN == window_position_and_size->y) { + SDL_Log("height%d\n", window_position_and_size->height); + SDL_Log("width%d\n", window_position_and_size->width); + + conf->window.height = window_position_and_size->height; + conf->window.width = window_position_and_size->width; + } + + write_config(conf); +} + // Write config to file void write_config(const config_params_s *conf) { @@ -102,6 +134,12 @@ void write_config(const config_params_s *conf) { snprintf(ini_values[initPointer++], INI_LINE_LENGTH, "wait_packets=%d\n", conf->wait_packets); snprintf(ini_values[initPointer++], INI_LINE_LENGTH, "integer_scaling=%s\n", conf->integer_scaling ? "true" : "false"); + snprintf(ini_values[initPointer++], INI_LINE_LENGTH, "persist_window_position_and_size=%s\n", + conf->persist_window_position_and_size ? "true" : "false"); + snprintf(ini_values[initPointer++], INI_LINE_LENGTH, "window_height=%d\n", conf->window.height); + snprintf(ini_values[initPointer++], INI_LINE_LENGTH, "window_width=%d\n", conf->window.width); + snprintf(ini_values[initPointer++], INI_LINE_LENGTH, "window_x=%d\n", conf->window.x); + snprintf(ini_values[initPointer++], INI_LINE_LENGTH, "window_y=%d\n", conf->window.y); snprintf(ini_values[initPointer++], INI_LINE_LENGTH, "[audio]\n"); snprintf(ini_values[initPointer++], INI_LINE_LENGTH, "audio_enabled=%s\n", conf->audio_enabled ? "true" : "false"); @@ -236,6 +274,12 @@ void read_graphics_config(const ini_t *ini, config_params_s *conf) { const char *param_fs = ini_get(ini, "graphics", "fullscreen"); const char *wait_packets = ini_get(ini, "graphics", "wait_packets"); const char *integer_scaling = ini_get(ini, "graphics", "integer_scaling"); + const char *param_persist_window_position_and_size = + ini_get(ini, "graphics", "persist_window_position_and_size"); + const char *window_height = ini_get(ini, "graphics", "window_height"); + const char *window_width = ini_get(ini, "graphics", "window_width"); + const char *window_x = ini_get(ini, "graphics", "window_x"); + const char *window_y = ini_get(ini, "graphics", "window_y"); if (param_fs != NULL && strcmpci(param_fs, "true") == 0) { conf->init_fullscreen = 1; @@ -243,6 +287,27 @@ void read_graphics_config(const ini_t *ini, config_params_s *conf) { conf->init_fullscreen = 0; } + if (param_persist_window_position_and_size != NULL && + strcmpci(param_persist_window_position_and_size, "true") == 0) { + conf->persist_window_position_and_size = 1; + } else { + conf->persist_window_position_and_size = 0; + } + + if (1 == conf->persist_window_position_and_size) { + if (window_height != NULL) + conf->window.height = SDL_atoi(window_height); + + if (window_width != NULL) + conf->window.width = SDL_atoi(window_width); + + if (window_x != NULL) + conf->window.x = SDL_atoi(window_x); + + if (window_y != NULL) + conf->window.y = SDL_atoi(window_y); + } + if (wait_packets != NULL) conf->wait_packets = SDL_atoi(wait_packets); diff --git a/src/config.h b/src/config.h index bfecd1c..a9abbe4 100644 --- a/src/config.h +++ b/src/config.h @@ -6,10 +6,21 @@ #include "ini.h" +typedef struct window_position_and_size_s { + int height; + int width; + int x; + int y; +} window_position_and_size_s; + typedef struct config_params_s { char *filename; unsigned int init_fullscreen; unsigned int integer_scaling; + + unsigned int persist_window_position_and_size; + window_position_and_size_s window; + unsigned int wait_packets; unsigned int audio_enabled; unsigned int audio_buffer_size; @@ -67,6 +78,8 @@ void read_key_config(const ini_t *ini, config_params_s *conf); void read_gamepad_config(const ini_t *ini, config_params_s *conf); // Expose write so settings UI can persist changes +void update_and_write_window_position_and_size_config( + config_params_s *conf, const window_position_and_size_s *window_position_and_size); void write_config(const config_params_s *conf); #endif diff --git a/src/events.c b/src/events.c index 777fe38..4da690e 100644 --- a/src/events.c +++ b/src/events.c @@ -7,6 +7,7 @@ #include "settings.h" #include #include +#include SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { struct app_context *ctx = appstate; @@ -23,6 +24,24 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { case SDL_EVENT_WINDOW_MOVED: // If the window size is changed, some systems might need a little nudge to fix scaling renderer_fix_texture_scaling_after_window_resize(&ctx->conf); + + if (ctx->conf.persist_window_position_and_size == 1) { + window_position_and_size_s window_position_and_size = { + .height = INT_MIN, .width = INT_MIN, .x = INT_MIN, .y = INT_MIN}; + + if (event->window.type == SDL_EVENT_WINDOW_MOVED) { + window_position_and_size.x = event->window.data1; + window_position_and_size.y = event->window.data2; + } + + if (event->window.type == SDL_EVENT_WINDOW_RESIZED) { + window_position_and_size.height = event->window.data2; + window_position_and_size.width = event->window.data1; + } + + update_and_write_window_position_and_size_config(&ctx->conf, &window_position_and_size); + } + break; // --- iOS specific events --- diff --git a/src/main.c b/src/main.c index ceeccf1..3556a57 100644 --- a/src/main.c +++ b/src/main.c @@ -18,8 +18,8 @@ #include "common.h" #include "config.h" #include "gamepads.h" -#include "render.h" #include "log_overlay.h" +#include "render.h" static void do_wait_for_device(struct app_context *ctx) { static Uint64 ticks_poll_device = 0; @@ -94,7 +94,9 @@ static config_params_s initialize_config(int argc, char *argv[], char **preferre if (TARGET_OS_IOS == 1) { // Predefined settings for iOS conf.init_fullscreen = 1; + conf.persist_window_position_and_size = 0; } + config_read(&conf); return conf; @@ -220,4 +222,4 @@ void SDL_AppQuit(void *appstate, SDL_AppResult result) { SDL_Log("Shutting down."); SDL_Quit(); } -} \ No newline at end of file +} diff --git a/src/render.c b/src/render.c index 54e52f5..15a4793 100644 --- a/src/render.c +++ b/src/render.c @@ -380,6 +380,17 @@ static void log_fps_stats(void) { } } +void rehydrate_window_position_and_size(const config_params_s *conf) { + SDL_Log("rehydrating window position and size"); + SDL_Log("height: %d\n", conf->window.height); + SDL_Log("width: %d\n", conf->window.width); + SDL_Log("x: %d\n", conf->window.x); + SDL_Log("y: %d\n", conf->window.y); + + SDL_SetWindowPosition(win, conf->window.x, conf->window.y); + SDL_SetWindowSize(win, conf->window.width, conf->window.height); +} + // Initializes SDL and creates a renderer and required surfaces int renderer_initialize(config_params_s *conf) { @@ -436,6 +447,8 @@ int renderer_initialize(config_params_s *conf) { renderer_set_font_mode(0); + rehydrate_window_position_and_size(conf); + SDL_SetHint(SDL_HINT_IOS_HIDE_HOME_INDICATOR, "1"); renderer_fix_texture_scaling_after_window_resize( conf); // iOS needs this, doesn't hurt on others either @@ -630,4 +643,4 @@ void renderer_clear_screen(void) { SDL_SetRenderTarget(rend, NULL); SDL_RenderClear(rend); -} \ No newline at end of file +} diff --git a/src/settings.c b/src/settings.c index f5672ac..62fdd3f 100644 --- a/src/settings.c +++ b/src/settings.c @@ -83,12 +83,15 @@ static void build_menu(const config_params_s *conf, setting_item_s *items, int * *count = 0; switch (g_settings.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, "Graphics ", ITEM_HEADER, NULL, 0, 0, 0); + add_item(items, count, "Integer scaling ", ITEM_TOGGLE_BOOL, (void *)&conf->integer_scaling, 0,0, 0); + // SDL apps are always full screen on iOS, hide the option if (TARGET_OS_IOS == 0) { - add_item(items, count, "Fullscreen ", ITEM_TOGGLE_BOOL, (void *)&conf->init_fullscreen, 0,0, 0); + add_item(items, count, "Fullscreen ", ITEM_TOGGLE_BOOL, (void *)&conf->init_fullscreen, 0,0, 0); + add_item(items, count, "Persist window position and size ", ITEM_TOGGLE_BOOL, (void *)&conf->persist_window_position_and_size, 0,0, 0); } + add_item(items, count, "", ITEM_HEADER, NULL, 0, 0, 0); // Audio routing does not work on iOS, hide the items when building for that if (TARGET_OS_IOS == 0) {