src/video/emscripten/SDL_emscriptenvideo.c
author Alon Zakai <alonzakai@gmail.com>
Tue, 13 Sep 2016 00:03:58 -0700
changeset 10335 77b7fccaaa01
parent 10334 0494a8336c48
child 10541 727f7d796b8d
permissions -rw-r--r--
add some detail to fullscreen workaround comment; version 6

Conflicts:
version.txt
     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
    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 {
    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
   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
   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 {
   177     Emscripten_FiniMouse();
   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     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 = css_w != scaled_w || 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 
   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         emscripten_set_canvas_size(window->w * data->pixel_ratio, window->h * data->pixel_ratio);
   266 
   267         /*scale canvas down*/
   268         if (!data->external_size && data->pixel_ratio != 1.0f) {
   269             emscripten_set_element_css_size(NULL, window->w, window->h);
   270         }
   271     }
   272 }
   273 
   274 static void
   275 Emscripten_DestroyWindow(_THIS, SDL_Window * window)
   276 {
   277     SDL_WindowData *data;
   278 
   279     if(window->driverdata) {
   280         data = (SDL_WindowData *) window->driverdata;
   281 
   282         Emscripten_UnregisterEventHandlers(data);
   283         if (data->egl_surface != EGL_NO_SURFACE) {
   284             SDL_EGL_DestroySurface(_this, data->egl_surface);
   285             data->egl_surface = EGL_NO_SURFACE;
   286         }
   287         SDL_free(window->driverdata);
   288         window->driverdata = NULL;
   289     }
   290 }
   291 
   292 static void
   293 Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
   294 {
   295     SDL_WindowData *data;
   296     if(window->driverdata) {
   297         data = (SDL_WindowData *) window->driverdata;
   298 
   299         if(fullscreen) {
   300             EmscriptenFullscreenStrategy strategy;
   301             SDL_bool is_desktop_fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
   302             int res;
   303 
   304             strategy.scaleMode = is_desktop_fullscreen ? EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH : EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT;
   305 
   306             if(!is_desktop_fullscreen) {
   307                 strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE;
   308             } else if(window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
   309                 strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
   310             } else {
   311                 strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
   312             }
   313 
   314             strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
   315 
   316             strategy.canvasResizedCallback = Emscripten_HandleCanvasResize;
   317             strategy.canvasResizedCallbackUserData = data;
   318 
   319             data->requested_fullscreen_mode = window->flags & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
   320             data->fullscreen_resize = is_desktop_fullscreen;
   321 
   322             res = emscripten_request_fullscreen_strategy(NULL, 1, &strategy);
   323             if(res != EMSCRIPTEN_RESULT_SUCCESS && res != EMSCRIPTEN_RESULT_DEFERRED) {
   324                 /* unset flags, fullscreen failed */
   325                 window->flags &= ~(SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
   326             }
   327         }
   328         else
   329             emscripten_exit_fullscreen();
   330     }
   331 }
   332 
   333 static void
   334 Emscripten_SetWindowTitle(_THIS, SDL_Window * window) {
   335     EM_ASM_INT({
   336       if (typeof Module['setWindowTitle'] !== 'undefined') {
   337         Module['setWindowTitle'](Module['Pointer_stringify']($0));
   338       }
   339       return 0;
   340     }, window->title);
   341 }
   342 
   343 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
   344 
   345 /* vi: set ts=4 sw=4 expandtab: */