src/video/emscripten/SDL_emscriptenevents.c
author kichikuou <KichikuouChrome@gmail.com>
Tue, 29 Jan 2019 12:14:54 +0000
changeset 12573 296d155a8ce5
parent 12572 397aa57185c4
child 12574 109c919d61f6
permissions -rw-r--r--
Emscripten: Do not consume mouseup event outside of the canvas
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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 static 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 static 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 static 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     double css_w, css_h;
   349 
   350     switch (mouseEvent->button) {
   351         case 0:
   352             sdl_button = SDL_BUTTON_LEFT;
   353             break;
   354         case 1:
   355             sdl_button = SDL_BUTTON_MIDDLE;
   356             break;
   357         case 2:
   358             sdl_button = SDL_BUTTON_RIGHT;
   359             break;
   360         default:
   361             return 0;
   362     }
   363 
   364     if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) {
   365         if (SDL_GetMouse()->relative_mode && !window_data->has_pointer_lock) {
   366             emscripten_request_pointerlock(NULL, 0);  /* try to regrab lost pointer lock. */
   367         }
   368         sdl_button_state = SDL_PRESSED;
   369         sdl_event_type = SDL_MOUSEBUTTONDOWN;
   370     } else {
   371         sdl_button_state = SDL_RELEASED;
   372         sdl_event_type = SDL_MOUSEBUTTONUP;
   373     }
   374     SDL_SendMouseButton(window_data->window, 0, sdl_button_state, sdl_button);
   375 
   376     /* Do not consume the event if the mouse is outside of the canvas. */
   377     emscripten_get_element_css_size(NULL, &css_w, &css_h);
   378     if (mouseEvent->canvasX < 0 || mouseEvent->canvasX >= css_w ||
   379         mouseEvent->canvasY < 0 || mouseEvent->canvasY >= css_h) {
   380         return 0;
   381     }
   382 
   383     return SDL_GetEventState(sdl_event_type) == SDL_ENABLE;
   384 }
   385 
   386 static EM_BOOL
   387 Emscripten_HandleMouseFocus(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
   388 {
   389     SDL_WindowData *window_data = userData;
   390 
   391     int mx = mouseEvent->canvasX, my = mouseEvent->canvasY;
   392     const int isPointerLocked = window_data->has_pointer_lock;
   393 
   394     if (!isPointerLocked) {
   395         /* rescale (in case canvas is being scaled)*/
   396         double client_w, client_h;
   397         emscripten_get_element_css_size(NULL, &client_w, &client_h);
   398 
   399         mx = mx * (window_data->window->w / client_w);
   400         my = my * (window_data->window->h / client_h);
   401         SDL_SendMouseMotion(window_data->window, 0, isPointerLocked, mx, my);
   402     }
   403 
   404     SDL_SetMouseFocus(eventType == EMSCRIPTEN_EVENT_MOUSEENTER ? window_data->window : NULL);
   405     return SDL_GetEventState(SDL_WINDOWEVENT) == SDL_ENABLE;
   406 }
   407 
   408 static EM_BOOL
   409 Emscripten_HandleWheel(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
   410 {
   411     SDL_WindowData *window_data = userData;
   412     SDL_SendMouseWheel(window_data->window, 0, (float)wheelEvent->deltaX, (float)-wheelEvent->deltaY, SDL_MOUSEWHEEL_NORMAL);
   413     return SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE;
   414 }
   415 
   416 static EM_BOOL
   417 Emscripten_HandleFocus(int eventType, const EmscriptenFocusEvent *wheelEvent, void *userData)
   418 {
   419     SDL_WindowData *window_data = userData;
   420     /* If the user switches away while keys are pressed (such as
   421      * via Alt+Tab), key release events won't be received. */
   422     if (eventType == EMSCRIPTEN_EVENT_BLUR) {
   423         SDL_ResetKeyboard();
   424     }
   425 
   426 
   427     SDL_SendWindowEvent(window_data->window, eventType == EMSCRIPTEN_EVENT_FOCUS ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
   428     return SDL_GetEventState(SDL_WINDOWEVENT) == SDL_ENABLE;
   429 }
   430 
   431 static EM_BOOL
   432 Emscripten_HandleTouch(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
   433 {
   434     SDL_WindowData *window_data = userData;
   435     int i;
   436     double client_w, client_h;
   437     int preventDefault = 0;
   438 
   439     SDL_TouchID deviceId = 1;
   440     if (SDL_AddTouch(deviceId, SDL_TOUCH_DEVICE_DIRECT, "") < 0) {
   441          return 0;
   442     }
   443 
   444     emscripten_get_element_css_size(NULL, &client_w, &client_h);
   445 
   446     for (i = 0; i < touchEvent->numTouches; i++) {
   447         SDL_FingerID id;
   448         float x, y;
   449         int mx, my;
   450 
   451         if (!touchEvent->touches[i].isChanged)
   452             continue;
   453 
   454         id = touchEvent->touches[i].identifier;
   455         x = touchEvent->touches[i].canvasX / client_w;
   456         y = touchEvent->touches[i].canvasY / client_h;
   457 
   458         mx = x * window_data->window->w;
   459         my = y * window_data->window->h;
   460 
   461         if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) {
   462             if (!window_data->finger_touching) {
   463                 window_data->finger_touching = SDL_TRUE;
   464                 window_data->first_finger = id;
   465                 SDL_SendMouseMotion(window_data->window, SDL_TOUCH_MOUSEID, 0, mx, my);
   466                 SDL_SendMouseButton(window_data->window, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT);
   467             }
   468             SDL_SendTouch(deviceId, id, SDL_TRUE, x, y, 1.0f);
   469 
   470             /* disable browser scrolling/pinch-to-zoom if app handles touch events */
   471             if (!preventDefault && SDL_GetEventState(SDL_FINGERDOWN) == SDL_ENABLE) {
   472                 preventDefault = 1;
   473             }
   474         } else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) {
   475             if ((window_data->finger_touching) && (window_data->first_finger == id)) {
   476                 SDL_SendMouseMotion(window_data->window, SDL_TOUCH_MOUSEID, 0, mx, my);
   477             }
   478             SDL_SendTouchMotion(deviceId, id, x, y, 1.0f);
   479         } else {
   480             if ((window_data->finger_touching) && (window_data->first_finger == id)) {
   481                 SDL_SendMouseButton(window_data->window, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT);
   482                 window_data->finger_touching = SDL_FALSE;
   483             }
   484             SDL_SendTouch(deviceId, id, SDL_FALSE, x, y, 1.0f);
   485 
   486             /* block browser's simulated mousedown/mouseup on touchscreen devices */
   487             preventDefault = 1;
   488         }
   489     }
   490 
   491     return preventDefault;
   492 }
   493 
   494 static EM_BOOL
   495 Emscripten_HandleKey(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
   496 {
   497     Uint32 scancode;
   498     SDL_bool prevent_default;
   499     SDL_bool is_nav_key;
   500 
   501     /* .keyCode is deprecated, but still the most reliable way to get keys */
   502     if (keyEvent->keyCode < SDL_arraysize(emscripten_scancode_table)) {
   503         scancode = emscripten_scancode_table[keyEvent->keyCode];
   504 
   505         if (scancode != SDL_SCANCODE_UNKNOWN) {
   506 
   507             if (keyEvent->location == DOM_KEY_LOCATION_RIGHT) {
   508                 switch (scancode) {
   509                     case SDL_SCANCODE_LSHIFT:
   510                         scancode = SDL_SCANCODE_RSHIFT;
   511                         break;
   512                     case SDL_SCANCODE_LCTRL:
   513                         scancode = SDL_SCANCODE_RCTRL;
   514                         break;
   515                     case SDL_SCANCODE_LALT:
   516                         scancode = SDL_SCANCODE_RALT;
   517                         break;
   518                     case SDL_SCANCODE_LGUI:
   519                         scancode = SDL_SCANCODE_RGUI;
   520                         break;
   521                 }
   522             }
   523             SDL_SendKeyboardKey(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_PRESSED : SDL_RELEASED, scancode);
   524         }
   525     }
   526 
   527     prevent_default = SDL_GetEventState(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_KEYDOWN : SDL_KEYUP) == SDL_ENABLE;
   528 
   529     /* if TEXTINPUT events are enabled we can't prevent keydown or we won't get keypress
   530      * we need to ALWAYS prevent backspace and tab otherwise chrome takes action and does bad navigation UX
   531      */
   532     is_nav_key = keyEvent->keyCode == 8 /* backspace */ ||
   533                  keyEvent->keyCode == 9 /* tab */ ||
   534                  keyEvent->keyCode == 37 /* left */ ||
   535                  keyEvent->keyCode == 38 /* up */ ||
   536                  keyEvent->keyCode == 39 /* right */ ||
   537                  keyEvent->keyCode == 40 /* down */;
   538 
   539     if (eventType == EMSCRIPTEN_EVENT_KEYDOWN && SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE && !is_nav_key)
   540         prevent_default = SDL_FALSE;
   541 
   542     return prevent_default;
   543 }
   544 
   545 static EM_BOOL
   546 Emscripten_HandleKeyPress(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
   547 {
   548     char text[5];
   549     if (Emscripten_ConvertUTF32toUTF8(keyEvent->charCode, text)) {
   550         SDL_SendKeyboardText(text);
   551     }
   552     return SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE;
   553 }
   554 
   555 static EM_BOOL
   556 Emscripten_HandleFullscreenChange(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData)
   557 {
   558     SDL_WindowData *window_data = userData;
   559     SDL_VideoDisplay *display;
   560 
   561     if(fullscreenChangeEvent->isFullscreen)
   562     {
   563         window_data->window->flags |= window_data->requested_fullscreen_mode;
   564 
   565         window_data->requested_fullscreen_mode = 0;
   566 
   567         if(!window_data->requested_fullscreen_mode)
   568             window_data->window->flags |= SDL_WINDOW_FULLSCREEN; /*we didn't reqest fullscreen*/
   569     }
   570     else
   571     {
   572         window_data->window->flags &= ~FULLSCREEN_MASK;
   573 
   574         /* reset fullscreen window if the browser left fullscreen */
   575         display = SDL_GetDisplayForWindow(window_data->window);
   576 
   577         if (display->fullscreen_window == window_data->window) {
   578             display->fullscreen_window = NULL;
   579         }
   580     }
   581 
   582     return 0;
   583 }
   584 
   585 static EM_BOOL
   586 Emscripten_HandleResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
   587 {
   588     SDL_WindowData *window_data = userData;
   589 
   590     /* update pixel ratio */
   591     if (window_data->window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
   592         window_data->pixel_ratio = emscripten_get_device_pixel_ratio();
   593     }
   594 
   595     if(!(window_data->window->flags & FULLSCREEN_MASK))
   596     {
   597         /* this will only work if the canvas size is set through css */
   598         if(window_data->window->flags & SDL_WINDOW_RESIZABLE)
   599         {
   600             double w = window_data->window->w;
   601             double h = window_data->window->h;
   602 
   603             if(window_data->external_size) {
   604                 emscripten_get_element_css_size(NULL, &w, &h);
   605             }
   606 
   607             emscripten_set_canvas_size(w * window_data->pixel_ratio, h * window_data->pixel_ratio);
   608 
   609             /* set_canvas_size unsets this */
   610             if (!window_data->external_size && window_data->pixel_ratio != 1.0f) {
   611                 emscripten_set_element_css_size(NULL, w, h);
   612             }
   613 
   614             SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, w, h);
   615         }
   616     }
   617 
   618     return 0;
   619 }
   620 
   621 EM_BOOL
   622 Emscripten_HandleCanvasResize(int eventType, const void *reserved, void *userData)
   623 {
   624     /*this is used during fullscreen changes*/
   625     SDL_WindowData *window_data = userData;
   626 
   627     if(window_data->fullscreen_resize)
   628     {
   629         double css_w, css_h;
   630         emscripten_get_element_css_size(NULL, &css_w, &css_h);
   631         SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, css_w, css_h);
   632     }
   633 
   634     return 0;
   635 }
   636 
   637 static EM_BOOL
   638 Emscripten_HandleVisibilityChange(int eventType, const EmscriptenVisibilityChangeEvent *visEvent, void *userData)
   639 {
   640     SDL_WindowData *window_data = userData;
   641     SDL_SendWindowEvent(window_data->window, visEvent->hidden ? SDL_WINDOWEVENT_HIDDEN : SDL_WINDOWEVENT_SHOWN, 0, 0);
   642     return 0;
   643 }
   644 
   645 void
   646 Emscripten_RegisterEventHandlers(SDL_WindowData *data)
   647 {
   648     const char *keyElement;
   649 
   650     /* There is only one window and that window is the canvas */
   651     emscripten_set_mousemove_callback("#canvas", data, 0, Emscripten_HandleMouseMove);
   652 
   653     emscripten_set_mousedown_callback("#canvas", data, 0, Emscripten_HandleMouseButton);
   654     emscripten_set_mouseup_callback("#document", data, 0, Emscripten_HandleMouseButton);
   655 
   656     emscripten_set_mouseenter_callback("#canvas", data, 0, Emscripten_HandleMouseFocus);
   657     emscripten_set_mouseleave_callback("#canvas", data, 0, Emscripten_HandleMouseFocus);
   658 
   659     emscripten_set_wheel_callback("#canvas", data, 0, Emscripten_HandleWheel);
   660 
   661     emscripten_set_focus_callback("#window", data, 0, Emscripten_HandleFocus);
   662     emscripten_set_blur_callback("#window", data, 0, Emscripten_HandleFocus);
   663 
   664     emscripten_set_touchstart_callback("#canvas", data, 0, Emscripten_HandleTouch);
   665     emscripten_set_touchend_callback("#canvas", data, 0, Emscripten_HandleTouch);
   666     emscripten_set_touchmove_callback("#canvas", data, 0, Emscripten_HandleTouch);
   667     emscripten_set_touchcancel_callback("#canvas", data, 0, Emscripten_HandleTouch);
   668 
   669     emscripten_set_pointerlockchange_callback("#document", data, 0, Emscripten_HandlePointerLockChange);
   670 
   671     /* Keyboard events are awkward */
   672     keyElement = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
   673     if (!keyElement) keyElement = "#window";
   674 
   675     emscripten_set_keydown_callback(keyElement, data, 0, Emscripten_HandleKey);
   676     emscripten_set_keyup_callback(keyElement, data, 0, Emscripten_HandleKey);
   677     emscripten_set_keypress_callback(keyElement, data, 0, Emscripten_HandleKeyPress);
   678 
   679     emscripten_set_fullscreenchange_callback("#document", data, 0, Emscripten_HandleFullscreenChange);
   680 
   681     emscripten_set_resize_callback("#window", data, 0, Emscripten_HandleResize);
   682 
   683     emscripten_set_visibilitychange_callback(data, 0, Emscripten_HandleVisibilityChange);
   684 }
   685 
   686 void
   687 Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
   688 {
   689     const char *target;
   690 
   691     /* only works due to having one window */
   692     emscripten_set_mousemove_callback("#canvas", NULL, 0, NULL);
   693 
   694     emscripten_set_mousedown_callback("#canvas", NULL, 0, NULL);
   695     emscripten_set_mouseup_callback("#document", NULL, 0, NULL);
   696 
   697     emscripten_set_mouseenter_callback("#canvas", NULL, 0, NULL);
   698     emscripten_set_mouseleave_callback("#canvas", NULL, 0, NULL);
   699 
   700     emscripten_set_wheel_callback("#canvas", NULL, 0, NULL);
   701 
   702     emscripten_set_focus_callback("#window", NULL, 0, NULL);
   703     emscripten_set_blur_callback("#window", NULL, 0, NULL);
   704 
   705     emscripten_set_touchstart_callback("#canvas", NULL, 0, NULL);
   706     emscripten_set_touchend_callback("#canvas", NULL, 0, NULL);
   707     emscripten_set_touchmove_callback("#canvas", NULL, 0, NULL);
   708     emscripten_set_touchcancel_callback("#canvas", NULL, 0, NULL);
   709 
   710     emscripten_set_pointerlockchange_callback("#document", NULL, 0, NULL);
   711 
   712     target = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
   713     if (!target) {
   714         target = "#window";
   715     }
   716 
   717     emscripten_set_keydown_callback(target, NULL, 0, NULL);
   718     emscripten_set_keyup_callback(target, NULL, 0, NULL);
   719     emscripten_set_keypress_callback(target, NULL, 0, NULL);
   720 
   721     emscripten_set_fullscreenchange_callback("#document", NULL, 0, NULL);
   722 
   723     emscripten_set_resize_callback("#window", NULL, 0, NULL);
   724 
   725     emscripten_set_visibilitychange_callback(NULL, 0, NULL);
   726 }
   727 
   728 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
   729 
   730 /* vi: set ts=4 sw=4 expandtab: */