src/video/emscripten/SDL_emscriptenvideo.c
author Charlie Birks <charlie@daftgames.net>
Thu, 09 May 2019 12:09:34 +0100
changeset 12724 19c373658809
parent 12578 b127ca09e7fe
permissions -rw-r--r--
Emscripten: Store canvas id in WindowData
Also replace all hardcoded uses of "#canvas" or NULL
icculus@9278
     1
/*
icculus@9278
     2
  Simple DirectMedia Layer
slouken@12503
     3
  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
icculus@9278
     4
icculus@9278
     5
  This software is provided 'as-is', without any express or implied
icculus@9278
     6
  warranty.  In no event will the authors be held liable for any damages
icculus@9278
     7
  arising from the use of this software.
icculus@9278
     8
icculus@9278
     9
  Permission is granted to anyone to use this software for any purpose,
icculus@9278
    10
  including commercial applications, and to alter it and redistribute it
icculus@9278
    11
  freely, subject to the following restrictions:
icculus@9278
    12
icculus@9278
    13
  1. The origin of this software must not be misrepresented; you must not
icculus@9278
    14
     claim that you wrote the original software. If you use this software
icculus@9278
    15
     in a product, an acknowledgment in the product documentation would be
icculus@9278
    16
     appreciated but is not required.
icculus@9278
    17
  2. Altered source versions must be plainly marked as such, and must not be
icculus@9278
    18
     misrepresented as being the original software.
icculus@9278
    19
  3. This notice may not be removed or altered from any source distribution.
icculus@9278
    20
*/
icculus@9278
    21
#include "../../SDL_internal.h"
icculus@9278
    22
icculus@9278
    23
#if SDL_VIDEO_DRIVER_EMSCRIPTEN
icculus@9278
    24
icculus@9278
    25
#include "SDL_video.h"
icculus@9278
    26
#include "SDL_mouse.h"
boris@10332
    27
#include "SDL_hints.h"
icculus@9278
    28
#include "../SDL_sysvideo.h"
icculus@9278
    29
#include "../SDL_pixels_c.h"
icculus@9278
    30
#include "../SDL_egl_c.h"
icculus@9278
    31
#include "../../events/SDL_events_c.h"
icculus@9278
    32
icculus@9278
    33
#include "SDL_emscriptenvideo.h"
icculus@9278
    34
#include "SDL_emscriptenopengles.h"
icculus@9278
    35
#include "SDL_emscriptenframebuffer.h"
icculus@9278
    36
#include "SDL_emscriptenevents.h"
icculus@9278
    37
#include "SDL_emscriptenmouse.h"
icculus@9278
    38
icculus@9278
    39
#define EMSCRIPTENVID_DRIVER_NAME "emscripten"
icculus@9278
    40
icculus@9278
    41
/* Initialization/Query functions */
icculus@9278
    42
static int Emscripten_VideoInit(_THIS);
icculus@9278
    43
static int Emscripten_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);
icculus@9278
    44
static void Emscripten_VideoQuit(_THIS);
icculus@9278
    45
icculus@9278
    46
static int Emscripten_CreateWindow(_THIS, SDL_Window * window);
icculus@9278
    47
static void Emscripten_SetWindowSize(_THIS, SDL_Window * window);
icculus@9278
    48
static void Emscripten_DestroyWindow(_THIS, SDL_Window * window);
icculus@9278
    49
static void Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
icculus@9278
    50
static void Emscripten_PumpEvents(_THIS);
boris@10331
    51
static void Emscripten_SetWindowTitle(_THIS, SDL_Window * window);
icculus@9278
    52
icculus@9278
    53
icculus@9278
    54
/* Emscripten driver bootstrap functions */
icculus@9278
    55
icculus@9278
    56
static int
icculus@9278
    57
Emscripten_Available(void)
icculus@9278
    58
{
icculus@9278
    59
    return (1);
icculus@9278
    60
}
icculus@9278
    61
icculus@9278
    62
static void
icculus@9278
    63
Emscripten_DeleteDevice(SDL_VideoDevice * device)
icculus@9278
    64
{
icculus@9278
    65
    SDL_free(device);
icculus@9278
    66
}
icculus@9278
    67
icculus@9278
    68
static SDL_VideoDevice *
icculus@9278
    69
