From 4aae6927e39e2e280932d0ff18c6423d24f34f2b Mon Sep 17 00:00:00 2001
From: Simon Gellis <simongellis@gmail.com>
Date: Thu, 31 Oct 2024 23:40:40 -0400
Subject: [PATCH] Support resizing the screen

---
 ui.c     | 22 +++++++++++++++++-
 window.c | 70 +++++++++++++++++++++++++++++++++++++-------------------
 window.h |  4 +++-
 3 files changed, 71 insertions(+), 25 deletions(-)

diff --git a/ui.c b/ui.c
index c3cb86e..1dbdd76 100644
--- a/ui.c
+++ b/ui.c
@@ -89,9 +89,10 @@ int uiLoadGame(UIContext *ui, const char *path) {
     return 0;
 }
 
-static const char *MENU_ITEMS[3] = {
+static const char *MENU_ITEMS[4] = {
     "File",
     "Emulation",
+    "Video",
     NULL
 };
 
@@ -169,6 +170,25 @@ int uiRun(UIContext *ui, bool running) {
                 nk_menu_end(ctx);
             }
 
+            if (nk_menu_begin_label(ctx, "Video", NK_TEXT_ALIGN_CENTERED, nk_vec2(windowScaleX(&ui->win, 100), windowGetScreenHeight(&ui->win)))) {
+                float multiplier = windowGetScreenSizeMultiplier(&ui->win);
+
+                nk_layout_row_dynamic(ctx, windowGetMenuHeight(&ui->win), 1);
+                if (nk_menu_item_label(ctx, multiplier == 1.0f ? "x1 *" : "x1", NK_TEXT_ALIGN_LEFT)) {
+                    windowSetScreenSizeMultiplier(&ui->win, 1.0f);
+                }
+                if (nk_menu_item_label(ctx, multiplier == 2.0f ? "x2 *" : "x2", NK_TEXT_ALIGN_LEFT)) {
+                    windowSetScreenSizeMultiplier(&ui->win, 2.0f);
+                }
+                if (nk_menu_item_label(ctx, multiplier == 3.0f ? "x3 *" : "x3", NK_TEXT_ALIGN_LEFT)) {
+                    windowSetScreenSizeMultiplier(&ui->win, 3.0f);
+                }
+                if (nk_menu_item_label(ctx, multiplier == 4.0f ? "x4 *" : "x4", NK_TEXT_ALIGN_LEFT)) {
+                    windowSetScreenSizeMultiplier(&ui->win, 4.0f);
+                }
+                nk_menu_end(ctx);
+            }
+
             windowMenubarEnd(&ui->win);
         }
         windowGuiEnd(&ui->win);
diff --git a/window.c b/window.c
index 30684b2..19658bc 100644
--- a/window.c
+++ b/window.c
@@ -56,7 +56,37 @@ static void applyStyles(struct nk_context *ctx, float scaleX, float scaleY) {
     ctx->style.contextual_button.padding = nk_vec2(20 * scaleX, 4 * scaleY);
 }
 
