[rcore] Add ToggleFullscreen() implementation for PLATFORM_WEB#3634
Conversation
|
@ubkp Thank you very much for the super-detailed review and documentation of this issue! Just let me some days to review it carefully. |
|
@ubkp Ok, just reviewed it. Again, thanks for the super-detailed review of all fullscreen modes available on Web, I worked on that long time ago but I couldn't come up with a proper solution, actually my focus was mostly on The proposed processes and assumptions look ok to me. About the known issues:
Isn't it possible to compare the
I think this is a minor detail, it's also highly dependant on users and I doubt many of them mix the toggle modes on their codebases. In that regards, I see many of the potential conflicts/issues depend on users mixing modes so I think it's ok to leave that good-practices work to them. If many users hit those constrains and complain, we can review it in the future. Thanks again for all the work put into this improvement. |
|
An additional note: All my review was based on provided code and example but not on actual "production code". I got several tools relying on it, if I see some unexpected issue with this update I will just report it. |
|
@raysan5 No problem, glad I could help. :)
We could use
I have a partial solution for it, but I'll start working on a full fix. 👍
Sure thing, if any issues arise, I'll promptly work on it! 👍 |
a. Preface
b. Context
raylibwithemscripten:I.
F11 fullscreenF11key.F11, bringing the whole page to fullscreen.F11 fullscreenwon't break the other "types" of "fullscreen". So the user can invoke theF11 fullscreenand then, after, any other "type" of "fullscreen" and that other "type" will work without a problem, albeit inside theF11 fullscreen"context"/"space".II.
HTML5 fullscreendocument.someElement.requestFullscreen().Emscripten fullscreen, although through different methods.III.
Emscripten fullscreenemscripten_request_fullscreen()oremscripten_request_fullscreen_strategy().HTML5 fullscreen, although through different methods.IV.
Emscripten soft fullscreenemscripten_enter_soft_fullscreen().emscripten_exit_soft_fullscreen()to leave and will expressly conflict with other "types" of "fullscreen" (speciallyEmscripten fullscreen).V.
Emscripten Module fullscreenModule.requestFullscreen().HTML5 fullscreenandEmscripten fullscreen, due to the inner "process" through which the "fullscreen" is created.c. Conclusion
F11 fullscreenis no problem and can co-exist without issues. We can just forget it's there.HTML5 fullscreenis insufficient. It's missing any sort of scale option (doc).Emscripten fullscreenis, at this moment, insufficient. It's missing an option to not scale the content but scale the canvas. I've tried all combinations (test_html5_fullscreen.cL154-L161) but none provided that.Emscripten soft fullscreenis not really an option for what we need.Emscripten Module fullscreenis not perfect (doesn't have the fine-grained scale options fromEmscripten fullscreen), but provides the minimal options we need.d. Changes
Since
HTML5 fullscreenandEmscripten fullscreendon't have the options we need right now, had to go with theEmscripten Module fullscreenroute.It's important to understand that there are 2 contexts for "fullscreen" for us:
One where we call it from inside
raylib.This we can track and control through
CORE.Window.flags,ToggleFullscreen(),ToggleBorderlessWindowed(),SetWindowState()andClearWindowState().And one where the user call it from outside
raylib.This we have no control of and we have limited capacity to track.
This happen, for example, when the user clicks the
Fullscreenbutton from theshell.htmlfile (L181).The problem of these 2 contexts is exactly not being able to track the one called from outside
raylib. Which causes theToggle*()and*WindowStates()to malfuction (e.g.: do nothing since it won't have the flags set or work inverted).To address that a new
platform.ourFullscreenbool variable was added (R76). When it'strueit means that "fullscreen" process is being operated byraylibcalls. When it'sfalseit means it wasn't us. We'll get in more detail later.To actually handle the "fullscreen" when
ToggleFullscreen()or*WindowStates()are called, we:First check if the document is already on "fullscreen" (R147-R148).
Note 1:
EM_ASM_INTwas used because we need to set flags insiderayliband outsideJS(R155-R157).Note 2:
document.fullscreenElementwas used because, under my testing, it appeared to be the most reliable way to check if we were on some "type" of "fullscreen".If it is, we leave whatever "type" of "fullscreen" we were in (R150).
Note:
EM_ASManddocument.exitFullscreen()were used because, under my testing, it appeared to be the most reliable way to leave whatever "type" of "fullscreen" was active.Since we left the whatever "type" of "fullscreen" we were in, we also reset the
raylibflags (R155-R157).But, before that (otherwise the flag would have been reseted), we check if we where on a (our)
FLAG_FULLSCREEN_MODE"fullscreen" (R152).If we were, it means this Toggle means leave fullscreen, so we stop there.
If we weren't, it means this Toggle is coming after some other "type" of "fullscreen" other than
FLAG_FULLSCREEN_MODE, so we will continue (R153) and proceed to enter a (our)FLAG_FULLSCREEN_MODE"fullscreen" (R161-R172).If we were not in any "type" of "fullscreen" to begin with, we continue to enter a (our)
FLAG_FULLSCREEN_MODE"fullscreen" (R159-R161).To enter (our)
FLAG_FULLSCREEN_MODE"fullscreen" we callModule.requestFullscreen(false, false);inside a shortsetTimeout(R164-R169) and them set the necessary flags (R170-R171).Note 1: the
Module.requestFullscreen();parameters arelockPointerthat we keep asfalseandresizeCanvasthat we keep asfalseto achieve the same type of fullscreen from the other platforms.Note 2: the
setTimeoutwas necessary to handle the browser mode change delay.Side-note: the old notes were kept as a reference in case it's needed in the future (R174-R238).
After this "fullscreen" call is done, it should trigger a "fullscreen change" event and the
EmscriptenFullscreenChangeCallback()(R1440).But, to allow that, the
emscripten_set_fullscreenchange_callbackhad to be changed toEMSCRIPTEN_EVENT_TARGET_WINDOWinstead of#canvas(L1139-R1178), because#canvaswon't be reliably tracked.Here is when
platform.ourFullscreen(that is set totrueright from the start ofToggleFullscreenR144) becomes important:If
platform.ourFullscreenistrue, we already cleaned and set the flags ourselves insideToggleFullscreen()(R155-R157, R170-R171), as needed. So all we need to do is reset theplatform.ourFullscreentofalse(R1444).However, if
platform.ourFullscreenisfalse, then this "fullscreen change" event if coming from outsideraylib.If it's coming from outside
rayliband is not on "fullscreen" (R1447-R1448), it means the user just left some "type" of "fullscreen" (e.g.: pressed theEsckey). So the assume we're no longer in some "type" of "fullscreen" and reset ourraylibflags to match that non-fullscreen state (R1450-R1452).Note: this will handle the item 4 issue.
Basically the same process happen for
ToggleBorderlessWindowed(). The only difference being the variable names, the respective flags and entering the "borderless windowed" "fullscreen" (R242):For that, we use
Module.requestFullscreen(false, true);instead, wheretruemeansresizeCanvas, achieving theFLAG_BORDERLESS_WINDOWED_MODE"fullscreen" (R268).We also need to use
canvas.style.width="unset";to handle the possibility of awidth="value%"like on the defaultshell.htmlfile (R271).There the
setTimeoutsare also used to handle the browser mode change delay (R266-R269).e. Known issues
With all this handling
ToggleFullscreen()andToggleBorderlessWindowed()and the*WindowState()become "hot swappable", meaning that the user can jump fromFLAG_FULLSCREEN_MODE"fullscreen" toFLAG_BORDERLESS_WINDOWED_MODE"fullscreen" with just a singleToggleBorderlessWindowed()orSetWindowState(FLAG_BORDERLESS_WINDOWED_MODE);call. The same being true for the opposite (fromFLAG_BORDERLESS_WINDOWED_MODEtoFLAG_FULLSCREEN_MODE).There's one limitation tho: at the moment, I wasn't able to find a way to parse the
Module.requestFullscreen()resizeCanvasparameter issued outsideraylib. Getting that information would allow to find which "type" of "fullscreen" was issued from outside. And that would allow to better toggle it out.E.g.: if the user press the
fullscreenbutton on theshell.htmlfile, the canvas will go fullscreen as expected (that's outsideraylib). Then if the user usesToggleFullscreen()it will leave the fullscreen and then reenters the "fullscreen" (that's insideraylib). From there, everything works as expected. But, if we had that information, we could instead just leave the fullscreen shortcutting it.It's a minor issue, but if we ever figure out how to get that info, we can handle that exactly at R1453.Note: found a way to handle it using the following code inside
ToggleFullscreen():const int cw = EM_ASM_INT( { return document.canvas.width; }, 0);const int csw = EM_ASM_INT( { return parseInt(document.canvas.style.width); }, 0);if (cw < csw) enterFullscreen = false;However, it causes
SetWindowState()to break on that case. IMHO, this is such a minor issue that doesn't justify the overengineering, so better just leave it as is.IMPORTANT NOTE: @raysan5 I just realized that the other platforms don't have these toggles operating in "hot swap". Please let me know if I should revert this here so it matches the other platforms behavior for parity and consistency.
As mentioned previously,
Emscripten Module fullscreenconflicts withHTML5 fullscreenandEmscripten fullscreen:If the user enters "fullscreen" with
ToggleFullscreen()orSetWindowState(), then leaves, then enters "fullscreen" withHTML5 fullscreen(e.g.: callingdocument.getElementById("canvas").requestFullscreen()) this will "break" the page content (but not the canvas, which will continue to work). By "break" I mean, for some reason, thatdocument.getElementById("canvas").requestFullscreen()call will, at that moment, cause the<canvas>to replace<body>.The same will happen if the user enters "fullscreen" with
HTML5 fullscreen(e.g.:document.getElementById("canvas").requestFullscreen()), then pressEscto leave, then enter "fullscreen" withToggleFullscreen(), then leaves, then enter "fullscreen" again withHTML5 fullscreen(e.g.:document.getElementById("canvas").requestFullscreen()).Unfortunately I have no solution for this yet. But as long both methods are not mixed, everything should work fine.
f. Outro
Nevertheless, I think this implementation covers most use cases and works for both a
minshell.html-style or ashell.html-style shell files.Also, hopefully this PR documents enough detail to help for context or future updates of "fullscreen" for
PLATFORM_WEB.g. Notes
Emscripten fullscreeneventually gets updated to provide an option to not scale the content but scale the canvas, we likely could replace theModuleEM_ASMcalls with something like:h. Code example
PLATFORM_WEB(requiresASYNCIFY) with:i. Environment
Linux(Ubuntu 22.04 64-bit) and tested onFirefox(115.3.1esr 64-bit) andChromium(117.0.5938.149 64-bit) for bothminshell.htmlandshell.htmlfiles.j. Edits