SDL_emscriptenvideo.c
Go to the documentation of this file.
/*
Simple DirectMedia Layer
Copyright (C) 1997-2019 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"
#if SDL_VIDEO_DRIVER_EMSCRIPTEN
#include "SDL_video.h"
#include "SDL_mouse.h"
#include "SDL_hints.h"
#include "../SDL_sysvideo.h"
#include "../SDL_pixels_c.h"
#include "../SDL_egl_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 int Emscripten_VideoInit(_THIS);
static int Emscripten_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);
static void Emscripten_VideoQuit(_THIS);
static int Emscripten_CreateWindow(_THIS, SDL_Window * window);
static void Emscripten_SetWindowSize(_THIS, SDL_Window * window);
static void Emscripten_DestroyWindow(_THIS, SDL_Window * window);
static void Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
static void Emscripten_PumpEvents(_THIS);
static void Emscripten_SetWindowTitle(_THIS, SDL_Window * window);
/* Emscripten driver bootstrap functions */
static int
Emscripten_Available(void)
{
return (1);
}
static void
Emscripten_DeleteDevice(SDL_VideoDevice * device)
{
SDL_free(device);
}
static SDL_VideoDevice *
Emscripten_CreateDevice(int devindex)
{
SDL_VideoDevice *device;
/* Initialize all variables that we clean on shutdown */
device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
if (!device) {
SDL_OutOfMemory();
return (0);
}
/* 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->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->SetWindowGrab = Emscripten_SetWindowGrab;*/
device->DestroyWindow = Emscripten_DestroyWindow;
device->SetWindowFullscreen = Emscripten_SetWindowFullscreen;
device->CreateWindowFramebuffer = Emscripten_CreateWindowFramebuffer;
device->UpdateWindowFramebuffer = Emscripten_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = Emscripten_DestroyWindowFramebuffer;
#if SDL_VIDEO_OPENGL_EGL
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_DeleteContext = Emscripten_GLES_DeleteContext;
device->GL_GetDrawableSize = Emscripten_GLES_GetDrawableSize;
#endif
device->free = Emscripten_DeleteDevice;
return device;
}
VideoBootStrap Emscripten_bootstrap = {
EMSCRIPTENVID_DRIVER_NAME, "SDL emscripten video driver",
Emscripten_Available, Emscripten_CreateDevice
};
int
Emscripten_VideoInit(_THIS)
{
SDL_DisplayMode mode;
/* Use a fake 32-bpp desktop mode */
mode.format = SDL_PIXELFORMAT_RGB888;
mode.w = EM_ASM_INT_V({
return screen.width;
});
mode.h = EM_ASM_INT_V({
return screen.height;
});
mode.refresh_rate = 0;
mode.driverdata = NULL;
if (SDL_AddBasicVideoDisplay(&mode) < 0) {
return -1;
}
SDL_AddDisplayMode(&_this->displays[0], &mode);
Emscripten_InitMouse();
/* We're done! */
return 0;
}
static int
Emscripten_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
{
/* can't do this */
return 0;
}
static void
Emscripten_VideoQuit(_THIS)
{
Emscripten_FiniMouse();
}
static void
Emscripten_PumpEvents(_THIS)
{
/* do nothing. */
}
static int
Emscripten_CreateWindow(_THIS, SDL_Window * window)
{
SDL_WindowData *wdata;
double scaled_w, scaled_h;
double css_w, css_h;
/* Allocate window internal data */
wdata = (SDL_WindowData *) SDL_calloc(1, sizeof(SDL_WindowData));
if (wdata == NULL) {
return SDL_OutOfMemory();
}
wdata->canvas_id = SDL_strdup("#canvas");
if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
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_WINDOWEVENT_RESIZED, css_w, css_h);
}
emscripten_set_canvas_element_size(wdata->canvas_id, scaled_w, 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);
}
}
#if SDL_VIDEO_OPENGL_EGL
if (window->flags & SDL_WINDOW_OPENGL) {
if (!_this->egl_data) {
if (SDL_GL_LoadLibrary(NULL) < 0) {
return -1;
}
}
wdata->egl_surface = SDL_EGL_CreateSurface(_this, 0);
if (wdata->egl_surface == EGL_NO_SURFACE) {
return SDL_SetError("Could not create GLES window surface");
}
}
#endif
wdata->window = window;
/* Setup driver data for this window */
window->driverdata = wdata;
/* One window, it always has focus */
SDL_SetMouseFocus(window);
SDL_SetKeyboardFocus(window);
Emscripten_RegisterEventHandlers(wdata);
/* Window has been successfully created */
return 0;
}
static void Emscripten_SetWindowSize(_THIS, SDL_Window * window)
{
SDL_WindowData *data;
if (window->driverdata) {
data = (SDL_WindowData *) window->driverdata;
/* update pixel ratio */
if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
data->pixel_ratio = emscripten_get_device_pixel_ratio();
}
emscripten_set_canvas_element_size(data->canvas_id, window->w * data->pixel_ratio, window->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->w, window->h);
}
}
}
static void
Emscripten_DestroyWindow(_THIS, SDL_Window * window)
{
SDL_WindowData *data;
if(window->driverdata) {
data = (SDL_WindowData *) window->driverdata;
Emscripten_UnregisterEventHandlers(data);
#if SDL_VIDEO_OPENGL_EGL
if (data->egl_surface != EGL_NO_SURFACE) {
SDL_EGL_DestroySurface(_this, data->egl_surface);
data->egl_surface = EGL_NO_SURFACE;
}
#endif
/* 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->driverdata);
window->driverdata = NULL;
}
}
static void
Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
{
SDL_WindowData *data;
if(window->driverdata) {
data = (SDL_WindowData *) window->driverdata;
if(fullscreen) {
EmscriptenFullscreenStrategy strategy;
SDL_bool is_desktop_fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
int res;
strategy.scaleMode = is_desktop_fullscreen ? EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH : EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT;
if(!is_desktop_fullscreen) {
strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE;
} else if(window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
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->requested_fullscreen_mode = window->flags & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
data->fullscreen_resize = is_desktop_fullscreen;
res = emscripten_request_fullscreen_strategy(data->canvas_id, 1, &strategy);
if(res != EMSCRIPTEN_RESULT_SUCCESS && res != EMSCRIPTEN_RESULT_DEFERRED) {
/* unset flags, fullscreen failed */
window->flags &= ~(SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
}
}
else
emscripten_exit_fullscreen();
}
}
static void
Emscripten_SetWindowTitle(_THIS, SDL_Window * window) {
EM_ASM_INT({
if (typeof Module['setWindowTitle'] !== 'undefined') {
Module['setWindowTitle'](UTF8ToString($0));
}
return 0;
}, window->title);
}
#endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
/* vi: set ts=4 sw=4 expandtab: */
Emscripten_DestroyWindowFramebuffer
void Emscripten_DestroyWindowFramebuffer(_THIS, SDL_Window *window)
SDL_WINDOW_ALLOW_HIGHDPI
@ SDL_WINDOW_ALLOW_HIGHDPI
Definition: SDL_video.h:113
screen
SDL_Renderer * screen
Definition: testgamecontroller.c:64
Emscripten_RegisterEventHandlers
void Emscripten_RegisterEventHandlers(SDL_WindowData *data)
Emscripten_InitMouse
void Emscripten_InitMouse()
Emscripten_FiniMouse
void Emscripten_FiniMouse()
SDL_mouse.h
SDL_PIXELFORMAT_RGB888
@ SDL_PIXELFORMAT_RGB888
Definition: SDL_pixels.h:236
NULL
#define NULL
Definition: begin_code.h:167
SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS
#define SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS
Minimize your SDL_Window if it loses key focus when in fullscreen mode. Defaults to true.
Definition: SDL_hints.h:332
mode
GLenum mode
Definition: SDL_opengl_glext.h:1122
SDL_emscriptenevents.h
SDL_WindowData
Definition: SDL_androidwindow.h:38
SDL_WINDOW_FULLSCREEN
@ SDL_WINDOW_FULLSCREEN
Definition: SDL_video.h:100
SDL_WINDOW_OPENGL
@ SDL_WINDOW_OPENGL
Definition: SDL_video.h:101
SDL_WINDOWEVENT_RESIZED
@ SDL_WINDOWEVENT_RESIZED
Definition: SDL_video.h:155
Emscripten_bootstrap
VideoBootStrap Emscripten_bootstrap
SDL_WINDOW_FULLSCREEN_DESKTOP
@ SDL_WINDOW_FULLSCREEN_DESKTOP
Definition: SDL_video.h:111
SDL_floor
#define SDL_floor
Definition: SDL_dynapi_overrides.h:431
SDL_GL_LoadLibrary
#define SDL_GL_LoadLibrary
Definition: SDL_dynapi_overrides.h:553
SDL_SetKeyboardFocus
void SDL_SetKeyboardFocus(SDL_Window *window)
Definition: SDL_keyboard.c:630
SDL_emscriptenopengles.h
SDL_emscriptenvideo.h
SDL_SetMouseFocus
void SDL_SetMouseFocus(SDL_Window *window)
Definition: SDL_mouse.c:211
SDL_WINDOW_RESIZABLE
@ SDL_WINDOW_RESIZABLE
Definition: SDL_video.h:105
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
SDL_Window
The type used to identify a window.
Definition: SDL_sysvideo.h:73
SDL_DisplayMode
The structure that defines a display mode.
Definition: SDL_video.h:53
SDL_AddDisplayMode
SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode)
Definition: SDL_video.c:751
_this
static SDL_VideoDevice * _this
Definition: SDL_video.c:118
Emscripten_CreateWindowFramebuffer
int Emscripten_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, void **pixels, int *pitch)
window
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
EGL_NO_SURFACE
#define EGL_NO_SURFACE
Definition: egl.h:100
SDL_emscriptenmouse.h
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
Emscripten_HandleCanvasResize
EM_BOOL Emscripten_HandleCanvasResize(int eventType, const void *reserved, void *userData)
SDL_VideoDevice::displays
SDL_VideoDisplay * displays
Definition: SDL_sysvideo.h:316
SDL_WindowData::canvas_id
char * canvas_id
Definition: SDL_emscriptenvideo.h:43
_THIS
#define _THIS
Definition: SDL_alsa_audio.h:31
SDL_WindowData::window
SDL_Window * window
Definition: SDL_cocoawindow.h:114
SDL_VideoDevice
Definition: SDL_sysvideo.h:148
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
Emscripten_UnregisterEventHandlers
void Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
SDL_WindowData::pixel_ratio
float pixel_ratio
Definition: SDL_emscriptenvideo.h:45
SDL_calloc
#define SDL_calloc
Definition: SDL_dynapi_overrides.h:375
SDL_AddBasicVideoDisplay
int SDL_AddBasicVideoDisplay(const SDL_DisplayMode *desktop_mode)
Definition: SDL_video.c:589
SDL_VideoDisplay
Definition: SDL_sysvideo.h:125
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
SDL_hints.h
SDL_SetHint
#define SDL_SetHint
Definition: SDL_dynapi_overrides.h:190
SDL_SendWindowEvent
int SDL_SendWindowEvent(SDL_Window *window, Uint8 windowevent, int data1, int data2)
Definition: SDL_windowevents.c:74
Emscripten_UpdateWindowFramebuffer
int Emscripten_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *rects, int numrects)
SDL_strdup
#define SDL_strdup
Definition: SDL_dynapi_overrides.h:397
SDL_video.h
SDL_emscriptenframebuffer.h
res
GLuint res
Definition: SDL_opengl_glext.h:7937
VideoBootStrap
Definition: SDL_sysvideo.h:397
device
static SDL_AudioDeviceID device
Definition: loopwave.c:37
SDL_WindowData::egl_surface
EGLSurface egl_surface
Definition: SDL_androidwindow.h:40
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:161
SDL_WindowData::external_size
SDL_bool external_size
Definition: SDL_emscriptenvideo.h:47