Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Support BORDERLESS_WINDOWED_MODE for non-exclusive fullscreen #3207

Closed
raysan5 opened this issue Jul 27, 2023 · 9 comments · Fixed by #3216
Closed

[core] Support BORDERLESS_WINDOWED_MODE for non-exclusive fullscreen #3207

raysan5 opened this issue Jul 27, 2023 · 9 comments · Fixed by #3216

Comments

@raysan5
Copy link
Owner

raysan5 commented Jul 27, 2023

Following conversation from closed issue #3198

Fullscreen mechanism does not work as expected in some cases.

raylib provides several mechanisms to toggle a fullscreen exclusive mode:

  • SetWindowState(FLAG_FULLSCREEN_MODE) to be called after InitWindow(), it actually setups the flag and calls the following function.
  • ToggleFullscreen() to be called after InitWindow(), it also setups the flag internally.

In both cases an exclusive fullscreen mode is requested, it means that the program tries to request the display to switch display-resolution to fit screen required resolution (the one provided to InitWindow() or the current screen/framebuffer resolution).
That resolution switch could be successful or not, depending on the display capabilities. raylib tries to obtain from display the closer resolution fitting the curren screen resolution, unfortunately, that's not always possible becaude depending on the monitor capabilities (like aspect ratio) only some resolutions are valid. For example, using a 800x450 screen resolution (16:9), when switching to exclusive fullscreen, some displays will accept it an others will switch to 800x600, giving unexpected results.

Nowadays, many games support what is called a borderless windowed fullscreen non-exclusive mode. It means, a window is created with the same size as the monitor current resolution and then the window-border is removed, giving the impression it's a fullscreen mode but actually just being another window over the other windows of the OS. The problem with that approach is that managing it adds an extra level of complexity that, maybe, should be managed by the user (instead of raylib).

When switching to borderless window mode, the display does not change resolution, it's the application that change screen size (framebuffer size) to match current monitor resolution, it has many implications: aspect-ratio, drawing, inputs, gameplay logic, physics... Consequently, the best approach is keeping a "logic/game resolution" (the resolution used for the game logic and render) and then scale that "screen" into the resolution of the full monitor ("output? resolution"). An example of that approach (but inside a window) is provided in raylib example core_window_letterbox. Note that screen-dependant inputs
(mouse/touch) must be scaled from the "output resolution" to the "game resolution".

Some possible references:

Still, thinking that maybe a BORDERLESS_WINDOWED_MODE should be managed from user side... but an example could be provided.

@ghost
Copy link

ghost commented Jul 28, 2023

@raysan5 Lets say the monitor size is 1024x768. And the screen/window size is 800x600. When it gets changed to BORDERLESS_WINDOWED_MODE which should happen?

  1. FLAG_WINDOW_UNDECORATED and FLAG_WINDOW_TOPMOST are applied; screen/window size is set to 1024x768; scale of the elements is not changed.

  2. Or FLAG_WINDOW_UNDECORATED and FLAG_WINDOW_TOPMOST are applied; screen/window size is set to 1024x768; scale of the elements is scaled up from 800x600 to match 1024x768.

Also, lets say the monitor has an aspect ratio of 16:9. And the screen/window has an aspect ratio of 4:3. Which should happen?

  1. Aspect ratio of the monitor is used.

  2. Or aspect ratio of the screen/window is used.

@raysan5
Copy link
Owner Author

raysan5 commented Jul 28, 2023

@ubkp Those are the key questions! No simple answer:

  • Easy solution: 1 and 3, it's up to the user to manage 2 and 4 at user side, an example would be required to illustrate it.
  • Complex solution: 2 and 4, managed internally by raylib, it would require logic/virtual resolutions and could imply many changes...

I think the best approach for now is the first one.

@ghost
Copy link

ghost commented Jul 28, 2023

@raysan5 Would it be acceptable that, instead of a BORDERLESS_WINDOWED_MODE flag, that could be allocated to a function that would receive two parameters (one scaling and one for the aspect ratio) so it's up for the user to choose?

I can see scenarios where all four combinations could be necessary depending on the program/game being made and/or user preference.

@raysan5
Copy link
Owner Author

raysan5 commented Jul 28, 2023

@ubkp But in those cases we are moving to the complex solution and users would expect raylib to manage internally the scaling and aspect ratio properly. I prefer to just scale the current framebuffer to current monitor resolution + borderless + topmost window and leave the scaling and aspect ratio management to user, similar to the core_window_letterbox example.

@ghost
Copy link

ghost commented Jul 28, 2023

@raysan5 Okay. I should have a PR ready during the weekend.

@ghost
Copy link

ghost commented Jul 31, 2023

@raysan5 Sorry the delay. I finished implementing BORDERLESS_WINDOWED_MODE but was having issues while testing on multi-monitor. Took me a bit to figure it out, but the problem was on the GetCurrentMonitor(). I'll open a separate issue about it. Should be a quick fix tho.

@ghost
Copy link

ghost commented Aug 7, 2023

@raysan5 Regarding scaling mechanisms, is there any specific reason to use scale (like core_window_letterbox.c L55, L93-L94) instead of setting CORE.Window.screenScale (L2275) directly? or with something like:

Vector2 GetScreenScale(void) {
    return (Vector2){ CORE.Window.screenScale.m0, CORE.Window.screenScale.m5 };
}

void SetScreenScale(float x, float y) {
    CORE.Window.screenScale = MatrixScale(x, y, CORE.Window.screenScale.m10);
}

@raysan5
Copy link
Owner Author

raysan5 commented Aug 7, 2023

@ubkp Several reasons, mostly design reasons:

  • Afair, CORE.Window.screenScale is only intended for main framebuffer scaling, not RenderTexture.
  • I try to minimize Get/Set functions in raylib, it would open a pandora-box if we want to make it consistent with all possible variables to be exposed.
  • I prefer to let the user manage it by themselfs, in case of specific needs.

@ghost
Copy link

ghost commented Aug 7, 2023

@raysan5 Got it! Thank you very much for the explanation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant