SDL  2.0
SDL_emscriptenevents.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2019 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(window_data->canvas_id, &client_w, &client_h);
321  xscale = window_data->window->w / client_w;
322  yscale = window_data->window->h / client_h;
323 
324  if (isPointerLocked) {
325  residualx += mouseEvent->movementX * xscale;
326  residualy += mouseEvent->movementY * yscale;
327  /* Let slow sub-pixel motion accumulate. Don't lose it. */
328  mx = residualx;
329  residualx -= mx;
330  my = residualy;
331  residualy -= my;
332  } else {
333  mx = mouseEvent->targetX * xscale;
334  my = mouseEvent->targetY * yscale;
335  }
336 
337  SDL_SendMouseMotion(window_data->window, 0, isPointerLocked, mx, my);
338  return 0;
339 }
340 
341 static EM_BOOL
342 Emscripten_HandleMouseButton(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
343 {
344  SDL_WindowData *window_data = userData;
345  Uint8 sdl_button;
346  Uint8 sdl_button_state;
347  SDL_EventType sdl_event_type;
348  double css_w, css_h;
349 
350  switch (mouseEvent->button) {
351  case 0:
352  sdl_button = SDL_BUTTON_LEFT;
353  break;
354  case 1:
355  sdl_button = SDL_BUTTON_MIDDLE;
356  break;
357  case 2:
358  sdl_button = SDL_BUTTON_RIGHT;
359  break;
360  default:
361  return 0;
362  }
363 
364  if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) {
365  if (SDL_GetMouse()->relative_mode && !window_data->has_pointer_lock) {
366  emscripten_request_pointerlock(NULL, 0); /* try to regrab lost pointer lock. */
367  }
368  sdl_button_state = SDL_PRESSED;
369  sdl_event_type = SDL_MOUSEBUTTONDOWN;
370  } else {
371  sdl_button_state = SDL_RELEASED;
372  sdl_event_type = SDL_MOUSEBUTTONUP;
373  }
374  SDL_SendMouseButton(window_data->window, 0, sdl_button_state, sdl_button);
375 
376  /* Do not consume the event if the mouse is outside of the canvas. */
377  emscripten_get_element_css_size(window_data->canvas_id, &css_w, &css_h);
378  if (mouseEvent->targetX < 0 || mouseEvent->targetX >= css_w ||
379  mouseEvent->targetY < 0 || mouseEvent->targetY >= css_h) {
380  return 0;
381  }
382 
383  return SDL_GetEventState(sdl_event_type) == SDL_ENABLE;
384 }
385 
386 static EM_BOOL
387 Emscripten_HandleMouseFocus(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
388 {
389  SDL_WindowData *window_data = userData;
390 
391  int mx = mouseEvent->targetX, my = mouseEvent->targetY;
392  const int isPointerLocked = window_data->has_pointer_lock;
393 
394  if (!isPointerLocked) {
395  /* rescale (in case canvas is being scaled)*/
396  double client_w, client_h;
397  emscripten_get_element_css_size(window_data->canvas_id, &client_w, &client_h);
398 
399  mx = mx * (window_data->window->w / client_w);
400  my = my * (window_data->window->h / client_h);
401  SDL_SendMouseMotion(window_data->window, 0, isPointerLocked, mx, my);
402  }
403 
404  SDL_SetMouseFocus(eventType == EMSCRIPTEN_EVENT_MOUSEENTER ? window_data->window : NULL);
406 }
407 
408 static EM_BOOL
409 Emscripten_HandleWheel(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
410 {
411  SDL_WindowData *window_data = userData;
412  SDL_SendMouseWheel(window_data->window, 0, (float)wheelEvent->deltaX, (float)-wheelEvent->deltaY, SDL_MOUSEWHEEL_NORMAL);
414 }
415 
416 static EM_BOOL
417 Emscripten_HandleFocus(int eventType, const EmscriptenFocusEvent *wheelEvent, void *userData)
418 {
419  SDL_WindowData *window_data = userData;
420  /* If the user switches away while keys are pressed (such as
421  * via Alt+Tab), key release events won't be received. */
422  if (eventType == EMSCRIPTEN_EVENT_BLUR) {
424  }
425 
426 
427  SDL_SendWindowEvent(window_data->window, eventType == EMSCRIPTEN_EVENT_FOCUS ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
429 }
430 
431 static EM_BOOL
432 Emscripten_HandleTouch(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
433 {
434  SDL_WindowData *window_data = (SDL_WindowData *) userData;
435  int i;
436  double client_w, client_h;
437  int preventDefault = 0;
438 
439  SDL_TouchID deviceId = 1;
440  if (SDL_AddTouch(deviceId, SDL_TOUCH_DEVICE_DIRECT, "") < 0) {
441  return 0;
442  }
443 
444  emscripten_get_element_css_size(window_data->canvas_id, &client_w, &client_h);
445 
446  for (i = 0; i < touchEvent->numTouches; i++) {
448  float x, y;
449 
450  if (!touchEvent->touches[i].isChanged)
451  continue;
452 
453  id = touchEvent->touches[i].identifier;
454  x = touchEvent->touches[i].targetX / client_w;
455  y = touchEvent->touches[i].targetY / client_h;
456 
457  if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) {
458  SDL_SendTouch(deviceId, id, SDL_TRUE, x, y, 1.0f);
459 
460  /* disable browser scrolling/pinch-to-zoom if app handles touch events */
461  if (!preventDefault && SDL_GetEventState(SDL_FINGERDOWN) == SDL_ENABLE) {
462  preventDefault = 1;
463  }
464  } else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) {
465  SDL_SendTouchMotion(deviceId, id, x, y, 1.0f);
466  } else {
467  SDL_SendTouch(deviceId, id, SDL_FALSE, x, y, 1.0f);
468 
469  /* block browser's simulated mousedown/mouseup on touchscreen devices */
470  preventDefault = 1;
471  }
472  }
473 
474  return preventDefault;
475 }
476 
477 static EM_BOOL
478 Emscripten_HandleKey(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
479 {
480  Uint32 scancode;
481  SDL_bool prevent_default;
482  SDL_bool is_nav_key;
483 
484  /* .keyCode is deprecated, but still the most reliable way to get keys */
485  if (keyEvent->keyCode < SDL_arraysize(emscripten_scancode_table)) {
486  scancode = emscripten_scancode_table[keyEvent->keyCode];
487 
488  if (scancode != SDL_SCANCODE_UNKNOWN) {
489 
490  if (keyEvent->location == DOM_KEY_LOCATION_RIGHT) {
491  switch (scancode) {
492  case SDL_SCANCODE_LSHIFT:
493  scancode = SDL_SCANCODE_RSHIFT;
494  break;
495  case SDL_SCANCODE_LCTRL:
496  scancode = SDL_SCANCODE_RCTRL;
497  break;
498  case SDL_SCANCODE_LALT:
499  scancode = SDL_SCANCODE_RALT;
500  break;
501  case SDL_SCANCODE_LGUI:
502  scancode = SDL_SCANCODE_RGUI;
503  break;
504  }
505  }
506  SDL_SendKeyboardKey(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_PRESSED : SDL_RELEASED, scancode);
507  }
508  }
509 
510  prevent_default = SDL_GetEventState(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_KEYDOWN : SDL_KEYUP) == SDL_ENABLE;
511 
512  /* if TEXTINPUT events are enabled we can't prevent keydown or we won't get keypress
513  * we need to ALWAYS prevent backspace and tab otherwise chrome takes action and does bad navigation UX
514  */
515  is_nav_key = keyEvent->keyCode == 8 /* backspace */ ||
516  keyEvent->keyCode == 9 /* tab */ ||
517  keyEvent->keyCode == 37 /* left */ ||
518  keyEvent->keyCode == 38 /* up */ ||
519  keyEvent->keyCode == 39 /* right */ ||
520  keyEvent->keyCode == 40 /* down */;
521 
522  if (eventType == EMSCRIPTEN_EVENT_KEYDOWN && SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE && !is_nav_key)
523  prevent_default = SDL_FALSE;
524 
525  return prevent_default;
526 }
527 
528 static EM_BOOL
529 Emscripten_HandleKeyPress(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
530 {
531  char text[5];
532  if (Emscripten_ConvertUTF32toUTF8(keyEvent->charCode, text)) {
533  SDL_SendKeyboardText(text);
534  }
536 }
537 
538 static EM_BOOL
539 Emscripten_HandleFullscreenChange(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData)
540 {
541  SDL_WindowData *window_data = userData;
542  SDL_VideoDisplay *display;
543 
544  if(fullscreenChangeEvent->isFullscreen)
545  {
546  window_data->window->flags |= window_data->requested_fullscreen_mode;
547 
548  window_data->requested_fullscreen_mode = 0;
549 
550  if(!window_data->requested_fullscreen_mode)
551  window_data->window->flags |= SDL_WINDOW_FULLSCREEN; /*we didn't reqest fullscreen*/
552  }
553  else
554  {
555  window_data->window->flags &= ~FULLSCREEN_MASK;
556 
557  /* reset fullscreen window if the browser left fullscreen */
558  display = SDL_GetDisplayForWindow(window_data->window);
559 
560  if (display->fullscreen_window == window_data->window) {
561  display->fullscreen_window = NULL;
562  }
563  }
564 
565  return 0;
566 }
567 
568 static EM_BOOL
569 Emscripten_HandleResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
570 {
571  SDL_WindowData *window_data = userData;
572  SDL_bool force = SDL_FALSE;
573 
574  /* update pixel ratio */
575  if (window_data->window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
576  if (window_data->pixel_ratio != emscripten_get_device_pixel_ratio()) {
577  window_data->pixel_ratio = emscripten_get_device_pixel_ratio();
578  force = SDL_TRUE;
579  }
580  }
581 
582  if(!(window_data->window->flags & FULLSCREEN_MASK))
583  {
584  /* this will only work if the canvas size is set through css */
585  if(window_data->window->flags & SDL_WINDOW_RESIZABLE)
586  {
587  double w = window_data->window->w;
588  double h = window_data->window->h;
589 
590  if(window_data->external_size) {
591  emscripten_get_element_css_size(window_data->canvas_id, &w, &h);
592  }
593 
594  emscripten_set_canvas_element_size(window_data->canvas_id, w * window_data->pixel_ratio, h * window_data->pixel_ratio);
595 
596  /* set_canvas_size unsets this */
597  if (!window_data->external_size && window_data->pixel_ratio != 1.0f) {
598  emscripten_set_element_css_size(window_data->canvas_id, w, h);
599  }
600 
601  if (force) {
602  /* force the event to trigger, so pixel ratio changes can be handled */
603  window_data->window->w = 0;
604  window_data->window->h = 0;
605  }
606 
608  }
609  }
610 
611  return 0;
612 }
613 
614 EM_BOOL
615 Emscripten_HandleCanvasResize(int eventType, const void *reserved, void *userData)
616 {
617  /*this is used during fullscreen changes*/
618  SDL_WindowData *window_data = userData;
619 
620  if(window_data->fullscreen_resize)
621  {
622  double css_w, css_h;
623  emscripten_get_element_css_size(window_data->canvas_id, &css_w, &css_h);
624  SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, css_w, css_h);
625  }
626 
627  return 0;
628 }
629 
630 static EM_BOOL
631 Emscripten_HandleVisibilityChange(int eventType, const EmscriptenVisibilityChangeEvent *visEvent, void *userData)
632 {
633  SDL_WindowData *window_data = userData;
634  SDL_SendWindowEvent(window_data->window, visEvent->hidden ? SDL_WINDOWEVENT_HIDDEN : SDL_WINDOWEVENT_SHOWN, 0, 0);
635  return 0;
636 }
637 
638 void
640 {
641  const char *keyElement;
642 
643  /* There is only one window and that window is the canvas */
644  emscripten_set_mousemove_callback(data->canvas_id, data, 0, Emscripten_HandleMouseMove);
645 
646  emscripten_set_mousedown_callback(data->canvas_id, data, 0, Emscripten_HandleMouseButton);
647  emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandleMouseButton);
648 
649  emscripten_set_mouseenter_callback(data->canvas_id, data, 0, Emscripten_HandleMouseFocus);
650  emscripten_set_mouseleave_callback(data->canvas_id, data, 0, Emscripten_HandleMouseFocus);
651 
652  emscripten_set_wheel_callback(data->canvas_id, data, 0, Emscripten_HandleWheel);
653 
654  emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleFocus);
655  emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleFocus);
656 
657  emscripten_set_touchstart_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
658  emscripten_set_touchend_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
659  emscripten_set_touchmove_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
660  emscripten_set_touchcancel_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
661 
662  emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandlePointerLockChange);
663 
664  /* Keyboard events are awkward */
666  if (!keyElement) keyElement = EMSCRIPTEN_EVENT_TARGET_WINDOW;
667 
668  emscripten_set_keydown_callback(keyElement, data, 0, Emscripten_HandleKey);
669  emscripten_set_keyup_callback(keyElement, data, 0, Emscripten_HandleKey);
670  emscripten_set_keypress_callback(keyElement, data, 0, Emscripten_HandleKeyPress);
671 
672  emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandleFullscreenChange);
673 
674  emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleResize);
675 
676  emscripten_set_visibilitychange_callback(data, 0, Emscripten_HandleVisibilityChange);
677 }
678 
679 void
681 {
682  const char *target;
683 
684  /* only works due to having one window */
685  emscripten_set_mousemove_callback(data->canvas_id, NULL, 0, NULL);
686 
687  emscripten_set_mousedown_callback(data->canvas_id, NULL, 0, NULL);
688  emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
689 
690  emscripten_set_mouseenter_callback(data->canvas_id, NULL, 0, NULL);
691  emscripten_set_mouseleave_callback(data->canvas_id, NULL, 0, NULL);
692 
693  emscripten_set_wheel_callback(data->canvas_id, NULL, 0, NULL);
694 
695  emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
696  emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
697 
698  emscripten_set_touchstart_callback(data->canvas_id, NULL, 0, NULL);
699  emscripten_set_touchend_callback(data->canvas_id, NULL, 0, NULL);
700  emscripten_set_touchmove_callback(data->canvas_id, NULL, 0, NULL);
701  emscripten_set_touchcancel_callback(data->canvas_id, NULL, 0, NULL);
702 
703  emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
704 
706  if (!target) {
707  target = EMSCRIPTEN_EVENT_TARGET_WINDOW;
708  }
709 
710  emscripten_set_keydown_callback(target, NULL, 0, NULL);
711  emscripten_set_keyup_callback(target, NULL, 0, NULL);
712  emscripten_set_keypress_callback(target, NULL, 0, NULL);
713 
714  emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
715 
716  emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
717 
718  emscripten_set_visibilitychange_callback(NULL, 0, NULL);
719 }
720 
721 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
722 
723 /* vi: set ts=4 sw=4 expandtab: */
GLuint id
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:178
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
EM_BOOL Emscripten_HandleCanvasResize(int eventType, const void *reserved, void *userData)
Sint64 SDL_FingerID
Definition: SDL_touch.h:42
GLfloat GLfloat GLfloat GLfloat h
#define SDL_BUTTON_RIGHT
Definition: SDL_mouse.h:284
#define SDL_GetHint
int SDL_SendTouch(SDL_TouchID id, SDL_FingerID fingerid, SDL_bool down, float x, float y, float pressure)
Definition: SDL_touch.c:242
#define SDL_ENABLE
Definition: SDL_events.h:759
GLfloat f
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
int SDL_SendWindowEvent(SDL_Window *window, Uint8 windowevent, int data1, int data2)
SDL_Window * window
void SDL_SetMouseFocus(SDL_Window *window)
Definition: SDL_mouse.c:211
void Emscripten_RegisterEventHandlers(SDL_WindowData *data)
int SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode)
Definition: SDL_keyboard.c:679
int SDL_SendTouchMotion(SDL_TouchID id, SDL_FingerID fingerid, float x, float y, float pressure)
Definition: SDL_touch.c:364
int SDL_AddTouch(SDL_TouchID touchID, SDL_TouchDeviceType type, const char *name)
Definition: SDL_touch.c:155
#define SDL_GetEventState(type)
Definition: SDL_events.h:772
int SDL_SendMouseMotion(SDL_Window *window, SDL_MouseID mouseID, int relative, int x, int y)
Definition: SDL_mouse.c:301
#define SDL_BUTTON_LEFT
Definition: SDL_mouse.h:282
uint8_t Uint8
Definition: SDL_stdinc.h:179
int SDL_SendKeyboardText(const char *text)
Definition: SDL_keyboard.c:789
GLubyte GLubyte GLubyte GLubyte w
SDL_EventType
The types of events that can be delivered.
Definition: SDL_events.h:55
Sint64 SDL_TouchID
Definition: SDL_touch.h:41
#define SDL_BUTTON_MIDDLE
Definition: SDL_mouse.h:283
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
SDL_Window * fullscreen_window
Definition: SDL_sysvideo.h:135
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
GLenum target
#define NULL
Definition: begin_code.h:167
SDL_bool
Definition: SDL_stdinc.h:161
int SDL_SendMouseWheel(SDL_Window *window, SDL_MouseID mouseID, float x, float y, SDL_MouseWheelDirection direction)
Definition: SDL_mouse.c:611
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1089
void SDL_ResetKeyboard(void)
Definition: SDL_keyboard.c:572
#define SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT
override the binding element for keyboard inputs for Emscripten builds
Definition: SDL_hints.h:911
uint32_t Uint32
Definition: SDL_stdinc.h:203
GLenum GLenum GLuint GLint GLint GLint yscale
Definition: gl2ext.h:2243
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
GLenum GLenum GLuint GLint GLint xscale
Definition: gl2ext.h:2243
#define FULLSCREEN_MASK
Definition: SDL_video.c:144
#define SDL_PRESSED
Definition: SDL_events.h:50
void Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
Uint32 flags
Definition: SDL_sysvideo.h:83
#define SDL_RELEASED
Definition: SDL_events.h:49
int SDL_SendMouseButton(SDL_Window *window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
Definition: SDL_mouse.c:605
SDL_Scancode
The SDL keyboard scancode representation.
Definition: SDL_scancode.h:43
SDL_bool fullscreen_resize