From 2b7773d933efadedabc94ad6f2c3304ec4143f99 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 9 Dec 2025 03:29:16 +0500 Subject: [PATCH] engine: platform: sdl2: rework picking suitable resolution when creating or modifying current window --- engine/client/cl_render.c | 2 +- engine/client/vid_common.h | 8 - engine/platform/sdl2/platform_sdl2.h | 1 - engine/platform/sdl2/vid_sdl2.c | 351 +++++++++++++-------------- engine/ref_api.h | 10 +- 5 files changed, 185 insertions(+), 187 deletions(-) diff --git a/engine/client/cl_render.c b/engine/client/cl_render.c index 0418d5c2..356bbec6 100644 --- a/engine/client/cl_render.c +++ b/engine/client/cl_render.c @@ -160,7 +160,7 @@ intptr_t CL_RenderGetParm( const int parm, const int arg, const qboolean checkRe case PARM_SHADOWDATA: return (intptr_t)world.shadowdata; case PARM_FULLSCREEN: - return refState.fullScreen; + return refState.window_mode == WINDOW_MODE_BORDERLESS || refState.window_mode == WINDOW_MODE_FULLSCREEN; case PARM_WIDESCREEN: return refState.wideScreen; case PARM_SCREEN_WIDTH: diff --git a/engine/client/vid_common.h b/engine/client/vid_common.h index 3acca8eb..6643fa65 100644 --- a/engine/client/vid_common.h +++ b/engine/client/vid_common.h @@ -9,14 +9,6 @@ typedef struct vidmode_s int height; } vidmode_t; -typedef enum window_mode_e -{ - WINDOW_MODE_WINDOWED = 0, - WINDOW_MODE_FULLSCREEN, - WINDOW_MODE_BORDERLESS, - WINDOW_MODE_COUNT, -} window_mode_t; - typedef struct { void *context; // handle to GL rendering context diff --git a/engine/platform/sdl2/platform_sdl2.h b/engine/platform/sdl2/platform_sdl2.h index 6fec0183..1d02d81b 100644 --- a/engine/platform/sdl2/platform_sdl2.h +++ b/engine/platform/sdl2/platform_sdl2.h @@ -25,7 +25,6 @@ void VID_RestoreScreenResolution( window_mode_t window_mode ); qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode ); void VID_DestroyWindow( void ); void GL_InitExtensions( void ); -qboolean GL_DeleteContext( void ); void VID_SaveWindowSize( int width, int height, qboolean maximized ); // diff --git a/engine/platform/sdl2/vid_sdl2.c b/engine/platform/sdl2/vid_sdl2.c index b549ea48..7e323cbb 100644 --- a/engine/platform/sdl2/vid_sdl2.c +++ b/engine/platform/sdl2/vid_sdl2.c @@ -379,46 +379,16 @@ GL_DeleteContext always return false ================= */ -qboolean GL_DeleteContext( void ) +static qboolean GL_DeleteContext( void ) { if( glw_state.context ) { - SDL_GL_DeleteContext(glw_state.context); + SDL_GL_DeleteContext( glw_state.context ); glw_state.context = NULL; } return false; } -/* -================= -GL_CreateContext -================= -*/ -static qboolean GL_CreateContext( void ) -{ - if( ( glw_state.context = SDL_GL_CreateContext( host.hWnd ) ) == NULL) - { - Con_Reportf( S_ERROR "%s: %s\n", __func__, SDL_GetError( )); - return GL_DeleteContext(); - } - return true; -} - -/* -================= -GL_UpdateContext -================= -*/ -static qboolean GL_UpdateContext( void ) -{ - if( SDL_GL_MakeCurrent( host.hWnd, glw_state.context ) < 0 ) - { - Con_Reportf( S_ERROR "%s: %s\n", __func__, SDL_GetError( )); - return GL_DeleteContext(); - } - return true; -} - void VID_SaveWindowSize( int width, int height, qboolean maximized ) { int render_w = width, render_h = height; @@ -436,49 +406,82 @@ void VID_SaveWindowSize( int width, int height, qboolean maximized ) R_SaveVideoMode( width, height, render_w, render_h, maximized ); } -static qboolean VID_SetScreenResolution( int width, int height, window_mode_t window_mode ) +static qboolean VID_GuessFullscreenMode( int display_index, const SDL_DisplayMode *want, SDL_DisplayMode *got ) { - SDL_DisplayMode got; - - if( window_mode == WINDOW_MODE_BORDERLESS ) + if( SDL_GetClosestDisplayMode( display_index, want, got ) == NULL ) { - if( SDL_GetDesktopDisplayMode( 0, &got ) < 0 ) + Con_Printf( S_ERROR "%s: SDL_GetClosestDisplayMode: %s\n", __func__, SDL_GetError( )); + + // fall back to native mode + if( SDL_GetDesktopDisplayMode( display_index, got ) < 0 ) { Con_Printf( S_ERROR "%s: SDL_GetDesktopDisplayMode: %s\n", __func__, SDL_GetError( )); + return false; } + if( got->w != want->w || got->h != want->h ) + Con_Reportf( S_NOTE "Got desktop display mode: %ix%i@%i\n", got->w, got->h, got->refresh_rate ); + } + else + { + if( got->w != want->w || got->h != want->h ) + Con_Reportf( S_NOTE "Got closest display mode: %ix%i@%i\n", got->w, got->h, got->refresh_rate ); + } + + return true; +} + +static int VID_GetDisplayIndex( const char *caller, const SDL_Point *pt ) +{ + int display_index; + + if( pt ) + { +#if SDL_VERSION_ATLEAST( 2, 24, 0 ) + display_index = SDL_GetPointDisplayIndex( pt ); +#else + display_index = 0; +#endif + } + else + { + if( !host.hWnd ) + return 0; + + display_index = SDL_GetWindowDisplayIndex( host.hWnd ); + } + + if( display_index < 0 ) + { + Con_Printf( S_ERROR "%s: SDL_Get%sDisplayIndex: %s\n", caller, pt ? "Point" : "Display", SDL_GetError()); + display_index = 0; + } + + return display_index; +} + +static qboolean VID_SetScreenResolution( int width, int height, window_mode_t window_mode, window_mode_t prev_window_mode ) +{ + int out_width, out_height; + + switch( window_mode ) + { + case WINDOW_MODE_BORDERLESS: if( SDL_SetWindowFullscreen( host.hWnd, SDL_WINDOW_FULLSCREEN_DESKTOP ) < 0 ) { Con_Printf( S_ERROR "%s: SDL_SetWindowFullscreen (borderless): %s\n", __func__, SDL_GetError( )); return false; } - } - else if( window_mode == WINDOW_MODE_FULLSCREEN ) + break; + case WINDOW_MODE_FULLSCREEN: { - SDL_DisplayMode want = { 0 }; - want.w = width; - want.h = height; + const SDL_DisplayMode want = { .w = width, .h = height }; + SDL_DisplayMode got; + int display_index = VID_GetDisplayIndex( __func__, NULL ); - if( SDL_GetClosestDisplayMode( 0, &want, &got ) == NULL ) - { - Con_Printf( S_ERROR "%s: SDL_GetClosestDisplayMode: %s\n", __func__, SDL_GetError( )); - - // fall back to native mode - if( SDL_GetDesktopDisplayMode( 0, &got ) < 0 ) - { - Con_Printf( S_ERROR "%s: SDL_GetDesktopDisplayMode: %s\n", __func__, SDL_GetError( )); - return false; - } - - if( got.w != want.w || got.h != want.h ) - Con_Reportf( S_NOTE "Got desktop display mode: %ix%i@%i\n", got.w, got.h, got.refresh_rate ); - } - else - { - if( got.w != want.w || got.h != want.h ) - Con_Reportf( S_NOTE "Got closest display mode: %ix%i@%i\n", got.w, got.h, got.refresh_rate ); - } + if( !VID_GuessFullscreenMode( display_index, &want, &got )) + return false; if( SDL_SetWindowDisplayMode( host.hWnd, &got ) < 0 ) { @@ -491,10 +494,37 @@ static qboolean VID_SetScreenResolution( int width, int height, window_mode_t wi Con_Printf( S_ERROR "%s: SDL_SetWindowFullscreen (fullscreen): %s\n", __func__, SDL_GetError( )); return false; } + break; + } + case WINDOW_MODE_WINDOWED: + { + VID_RestoreScreenResolution( window_mode ); + + if( SDL_SetWindowFullscreen( host.hWnd, 0 ) < 0 ) + { + Con_Printf( S_ERROR "%s: SDL_SetWindowFullscreen (windowed): %s\n", __func__, SDL_GetError( )); + return false; + } + + SDL_SetWindowResizable( host.hWnd, SDL_TRUE ); + SDL_SetWindowBordered( host.hWnd, SDL_TRUE ); + + qboolean maximized = FBitSet( SDL_GetWindowFlags( host.hWnd ), SDL_WINDOW_MAXIMIZED ) != 0; + if( !maximized ) + SDL_SetWindowSize( host.hWnd, width, height ); + + break; + } } - SDL_SetWindowSize( host.hWnd, got.w, got.h ); - VID_SaveWindowSize( got.w, got.h, true ); + SDL_GetWindowSize( host.hWnd, &out_width, &out_height ); + + Con_Reportf( "%s: Setting video mode to %dx%d %s\n", __func__, out_width, out_height, + window_mode == WINDOW_MODE_BORDERLESS ? "borderless" : + window_mode == WINDOW_MODE_FULLSCREEN ? "fullscreen" : "windowed" ); + + VID_SaveWindowSize( out_width, out_height, FBitSet( SDL_GetWindowFlags( host.hWnd ), SDL_WINDOW_MAXIMIZED ) != 0 ); + return true; } @@ -587,7 +617,7 @@ static qboolean VID_CreateWindowWithSafeGL( const char *wndname, const SDL_Rect if( host.hWnd ) break; - Con_Reportf( S_ERROR "%s: couldn't create '%s' with safegl level %d: %s\n", __func__, wndname, glw_state.safe, SDL_GetError()); + Con_Printf( S_ERROR "%s: couldn't create '%s' with safegl level %d: %s\n", __func__, wndname, glw_state.safe, SDL_GetError()); glw_state.safe++; @@ -621,24 +651,27 @@ VID_CreateWindow */ qboolean VID_CreateWindow( int input_width, int input_height, window_mode_t window_mode ) { - qboolean maximized = vid_maximized.value != 0.0f; - Uint32 wndFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_ALLOW_HIGHDPI; + Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_ALLOW_HIGHDPI; SDL_Rect rect = { window_xpos.value, window_ypos.value, input_width, input_height }; + const qboolean position_undefined = rect.x < 0 || rect.y < 0; if( !glw_state.software ) - SetBits( wndFlags, SDL_WINDOW_OPENGL ); + SetBits( flags, SDL_WINDOW_OPENGL ); - if( window_mode == WINDOW_MODE_WINDOWED ) + if( position_undefined ) + rect.x = rect.y = SDL_WINDOWPOS_UNDEFINED; + + switch( window_mode ) { - SetBits( wndFlags, SDL_WINDOW_RESIZABLE ); - if( maximized ) - SetBits( wndFlags, SDL_WINDOW_MAXIMIZED ); + // in windowed mode, we only want to ensure that + // window fits on any display, and if not, reset position + case WINDOW_MODE_WINDOWED: + SetBits( flags, SDL_WINDOW_RESIZABLE ); - if( rect.x < 0 || rect.y < 0 ) - { - rect.x = rect.y = SDL_WINDOWPOS_UNDEFINED; - } - else + if( vid_maximized.value != 0.0f ) + SetBits( flags, SDL_WINDOW_MAXIMIZED ); + + if( !position_undefined ) { const int num_displays = SDL_GetNumVideoDisplays(); qboolean window_fits = false; @@ -671,72 +704,86 @@ qboolean VID_CreateWindow( int input_width, int input_height, window_mode_t wind rect.x = rect.y = SDL_WINDOWPOS_UNDEFINED; } } - } - else + break; + // in fullscreen modes, we keep positions + // (as they might indicate chosen display in multimonitor configs) + case WINDOW_MODE_BORDERLESS: + SetBits( flags, SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_BORDERLESS ); + break; + // in true fullscreen mode, we need to guess better video mode + case WINDOW_MODE_FULLSCREEN: { - if( window_mode == WINDOW_MODE_FULLSCREEN ) - // need input grab only in true fullscreen mode - SetBits( wndFlags, SDL_WINDOW_FULLSCREEN | SDL_WINDOW_INPUT_GRABBED ); - else - SetBits( wndFlags, SDL_WINDOW_FULLSCREEN_DESKTOP ); - SetBits( wndFlags, SDL_WINDOW_BORDERLESS ); + const SDL_DisplayMode want = { .w = rect.w, .h = rect.h }; + SDL_DisplayMode got; + int display_index = 0; - if ( window_xpos.value < 0 || window_ypos.value < 0 ) - rect.x = rect.y = SDL_WINDOWPOS_UNDEFINED; + SetBits( flags, SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS ); + + if( !position_undefined ) + { + const SDL_Point pt = { .x = rect.x, .y = rect.y }; + + display_index = VID_GetDisplayIndex( __func__, &pt ); + } + + if( VID_GuessFullscreenMode( display_index, &want, &got )) + { + rect.w = got.w; + rect.h = got.h; + } + + break; + } } - if( !VID_CreateWindowWithSafeGL( GI->title, &rect, wndFlags )) + if( !VID_CreateWindowWithSafeGL( GI->title, &rect, flags )) return false; - // update window size if it was resized - SDL_GetWindowSize( host.hWnd, &rect.w, &rect.h ); - - if( window_mode != WINDOW_MODE_WINDOWED ) - { - if( !VID_SetScreenResolution( rect.w, rect.h, window_mode )) - return false; - } - else VID_RestoreScreenResolution( window_mode ); - VID_SetWindowIcon( host.hWnd ); SDL_ShowWindow( host.hWnd ); if( glw_state.software ) { - int sdl_renderer = -2; char cmd[64]; if( Sys_GetParmFromCmdLine( "-sdl_renderer", cmd )) - sdl_renderer = Q_atoi( cmd ); - - if( sdl_renderer >= -1 ) { + int sdl_renderer = Q_max( -1, Q_atoi( cmd )); + sw.renderer = SDL_CreateRenderer( host.hWnd, sdl_renderer, 0 ); + if( !sw.renderer ) - Con_Printf( S_ERROR "failed to create SDL renderer: %s\n", SDL_GetError() ); - else { - SDL_RendererInfo info; - SDL_GetRendererInfo( sw.renderer, &info ); - Con_Printf( "SDL_Renderer %s initialized\n", info.name ); + Con_Printf( S_ERROR "%s: SDL_CreateRenderer: %s\n", __func__, SDL_GetError( )); + return false; } + + SDL_RendererInfo info; + SDL_GetRendererInfo( sw.renderer, &info ); + Con_Printf( "SDL_Renderer %s initialized\n", info.name ); } } else { - while( !GL_CreateContext( )) + glw_state.context = SDL_GL_CreateContext( host.hWnd ); + + if( !glw_state.context ) { - glw_state.safe++; - if( glw_state.safe > SAFE_DONTCARE ) - return false; - GL_SetupAttributes(); // re-choose attributes + Con_Printf( S_ERROR "%s: SDL_GL_CreateContext: %s\n", __func__, SDL_GetError()); + return false; } - if( !GL_UpdateContext( )) + if( SDL_GL_MakeCurrent( host.hWnd, glw_state.context ) < 0 ) + { + Con_Printf( S_ERROR "%s: SDL_GL_MakeCurrent: %s\n", __func__, SDL_GetError( )); + GL_DeleteContext(); return false; + } } - VID_SaveWindowSize( rect.w, rect.h, maximized ); + // update window size if it was resized + SDL_GetWindowSize( host.hWnd, &rect.w, &rect.h ); + VID_SaveWindowSize( rect.w, rect.h, FBitSet( SDL_GetWindowFlags( host.hWnd ), SDL_WINDOW_MAXIMIZED )); return true; } @@ -751,16 +798,12 @@ void VID_DestroyWindow( void ) GL_DeleteContext(); VID_RestoreScreenResolution( (window_mode_t)vid_fullscreen.value ); - if( host.hWnd ) - { - SDL_DestroyWindow ( host.hWnd ); - host.hWnd = NULL; - } - if( refState.fullScreen ) - { - refState.fullScreen = false; - } + if( host.hWnd ) + SDL_DestroyWindow( host.hWnd ); + + host.hWnd = NULL; + refState.window_mode = WINDOW_MODE_WINDOWED; } /* @@ -874,25 +917,13 @@ qboolean R_Init_Video( const int type ) string safe; qboolean retval; SDL_DisplayMode displayMode; -#if SDL_VERSION_ATLEAST( 2, 24, 0 ) - SDL_Point point = { window_xpos.value, window_ypos.value }; - int displayIndex = SDL_GetPointDisplayIndex( &point ); -#else - int displayIndex = 0; -#endif - - SDL_GetCurrentDisplayMode( displayIndex, &displayMode ); + const SDL_Point point = { window_xpos.value, window_ypos.value }; + SDL_GetCurrentDisplayMode( VID_GetDisplayIndex( __func__, &point ), &displayMode ); refState.desktopBitsPixel = SDL_BITSPERPIXEL( displayMode.format ); -#ifdef SDL_HINT_QTWAYLAND_WINDOW_FLAGS - SDL_SetHint( SDL_HINT_QTWAYLAND_WINDOW_FLAGS, "OverridesSystemGestures" ); -#endif -#ifdef SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION - SDL_SetHint( SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION, "landscape" ); -#endif - if( Sys_CheckParm( "-egl" ) ) + if( Sys_CheckParm( "-egl" )) { // EGL doesn't mean we want GLES context // so force it only on Windows, where GL is usually created via WGL @@ -903,6 +934,8 @@ qboolean R_Init_Video( const int type ) SDL_SetHint( SDL_HINT_VIDEO_X11_FORCE_EGL, "1" ); } + SDL_SetHint( SDL_HINT_QTWAYLAND_WINDOW_FLAGS, "OverridesSystemGestures" ); + SDL_SetHint( SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION, "landscape" ); SDL_SetHint( SDL_HINT_VIDEO_X11_XRANDR, "1" ); SDL_SetHint( SDL_HINT_VIDEO_X11_XVIDMODE, "1" ); @@ -954,56 +987,22 @@ qboolean R_Init_Video( const int type ) rserr_t R_ChangeDisplaySettings( int width, int height, window_mode_t window_mode ) { - SDL_DisplayMode displayMode; - - if( SDL_GetCurrentDisplayMode( 0, &displayMode ) < 0 ) - { - Con_Printf( S_ERROR "SDL_GetCurrentDisplayMode: %s\n", SDL_GetError( )); - return rserr_invalid_mode; - } - - // check our desktop attributes - refState.desktopBitsPixel = SDL_BITSPERPIXEL( displayMode.format ); - if( window_mode == WINDOW_MODE_BORDERLESS ) - { - width = displayMode.w; - height = displayMode.h; - } - - refState.fullScreen = window_mode != WINDOW_MODE_WINDOWED; - Con_Reportf( "%s: Setting video mode to %dx%d %s\n", __func__, width, height, - window_mode == WINDOW_MODE_BORDERLESS ? "borderless" : - window_mode == WINDOW_MODE_WINDOWED ? "windowed" : - "fullscreen" ); + SDL_DisplayMode display_mode; if( !host.hWnd ) { if( !VID_CreateWindow( width, height, window_mode )) return rserr_invalid_mode; } - else if( refState.fullScreen ) - { - if( !VID_SetScreenResolution( width, height, window_mode )) - return rserr_invalid_fullscreen; - } else { - VID_RestoreScreenResolution( window_mode ); - - if( SDL_SetWindowFullscreen( host.hWnd, 0 ) < 0 ) - { - Con_Printf( S_ERROR "SDL_SetWindowFullscreen: %s\n", SDL_GetError( )); + if( !VID_SetScreenResolution( width, height, window_mode, refState.window_mode )) return rserr_invalid_fullscreen; - } -#if SDL_VERSION_ATLEAST( 2, 0, 5 ) - SDL_SetWindowResizable( host.hWnd, SDL_TRUE ); -#endif - SDL_SetWindowBordered( host.hWnd, SDL_TRUE ); - SDL_SetWindowSize( host.hWnd, width, height ); - - VID_SaveWindowSize( width, height, FBitSet( SDL_GetWindowFlags( host.hWnd ), SDL_WINDOW_MAXIMIZED ) != 0 ); } + SDL_GetWindowDisplayMode( host.hWnd, &display_mode ); + refState.desktopBitsPixel = SDL_BITSPERPIXEL( display_mode.format ); + refState.window_mode = window_mode; return rserr_ok; } diff --git a/engine/ref_api.h b/engine/ref_api.h index 11f5869e..a14d0291 100644 --- a/engine/ref_api.h +++ b/engine/ref_api.h @@ -105,6 +105,14 @@ typedef enum DEMO_QUAKE1 } demo_mode; +typedef enum window_mode_e +{ + WINDOW_MODE_WINDOWED = 0, + WINDOW_MODE_FULLSCREEN, + WINDOW_MODE_BORDERLESS, + WINDOW_MODE_COUNT, +} window_mode_t; + typedef enum ref_window_type_e { REF_WINDOW_TYPE_NULL = 0, @@ -129,7 +137,7 @@ typedef struct ref_globals_s int width; int height; - qboolean fullScreen; + window_mode_t window_mode; qboolean wideScreen; vec3_t vieworg;