raylib icon indicating copy to clipboard operation
raylib copied to clipboard

[rcore][glfw] Window System Improvements

Open SoloByte opened this issue 1 year ago • 13 comments

I have spent the past 4 weeks trying to fix problems (on macOS & Windows) related to the windowing system in the glfw backend. I can come up with 6 points right now that should/could be addressed:

  • [ ] A. Changing/Fixing Borderless Fullscreen Mode to how it is described in the glfw documentation. It is easy to do and does not change a lot. It fixes Borderless Fullscreen on macOS. (I have a branch for it and tested it)
  • [ ] B. Fixing Fullscreen mode. It works already but there is a few small things. (I have a branch for it and tested it)
  • [ ] C. Enhancing Fullscreen mode. Currently the window size has to be used to set the fullscreen resolution. This means that raylib can not restore the window to the correct size after exiting fullscreen because it will always restore to the window size that was used to set  the fullscreen resolution. I would propose a SetDesiredFullscreenResolution(int width, int height)  function. This function can be used to set the desired fullscreen size. The CORE.WINDOW.desiredFullscreenSize would default to 0,0 and in case it is 0,0 the CORE.Window.screen.size would be used in ToggleFullscreen() just like now. This way everything works the same as before but additionally the fullscreen resolution can be changed without touching the window size and raylib can restore the window size correctly.
  • [ ] D. GLFW documentation states that the WindowSizeCallback() should NOT be used to set glViewport(). Right now the WindowSizeCallback() calls SetupViewport() → rlViewport() → glViewport() . The framebuffer size instead of the window size should be used instead. This is easy to fix and the FramebufferSizeCallback()of glfw can be added for that.
  • [ ] E. Adding a function for getting all supported video modes of a monitor. This is important for fullscreen.  Right now you can only guess as a user of raylib what fullscreen resolutions glfw would accept. If a non supported resolution is used glfw will chose one making it hard to know what happens. This function for example would help a lot if an options menus would want to list all supported fullscreen resolutions for the user the choose from.
  • [ ] F. Remove/disable the high dpi flag for now (see below)

I would like to discuss each of those points to know which one I can implement and submit a PR for. I can also at any time create a branch and implement something as a basis for discussing it or to simply showcase it. I will then mark each point or make it ~~strikethrough~~ based on if it was accepted or not. 

I would also like to keep this issue relative compact and to the point, so I would open a new issue for any of the points above that need to be discussed further.

I will make a comment below for each point to be able to give it a 👍 or a 👎.

The points above are ordered from most important to least important (based on my opinion and how easy they are to implement). 

@raysan5

  • I can close my other window system related issues to not bloat the issue section.
  • I can also remove any points right away that you don't want implemented/added.

GLFW Documentation

Docs

WindowSizeCallback vs FramebufferSizeCallback

Do not pass the window size to glViewport or other pixel-based OpenGL calls. The window size is in screen coordinates, not pixels. Use the framebuffer size, which is in pixels, for pixel-based calls.

Borderless Fullscreen

If the closest match for the desired video mode is the current one, the video mode will not be changed, making window creation faster and application switching much smoother. This is sometimes called windowed full screen or borderless full screen window and counts as a full screen window. To create such a window, request the current video mode.

const GLFWvidmode* mode = glfwGetVideoMode(monitor);
glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);

Removing/Disabling HIGH Dpi Flag

NOTE 1: I am fine with keeping the High DPI functionality and I would be happy to help anyone who would be willing to tackle the High DPI mode and I would also be happy to help in testing on macOS & Windows. 

NOTE 2: By disabling it I mean making sure the flag can not be set to true (with a Tracelog Info/Warning).

After looking at window system in raylib for the last 4 weeks and trying many different things to fix everything I have come to the conclusion that in my opinion the High DPI flag does more harm than good.

Currently the high dpi flag does not work on at least macOS & windows as soon as a high dpi monitor is used. There are multiple issues with resizing the window, borderless fullscreen, fullscreen and restoring the window after exiting borderless fullscreen and fullscreen.

In short it does not work correctly and introduces bugs and complicates things a lot. 

To complicate it further all of this is only a problem when setting the system scale to something else than 100%. So if someone develops an app/game on a 1920x1080 monitor thinking it will work on a 4k monitor as well (with the high dpi flag enabled) will have a bad surprise. Basically anyone working on a non high dpi monitor does not need the high dpi flag and anyone working on a high dpi monitor cant use the high dpi flag.

On macOS it does not make sense to be able to disable high dpi anyway because it is always on and handled by the operation system.

On windows the high dpi flag could work but there is always something that does not work correctly. 

Because of the vast amount of different setups with 3 major platforms, different system settings, different monitors and monitor behaviors it would be a lot of work and extra code (and extra complexity) to make the high dpi flag work consistently and correctly across this huge spectrum of possibilities. (not to think of the other backends as well) 

Additionally using relative values for the UI instead of absolute (hardcoded) values makes the high dpi mode obsolete already. 

Because of all of this I would propose to remove the high dpi flag altogether. (it already should be removed for macOS and Wayland)

  • It would remove complexity (of the already complex) window system
  • it would make maintaining the code much easier
  • it would get rid of a lot of bugs and problems
  • in the high dpi world a lot of things can change and raylib would need to keep up to date with it. (Windows could start handling it different, GLFW could change something, and so on)
  • In my opinion raylib does not have to take care of everything. By design it is kept simple and to the point and I think the high dpi system does not fit into it