+/* scale the window for High-DPI displays */
+static void scaleWindow(WindowContext *win) {
+    int renderW, renderH;
+    int oldWindowX, oldWindowY;
+    int newWindowX, newWindowY;
+    int oldWindowW, oldWindowH;
+    int newWindowW, newWindowH;
+    float scaleX, scaleY;
+    float hdpi, vdpi;
+    SDL_GetRendererOutputSize(win->renderer, &renderW, &renderH);
+    SDL_GetWindowPosition(win->window, &oldWindowX, &oldWindowY);
+    SDL_GetWindowSize(win->window, &oldWindowW, &oldWindowH);
+    SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(win->window), NULL, &hdpi, &vdpi);
+    scaleX = (float)(renderW) / (float)(oldWindowW);
+    scaleY = (float)(renderH) / (float)(oldWindowH);
+    win->screenScaleX = (hdpi / 96) * scaleX;
+    win->screenScaleY = (vdpi / 96) * scaleY;
+
+    newWindowW = SCREEN_WIDTH * win->screenSizeMultiplier * (hdpi / 96) / scaleX;
+    newWindowH = (SCREEN_HEIGHT * win->screenSizeMultiplier + MENU_HEIGHT) * (vdpi / 96) / scaleY;
+    newWindowX = oldWindowX - (newWindowW - oldWindowW) / 2;
+    newWindowY = oldWindowY - (newWindowH - oldWindowH) / 2;
+    if (newWindowX < 0) newWindowX = 0;
+    if (newWindowY < 0) newWindowY = 0;
+    SDL_SetWindowSize(win->window, newWindowW, newWindowH);
+    SDL_SetWindowPosition(win->window, newWindowX, newWindowY);
+}
+
 int windowInit(WindowContext *win, const char *title) {
+    win->screenSizeMultiplier = 1.0f;
+
     win->window = SDL_CreateWindow(title,
         SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
         SCREEN_WIDTH, SCREEN_HEIGHT + MENU_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI);
@@ -86,25 +116,7 @@ int windowInit(WindowContext *win, const char *title) {
     SDL_SetTextureColorMod(win->rightEye, 0, 0xc6, 0xf0);
     SDL_SetTextureBlendMode(win->rightEye, SDL_BLENDMODE_ADD);
 
-    /* scale the window and renderer for High-DPI displays */
-    {
-        int renderW, renderH;
-        int windowW, windowH;
-        float scaleX, scaleY;
-        float hdpi, vdpi;
-        SDL_GetRendererOutputSize(win->renderer, &renderW, &renderH);
-        SDL_GetWindowSize(win->window, &windowW, &windowH);
-        SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(win->window), NULL, &hdpi, &vdpi);
-        scaleX = (float)(renderW) / (float)(windowW);
-        scaleY = (float)(renderH) / (float)(windowH);
-
-        win->winWidth = SCREEN_WIDTH * (hdpi / 96) / scaleX;
-        win->winHeight = (SCREEN_HEIGHT + MENU_HEIGHT) * (vdpi / 96) / scaleY;
-        win->screenScaleX = (hdpi / 96) * scaleX;
-        win->screenScaleY = (vdpi / 96) * scaleY;
-
-        SDL_SetWindowSize(win->window, win->winWidth, win->winHeight);
-    }
+    scaleWindow(win);
 
     win->nk = nk_sdl_init(win->window, win->renderer);
     applyStyles(win->nk, win->screenScaleX, win->screenScaleY);
@@ -143,6 +155,16 @@ void windowDestroy(WindowContext *win) {
     SDL_DestroyWindow(win->window);
 }
 
+float windowGetScreenSizeMultiplier(WindowContext *win) {
+    return win->screenSizeMultiplier;
+}
+
+void windowSetScreenSizeMultiplier(WindowContext *win, float multiplier) {
+    win->screenSizeMultiplier = multiplier;
+    scaleWindow(win);
+    applyStyles(win->nk, win->screenScaleX, win->screenScaleY);
+}
+
 static void copyScreenTexture(uint8_t *dst, const uint8_t *src, int pitch) {
     int x, y, i;
     uint8_t color;
@@ -186,15 +208,15 @@ int windowGetMenuHeight(WindowContext *win) {
 }
 
 int windowGetScreenHeight(WindowContext *win) {
-    return SCREEN_HEIGHT * win->screenScaleY;
+    return SCREEN_HEIGHT * win->screenSizeMultiplier * win->screenScaleY;
 }
 
 void windowDisplayBegin(WindowContext *win) {
     SDL_Rect dst;
     dst.x = 0;
     dst.y = MENU_HEIGHT * win->screenScaleY;
-    dst.w = SCREEN_WIDTH * win->screenScaleX;
-    dst.h = SCREEN_HEIGHT * win->screenScaleY;
+    dst.w = SCREEN_WIDTH * win->screenSizeMultiplier * win->screenScaleX;
+    dst.h = SCREEN_HEIGHT * win->screenSizeMultiplier * win->screenScaleY;
 
     SDL_RenderClear(win->renderer);
     SDL_RenderCopy(win->renderer, win->leftEye, NULL, &dst);
@@ -207,7 +229,9 @@ void windowDisplayEnd(WindowContext *win) {
 }
 
 bool windowGuiBegin(WindowContext *win, const char *title) {
-    return nk_begin(win->nk, title, nk_rect(0, 0, SCREEN_WIDTH * win->screenScaleX, MENU_HEIGHT * win->screenScaleY), NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_BACKGROUND);
+    return nk_begin(win->nk, title,
+        nk_rect(0, 0, SCREEN_WIDTH * win->screenSizeMultiplier * win->screenScaleX, MENU_HEIGHT * win->screenScaleY),
+        NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_BACKGROUND);
 }
 
 void windowGuiEnd(WindowContext *win) {
diff --git a/window.h b/window.h
index 394c816..4775638 100644
--- a/window.h
+++ b/window.h
@@ -6,7 +6,7 @@
 
 typedef struct WindowContext {
     SDL_Window *window;
-    int winWidth, winHeight;
+    float screenSizeMultiplier;
     float screenScaleX, screenScaleY;
     SDL_Renderer *renderer;
     SDL_Texture *leftEye;
@@ -18,6 +18,8 @@ typedef struct WindowContext {
 int windowInit(WindowContext *win, const char *title);
 void windowDestroy(WindowContext *win);
 
+float windowGetScreenSizeMultiplier(WindowContext *win);
+void windowSetScreenSizeMultiplier(WindowContext *win, float multiplier);
 void windowUpdate(WindowContext *win, const uint8_t *left, const uint8_t *right);
 
 int windowScaleX(WindowContext *win, int x);