SDL_emscriptenevents.c (29786B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 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_KP_5, 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_NONUSBACKSLASH, 107 /* 61 */ SDL_SCANCODE_EQUALS, 108 /* 62 */ SDL_SCANCODE_UNKNOWN, 109 /* 63 */ SDL_SCANCODE_MINUS, 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_GRAVE, 207 /* 161 */ SDL_SCANCODE_UNKNOWN, 208 /* 162 */ SDL_SCANCODE_UNKNOWN, 209 /* 163 */ SDL_SCANCODE_KP_HASH, /*KaiOS phone keypad*/ 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_KP_MULTIPLY, /*KaiOS phone keypad*/ 217 /* 171 */ SDL_SCANCODE_RIGHTBRACKET, 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(window_data->canvas_id, 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); 405 return SDL_GetEventState(SDL_WINDOWEVENT) == SDL_ENABLE; 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); 413 return SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE; 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) { 423 SDL_ResetKeyboard(); 424 } 425 426 427 SDL_SendWindowEvent(window_data->window, eventType == EMSCRIPTEN_EVENT_FOCUS ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST, 0, 0); 428 return SDL_GetEventState(SDL_WINDOWEVENT) == SDL_ENABLE; 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++) { 447 SDL_FingerID id; 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, window_data->window, 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, window_data->window, x, y, 1.0f); 466 } else { 467 SDL_SendTouch(deviceId, id, window_data->window, 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 (keyEvent->keyCode == 0) { 489 /* KaiOS Left Soft Key and Right Soft Key, they act as OK/Next/Menu and Cancel/Back/Clear */ 490 if (SDL_strncmp(keyEvent->key, "SoftLeft", 9) == 0) { 491 scancode = SDL_SCANCODE_AC_FORWARD; 492 } 493 if (SDL_strncmp(keyEvent->key, "SoftRight", 10) == 0) { 494 scancode = SDL_SCANCODE_AC_BACK; 495 } 496 } 497 498 if (scancode != SDL_SCANCODE_UNKNOWN) { 499 500 if (keyEvent->location == DOM_KEY_LOCATION_RIGHT) { 501 switch (scancode) { 502 case SDL_SCANCODE_LSHIFT: 503 scancode = SDL_SCANCODE_RSHIFT; 504 break; 505 case SDL_SCANCODE_LCTRL: 506 scancode = SDL_SCANCODE_RCTRL; 507 break; 508 case SDL_SCANCODE_LALT: 509 scancode = SDL_SCANCODE_RALT; 510 break; 511 case SDL_SCANCODE_LGUI: 512 scancode = SDL_SCANCODE_RGUI; 513 break; 514 } 515 } 516 else if (keyEvent->location == DOM_KEY_LOCATION_NUMPAD) { 517 switch (scancode) { 518 case SDL_SCANCODE_0: 519 case SDL_SCANCODE_INSERT: 520 scancode = SDL_SCANCODE_KP_0; 521 break; 522 case SDL_SCANCODE_1: 523 case SDL_SCANCODE_END: 524 scancode = SDL_SCANCODE_KP_1; 525 break; 526 case SDL_SCANCODE_2: 527 case SDL_SCANCODE_DOWN: 528 scancode = SDL_SCANCODE_KP_2; 529 break; 530 case SDL_SCANCODE_3: 531 case SDL_SCANCODE_PAGEDOWN: 532 scancode = SDL_SCANCODE_KP_3; 533 break; 534 case SDL_SCANCODE_4: 535 case SDL_SCANCODE_LEFT: 536 scancode = SDL_SCANCODE_KP_4; 537 break; 538 case SDL_SCANCODE_5: 539 scancode = SDL_SCANCODE_KP_5; 540 break; 541 case SDL_SCANCODE_6: 542 case SDL_SCANCODE_RIGHT: 543 scancode = SDL_SCANCODE_KP_6; 544 break; 545 case SDL_SCANCODE_7: 546 case SDL_SCANCODE_HOME: 547 scancode = SDL_SCANCODE_KP_7; 548 break; 549 case SDL_SCANCODE_8: 550 case SDL_SCANCODE_UP: 551 scancode = SDL_SCANCODE_KP_8; 552 break; 553 case SDL_SCANCODE_9: 554 case SDL_SCANCODE_PAGEUP: 555 scancode = SDL_SCANCODE_KP_9; 556 break; 557 case SDL_SCANCODE_RETURN: 558 scancode = SDL_SCANCODE_KP_ENTER; 559 break; 560 case SDL_SCANCODE_DELETE: 561 scancode = SDL_SCANCODE_KP_PERIOD; 562 break; 563 } 564 } 565 SDL_SendKeyboardKey(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_PRESSED : SDL_RELEASED, scancode); 566 } 567 } 568 569 prevent_default = SDL_GetEventState(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_KEYDOWN : SDL_KEYUP) == SDL_ENABLE; 570 571 /* if TEXTINPUT events are enabled we can't prevent keydown or we won't get keypress 572 * we need to ALWAYS prevent backspace and tab otherwise chrome takes action and does bad navigation UX 573 */ 574 is_nav_key = keyEvent->keyCode == 8 /* backspace */ || 575 keyEvent->keyCode == 9 /* tab */ || 576 keyEvent->keyCode == 37 /* left */ || 577 keyEvent->keyCode == 38 /* up */ || 578 keyEvent->keyCode == 39 /* right */ || 579 keyEvent->keyCode == 40 /* down */; 580 581 if (eventType == EMSCRIPTEN_EVENT_KEYDOWN && SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE && !is_nav_key) 582 prevent_default = SDL_FALSE; 583 584 return prevent_default; 585 } 586 587 static EM_BOOL 588 Emscripten_HandleKeyPress(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData) 589 { 590 char text[5]; 591 if (Emscripten_ConvertUTF32toUTF8(keyEvent->charCode, text)) { 592 SDL_SendKeyboardText(text); 593 } 594 return SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE; 595 } 596 597 static EM_BOOL 598 Emscripten_HandleFullscreenChange(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData) 599 { 600 SDL_WindowData *window_data = userData; 601 SDL_VideoDisplay *display; 602 603 if(fullscreenChangeEvent->isFullscreen) 604 { 605 window_data->window->flags |= window_data->requested_fullscreen_mode; 606 607 window_data->requested_fullscreen_mode = 0; 608 609 if(!window_data->requested_fullscreen_mode) 610 window_data->window->flags |= SDL_WINDOW_FULLSCREEN; /*we didn't request fullscreen*/ 611 } 612 else 613 { 614 window_data->window->flags &= ~FULLSCREEN_MASK; 615 616 /* reset fullscreen window if the browser left fullscreen */ 617 display = SDL_GetDisplayForWindow(window_data->window); 618 619 if (display->fullscreen_window == window_data->window) { 620 display->fullscreen_window = NULL; 621 } 622 } 623 624 return 0; 625 } 626 627 static EM_BOOL 628 Emscripten_HandleResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) 629 { 630 SDL_WindowData *window_data = userData; 631 SDL_bool force = SDL_FALSE; 632 633 /* update pixel ratio */ 634 if (window_data->window->flags & SDL_WINDOW_ALLOW_HIGHDPI) { 635 if (window_data->pixel_ratio != emscripten_get_device_pixel_ratio()) { 636 window_data->pixel_ratio = emscripten_get_device_pixel_ratio(); 637 force = SDL_TRUE; 638 } 639 } 640 641 if(!(window_data->window->flags & FULLSCREEN_MASK)) 642 { 643 /* this will only work if the canvas size is set through css */ 644 if(window_data->window->flags & SDL_WINDOW_RESIZABLE) 645 { 646 double w = window_data->window->w; 647 double h = window_data->window->h; 648 649 if(window_data->external_size) { 650 emscripten_get_element_css_size(window_data->canvas_id, &w, &h); 651 } 652 653 emscripten_set_canvas_element_size(window_data->canvas_id, w * window_data->pixel_ratio, h * window_data->pixel_ratio); 654 655 /* set_canvas_size unsets this */ 656 if (!window_data->external_size && window_data->pixel_ratio != 1.0f) { 657 emscripten_set_element_css_size(window_data->canvas_id, w, h); 658 } 659 660 if (force) { 661 /* force the event to trigger, so pixel ratio changes can be handled */ 662 window_data->window->w = 0; 663 window_data->window->h = 0; 664 } 665 666 SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, w, h); 667 } 668 } 669 670 return 0; 671 } 672 673 EM_BOOL 674 Emscripten_HandleCanvasResize(int eventType, const void *reserved, void *userData) 675 { 676 /*this is used during fullscreen changes*/ 677 SDL_WindowData *window_data = userData; 678 679 if(window_data->fullscreen_resize) 680 { 681 double css_w, css_h; 682 emscripten_get_element_css_size(window_data->canvas_id, &css_w, &css_h); 683 SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, css_w, css_h); 684 } 685 686 return 0; 687 } 688 689 static EM_BOOL 690 Emscripten_HandleVisibilityChange(int eventType, const EmscriptenVisibilityChangeEvent *visEvent, void *userData) 691 { 692 SDL_WindowData *window_data = userData; 693 SDL_SendWindowEvent(window_data->window, visEvent->hidden ? SDL_WINDOWEVENT_HIDDEN : SDL_WINDOWEVENT_SHOWN, 0, 0); 694 return 0; 695 } 696 697 static const char* 698 Emscripten_HandleBeforeUnload(int eventType, const void *reserved, void *userData) 699 { 700 /* This event will need to be handled synchronously, e.g. using 701 SDL_AddEventWatch, as the page is being closed *now*. */ 702 /* No need to send a SDL_QUIT, the app won't get control again. */ 703 SDL_SendAppEvent(SDL_APP_TERMINATING); 704 return ""; /* don't trigger confirmation dialog */ 705 } 706 707 void 708 Emscripten_RegisterEventHandlers(SDL_WindowData *data) 709 { 710 const char *keyElement; 711 712 /* There is only one window and that window is the canvas */ 713 emscripten_set_mousemove_callback(data->canvas_id, data, 0, Emscripten_HandleMouseMove); 714 715 emscripten_set_mousedown_callback(data->canvas_id, data, 0, Emscripten_HandleMouseButton); 716 emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandleMouseButton); 717 718 emscripten_set_mouseenter_callback(data->canvas_id, data, 0, Emscripten_HandleMouseFocus); 719 emscripten_set_mouseleave_callback(data->canvas_id, data, 0, Emscripten_HandleMouseFocus); 720 721 emscripten_set_wheel_callback(data->canvas_id, data, 0, Emscripten_HandleWheel); 722 723 emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleFocus); 724 emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleFocus); 725 726 emscripten_set_touchstart_callback(data->canvas_id, data, 0, Emscripten_HandleTouch); 727 emscripten_set_touchend_callback(data->canvas_id, data, 0, Emscripten_HandleTouch); 728 emscripten_set_touchmove_callback(data->canvas_id, data, 0, Emscripten_HandleTouch); 729 emscripten_set_touchcancel_callback(data->canvas_id, data, 0, Emscripten_HandleTouch); 730 731 emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandlePointerLockChange); 732 733 /* Keyboard events are awkward */ 734 keyElement = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT); 735 if (!keyElement) keyElement = EMSCRIPTEN_EVENT_TARGET_WINDOW; 736 737 emscripten_set_keydown_callback(keyElement, data, 0, Emscripten_HandleKey); 738 emscripten_set_keyup_callback(keyElement, data, 0, Emscripten_HandleKey); 739 emscripten_set_keypress_callback(keyElement, data, 0, Emscripten_HandleKeyPress); 740 741 emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandleFullscreenChange); 742 743 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleResize); 744 745 emscripten_set_visibilitychange_callback(data, 0, Emscripten_HandleVisibilityChange); 746 747 emscripten_set_beforeunload_callback(data, Emscripten_HandleBeforeUnload); 748 } 749 750 void 751 Emscripten_UnregisterEventHandlers(SDL_WindowData *data) 752 { 753 const char *target; 754 755 /* only works due to having one window */ 756 emscripten_set_mousemove_callback(data->canvas_id, NULL, 0, NULL); 757 758 emscripten_set_mousedown_callback(data->canvas_id, NULL, 0, NULL); 759 emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL); 760 761 emscripten_set_mouseenter_callback(data->canvas_id, NULL, 0, NULL); 762 emscripten_set_mouseleave_callback(data->canvas_id, NULL, 0, NULL); 763 764 emscripten_set_wheel_callback(data->canvas_id, NULL, 0, NULL); 765 766 emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL); 767 emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL); 768 769 emscripten_set_touchstart_callback(data->canvas_id, NULL, 0, NULL); 770 emscripten_set_touchend_callback(data->canvas_id, NULL, 0, NULL); 771 emscripten_set_touchmove_callback(data->canvas_id, NULL, 0, NULL); 772 emscripten_set_touchcancel_callback(data->canvas_id, NULL, 0, NULL); 773 774 emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL); 775 776 target = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT); 777 if (!target) { 778 target = EMSCRIPTEN_EVENT_TARGET_WINDOW; 779 } 780 781 emscripten_set_keydown_callback(target, NULL, 0, NULL); 782 emscripten_set_keyup_callback(target, NULL, 0, NULL); 783 emscripten_set_keypress_callback(target, NULL, 0, NULL); 784 785 emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL); 786 787 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL); 788 789 emscripten_set_visibilitychange_callback(NULL, 0, NULL); 790 791 emscripten_set_beforeunload_callback(NULL, NULL); 792 } 793 794 #endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */ 795 796 /* vi: set ts=4 sw=4 expandtab: */