src/video/emscripten/SDL_emscriptenvideo.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 01 Jan 2017 18:33:28 -0800
changeset 10737 3406a0f8b041
parent 10541 727f7d796b8d
child 10895 fd63ed9b0746
permissions -rw-r--r--
Updated copyright for 2017
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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 = 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 
   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
   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
   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
   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: */