raylib
raylib copied to clipboard
[rcore][desktop_glfw] Reviewed `ToggleFullScreen()`, `ToggleBorderlessWindowed()` and `FLAG_WINDOW_HIGHDPI` issues
dont merge it yet
You can test this PR again, code updated with new pipeline.
-
should fix most issues related to
ToggleFullScreen(),ToggleBorderlessWindowed()andFLAG_WINDOW_HIGHDPI -
use a custom implementation of
SetupFramebuffer()without affecting other platform backends and without changingrcore.c(edit: well, i had to add some changes torcore.cbut they should not impact other platforms) -
adds a new experimental
FLAG_RESCALE_CONTENTthat keeps the size of what you requested inInitWindow()and rescale and center it with borders. (so if you requested a 500x500 drawing surface, you'll allways have 500x500 drawing surface rescaled with the same aspect ratio that always fits inside the window or fullscreen window). see screen capture here
Need confirmations and testing on :
- [ ] MacOs <-- (i don't have access to this system)
- [ ] Windows + WLS ? (i won't bother install WLS on my Windows machines, so if one of you use that, you'll be welcome to test)
- [ ] GNOME 3
- [ ] Non-ubuntu Linux distro with Wayland based desktop
- [x] Ubuntu + Wayland <--- functional
- [x] Ubuntu + Wayland + Xwayland <--- functional, but less reliable than true X11 or full Wayland
- [x] Linux + X11 (Cinnamon desktop)
- [x] Linux + X11 (MATE desktop)
- [x] Windows 11 - using Msys2 + UCRT64
- [x] Windows 10 - using Msys2 + UCRT64
I posted a minimal test source code in a message below. Press B for borderless fullscreen, and F for hardware fullscreen.
:warning: If you have VirtualBox running at the same time on your computer, it is recommend to close it when testing. (i found that it could interfere with fullscreen mode on my computer. I don't know what cause that.)
Here is a list of the concerned issues :
- #3872
- #3972
- #4149
- #4145
thanks to @SoloByte and to @paulmelis for their help and efforts to solve this issue (but i'd need you to test again the new update of this PR)
Here are some notes (and TODO) before I forget :
Ubuntu + Wayland + GLFW_LINUX_ENABLE_X11=FALSE :
- [ ] in Wayland mode, the console is spammed by
WARNING : GLFW: Error: 65548 Description: Wayland: The platform does not provide the window positionif the application loop make use of a Raylib function that internally callsglfwGetWindowPos(). I don't know if it should be fixed, or left that way so the programmer is really aware that GLFW's Wayland support is incomplete. - [x]
GetCurrentMonitor()won't work because Wayland does not allow to know the position of a window ... - [X] in Wayland mode, every
glfwSetWindowPos()andglfwGetWindowPos()generate an error because "The platform does not support setting the window position". This error should be cleared byglGetError(NULL)to avoid future misinterpretation ofglGetError()calls. - [x] binary compiled with
GLFW_LINUX_ENABLE_X11=FALSEfails to init window because or GLFW erros : 65548 (wayland does not provide the window position or does not allow setting the position) - [X] test on real computer
Ubuntu + Wayland + GLFW_LINUX_ENABLE_X11=TRUE :
- [ ] despite the effects of
ToggleFullscreen()is the same asToggleBorderlessWindowed(), theGetMonitorWidth/Height()return a 640x480 resolution instead. I don't know if this is because of Ubuntu, because of my old laptop, or because something wrong in the code ... :-/ maybe i should just simplify the problem by callingToggleBorderlessWindowed()fromToggleFullscreen()on Wayland ? - [ ] in X11 compilation mode, toggling
ToggleBorderlessWindowed()many times fails to restore the position (the restored window move down each time), or shrink it each time if resizeable. - [x] check again compiling with
GLFW_LINUX_ENABLE_WAYLAND=FALSEand elucidate why it is enabled by default since GLFW's Wayland is still experimental, and since applications compiled for X11 work better on Ubuntu+Wayland than when compiled with wayland support. THat does not make sense. - [x] need clarifications regarding X11 / Wayland compatibility
- [X] try to run a binary compiled on X11 computer for X11, and see if it works on Ubuntu edit: it just work out of the box.
- [X] test on real computer
MacOS TODO :
- [ ] review changes made by @SoloByte and merge them once Wayland and MS Windows issues are fixed (just in case it change the pipeline)
- [ ] on MacOS, if user requests 1080p on a 4K display, MacOS will upscale the 1080p over the 4K resolution :
GetMonitorW/H()will return the 1080p reso, whileGetWindowScaleDPI()will return{ 2.0 , 2.0 }. From the hardware POV it is true and correct since MacOS upscale the 1080p over 4K with a DPI scale of x2, but from programmer's POV it is misleading because it could be interpreted as DPI scale x2 inside a 1080p resolution. - [ ] find a version of Xcode that still works under MacOS 10 so i can test it in VirtualBox
- [ ] keep in mind the implementation of
SetupViewport()might interfere in__APPLE__case
LM Cinnamon (X11) TODO :
- [ ] previous display resolution not restored from hardware fullscreeen on my 4K TV (which also has a firmware bug with low resolution) : edit some similar issues mentioned on the web with Unity and Cinnamon. edit2: tried Cinnamon in software rendering mode just to compare, and same problem.
- [x] WONTFIX :
GetWindowScaleDPI()returns{ 1.0 , 1.0 }when display is 75% and 100%, and returns{ 2.0 , 2.0 }when display is 125%, 150%, 175% and 200%. Must comes from GLFW or Cinnamon.
Windows 11 TODO :
- [ ] hardware fullscreen does not get focus when toggling from windowed window (HDPI off, CR off)
- [x] ~see if an alternative implementation of
_SetupFramebuffer()could help workaround the effect of the resizing of the window by Windows 11 when moving the window from a display with 125% dpi scale, to a display with 100% dpi scale~ edit : each version of Windows might have a slightly inconsistent behavior regarding HDPI flag and rescaling, because GLFW does not init the window with the same algorithm of DPI awareness : https://github.com/raysan5/raylib/blob/996f50393ec51fa33b361546986afc9762849a76/src/external/glfw/src/win32_init.c#L692C1-L697C30
"TODO" list
- [ ] once code stabilized, test if possible to go from strait from a full screen mode to another.
- [ ] check
EnableCursor()andDisableCursor()withFLAG_RESCALE_CONTENT - [ ] check
BeginScissorMode() - [ ] once stabilized, it might be relevant to create an example code to show and explain how and why replicate the effect of
GLFW_AUTO_ICONIFYcode-side in different setup (single monitor / multi monitor) etc etc - [ ] update
InitWindow()so it checks the value returned byInitPlatform()==> #4164 - [ ] make bi-linear interpolation optional when
FLAG_WINDOW_HIGHDPIis on ? - [ ] check
GetMousePosition()and gesture stuff - [ ] don't forget to allow to toggle to a resolution larger than the desktop size even if the size of the window is limited.
- [ ] check possible interferences with rendering to texture ? (need clarification on this
CORE.window.somethingFBO) - [ ] given that
InitWindow(0,0);was interpreted as an implicitFLAG_BORDERLESS_WINDOWEDrequest, we should discuss and formalize the expected behavior ofInitwindow( 800,0)andInitWindow(0,800)as it could be interpreted as the desire to force the width or height of the restored window or to set a window with the yet unknown width or height of desktop area .... - [x] review
CursorEnterCallback()so that it works correctly withFLAG_RESCALE_CONTENT - [x] i have no idea if Wayland also require the
SetMouseScale()on line 1540. - [x] force
GetWindowScaleDPI()to return{ 1.0 , 1.0 }in hardware fullscreen mode so that it is consistent on all OS / Desktop ? (FLAG_WINDOW_HIGHDPIhas no effect in hardware fullscreen mode anyway) - [x] depending on the OS, in HDPI mode, the value of
CORE.Input.Mouse.scaleandCORE.Input.Mouse.offsetmight be preset by the platform. The consequence is that the behavior ofSetMouseScale()andSetMouseOffset()won't be consistent. Maybe we should addCORE.Input.Mouse.userScaleandCORE.Input.Mouse.userOffsetinto the pipeline. - [x] check possible interferences with stereo-view / VR rendering example ?
- [x]
SetWindowMonitor()does not work in windowed fullscreen mode ? - [x] better monitor resolution choice into
IniWindow()andToggleFullscreen() - [x] desktop orignal display resolution not restored when toggling back from lower fullscreen resolution on Cinamon desktop (~Because i've done something stupid that i'm going to fix now~ I was wrongfully accused by myself, in fact, i was innocent)
- [x] review the issue between
FLAG_FULLSCREEN_MODEandFLAG_WINDOW_HIGHDPI - [x] review the "windowed fullscreen mode" triggered by
InitWindow(0,0) - [x] study the role of
CORE.Window.renderOffset - [x] study the role of
SetupFramebuffer()--> WTH is wrong with that func ? it does not use its own args ! - [x] SoloByte said : We should also test what happens when focus is lost while being in fullscreen/borderless fullscreen mode in general (like tabbing out to another window) and what happens when focus is gained again.
- [x] understand the purpose of
if (requestWindowedFullscreen) glfwWindowHint(GLFW_AUTO_ICONIFY, 0);inInitPlatform() - [x] raylib source code mention render content with black borders (need study)
- [x] mouse is not rescaled on Windows 10 hardware fullscreen mode (single monitor) and DPI 1.25 ? ==> So, in Windows, we have to unscale the mouse when toggling to hardware fullscreen mode, and scale the mouse when in windowed mode. On X11, the default is OK.
- [x] SoloByte said : Another problem here might be if high dpi flag is disabled (...) If GLFW_SCALE_FRAMEBUFFER defaults to false this should not be a problem but if it defaults to true it is a problem for MacOS and Wayland...
- [x] test when
SetWindowState(FLAG_FULLSCREEN_MODE)callsToggleFullscreenMode() - [x] test when
SetWindowState(FLAG_BORDERLESS_WINDOWED_MODE)callsToggleBorderlessWindowed() - [x] add a focus on hardware fullscreen window ?
- [x] see if the call to
WindowSizeCallback()insideToggleFullscreen()andToggleBorderlessWindow()is related to the glitch observed on the sluggish N4000 laptop when toggling exaggeratedly fast between hardware-fullscreen and windowed mode when DPI scale is set to 125% on the small 1024x768 display (might be a too long delay between the refresh and reorganization of the Window's taskbar and the updates of the GLFW+Raylib window) seems to have fixed the glitch - [x] verify if there is a warning or error message when testing on the N4000 laptop under Linux with DPI scale set to 200% and
FLAG_WHATEVER_HDPIis enabled (might be related to mesa and weak intel igp ?) - [x] if
FLAG_WINDOW_HIGHDPIis enabled, according to GLFW documentation, Wayland needsglfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE);which is an alias ofGLFW_COCOA_RETINA_FRAMEBUFFER - [X] Test and fix
MaximizeWindow()andMinimizeWindow() - [X] SoloByte said : This means that CORE.Window.screen.width & CORE.Window.screen.height will not be correct until the window resize callback will be called.
- [X] might want to update update the values of
CORE.window.screen.width/heightdirectly into ToggleFullscreen() like i did inToggleBorderlessWindowed()to solve #3929
"Memo" list
- in multi-monitor setups, if each display has a different DPI scaling, Windows 11 will resize any window (including Calc.exe) that is moved from a monitor to another despite
FLAG_WINDOW_HIGHDPIis off, and despite GLFW is instructed not to apply this bahavior. - wherever there is a
__APPLE__special case, it might also apply to Wayland using aif ( glfwGetPlatform() == GLFW_PLATFORM_WAYLAND), so while debugging Wayland support, it is relevant to look for__APPLE__too just in case. - :warning: : Wayland special cases should be handled at runtime instead of through C preprocessor, because the binary executable is compiled for Linux, not for Wayland or X11 :
if ( glfwGetPlatform() == GLFW_PLATFORM_WAYLAND)instead of#ifdef:warning: maybe i was wrong, since the wiki that i did not fully read mention a compile time option required for Wayland - better activates at least a tiny DPI scale in the desktop env when developing/testing, so it is possible to actually see the difference when
FLAG_WINDOW_HIGHDPIis ON/OFF and not get confused
- full screen mode and windowed full screen mode can be toggled multiple manners :
| hardware fullscreen mode | windowed fullscreen mode |
|---|---|
ToggleFullscreen() |
ToggleBorderlessWindowed() |
SetWindowState(FLAG_FULLSCREEN_MODE) |
SetWindowState(FLAG_BORDERLESS_WINDOWED_MODE) |
ClearWindowState(FLAG_FULLSCREEN_MODE) |
ClearWindowState(FLAG_BORDERLESS_WINDOWED_MODE) |
InitWindow( 0 , 0 ) |
|
SetConfigFlags(FLAG_FULLSCREEN_MODE); InitWindow( w ,h ); |
SetConfigFlags(FLAG_BORDERLESS_WINDOWED_MODE); InitWindow( w ,h ); |
"Can't reproduce. Fixed ?" list
- [ ] Paulmelis said : When the FLAG_WINDOW_HIGHDPI flag is not set the MATE menu and window bar at top and bottom respectively stay visible sometimes. And in other cases the raylib window fails to gain focus after switching to fullscreen with F.
"Can't fix ? / Known issues" list
- on Wayland : GLFW 3.4 does not yet support setting the position of the mouse cursor, so
SetMousePosition()won't work as expected. - on Ubuntu + Wayland : as it not possible to know the position of a window,
GetCurrentMonitor()will always return the last monitor that was associated to the window usingSetWindowMonitor(). If the user moves the window manually from one monitor to an other, this value won't be updated automatically. - on Ubuntu : there seems to be no real "hardware fullscreen mode".
ToggleFullscreen()seems to have the exact same effect thanToggleBorderlessWindowed(). Duno if it specific to my graphics drivers, or if it this way for all computers running Ubuntu, or if it is related to the fact that the frame rate is left toGLFW_DONT_CARE. - on Linux Mint + Cinnamon (X11) :
GetWindowScaleDPI()returns{ 1.0 , 1.0} when display is 75% and 100%, and returns { 2.0 , 2.0 } when display is 125%, 150%, 175% and 200%. Problem must comes from GLFW or Cinnamon. - on Linux Mint + Cinnamon (X11) : the display resolution is not restored from hardware fullscreen mode.
- each version of MS Windows might have a very slightly inconsistent behavior regarding
FLAG_WINDOW_HIGHDPIandFLAG_WINDOW_RESIZEABLE. This is because GLFW does not init the window with the same algorithm of DPI awareness : https://github.com/raysan5/raylib/blob/996f50393ec51fa33b361546986afc9762849a76/src/external/glfw/src/win32_init.c#L692C1-L697C30 - on MS Windows 11 : on multi monitor setup, if each monitor has a different DPI scale, and if the Raylib window is resizable, moving the window from one monitor to an other multiple times might shrink it a little. Same might happen if toggling from fullscreen mode many times.
ToggleFullscreen(): on sluggish computers, when toggling from a low resolution fullscreen mode (like 640x480) back to windowed mode, the previous position of the window might not be restored correctly because GLFW might try to restore the window size and position before the OS had time to fully restore the previous monitor resolution and the desktop UI (menu-bar). The consequence is that the size of the desktop might appear too small to GLFW to restore the window.ToggleFullscreen(): on my Linux X11, if VirtualBox is running, the Raylib's fullscreen window fails to keep the focus and is minimized.
Test source code :
#include <stdio.h>
#include "raylib.h"
#include "raymath.h"
void update();
void draw();
int main( int argc , char **argv )
{
// SetWindowState( FLAG_MSAA_4X_HINT );
SetConfigFlags(FLAG_WINDOW_HIGHDPI); // <======= ???
// SetConfigFlags(FLAG_FULLSCREEN_MODE);
// SetConfigFlags(FLAG_BORDERLESS_WINDOWED_MODE);
SetConfigFlags(FLAG_RESCALE_CONTENT); //!\ HIGHLIY EXPERIMENTAL FLAG disable it for backward compatible mode
// SetConfigFlags(FLAG_RESCALE_CONTENT_LINEAR); //!\ HIGHLIY EXPERIMENTAL FLAG disable it for backward compatible mode
// SetConfigFlags(FLAG_TEXT_LINEAR_FILTER);
SetConfigFlags( FLAG_WINDOW_RESIZABLE );
InitWindow( 700 , 500 , "Test" );
// InitWindow( 500 , 0 , "Test" ); // <== full screen windowed mode
// InitWindow( 0 , 500 , "Test" ); // <== full screen windowed mode
// InitWindow( 0 , 0 , "Test" ); // <== full screen windowed mode
if ( ! IsWindowReady() )
{
// Platform initialization probably failed
return -1;
}
// ClearWindowState( FLAG_VSYNC_HINT );
// SetMouseOffset( 10, 10);
// SetMouseScale( 0.5 , 0.5 );
SetTargetFPS( 60 );
while( ! WindowShouldClose() )
{
update();
BeginDrawing();
{
ClearBackground( GRAY );
draw();
}
EndDrawing();
}
CloseWindow();
}
void update()
{
if ( IsKeyPressed( KEY_ZERO ) )
{
SetWindowMonitor(0);
}
else
if ( IsKeyPressed( KEY_ONE ) )
{
SetWindowMonitor(1);
}
else
if ( IsKeyPressed( KEY_F ) )
{
// SetWindowSize( 1280 , 720 );
ToggleFullscreen();
printf("Toggled, screen now %d x %d\n", GetScreenWidth(), GetScreenHeight());
printf("Toggled, render now %d x %d\n", GetRenderWidth(), GetRenderHeight());
}
else
if ( IsKeyPressed( KEY_G ) )
{
SetWindowState(FLAG_FULLSCREEN_MODE);
}
else
if ( IsKeyPressed( KEY_B ) )
{
ToggleBorderlessWindowed();
printf("Toggled, screen now %d x %d\n", GetScreenWidth(), GetScreenHeight());
printf("Toggled, render now %d x %d\n", GetRenderWidth(), GetRenderHeight());
}
else
if ( IsKeyPressed( KEY_N ) )
{
SetWindowState(FLAG_BORDERLESS_WINDOWED_MODE);
}
else
if ( IsKeyPressed( KEY_C ) )
{
ClearWindowState(FLAG_FULLSCREEN_MODE);
ClearWindowState(FLAG_BORDERLESS_WINDOWED_MODE);
}
else
if ( IsKeyPressed( KEY_X ) )
{
MaximizeWindow();
}
else
if ( IsKeyPressed( KEY_V ) )//|| !IsWindowFocused() )
{
MinimizeWindow();
}
else
if ( IsKeyPressed( KEY_R ) )
{
RestoreWindow();
}
else
if ( IsKeyPressed( KEY_S ) )
{
if ( IsWindowState(FLAG_RESCALE_CONTENT) )
{
ClearWindowState(FLAG_RESCALE_CONTENT);
}
else
{
SetWindowState(FLAG_RESCALE_CONTENT);
}
}
else
if ( IsKeyPressed( KEY_T ) )
{
if ( IsWindowState(FLAG_TEXT_LINEAR_FILTER) )
{
ClearWindowState(FLAG_TEXT_LINEAR_FILTER);
}
else
{
SetWindowState(FLAG_TEXT_LINEAR_FILTER);
}
}
else
if ( IsKeyPressed( KEY_KP_5 ) ) { SetMousePosition( GetScreenWidth()/2 , GetScreenHeight()/2 ); }
else
if ( IsKeyPressed( KEY_KP_7 ) ) { SetMousePosition( 0 , 0 ); }
else
if ( IsKeyPressed( KEY_KP_9 ) ) { SetMousePosition( GetScreenWidth() , 0 ); }
else
if ( IsKeyPressed( KEY_KP_3 ) ) { SetMousePosition( GetScreenWidth() , GetScreenHeight() ); }
else
if ( IsKeyPressed( KEY_KP_1 ) ) { SetMousePosition( 0 , GetScreenHeight() ); }
}
void draw()
{
Vector2 mouse = GetMousePosition();
int sw = GetScreenWidth();
int sh = GetScreenHeight();
int rw = GetRenderWidth();
int rh = GetRenderHeight();
Vector2 dpi = GetWindowScaleDPI();
int monitor = GetCurrentMonitor();
int mw = GetMonitorWidth( monitor );
int mh = GetMonitorHeight( monitor );
Rectangle screenRect = { 0.0 , 0.0 , sw , sh };
Rectangle renderRect = { 0.0 , 0.0 , rw , rh };
// Draw the border of the screen :
DrawRectangleRec( screenRect , RAYWHITE );
DrawRectangleLinesEx( screenRect , 4.0f , RED );
DrawRectangleLinesEx( renderRect , 4.0f , GREEN );
// Draw the text NOT in the center :
int font_size = 20 ;
int y = 0 ;
DrawText( TextFormat( "Mouse : %.2f x %.2f / %d x %d" , mouse.x , mouse.y , GetMouseX(), GetMouseY() ) , 10 , y+=font_size , font_size , BLACK );
DrawText( TextFormat( "Screen : %d x %d" , sw , sh ) , 10 , y+=font_size , font_size , BROWN );
DrawText( TextFormat( "Render : %d x %d pixels" , rw , rh ) , 10 , y+=font_size , font_size , DARKGREEN );
DrawText( TextFormat( "Monitor[%d] : %d x %d pixels" , monitor , mw , mh ) , 10 , y+=font_size , font_size , DARKBLUE );
DrawText( TextFormat( "DPI : %f x %f" , dpi.x , dpi.y ) , 10 , y+=font_size , font_size , BLACK );
y+=font_size;
DrawText( TextFormat( "screenRect : %f x %f" , screenRect.width , screenRect.height ) , 10 , y+=font_size , font_size , RED );
DrawText( TextFormat( "renderRect : %f x %f" , renderRect.width , renderRect.height ) , 10 , y+=font_size , font_size , GREEN );
y+=font_size;
DrawText( IsWindowFullscreen() ? "FULLSCREEN MODE" : "" , 10 , y , font_size , BLUE );
DrawText( IsWindowState( FLAG_BORDERLESS_WINDOWED_MODE ) ? "BORDERLESS WINDOWED MODE" : "" , 10 , y , font_size , PINK );
DrawText( !IsWindowFullscreen() && !IsWindowState( FLAG_BORDERLESS_WINDOWED_MODE ) ? "Windowed mode" : "" , 10 , y , font_size , YELLOW );
y+=font_size;
DrawText( IsWindowFocused() ? "HAS FOCUS" : "does not have focus" , 10 , y+=font_size , font_size , ORANGE );
DrawText( IsWindowState(FLAG_RESCALE_CONTENT) ? "RESCALE CONTENT" : "does not rescale content" , 10 , y+=font_size , font_size , DARKPURPLE );
DrawText( IsCursorOnScreen() ? "CURSOR on SCREEN" : "cursor NOT on screen" , 10 , y+=font_size , font_size , DARKGREEN );
y+=font_size;
DrawText( "[F] ToggleFullscreen()" , 10 , y+=font_size , font_size , GRAY );
DrawText( "[G] SetWindowState(FLAG_FULLSCREEN_MODE);" , 10 , y+=font_size , font_size , GRAY );
y+=font_size;
DrawText( "[B] ToggleBorderlessWindowed()" , 10 , y+=font_size , font_size , GRAY );
DrawText( "[N] SetWindowState(FLAG_BORDERLESS_WINDOWED_MODE);" , 10 , y+=font_size , font_size , GRAY );
y+=font_size;
DrawText( "[C] ClearWindowState(FLAG_xxx_MODE);" , 10 , y+=font_size , font_size , GRAY );
y+=font_size;
DrawText( "[X] MaximizeWindow();" , 10 , y+=font_size , font_size , GRAY );
DrawText( "[V] MinimizeWindow();" , 10 , y+=font_size , font_size , GRAY );
DrawText( "[R] RestoreWindow();" , 10 , y+=font_size , font_size , GRAY );
y+=font_size;
DrawText( "[T] toggle `FLAG_TEXT_LINEAR_FILTER`" , 10 , y+=font_size , font_size , GRAY );
// Draw a software mouse cursor for debug purpose :
DrawCircleSector( mouse , 30, 45, 90, 1, WHITE);
DrawCircleSectorLines( mouse , 30, 45, 90, 1, BLACK);
// Draw a target :
DrawLine( GetMouseX() , 0 , GetMouseX() , sh , BLUE );
DrawLine( 0 , GetMouseY() , sw , GetMouseY() , BLUE );
// Center of screen :
DrawLine( sw/2-10 , sh/2, sw/2+10, sh/2, RED );
DrawLine( sw/2 , sh/2-10, sw/2, sh/2+10, RED );
}
@SuperUserNameMan thanks for all the work to fix all of this :)
When exiting fullscreen with ToggleFullscreen() function the position of the window is not set to previos position (like in borderless fullscreen) but instead it is set to CORE.Window.position.x, CORE.Window.position.y.
I don´t know if this matters.
#3929 maybe related as well?
Just tested your testing code above. The ToggleBorderlessWindowed() works fine, but the ToggleFullscreen() gives me a 640x480 (or sometimes 800x600) render, is that to be expected? My display is 1920x1080 (so also no high DPI), even though I tested with the flag set.
When the FLAG_WINDOW_HIGHDPI flag is not set the MATE menu and window bar at top and bottom respectively stay visible sometimes. And in other cases the raylib window fails to gain focus after switching to fullscreen with F.
Just tested your testing code above. The ToggleBorderlessWindowed() works fine, but the ToggleFullscreen() gives me a 640x480 (or sometimes 800x600) render, is that to be expected? My display is 1920x1080 (so also no high DPI), even though I tested with the flag set.
Yes, because ToggleFullscreen() asks an "hardware fullscreen".
The size of the render is set to the hardware resolution that is arbitrarily picked by GLFW to fit your current window's content dimensions. GLFW just tries to choose the "closest" hardware resolution supported by the monitor and the GPU. On your system, you get 640x480 or 800x600, but on some other computers, you might get something totally different.
For now, the only way to somehow influence the choice made by GLFW, is to resize your window to an hardware monitor resolution of your choice before calling ToggleFulscreen()
SetWindowSize( 1280 , 720 );
ToggleFullscreen();
1280x720 is an hardware resolution that has great chances to be supported on most monitor and graphics cards, so you'd have great chances that GLFW gives you this fullscreen resolution.
When the FLAG_WINDOW_HIGHDPI flag is not set the MATE menu and window bar at top and bottom respectively stay visible sometimes. And in other cases the raylib window fails to gain focus after switching to fullscreen with F.
Currently trying to find a computer with a Wayland desktop. So I add that to my TODO list with issues mentioned by @SoloByte
Currently trying to find a computer with a Wayland desktop. So I add that to my TODO list with issues mentioned by @SoloByte
Note that I'm not running wayland, but plain X server
Yes, because
ToggleFullscreen()asks an "hardware fullscreen".The size of the render is set to the hardware resolution that is arbitrarily picked by GLFW to fit your current window's content dimensions. GLFW just tries to choose the "closest" hardware resolution supported by the monitor and the GPU. On your system, you get 640x480 or 800x600, but on some other computers, you might get something totally different.
This shows another problem I think. Right now (even with your fixes) if you toggle the fullscreen with a screen size that is not supported by the monitor, glfw will pick the closest one and activate the fullscreen with the new size. This means that CORE.Window.screen.width & CORE.Window.screen.height will not be correct until the window resize callback will be called.
One solution to this could be to get all supported resolutions from the monitor and then:
- Check if current screen size is in this list, if it is we can proceed as normal
- If it is not find the closest resolution with the same aspect ratio
- If we find one pick this as new screen size and activate fullscreen
- if there is no resolution with the same aspect ratio pick the closest one and proceed
There is also a fundamental question about fullscreen mode. Should the fullscreen mode factor in the system dpi or not?
The only thing I found the high dpi flag does on window initialization is this.
If I can clear the high dpi flag and make fullscreen use the non-scaled resolution then this question is irrelevant.
Explanation
If I make a game and the user selects fullscreen mode with 4k resolution on a 4k monitor than I might want the screen size & render size to be the 4k and not scaled by the dpi. (Because my UI system might take care of scaling already)
If I make a game and the user selects fullscreen mode with 4k resolution on a 4k monitor than I might want the screen size & render size to be the 4k and not scaled by the dpi. (Because my UI system might take care of scaling already)
If i understood correctly, and if my previous tests were correct, the problem is that once we set this, we can't disable it unless we destroy and recreate the window (but we might lose the opengl context in the process if i understood correctly). That's why i was strongly claiming that the programmer should choose between FLAG_WHATEVER_HIGHDPI and ToggleFullscreen(), and should not use them together.
This shows another problem I think. Right now (even with your fixes) if you toggle the fullscreen with a screen size that is not supported by the monitor, glfw will pick the closest one and activate the fullscreen with the new size. This means that CORE.Window.screen.width & CORE.Window.screen.height will not be correct until the window resize callback will be called.
Yeah, that seems to cause some unexpected delays and lags with unexpected side-effects that i'm having some difficulties to grasp yet ...
There is also some unexpected behaviors on my LinuxMint + Cinamon desktop + 4K TV that i'm unable to fix or workaround currently ...
https://github.com/SuperUserNameMan/raylib/blob/7ddb880f020d31ea1732473a1bb4848a9c0566ee/src/platforms/rcore_desktop_glfw.c#L191
For unknown reasons, this does not restore the previous monitor/desktop resolution on my X11 Cinamon desktop. The TV remains at the fullscreen resolution it was just before.
edit: works fine with X11 MATE desktop.
There is also ancient code for "windowed fullscreen" that might require to be removed or fixed: https://github.com/SuperUserNameMan/raylib/blob/7ddb880f020d31ea1732473a1bb4848a9c0566ee/src/platforms/rcore_desktop_glfw.c#L1455-L1456
edit: actually, that is only when FLAG_WINDOW_HIGHDPI is enabled that it is buggy.
One solution to this could be to get all supported resolutions from the monitor and then:
InitPlatform() uses that approach for when FLAG_FULLSCREEN_MODE is set before InitWindow() :
https://github.com/SuperUserNameMan/raylib/blob/7ddb880f020d31ea1732473a1bb4848a9c0566ee/src/platforms/rcore_desktop_glfw.c#L1415C1-L1432C10
edit FLAG_FULLSCREEN_MODE needs to be fixed too, as the window is not fullscreenized once the video mode is changed on X11 MATE Linux.
The more i'm digging into the code, the more i have the feeling that i'm going to do the overhaul i did not want to do .........
I need to do a map from InitWindow() up to ToggleFullscreen().
@SoloByte :
If I make a game and the user selects fullscreen mode with 4k resolution on a 4k monitor than I might want the screen size & render size to be the 4k and not scaled by the dpi. (Because my UI system might take care of scaling already)
In that case, if your UI system already take care of DPI scaling (probably using GetWindowScaleDPI(), you don't need to activate FLAG_WINDOW_HIGHDPI. If you can manage DPI rescaling in fullscreen mode, you can manage it in windowed mode too. Same code i think, no ?
The more i'm digging into the code, the more i have the feeling that i'm going to do the overhaul i did not want to do
I hoped that your fix solved all the problems but in reality I was pretty sure an overhaul is needed because otherwise those problems would have already been fixed. Right now it just feels like a rabbit hole of endless despair we went down into and the only way to get out is to start from the beginning ;)
I need to do a map from
InitWindow()up toToggleFullscreen().
That is a really good idea! If I find the time I might also do something like this. Maybe for glfw as well how the window system is done in the docs.
Another problem here might be if high dpi flag is disabled:
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);is called to setGLFW_SCALE_TO_MONITORtofalsebutglfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE);is never called to do the same forGLFW_SCALE_FRAMEBUFFER
If GLFW_SCALE_FRAMEBUFFER defaults to false this should not be a problem but if it defaults to true it is a problem for MacOS and Wayland...
If you can manage DPI rescaling in fullscreen mode, you can manage it in windowed mode too. Same code i think, no ?
Yes absolutely. Right now I am just not sure if it is actually possible to disable high dpi or the scaling of the frame buffer when the window is on a high dpi monitor. (at least not possible for macOS & Wayland)
I will have to test it and I will report back on this. (with probably more questions than answers ...)
I don´t have a lot of time over the weekend, so if don´t respond it is because of that 🙃
InitPlatform()uses that approach for whenFLAG_FULLSCREEN_MODEis set beforeInitWindow():https://github.com/SuperUserNameMan/raylib/blob/7ddb880f020d31ea1732473a1bb4848a9c0566ee/src/platforms/rcore_desktop_glfw.c#L1415C1-L1432C10
Yes I know but it is the simplified version of just finding the closest resolution. (does not take aspect ratio into account) Doing this before setting Fullscreen is probably always a good idea. (in a potentially new system as well as the current system)
I did not want to touch rcore.c, but void SetupFramebuffer(int width, int height) just make no sense.
Am I hallucinating, or it does not even make use of its arguments ?
I have not published the changes yet, because it would break FLAG_WINDOW_HIGHDPI support again, but i think i've managed to fix SetupFramebuffer() and WindowSizeCallback().
edit : this change will be made optional using a new flag.
With these fixes, i think that the hardware fullscreen mode set by FLAG_FULLSCREEN_MODE and ToggleFullscreen() is working like it was intended to work, ie with the CORE.window.screen surface rescaled with the same aspect ratio into/by the CORE.window.render surface which is centered on the display surface with borders around.
Windowed mode with a 500x500 screen surface :
Fullscreen 800x600 mode, the 500x500 screen surface is upscaled and centered, with borders (they are grey, but their color can be select with the ClearBackground().
The behavior of ToggleBorderlessWindowed() on the other hand is not changed, so it keeps acting like a maximized window without decoration and a screen surface as large as the monitor resolution.
Am I hallucinating, or it does not even make use of its arguments ?
It does not use them at all but I guess if you have fixed the entire function, you have fixed this too ;)
This looks really great!
I have a few questions:
- [x] 1. In windowed mode the screen size & render size will always be the same? -> Yes
- [x] 2. In "true" fullscreen mode the render surface is scaled to the biggest size that fits into the monitor size while still retaining the aspect ratio of the screen size, correct? -> When
FLAG_RESCALE_CONTENTis on - [x] 3. What happens when you enabled the high dpi flag now? And what should happen ? -> Nothing on wayland & apple, otherwise screenSize = framebufferSize / scale
- [x] 4. Does your new fullscreen function have w/h parameters or how do you set the 800x600 in your example and the 500x500 screen size? -> Yes, desiredWidth/Height
OBSOLETE
This part here in InitPlatform() is weird...
We should find the closest supported video mode first and then calculate the other values.
For instance calculating the postion
CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
might use the wrong display width/height if it is changed here:
for (int i = 0; i < count; i++)
{
if ((unsigned int)modes[i].width >= CORE.Window.screen.width)
{
if ((unsigned int)modes[i].height >= CORE.Window.screen.height)
{
CORE.Window.display.width = modes[i].width;
CORE.Window.display.height = modes[i].height;
break;
}
}
}
Then I think we might want to set previous position/ size here as well (all this code is from the branch when we init a fullscreen window) to have sensible values for restoring to windowed?
But what are the sensible values for the window when initialized in fullscreen? We could say that the sensible values in this case would be half the monitor size & the center of the window. (we use this values only for previous size & position) in case the fullscreen is ever exited.
// Remember center for switching from fullscreen to window
if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
{
// If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed.
// Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11.
CORE.Window.position.x = CORE.Window.display.width/4;
CORE.Window.position.y = CORE.Window.display.height/4;
}
else
{
CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
}
if (CORE.Window.position.x < 0) CORE.Window.position.x = 0;
if (CORE.Window.position.y < 0) CORE.Window.position.y = 0;
This also allows for a bigger screen size than display size when calculating those values which would result in a negative position which results in the position beging (0,0) again... even though it causes problems?
Isn't this a pretty random position for fixing the pos 0,0 problem?
CORE.Window.position.x = CORE.Window.display.width/4;
CORE.Window.position.y = CORE.Window.display.height/4;
I think that the window position does not matter at all when entering fullscreen mode in glfw which means we can just use the "sensible" default values I mentioned above and also set the Core.Window.position.x/y to the center of the display. (Or am I wrong here?)
Is this still valid?
// If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed. // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11.
I won't answer all your questions tonight because i'm extremely tired, and also because i haven't updated this PR with most recent changes (that you can explore in this alternative branch : https://github.com/SuperUserNameMan/raylib/tree/2024-07-13-WIP-SetFrameBuffer-fix)
I haven't tested it on other OS than my Linux + X11 yet, because i'm still struggling to fix some details into InitPlatform(), so it is still very alpha.
The "rescaled and centered with border" full screen mode that you see in the screen captures are only enabled with a new FLAG_RESCALE_CONTENT.
I won't answer all your questions tonight because i'm extremely tired, and also because i haven't updated this PR with most recent changes
No worries, take your time 👍(and pls skip any questions that are already obsolete)
i haven't updated this PR with most recent changes (that you can explore in this alternative branch : https://github.com/SuperUserNameMan/raylib/tree/2024-07-13-WIP-SetFrameBuffer-fix)
Your changes look awesome and it is much cleaner than it was before! I haven't found anything while reading over it, just a question about the GLFW_AUTO_ICONIFY flag.
Why would we want it to be false in fullscreen mode?
Don't we want it to be true in fullscreen & borderless fullscreen mode and false in windowed mode?
I have tested your branch on macOS. I have tested it with a 4k monitor. (I will test the PR changes next.)
I have tested it with highDpi flag enabled & disabled. I have tested fullscreen, borderless fullscreen, maximized, windowed, macOS Fullscreen (green button) & restoring window
Notes:
- Fullscreen with high dpi flag enabled is blurry while fullscreen with high dpi flag disabled is clear. (even though they have the exact same values)
- When I launch your test application the window stays gray until I resize the window (resize directly, maximize, etc.)
High DPI Flag Disabled
- Everything works and I have found not any issues.
- Borderless Fullscreen, Fullscreen, and MacOS Fullscreen restore the window correctly.
- MacOS fullscreen leaves top bar intact (which is to be expected) and because of that has different values than borderless fullscreen
Here are some pictures of the different modes:
Windowed
Fullscreen
MacOs Fullscreen
Borderless Fullscreen
High DPI Flag Enabled
- I can't tell if Fullscreen works correctly ^^ (but it restores window correctly)
- Minimize, Maximize, MacOS fullscreen work correctly
- Borderless Fullscreen works correctly BUT does not restore window correctly!
Borderless Fullscreen values:
Windowed Mode
Screen 500x500 Render 1000x1000 Monitor 1920x1080 Dpi 2, 2
Activate Borderless Fullscreen Mode
Screen 1920x1080 Render 3840x2160 Monitor 1920x1080 DPI 2, 2
Deactivate Borderless Fullscreen Mode
Screen 1000x1000 Render 2000x2000 Monitor 1920x1080 Dpi 2, 2
Screen size increase by a factor of 2 everytime borderless fullscreen is activated and then deactivated. (500x500 -> 1000x1000->2000x2000 and so on)
Windowed
Fullscreen
MacOs Fullscreen
Borderless Fullscreen
Windowed after Borderless Fullscreen
I'm a little slow today ... foggy brain.
Why would we want it to be false in fullscreen mode? Don't we want it to be true in fullscreen & borderless fullscreen mode and false in windowed mode?
I haven't yet figured out why it was required. Added in the TODO.
- Does your new fullscreen function have w/h parameters or how do you set the 800x600 in your example and the 500x500 screen size? -> Yes, desiredWidth/Height
As you have noticed, there is an incoming static bool _ActivateFullscreenMode(int monitorIndex, int desiredWidth, int desiredHeight, int desiredRefreshRate); that will allow us to add a ToggleFullscreenEx( int desiredWhatever, etc)
Fullscreen with high dpi flag enabled is blurry while fullscreen with high dpi flag disabled is clear. (even though they have the exact same values)
Yes, it's because bi-linear interpolation is activated i don't remember where when FLAG_WINDOW_HIGHDPI is enabled.
If current API does not already allow us too control that, I might make that optional through a new FLAG_.
When I launch your test application the window stays gray until I resize the window (resize directly, maximize, etc.)
Yes that's one of the bug i was struggling with yesterday. Each time i fixed it, i had an another issue with an other window/fullscreen/flag mode ... It's like i was struggling with an unsolvable rubik's cube. Maybe i'll have more luck today :-S
edit there will be so much more features to test afterward ... like the stereo/VR rendering, render to texture, gesture scaling, etc .... X-(
edit there will be so much more features to test afterward ... like the stereo/VR rendering, render to texture, gesture scaling, etc .... X-(
It is more clear to me now why nobody wanted to touch it 🙃
From the glfw docs about GLFW_AUTO_ICONIFY
By default, the original video mode of the monitor will be restored and the window iconified if it loses input focus, to allow the user to switch back to the desktop. This behavior can be disabled with the
GLFW_AUTO_ICONIFYwindow hint, for example if you wish to simultaneously cover multiple monitors with full screen windows.
I think auto iconify is only relevant for true fullscreen mode and it should be set to true or left at the default state.
Is there a scenario where a raylib user wants to make an application/game that has GLFW_AUTO_ICONIFY set to false?
It would not restore the hardware resolution of the monitor and the fullscreen window would just stay fullscreen when tabbing out of it for instance.
I will try to test the auto iconify behaviour directly with glfw and see what happens when it is set to false.
Update
This hint is ignored for windowed mode windows!
I have tested the GLFW_AUTO_ICONIFY behaviour directly with glfw.
It does only affect windows that have a monitor set (either with CreateWindow() or SetWindowMonitor() functions) like fullscreen and borderless fullscreen. Losing focus of the window like tabbing out still works regardless of the GLFW_AUTO_ICONIFY flag.
Setting GLFW_AUTO_ICONIFY flag to false has some advantages! I think we should set this flag to false and do our own auto iconify routine.
Currently with GLFW_AUTO_ICONIFY set to true what happens is that the proper resolutions is restored when a fullscreen window loses focus but it is also minimized. The problem is when I tab out of a fullscreen window to another window I can no longer tab back into the fullscreen window because it is minimized (even in borderless fullscreen tabing back is not possible)
If GLFW_AUTO_ICONIFY is set to false then the window is never minimized when it loses focus so tabing around windows works fine but in true fullscreen mode the resolution is no longer restored.
So my idea would be to set this flag to false before the window is created. The window focus callback should tell us when the window loses focus and if it is in "true" fullscreen mode we restore the resolution of the monitor. We could add a flag that lets the user decide if they want the automatic minimization of the window.
GLFW_AUTO_ICONIFY False
Fullscreen
- Hardware resolution stays the same from what the fullscreen mode has set
- The window is not minimized
Borderless Fullscreen
- Hardware resolution stays the same from what the fullscreen mode has set but it is not an issues because by definition it uses the same resolution as the monitor anyway.
- The window is not minimized
GLFW_AUTO_ICONIFY True
Fullscreen
- Resolutions is restored
- The window is minimized
Borderless Fullscreen
- Resolutions is restored, irrelevant because by definition it uses the same resolution as the monitor anyway.
- The window is minimized
Back from a nap as new as the old SuperUserNameMan i was yesterday at the same hour.
Is there a scenario where a raylib user wants to make an application/game that has GLFW_AUTO_ICONIFY set to false?
Maybe for youtubers who stream gaming session in real time ? If they have dual-monitor, they could keep fullscreen on one monitor, and interact with their other streaming tools on the other monitor ? (i suspect they'd rather run the game in windowed mode though)
In a dual monitor scenario :
- It could also be useful for debugging purpose.
- or for game-dev purpose when you can change files and scripts in real time.
Setting GLFW_AUTO_ICONIFY flag to false has some advantages! I think we should set this flag to false and do our own auto iconify routine.
Yes, I agree. That's probably the best default choice and best philosophy : don't enforce the option that can't be workarounded programmer's side.
As glfwWindowHint() is set once for all before window creation, and as we allow to toggle between different fulls screen modes, we should default to GL_FALSE and let the programmer replicate the effect of auto-iconify by themself if they need that feature.
edit : it can be modified afterward using glfwSetWindowAttrib()
Plus, that's pretty easy to implement even for a beginner : if ( ! IsWindowFocused() ) MinimizeWindow();
So my idea would be to set this flag to false before the window is created. The window focus callback should tell us when the window loses focus and if it is in "true" fullscreen mode we restore the resolution of the monitor. We could add a flag that lets the user decide if they want the automatic minimization of the window.
Well, we have enough room for up to 32 FLAG_ in total. But i'm not sure how far we should go that way however.
Because we could over-complicate the life of the maintainers of other platform backends, and if an other platform backend does not implement this feature, we may over-complicate the life of the Raylib's user.
IMO, even the FLAG_RESCALE_CONTENT flag i've added in the other branch is already too much.
I have tested the GLFW_AUTO_ICONIFY behaviour directly with glfw.
I tested the two ON/OFF option on a dual monitor configuration.
And I agree with you : it's better OFF so we can keep the fullscreen visible on one monitor while doing stuff on the other monitor if we want to, and implement auto-iconify ourselves using a simple if ( ! IsWindowFocused() ) MinimizeWindow(); i we want that.
Oh wait ! it can be modified afterward using glfwSetWindowAttrib().
As
glfwWindowHint()is set once for all before window creation, and as we allow to toggle between different fulls screen modes, we should default toGL_FALSEand let the programmer replicate the effect of auto-iconify by themself if they need that feature.edit : it can be modified afterward using
glfwSetWindowAttrib()Plus, that's pretty easy to implement even for a beginner :
if ( ! IsWindowFocused() ) MinimizeWindow();
I tested the two ON/OFF option on a dual monitor configuration. And I agree with you : it's better OFF so we can keep the fullscreen visible on one monitor while doing stuff on the other monitor if we want to, and implement auto-iconify ourselves using a simple
if ( ! IsWindowFocused() ) MinimizeWindow();i we want that.
I agree. We definitely don't need the auto iconify (or auto minimize) functionality because it is easy to implement for the end user and it would be always wrong for a part of the community.
But the glfw auto iconfiy functionality also restores the hardware resolution of the monitor when a fullscreen monitor loses focus which does make sense for single monitor setups (and certain multi monitor setups as well). Should we replicate this or leave it to the user as well? It should be easy to implement as well:
if ( ! IsWindowFocused() && IsWindowFullscreen()) {
ToggleFullscreen(); //or RestoreWindow();
}
I have tested the code from this PR on macOS as well and it suffers from the same problem that everytime I exit borderless fullscreen or fullscreen the screen size doubles. (500x500 to 1000x1000 to 2000x2000 and so on) This only happens with the high dpi flag enabled. When the high dpi flag is enabled the dpi scale is 2x2 exactly.
Edit
I think using the render size to save the previous size might be the cause for this on macOS.