SoloByte avatar Aug 06 '24 10:08 SoloByte

A

Changing/Fixing Borderless Fullscreen Mode to how it is described in the glfw documentation. It is easy to do and does not change a lot. It fixes Borderless Fullscreen on macOS. (I have a branch for it and tested it)

SoloByte avatar Aug 06 '24 10:08 SoloByte

B

Fixing Fullscreen mode. It works already but there is a few small things. (I have a branch for it and tested it)

SoloByte avatar Aug 06 '24 10:08 SoloByte

C

Enhancing Fullscreen mode. Currently the window size has to be used to set the fullscreen resolution. This means that raylib can not restore the window to the correct size after exiting fullscreen because it will always restore to the window size that was used to set  the fullscreen resolution. I would propose a SetDesiredFullscreenResolution(int width, int height)  function. This function can be used to set the desired fullscreen size. The CORE.WINDOW.desiredFullscreenSize would default to 0,0 and in case it is 0,0 the CORE.Window.screen.size would be used in ToggleFullscreen() just like now. This way everything works the same as before but additionally the fullscreen resolution can be changed without touching the window size and raylib can restore the window size correctly.

SoloByte avatar Aug 06 '24 10:08 SoloByte

D

GLFW documentation states that the WindowSizeCallback() should NOT be used to set glViewport(). Right now the WindowSizeCallback() calls SetupViewport() → rlViewport() → glViewport() . The framebuffer size instead of the window size should be used instead. This is easy to fix and the FramebufferSizeCallback()of glfw can be added for that.

SoloByte avatar Aug 06 '24 10:08 SoloByte

E

Adding a function for getting all supported video modes of a monitor. This is important for fullscreen.  Right now you can only guess as a user of raylib what fullscreen resolutions glfw would accept. If a non supported resolution is used glfw will chose one making it hard to know what happens. This function for example would help a lot if an options menus would want to list all supported fullscreen resolutions for the user the choose from.

SoloByte avatar Aug 06 '24 10:08 SoloByte

F

Remove/disable the high dpi flag for now

SoloByte avatar Aug 06 '24 10:08 SoloByte

Regarding point C: Is there a reason to store the desired width and height inside raylib rather then passing them directly to something like ToggleFullscreenEx(int width, int height) ? And if we are going to have a function to retrieve display resolutions (I assume it includes refresh rates too), we might as well have ToggleFullscreenEx(int width, int height, int refreshRate) ?

veins1 avatar Aug 06 '24 18:08 veins1

@asdqwe It's fine, GLFW sets it to the closest valid mode.

veins1 avatar Aug 07 '24 00:08 veins1

Regarding point C: Is there a reason to store the desired width and height inside raylib rather then passing them directly to something like ToggleFullscreenEx(int width, int height) ? And if we are going to have a function to retrieve display resolutions (I assume it includes refresh rates too), we might as well have ToggleFullscreenEx(int width, int height, int refreshRate) ?

This would be my preferred method but slightly different. (see below)

Edit: You can not use ToggleFullscreenEx(int width, int height, int refreshRate) because the toggle part does not really work with parameters. You would have to use width, height, refreshrate even if fullscreen would be toggled off. So you would have to add SetFullscreen(int width, int height, int refreshRate) instead and RestoreWindow() would toggle fullscreen off.

I suggested my version because it would keep raylib "intact" without breaking anything and would add a minimal amount of extra things.

SoloByte avatar Aug 09 '24 09:08 SoloByte

I can close my other window system related issues to not bloat the issue section.

@SoloByte Yes, please, I'm a bit overwhelmed with so many window-related issues and discussions. I have not enough time to review everything properly...

raysan5 avatar Aug 24 '24 18:08 raysan5

May I know how far this has come, its such an interesting topic!

CodingMadness avatar Oct 29 '24 08:10 CodingMadness

@CodingMadness the was a closed PR because the amount of changes was too big to manage. Strategy should probably be reviewed and move to user side HighDPI management as much as possible.

raysan5 avatar Oct 29 '24 08:10 raysan5

Any progress on this topic?

CodingMadness avatar Jan 28 '25 01:01 CodingMadness

While I can get an idea of the complexities involved in windowing systems, it can be hard to believe that a library with the popularity of raylib still has issues with this, being such foundational features in modern games (like borderless full screen, it works well but it covers other windows?) The efforts of this PR have apparently gone nowhere which is unfortunate

brunodeangelis avatar Aug 08 '25 03:08 brunodeangelis

Unfortunately it is very hard to find a common solution that works for everyone and can be merged without creating to much work for maintainers.

Basically we would have to agree what the problems are, then what raylib should handle and what the users of raylib should handle and finally how the new system should look like and work. Because windowing is so fundamental, you would have to change / rewrite the entire system to get it all to work.

Additionally the different backends do not make it much easier because they all bring their own quirks, bugs, etc into to the mix.

I would like to have it fixed properly as a whole (as in rewriting is completely) so windowing works the same regardless of the platform. But to find someone to design the new system, then implement it and implement it in a way that the maintainers are happy with, is just not easy.

SoloByte avatar Aug 08 '25 11:08 SoloByte

The efforts of this PR have apparently gone nowhere which is unfortunate

Yes, because we basically just tried to fix it without really thinking about the people who have to maintain it. Especially, because it was not a rewrite that can make stuff cleaner and easier. Instead workarounds were added until most things worked ^^

SoloByte avatar Aug 08 '25 11:08 SoloByte