SDL  2.0
SDL_emscriptenvideo.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
4 
5  This software is provided 'as-is', without any express or implied
6  warranty. In no event will the authors be held liable for any damages
7  arising from the use of this software.
8 
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #if SDL_VIDEO_DRIVER_EMSCRIPTEN
24 
25 #include "SDL_video.h"
26 #include "SDL_mouse.h"
27 #include "SDL_hints.h"
28 #include "../SDL_sysvideo.h"
29 #include "../SDL_pixels_c.h"
30 #include "../SDL_egl_c.h"
31 #include "../../events/SDL_events_c.h"
32 
33 #include "SDL_emscriptenvideo.h"
34 #include "SDL_emscriptenopengles.h"
36 #include "SDL_emscriptenevents.h"
37 #include "SDL_emscriptenmouse.h"
38 
39 #define EMSCRIPTENVID_DRIVER_NAME "emscripten"
40 
41 /* Initialization/Query functions */
42 static int Emscripten_VideoInit(_THIS);
43 static int Emscripten_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);
44 static void Emscripten_VideoQuit(_THIS);
45 
46 static int Emscripten_CreateWindow(_THIS, SDL_Window * window);
47 static void Emscripten_SetWindowSize(_THIS, SDL_Window * window);
48 static void Emscripten_DestroyWindow(_THIS, SDL_Window * window);
49 static void Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
50 static void Emscripten_PumpEvents(_THIS);
51 static void Emscripten_SetWindowTitle(_THIS, SDL_Window * window);
52 
53 
54 /* Emscripten driver bootstrap functions */
55 
56 static int
57 Emscripten_Available(void)
58 {
59  return (1);
60 }
61 
62 static void
63 Emscripten_DeleteDevice(SDL_VideoDevice * device)
64 {
66 }
67 
68 static SDL_VideoDevice *
69 Emscripten_CreateDevice(int devindex)
70 {
72 
73  /* Initialize all variables that we clean on shutdown */
75  if (!device) {
77  return (0);
78  }
79 
80  /* Firefox sends blur event which would otherwise prevent full screen
81  * when the user clicks to allow full screen.
82  * See https://bugzilla.mozilla.org/show_bug.cgi?id=1144964
83  */
85 
86  /* Set the function pointers */
87  device->VideoInit = Emscripten_VideoInit;
88  device->VideoQuit = Emscripten_VideoQuit;
89  device->SetDisplayMode = Emscripten_SetDisplayMode;
90 
91 
92  device->PumpEvents = Emscripten_PumpEvents;
93 
94  device->CreateSDLWindow = Emscripten_CreateWindow;
95  device->SetWindowTitle = Emscripten_SetWindowTitle;
96  /*device->SetWindowIcon = Emscripten_SetWindowIcon;
97  device->SetWindowPosition = Emscripten_SetWindowPosition;*/
98  device->SetWindowSize = Emscripten_SetWindowSize;
99  /*device->ShowWindow = Emscripten_ShowWindow;
100  device->HideWindow = Emscripten_HideWindow;
101  device->RaiseWindow = Emscripten_RaiseWindow;
102  device->MaximizeWindow = Emscripten_MaximizeWindow;
103  device->MinimizeWindow = Emscripten_MinimizeWindow;
104  device->RestoreWindow = Emscripten_RestoreWindow;
105  device->SetWindowGrab = Emscripten_SetWindowGrab;*/
106  device->DestroyWindow = Emscripten_DestroyWindow;
107  device->SetWindowFullscreen = Emscripten_SetWindowFullscreen;
108 
109  device->CreateWindowFramebuffer = Emscripten_CreateWindowFramebuffer;
110  device->UpdateWindowFramebuffer = Emscripten_UpdateWindowFramebuffer;
111  device->DestroyWindowFramebuffer = Emscripten_DestroyWindowFramebuffer;
112 
113 #if SDL_VIDEO_OPENGL_EGL
114  device->GL_LoadLibrary = Emscripten_GLES_LoadLibrary;
115  device->GL_GetProcAddress = Emscripten_GLES_GetProcAddress;
116  device->GL_UnloadLibrary = Emscripten_GLES_UnloadLibrary;
117  device->GL_CreateContext = Emscripten_GLES_CreateContext;
118  device->GL_MakeCurrent = Emscripten_GLES_MakeCurrent;
119  device->GL_SetSwapInterval = Emscripten_GLES_SetSwapInterval;
120  device->GL_GetSwapInterval = Emscripten_GLES_GetSwapInterval;
121  device->GL_SwapWindow = Emscripten_GLES_SwapWindow;
122  device->GL_DeleteContext = Emscripten_GLES_DeleteContext;
123  device->GL_GetDrawableSize = Emscripten_GLES_GetDrawableSize;
124 #endif
125 
126  device->free = Emscripten_DeleteDevice;
127 
128  return device;
129 }
130 
132  EMSCRIPTENVID_DRIVER_NAME, "SDL emscripten video driver",
133  Emscripten_Available, Emscripten_CreateDevice
134 };
135 
136 
137 int
138 Emscripten_VideoInit(_THIS)
139 {
141 
142  /* Use a fake 32-bpp desktop mode */
143  mode.format = SDL_PIXELFORMAT_RGB888;
144 
145  mode.w = EM_ASM_INT_V({
146  return screen.width;
147  });
148 
149  mode.h = EM_ASM_INT_V({
150  return screen.height;
151  });
152 
153  mode.refresh_rate = 0;
154  mode.driverdata = NULL;
155  if (SDL_AddBasicVideoDisplay(&mode) < 0) {
156  return -1;
157  }
158 
160 
162 
163  /* We're done! */
164  return 0;
165 }
166 
167 static int
168 Emscripten_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
169 {
170  /* can't do this */
171  return 0;
172 }
173 
174 static void
175 Emscripten_VideoQuit(_THIS)
176 {
178 }
179 
180 static void
181 Emscripten_PumpEvents(_THIS)
182 {
183  /* do nothing. */
184 }
185 
186 static int
187 Emscripten_CreateWindow(_THIS, SDL_Window * window)
188 {
189  SDL_WindowData *wdata;
190  double scaled_w, scaled_h;
191  double css_w, css_h;
192 
193  /* Allocate window internal data */
194  wdata = (SDL_WindowData *) SDL_calloc(1, sizeof(SDL_WindowData));
195  if (wdata == NULL) {
196  return SDL_OutOfMemory();
197  }
198 
199  wdata->canvas_id = SDL_strdup("#canvas");
200 
201  if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
202  wdata->pixel_ratio = emscripten_get_device_pixel_ratio();
203  } else {
204  wdata->pixel_ratio = 1.0f;
205  }
206 
207  scaled_w = SDL_floor(window->w * wdata->pixel_ratio);
208  scaled_h = SDL_floor(window->h * wdata->pixel_ratio);
209 
210  /* set a fake size to check if there is any CSS sizing the canvas */
211  emscripten_set_canvas_element_size(wdata->canvas_id, 1, 1);
212  emscripten_get_element_css_size(wdata->canvas_id, &css_w, &css_h);
213 
214  wdata->external_size = SDL_floor(css_w) != 1 || SDL_floor(css_h) != 1;
215 
216  if ((window->flags & SDL_WINDOW_RESIZABLE) && wdata->external_size) {
217  /* external css has resized us */
218  scaled_w = css_w * wdata->pixel_ratio;
219  scaled_h = css_h * wdata->pixel_ratio;
220 
222  }
223  emscripten_set_canvas_element_size(wdata->canvas_id, scaled_w, scaled_h);
224 
225  /* if the size is not being controlled by css, we need to scale down for hidpi */
226  if (!wdata->external_size) {
227  if (wdata->pixel_ratio != 1.0f) {
228  /*scale canvas down*/
229  emscripten_set_element_css_size(wdata->canvas_id, window->w, window->h);
230  }
231  }
232 
233 #if SDL_VIDEO_OPENGL_EGL
234  if (window->flags & SDL_WINDOW_OPENGL) {
235  if (!_this->egl_data) {
236  if (SDL_GL_LoadLibrary(NULL) < 0) {
237  return -1;
238  }
239  }
240  wdata->egl_surface = SDL_EGL_CreateSurface(_this, 0);
241 
242  if (wdata->egl_surface == EGL_NO_SURFACE) {
243  return SDL_SetError("Could not create GLES window surface");
244  }
245  }
246 #endif
247 
248  wdata->window = window;
249 
250  /* Setup driver data for this window */
251  window->driverdata = wdata;
252 
253  /* One window, it always has focus */
256 
258 
259  /* Window has been successfully created */
260  return 0;
261 }
262 
263 static void Emscripten_SetWindowSize(_THIS, SDL_Window * window)
264 {
266 
267  if (window->driverdata) {
268  data = (SDL_WindowData *) window->driverdata;
269  /* update pixel ratio */
270  if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
271  data->pixel_ratio = emscripten_get_device_pixel_ratio();
272  }
273  emscripten_set_canvas_element_size(data->canvas_id, window->w * data->pixel_ratio, window->h * data->pixel_ratio);
274 
275  /*scale canvas down*/
276  if (!data->external_size && data->pixel_ratio != 1.0f) {
277  emscripten_set_element_css_size(data->canvas_id, window->w, window->h);
278  }
279  }
280 }
281 
282 static void
283 Emscripten_DestroyWindow(_THIS, SDL_Window * window)
284 {
286 
287  if(window->driverdata) {
288  data = (SDL_WindowData *) window->driverdata;
289 
291 #if SDL_VIDEO_OPENGL_EGL
292  if (data->egl_surface != EGL_NO_SURFACE) {
293  SDL_EGL_DestroySurface(_this, data->egl_surface);
294  data->egl_surface = EGL_NO_SURFACE;
295  }
296 #endif
297 
298  /* We can't destroy the canvas, so resize it to zero instead */
299  emscripten_set_canvas_element_size(data->canvas_id, 0, 0);
300  SDL_free(data->canvas_id);
301 
302  SDL_free(window->driverdata);
303  window->driverdata = NULL;
304  }
305 }
306 
307 static void
308 Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
309 {
311  if(window->driverdata) {
312  data = (SDL_WindowData *) window->driverdata;
313 
314  if(fullscreen) {
315  EmscriptenFullscreenStrategy strategy;
316  SDL_bool is_desktop_fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
317  int res;
318 
319  strategy.scaleMode = is_desktop_fullscreen ? EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH : EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT;
320 
321  if(!is_desktop_fullscreen) {
322  strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE;
323  } else if(window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
324  strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
325  } else {
326  strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
327  }
328 
329  strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
330 
331  strategy.canvasResizedCallback = Emscripten_HandleCanvasResize;
332  strategy.canvasResizedCallbackUserData = data;
333 
334  data->requested_fullscreen_mode = window->flags & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
335  data->fullscreen_resize = is_desktop_fullscreen;
336 
337  res = emscripten_request_fullscreen_strategy(data->canvas_id, 1, &strategy);
338  if(res != EMSCRIPTEN_RESULT_SUCCESS && res != EMSCRIPTEN_RESULT_DEFERRED) {
339  /* unset flags, fullscreen failed */
341  }
342  }
343  else
344  emscripten_exit_fullscreen();
345  }
346 }
347 
348 static void
349 Emscripten_SetWindowTitle(_THIS, SDL_Window * window) {
350  EM_ASM_INT({
351  if (typeof Module['setWindowTitle'] !== 'undefined') {
352  Module['setWindowTitle'](UTF8ToString($0));
353  }
354  return 0;
355  }, window->title);
356 }
357 
358 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
359 
360 /* 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