SDL  2.0
SDL_emscriptenvideo.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 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 static int Emscripten_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect);
46 
47 static int Emscripten_CreateWindow(_THIS, SDL_Window * window);
48 static void Emscripten_SetWindowSize(_THIS, SDL_Window * window);
49 static void Emscripten_DestroyWindow(_THIS, SDL_Window * window);
50 static void Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
51 static void Emscripten_PumpEvents(_THIS);
52 static void Emscripten_SetWindowTitle(_THIS, SDL_Window * window);
53 
54 
55 /* Emscripten driver bootstrap functions */
56 
57 static void
58 Emscripten_DeleteDevice(SDL_VideoDevice * device)
59 {
61 }
62 
63 static SDL_VideoDevice *
64 Emscripten_CreateDevice(int devindex)
65 {
67 
68  /* Initialize all variables that we clean on shutdown */
70  if (!device) {
72  return (0);
73  }
74 
75  /* Firefox sends blur event which would otherwise prevent full screen
76  * when the user clicks to allow full screen.
77  * See https://bugzilla.mozilla.org/show_bug.cgi?id=1144964
78  */
80 
81  /* Set the function pointers */
82  device->VideoInit = Emscripten_VideoInit;
83  device->VideoQuit = Emscripten_VideoQuit;
84  device->GetDisplayUsableBounds = Emscripten_GetDisplayUsableBounds;
85  device->SetDisplayMode = Emscripten_SetDisplayMode;
86 
87 
88  device->PumpEvents = Emscripten_PumpEvents;
89 
90  device->CreateSDLWindow = Emscripten_CreateWindow;
91  device->SetWindowTitle = Emscripten_SetWindowTitle;
92  /*device->SetWindowIcon = Emscripten_SetWindowIcon;
93  device->SetWindowPosition = Emscripten_SetWindowPosition;*/
94  device->SetWindowSize = Emscripten_SetWindowSize;
95  /*device->ShowWindow = Emscripten_ShowWindow;
96  device->HideWindow = Emscripten_HideWindow;
97  device->RaiseWindow = Emscripten_RaiseWindow;
98  device->MaximizeWindow = Emscripten_MaximizeWindow;
99  device->MinimizeWindow = Emscripten_MinimizeWindow;
100  device->RestoreWindow = Emscripten_RestoreWindow;
101  device->SetWindowGrab = Emscripten_SetWindowGrab;*/
102  device->DestroyWindow = Emscripten_DestroyWindow;
103  device->SetWindowFullscreen = Emscripten_SetWindowFullscreen;
104 
105  device->CreateWindowFramebuffer = Emscripten_CreateWindowFramebuffer;
106  device->UpdateWindowFramebuffer = Emscripten_UpdateWindowFramebuffer;
107  device->DestroyWindowFramebuffer = Emscripten_DestroyWindowFramebuffer;
108 
109 #if SDL_VIDEO_OPENGL_EGL
110  device->GL_LoadLibrary = Emscripten_GLES_LoadLibrary;
111  device->GL_GetProcAddress = Emscripten_GLES_GetProcAddress;
112  device->GL_UnloadLibrary = Emscripten_GLES_UnloadLibrary;
113  device->GL_CreateContext = Emscripten_GLES_CreateContext;
114  device->GL_MakeCurrent = Emscripten_GLES_MakeCurrent;
115  device->GL_SetSwapInterval = Emscripten_GLES_SetSwapInterval;
116  device->GL_GetSwapInterval = Emscripten_GLES_GetSwapInterval;
117  device->GL_SwapWindow = Emscripten_GLES_SwapWindow;
118  device->GL_DeleteContext = Emscripten_GLES_DeleteContext;
119  device->GL_GetDrawableSize = Emscripten_GLES_GetDrawableSize;
120 #endif
121 
122  device->free = Emscripten_DeleteDevice;
123 
124  return device;
125 }
126 
128  EMSCRIPTENVID_DRIVER_NAME, "SDL emscripten video driver",
129  Emscripten_CreateDevice
130 };
131 
132 
133 int
134 Emscripten_VideoInit(_THIS)
135 {
137 
138  /* Use a fake 32-bpp desktop mode */
139  mode.format = SDL_PIXELFORMAT_RGB888;
140 
141  mode.w = EM_ASM_INT_V({
142  return screen.width;
143  });
144 
145  mode.h = EM_ASM_INT_V({
146  return screen.height;
147  });
148 
149  mode.refresh_rate = 0;
150  mode.driverdata = NULL;
151  if (SDL_AddBasicVideoDisplay(&mode) < 0) {
152  return -1;
153  }
154 
156 
158 
159  /* We're done! */
160  return 0;
161 }
162 
163 static int
164 Emscripten_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
165 {
166  /* can't do this */
167  return 0;
168 }
169 
170 static void
171 Emscripten_VideoQuit(_THIS)
172 {
174 }
175 
176 static int
177 Emscripten_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
178 {
179  if (rect) {
180  rect->x = 0;
181  rect->y = 0;
182  rect->w = EM_ASM_INT_V({
183  return window.innerWidth;
184  });
185  rect->h = EM_ASM_INT_V({
186  return window.innerHeight;
187  });
188  }
189  return 0;
190 }
191 
192 static void
193 Emscripten_PumpEvents(_THIS)
194 {
195  /* do nothing. */
196 }
197 
198 static int
199 Emscripten_CreateWindow(_THIS, SDL_Window * window)
200 {
201  SDL_WindowData *wdata;
202  double scaled_w, scaled_h;
203  double css_w, css_h;
204 
205  /* Allocate window internal data */
206  wdata = (SDL_WindowData *) SDL_calloc(1, sizeof(SDL_WindowData));
207  if (wdata == NULL) {
208  return SDL_OutOfMemory();
209  }
210 
211  wdata->canvas_id = SDL_strdup("#canvas");
212 
213  if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
214  wdata->pixel_ratio = emscripten_get_device_pixel_ratio();
215  } else {
216  wdata->pixel_ratio = 1.0f;
217  }
218 
219  scaled_w = SDL_floor(window->w * wdata->pixel_ratio);
220  scaled_h = SDL_floor(window->h * wdata->pixel_ratio);
221 
222  /* set a fake size to check if there is any CSS sizing the canvas */
223  emscripten_set_canvas_element_size(wdata->canvas_id, 1, 1);
224  emscripten_get_element_css_size(wdata->canvas_id, &css_w, &css_h);
225 
226  wdata->external_size = SDL_floor(css_w) != 1 || SDL_floor(css_h) != 1;
227 
228  if ((window->flags & SDL_WINDOW_RESIZABLE) && wdata->external_size) {
229  /* external css has resized us */
230  scaled_w = css_w * wdata->pixel_ratio;
231  scaled_h = css_h * wdata->pixel_ratio;
232 
234  }
235  emscripten_set_canvas_element_size(wdata->canvas_id, scaled_w, scaled_h);
236 
237  /* if the size is not being controlled by css, we need to scale down for hidpi */
238  if (!wdata->external_size) {
239  if (wdata->pixel_ratio != 1.0f) {
240  /*scale canvas down*/
241  emscripten_set_element_css_size(wdata->canvas_id, window->w, window->h);
242  }
243  }
244 
245 #if SDL_VIDEO_OPENGL_EGL
246  if (window->flags & SDL_WINDOW_OPENGL) {
247  if (!_this->egl_data) {
248  if (SDL_GL_LoadLibrary(NULL) < 0) {
249  return -1;
250  }
251  }
252  wdata->egl_surface = SDL_EGL_CreateSurface(_this, 0);
253 
254  if (wdata->egl_surface == EGL_NO_SURFACE) {
255  return SDL_SetError("Could not create GLES window surface");
256  }
257  }
258 #endif
259 
260  wdata->window = window;
261 
262  /* Setup driver data for this window */
263  window->driverdata = wdata;
264 
265  /* One window, it always has focus */
268 
270 
271  /* Window has been successfully created */
272  return 0;
273 }
274 
275 static void Emscripten_SetWindowSize(_THIS, SDL_Window * window)
276 {
278 
279  if (window->driverdata) {
280  data = (SDL_WindowData *) window->driverdata;
281  /* update pixel ratio */
282  if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
283  data->pixel_ratio = emscripten_get_device_pixel_ratio();
284  }
285  emscripten_set_canvas_element_size(data->canvas_id, window->w * data->pixel_ratio, window->h * data->pixel_ratio);
286 
287  /*scale canvas down*/
288  if (!data->external_size && data->pixel_ratio != 1.0f) {
289  emscripten_set_element_css_size(data->canvas_id, window->w, window->h);
290  }
291  }
292 }
293 
294 static void
295 Emscripten_DestroyWindow(_THIS, SDL_Window * window)
296 {
298 
299  if(window->driverdata) {
300  data = (SDL_WindowData *) window->driverdata;
301 
303 #if SDL_VIDEO_OPENGL_EGL
304  if (data->egl_surface != EGL_NO_SURFACE) {
305  SDL_EGL_DestroySurface(_this, data->egl_surface);
306  data->egl_surface = EGL_NO_SURFACE;
307  }
308 #endif
309 
310  /* We can't destroy the canvas, so resize it to zero instead */
311  emscripten_set_canvas_element_size(data->canvas_id, 0, 0);
312  SDL_free(data->canvas_id);
313 
314  SDL_free(window->driverdata);
315  window->driverdata = NULL;
316  }
317 }
318 
319 static void
320 Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
321 {
323  if(window->driverdata) {
324  data = (SDL_WindowData *) window->driverdata;
325 
326  if(fullscreen) {
327  EmscriptenFullscreenStrategy strategy;
328  SDL_bool is_desktop_fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
329  int res;
330 
331  strategy.scaleMode = is_desktop_fullscreen ? EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH : EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT;
332 
333  if(!is_desktop_fullscreen) {
334  strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE;
335  } else if(window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
336  strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
337  } else {
338  strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
339  }
340 
341  strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
342 
343  strategy.canvasResizedCallback = Emscripten_HandleCanvasResize;
344  strategy.canvasResizedCallbackUserData = data;
345 
346  data->requested_fullscreen_mode = window->flags & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
347  data->fullscreen_resize = is_desktop_fullscreen;
348 
349  res = emscripten_request_fullscreen_strategy(data->canvas_id, 1, &strategy);
350  if(res != EMSCRIPTEN_RESULT_SUCCESS && res != EMSCRIPTEN_RESULT_DEFERRED) {
351  /* unset flags, fullscreen failed */
353  }
354  }
355  else
356  emscripten_exit_fullscreen();
357  }
358 }
359 
360 static void
361 Emscripten_SetWindowTitle(_THIS, SDL_Window * window) {
362  EM_ASM_INT({
363  if (typeof setWindowTitle !== 'undefined') {
364  setWindowTitle(UTF8ToString($0));
365  }
366  return 0;
367  }, window->title);
368 }
369 
370 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
371 
372 /* vi: set ts=4 sw=4 expandtab: */
#define _THIS
#define SDL_SetError
#define SDL_floor
#define SDL_GL_LoadLibrary
#define SDL_free
#define SDL_strdup
#define SDL_SetHint
#define SDL_calloc
void Emscripten_RegisterEventHandlers(SDL_WindowData *data)
EM_BOOL Emscripten_HandleCanvasResize(int eventType, const void *reserved, void *userData)
void Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
void Emscripten_DestroyWindowFramebuffer(_THIS, SDL_Window *window)
int Emscripten_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *rects, int numrects)
int Emscripten_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format, void **pixels, int *pitch)
void Emscripten_FiniMouse()
void Emscripten_InitMouse()
#define SDL_OutOfMemory()
Definition: SDL_error.h:88
#define SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS
Minimize your SDL_Window if it loses key focus when in fullscreen mode. Defaults to false.
Definition: SDL_hints.h:377
void SDL_SetKeyboardFocus(SDL_Window *window)
Definition: SDL_keyboard.c:634
void SDL_SetMouseFocus(SDL_Window *window)
Definition: SDL_mouse.c:208
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLuint res
GLenum mode
@ SDL_PIXELFORMAT_RGB888
Definition: SDL_pixels.h:246
SDL_bool
Definition: SDL_stdinc.h:168
int SDL_AddBasicVideoDisplay(const SDL_DisplayMode *desktop_mode)
Definition: SDL_video.c:593
VideoBootStrap Emscripten_bootstrap
SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode)
Definition: SDL_video.c:792
static SDL_VideoDevice * _this
Definition: SDL_video.c:126
@ SDL_WINDOW_ALLOW_HIGHDPI
Definition: SDL_video.h:112
@ SDL_WINDOW_OPENGL
Definition: SDL_video.h:100
@ SDL_WINDOW_RESIZABLE
Definition: SDL_video.h:104
@ SDL_WINDOW_FULLSCREEN_DESKTOP
Definition: SDL_video.h:110
@ SDL_WINDOW_FULLSCREEN
Definition: SDL_video.h:99
@ SDL_WINDOWEVENT_RESIZED
Definition: SDL_video.h:155
int SDL_SendWindowEvent(SDL_Window *window, Uint8 windowevent, int data1, int data2)
#define NULL
Definition: begin_code.h:163
#define EGL_NO_SURFACE
Definition: egl.h:100
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
static SDL_AudioDeviceID device
Definition: loopwave.c:37
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro pixld1_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl else error unsupported endif endm macro pixld2_s mem_operand if mov asr add asl add asl mov asr sub UNIT_X add asl mov asr add asl add asl mov asr add UNIT_X add asl else pixld1_s mem_operand pixld1_s mem_operand endif endm macro pixld0_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl endif endm macro pixld_s_internal mem_operand if mem_operand pixld2_s mem_operand pixdeinterleave basereg elseif mem_operand elseif mem_operand elseif mem_operand elseif mem_operand pixld0_s mem_operand else pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else error unsupported mem_operand if bpp mem_operand endif endm macro vuzp8 reg2 vuzp d d &reg2 endm macro vzip8 reg2 vzip d d &reg2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld[DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld if[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
The structure that defines a display mode.
Definition: SDL_video.h:54
A rectangle, with the origin at the upper left (integer).
Definition: SDL_rect.h:78
int h
Definition: SDL_rect.h:80
int w
Definition: SDL_rect.h:80
int y
Definition: SDL_rect.h:79
int x
Definition: SDL_rect.h:79
SDL_VideoDisplay * displays
Definition: SDL_sysvideo.h:326
SDL_Window * window
EGLSurface egl_surface
The type used to identify a window.
Definition: SDL_sysvideo.h:75
SDL_Renderer * screen
SDL_Rect rect
Definition: testrelative.c:27