• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 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"
35 #include "SDL_emscriptenframebuffer.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
Emscripten_Available(void)57 Emscripten_Available(void)
58 {
59     return (1);
60 }
61 
62 static void
Emscripten_DeleteDevice(SDL_VideoDevice * device)63 Emscripten_DeleteDevice(SDL_VideoDevice * device)
64 {
65     SDL_free(device);
66 }
67 
68 static SDL_VideoDevice *
Emscripten_CreateDevice(int devindex)69 Emscripten_CreateDevice(int devindex)
70 {
71     SDL_VideoDevice *device;
72 
73     /* Initialize all variables that we clean on shutdown */
74     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
75     if (!device) {
76         SDL_OutOfMemory();
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     */
84     SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
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->CreateWindow = Emscripten_CreateWindow;
95     /*device->CreateWindowFrom = Emscripten_CreateWindowFrom;*/
96     device->SetWindowTitle = Emscripten_SetWindowTitle;
97     /*device->SetWindowIcon = Emscripten_SetWindowIcon;
98     device->SetWindowPosition = Emscripten_SetWindowPosition;*/
99     device->SetWindowSize = Emscripten_SetWindowSize;
100     /*device->ShowWindow = Emscripten_ShowWindow;
101     device->HideWindow = Emscripten_HideWindow;
102     device->RaiseWindow = Emscripten_RaiseWindow;
103     device->MaximizeWindow = Emscripten_MaximizeWindow;
104     device->MinimizeWindow = Emscripten_MinimizeWindow;
105     device->RestoreWindow = Emscripten_RestoreWindow;
106     device->SetWindowGrab = Emscripten_SetWindowGrab;*/
107     device->DestroyWindow = Emscripten_DestroyWindow;
108     device->SetWindowFullscreen = Emscripten_SetWindowFullscreen;
109 
110     device->CreateWindowFramebuffer = Emscripten_CreateWindowFramebuffer;
111     device->UpdateWindowFramebuffer = Emscripten_UpdateWindowFramebuffer;
112     device->DestroyWindowFramebuffer = Emscripten_DestroyWindowFramebuffer;
113 
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 
125     device->free = Emscripten_DeleteDevice;
126 
127     return device;
128 }
129 
130 VideoBootStrap Emscripten_bootstrap = {
131     EMSCRIPTENVID_DRIVER_NAME, "SDL emscripten video driver",
132     Emscripten_Available, Emscripten_CreateDevice
133 };
134 
135 
136 int
Emscripten_VideoInit(_THIS)137 Emscripten_VideoInit(_THIS)
138 {
139     SDL_DisplayMode mode;
140 
141     /* Use a fake 32-bpp desktop mode */
142     mode.format = SDL_PIXELFORMAT_RGB888;
143 
144     mode.w = EM_ASM_INT_V({
145         return screen.width;
146     });
147 
148     mode.h = EM_ASM_INT_V({
149         return screen.height;
150     });
151 
152     mode.refresh_rate = 0;
153     mode.driverdata = NULL;
154     if (SDL_AddBasicVideoDisplay(&mode) < 0) {
155         return -1;
156     }
157 
158     SDL_zero(mode);
159     SDL_AddDisplayMode(&_this->displays[0], &mode);
160 
161     Emscripten_InitMouse();
162 
163     /* We're done! */
164     return 0;
165 }
166 
167 static int
Emscripten_SetDisplayMode(_THIS,SDL_VideoDisplay * display,SDL_DisplayMode * mode)168 Emscripten_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
169 {
170     /* can't do this */
171     return 0;
172 }
173 
174 static void
Emscripten_VideoQuit(_THIS)175 Emscripten_VideoQuit(_THIS)
176 {
177     Emscripten_FiniMouse();
178 }
179 
180 static void
Emscripten_PumpEvents(_THIS)181 Emscripten_PumpEvents(_THIS)
182 {
183     /* do nothing. */
184 }
185 
186 static int
Emscripten_CreateWindow(_THIS,SDL_Window * window)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     if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
200         wdata->pixel_ratio = emscripten_get_device_pixel_ratio();
201     } else {
202         wdata->pixel_ratio = 1.0f;
203     }
204 
205     scaled_w = SDL_floor(window->w * wdata->pixel_ratio);
206     scaled_h = SDL_floor(window->h * wdata->pixel_ratio);
207 
208     emscripten_set_canvas_size(scaled_w, scaled_h);
209 
210     emscripten_get_element_css_size(NULL, &css_w, &css_h);
211 
212     wdata->external_size = SDL_floor(css_w) != scaled_w || SDL_floor(css_h) != scaled_h;
213 
214     if ((window->flags & SDL_WINDOW_RESIZABLE) && wdata->external_size) {
215         /* external css has resized us */
216         scaled_w = css_w * wdata->pixel_ratio;
217         scaled_h = css_h * wdata->pixel_ratio;
218 
219         emscripten_set_canvas_size(scaled_w, scaled_h);
220         SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, css_w, css_h);
221     }
222 
223     /* if the size is not being controlled by css, we need to scale down for hidpi */
224     if (!wdata->external_size) {
225         if (wdata->pixel_ratio != 1.0f) {
226             /*scale canvas down*/
227             emscripten_set_element_css_size(NULL, window->w, window->h);
228         }
229     }
230 
231     if (window->flags & SDL_WINDOW_OPENGL) {
232         if (!_this->egl_data) {
233             if (SDL_GL_LoadLibrary(NULL) < 0) {
234                 return -1;
235             }
236         }
237         wdata->egl_surface = SDL_EGL_CreateSurface(_this, 0);
238 
239         if (wdata->egl_surface == EGL_NO_SURFACE) {
240             return SDL_SetError("Could not create GLES window surface");
241         }
242     }
243 
244     wdata->window = window;
245 
246     /* Setup driver data for this window */
247     window->driverdata = wdata;
248 
249     /* One window, it always has focus */
250     SDL_SetMouseFocus(window);
251     SDL_SetKeyboardFocus(window);
252 
253     Emscripten_RegisterEventHandlers(wdata);
254 
255     /* Window has been successfully created */
256     return 0;
257 }
258 
Emscripten_SetWindowSize(_THIS,SDL_Window * window)259 static void Emscripten_SetWindowSize(_THIS, SDL_Window * window)
260 {
261     SDL_WindowData *data;
262 
263     if (window->driverdata) {
264         data = (SDL_WindowData *) window->driverdata;
265         /* update pixel ratio */
266         data->pixel_ratio = emscripten_get_device_pixel_ratio();
267         emscripten_set_canvas_size(window->w * data->pixel_ratio, window->h * data->pixel_ratio);
268 
269         /*scale canvas down*/
270         if (!data->external_size && data->pixel_ratio != 1.0f) {
271             emscripten_set_element_css_size(NULL, window->w, window->h);
272         }
273     }
274 }
275 
276 static void
Emscripten_DestroyWindow(_THIS,SDL_Window * window)277 Emscripten_DestroyWindow(_THIS, SDL_Window * window)
278 {
279     SDL_WindowData *data;
280 
281     if(window->driverdata) {
282         data = (SDL_WindowData *) window->driverdata;
283 
284         Emscripten_UnregisterEventHandlers(data);
285         if (data->egl_surface != EGL_NO_SURFACE) {
286             SDL_EGL_DestroySurface(_this, data->egl_surface);
287             data->egl_surface = EGL_NO_SURFACE;
288         }
289         SDL_free(window->driverdata);
290         window->driverdata = NULL;
291     }
292 }
293 
294 static void
Emscripten_SetWindowFullscreen(_THIS,SDL_Window * window,SDL_VideoDisplay * display,SDL_bool fullscreen)295 Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
296 {
297     SDL_WindowData *data;
298     if(window->driverdata) {
299         data = (SDL_WindowData *) window->driverdata;
300 
301         if(fullscreen) {
302             EmscriptenFullscreenStrategy strategy;
303             SDL_bool is_desktop_fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
304             int res;
305 
306             strategy.scaleMode = is_desktop_fullscreen ? EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH : EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT;
307 
308             if(!is_desktop_fullscreen) {
309                 strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE;
310             } else if(window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
311                 strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
312             } else {
313                 strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
314             }
315 
316             strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
317 
318             strategy.canvasResizedCallback = Emscripten_HandleCanvasResize;
319             strategy.canvasResizedCallbackUserData = data;
320 
321             data->requested_fullscreen_mode = window->flags & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
322             data->fullscreen_resize = is_desktop_fullscreen;
323 
324             res = emscripten_request_fullscreen_strategy(NULL, 1, &strategy);
325             if(res != EMSCRIPTEN_RESULT_SUCCESS && res != EMSCRIPTEN_RESULT_DEFERRED) {
326                 /* unset flags, fullscreen failed */
327                 window->flags &= ~(SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
328             }
329         }
330         else
331             emscripten_exit_fullscreen();
332     }
333 }
334 
335 static void
Emscripten_SetWindowTitle(_THIS,SDL_Window * window)336 Emscripten_SetWindowTitle(_THIS, SDL_Window * window) {
337     EM_ASM_INT({
338       if (typeof Module['setWindowTitle'] !== 'undefined') {
339         Module['setWindowTitle'](Module['Pointer_stringify']($0));
340       }
341       return 0;
342     }, window->title);
343 }
344 
345 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
346 
347 /* vi: set ts=4 sw=4 expandtab: */
348