src/video/emscripten/SDL_emscriptenevents.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 12 Aug 2016 19:59:00 -0400
changeset 10284 c09f06c4e8c8
parent 9998 f67cf37e9cd4
child 10316 76bcc22dc5e4
permissions -rw-r--r--
emscripten: send fake mouse events for touches, like other targets do.

(This really should be handled at the higher level and not in the individual
targets, but this fixes the immediate bug.)
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 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_UNKNOWN,
   221     /* 175 */   SDL_SCANCODE_UNKNOWN,
   222     /* 176 */   SDL_SCANCODE_UNKNOWN,
   223     /* 177 */   SDL_SCANCODE_UNKNOWN,
   224     /* 178 */   SDL_SCANCODE_UNKNOWN,
   225     /* 179 */   SDL_SCANCODE_UNKNOWN,
   226     /* 180 */   SDL_SCANCODE_UNKNOWN,
   227     /* 181 */   SDL_SCANCODE_UNKNOWN,
   228     /* 182 */   SDL_SCANCODE_UNKNOWN,
   229     /* 183 */   SDL_SCANCODE_UNKNOWN,
   230     /* 184 */   SDL_SCANCODE_UNKNOWN,
   231     /* 185 */   SDL_SCANCODE_UNKNOWN,
   232     /* 186 */   SDL_SCANCODE_SEMICOLON, /*IE, Chrome, D3E legacy*/
   233     /* 187 */   SDL_SCANCODE_EQUALS, /*IE, Chrome, D3E legacy*/
   234     /* 188 */   SDL_SCANCODE_COMMA,
   235     /* 189 */   SDL_SCANCODE_MINUS, /*IE, Chrome, D3E legacy*/
   236     /* 190 */   SDL_SCANCODE_PERIOD,
   237     /* 191 */   SDL_SCANCODE_SLASH,
   238     /* 192 */   SDL_SCANCODE_GRAVE, /*FX, D3E legacy (SDL_SCANCODE_APOSTROPHE in IE/Chrome)*/
   239     /* 193 */   SDL_SCANCODE_UNKNOWN,
   240     /* 194 */   SDL_SCANCODE_UNKNOWN,
   241     /* 195 */   SDL_SCANCODE_UNKNOWN,
   242     /* 196 */   SDL_SCANCODE_UNKNOWN,
   243     /* 197 */   SDL_SCANCODE_UNKNOWN,
   244     /* 198 */   SDL_SCANCODE_UNKNOWN,
   245     /* 199 */   SDL_SCANCODE_UNKNOWN,
   246     /* 200 */   SDL_SCANCODE_UNKNOWN,
   247     /* 201 */   SDL_SCANCODE_UNKNOWN,
   248     /* 202 */   SDL_SCANCODE_UNKNOWN,
   249     /* 203 */   SDL_SCANCODE_UNKNOWN,
   250     /* 204 */   SDL_SCANCODE_UNKNOWN,
   251     /* 205 */   SDL_SCANCODE_UNKNOWN,
   252     /* 206 */   SDL_SCANCODE_UNKNOWN,
   253     /* 207 */   SDL_SCANCODE_UNKNOWN,
   254     /* 208 */   SDL_SCANCODE_UNKNOWN,
   255     /* 209 */   SDL_SCANCODE_UNKNOWN,
   256     /* 210 */   SDL_SCANCODE_UNKNOWN,
   257     /* 211 */   SDL_SCANCODE_UNKNOWN,
   258     /* 212 */   SDL_SCANCODE_UNKNOWN,
   259     /* 213 */   SDL_SCANCODE_UNKNOWN,
   260     /* 214 */   SDL_SCANCODE_UNKNOWN,
   261     /* 215 */   SDL_SCANCODE_UNKNOWN,
   262     /* 216 */   SDL_SCANCODE_UNKNOWN,
   263     /* 217 */   SDL_SCANCODE_UNKNOWN,
   264     /* 218 */   SDL_SCANCODE_UNKNOWN,
   265     /* 219 */   SDL_SCANCODE_LEFTBRACKET,
   266     /* 220 */   SDL_SCANCODE_BACKSLASH,
   267     /* 221 */   SDL_SCANCODE_RIGHTBRACKET,
   268     /* 222 */   SDL_SCANCODE_APOSTROPHE, /*FX, D3E legacy*/
   269 };
   270 
   271 
   272 /* "borrowed" from SDL_windowsevents.c */
   273 int
   274 Emscripten_ConvertUTF32toUTF8(Uint32 codepoint, char * text)
   275 {
   276     if (codepoint <= 0x7F) {
   277         text[0] = (char) codepoint;
   278         text[1] = '\0';
   279     } else if (codepoint <= 0x7FF) {
   280         text[0] = 0xC0 | (char) ((codepoint >> 6) & 0x1F);
   281         text[1] = 0x80 | (char) (codepoint & 0x3F);
   282         text[2] = '\0';
   283     } else if (codepoint <= 0xFFFF) {
   284         text[0] = 0xE0 | (char) ((codepoint >> 12) & 0x0F);
   285         text[1] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
   286         text[2] = 0x80 | (char) (codepoint & 0x3F);
   287         text[3] = '\0';
   288     } else if (codepoint <= 0x10FFFF) {
   289         text[0] = 0xF0 | (char) ((codepoint >> 18) & 0x0F);
   290         text[1] = 0x80 | (char) ((codepoint >> 12) & 0x3F);
   291         text[2] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
   292         text[3] = 0x80 | (char) (codepoint & 0x3F);
   293         text[4] = '\0';
   294     } else {
   295         return SDL_FALSE;
   296     }
   297     return SDL_TRUE;
   298 }
   299 
   300 EM_BOOL
   301 Emscripten_HandleMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
   302 {
   303     SDL_WindowData *window_data = userData;
   304     int mx = mouseEvent->canvasX, my = mouseEvent->canvasY;
   305     EmscriptenPointerlockChangeEvent pointerlock_status;
   306 
   307     /* check for pointer lock */
   308     emscripten_get_pointerlock_status(&pointerlock_status);
   309 
   310     if (pointerlock_status.isActive) {
   311         mx = mouseEvent->movementX;
   312         my = mouseEvent->movementY;
   313     }
   314 
   315     /* rescale (in case canvas is being scaled)*/
   316     double client_w, client_h;
   317     emscripten_get_element_css_size(NULL, &client_w, &client_h);
   318 
   319     mx = mx * (window_data->window->w / (client_w * window_data->pixel_ratio));
   320     my = my * (window_data->window->h / (client_h * window_data->pixel_ratio));
   321 
   322     SDL_SendMouseMotion(window_data->window, 0, pointerlock_status.isActive, mx, my);
   323     return 0;
   324 }
   325 
   326 EM_BOOL
   327 Emscripten_HandleMouseButton(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
   328 {
   329     SDL_WindowData *window_data = userData;
   330     uint32_t sdl_button;
   331     switch (mouseEvent->button) {
   332         case 0:
   333             sdl_button = SDL_BUTTON_LEFT;
   334             break;
   335         case 1:
   336             sdl_button = SDL_BUTTON_MIDDLE;
   337             break;
   338         case 2:
   339             sdl_button = SDL_BUTTON_RIGHT;
   340             break;
   341         default:
   342             return 0;
   343     }
   344     SDL_SendMouseButton(window_data->window, 0, eventType == EMSCRIPTEN_EVENT_MOUSEDOWN ? SDL_PRESSED : SDL_RELEASED, sdl_button);
   345     return 1;
   346 }
   347 
   348 EM_BOOL
   349 Emscripten_HandleMouseFocus(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
   350 {
   351     SDL_WindowData *window_data = userData;
   352     SDL_SendWindowEvent(window_data->window, eventType == EMSCRIPTEN_EVENT_MOUSEENTER ? SDL_WINDOWEVENT_ENTER : SDL_WINDOWEVENT_LEAVE, 0, 0);
   353     return 1;
   354 }
   355 
   356 EM_BOOL
   357 Emscripten_HandleWheel(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
   358 {
   359     SDL_WindowData *window_data = userData;
   360     SDL_SendMouseWheel(window_data->window, 0, wheelEvent->deltaX, -wheelEvent->deltaY, SDL_MOUSEWHEEL_NORMAL);
   361     return 1;
   362 }
   363 
   364 EM_BOOL
   365 Emscripten_HandleFocus(int eventType, const EmscriptenFocusEvent *wheelEvent, void *userData)
   366 {
   367     SDL_WindowData *window_data = userData;
   368     SDL_SendWindowEvent(window_data->window, eventType == EMSCRIPTEN_EVENT_FOCUS ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
   369     return 1;
   370 }
   371 
   372 EM_BOOL
   373 Emscripten_HandleTouch(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
   374 {
   375     SDL_WindowData *window_data = userData;
   376     int i;
   377 
   378     SDL_TouchID deviceId = 1;
   379     if (SDL_AddTouch(deviceId, "") < 0) {
   380          return 0;
   381     }
   382 
   383     for (i = 0; i < touchEvent->numTouches; i++) {
   384         SDL_FingerID id;
   385         float x, y;
   386 
   387         if (!touchEvent->touches[i].isChanged)
   388             continue;
   389 
   390         id = touchEvent->touches[i].identifier;
   391         x = touchEvent->touches[i].canvasX / (float)window_data->windowed_width;
   392         y = touchEvent->touches[i].canvasY / (float)window_data->windowed_height;
   393 
   394         if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) {
   395             if (!window_data->finger_touching) {
   396                 window_data->finger_touching = SDL_TRUE;
   397                 window_data->first_finger = id;
   398                 SDL_SendMouseMotion(window_data->window, SDL_TOUCH_MOUSEID, 0, x, y);
   399                 SDL_SendMouseButton(window_data->window, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT);
   400             }
   401             SDL_SendTouch(deviceId, id, SDL_TRUE, x, y, 1.0f);
   402         } else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) {
   403             if ((window_data->finger_touching) && (window_data->first_finger == id)) {
   404                 SDL_SendMouseMotion(window_data->window, SDL_TOUCH_MOUSEID, 0, x, y);
   405             }
   406             SDL_SendTouchMotion(deviceId, id, x, y, 1.0f);
   407         } else {
   408             if ((window_data->finger_touching) && (window_data->first_finger == id)) {
   409                 SDL_SendMouseButton(window_data->window, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT);
   410                 window_data->finger_touching = SDL_FALSE;
   411             }
   412             SDL_SendTouch(deviceId, id, SDL_FALSE, x, y, 1.0f);
   413         }
   414     }
   415 
   416 
   417     return 1;
   418 }
   419 
   420 EM_BOOL
   421 Emscripten_HandleKey(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
   422 {
   423     Uint32 scancode;
   424 
   425     /* .keyCode is deprecated, but still the most reliable way to get keys */
   426     if (keyEvent->keyCode < SDL_arraysize(emscripten_scancode_table)) {
   427         scancode = emscripten_scancode_table[keyEvent->keyCode];
   428 
   429         if (scancode != SDL_SCANCODE_UNKNOWN) {
   430 
   431             if (keyEvent->location == DOM_KEY_LOCATION_RIGHT) {
   432                 switch (scancode) {
   433                     case SDL_SCANCODE_LSHIFT:
   434                         scancode = SDL_SCANCODE_RSHIFT;
   435                         break;
   436                     case SDL_SCANCODE_LCTRL:
   437                         scancode = SDL_SCANCODE_RCTRL;
   438                         break;
   439                     case SDL_SCANCODE_LALT:
   440                         scancode = SDL_SCANCODE_RALT;
   441                         break;
   442                     case SDL_SCANCODE_LGUI:
   443                         scancode = SDL_SCANCODE_RGUI;
   444                         break;
   445                 }
   446             }
   447             SDL_SendKeyboardKey(eventType == EMSCRIPTEN_EVENT_KEYDOWN ?
   448                                 SDL_PRESSED : SDL_RELEASED, scancode);
   449         }
   450     }
   451 
   452     /* if we prevent keydown, we won't get keypress
   453      * also we need to ALWAYS prevent backspace and tab otherwise chrome takes action and does bad navigation UX
   454      */
   455     return SDL_GetEventState(SDL_TEXTINPUT) != SDL_ENABLE || eventType != EMSCRIPTEN_EVENT_KEYDOWN
   456             || keyEvent->keyCode == 8 /* backspace */ || keyEvent->keyCode == 9 /* tab */;
   457 }
   458 
   459 EM_BOOL
   460 Emscripten_HandleKeyPress(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
   461 {
   462     char text[5];
   463     if (Emscripten_ConvertUTF32toUTF8(keyEvent->charCode, text)) {
   464         SDL_SendKeyboardText(text);
   465     }
   466     return 1;
   467 }
   468 
   469 EM_BOOL
   470 Emscripten_HandleFullscreenChange(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData)
   471 {
   472     /*make sure this is actually our element going fullscreen*/
   473     if(SDL_strcmp(fullscreenChangeEvent->id, "SDLFullscreenElement") != 0)
   474         return 0;
   475 
   476     SDL_WindowData *window_data = userData;
   477     if(fullscreenChangeEvent->isFullscreen)
   478     {
   479         SDL_bool is_desktop_fullscreen;
   480         window_data->window->flags |= window_data->requested_fullscreen_mode;
   481 
   482         if(!window_data->requested_fullscreen_mode)
   483             window_data->window->flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; /*we didn't reqest fullscreen*/
   484 
   485         window_data->requested_fullscreen_mode = 0;
   486 
   487         is_desktop_fullscreen = (window_data->window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
   488 
   489         /*update size*/
   490         if(window_data->window->flags & SDL_WINDOW_RESIZABLE || is_desktop_fullscreen)
   491         {
   492             emscripten_set_canvas_size(fullscreenChangeEvent->screenWidth, fullscreenChangeEvent->screenHeight);
   493             SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, fullscreenChangeEvent->screenWidth, fullscreenChangeEvent->screenHeight);
   494         }
   495         else
   496         {
   497             /*preserve ratio*/
   498             double w = window_data->window->w;
   499             double h = window_data->window->h;
   500             double factor = SDL_min(fullscreenChangeEvent->screenWidth / w, fullscreenChangeEvent->screenHeight / h);
   501             emscripten_set_element_css_size(NULL, w * factor, h * factor);
   502         }
   503     }
   504     else
   505     {
   506         EM_ASM({
   507             //un-reparent canvas (similar to Module.requestFullscreen)
   508             var canvas = Module['canvas'];
   509             if(canvas.parentNode.id == "SDLFullscreenElement") {
   510                 var canvasContainer = canvas.parentNode;
   511                 canvasContainer.parentNode.insertBefore(canvas, canvasContainer);
   512                 canvasContainer.parentNode.removeChild(canvasContainer);
   513             }
   514         });
   515         double unscaled_w = window_data->windowed_width / window_data->pixel_ratio;
   516         double unscaled_h = window_data->windowed_height / window_data->pixel_ratio;
   517         emscripten_set_canvas_size(window_data->windowed_width, window_data->windowed_height);
   518 
   519         if (!window_data->external_size && window_data->pixel_ratio != 1.0f) {
   520             emscripten_set_element_css_size(NULL, unscaled_w, unscaled_h);
   521         }
   522 
   523         SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, unscaled_w, unscaled_h);
   524 
   525         window_data->window->flags &= ~FULLSCREEN_MASK;
   526     }
   527 
   528     return 0;
   529 }
   530 
   531 EM_BOOL
   532 Emscripten_HandleResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
   533 {
   534     SDL_WindowData *window_data = userData;
   535     if(window_data->window->flags & FULLSCREEN_MASK)
   536     {
   537         SDL_bool is_desktop_fullscreen = (window_data->window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
   538 
   539         if(window_data->window->flags & SDL_WINDOW_RESIZABLE || is_desktop_fullscreen)
   540         {
   541             emscripten_set_canvas_size(uiEvent->windowInnerWidth * window_data->pixel_ratio, uiEvent->windowInnerHeight * window_data->pixel_ratio);
   542             SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, uiEvent->windowInnerWidth, uiEvent->windowInnerHeight);
   543         }
   544     }
   545     else
   546     {
   547         /* this will only work if the canvas size is set through css */
   548         if(window_data->window->flags & SDL_WINDOW_RESIZABLE)
   549         {
   550             double w = window_data->window->w;
   551             double h = window_data->window->h;
   552 
   553             if(window_data->external_size) {
   554                 emscripten_get_element_css_size(NULL, &w, &h);
   555             }
   556 
   557             emscripten_set_canvas_size(w * window_data->pixel_ratio, h * window_data->pixel_ratio);
   558 
   559             /* set_canvas_size unsets this */
   560             if (!window_data->external_size && window_data->pixel_ratio != 1.0f) {
   561                 emscripten_set_element_css_size(NULL, w, h);
   562             }
   563 
   564             SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, w, h);
   565         }
   566     }
   567 
   568     return 0;
   569 }
   570 
   571 EM_BOOL
   572 Emscripten_HandleVisibilityChange(int eventType, const EmscriptenVisibilityChangeEvent *visEvent, void *userData)
   573 {
   574     SDL_WindowData *window_data = userData;
   575     SDL_SendWindowEvent(window_data->window, visEvent->hidden ? SDL_WINDOWEVENT_HIDDEN : SDL_WINDOWEVENT_SHOWN, 0, 0);
   576     return 0;
   577 }
   578 
   579 void
   580 Emscripten_RegisterEventHandlers(SDL_WindowData *data)
   581 {
   582     /* There is only one window and that window is the canvas */
   583     emscripten_set_mousemove_callback("#canvas", data, 0, Emscripten_HandleMouseMove);
   584 
   585     emscripten_set_mousedown_callback("#canvas", data, 0, Emscripten_HandleMouseButton);
   586     emscripten_set_mouseup_callback("#canvas", data, 0, Emscripten_HandleMouseButton);
   587 
   588     emscripten_set_mouseenter_callback("#canvas", data, 0, Emscripten_HandleMouseFocus);
   589     emscripten_set_mouseleave_callback("#canvas", data, 0, Emscripten_HandleMouseFocus);
   590 
   591     emscripten_set_wheel_callback("#canvas", data, 0, Emscripten_HandleWheel);
   592 
   593     emscripten_set_focus_callback("#canvas", data, 0, Emscripten_HandleFocus);
   594     emscripten_set_blur_callback("#canvas", data, 0, Emscripten_HandleFocus);
   595 
   596     emscripten_set_touchstart_callback("#canvas", data, 0, Emscripten_HandleTouch);
   597     emscripten_set_touchend_callback("#canvas", data, 0, Emscripten_HandleTouch);
   598     emscripten_set_touchmove_callback("#canvas", data, 0, Emscripten_HandleTouch);
   599     emscripten_set_touchcancel_callback("#canvas", data, 0, Emscripten_HandleTouch);
   600 
   601     /* Keyboard events are awkward */
   602     const char *keyElement = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
   603     if (!keyElement) keyElement = "#window";
   604 
   605     emscripten_set_keydown_callback(keyElement, data, 0, Emscripten_HandleKey);
   606     emscripten_set_keyup_callback(keyElement, data, 0, Emscripten_HandleKey);
   607     emscripten_set_keypress_callback(keyElement, data, 0, Emscripten_HandleKeyPress);
   608 
   609     emscripten_set_fullscreenchange_callback("#document", data, 0, Emscripten_HandleFullscreenChange);
   610 
   611     emscripten_set_resize_callback("#window", data, 0, Emscripten_HandleResize);
   612 
   613     emscripten_set_visibilitychange_callback(data, 0, Emscripten_HandleVisibilityChange);
   614 }
   615 
   616 void
   617 Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
   618 {
   619     /* only works due to having one window */
   620     emscripten_set_mousemove_callback("#canvas", NULL, 0, NULL);
   621 
   622     emscripten_set_mousedown_callback("#canvas", NULL, 0, NULL);
   623     emscripten_set_mouseup_callback("#canvas", NULL, 0, NULL);
   624 
   625     emscripten_set_mouseenter_callback("#canvas", NULL, 0, NULL);
   626     emscripten_set_mouseleave_callback("#canvas", NULL, 0, NULL);
   627 
   628     emscripten_set_wheel_callback("#canvas", NULL, 0, NULL);
   629 
   630     emscripten_set_focus_callback("#canvas", NULL, 0, NULL);
   631     emscripten_set_blur_callback("#canvas", NULL, 0, NULL);
   632 
   633     emscripten_set_touchstart_callback("#canvas", NULL, 0, NULL);
   634     emscripten_set_touchend_callback("#canvas", NULL, 0, NULL);
   635     emscripten_set_touchmove_callback("#canvas", NULL, 0, NULL);
   636     emscripten_set_touchcancel_callback("#canvas", NULL, 0, NULL);
   637 
   638     const char *target = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
   639     if (!target) {
   640         target = "#window";
   641     }
   642 
   643     emscripten_set_keydown_callback(target, NULL, 0, NULL);
   644     emscripten_set_keyup_callback(target, NULL, 0, NULL);
   645 
   646     emscripten_set_keypress_callback(target, NULL, 0, NULL);
   647 
   648     emscripten_set_fullscreenchange_callback("#document", NULL, 0, NULL);
   649 
   650     emscripten_set_resize_callback("#window", NULL, 0, NULL);
   651 
   652     emscripten_set_visibilitychange_callback(NULL, 0, NULL);
   653 }
   654 
   655 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
   656 
   657 /* vi: set ts=4 sw=4 expandtab: */