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