src/video/emscripten/SDL_emscriptenevents.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 14 Aug 2017 21:28:04 -0700
changeset 11300 68a80d7afec3
parent 11067 f4d12a22446e
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Fixed bug 2293 - Precise scrolling events

Martijn Courteaux

I implemented precise scrolling events. I have been through all the folders in /src/video/[platform] to implement where possible. This works on OS X, but I can't speak for others. Build farm will figure that out, I guess. I think this patch should introduce precise scrolling on OS X, Wayland, Mir, Windows, Android, Nacl, Windows RT.

The way I provide precise scrolling events is by adding two float fields to the SDL_MouseWheelScrollEvent datastructure, called "preciseX" and "preciseY". The old integer fields "x" and "y" are still present. The idea is that every platform specific code normalises the scroll amounts and forwards them to the SDL_SendMouseWheel function. It is this function that will now accumulate these (using a static variable, as I have seen how it was implemented in the Windows specific code) and once we hit a unit size, set the traditional integer "x" and "y" fields.

I believe this is pretty solid way of doing it, although I'm not the expert here.

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