Emscripten_CreateDevice(int devindex)
icculus@9278
    70
{
icculus@9278
    71
    SDL_VideoDevice *device;
icculus@9278
    72
icculus@9278
    73
    /* Initialize all variables that we clean on shutdown */
icculus@9278
    74
    device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
icculus@9278
    75
    if (!device) {
icculus@9278
    76
        SDL_OutOfMemory();
icculus@9278
    77
        return (0);
icculus@9278
    78
    }
icculus@9278
    79
alonzakai@10335
    80
    /* Firefox sends blur event which would otherwise prevent full screen
alonzakai@10335
    81
     * when the user clicks to allow full screen.
alonzakai@10335
    82
     * See https://bugzilla.mozilla.org/show_bug.cgi?id=1144964
alonzakai@10335
    83
    */
boris@10332
    84
    SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
boris@10332
    85
icculus@9278
    86
    /* Set the function pointers */
icculus@9278
    87
    device->VideoInit = Emscripten_VideoInit;
icculus@9278
    88
    device->VideoQuit = Emscripten_VideoQuit;
icculus@9278
    89
    device->SetDisplayMode = Emscripten_SetDisplayMode;
icculus@9278
    90
icculus@9278
    91
icculus@9278
    92
    device->PumpEvents = Emscripten_PumpEvents;
icculus@9278
    93
slouken@11383
    94
    device->CreateSDLWindow = Emscripten_CreateWindow;
icculus@9278
    95
    device->SetWindowTitle = Emscripten_SetWindowTitle;
boris@10331
    96
    /*device->SetWindowIcon = Emscripten_SetWindowIcon;
icculus@9278
    97
    device->SetWindowPosition = Emscripten_SetWindowPosition;*/
icculus@9278
    98
    device->SetWindowSize = Emscripten_SetWindowSize;
icculus@9278
    99
    /*device->ShowWindow = Emscripten_ShowWindow;
icculus@9278
   100
    device->HideWindow = Emscripten_HideWindow;
icculus@9278
   101
    device->RaiseWindow = Emscripten_RaiseWindow;
icculus@9278
   102
    device->MaximizeWindow = Emscripten_MaximizeWindow;
icculus@9278
   103
    device->MinimizeWindow = Emscripten_MinimizeWindow;
icculus@9278
   104
    device->RestoreWindow = Emscripten_RestoreWindow;
icculus@9278
   105
    device->SetWindowGrab = Emscripten_SetWindowGrab;*/
icculus@9278
   106
    device->DestroyWindow = Emscripten_DestroyWindow;
icculus@9278
   107
    device->SetWindowFullscreen = Emscripten_SetWindowFullscreen;
icculus@9278
   108
icculus@9278
   109
    device->CreateWindowFramebuffer = Emscripten_CreateWindowFramebuffer;
icculus@9278
   110
    device->UpdateWindowFramebuffer = Emscripten_UpdateWindowFramebuffer;
icculus@9278
   111
    device->DestroyWindowFramebuffer = Emscripten_DestroyWindowFramebuffer;
icculus@9278
   112
philipp@11191
   113
#if SDL_VIDEO_OPENGL_EGL
icculus@9278
   114
    device->GL_LoadLibrary = Emscripten_GLES_LoadLibrary;
icculus@9278
   115
    device->GL_GetProcAddress = Emscripten_GLES_GetProcAddress;
icculus@9278
   116
    device->GL_UnloadLibrary = Emscripten_GLES_UnloadLibrary;
icculus@9278
   117
    device->GL_CreateContext = Emscripten_GLES_CreateContext;
icculus@9278
   118
    device->GL_MakeCurrent = Emscripten_GLES_MakeCurrent;
icculus@9278
   119
    device->GL_SetSwapInterval = Emscripten_GLES_SetSwapInterval;
icculus@9278
   120
    device->GL_GetSwapInterval = Emscripten_GLES_GetSwapInterval;
icculus@9278
   121
    device->GL_SwapWindow = Emscripten_GLES_SwapWindow;
icculus@9278
   122
    device->GL_DeleteContext = Emscripten_GLES_DeleteContext;
icculus@9278
   123
    device->GL_GetDrawableSize = Emscripten_GLES_GetDrawableSize;
philipp@11191
   124
#endif
icculus@9278
   125
icculus@9278
   126
    device->free = Emscripten_DeleteDevice;
icculus@9278
   127
icculus@9278
   128
    return device;
icculus@9278
   129
}
icculus@9278
   130
