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