src/video/emscripten/SDL_emscriptenvideo.c
author Charlie Birks <admin@daftgames.net>
Fri, 17 Feb 2017 10:13:07 +0000
changeset 10895 fd63ed9b0746
parent 10737 3406a0f8b041
child 11191 c3b94a174d81
permissions -rw-r--r--
Emscripten: only update pixel ratio if HiDPI is enabled
     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         if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
   267             data->pixel_ratio = emscripten_get_device_pixel_ratio();
   268         }
   269         emscripten_set_canvas_size(window->w * data->pixel_ratio, window->h * data->pixel_ratio);
   270 
   271         /*scale canvas down*/
   272         if (!data->external_size && data->pixel_ratio != 1.0f) {
   273             emscripten_set_element_css_size(NULL, window->w, window->h);
   274         }
   275     }
   276 }
   277 
   278 static void
   279 Emscripten_DestroyWindow(_THIS, SDL_Window * window)
   280 {
   281     SDL_WindowData *data;
   282 
   283     if(window->driverdata) {
   284         data = (SDL_WindowData *) window->driverdata;
   285 
   286         Emscripten_UnregisterEventHandlers(data);
   287         if (data->egl_surface != EGL_NO_SURFACE) {
   288             SDL_EGL_DestroySurface(_this, data->egl_surface);
   289             data->egl_surface = EGL_NO_SURFACE;
   290         }
   291         SDL_free(window->driverdata);
   292         window->driverdata = NULL;
   293     }
   294 }
   295 
   296 static void
   297 Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
   298 {
   299     SDL_WindowData *data;
   300     if(window->driverdata) {
   301         data = (SDL_WindowData *) window->driverdata;
   302 
   303         if(fullscreen) {
   304             EmscriptenFullscreenStrategy strategy;
   305             SDL_bool is_desktop_fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
   306             int res;
   307 
   308             strategy.scaleMode = is_desktop_fullscreen ? EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH : EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT;
   309 
   310             if(!is_desktop_fullscreen) {
   311                 strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE;
   312             } else if(window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
   313                 strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
   314             } else {
   315                 strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
   316             }
   317 
   318             strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
   319 
   320             strategy.canvasResizedCallback = Emscripten_HandleCanvasResize;
   321             strategy.canvasResizedCallbackUserData = data;
   322 
   323             data->requested_fullscreen_mode = window->flags & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
   324             data->fullscreen_resize = is_desktop_fullscreen;
   325 
   326             res = emscripten_request_fullscreen_strategy(NULL, 1, &strategy);
   327             if(res != EMSCRIPTEN_RESULT_SUCCESS && res != EMSCRIPTEN_RESULT_DEFERRED) {
   328                 /* unset flags, fullscreen failed */
   329                 window->flags &= ~(SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
   330             }
   331         }
   332         else
   333             emscripten_exit_fullscreen();
   334     }
   335 }
   336 
   337 static void
   338 Emscripten_SetWindowTitle(_THIS, SDL_Window * window) {
   339     EM_ASM_INT({
   340       if (typeof Module['setWindowTitle'] !== 'undefined') {
   341         Module['setWindowTitle'](Module['Pointer_stringify']($0));
   342       }
   343       return 0;
   344     }, window->title);
   345 }
   346 
   347 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
   348 
   349 /* vi: set ts=4 sw=4 expandtab: */