icculus@9278
   131
VideoBootStrap Emscripten_bootstrap = {
icculus@9278
   132
    EMSCRIPTENVID_DRIVER_NAME, "SDL emscripten video driver",
icculus@9278
   133
    Emscripten_Available, Emscripten_CreateDevice
icculus@9278
   134
};
icculus@9278
   135
icculus@9278
   136
icculus@9278
   137
int
icculus@9278
   138
Emscripten_VideoInit(_THIS)
icculus@9278
   139
{
icculus@9278
   140
    SDL_DisplayMode mode;
icculus@9278
   141
icculus@9278
   142
    /* Use a fake 32-bpp desktop mode */
icculus@9278
   143
    mode.format = SDL_PIXELFORMAT_RGB888;
icculus@9278
   144
admin@10333
   145
    mode.w = EM_ASM_INT_V({
admin@10333
   146
        return screen.width;
admin@10333
   147
    });
icculus@9278
   148
admin@10333
   149
    mode.h = EM_ASM_INT_V({
admin@10333
   150
        return screen.height;
admin@10333
   151
    });
icculus@9278
   152
icculus@9278
   153
    mode.refresh_rate = 0;
icculus@9278
   154
    mode.driverdata = NULL;
icculus@9278
   155
    if (SDL_AddBasicVideoDisplay(&mode) < 0) {
icculus@9278
   156
        return -1;
icculus@9278
   157
    }
icculus@9278
   158
icculus@9278
   159
    SDL_AddDisplayMode(&_this->displays[0], &mode);
icculus@9278
   160
icculus@9278
   161
    Emscripten_InitMouse();
icculus@9278
   162
icculus@9278
   163
    /* We're done! */
icculus@9278
   164
    return 0;
icculus@9278
   165
}
icculus@9278
   166
icculus@9278
   167
static int
icculus@9278
   168
Emscripten_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
icculus@9278
   169
{
icculus@9278
   170
    /* can't do this */
icculus@9278
   171
    return 0;
icculus@9278
   172
}
icculus@9278
   173
icculus@9278
   174
static void
icculus@9278
   175
Emscripten_VideoQuit(_THIS)
icculus@9278
   176
{
icculus@9278
   177
    Emscripten_FiniMouse();
icculus@9278
   178
}
icculus@9278
   179
icculus@9278
   180
static void
icculus@9278
   181
Emscripten_PumpEvents(_THIS)
icculus@9278
   182
{
icculus@9278
   183
    /* do nothing. */
icculus@9278
   184
}
icculus@9278
   185
icculus@9278
   186
static int
icculus@9278
   187
Emscripten_CreateWindow(_THIS, SDL_Window * window)
icculus@9278
   188
{
icculus@9278
   189
    SDL_WindowData *wdata;
icculus@9278
   190
    double scaled_w, scaled_h;
icculus@9278
   191
    double css_w, css_h;
icculus@9278
   192
icculus@9278
   193
    /* Allocate window internal data */
icculus@9278
   194
    wdata = (SDL_WindowData *) SDL_calloc(1, sizeof(SDL_WindowData));
icculus@9278
   195
    if (wdata == NULL) {
icculus@9278
   196
        return SDL_OutOfMemory();
icculus@9278
   197
    }
icculus@9278
   198
charlie@12724
   199
    wdata->canvas_id = SDL_strdup("#canvas");
charlie@12724
   200
icculus@9278
   201
    if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
icculus@9278
   202
        wdata->pixel_ratio = emscripten_get_device_pixel_ratio();
icculus@9278
   203
    } else {
icculus@9278
   204
        wdata->pixel_ratio = 1.0f;
icculus@9278
   205
    }
icculus@9278
   206
icculus@9278
   207
    scaled_w = SDL_floor(window->w * wdata->pixel_ratio);
icculus@9278
   208
    scaled_h = SDL_floor(window->h * wdata->pixel_ratio);
icculus@9278
   209
charlie@12571
   210
    /* set a fake size to check if there is any CSS sizing the canvas */
