Handling Fullscreen and Orientation Changes in SDL2 and Emscripten
My past few days got rather side-tracked getting in the weeds on how the SDL2 port in Emscripten deals with toggling in and out of fullscreen mode, and how it deals with orientation changes in the mobile web environment. It was a frustrating experience, so I thought I'd summarize my experience in case anyone out there is attempting the same thing.
I had window resizing and a fullscreen toggle working just fine a few days ago... on a desktop browser. But this doesn't automatically translate to everything working fine on a mobile browser environment.
Sample Projects for Fullscreen and Orientation Changes
I spent today going back to the basics. Over on my CMake Testing Grounds repository, you'll find a new folder named sdl2-emsdk-fullscreen-tests. This contains two projects:
- The
no-dprfolder ignores Device Pixel Ratio, and allows the user to toggle into fullscreen mode against the canvas. - The
with-dprfolder respects Device Pixel Ratio, and uses a "soft" fullscreen approach in the browser.
Both projects:
- Display a debug grid every 100 pixels.
- Display red markers in the four corners.
- Display the most recent resize or mouse button event in five anchored locations.
- Display the current canvas (or window) size in four anchored locations in blue.
- Have a filled rectangle in the upper-right corner; clicking on this toggles in and out of fullscreen.
Notes About The SDL2 Emscripten Port
I found that things were pretty spotty when searching for good information specifically about the behaviour of the mobile web environment. Emscripten has an entire section on screen orientation and fullscreen mode on their html5.h reference page, but it comes from a documentation standpoint -- not necessarily a developer or user experience standpoint. SDL2 has a "state of things" readme related to Emscripten, but it's more of a build guide.
What I found is that the SDL2 Emscripten port wants you to think about the various forms of window resize events in one of two ways:
- Let SDL2 and Emscripten take complete control of all forms of window resizing.
- You take complete control of all forms of window resizing yourself.
You have to choose one or the other, or you might end up writing code somewhere that actively fights what the SDL2 Emscripten port is doing behind the scenes. Once that started happening in my sample project, it became a losing battle that just kept getting more frustrating.
The choice seems to come down to the flags argument in SDL_CreateWindow:
- Turn on
SDL_WINDOW_RESIZABLEif you want the SDL2 Emscripten port to take control. This is the path of least resistance, as long as you are fine with giving up all control. Don't try to change the canvas size, don't try to callSDL_SetWindowSize, and don't register any resize-related event handlers that mess with canvas or window size. - Leave
SDL_WINDOW_RESIZABLEoff if you want to take control yourself. Here, you have to deal with updating the canvas size, callingSDL_SetWindowSizeto ensure the backing buffer stays in sync, and registering resize event handlers as necessary.
Symptoms I Encountered
Here are some of the things I recall happening:
- Toggling into fullscreen caused all rendering to become "scrunched" in one corner.
- Mouse / touch events started registering as if they were in an "old" co-ordinate system (e.g. portrait when I was actually in landsape, or vice-versa).
- The canvas screen would turn completely blank.
In general, I think what's happening if you start to experience any of these symptoms is the SDL backing buffer has gone out of sync with the canvas.
I found that the biggest trouble came when I wanted to respect the device pixel ratio (DPR). As near as I can figure, if you let the SDL2 Emscripten port take control of all resize handling, it won't respect DPR. It's possible I'm missing something, but that seemed to be what I was experiencing. The smartphone I was testing on has a DPR of 2.5. The display is 1080p, but if a game doesn't handle DPR it will only have 432 pixels to work with.
I suspect that a lot of these issues have to do with the way different mobile devices handle fullscreen toggling and orientation changes. They will often animate the change, and events can trigger before the browser finishes all of its layout changes. So Emscripten probably has to deal with race conditions and varying behaviour across devices.
Failsafes
I settled on two potential failsafes:
- Use soft fullscreen, meaning that toggling into fullscreen mode just hides all HTML elements besides the canvas.
- On any mouse button (or touch) event, detect if the canvas size (as reported via
emscripten_get_canvas_element_size) has diverged from the window size (as reported viaSDL_GetWindowSize). If so, and the user is currently in fullscreen mode, forcibly toggle back out of fullscreen mode.
Version 1.1 of the CMake + SDL2 + Emscripten Sample Project
For about a day or so, the version 1.1 sample that I had posted was not playing well with mobile devices. It should be in a much better state now. Though I do apologize for how small the fullscreen toggle icon is on mobile devices. 😅