src/video/emscripten/SDL_emscriptenevents.c
author Charlie Birks <admin@daftgames.net>
Thu, 09 Apr 2020 15:57:12 +0100
changeset 13723 30ce6dd90b8f
parent 13722 3213634a13e5
permissions -rw-r--r--
emscripten: Add a few keyCode mappings for German keyboards

From @sy2002 in https://github.com/emscripten-ports/SDL2/issues/108
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2020 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_KP_5,
    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_NONUSBACKSLASH,
   107     /*  61 */   SDL_SCANCODE_EQUALS,
   108     /*  62 */   SDL_SCANCODE_UNKNOWN,
   109     /*  63 */   SDL_SCANCODE_MINUS,
   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_GRAVE,
   207     /* 161 */   SDL_SCANCODE_UNKNOWN,
   208     /* 162 */   SDL_SCANCODE_UNKNOWN,
   209     /* 163 */   SDL_SCANCODE_KP_HASH, /*KaiOS phone keypad*/
   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_KP_MULTIPLY, /*KaiOS phone keypad*/
   217     /* 171 */   SDL_SCANCODE_RIGHTBRACKET,
   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(window_data->canvas_id, 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, window_data->window, 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, window_data->window, x, y, 1.0f);
   466         } else {
   467             SDL_SendTouch(deviceId, id, window_data->window, 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 (keyEvent->keyCode == 0) {
   489             /* KaiOS Left Soft Key and Right Soft Key, they act as OK/Next/Menu and Cancel/Back/Clear */
   490             if (SDL_strncmp(keyEvent->key, "SoftLeft", 9) == 0) {
   491                 scancode = SDL_SCANCODE_AC_FORWARD;
   492             }
   493             if (SDL_strncmp(keyEvent->key, "SoftRight", 10) == 0) {
   494                 scancode = SDL_SCANCODE_AC_BACK;
   495             }
   496         }
   497 
   498         if (scancode != SDL_SCANCODE_UNKNOWN) {
   499 
   500             if (keyEvent->location == DOM_KEY_LOCATION_RIGHT) {
   501                 switch (scancode) {
   502                     case SDL_SCANCODE_LSHIFT:
   503                         scancode = SDL_SCANCODE_RSHIFT;
   504                         break;
   505                     case SDL_SCANCODE_LCTRL:
   506                         scancode = SDL_SCANCODE_RCTRL;
   507                         break;
   508                     case SDL_SCANCODE_LALT:
   509                         scancode = SDL_SCANCODE_RALT;
   510                         break;
   511                     case SDL_SCANCODE_LGUI:
   512                         scancode = SDL_SCANCODE_RGUI;
   513                         break;
   514                 }
   515             }
   516             else if (keyEvent->location == DOM_KEY_LOCATION_NUMPAD) {
   517                 switch (scancode) {
   518                     case SDL_SCANCODE_0:
   519                     case SDL_SCANCODE_INSERT:
   520                         scancode = SDL_SCANCODE_KP_0;
   521                         break;
   522                     case SDL_SCANCODE_1:
   523                     case SDL_SCANCODE_END:
   524                         scancode = SDL_SCANCODE_KP_1;
   525                         break;
   526                     case SDL_SCANCODE_2:
   527                     case SDL_SCANCODE_DOWN:
   528                         scancode = SDL_SCANCODE_KP_2;
   529                         break;
   530                     case SDL_SCANCODE_3:
   531                     case SDL_SCANCODE_PAGEDOWN:
   532                         scancode = SDL_SCANCODE_KP_3;
   533                         break;
   534                     case SDL_SCANCODE_4:
   535                     case SDL_SCANCODE_LEFT:
   536                         scancode = SDL_SCANCODE_KP_4;
   537                         break;
   538                     case SDL_SCANCODE_5:
   539                         scancode = SDL_SCANCODE_KP_5;
   540                         break;
   541                     case SDL_SCANCODE_6:
   542                     case SDL_SCANCODE_RIGHT:
   543                         scancode = SDL_SCANCODE_KP_6;
   544                         break;
   545                     case SDL_SCANCODE_7:
   546                     case SDL_SCANCODE_HOME:
   547                         scancode = SDL_SCANCODE_KP_7;
   548                         break;
   549                     case SDL_SCANCODE_8:
   550                     case SDL_SCANCODE_UP:
   551                         scancode = SDL_SCANCODE_KP_8;
   552                         break;
   553                     case SDL_SCANCODE_9:
   554                     case SDL_SCANCODE_PAGEUP:
   555                         scancode = SDL_SCANCODE_KP_9;
   556                         break;
   557                     case SDL_SCANCODE_RETURN:
   558                         scancode = SDL_SCANCODE_KP_ENTER;
   559                         break;
   560                     case SDL_SCANCODE_DELETE:
   561                         scancode = SDL_SCANCODE_KP_PERIOD;
   562                         break;
   563                 }
   564             }
   565             SDL_SendKeyboardKey(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_PRESSED : SDL_RELEASED, scancode);
   566         }
   567     }
   568 
   569     prevent_default = SDL_GetEventState(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_KEYDOWN : SDL_KEYUP) == SDL_ENABLE;
   570 
   571     /* if TEXTINPUT events are enabled we can't prevent keydown or we won't get keypress
   572      * we need to ALWAYS prevent backspace and tab otherwise chrome takes action and does bad navigation UX
   573      */
   574     is_nav_key = keyEvent->keyCode == 8 /* backspace */ ||
   575                  keyEvent->keyCode == 9 /* tab */ ||
   576                  keyEvent->keyCode == 37 /* left */ ||
   577                  keyEvent->keyCode == 38 /* up */ ||
   578                  keyEvent->keyCode == 39 /* right */ ||
   579                  keyEvent->keyCode == 40 /* down */;
   580 
   581     if (eventType == EMSCRIPTEN_EVENT_KEYDOWN && SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE && !is_nav_key)
   582         prevent_default = SDL_FALSE;
   583 
   584     return prevent_default;
   585 }
   586 
   587 static EM_BOOL
   588 Emscripten_HandleKeyPress(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
   589 {
   590     char text[5];
   591     if (Emscripten_ConvertUTF32toUTF8(keyEvent->charCode, text)) {
   592         SDL_SendKeyboardText(text);
   593     }
   594     return SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE;
   595 }
   596 
   597 static EM_BOOL
   598 Emscripten_HandleFullscreenChange(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData)
   599 {
   600     SDL_WindowData *window_data = userData;
   601     SDL_VideoDisplay *display;
   602 
   603     if(fullscreenChangeEvent->isFullscreen)
   604     {
   605         window_data->window->flags |= window_data->requested_fullscreen_mode;
   606 
   607         window_data->requested_fullscreen_mode = 0;
   608 
   609         if(!window_data->requested_fullscreen_mode)
   610             window_data->window->flags |= SDL_WINDOW_FULLSCREEN; /*we didn't request fullscreen*/
   611     }
   612     else
   613     {
   614         window_data->window->flags &= ~FULLSCREEN_MASK;
   615 
   616         /* reset fullscreen window if the browser left fullscreen */
   617         display = SDL_GetDisplayForWindow(window_data->window);
   618 
   619         if (display->fullscreen_window == window_data->window) {
   620             display->fullscreen_window = NULL;
   621         }
   622     }
   623 
   624     return 0;
   625 }
   626 
   627 static EM_BOOL
   628 Emscripten_HandleResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
   629 {
   630     SDL_WindowData *window_data = userData;
   631     SDL_bool force = SDL_FALSE;
   632 
   633     /* update pixel ratio */
   634     if (window_data->window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
   635         if (window_data->pixel_ratio != emscripten_get_device_pixel_ratio()) {
   636             window_data->pixel_ratio = emscripten_get_device_pixel_ratio();
   637             force = SDL_TRUE;
   638         }
   639     }
   640 
   641     if(!(window_data->window->flags & FULLSCREEN_MASK))
   642     {
   643         /* this will only work if the canvas size is set through css */
   644         if(window_data->window->flags & SDL_WINDOW_RESIZABLE)
   645         {
   646             double w = window_data->window->w;
   647             double h = window_data->window->h;
   648 
   649             if(window_data->external_size) {
   650                 emscripten_get_element_css_size(window_data->canvas_id, &w, &h);
   651             }
   652 
   653             emscripten_set_canvas_element_size(window_data->canvas_id, w * window_data->pixel_ratio, h * window_data->pixel_ratio);
   654 
   655             /* set_canvas_size unsets this */
   656             if (!window_data->external_size && window_data->pixel_ratio != 1.0f) {
   657                 emscripten_set_element_css_size(window_data->canvas_id, w, h);
   658             }
   659 
   660             if (force) {
   661                /* force the event to trigger, so pixel ratio changes can be handled */
   662                window_data->window->w = 0;
   663                window_data->window->h = 0;
   664             }
   665 
   666             SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, w, h);
   667         }
   668     }
   669 
   670     return 0;
   671 }
   672 
   673 EM_BOOL
   674 Emscripten_HandleCanvasResize(int eventType, const void *reserved, void *userData)
   675 {
   676     /*this is used during fullscreen changes*/
   677     SDL_WindowData *window_data = userData;
   678 
   679     if(window_data->fullscreen_resize)
   680     {
   681         double css_w, css_h;
   682         emscripten_get_element_css_size(window_data->canvas_id, &css_w, &css_h);
   683         SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, css_w, css_h);
   684     }
   685 
   686     return 0;
   687 }
   688 
   689 static EM_BOOL
   690 Emscripten_HandleVisibilityChange(int eventType, const EmscriptenVisibilityChangeEvent *visEvent, void *userData)
   691 {
   692     SDL_WindowData *window_data = userData;
   693     SDL_SendWindowEvent(window_data->window, visEvent->hidden ? SDL_WINDOWEVENT_HIDDEN : SDL_WINDOWEVENT_SHOWN, 0, 0);
   694     return 0;
   695 }
   696 
   697 static const char*
   698 Emscripten_HandleBeforeUnload(int eventType, const void *reserved, void *userData)
   699 {
   700     /* This event will need to be handled synchronously, e.g. using
   701        SDL_AddEventWatch, as the page is being closed *now*. */
   702     /* No need to send a SDL_QUIT, the app won't get control again. */
   703     SDL_SendAppEvent(SDL_APP_TERMINATING);
   704     return ""; /* don't trigger confirmation dialog */
   705 }
   706 
   707 void
   708 Emscripten_RegisterEventHandlers(SDL_WindowData *data)
   709 {
   710     const char *keyElement;
   711 
   712     /* There is only one window and that window is the canvas */
   713     emscripten_set_mousemove_callback(data->canvas_id, data, 0, Emscripten_HandleMouseMove);
   714 
   715     emscripten_set_mousedown_callback(data->canvas_id, data, 0, Emscripten_HandleMouseButton);
   716     emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandleMouseButton);
   717 
   718     emscripten_set_mouseenter_callback(data->canvas_id, data, 0, Emscripten_HandleMouseFocus);
   719     emscripten_set_mouseleave_callback(data->canvas_id, data, 0, Emscripten_HandleMouseFocus);
   720 
   721     emscripten_set_wheel_callback(data->canvas_id, data, 0, Emscripten_HandleWheel);
   722 
   723     emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleFocus);
   724     emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleFocus);
   725 
   726     emscripten_set_touchstart_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
   727     emscripten_set_touchend_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
   728     emscripten_set_touchmove_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
   729     emscripten_set_touchcancel_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
   730 
   731     emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandlePointerLockChange);
   732 
   733     /* Keyboard events are awkward */
   734     keyElement = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
   735     if (!keyElement) keyElement = EMSCRIPTEN_EVENT_TARGET_WINDOW;
   736 
   737     emscripten_set_keydown_callback(keyElement, data, 0, Emscripten_HandleKey);
   738     emscripten_set_keyup_callback(keyElement, data, 0, Emscripten_HandleKey);
   739     emscripten_set_keypress_callback(keyElement, data, 0, Emscripten_HandleKeyPress);
   740 
   741     emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandleFullscreenChange);
   742 
   743     emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleResize);
   744 
   745     emscripten_set_visibilitychange_callback(data, 0, Emscripten_HandleVisibilityChange);
   746 
   747     emscripten_set_beforeunload_callback(data, Emscripten_HandleBeforeUnload);
   748 }
   749 
   750 void
   751 Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
   752 {
   753     const char *target;
   754 
   755     /* only works due to having one window */
   756     emscripten_set_mousemove_callback(data->canvas_id, NULL, 0, NULL);
   757 
   758     emscripten_set_mousedown_callback(data->canvas_id, NULL, 0, NULL);
   759     emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
   760 
   761     emscripten_set_mouseenter_callback(data->canvas_id, NULL, 0, NULL);
   762     emscripten_set_mouseleave_callback(data->canvas_id, NULL, 0, NULL);
   763 
   764     emscripten_set_wheel_callback(data->canvas_id, NULL, 0, NULL);
   765 
   766     emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
   767     emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
   768 
   769     emscripten_set_touchstart_callback(data->canvas_id, NULL, 0, NULL);
   770     emscripten_set_touchend_callback(data->canvas_id, NULL, 0, NULL);
   771     emscripten_set_touchmove_callback(data->canvas_id, NULL, 0, NULL);
   772     emscripten_set_touchcancel_callback(data->canvas_id, NULL, 0, NULL);
   773 
   774     emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
   775 
   776     target = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
   777     if (!target) {
   778         target = EMSCRIPTEN_EVENT_TARGET_WINDOW;
   779     }
   780 
   781     emscripten_set_keydown_callback(target, NULL, 0, NULL);
   782     emscripten_set_keyup_callback(target, NULL, 0, NULL);
   783     emscripten_set_keypress_callback(target, NULL, 0, NULL);
   784 
   785     emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
   786 
   787     emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
   788 
   789     emscripten_set_visibilitychange_callback(NULL, 0, NULL);
   790 
   791     emscripten_set_beforeunload_callback(NULL, NULL);
   792 }
   793 
   794 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
   795 
   796 /* vi: set ts=4 sw=4 expandtab: */