charlie@12724
   211
    emscripten_set_canvas_element_size(wdata->canvas_id, 1, 1);
charlie@12724
   212
    emscripten_get_element_css_size(wdata->canvas_id, &css_w, &css_h);
icculus@9278
   213
charlie@12571
   214
    wdata->external_size = SDL_floor(css_w) != 1 || SDL_floor(css_h) != 1;
icculus@9278
   215
icculus@9278
   216
    if ((window->flags & SDL_WINDOW_RESIZABLE) && wdata->external_size) {
icculus@9278
   217
        /* external css has resized us */
icculus@9278
   218
        scaled_w = css_w * wdata->pixel_ratio;
icculus@9278
   219
        scaled_h = css_h * wdata->pixel_ratio;
icculus@9278
   220
icculus@9278
   221
        SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, css_w, css_h);
icculus@9278
   222
    }
charlie@12724
   223
    emscripten_set_canvas_element_size(wdata->canvas_id, scaled_w, scaled_h);
charlie@12571
   224
icculus@9278
   225
    /* if the size is not being controlled by css, we need to scale down for hidpi */
icculus@9278
   226
    if (!wdata->external_size) {
icculus@9278
   227
        if (wdata->pixel_ratio != 1.0f) {
icculus@9278
   228
            /*scale canvas down*/
charlie@12724
   229
            emscripten_set_element_css_size(wdata->canvas_id, window->w, window->h);
icculus@9278
   230
        }
icculus@9278
   231
    }
icculus@9278
   232
philipp@11191
   233
#if SDL_VIDEO_OPENGL_EGL
icculus@9278
   234
    if (window->flags & SDL_WINDOW_OPENGL) {
icculus@9278
   235
        if (!_this->egl_data) {
icculus@9278
   236
            if (SDL_GL_LoadLibrary(NULL) < 0) {
icculus@9278
   237
                return -1;
icculus@9278
   238
            }
icculus@9278
   239
        }
icculus@9300
   240
        wdata->egl_surface = SDL_EGL_CreateSurface(_this, 0);
icculus@9278
   241
icculus@9278
   242
        if (wdata->egl_surface == EGL_NO_SURFACE) {
icculus@9278
   243
            return SDL_SetError("Could not create GLES window surface");
icculus@9278
   244
        }
icculus@9278
   245
    }
philipp@11191
   246
#endif
icculus@9278
   247
icculus@9278
   248
    wdata->window = window;
icculus@9278
   249
icculus@9278
   250
    /* Setup driver data for this window */
icculus@9278
   251
    window->driverdata = wdata;
icculus@9278
   252
icculus@9278
   253
    /* One window, it always has focus */
icculus@9278
   254
    SDL_SetMouseFocus(window);
icculus@9278
   255
    SDL_SetKeyboardFocus(window);
icculus@9278
   256
icculus@9278
   257
    Emscripten_RegisterEventHandlers(wdata);
icculus@9278
   258
icculus@9278
   259
    /* Window has been successfully created */
icculus@9278
   260
    return 0;
icculus@9278
   261
}
icculus@9278
   262
icculus@9278
   263
static void Emscripten_SetWindowSize(_THIS, SDL_Window * window)
icculus@9278
   264
{
icculus@9278
   265
    SDL_WindowData *data;
icculus@9278
   266
icculus@9278
   267
    if (window->driverdata) {
icculus@9278
   268
        data = (SDL_WindowData *) window->driverdata;
csongor@10541
   269
        /* update pixel ratio */
admin@10895
   270
        if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
admin@10895
   271
            data->pixel_ratio = emscripten_get_device_pixel_ratio();
admin@10895
   272
        }
charlie@12724
   273
        emscripten_set_canvas_element_size(data->canvas_id, window->w * data->pixel_ratio, window->h * data->pixel_ratio);
icculus@9278
   274
icculus@9278
   275
        /*scale canvas down*/
icculus@9278
   276
        if (!data->external_size && data->pixel_ratio != 1.0f) {
charlie@12724
   277
            emscripten_set_element_css_size(data->canvas_id, window->w, window->h);
icculus@9278
   278
        }
icculus@9278
   279
    }
icculus@9278
   280
}
icculus@9278
   281
