diff --git a/src/desktop/backends/glfw2.c b/src/desktop/backends/glfw2.c index f42269da..7b3dd67c 100644 --- a/src/desktop/backends/glfw2.c +++ b/src/desktop/backends/glfw2.c @@ -3,6 +3,7 @@ #ifdef _WIN32 #include +#include #endif #ifdef ENABLE_SW_RENDERER @@ -39,9 +40,29 @@ bool platformGetScaledWindowSize(int32_t* outW, int32_t* outH) { return platformGetWindowSize(outW, outH); } +static void platformGetTrueDesktopSize(int* outW, int* outH) { + GLFWvidmode list[256]; + int numModes = glfwGetVideoModes(list, 256); + + if (numModes > 0) { + *outW = list[numModes - 1].Width; + *outH = list[numModes - 1].Height; + } else { + GLFWvidmode mode; + glfwGetDesktopMode(&mode); + *outW = mode.Width; + *outH = mode.Height; + } +} + void platformSetWindowSize(int32_t width, int32_t height) { if (width <= 0 || height <= 0) return; + if (!platformCacheWindowSize(width, height)) return; glfwSetWindowSize(width, height); + + int desktopW, desktopH; + platformGetTrueDesktopSize(&desktopW, &desktopH); + glfwSetWindowPos(((desktopW - width) / 2), ((desktopH - height) / 2)); } void platformGetMousePos(double *xPos, double *yPos) { @@ -139,8 +160,8 @@ static void GLFWCALL mouseButtonCallback(int button, int action) { else if (action == GLFW_RELEASE) RunnerMouse_onButtonUp(g_runner->mouse, gmlButton); } -static int g_last_wheel_pos = 0; static void GLFWCALL scrollCallback(int pos) { + static int g_last_wheel_pos = 0; double yoffset = (double)(pos - g_last_wheel_pos); g_last_wheel_pos = pos; if (g_runner) RunnerMouse_onWheel(g_runner->mouse, yoffset); @@ -151,7 +172,9 @@ bool platformInit(int32_t reqW, int32_t reqH, const char *title, bool headless) fprintf(stderr, "Headless mode is not supported with GLFW 2\n"); return false; } - +#ifdef _WIN32 + timeBeginPeriod(1); +#endif #ifdef GLFW_OPENGL_VERSION_MAJOR if (gfx == SOFTWARE) { glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 1); @@ -177,6 +200,9 @@ bool platformInit(int32_t reqW, int32_t reqH, const char *title, bool headless) // Init GLFW if (!glfwInit()) { fprintf(stderr, "Failed to initialize GLFW\n"); +#ifdef _WIN32 + timeEndPeriod(1); +#endif return false; } @@ -196,12 +222,27 @@ bool platformInit(int32_t reqW, int32_t reqH, const char *title, bool headless) } #endif - int window = glfwOpenWindow(reqW, reqH, 8, 8, 8, 8, 24, 8, GLFW_WINDOW); + int desktopW, desktopH; + platformGetTrueDesktopSize(&desktopW, &desktopH); + + int finalW = reqW; + int finalH = reqH; + if (reqW >= desktopW || reqH >= desktopH) { + platformGetBestFitRes(reqW, reqH, desktopW, desktopH, &finalW, &finalH); + fprintf(stderr, "Warning: Requested resolution %dx%d is bigger than %dx%d, adjusting to %dx%d\n", + reqW, reqH, desktopW, desktopH, finalW, finalH); + } + + int window = glfwOpenWindow(finalW, finalH, 8, 8, 8, 8, 24, 8, GLFW_WINDOW); if (!window) { fprintf(stderr, "Failed to create GLFW window\n"); glfwTerminate(); +#ifdef _WIN32 + timeEndPeriod(1); +#endif return false; } + glfwSetWindowPos(((desktopW - finalW) / 2), ((desktopH - finalH) / 2)); glfwSetWindowTitle(title); @@ -220,14 +261,27 @@ bool platformInit(int32_t reqW, int32_t reqH, const char *title, bool headless) void platformExit(void) { glfwCloseWindow(); glfwTerminate(); +#ifdef _WIN32 + timeEndPeriod(1); +#endif } +// GLFW2's mouse cursor locks the mouse position when it's invisible on Windows. +// This just makes it visible/invisible as intended. static void platformSetCursor(int32_t cursorType) { // GLFW2 only supports showing/hiding if (cursorType == GML_CR_NONE) { +#ifdef _WIN32 + while (ShowCursor(FALSE) >= 0); +#else glfwDisable(GLFW_MOUSE_CURSOR); +#endif } else { +#ifdef _WIN32 + while (ShowCursor(TRUE) < 0); +#else glfwEnable(GLFW_MOUSE_CURSOR); +#endif } } @@ -269,7 +323,7 @@ void platformSwapBuffers(void) { void *platformGetProcAddress(const char *name) { #ifdef _WIN32 - // glfw2's glfwGetProcAddress is broken on Windows. + // GLFW2's glfwGetProcAddress is broken on Windows. // This just implements it in a way that's fixed so it can be passed to GLAD. void *ret = (void *)wglGetProcAddress(name); // Fallback for driver-specific error codes and legacy OpenGL core functions. @@ -292,7 +346,7 @@ bool platformHandleEvents(void) { } void platformSleepUntil(uint64_t time) { - double remaining = ((int64_t)time - nowNanos()) / 1000000000.0; + double remaining = ((int64_t)time - (int64_t)nowNanos()) / 1000000000.0; if (remaining > 0.002) // glfwSleep takes seconds as a double glfwSleep(remaining - 0.001); diff --git a/src/desktop/backends/glfw3.c b/src/desktop/backends/glfw3.c index a4472f57..433a57c2 100644 --- a/src/desktop/backends/glfw3.c +++ b/src/desktop/backends/glfw3.c @@ -5,6 +5,7 @@ #ifdef _WIN32 #include +#include #endif #ifdef ENABLE_SW_RENDERER @@ -57,13 +58,20 @@ bool platformGetScaledWindowSize(int32_t* outW, int32_t* outH) { } void platformSetWindowSize(int32_t width, int32_t height) { - if (width <= 0 || height <= 0) return; - if (!window) return; + if (width <= 0 || height <= 0 || !window) return; + if (glfwGetWindowAttrib(window, GLFW_MAXIMIZED)) return; + if (!platformCacheWindowSize(width, height)) return; + float xs = 1.0f, ys = 1.0f; glfwGetWindowContentScale(window, &xs, &ys); int logicalW, logicalH; framebufferToLogical(xs, ys, width, height, &logicalW, &logicalH); glfwSetWindowSize(window, logicalW, logicalH); + + const GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + if (mode != NULL) { + glfwSetWindowPos(window, ((mode->width - logicalW) / 2), ((mode->height - logicalH) / 2)); + } } void platformGetMousePos(double *xPos, double *yPos) { @@ -72,7 +80,7 @@ void platformGetMousePos(double *xPos, double *yPos) { } static bool platformGetWindowFocus(void) { - return glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; + return glfwGetWindowAttrib(window, GLFW_FOCUSED); } static void glfwErrorCallback(int code, const char* description) { @@ -170,10 +178,16 @@ static void scrollCallback(GLFWwindow* window, double xoffset, double yoffset) { } bool platformInit(int32_t reqW, int32_t reqH, const char *title, bool headless) { +#ifdef _WIN32 + timeBeginPeriod(1); +#endif // Init GLFW glfwSetErrorCallback(glfwErrorCallback); if (!glfwInit()) { fprintf(stderr, "Failed to initialize GLFW\n"); +#ifdef _WIN32 + timeEndPeriod(1); +#endif return false; } @@ -224,10 +238,26 @@ bool platformInit(int32_t reqW, int32_t reqH, const char *title, bool headless) if (headless) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); - window = glfwCreateWindow(reqW, reqH, title, nullptr, nullptr); + int finalW = reqW; + int finalH = reqH; + GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor(); + if (primaryMonitor) { + int workX, workY, workW, workH; + glfwGetMonitorWorkarea(primaryMonitor, &workX, &workY, &workW, &workH); + if (reqW >= workW || reqH >= workH) { + platformGetBestFitRes(reqW, reqH, workW, workH, &finalW, &finalH); + fprintf(stderr, "Warning: Requested resolution %dx%d is bigger than %dx%d, adjusting to %dx%d\n", + reqW, reqH, workW, workH, finalW, finalH); + } + } + + window = glfwCreateWindow(finalW, finalH, title, nullptr, nullptr); if (!window) { fprintf(stderr, "Failed to create GLFW window\n"); glfwTerminate(); +#ifdef _WIN32 + timeEndPeriod(1); +#endif return false; } @@ -236,7 +266,7 @@ bool platformInit(int32_t reqW, int32_t reqH, const char *title, bool headless) // If we don't do this, the window will be larger than it should be if you are using Wayland fractional scaling // We set the window size AFTER the window creation so we can use glfwGetWindowContentScale - platformSetWindowSize(reqW, reqH); + platformSetWindowSize(finalW, finalH); // Set up keyboard input glfwSetKeyCallback(window, keyCallback); @@ -251,6 +281,9 @@ bool platformInit(int32_t reqW, int32_t reqH, const char *title, bool headless) void platformExit(void) { glfwDestroyWindow(window); glfwTerminate(); +#ifdef _WIN32 + timeEndPeriod(1); +#endif } static void platformSetCursor(int32_t cursorType) { diff --git a/src/desktop/backends/sdl1.c b/src/desktop/backends/sdl1.c index 30b04b54..4bd2c1a4 100644 --- a/src/desktop/backends/sdl1.c +++ b/src/desktop/backends/sdl1.c @@ -41,9 +41,10 @@ bool platformGetScaledWindowSize(int32_t* outW, int32_t* outH) { void platformSetWindowSize(int32_t width, int32_t height) { if (width <= 0 || height <= 0) return; + if (!platformCacheWindowSize(width, height)) return; fbWidth = width; fbHeight = height; - scr = SDL_SetVideoMode(width, height, 0, (gfx == SOFTWARE ? 0 : SDL_OPENGL) | SDL_RESIZABLE); + scr = SDL_SetVideoMode(fbWidth, fbHeight, 0, (gfx == SOFTWARE ? 0 : SDL_OPENGL) | SDL_RESIZABLE); } void platformGetMousePos(double *xPos, double *yPos) { @@ -59,30 +60,76 @@ static bool platformGetWindowFocus(void) { return SDL_GetAppState() & SDL_APPINPUTFOCUS; } +#if !SDL_VERSION_ATLEAST(1, 2, 10) +static inline void platformGetTrueDesktopSize(int* outW, int* outH) { + SDL_Rect** modes = SDL_ListModes(NULL, SDL_FULLSCREEN); + if (modes != (SDL_Rect**)0 && modes != (SDL_Rect**)-1) { + int maxHardwareW = 0; + int maxHardwareH = 0; + int i; + + for (i = 0; modes[i]; ++i) { + if (modes[i]->w > maxHardwareW) maxHardwareW = modes[i]->w; + if (modes[i]->h > maxHardwareH) maxHardwareH = modes[i]->h; + } + + if (maxHardwareW > 0 && maxHardwareH > 0) { + if (maxHardwareW < *outW) *outW = maxHardwareW; + if (maxHardwareH < *outH) *outH = maxHardwareH; + } + } +} +#endif + bool platformInit(int32_t reqW, int32_t reqH, const char *title, bool headless) { if (headless && gfx != SOFTWARE) { fprintf(stderr, "Headless mode on SDL 1.2 requires the software renderer!\n"); return false; } +#if SDL_VERSION_ATLEAST(1, 2, 10) // Old SDL1.2: Center pos doesn't matter assuming it's running in low res + SDL_putenv("SDL_VIDEO_WINDOW_POS=center"); +#endif + // Init SDL if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER)) { fprintf(stderr, "Failed to initialize SDL\n"); return false; } - fbWidth = reqW; - fbHeight = reqH; - if(!headless) { + int finalW = reqW; + int finalH = reqH; +#if SDL_VERSION_ATLEAST(1, 2, 10) + const SDL_VideoInfo* info = SDL_GetVideoInfo(); + if (info && (reqW >= info->current_w || reqH >= info->current_h)) { + platformGetBestFitRes(reqW, reqH, info->current_w, info->current_h, &finalW, &finalH); + fprintf(stderr, "Warning: Requested resolution %dx%d is bigger than %dx%d, adjusting to %dx%d\n", + reqW, reqH, info->current_w, info->current_h, finalW, finalH); + } +#else + // Old SDL1.2: Set a default lower res then check if the screen supports it, if not, use the max supported res + int oldW = 800; + int oldH = 600; + platformGetTrueDesktopSize(oldW, oldH); + if (reqW >= oldW || reqH >= oldH) { + platformGetBestFitRes(reqW, reqH, oldW, oldH, &finalW, &finalH); + fprintf(stderr, "Warning: Requested resolution %dx%d is bigger than %dx%d, adjusting to %dx%d\n", + reqW, reqH, oldW, oldH, finalW, finalH); + } +#endif + + fbWidth = finalW; + fbHeight = finalH; + if (!headless) { scr = SDL_SetVideoMode(fbWidth, fbHeight, 0, (gfx == SOFTWARE ? 0 : SDL_OPENGL) | SDL_RESIZABLE); if (!scr && gfx == SOFTWARE) { SDL_Rect** modes = SDL_ListModes(NULL, SDL_FULLSCREEN); if (modes && modes != (SDL_Rect**) -1 && modes[0]) { fprintf(stderr, "Warning: %dx%d unavailable, falling back to %dx%d: %s\n", - reqW, reqH, modes[0]->w, modes[0]->h, SDL_GetError()); - scr = SDL_SetVideoMode(modes[0]->w, modes[0]->h, 0, 0); + fbWidth, fbHeight, modes[0]->w, modes[0]->h, SDL_GetError()); fbWidth = modes[0]->w; fbHeight = modes[0]->h; + scr = SDL_SetVideoMode(fbWidth, fbHeight, 0, 0); } } if (!scr) { @@ -217,21 +264,30 @@ static int32_t SDLMouseButtonToGml(int sdlButton) { bool platformHandleEvents(void) { SDL_Event e; while (SDL_PollEvent(&e)) { + switch (e.type) { + default: + if (InputRecording_isPlaybackActive(globalInputRecording)) continue; + break; + case SDL_VIDEORESIZE: + case SDL_QUIT: + break; + } switch(e.type) { case SDL_KEYDOWN: + // SDL1.2 needs to manually intercept Alt+F4 to exit properly + if (e.key.keysym.sym == SDLK_F4 && (e.key.keysym.mod & KMOD_ALT)) { + return true; + } // During playback, suppress real keyboard input - if (InputRecording_isPlaybackActive(globalInputRecording)) break; RunnerKeyboard_onKeyDown(g_runner->keyboard, SDLKeyToGml(e.key.keysym.sym)); if (e.key.keysym.unicode != 0) RunnerKeyboard_onCharacter(g_runner->keyboard, e.key.keysym.unicode); break; case SDL_KEYUP: // During playback, suppress real keyboard input - if (InputRecording_isPlaybackActive(globalInputRecording)) break; RunnerKeyboard_onKeyUp(g_runner->keyboard, SDLKeyToGml(e.key.keysym.sym)); break; case SDL_MOUSEBUTTONDOWN: - if (InputRecording_isPlaybackActive(globalInputRecording)) break; if (e.button.button == SDL_BUTTON_WHEELUP) { RunnerMouse_onWheel(g_runner->mouse, 1.0); } else if (e.button.button == SDL_BUTTON_WHEELDOWN) { @@ -242,7 +298,6 @@ bool platformHandleEvents(void) { } break; case SDL_MOUSEBUTTONUP: - if (InputRecording_isPlaybackActive(globalInputRecording)) break; if (e.button.button != SDL_BUTTON_WHEELUP && e.button.button != SDL_BUTTON_WHEELDOWN) { int32_t gmlBtn = SDLMouseButtonToGml(e.button.button); if (gmlBtn >= 0) RunnerMouse_onButtonUp(g_runner->mouse, gmlBtn); diff --git a/src/desktop/backends/sdl2.c b/src/desktop/backends/sdl2.c index 91029a63..979b3970 100644 --- a/src/desktop/backends/sdl2.c +++ b/src/desktop/backends/sdl2.c @@ -48,22 +48,36 @@ bool platformGetScaledWindowSize(int32_t* outW, int32_t* outH) { return true; } -static void platformGetWindowScale(float *scale_x, float *scale_y) { - if (!scale_x || !scale_y) return; +static float platformGetWindowScale(void) { int32_t draw_w, draw_h; int logical_w, logical_h; platformGetWindowSize(&draw_w, &draw_h); SDL_GetWindowSize(window, &logical_w, &logical_h); - *scale_x = (logical_w > 0) ? (float)draw_w / logical_w : 1.0f; - *scale_y = (logical_h > 0) ? (float)draw_h / logical_h : 1.0f; + return (logical_h > 0) ? (float)draw_h / logical_h : 1.0f; } void platformSetWindowSize(int32_t width, int32_t height) { if (width <= 0 || height <= 0) return; + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED) return; + + // Account for correct size adjustment for multiple monitors + int32_t finalW = width; + int32_t finalH = height; + SDL_Rect usableBounds; + int displayIndex = SDL_GetWindowDisplayIndex(window); + if (displayIndex < 0) displayIndex = 0; + if (SDL_GetDisplayUsableBounds(displayIndex, &usableBounds) == 0) { + if (width > usableBounds.w || height > usableBounds.h) { + platformGetBestFitRes(width, height, usableBounds.w, usableBounds.h, &finalW, &finalH); + } + } - float scale_x, scale_y; - platformGetWindowScale(&scale_x, &scale_y); - SDL_SetWindowSize(window, (int)(width / scale_x), (int)(height / scale_y)); + if (!platformCacheWindowSize(width, height)) return; + + // Scale window down to account for HIDPI + float scale = platformGetWindowScale(); + SDL_SetWindowSize(window, (int)(finalW / scale), (int)(finalH / scale)); + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); if (gfx == SOFTWARE) scr = SDL_GetWindowSurface(window); @@ -73,10 +87,9 @@ void platformGetMousePos(double *xPos, double *yPos) { if (!xPos || !yPos) return; int mx = 0, my = 0; SDL_GetMouseState(&mx, &my); - float scale_x, scale_y; - platformGetWindowScale(&scale_x, &scale_y); - *xPos = (double)mx * scale_x; - *yPos = (double)my * scale_y; + float scale = platformGetWindowScale(); + *xPos = (double)mx * scale; + *yPos = (double)my * scale; } static bool platformGetWindowFocus(void) { @@ -114,25 +127,36 @@ bool platformInit(int reqW, int reqH, const char *title, bool headless) { else flags = (gfx == SOFTWARE ? 0 : SDL_WINDOW_OPENGL) | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; + int finalW = reqW; + int finalH = reqH; + SDL_Rect usableBounds; + if (SDL_GetDisplayUsableBounds(0, &usableBounds) == 0) { + if (reqW >= usableBounds.w || reqH >= usableBounds.h) { + platformGetBestFitRes(reqW, reqH, usableBounds.w, usableBounds.h, &finalW, &finalH); + fprintf(stderr, "Warning: Requested resolution %dx%d is bigger than %dx%d, adjusting to %dx%d\n", + reqW, reqH, usableBounds.w, usableBounds.h, finalW, finalH); + } + } + window = SDL_CreateWindow( title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - reqW, reqH, + finalW, finalH, flags ); if (!window && gfx == SOFTWARE) { SDL_DisplayMode mode; if (SDL_GetDisplayMode(0, 0, &mode) == 0) { fprintf(stderr, "Warning: %dx%d unavailable, falling back to %dx%d: %s\n", - reqW, reqH, mode.w, mode.h, SDL_GetError()); - reqW = mode.w; - reqH = mode.h; + finalW, finalH, mode.w, mode.h, SDL_GetError()); + finalW = mode.w; + finalH = mode.h; window = SDL_CreateWindow( title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - mode.w, mode.h, + finalW, finalH, flags ); } @@ -151,7 +175,7 @@ bool platformInit(int reqW, int reqH, const char *title, bool headless) { scr = SDL_GetWindowSurface(window); // If we don't do this, the window will be larger than it should be on HiDPI displays. - platformSetWindowSize(reqW, reqH); + platformSetWindowSize(finalW, finalH); return true; } @@ -324,8 +348,10 @@ bool platformHandleEvents(void) { switch (e.type) { default: if (InputRecording_isPlaybackActive(globalInputRecording)) continue; + break; case SDL_WINDOWEVENT: case SDL_QUIT: + break; } switch(e.type) { case SDL_KEYDOWN: diff --git a/src/desktop/backends/sdl3.c b/src/desktop/backends/sdl3.c index eea805e2..94e61151 100644 --- a/src/desktop/backends/sdl3.c +++ b/src/desktop/backends/sdl3.c @@ -14,7 +14,6 @@ #include "runner_mouse.h" static Runner *g_runner; -static int32_t fbWidth, fbHeight; static SDL_Surface* scr; static SDL_Window *window; static SDL_Gamepad* openControllers[MAX_GAMEPADS]; @@ -45,20 +44,64 @@ void platformSetWindowTitle(const char* title) { bool platformGetWindowSize(int32_t* outW, int32_t* outH) { if (!outW || !outH) return false; - *outW = fbWidth; - *outH = fbHeight; + if (gfx == SOFTWARE) { + if (scr->w <= 0 || scr->h <= 0) return false; + *outW = scr->w; + *outH = scr->h; + } else { + int w = 0; + int h = 0; + SDL_GetWindowSizeInPixels(window, &w, &h); + if (w <= 0 || h <= 0) return false; + *outW = w; + *outH = h; + } return true; } bool platformGetScaledWindowSize(int32_t* outW, int32_t* outH) { - return platformGetWindowSize(outW, outH); + if (!outW || !outH) return false; + int w = 0; + int h = 0; + SDL_GetWindowSize(window, &w, &h); + if (w <= 0 || h <= 0) return false; + *outW = w; + *outH = h; + return true; } void platformSetWindowSize(int32_t width, int32_t height) { if (width <= 0 || height <= 0) return; - fbWidth = width; - fbHeight = height; - SDL_SetWindowSize(window, width, height); + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED) return; + + // Account for correct size adjustment for multiple monitors + int32_t finalW = width; + int32_t finalH = height; + SDL_Rect usableBounds; + SDL_DisplayID displayID = SDL_GetDisplayForWindow(window); + if (displayID == 0) displayID = SDL_GetPrimaryDisplay(); + if (SDL_GetDisplayUsableBounds(displayID, &usableBounds)) { + if (width > usableBounds.w || height > usableBounds.h) { + platformGetBestFitRes(width, height, usableBounds.w, usableBounds.h, &finalW, &finalH); + } + } + + if (!platformCacheWindowSize(width, height)) return; + + // No scale here for HIDPI, SDL3 seems to do it internally. + SDL_SetWindowSize(window, finalW, finalH); + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + SDL_SyncWindow(window); + + // Move the window a bit down so the title bar is visible on large sizes. + int top_border, windowX, windowY; + SDL_GetWindowBordersSize(window, &top_border, NULL, NULL, NULL); + SDL_GetWindowPosition(window, &windowX, &windowY); + if (top_border > 0) { + windowY += top_border; + SDL_SetWindowPosition(window, windowX, windowY); + } + if (gfx == SOFTWARE) scr = SDL_GetWindowSurface(window); } @@ -104,13 +147,34 @@ bool platformInit(int reqW, int reqH, const char *title, bool headless) { #endif } - Uint32 flags = (gfx == SOFTWARE ? 0 : SDL_WINDOW_OPENGL) | (headless ? SDL_WINDOW_HIDDEN : SDL_WINDOW_RESIZABLE); - fbWidth = reqW; - fbHeight = reqH; + Uint32 flags; + if (headless) + flags = (gfx == SOFTWARE ? 0 : SDL_WINDOW_OPENGL) | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; + else + flags = (gfx == SOFTWARE ? 0 : SDL_WINDOW_OPENGL) | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY; + + int finalW = reqW; + int finalH = reqH; + int num_displays = 0; + SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); + if (displays && num_displays > 0) { + SDL_DisplayID primaryDisplay = displays[0]; + SDL_Rect usableBounds; + if (SDL_GetDisplayUsableBounds(primaryDisplay, &usableBounds)) { + if (reqW >= usableBounds.w || reqH >= usableBounds.h) { + platformGetBestFitRes(reqW, reqH, usableBounds.w, usableBounds.h, &finalW, &finalH); + fprintf(stderr, "Warning: Requested resolution %dx%d is bigger than %dx%d, adjusting to %dx%d\n", + reqW, reqH, usableBounds.w, usableBounds.h, finalW, finalH); + } + } + } + if (displays) + SDL_free(displays); + window = SDL_CreateWindow( title, - fbWidth, - fbHeight, + finalW, + finalH, flags ); if (!window && gfx == SOFTWARE) { @@ -118,13 +182,13 @@ bool platformInit(int reqW, int reqH, const char *title, bool headless) { const SDL_DisplayMode *mode = SDL_GetCurrentDisplayMode(display_id); if (mode != NULL) { fprintf(stderr, "Warning: %dx%d unavailable, falling back to %dx%d: %s\n", - reqW, reqH, mode->w, mode->h, SDL_GetError()); - fbWidth = mode->w; - fbHeight = mode->h; + finalW, finalH, mode->w, mode->h, SDL_GetError()); + finalW = mode->w; + finalH = mode->h; window = SDL_CreateWindow( title, - fbWidth, - fbHeight, + finalW, + finalH, flags ); } @@ -142,6 +206,9 @@ bool platformInit(int reqW, int reqH, const char *title, bool headless) { } else scr = SDL_GetWindowSurface(window); + // If we don't do this, the window will be larger than it should be on HiDPI displays. + platformSetWindowSize(finalW, finalH); + return true; } @@ -339,45 +406,44 @@ static void mapSdl3ToGml(SDL_Gamepad* gp, GamepadSlot* slot) { } bool platformHandleEvents(void) { - bool should_exit = false; SDL_Event e; while (SDL_PollEvent(&e)) { + switch (e.type) { + default: + if (InputRecording_isPlaybackActive(globalInputRecording)) continue; + break; + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + case SDL_EVENT_QUIT: + break; + } switch(e.type) { case SDL_EVENT_KEY_DOWN: // During playback, suppress real keyboard input - if (InputRecording_isPlaybackActive(globalInputRecording)) break; if (e.key.repeat != 0) break; RunnerKeyboard_onKeyDown(g_runner->keyboard, SDLKeyToGml(e.key.key)); break; case SDL_EVENT_KEY_UP: // During playback, suppress real keyboard input - if (InputRecording_isPlaybackActive(globalInputRecording)) break; RunnerKeyboard_onKeyUp(g_runner->keyboard, SDLKeyToGml(e.key.key)); break; case SDL_EVENT_TEXT_INPUT: // During playback, suppress real keyboard input - if (InputRecording_isPlaybackActive(globalInputRecording)) break; RunnerKeyboard_onCharacter(g_runner->keyboard, utf8_to_codepoint(e.text.text)); break; case SDL_EVENT_MOUSE_BUTTON_DOWN: { - if (InputRecording_isPlaybackActive(globalInputRecording)) break; int32_t gmlBtn = SDLMouseButtonToGml(e.button.button); if (gmlBtn >= 0) RunnerMouse_onButtonDown(g_runner->mouse, gmlBtn); } break; case SDL_EVENT_MOUSE_BUTTON_UP: { - if (InputRecording_isPlaybackActive(globalInputRecording)) break; int32_t gmlBtn = SDLMouseButtonToGml(e.button.button); if (gmlBtn >= 0) RunnerMouse_onButtonUp(g_runner->mouse, gmlBtn); } break; case SDL_EVENT_MOUSE_WHEEL: - if (InputRecording_isPlaybackActive(globalInputRecording)) break; if (e.wheel.y != 0) RunnerMouse_onWheel(g_runner->mouse, (float)e.wheel.y); break; - case SDL_EVENT_WINDOW_RESIZED: - fbWidth = e.window.data1; - fbHeight = e.window.data2; + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: if (gfx == SOFTWARE) scr = SDL_GetWindowSurface(window); break; @@ -406,8 +472,7 @@ bool platformHandleEvents(void) { break; } case SDL_EVENT_QUIT: - should_exit = true; - break; + return true; default: break; } @@ -463,7 +528,7 @@ bool platformHandleEvents(void) { } } - return should_exit; + return false; } void platformSleepUntil(uint64_t time) { diff --git a/src/desktop/main.c b/src/desktop/main.c index 1e635fc0..22351458 100644 --- a/src/desktop/main.c +++ b/src/desktop/main.c @@ -878,8 +878,8 @@ static void onCrashSignal(int sig) { // ===[ MAIN ]=== int main(int argc, char* argv[]) { setbuf(stderr, NULL); -#ifdef _WIN32 - timeBeginPeriod(1); +#if defined(USE_SDL2) || defined(USE_SDL3) + setbuf(stdout, NULL); #endif CommandLineArgs args; @@ -1803,9 +1803,6 @@ int main(int argc, char* argv[]) { arrfree(newArguments); } -#ifdef _WIN32 - timeEndPeriod(1); -#endif printf("Bye! :3\n"); } } diff --git a/src/desktop/platformdefs.h b/src/desktop/platformdefs.h index e45b5c49..90e83843 100644 --- a/src/desktop/platformdefs.h +++ b/src/desktop/platformdefs.h @@ -19,6 +19,31 @@ void platformSetWindowSize(int32_t width, int32_t height); void platformSetWindowTitle(const char* title); void platformSleepUntil(uint64_t time); +// Some games call platformSetWindowSize every frame so this avoids redundant resizing +static inline bool platformCacheWindowSize(int32_t width, int32_t height) { + static int32_t last_width = -1; + static int32_t last_height = -1; + if (width == last_width && height == last_height) return false; + last_width = width; + last_height = height; + return true; +} + +// Some games call an initial resolution larger than the screen so resize properly +static inline void platformGetBestFitRes(int reqW, int reqH, int screenW, int screenH, int *finalW, int *finalH) { + if (!finalW || !finalH) return; + if ((float)reqW / reqH > (float)screenW / screenH) { + *finalH = screenH; + *finalW = screenW; + } else { + float scaleX = (float)screenW / reqW; + float scaleY = (float)screenH / reqH; + float scale = (scaleX < scaleY) ? scaleX : scaleY; + *finalW = (int)(reqW * scale); + *finalH = (int)(reqH * scale); + } +} + enum GraphicsAPI { SOFTWARE, MODERN_GL,