src/video/emscripten/SDL_emscriptenevents.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 18 Jun 2019 16:53:49 -0400
changeset 12883 72ab279ffcaa
parent 12727 7bbbfe01d943
permissions -rw-r--r--
cocoa: Patched to compile and also handle possible malloc failure.
     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(window_data->canvas_id, &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->targetX * xscale;
   334         my = mouseEvent->targetY * 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(window_data->canvas_id, &css_w, &css_h);
   378     if (mouseEvent->targetX < 0 || mouseEvent->targetX >= css_w ||
   379         mouseEvent->targetY < 0 || mouseEvent->targetY >= 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->targetX, my = mouseEvent->targetY;
   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(window_data->canvas_id, &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 = (SDL_WindowData *) 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(window_data->canvas_id, &client_w, &client_h);
   445 
   446     for (i = 0; i < touchEvent->numTouches; i++) {
   447         SDL_FingerID id;
   448         float x, y;
   449 
   450         if (!touchEvent->touches[i].isChanged)
   451             continue;
   452 
   453         id = touchEvent->touches[i].identifier;
   454         x = touchEvent->touches[i].targetX / client_w;
   455         y = touchEvent->touches[i].targetY / client_h;
   456 
   457         if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) {
   458             SDL_SendTouch(deviceId, id, SDL_TRUE, x, y, 1.0f);
   459 
   460             /* disable browser scrolling/pinch-to-zoom if app handles touch events */
   461             if (!preventDefault && SDL_GetEventState(SDL_FINGERDOWN) == SDL_ENABLE) {
   462                 preventDefault = 1;
   463             }
   464         } else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) {
   465             SDL_SendTouchMotion(deviceId, id, x, y, 1.0f);
   466         } else {
   467             SDL_SendTouch(deviceId, id, SDL_FALSE, x, y, 1.0f);
   468 
   469             /* block browser's simulated mousedown/mouseup on touchscreen devices */
   470             preventDefault = 1;
   471         }
   472     }
   473 
   474     return preventDefault;
   475 }
   476 
   477 static EM_BOOL
   478 Emscripten_HandleKey(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
   479 {
   480     Uint32 scancode;
   481     SDL_bool prevent_default;
   482     SDL_bool is_nav_key;
   483 
   484     /* .keyCode is deprecated, but still the most reliable way to get keys */
   485     if (keyEvent->keyCode < SDL_arraysize(emscripten_scancode_table)) {
   486         scancode = emscripten_scancode_table[keyEvent->keyCode];
   487 
   488         if (scancode != SDL_SCANCODE_UNKNOWN) {
   489 
   490             if (keyEvent->location == DOM_KEY_LOCATION_RIGHT) {
   491                 switch (scancode) {
   492                     case SDL_SCANCODE_LSHIFT:
   493                         scancode = SDL_SCANCODE_RSHIFT;
   494                         break;
   495                     case SDL_SCANCODE_LCTRL:
   496                         scancode = SDL_SCANCODE_RCTRL;
   497                         break;
   498                     case SDL_SCANCODE_LALT:
   499                         scancode = SDL_SCANCODE_RALT;
   500                         break;
   501                     case SDL_SCANCODE_LGUI:
   502                         scancode = SDL_SCANCODE_RGUI;
   503                         break;
   504                 }
   505             }
   506             SDL_SendKeyboardKey(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_PRESSED : SDL_RELEASED, scancode);
   507         }
   508     }
   509 
   510     prevent_default = SDL_GetEventState(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_KEYDOWN : SDL_KEYUP) == SDL_ENABLE;
   511 
   512     /* if TEXTINPUT events are enabled we can't prevent keydown or we won't get keypress
   513      * we need to ALWAYS prevent backspace and tab otherwise chrome takes action and does bad navigation UX
   514      */
   515     is_nav_key = keyEvent->keyCode == 8 /* backspace */ ||
   516                  keyEvent->keyCode == 9 /* tab */ ||
   517                  keyEvent->keyCode == 37 /* left */ ||
   518                  keyEvent->keyCode == 38 /* up */ ||
   519                  keyEvent->keyCode == 39 /* right */ ||
   520                  keyEvent->keyCode == 40 /* down */;
   521 
   522     if (eventType == EMSCRIPTEN_EVENT_KEYDOWN && SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE && !is_nav_key)
   523         prevent_default = SDL_FALSE;
   524 
   525     return prevent_default;
   526 }
   527 
   528 static EM_BOOL
   529 Emscripten_HandleKeyPress(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
   530 {
   531     char text[5];
   532     if (Emscripten_ConvertUTF32toUTF8(keyEvent->charCode, text)) {
   533         SDL_SendKeyboardText(text);
   534     }
   535     return SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE;
   536 }
   537 
   538 static EM_BOOL
   539 Emscripten_HandleFullscreenChange(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData)
   540 {
   541     SDL_WindowData *window_data = userData;
   542     SDL_VideoDisplay *display;
   543 
   544     if(fullscreenChangeEvent->isFullscreen)
   545     {
   546         window_data->window->flags |= window_data->requested_fullscreen_mode;
   547 
   548         window_data->requested_fullscreen_mode = 0;
   549 
   550         if(!window_data->requested_fullscreen_mode)
   551             window_data->window->flags |= SDL_WINDOW_FULLSCREEN; /*we didn't reqest fullscreen*/
   552     }
   553     else
   554     {
   555         window_data->window->flags &= ~FULLSCREEN_MASK;
   556 
   557         /* reset fullscreen window if the browser left fullscreen */
   558         display = SDL_GetDisplayForWindow(window_data->window);
   559 
   560         if (display->fullscreen_window == window_data->window) {
   561             display->fullscreen_window = NULL;
   562         }
   563     }
   564 
   565     return 0;
   566 }
   567 
   568 static EM_BOOL
   569 Emscripten_HandleResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
   570 {
   571     SDL_WindowData *window_data = userData;
   572     SDL_bool force = SDL_FALSE;
   573 
   574     /* update pixel ratio */
   575     if (window_data->window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
   576         if (window_data->pixel_ratio != emscripten_get_device_pixel_ratio()) {
   577             window_data->pixel_ratio = emscripten_get_device_pixel_ratio();
   578             force = SDL_TRUE;
   579         }
   580     }
   581 
   582     if(!(window_data->window->flags & FULLSCREEN_MASK))
   583     {
   584         /* this will only work if the canvas size is set through css */
   585         if(window_data->window->flags & SDL_WINDOW_RESIZABLE)
   586         {
   587             double w = window_data->window->w;
   588             double h = window_data->window->h;
   589 
   590             if(window_data->external_size) {
   591                 emscripten_get_element_css_size(window_data->canvas_id, &w, &h);
   592             }
   593 
   594             emscripten_set_canvas_element_size(window_data->canvas_id, w * window_data->pixel_ratio, h * window_data->pixel_ratio);
   595 
   596             /* set_canvas_size unsets this */
   597             if (!window_data->external_size && window_data->pixel_ratio != 1.0f) {
   598                 emscripten_set_element_css_size(window_data->canvas_id, w, h);
   599             }
   600 
   601             if (force) {
   602                /* force the event to trigger, so pixel ratio changes can be handled */
   603                window_data->window->w = 0;
   604                window_data->window->h = 0;
   605             }
   606 
   607             SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, w, h);
   608         }
   609     }
   610 
   611     return 0;
   612 }
   613 
   614 EM_BOOL
   615 Emscripten_HandleCanvasResize(int eventType, const void *reserved, void *userData)
   616 {
   617     /*this is used during fullscreen changes*/
   618     SDL_WindowData *window_data = userData;
   619 
   620     if(window_data->fullscreen_resize)
   621     {
   622         double css_w, css_h;
   623         emscripten_get_element_css_size(window_data->canvas_id, &css_w, &css_h);
   624         SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, css_w, css_h);
   625     }
   626 
   627     return 0;
   628 }
   629 
   630 static EM_BOOL
   631 Emscripten_HandleVisibilityChange(int eventType, const EmscriptenVisibilityChangeEvent *visEvent, void *userData)
   632 {
   633     SDL_WindowData *window_data = userData;
   634     SDL_SendWindowEvent(window_data->window, visEvent->hidden ? SDL_WINDOWEVENT_HIDDEN : SDL_WINDOWEVENT_SHOWN, 0, 0);
   635     return 0;
   636 }
   637 
   638 void
   639 Emscripten_RegisterEventHandlers(SDL_WindowData *data)
   640 {
   641     const char *keyElement;
   642 
   643     /* There is only one window and that window is the canvas */
   644     emscripten_set_mousemove_callback(data->canvas_id, data, 0, Emscripten_HandleMouseMove);
   645 
   646     emscripten_set_mousedown_callback(data->canvas_id, data, 0, Emscripten_HandleMouseButton);
   647     emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandleMouseButton);
   648 
   649     emscripten_set_mouseenter_callback(data->canvas_id, data, 0, Emscripten_HandleMouseFocus);
   650     emscripten_set_mouseleave_callback(data->canvas_id, data, 0, Emscripten_HandleMouseFocus);
   651 
   652     emscripten_set_wheel_callback(data->canvas_id, data, 0, Emscripten_HandleWheel);
   653 
   654     emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleFocus);
   655     emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleFocus);
   656 
   657     emscripten_set_touchstart_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
   658     emscripten_set_touchend_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
   659     emscripten_set_touchmove_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
   660     emscripten_set_touchcancel_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
   661 
   662     emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandlePointerLockChange);
   663 
   664     /* Keyboard events are awkward */
   665     keyElement = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
   666     if (!keyElement) keyElement = EMSCRIPTEN_EVENT_TARGET_WINDOW;
   667 
   668     emscripten_set_keydown_callback(keyElement, data, 0, Emscripten_HandleKey);
   669     emscripten_set_keyup_callback(keyElement, data, 0, Emscripten_HandleKey);
   670     emscripten_set_keypress_callback(keyElement, data, 0, Emscripten_HandleKeyPress);
   671 
   672     emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandleFullscreenChange);
   673 
   674     emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleResize);
   675 
   676     emscripten_set_visibilitychange_callback(data, 0, Emscripten_HandleVisibilityChange);
   677 }
   678 
   679 void
   680 Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
   681 {
   682     const char *target;
   683 
   684     /* only works due to having one window */
   685     emscripten_set_mousemove_callback(data->canvas_id, NULL, 0, NULL);
   686 
   687     emscripten_set_mousedown_callback(data->canvas_id, NULL, 0, NULL);
   688     emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
   689 
   690     emscripten_set_mouseenter_callback(data->canvas_id, NULL, 0, NULL);
   691     emscripten_set_mouseleave_callback(data->canvas_id, NULL, 0, NULL);
   692 
   693     emscripten_set_wheel_callback(data->canvas_id, NULL, 0, NULL);
   694 
   695     emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
   696     emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
   697 
   698     emscripten_set_touchstart_callback(data->canvas_id, NULL, 0, NULL);
   699     emscripten_set_touchend_callback(data->canvas_id, NULL, 0, NULL);
   700     emscripten_set_touchmove_callback(data->canvas_id, NULL, 0, NULL);
   701     emscripten_set_touchcancel_callback(data->canvas_id, NULL, 0, NULL);
   702 
   703     emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
   704 
   705     target = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
   706     if (!target) {
   707         target = EMSCRIPTEN_EVENT_TARGET_WINDOW;
   708     }
   709 
   710     emscripten_set_keydown_callback(target, NULL, 0, NULL);
   711     emscripten_set_keyup_callback(target, NULL, 0, NULL);
   712     emscripten_set_keypress_callback(target, NULL, 0, NULL);
   713 
   714     emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
   715 
   716     emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
   717 
   718     emscripten_set_visibilitychange_callback(NULL, 0, NULL);
   719 }
   720 
   721 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
   722 
   723 /* vi: set ts=4 sw=4 expandtab: */