| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- /*
- Simple DirectMedia Layer
- Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
- */
- #include "SDL_internal.h"
- #ifdef SDL_VIDEO_DRIVER_EMSCRIPTEN
- #include "../SDL_sysvideo.h"
- #include "../SDL_pixels_c.h"
- #include "../../events/SDL_events_c.h"
- #include "SDL_emscriptenvideo.h"
- #include "SDL_emscriptenopengles.h"
- #include "SDL_emscriptenframebuffer.h"
- #include "SDL_emscriptenevents.h"
- #include "SDL_emscriptenmouse.h"
- #define EMSCRIPTENVID_DRIVER_NAME "emscripten"
- // Initialization/Query functions
- static bool Emscripten_VideoInit(SDL_VideoDevice *_this);
- static bool Emscripten_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
- static void Emscripten_VideoQuit(SDL_VideoDevice *_this);
- static bool Emscripten_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect);
- static bool Emscripten_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
- static void Emscripten_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window);
- static void Emscripten_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h);
- static void Emscripten_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
- static SDL_FullscreenResult Emscripten_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen);
- static void Emscripten_PumpEvents(SDL_VideoDevice *_this);
- static void Emscripten_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
- // Emscripten driver bootstrap functions
- static void Emscripten_DeleteDevice(SDL_VideoDevice *device)
- {
- SDL_free(device);
- }
- static SDL_SystemTheme Emscripten_GetSystemTheme(void)
- {
- /* Technically, light theme can mean explicit light theme or no preference.
- https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme#syntax */
- int theme_code = EM_ASM_INT({
- if (!window.matchMedia) {
- return -1;
- }
- if (window.matchMedia('(prefers-color-scheme: light)').matches) {
- return 0;
- }
- if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
- return 1;
- }
- return -1;
- });
- switch (theme_code) {
- case 0:
- return SDL_SYSTEM_THEME_LIGHT;
- case 1:
- return SDL_SYSTEM_THEME_DARK;
- default:
- return SDL_SYSTEM_THEME_UNKNOWN;
- }
- }
- static void Emscripten_ListenSystemTheme(void)
- {
- MAIN_THREAD_EM_ASM({
- if (window.matchMedia) {
- if (typeof(Module['SDL3']) === 'undefined') {
- Module['SDL3'] = {};
- }
- var SDL3 = Module['SDL3'];
- SDL3.eventHandlerThemeChanged = function(event) {
- _Emscripten_SendSystemThemeChangedEvent();
- };
- SDL3.themeChangedMatchMedia = window.matchMedia('(prefers-color-scheme: dark)');
- SDL3.themeChangedMatchMedia.addEventListener('change', SDL3.eventHandlerThemeChanged);
- }
- });
- }
- static void Emscripten_UnlistenSystemTheme(void)
- {
- MAIN_THREAD_EM_ASM({
- if (typeof(Module['SDL3']) !== 'undefined') {
- var SDL3 = Module['SDL3'];
- SDL3.themeChangedMatchMedia.removeEventListener('change', SDL3.eventHandlerThemeChanged);
- SDL3.themeChangedMatchMedia = undefined;
- SDL3.eventHandlerThemeChanged = undefined;
- }
- });
- }
- EMSCRIPTEN_KEEPALIVE void Emscripten_SendSystemThemeChangedEvent(void)
- {
- SDL_SetSystemTheme(Emscripten_GetSystemTheme());
- }
- static SDL_VideoDevice *Emscripten_CreateDevice(void)
- {
- SDL_VideoDevice *device;
- // Initialize all variables that we clean on shutdown
- device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice));
- if (!device) {
- return NULL;
- }
- /* Firefox sends blur event which would otherwise prevent full screen
- * when the user clicks to allow full screen.
- * See https://bugzilla.mozilla.org/show_bug.cgi?id=1144964
- */
- SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
- // Set the function pointers
- device->VideoInit = Emscripten_VideoInit;
- device->VideoQuit = Emscripten_VideoQuit;
- device->GetDisplayUsableBounds = Emscripten_GetDisplayUsableBounds;
- device->SetDisplayMode = Emscripten_SetDisplayMode;
- device->PumpEvents = Emscripten_PumpEvents;
- device->CreateSDLWindow = Emscripten_CreateWindow;
- device->SetWindowTitle = Emscripten_SetWindowTitle;
- /*device->SetWindowIcon = Emscripten_SetWindowIcon;
- device->SetWindowPosition = Emscripten_SetWindowPosition;*/
- device->SetWindowSize = Emscripten_SetWindowSize;
- /*device->ShowWindow = Emscripten_ShowWindow;
- device->HideWindow = Emscripten_HideWindow;
- device->RaiseWindow = Emscripten_RaiseWindow;
- device->MaximizeWindow = Emscripten_MaximizeWindow;
- device->MinimizeWindow = Emscripten_MinimizeWindow;
- device->RestoreWindow = Emscripten_RestoreWindow;
- device->SetWindowMouseGrab = Emscripten_SetWindowMouseGrab;*/
- device->GetWindowSizeInPixels = Emscripten_GetWindowSizeInPixels;
- device->DestroyWindow = Emscripten_DestroyWindow;
- device->SetWindowFullscreen = Emscripten_SetWindowFullscreen;
- device->CreateWindowFramebuffer = Emscripten_CreateWindowFramebuffer;
- device->UpdateWindowFramebuffer = Emscripten_UpdateWindowFramebuffer;
- device->DestroyWindowFramebuffer = Emscripten_DestroyWindowFramebuffer;
- device->GL_LoadLibrary = Emscripten_GLES_LoadLibrary;
- device->GL_GetProcAddress = Emscripten_GLES_GetProcAddress;
- device->GL_UnloadLibrary = Emscripten_GLES_UnloadLibrary;
- device->GL_CreateContext = Emscripten_GLES_CreateContext;
- device->GL_MakeCurrent = Emscripten_GLES_MakeCurrent;
- device->GL_SetSwapInterval = Emscripten_GLES_SetSwapInterval;
- device->GL_GetSwapInterval = Emscripten_GLES_GetSwapInterval;
- device->GL_SwapWindow = Emscripten_GLES_SwapWindow;
- device->GL_DestroyContext = Emscripten_GLES_DestroyContext;
- device->free = Emscripten_DeleteDevice;
- Emscripten_ListenSystemTheme();
- device->system_theme = Emscripten_GetSystemTheme();
- return device;
- }
- VideoBootStrap Emscripten_bootstrap = {
- EMSCRIPTENVID_DRIVER_NAME, "SDL emscripten video driver",
- Emscripten_CreateDevice,
- NULL // no ShowMessageBox implementation
- };
- bool Emscripten_VideoInit(SDL_VideoDevice *_this)
- {
- SDL_DisplayMode mode;
- // Use a fake 32-bpp desktop mode
- SDL_zero(mode);
- mode.format = SDL_PIXELFORMAT_XRGB8888;
- emscripten_get_screen_size(&mode.w, &mode.h);
- mode.pixel_density = emscripten_get_device_pixel_ratio();
- if (SDL_AddBasicVideoDisplay(&mode) == 0) {
- return false;
- }
- Emscripten_InitMouse();
- // Assume we have a mouse and keyboard
- SDL_AddKeyboard(SDL_DEFAULT_KEYBOARD_ID, NULL, false);
- SDL_AddMouse(SDL_DEFAULT_MOUSE_ID, NULL, false);
- // We're done!
- return true;
- }
- static bool Emscripten_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
- {
- // can't do this
- return true;
- }
- static void Emscripten_VideoQuit(SDL_VideoDevice *_this)
- {
- Emscripten_QuitMouse();
- Emscripten_UnlistenSystemTheme();
- }
- static bool Emscripten_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect)
- {
- if (rect) {
- rect->x = 0;
- rect->y = 0;
- rect->w = MAIN_THREAD_EM_ASM_INT({
- return window.innerWidth;
- });
- rect->h = MAIN_THREAD_EM_ASM_INT({
- return window.innerHeight;
- });
- }
- return true;
- }
- static void Emscripten_PumpEvents(SDL_VideoDevice *_this)
- {
- // do nothing.
- }
- static bool Emscripten_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
- {
- SDL_WindowData *wdata;
- double scaled_w, scaled_h;
- double css_w, css_h;
- const char *selector;
- // Allocate window internal data
- wdata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
- if (!wdata) {
- return false;
- }
- selector = SDL_GetHint(SDL_HINT_EMSCRIPTEN_CANVAS_SELECTOR);
- if (!selector) {
- selector = "#canvas";
- }
- wdata->canvas_id = SDL_strdup(selector);
- if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
- wdata->pixel_ratio = emscripten_get_device_pixel_ratio();
- } else {
- wdata->pixel_ratio = 1.0f;
- }
- scaled_w = SDL_floor(window->w * wdata->pixel_ratio);
- scaled_h = SDL_floor(window->h * wdata->pixel_ratio);
- // set a fake size to check if there is any CSS sizing the canvas
- emscripten_set_canvas_element_size(wdata->canvas_id, 1, 1);
- emscripten_get_element_css_size(wdata->canvas_id, &css_w, &css_h);
- wdata->external_size = SDL_floor(css_w) != 1 || SDL_floor(css_h) != 1;
- if ((window->flags & SDL_WINDOW_RESIZABLE) && wdata->external_size) {
- // external css has resized us
- scaled_w = css_w * wdata->pixel_ratio;
- scaled_h = css_h * wdata->pixel_ratio;
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, SDL_lroundf(css_w), SDL_lroundf(css_h));
- }
- emscripten_set_canvas_element_size(wdata->canvas_id, SDL_lroundf(scaled_w), SDL_lroundf(scaled_h));
- // if the size is not being controlled by css, we need to scale down for hidpi
- if (!wdata->external_size) {
- if (wdata->pixel_ratio != 1.0f) {
- // scale canvas down
- emscripten_set_element_css_size(wdata->canvas_id, window->w, window->h);
- }
- }
- wdata->window = window;
- // Setup driver data for this window
- window->internal = wdata;
- // One window, it always has focus
- SDL_SetMouseFocus(window);
- SDL_SetKeyboardFocus(window);
- Emscripten_RegisterEventHandlers(wdata);
- // Window has been successfully created
- return true;
- }
- static void Emscripten_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_WindowData *data;
- if (window->internal) {
- data = window->internal;
- // update pixel ratio
- if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
- data->pixel_ratio = emscripten_get_device_pixel_ratio();
- }
- emscripten_set_canvas_element_size(data->canvas_id, SDL_lroundf(window->floating.w * data->pixel_ratio), SDL_lroundf(window->floating.h * data->pixel_ratio));
- // scale canvas down
- if (!data->external_size && data->pixel_ratio != 1.0f) {
- emscripten_set_element_css_size(data->canvas_id, window->floating.w, window->floating.h);
- }
- SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->floating.w, window->floating.h);
- }
- }
- static void Emscripten_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h)
- {
- SDL_WindowData *data;
- if (window->internal) {
- data = window->internal;
- *w = SDL_lroundf(window->w * data->pixel_ratio);
- *h = SDL_lroundf(window->h * data->pixel_ratio);
- }
- }
- static void Emscripten_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
- {
- SDL_WindowData *data;
- if (window->internal) {
- data = window->internal;
- Emscripten_UnregisterEventHandlers(data);
- // We can't destroy the canvas, so resize it to zero instead
- emscripten_set_canvas_element_size(data->canvas_id, 0, 0);
- SDL_free(data->canvas_id);
- SDL_free(window->internal);
- window->internal = NULL;
- }
- }
- static SDL_FullscreenResult Emscripten_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen)
- {
- SDL_WindowData *data;
- int res = -1;
- if (window->internal) {
- data = window->internal;
- if (fullscreen) {
- EmscriptenFullscreenStrategy strategy;
- bool is_fullscreen_desktop = !window->fullscreen_exclusive;
- SDL_zero(strategy);
- strategy.scaleMode = is_fullscreen_desktop ? EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH : EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT;
- if (!is_fullscreen_desktop) {
- strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE;
- } else if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
- strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
- } else {
- strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
- }
- strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
- strategy.canvasResizedCallback = Emscripten_HandleCanvasResize;
- strategy.canvasResizedCallbackUserData = data;
- data->fullscreen_mode_flags = (window->flags & SDL_WINDOW_FULLSCREEN);
- data->fullscreen_resize = is_fullscreen_desktop;
- res = emscripten_request_fullscreen_strategy(data->canvas_id, 1, &strategy);
- } else {
- res = emscripten_exit_fullscreen();
- }
- }
- if (res == EMSCRIPTEN_RESULT_SUCCESS) {
- return SDL_FULLSCREEN_SUCCEEDED;
- } else if (res == EMSCRIPTEN_RESULT_DEFERRED) {
- return SDL_FULLSCREEN_PENDING;
- } else {
- return SDL_FULLSCREEN_FAILED;
- }
- }
- static void Emscripten_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
- {
- emscripten_set_window_title(window->title);
- }
- #endif // SDL_VIDEO_DRIVER_EMSCRIPTEN
|