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 {
65  SDL_free(device);
66 }
67 
68 static SDL_VideoDevice *
69 Emscripten_CreateDevice(int devindex)
70 {
72 
73  /* Initialize all variables that we clean on shutdown */
74  device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
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 
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 */
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 
159  SDL_AddDisplayMode(&_this->displays[0], &mode);
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 
221  SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, css_w, css_h);
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 */
254  SDL_SetMouseFocus(window);
255  SDL_SetKeyboardFocus(window);
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 
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: */
void SDL_SetKeyboardFocus(SDL_Window *window)
Definition: SDL_keyboard.c:630
EM_BOOL Emscripten_HandleCanvasResize(int eventType, const void *reserved, void *userData)
#define EGL_NO_SURFACE
Definition: egl.h:100
void Emscripten_FiniMouse()
void(* free)(_THIS)
Definition: SDL_sysvideo.h:394
#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
int SDL_AddBasicVideoDisplay(const SDL_DisplayMode *desktop_mode)
Definition: SDL_video.c:589
int(* GL_SetSwapInterval)(_THIS, int interval)
Definition: SDL_sysvideo.h:261
The structure that defines a display mode.
Definition: SDL_video.h:53
void(* SetWindowSize)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:216
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
int SDL_SendWindowEvent(SDL_Window *window, Uint8 windowevent, int data1, int data2)
SDL_Window * window
VideoBootStrap Emscripten_bootstrap
void SDL_SetMouseFocus(SDL_Window *window)
Definition: SDL_mouse.c:211
void Emscripten_DestroyWindowFramebuffer(_THIS, SDL_Window *window)
#define SDL_GL_LoadLibrary
#define SDL_floor
void Emscripten_RegisterEventHandlers(SDL_WindowData *data)
int(* GL_LoadLibrary)(_THIS, const char *path)
Definition: SDL_sysvideo.h:255
GLuint res
int(* SetDisplayMode)(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
Definition: SDL_sysvideo.h:205
void(* GL_GetDrawableSize)(_THIS, SDL_Window *window, int *w, int *h)
Definition: SDL_sysvideo.h:260
static SDL_VideoDevice * _this
Definition: SDL_video.c:118
static SDL_AudioDeviceID device
Definition: loopwave.c:37
int Emscripten_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, void **pixels, int *pitch)
SDL_GLContext(* GL_CreateContext)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:258
#define _THIS
int(* GL_MakeCurrent)(_THIS, SDL_Window *window, SDL_GLContext context)
Definition: SDL_sysvideo.h:259
#define SDL_free
void * driverdata
Definition: SDL_video.h:59
GLenum mode
SDL_VideoDisplay * displays
Definition: SDL_sysvideo.h:316
void(* DestroyWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:235
char * title
Definition: SDL_sysvideo.h:77
void(* DestroyWindowFramebuffer)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:238
void(* GL_UnloadLibrary)(_THIS)
Definition: SDL_sysvideo.h:257
#define NULL
Definition: begin_code.h:167
int(* CreateSDLWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:211
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:161
void(* VideoQuit)(_THIS)
Definition: SDL_sysvideo.h:167
#define SDL_SetError
#define SDL_calloc
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
#define SDL_SetHint
int(* GL_SwapWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:263
#define SDL_strdup
The type used to identify a window.
Definition: SDL_sysvideo.h:73
SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode)
Definition: SDL_video.c:751
int(* VideoInit)(_THIS)
Definition: SDL_sysvideo.h:161
void(* SetWindowFullscreen)(_THIS, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
Definition: SDL_sysvideo.h:231
int(* UpdateWindowFramebuffer)(_THIS, SDL_Window *window, const SDL_Rect *rects, int numrects)
Definition: SDL_sysvideo.h:237
void * driverdata
Definition: SDL_sysvideo.h:111
void(* GL_DeleteContext)(_THIS, SDL_GLContext context)
Definition: SDL_sysvideo.h:264
Uint32 format
Definition: SDL_video.h:55
void(* SetWindowTitle)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:213
void Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
int(* GL_GetSwapInterval)(_THIS)
Definition: SDL_sysvideo.h:262
Uint32 flags
Definition: SDL_sysvideo.h:83
int Emscripten_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *rects, int numrects)
SDL_Renderer * screen
SDL_bool fullscreen_resize
int(* CreateWindowFramebuffer)(_THIS, SDL_Window *window, Uint32 *format, void **pixels, int *pitch)
Definition: SDL_sysvideo.h:236
void *(* GL_GetProcAddress)(_THIS, const char *proc)
Definition: SDL_sysvideo.h:256
EGLSurface egl_surface
void Emscripten_InitMouse()
void(* PumpEvents)(_THIS)
Definition: SDL_sysvideo.h:281