src/video/emscripten/SDL_emscriptenevents.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 20 Apr 2017 13:00:54 -0400
changeset 10976 1da5502cf8ea
parent 10895 fd63ed9b0746
child 11002 ba0ce5b958d2
permissions -rw-r--r--
emscripten: keep track of pointer lock losses and maybe regrab pointer later.

If an Emscripten app is in relative mouse mode and the user presses Escape
(or whatever is appropriate), then the pointer lock is broken by the browser.

This keeps track of those losses, and next time the user presses a mouse
button down on the canvas, if the app is still meant to be in relative mouse
mode, we will attempt to regrab the pointer.

This makes it much more seamless for things like first-person shooters, and
the app doesn't need any manual intervention.
     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 
    22 
    23 #include "../../SDL_internal.h"
    24 
    25 #if SDL_VIDEO_DRIVER_EMSCRIPTEN
    26 
    27 #include <emscripten/html5.h>
    28 
    29 #include "../../events/SDL_events_c.h"
    30 #include "../../events/SDL_keyboard_c.h"
    31 #include "../../events/SDL_touch_c.h"
    32 
    33 #include "SDL_emscriptenevents.h"
    34 #include "SDL_emscriptenvideo.h"
    35 
    36 #include "SDL_hints.h"
    37 
    38 #define FULLSCREEN_MASK ( SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN )
    39 
    40 /*
    41 .keyCode to scancode
    42 https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
    43 https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
    44 */
    45 static const SDL_Scancode emscripten_scancode_table[] = {
    46     /*  0 */    SDL_SCANCODE_UNKNOWN,
    47     /*  1 */    SDL_SCANCODE_UNKNOWN,
    48     /*  2 */    SDL_SCANCODE_UNKNOWN,
    49     /*  3 */    SDL_SCANCODE_CANCEL,
    50     /*  4 */    SDL_SCANCODE_UNKNOWN,
    51     /*  5 */    SDL_SCANCODE_UNKNOWN,
    52     /*  6 */    SDL_SCANCODE_HELP,
    53     /*  7 */    SDL_SCANCODE_UNKNOWN,
    54     /*  8 */    SDL_SCANCODE_BACKSPACE,
    55     /*  9 */    SDL_SCANCODE_TAB,
    56     /*  10 */   SDL_SCANCODE_UNKNOWN,
    57     /*  11 */   SDL_SCANCODE_UNKNOWN,
    58     /*  12 */   SDL_SCANCODE_UNKNOWN,
    59     /*  13 */   SDL_SCANCODE_RETURN,
    60     /*  14 */   SDL_SCANCODE_UNKNOWN,
    61     /*  15 */   SDL_SCANCODE_UNKNOWN,
    62     /*  16 */   SDL_SCANCODE_LSHIFT,
    63     /*  17 */   SDL_SCANCODE_LCTRL,
    64     /*  18 */   SDL_SCANCODE_LALT,
    65     /*  19 */   SDL_SCANCODE_PAUSE,
    66     /*  20 */   SDL_SCANCODE_CAPSLOCK,
    67     /*  21 */   SDL_SCANCODE_UNKNOWN,
    68     /*  22 */   SDL_SCANCODE_UNKNOWN,
    69     /*  23 */   SDL_SCANCODE_UNKNOWN,
    70     /*  24 */   SDL_SCANCODE_UNKNOWN,
    71     /*  25 */   SDL_SCANCODE_UNKNOWN,
    72     /*  26 */   SDL_SCANCODE_UNKNOWN,
    73     /*  27 */   SDL_SCANCODE_ESCAPE,
    74     /*  28 */   SDL_SCANCODE_UNKNOWN,
    75     /*  29 */   SDL_SCANCODE_UNKNOWN,
    76     /*  30 */   SDL_SCANCODE_UNKNOWN,
    77     /*  31 */   SDL_SCANCODE_UNKNOWN,
    78     /*  32 */   SDL_SCANCODE_SPACE,
    79     /*  33 */   SDL_SCANCODE_PAGEUP,
    80     /*  34 */   SDL_SCANCODE_PAGEDOWN,
    81     /*  35 */   SDL_SCANCODE_END,
    82     /*  36 */   SDL_SCANCODE_HOME,
    83     /*  37 */   SDL_SCANCODE_LEFT,
    84     /*  38 */   SDL_SCANCODE_UP,
    85     /*  39 */   SDL_SCANCODE_RIGHT,
    86     /*  40 */   SDL_SCANCODE_DOWN,
    87     /*  41 */   SDL_SCANCODE_UNKNOWN,
    88     /*  42 */   SDL_SCANCODE_UNKNOWN,
    89     /*  43 */   SDL_SCANCODE_UNKNOWN,
    90     /*  44 */   SDL_SCANCODE_UNKNOWN,
    91     /*  45 */   SDL_SCANCODE_INSERT,
    92     /*  46 */   SDL_SCANCODE_DELETE,
    93     /*  47 */   SDL_SCANCODE_UNKNOWN,
    94     /*  48 */   SDL_SCANCODE_0,
    95     /*  49 */   SDL_SCANCODE_1,
    96     /*  50 */   SDL_SCANCODE_2,
    97     /*  51 */   SDL_SCANCODE_3,
    98     /*  52 */   SDL_SCANCODE_4,
    99     /*  53 */   SDL_SCANCODE_5,
   100     /*  54 */   SDL_SCANCODE_6,
   101     /*  55 */   SDL_SCANCODE_7,
   102     /*  56 */   SDL_SCANCODE_8,
   103     /*  57 */   SDL_SCANCODE_9,
   104     /*  58 */   SDL_SCANCODE_UNKNOWN,
   105     /*  59 */   SDL_SCANCODE_SEMICOLON,
   106     /*  60 */   SDL_SCANCODE_UNKNOWN,
   107     /*  61 */   SDL_SCANCODE_EQUALS,
   108     /*  62 */   SDL_SCANCODE_UNKNOWN,
   109     /*  63 */   SDL_SCANCODE_UNKNOWN,
   110     /*  64 */   SDL_SCANCODE_UNKNOWN,
   111     /*  65 */   SDL_SCANCODE_A,
   112     /*  66 */   SDL_SCANCODE_B,
   113     /*  67 */   SDL_SCANCODE_C,
   114     /*  68 */   SDL_SCANCODE_D,
   115     /*  69 */   SDL_SCANCODE_E,
   116     /*  70 */   SDL_SCANCODE_F,
   117     /*  71 */   SDL_SCANCODE_G,
   118     /*  72 */   SDL_SCANCODE_H,
   119     /*  73 */   SDL_SCANCODE_I,
   120     /*  74 */   SDL_SCANCODE_J,
   121     /*  75 */   SDL_SCANCODE_K,
   122     /*  76 */   SDL_SCANCODE_L,
   123     /*  77 */   SDL_SCANCODE_M,
   124     /*  78 */   SDL_SCANCODE_N,
   125     /*  79 */   SDL_SCANCODE_O,
   126     /*  80 */   SDL_SCANCODE_P,
   127     /*  81 */   SDL_SCANCODE_Q,
   128     /*  82 */   SDL_SCANCODE_R,
   129     /*  83 */   SDL_SCANCODE_S,
   130     /*  84 */   SDL_SCANCODE_T,
   131     /*  85 */   SDL_SCANCODE_U,
   132     /*  86 */   SDL_SCANCODE_V,
   133     /*  87 */   SDL_SCANCODE_W,
   134     /*  88 */   SDL_SCANCODE_X,
   135     /*  89 */   SDL_SCANCODE_Y,
   136     /*  90 */   SDL_SCANCODE_Z,
   137     /*  91 */   SDL_SCANCODE_LGUI,
   138     /*  92 */   SDL_SCANCODE_UNKNOWN,
   139     /*  93 */   SDL_SCANCODE_APPLICATION,
   140     /*  94 */   SDL_SCANCODE_UNKNOWN,
   141     /*  95 */   SDL_SCANCODE_UNKNOWN,
   142     /*  96 */   SDL_SCANCODE_KP_0,
   143     /*  97 */   SDL_SCANCODE_KP_1,
   144     /*  98 */   SDL_SCANCODE_KP_2,
   145     /*  99 */   SDL_SCANCODE_KP_3,
   146     /* 100 */   SDL_SCANCODE_KP_4,
   147     /* 101 */   SDL_SCANCODE_KP_5,
   148     /* 102 */   SDL_SCANCODE_KP_6,
   149     /* 103 */   SDL_SCANCODE_KP_7,
   150     /* 104 */   SDL_SCANCODE_KP_8,
   151     /* 105 */   SDL_SCANCODE_KP_9,
   152     /* 106 */   SDL_SCANCODE_KP_MULTIPLY,
   153     /* 107 */   SDL_SCANCODE_KP_PLUS,
   154     /* 108 */   SDL_SCANCODE_UNKNOWN,
   155     /* 109 */   SDL_SCANCODE_KP_MINUS,
   156     /* 110 */   SDL_SCANCODE_KP_PERIOD,
   157     /* 111 */   SDL_SCANCODE_KP_DIVIDE,
   158     /* 112 */   SDL_SCANCODE_F1,
   159     /* 113 */   SDL_SCANCODE_F2,
   160     /* 114 */   SDL_SCANCODE_F3,
   161     /* 115 */   SDL_SCANCODE_F4,
   162     /* 116 */   SDL_SCANCODE_F5,
   163     /* 117 */   SDL_SCANCODE_F6,
   164     /* 118 */   SDL_SCANCODE_F7,
   165     /* 119 */   SDL_SCANCODE_F8,
   166     /* 120 */   SDL_SCANCODE_F9,
   167     /* 121 */   SDL_SCANCODE_F10,
   168     /* 122 */   SDL_SCANCODE_F11,
   169     /* 123 */   SDL_SCANCODE_F12,
   170     /* 124 */   SDL_SCANCODE_F13,
   171     /* 125 */   SDL_SCANCODE_F14,
   172     /* 126 */   SDL_SCANCODE_F15,
   173     /* 127 */   SDL_SCANCODE_F16,
   174     /* 128 */   SDL_SCANCODE_F17,
   175     /* 129 */   SDL_SCANCODE_F18,
   176     /* 130 */   SDL_SCANCODE_F19,
   177     /* 131 */   SDL_SCANCODE_F20,
   178     /* 132 */   SDL_SCANCODE_F21,
   179     /* 133 */   SDL_SCANCODE_F22,
   180     /* 134 */   SDL_SCANCODE_F23,
   181     /* 135 */   SDL_SCANCODE_F24,
   182     /* 136 */   SDL_SCANCODE_UNKNOWN,
   183     /* 137 */   SDL_SCANCODE_UNKNOWN,
   184     /* 138 */   SDL_SCANCODE_UNKNOWN,
   185     /* 139 */   SDL_SCANCODE_UNKNOWN,
   186     /* 140 */   SDL_SCANCODE_UNKNOWN,
   187     /* 141 */   SDL_SCANCODE_UNKNOWN,
   188     /* 142 */   SDL_SCANCODE_UNKNOWN,
   189     /* 143 */   SDL_SCANCODE_UNKNOWN,
   190     /* 144 */   SDL_SCANCODE_NUMLOCKCLEAR,
   191     /* 145 */   SDL_SCANCODE_SCROLLLOCK,
   192     /* 146 */   SDL_SCANCODE_UNKNOWN,
   193     /* 147 */   SDL_SCANCODE_UNKNOWN,
   194     /* 148 */   SDL_SCANCODE_UNKNOWN,
   195     /* 149 */   SDL_SCANCODE_UNKNOWN,
   196     /* 150 */   SDL_SCANCODE_UNKNOWN,
   197     /* 151 */   SDL_SCANCODE_UNKNOWN,
   198     /* 152 */   SDL_SCANCODE_UNKNOWN,
   199     /* 153 */   SDL_SCANCODE_UNKNOWN,
   200     /* 154 */   SDL_SCANCODE_UNKNOWN,
   201     /* 155 */   SDL_SCANCODE_UNKNOWN,
   202     /* 156 */   SDL_SCANCODE_UNKNOWN,
   203     /* 157 */   SDL_SCANCODE_UNKNOWN,
   204     /* 158 */   SDL_SCANCODE_UNKNOWN,
   205     /* 159 */   SDL_SCANCODE_UNKNOWN,
   206     /* 160 */   SDL_SCANCODE_UNKNOWN,
   207     /* 161 */   SDL_SCANCODE_UNKNOWN,
   208     /* 162 */   SDL_SCANCODE_UNKNOWN,
   209     /* 163 */   SDL_SCANCODE_UNKNOWN,
   210     /* 164 */   SDL_SCANCODE_UNKNOWN,
   211     /* 165 */   SDL_SCANCODE_UNKNOWN,
   212     /* 166 */   SDL_SCANCODE_UNKNOWN,
   213     /* 167 */   SDL_SCANCODE_UNKNOWN,
   214     /* 168 */   SDL_SCANCODE_UNKNOWN,
   215     /* 169 */   SDL_SCANCODE_UNKNOWN,
   216     /* 170 */   SDL_SCANCODE_UNKNOWN,
   217     /* 171 */   SDL_SCANCODE_UNKNOWN,
   218     /* 172 */   SDL_SCANCODE_UNKNOWN,
   219     /* 173 */   SDL_SCANCODE_MINUS, /*FX*/
   220     /* 174 */   SDL_SCANCODE_VOLUMEDOWN, /*IE, Chrome*/
   221     /* 175 */   SDL_SCANCODE_VOLUMEUP, /*IE, Chrome*/
   222     /* 176 */   SDL_SCANCODE_AUDIONEXT, /*IE, Chrome*/
   223     /* 177 */   SDL_SCANCODE_AUDIOPREV, /*IE, Chrome*/
   224     /* 178 */   SDL_SCANCODE_UNKNOWN,
   225     /* 179 */   SDL_SCANCODE_AUDIOPLAY, /*IE, Chrome*/
   226     /* 180 */   SDL_SCANCODE_UNKNOWN,
   227     /* 181 */   SDL_SCANCODE_AUDIOMUTE, /*FX*/
   228     /* 182 */   SDL_SCANCODE_VOLUMEDOWN, /*FX*/
   229     /* 183 */   SDL_SCANCODE_VOLUMEUP, /*FX*/
   230     /* 184 */   SDL_SCANCODE_UNKNOWN,
   231     /* 185 */   SDL_SCANCODE_UNKNOWN,
   232     /* 186 */   SDL_SCANCODE_SEMICOLON, /*IE, Chrome, D3E legacy*/
   233     /* 187 */   SDL_SCANCODE_EQUALS, /*IE, Chrome, D3E legacy*/
   234     /* 188 */   SDL_SCANCODE_COMMA,
   235     /* 189 */   SDL_SCANCODE_MINUS, /*IE, Chrome, D3E legacy*/
   236     /* 190 */   SDL_SCANCODE_PERIOD,
   237     /* 191 */   SDL_SCANCODE_SLASH,
   238     /* 192 */   SDL_SCANCODE_GRAVE, /*FX, D3E legacy (SDL_SCANCODE_APOSTROPHE in IE/Chrome)*/
   239     /* 193 */   SDL_SCANCODE_UNKNOWN,
   240     /* 194 */   SDL_SCANCODE_UNKNOWN,
   241     /* 195 */   SDL_SCANCODE_UNKNOWN,
   242     /* 196 */   SDL_SCANCODE_UNKNOWN,
   243     /* 197 */   SDL_SCANCODE_UNKNOWN,
   244     /* 198 */   SDL_SCANCODE_UNKNOWN,
   245     /* 199 */   SDL_SCANCODE_UNKNOWN,
   246     /* 200 */   SDL_SCANCODE_UNKNOWN,
   247     /* 201 */   SDL_SCANCODE_UNKNOWN,
   248     /* 202 */   SDL_SCANCODE_UNKNOWN,
   249     /* 203 */   SDL_SCANCODE_UNKNOWN,
   250     /* 204 */   SDL_SCANCODE_UNKNOWN,
   251     /* 205 */   SDL_SCANCODE_UNKNOWN,
   252     /* 206 */   SDL_SCANCODE_UNKNOWN,
   253     /* 207 */   SDL_SCANCODE_UNKNOWN,
   254     /* 208 */   SDL_SCANCODE_UNKNOWN,
   255     /* 209 */   SDL_SCANCODE_UNKNOWN,
   256     /* 210 */   SDL_SCANCODE_UNKNOWN,
   257     /* 211 */   SDL_SCANCODE_UNKNOWN,
   258     /* 212 */   SDL_SCANCODE_UNKNOWN,
   259     /* 213 */   SDL_SCANCODE_UNKNOWN,
   260     /* 214 */   SDL_SCANCODE_UNKNOWN,
   261     /* 215 */   SDL_SCANCODE_UNKNOWN,
   262     /* 216 */   SDL_SCANCODE_UNKNOWN,
   263     /* 217 */   SDL_SCANCODE_UNKNOWN,
   264     /* 218 */   SDL_SCANCODE_UNKNOWN,
   265     /* 219 */   SDL_SCANCODE_LEFTBRACKET,
   266     /* 220 */   SDL_SCANCODE_BACKSLASH,
   267     /* 221 */   SDL_SCANCODE_RIGHTBRACKET,
   268     /* 222 */   SDL_SCANCODE_APOSTROPHE, /*FX, D3E legacy*/
   269 };
   270 
   271 
   272 /* "borrowed" from SDL_windowsevents.c */
   273 int
   274 Emscripten_ConvertUTF32toUTF8(Uint32 codepoint, char * text)
   275 {
   276     if (codepoint <= 0x7F) {
   277         text[0] = (char) codepoint;
   278         text[1] = '\0';
   279     } else if (codepoint <= 0x7FF) {
   280         text[0] = 0xC0 | (char) ((codepoint >> 6) & 0x1F);
   281         text[1] = 0x80 | (char) (codepoint & 0x3F);
   282         text[2] = '\0';
   283     } else if (codepoint <= 0xFFFF) {
   284         text[0] = 0xE0 | (char) ((codepoint >> 12) & 0x0F);
   285         text[1] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
   286         text[2] = 0x80 | (char) (codepoint & 0x3F);
   287         text[3] = '\0';
   288     } else if (codepoint <= 0x10FFFF) {
   289         text[0] = 0xF0 | (char) ((codepoint >> 18) & 0x0F);
   290         text[1] = 0x80 | (char) ((codepoint >> 12) & 0x3F);
   291         text[2] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
   292         text[3] = 0x80 | (char) (codepoint & 0x3F);
   293         text[4] = '\0';
   294     } else {
   295         return SDL_FALSE;
   296     }
   297     return SDL_TRUE;
   298 }
   299 
   300 static EM_BOOL
   301 Emscripten_HandlePointerLockChange(int eventType, const EmscriptenPointerlockChangeEvent *changeEvent, void *userData)
   302 {
   303     SDL_WindowData *window_data = (SDL_WindowData *) userData;
   304     /* keep track of lock losses, so we can regrab if/when appropriate. */
   305     window_data->has_pointer_lock = changeEvent->isActive;
   306     return 0;
   307 }
   308 
   309 
   310 EM_BOOL
   311 Emscripten_HandleMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
   312 {
   313     SDL_WindowData *window_data = userData;
   314     const int isPointerLocked = window_data->has_pointer_lock;
   315     int mx, my;
   316     static double residualx = 0, residualy = 0;
   317 
   318     /* rescale (in case canvas is being scaled)*/
   319     double client_w, client_h, xscale, yscale;
   320     emscripten_get_element_css_size(NULL, &client_w, &client_h);
   321     xscale = window_data->window->w / client_w;
   322     yscale = window_data->window->h / client_h;
   323 
   324     if (isPointerLocked) {
   325         residualx += mouseEvent->movementX * xscale;
   326         residualy += mouseEvent->movementY * yscale;
   327         /* Let slow sub-pixel motion accumulate. Don't lose it. */
   328         mx = residualx;
   329         residualx -= mx;
   330         my = residualy;
   331         residualy -= my;
   332     } else {
   333         mx = mouseEvent->canvasX * xscale;
   334         my = mouseEvent->canvasY * yscale;
   335     }
   336 
   337     SDL_SendMouseMotion(window_data->window, 0, isPointerLocked, mx, my);
   338     return 0;
   339 }
   340 
   341 EM_BOOL
   342 Emscripten_HandleMouseButton(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
   343 {
   344     SDL_WindowData *window_data = userData;
   345     Uint8 sdl_button;
   346     Uint8 sdl_button_state;
   347     SDL_EventType sdl_event_type;
   348 
   349     switch (mouseEvent->button) {
   350         case 0:
   351             sdl_button = SDL_BUTTON_LEFT;
   352             break;
   353         case 1:
   354             sdl_button = SDL_BUTTON_MIDDLE;
   355             break;
   356         case 2:
   357             sdl_button = SDL_BUTTON_RIGHT;
   358             break;
   359         default:
   360             return 0;
   361     }
   362 
   363     if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) {
   364         if (SDL_GetMouse()->relative_mode && !window_data->has_pointer_lock) {
   365             emscripten_request_pointerlock(NULL, 0);  /* try to regrab lost pointer lock. */
   366         }
   367         sdl_button_state = SDL_PRESSED;
   368         sdl_event_type = SDL_MOUSEBUTTONDOWN;
   369     } else {
   370         sdl_button_state = SDL_RELEASED;
   371         sdl_event_type = SDL_MOUSEBUTTONUP;
   372     }
   373     SDL_SendMouseButton(window_data->window, 0, sdl_button_state, sdl_button);
   374     return SDL_GetEventState(sdl_event_type) == SDL_ENABLE;
   375 }
   376 
   377 EM_BOOL
   378 Emscripten_HandleMouseFocus(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
   379 {
   380     SDL_WindowData *window_data = userData;
   381 
   382     int mx = mouseEvent->canvasX, my = mouseEvent->canvasY;
   383     const int isPointerLocked = window_data->has_pointer_lock;
   384 
   385     if (!isPointerLocked) {
   386         /* rescale (in case canvas is being scaled)*/
   387         double client_w, client_h;
   388         emscripten_get_element_css_size(NULL, &client_w, &client_h);
   389 
   390         mx = mx * (window_data->window->w / client_w);
   391         my = my * (window_data->window->h / client_h);
   392         SDL_SendMouseMotion(window_data->window, 0, isPointerLocked, mx, my);
   393     }
   394 
   395     SDL_SetMouseFocus(eventType == EMSCRIPTEN_EVENT_MOUSEENTER ? window_data->window : NULL);
   396     return SDL_GetEventState(SDL_WINDOWEVENT) == SDL_ENABLE;
   397 }
   398 
   399 EM_BOOL
   400 Emscripten_HandleWheel(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
   401 {
   402     SDL_WindowData *window_data = userData;
   403     SDL_SendMouseWheel(window_data->window, 0, wheelEvent->deltaX, -wheelEvent->deltaY, SDL_MOUSEWHEEL_NORMAL);
   404     return SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE;
   405 }
   406 
   407 EM_BOOL
   408 Emscripten_HandleFocus(int eventType, const EmscriptenFocusEvent *wheelEvent, void *userData)
   409 {
   410     SDL_WindowData *window_data = userData;
   411     /* If the user switches away while keys are pressed (such as
   412      * via Alt+Tab), key release events won't be received. */
   413     if (eventType == EMSCRIPTEN_EVENT_BLUR) {
   414         SDL_ResetKeyboard();
   415     }
   416 
   417 
   418     SDL_SendWindowEvent(window_data->window, eventType == EMSCRIPTEN_EVENT_FOCUS ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
   419     return SDL_GetEventState(SDL_WINDOWEVENT) == SDL_ENABLE;
   420 }
   421 
   422 EM_BOOL
   423 Emscripten_HandleTouch(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
   424 {
   425     SDL_WindowData *window_data = userData;
   426     int i;
   427     double client_w, client_h;
   428     int preventDefault = 0;
   429 
   430     SDL_TouchID deviceId = 1;
   431     if (SDL_AddTouch(deviceId, "") < 0) {
   432          return 0;
   433     }
   434 
   435     emscripten_get_element_css_size(NULL, &client_w, &client_h);
   436 
   437     for (i = 0; i < touchEvent->numTouches; i++) {
   438         SDL_FingerID id;
   439         float x, y;
   440 
   441         if (!touchEvent->touches[i].isChanged)
   442             continue;
   443 
   444         id = touchEvent->touches[i].identifier;
   445         x = touchEvent->touches[i].canvasX / client_w;
   446         y = touchEvent->touches[i].canvasY / client_h;
   447 
   448         if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) {
   449             if (!window_data->finger_touching) {
   450                 window_data->finger_touching = SDL_TRUE;
   451                 window_data->first_finger = id;
   452                 SDL_SendMouseMotion(window_data->window, SDL_TOUCH_MOUSEID, 0, x, y);
   453                 SDL_SendMouseButton(window_data->window, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT);
   454             }
   455             SDL_SendTouch(deviceId, id, SDL_TRUE, x, y, 1.0f);
   456 
   457             if (!preventDefault && SDL_GetEventState(SDL_FINGERDOWN) == SDL_ENABLE) {
   458                 preventDefault = 1;
   459             }
   460         } else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) {
   461             if ((window_data->finger_touching) && (window_data->first_finger == id)) {
   462                 SDL_SendMouseMotion(window_data->window, SDL_TOUCH_MOUSEID, 0, x, y);
   463             }
   464             SDL_SendTouchMotion(deviceId, id, x, y, 1.0f);
   465 
   466             if (!preventDefault && SDL_GetEventState(SDL_FINGERMOTION) == SDL_ENABLE) {
   467                 preventDefault = 1;
   468             }
   469         } else {
   470             if ((window_data->finger_touching) && (window_data->first_finger == id)) {
   471                 SDL_SendMouseButton(window_data->window, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT);
   472                 window_data->finger_touching = SDL_FALSE;
   473             }
   474             SDL_SendTouch(deviceId, id, SDL_FALSE, x, y, 1.0f);
   475 
   476             if (!preventDefault && SDL_GetEventState(SDL_FINGERUP) == SDL_ENABLE) {
   477                 preventDefault = 1;
   478             }
   479         }
   480     }
   481 
   482     return preventDefault;
   483 }
   484 
   485 EM_BOOL
   486 Emscripten_HandleKey(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
   487 {
   488     Uint32 scancode;
   489 
   490     /* .keyCode is deprecated, but still the most reliable way to get keys */
   491     if (keyEvent->keyCode < SDL_arraysize(emscripten_scancode_table)) {
   492         scancode = emscripten_scancode_table[keyEvent->keyCode];
   493 
   494         if (scancode != SDL_SCANCODE_UNKNOWN) {
   495 
   496             if (keyEvent->location == DOM_KEY_LOCATION_RIGHT) {
   497                 switch (scancode) {
   498                     case SDL_SCANCODE_LSHIFT:
   499                         scancode = SDL_SCANCODE_RSHIFT;
   500                         break;
   501                     case SDL_SCANCODE_LCTRL:
   502                         scancode = SDL_SCANCODE_RCTRL;
   503                         break;
   504                     case SDL_SCANCODE_LALT:
   505                         scancode = SDL_SCANCODE_RALT;
   506                         break;
   507                     case SDL_SCANCODE_LGUI:
   508                         scancode = SDL_SCANCODE_RGUI;
   509                         break;
   510                 }
   511             }
   512             SDL_SendKeyboardKey(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_PRESSED : SDL_RELEASED, scancode);
   513         }
   514     }
   515 
   516     SDL_bool prevent_default = SDL_GetEventState(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_KEYDOWN : SDL_KEYUP) == SDL_ENABLE;
   517 
   518     /* if TEXTINPUT events are enabled we can't prevent keydown or we won't get keypress
   519      * we need to ALWAYS prevent backspace and tab otherwise chrome takes action and does bad navigation UX
   520      */
   521     if (eventType == EMSCRIPTEN_EVENT_KEYDOWN && SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE && keyEvent->keyCode != 8 /* backspace */ && keyEvent->keyCode != 9 /* tab */)
   522         prevent_default = SDL_FALSE;
   523 
   524     return prevent_default;
   525 }
   526 
   527 EM_BOOL
   528 Emscripten_HandleKeyPress(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
   529 {
   530     char text[5];
   531     if (Emscripten_ConvertUTF32toUTF8(keyEvent->charCode, text)) {
   532         SDL_SendKeyboardText(text);
   533     }
   534     return SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE;
   535 }
   536 
   537 EM_BOOL
   538 Emscripten_HandleFullscreenChange(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData)
   539 {
   540     SDL_WindowData *window_data = userData;
   541     if(fullscreenChangeEvent->isFullscreen)
   542     {
   543         window_data->window->flags |= window_data->requested_fullscreen_mode;
   544 
   545         window_data->requested_fullscreen_mode = 0;
   546 
   547         if(!window_data->requested_fullscreen_mode)
   548             window_data->window->flags |= SDL_WINDOW_FULLSCREEN; /*we didn't reqest fullscreen*/
   549     }
   550     else
   551     {
   552         window_data->window->flags &= ~FULLSCREEN_MASK;
   553     }
   554 
   555     return 0;
   556 }
   557 
   558 EM_BOOL
   559 Emscripten_HandleResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
   560 {
   561     SDL_WindowData *window_data = userData;
   562 
   563     /* update pixel ratio */
   564     if (window_data->window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
   565         window_data->pixel_ratio = emscripten_get_device_pixel_ratio();
   566     }
   567 
   568     if(!(window_data->window->flags & FULLSCREEN_MASK))
   569     {
   570         /* this will only work if the canvas size is set through css */
   571         if(window_data->window->flags & SDL_WINDOW_RESIZABLE)
   572         {
   573             double w = window_data->window->w;
   574             double h = window_data->window->h;
   575 
   576             if(window_data->external_size) {
   577                 emscripten_get_element_css_size(NULL, &w, &h);
   578             }
   579 
   580             emscripten_set_canvas_size(w * window_data->pixel_ratio, h * window_data->pixel_ratio);
   581 
   582             /* set_canvas_size unsets this */
   583             if (!window_data->external_size && window_data->pixel_ratio != 1.0f) {
   584                 emscripten_set_element_css_size(NULL, w, h);
   585             }
   586 
   587             SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, w, h);
   588         }
   589     }
   590 
   591     return 0;
   592 }
   593 
   594 EM_BOOL
   595 Emscripten_HandleCanvasResize(int eventType, const void *reserved, void *userData)
   596 {
   597     /*this is used during fullscreen changes*/
   598     SDL_WindowData *window_data = userData;
   599 
   600     if(window_data->fullscreen_resize)
   601     {
   602         double css_w, css_h;
   603         emscripten_get_element_css_size(NULL, &css_w, &css_h);
   604         SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, css_w, css_h);
   605     }
   606 
   607     return 0;
   608 }
   609 
   610 EM_BOOL
   611 Emscripten_HandleVisibilityChange(int eventType, const EmscriptenVisibilityChangeEvent *visEvent, void *userData)
   612 {
   613     SDL_WindowData *window_data = userData;
   614     SDL_SendWindowEvent(window_data->window, visEvent->hidden ? SDL_WINDOWEVENT_HIDDEN : SDL_WINDOWEVENT_SHOWN, 0, 0);
   615     return 0;
   616 }
   617 
   618 void
   619 Emscripten_RegisterEventHandlers(SDL_WindowData *data)
   620 {
   621     /* There is only one window and that window is the canvas */
   622     emscripten_set_mousemove_callback("#canvas", data, 0, Emscripten_HandleMouseMove);
   623 
   624     emscripten_set_mousedown_callback("#canvas", data, 0, Emscripten_HandleMouseButton);
   625     emscripten_set_mouseup_callback("#document", data, 0, Emscripten_HandleMouseButton);
   626 
   627     emscripten_set_mouseenter_callback("#canvas", data, 0, Emscripten_HandleMouseFocus);
   628     emscripten_set_mouseleave_callback("#canvas", data, 0, Emscripten_HandleMouseFocus);
   629 
   630     emscripten_set_wheel_callback("#canvas", data, 0, Emscripten_HandleWheel);
   631 
   632     emscripten_set_focus_callback("#window", data, 0, Emscripten_HandleFocus);
   633     emscripten_set_blur_callback("#window", data, 0, Emscripten_HandleFocus);
   634 
   635     emscripten_set_touchstart_callback("#canvas", data, 0, Emscripten_HandleTouch);
   636     emscripten_set_touchend_callback("#canvas", data, 0, Emscripten_HandleTouch);
   637     emscripten_set_touchmove_callback("#canvas", data, 0, Emscripten_HandleTouch);
   638     emscripten_set_touchcancel_callback("#canvas", data, 0, Emscripten_HandleTouch);
   639 
   640     emscripten_set_pointerlockchange_callback(NULL, data, 0, Emscripten_HandlePointerLockChange);
   641 
   642     /* Keyboard events are awkward */
   643     const char *keyElement = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
   644     if (!keyElement) keyElement = "#window";
   645 
   646     emscripten_set_keydown_callback(keyElement, data, 0, Emscripten_HandleKey);
   647     emscripten_set_keyup_callback(keyElement, data, 0, Emscripten_HandleKey);
   648     emscripten_set_keypress_callback(keyElement, data, 0, Emscripten_HandleKeyPress);
   649 
   650     emscripten_set_fullscreenchange_callback("#document", data, 0, Emscripten_HandleFullscreenChange);
   651 
   652     emscripten_set_resize_callback("#window", data, 0, Emscripten_HandleResize);
   653 
   654     emscripten_set_visibilitychange_callback(data, 0, Emscripten_HandleVisibilityChange);
   655 }
   656 
   657 void
   658 Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
   659 {
   660     /* only works due to having one window */
   661     emscripten_set_mousemove_callback("#canvas", NULL, 0, NULL);
   662 
   663     emscripten_set_mousedown_callback("#canvas", NULL, 0, NULL);
   664     emscripten_set_mouseup_callback("#document", NULL, 0, NULL);
   665 
   666     emscripten_set_mouseenter_callback("#canvas", NULL, 0, NULL);
   667     emscripten_set_mouseleave_callback("#canvas", NULL, 0, NULL);
   668 
   669     emscripten_set_wheel_callback("#canvas", NULL, 0, NULL);
   670 
   671     emscripten_set_focus_callback("#window", NULL, 0, NULL);
   672     emscripten_set_blur_callback("#window", NULL, 0, NULL);
   673 
   674     emscripten_set_touchstart_callback("#canvas", NULL, 0, NULL);
   675     emscripten_set_touchend_callback("#canvas", NULL, 0, NULL);
   676     emscripten_set_touchmove_callback("#canvas", NULL, 0, NULL);
   677     emscripten_set_touchcancel_callback("#canvas", NULL, 0, NULL);
   678 
   679     const char *target = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
   680     if (!target) {
   681         target = "#window";
   682     }
   683 
   684     emscripten_set_keydown_callback(target, NULL, 0, NULL);
   685     emscripten_set_keyup_callback(target, NULL, 0, NULL);
   686 
   687     emscripten_set_keypress_callback(target, NULL, 0, NULL);
   688 
   689     emscripten_set_fullscreenchange_callback("#document", NULL, 0, NULL);
   690 
   691     emscripten_set_resize_callback("#window", NULL, 0, NULL);
   692 
   693     emscripten_set_visibilitychange_callback(NULL, 0, NULL);
   694 }
   695 
   696 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
   697 
   698 /* vi: set ts=4 sw=4 expandtab: */