icculus@9278
   282
static void
icculus@9278
   283
Emscripten_DestroyWindow(_THIS, SDL_Window * window)
icculus@9278
   284
{
icculus@9278
   285
    SDL_WindowData *data;
icculus@9278
   286
icculus@9278
   287
    if(window->driverdata) {
icculus@9278
   288
        data = (SDL_WindowData *) window->driverdata;
icculus@9278
   289
icculus@9278
   290
        Emscripten_UnregisterEventHandlers(data);
philipp@11191
   291
#if SDL_VIDEO_OPENGL_EGL
icculus@9278
   292
        if (data->egl_surface != EGL_NO_SURFACE) {
icculus@9278
   293
            SDL_EGL_DestroySurface(_this, data->egl_surface);
icculus@9278
   294
            data->egl_surface = EGL_NO_SURFACE;
icculus@9278
   295
        }
philipp@11191
   296
#endif
charlie@12578
   297
charlie@12578
   298
        /* We can't destroy the canvas, so resize it to zero instead */
charlie@12724
   299
        emscripten_set_canvas_element_size(data->canvas_id, 0, 0);
charlie@12724
   300
        SDL_free(data->canvas_id);
charlie@12578
   301
icculus@9278
   302
        SDL_free(window->driverdata);
icculus@9278
   303
        window->driverdata = NULL;
icculus@9278
   304
    }
icculus@9278
   305
}
icculus@9278
   306
icculus@9278
   307
static void
icculus@9278
   308
Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
icculus@9278
   309
{
icculus@9278
   310
    SDL_WindowData *data;
icculus@9278
   311
    if(window->driverdata) {
icculus@9278
   312
        data = (SDL_WindowData *) window->driverdata;
icculus@9278
   313
icculus@9278
   314
        if(fullscreen) {
slouken@10317
   315
            EmscriptenFullscreenStrategy strategy;
slouken@10317
   316
            SDL_bool is_desktop_fullscreen = (window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
admin@10334
   317
            int res;
slouken@10317
   318
slouken@10317
   319
            strategy.scaleMode = is_desktop_fullscreen ? EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH : EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT;
slouken@10317
   320
slouken@10317
   321
            if(!is_desktop_fullscreen) {
slouken@10317
   322
                strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE;
slouken@10317
   323
            } else if(window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
slouken@10317
   324
                strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
slouken@10317
   325
            } else {
slouken@10317
   326
                strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
slouken@10317
   327
            }
slouken@10317
   328
slouken@10317
   329
            strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
slouken@10317
   330
slouken@10317
   331
            strategy.canvasResizedCallback = Emscripten_HandleCanvasResize;
slouken@10317
   332
            strategy.canvasResizedCallbackUserData = data;
slouken@10317
   333
icculus@9278
   334
            data->requested_fullscreen_mode = window->flags & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
slouken@10317
   335
            data->fullscreen_resize = is_desktop_fullscreen;
icculus@9278
   336
charlie@12724
   337
            res = emscripten_request_fullscreen_strategy(data->canvas_id, 1, &strategy);
admin@10334
   338
            if(res != EMSCRIPTEN_RESULT_SUCCESS && res != EMSCRIPTEN_RESULT_DEFERRED) {
admin@10334
   339
                /* unset flags, fullscreen failed */
admin@10334
   340
                window->flags &= ~(SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
admin@10334
   341
            }
icculus@9278
   342
        }
icculus@9278
   343
        else
icculus@9278
   344
            emscripten_exit_fullscreen();
icculus@9278
   345
    }
icculus@9278
   346
}
icculus@9278
   347
boris@10331
   348
static void
boris@10331
   349
Emscripten_SetWindowTitle(_THIS, SDL_Window * window) {
boris@10331
   350
    EM_ASM_INT({
boris@10331
   351
      if (typeof Module['setWindowTitle'] !== 'undefined') {
charlie@12576
   352
        Module['setWindowTitle'](UTF8ToString($0));
boris@10331
   353
      }
boris@10331
   354
      return 0;
boris@10331
   355
    }, window->title);
boris@10331
   356
}
boris@10331
   357
icculus@9278
   358
#endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
icculus@9278
   359
icculus@9278
   360
/* vi: set ts=4 sw=4 expandtab: */