#include "../../SDL_internal.h"
#if SDL_VIDEO_DRIVER_EMSCRIPTEN
#include <emscripten/html5.h>
#include <emscripten/dom_pk_codes.h>
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_touch_c.h"
#include "SDL_emscriptenevents.h"
#include "SDL_emscriptenvideo.h"
#include "SDL_hints.h"
#define FULLSCREEN_MASK ( SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN )
static const SDL_Keycode emscripten_keycode_table[] = {
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_CANCEL,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_HELP,
SDLK_UNKNOWN,
SDLK_BACKSPACE,
SDLK_TAB,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_KP_5,
SDLK_RETURN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_LSHIFT,
SDLK_LCTRL,
SDLK_LALT,
SDLK_PAUSE,
SDLK_CAPSLOCK,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_ESCAPE,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_SPACE,
SDLK_PAGEUP,
SDLK_PAGEDOWN,
SDLK_END,
SDLK_HOME,
SDLK_LEFT,
SDLK_UP,
SDLK_RIGHT,
SDLK_DOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_INSERT,
SDLK_DELETE,
SDLK_UNKNOWN,
SDLK_0,
SDLK_1,
SDLK_2,
SDLK_3,
SDLK_4,
SDLK_5,
SDLK_6,
SDLK_7,
SDLK_8,
SDLK_9,
SDLK_UNKNOWN,
SDLK_SEMICOLON,
SDLK_BACKSLASH ,
SDLK_EQUALS,
SDLK_UNKNOWN,
SDLK_MINUS,
SDLK_UNKNOWN,
SDLK_a,
SDLK_b,
SDLK_c,
SDLK_d,
SDLK_e,
SDLK_f,
SDLK_g,
SDLK_h,
SDLK_i,
SDLK_j,
SDLK_k,
SDLK_l,
SDLK_m,
SDLK_n,
SDLK_o,
SDLK_p,
SDLK_q,
SDLK_r,
SDLK_s,
SDLK_t,
SDLK_u,
SDLK_v,
SDLK_w,
SDLK_x,
SDLK_y,
SDLK_z,
SDLK_LGUI,
SDLK_UNKNOWN,
SDLK_APPLICATION,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_KP_0,
SDLK_KP_1,
SDLK_KP_2,
SDLK_KP_3,
SDLK_KP_4,
SDLK_KP_5,
SDLK_KP_6,
SDLK_KP_7,
SDLK_KP_8,
SDLK_KP_9,
SDLK_KP_MULTIPLY,
SDLK_KP_PLUS,
SDLK_UNKNOWN,
SDLK_KP_MINUS,
SDLK_KP_PERIOD,
SDLK_KP_DIVIDE,
SDLK_F1,
SDLK_F2,
SDLK_F3,
SDLK_F4,
SDLK_F5,
SDLK_F6,
SDLK_F7,
SDLK_F8,
SDLK_F9,
SDLK_F10,
SDLK_F11,
SDLK_F12,
SDLK_F13,
SDLK_F14,
SDLK_F15,
SDLK_F16,
SDLK_F17,
SDLK_F18,
SDLK_F19,
SDLK_F20,
SDLK_F21,
SDLK_F22,
SDLK_F23,
SDLK_F24,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_NUMLOCKCLEAR,
SDLK_SCROLLLOCK,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_BACKQUOTE,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_KP_HASH,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_KP_MULTIPLY,
SDLK_RIGHTBRACKET,
SDLK_UNKNOWN,
SDLK_MINUS,
SDLK_VOLUMEDOWN,
SDLK_VOLUMEUP,
SDLK_AUDIONEXT,
SDLK_AUDIOPREV,
SDLK_UNKNOWN,
SDLK_AUDIOPLAY,
SDLK_UNKNOWN,
SDLK_AUDIOMUTE,
SDLK_VOLUMEDOWN,
SDLK_VOLUMEUP,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_SEMICOLON,
SDLK_EQUALS,
SDLK_COMMA,
SDLK_MINUS,
SDLK_PERIOD,
SDLK_SLASH,
SDLK_BACKQUOTE,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_UNKNOWN,
SDLK_LEFTBRACKET,
SDLK_BACKSLASH,
SDLK_RIGHTBRACKET,
SDLK_QUOTE,
};
static const SDL_Scancode emscripten_scancode_table[] = {
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_ESCAPE,
SDL_SCANCODE_0,
SDL_SCANCODE_1,
SDL_SCANCODE_2,
SDL_SCANCODE_3,
SDL_SCANCODE_4,
SDL_SCANCODE_5,
SDL_SCANCODE_6,
SDL_SCANCODE_7,
SDL_SCANCODE_8,
SDL_SCANCODE_9,
SDL_SCANCODE_MINUS,
SDL_SCANCODE_EQUALS,
SDL_SCANCODE_BACKSPACE,
SDL_SCANCODE_TAB,
SDL_SCANCODE_Q,
SDL_SCANCODE_W,
SDL_SCANCODE_E,
SDL_SCANCODE_R,
SDL_SCANCODE_T,
SDL_SCANCODE_Y,
SDL_SCANCODE_U,
SDL_SCANCODE_I,
SDL_SCANCODE_O,
SDL_SCANCODE_P,
SDL_SCANCODE_LEFTBRACKET,
SDL_SCANCODE_RIGHTBRACKET,
SDL_SCANCODE_RETURN,
SDL_SCANCODE_LCTRL,
SDL_SCANCODE_A,
SDL_SCANCODE_S,
SDL_SCANCODE_D,
SDL_SCANCODE_F,
SDL_SCANCODE_G,
SDL_SCANCODE_H,
SDL_SCANCODE_J,
SDL_SCANCODE_K,
SDL_SCANCODE_L,
SDL_SCANCODE_SEMICOLON,
SDL_SCANCODE_APOSTROPHE,
SDL_SCANCODE_GRAVE,
SDL_SCANCODE_LSHIFT,
SDL_SCANCODE_BACKSLASH,
SDL_SCANCODE_Z,
SDL_SCANCODE_X,
SDL_SCANCODE_C,
SDL_SCANCODE_V,
SDL_SCANCODE_B,
SDL_SCANCODE_N,
SDL_SCANCODE_M,
SDL_SCANCODE_COMMA,
SDL_SCANCODE_PERIOD,
SDL_SCANCODE_SLASH,
SDL_SCANCODE_RSHIFT,
SDL_SCANCODE_KP_MULTIPLY,
SDL_SCANCODE_LALT,
SDL_SCANCODE_SPACE,
SDL_SCANCODE_CAPSLOCK,
SDL_SCANCODE_F1,
SDL_SCANCODE_F2,
SDL_SCANCODE_F3,
SDL_SCANCODE_F4,
SDL_SCANCODE_F5,
SDL_SCANCODE_F6,
SDL_SCANCODE_F7,
SDL_SCANCODE_F8,
SDL_SCANCODE_F9,
SDL_SCANCODE_F10,
SDL_SCANCODE_PAUSE,
SDL_SCANCODE_SCROLLLOCK,
SDL_SCANCODE_KP_7,
SDL_SCANCODE_KP_8,
SDL_SCANCODE_KP_9,
SDL_SCANCODE_KP_MINUS,
SDL_SCANCODE_KP_4,
SDL_SCANCODE_KP_5,
SDL_SCANCODE_KP_6,
SDL_SCANCODE_KP_PLUS,
SDL_SCANCODE_KP_1,
SDL_SCANCODE_KP_2,
SDL_SCANCODE_KP_3,
SDL_SCANCODE_KP_0,
SDL_SCANCODE_KP_PERIOD,
SDL_SCANCODE_PRINTSCREEN,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_NONUSBACKSLASH,
SDL_SCANCODE_F11,
SDL_SCANCODE_F12,
SDL_SCANCODE_KP_EQUALS,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_F13,
SDL_SCANCODE_F14,
SDL_SCANCODE_F15,
SDL_SCANCODE_F16,
SDL_SCANCODE_F17,
SDL_SCANCODE_F18,
SDL_SCANCODE_F19,
SDL_SCANCODE_F20,
SDL_SCANCODE_F21,
SDL_SCANCODE_F22,
SDL_SCANCODE_F23,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_INTERNATIONAL2,
SDL_SCANCODE_LANG2,
SDL_SCANCODE_LANG1,
SDL_SCANCODE_INTERNATIONAL1,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_F24,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_INTERNATIONAL4,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_INTERNATIONAL5,
SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_INTERNATIONAL3,
SDL_SCANCODE_KP_COMMA
};
static SDL_Scancode
Emscripten_MapScanCode(const char *code)
{
const DOM_PK_CODE_TYPE pk_code = emscripten_compute_dom_pk_code(code);
if (pk_code < SDL_arraysize(emscripten_scancode_table)) {
return emscripten_scancode_table[pk_code];
}
switch (pk_code) {
case DOM_PK_PASTE:
return SDL_SCANCODE_PASTE;
case DOM_PK_MEDIA_TRACK_PREVIOUS:
return SDL_SCANCODE_AUDIOPREV;
case DOM_PK_CUT:
return SDL_SCANCODE_CUT;
case DOM_PK_COPY:
return SDL_SCANCODE_COPY;
case DOM_PK_MEDIA_TRACK_NEXT:
return SDL_SCANCODE_AUDIONEXT;
case DOM_PK_NUMPAD_ENTER:
return SDL_SCANCODE_KP_ENTER;
case DOM_PK_CONTROL_RIGHT:
return SDL_SCANCODE_RCTRL;
case DOM_PK_AUDIO_VOLUME_MUTE:
return SDL_SCANCODE_AUDIOMUTE;
case DOM_PK_LAUNCH_APP_2:
return SDL_SCANCODE_CALCULATOR;
case DOM_PK_MEDIA_PLAY_PAUSE:
return SDL_SCANCODE_AUDIOPLAY;
case DOM_PK_MEDIA_STOP:
return SDL_SCANCODE_AUDIOSTOP;
case DOM_PK_EJECT:
return SDL_SCANCODE_EJECT;
case DOM_PK_AUDIO_VOLUME_DOWN:
return SDL_SCANCODE_VOLUMEDOWN;
case DOM_PK_AUDIO_VOLUME_UP:
return SDL_SCANCODE_VOLUMEUP;
case DOM_PK_BROWSER_HOME:
return SDL_SCANCODE_AC_HOME;
case DOM_PK_NUMPAD_DIVIDE:
return SDL_SCANCODE_KP_DIVIDE;
case DOM_PK_ALT_RIGHT:
return SDL_SCANCODE_RALT;
case DOM_PK_HELP:
return SDL_SCANCODE_HELP;
case DOM_PK_NUM_LOCK:
return SDL_SCANCODE_NUMLOCKCLEAR;
case DOM_PK_HOME:
return SDL_SCANCODE_HOME;
case DOM_PK_ARROW_UP:
return SDL_SCANCODE_UP;
case DOM_PK_PAGE_UP:
return SDL_SCANCODE_PAGEUP;
case DOM_PK_ARROW_LEFT:
return SDL_SCANCODE_LEFT;
case DOM_PK_ARROW_RIGHT:
return SDL_SCANCODE_RIGHT;
case DOM_PK_END:
return SDL_SCANCODE_END;
case DOM_PK_ARROW_DOWN:
return SDL_SCANCODE_DOWN;
case DOM_PK_PAGE_DOWN:
return SDL_SCANCODE_PAGEDOWN;
case DOM_PK_INSERT:
return SDL_SCANCODE_INSERT;
case DOM_PK_DELETE:
return SDL_SCANCODE_DELETE;
case DOM_PK_META_LEFT:
return SDL_SCANCODE_LGUI;
case DOM_PK_META_RIGHT:
return SDL_SCANCODE_RGUI;
case DOM_PK_CONTEXT_MENU:
return SDL_SCANCODE_APPLICATION;
case DOM_PK_POWER:
return SDL_SCANCODE_POWER;
case DOM_PK_BROWSER_SEARCH:
return SDL_SCANCODE_AC_SEARCH;
case DOM_PK_BROWSER_FAVORITES:
return SDL_SCANCODE_AC_BOOKMARKS;
case DOM_PK_BROWSER_REFRESH:
return SDL_SCANCODE_AC_REFRESH;
case DOM_PK_BROWSER_STOP:
return SDL_SCANCODE_AC_STOP;
case DOM_PK_BROWSER_FORWARD:
return SDL_SCANCODE_AC_FORWARD;
case DOM_PK_BROWSER_BACK:
return SDL_SCANCODE_AC_BACK;
case DOM_PK_LAUNCH_APP_1:
return SDL_SCANCODE_COMPUTER;
case DOM_PK_LAUNCH_MAIL:
return SDL_SCANCODE_MAIL;
case DOM_PK_MEDIA_SELECT:
return SDL_SCANCODE_MEDIASELECT;
}
return SDL_SCANCODE_UNKNOWN;
}
static SDL_Keycode
Emscripten_MapKeyCode(const EmscriptenKeyboardEvent *keyEvent)
{
SDL_Keycode keycode = SDLK_UNKNOWN;
if (keyEvent->keyCode < SDL_arraysize(emscripten_keycode_table)) {
keycode = emscripten_keycode_table[keyEvent->keyCode];
if (keycode != SDLK_UNKNOWN) {
if (keyEvent->location == DOM_KEY_LOCATION_RIGHT) {
switch (keycode) {
case SDLK_LSHIFT:
keycode = SDLK_RSHIFT;
break;
case SDLK_LCTRL:
keycode = SDLK_RCTRL;
break;
case SDLK_LALT:
keycode = SDLK_RALT;
break;
case SDLK_LGUI:
keycode = SDLK_RGUI;
break;
}
} else if (keyEvent->location == DOM_KEY_LOCATION_NUMPAD) {
switch (keycode) {
case SDLK_0:
case SDLK_INSERT:
keycode = SDLK_KP_0;
break;
case SDLK_1:
case SDLK_END:
keycode = SDLK_KP_1;
break;
case SDLK_2:
case SDLK_DOWN:
keycode = SDLK_KP_2;
break;
case SDLK_3:
case SDLK_PAGEDOWN:
keycode = SDLK_KP_3;
break;
case SDLK_4:
case SDLK_LEFT:
keycode = SDLK_KP_4;
break;
case SDLK_5:
keycode = SDLK_KP_5;
break;
case SDLK_6:
case SDLK_RIGHT:
keycode = SDLK_KP_6;
break;
case SDLK_7:
case SDLK_HOME:
keycode = SDLK_KP_7;
break;
case SDLK_8:
case SDLK_UP:
keycode = SDLK_KP_8;
break;
case SDLK_9:
case SDLK_PAGEUP:
keycode = SDLK_KP_9;
break;
case SDLK_RETURN:
keycode = SDLK_KP_ENTER;
break;
case SDLK_DELETE:
keycode = SDLK_KP_PERIOD;
break;
}
}
}
}
return keycode;
}
static int
Emscripten_ConvertUTF32toUTF8(Uint32 codepoint, char * text)
{
if (codepoint <= 0x7F) {
text[0] = (char) codepoint;
text[1] = '\0';
} else if (codepoint <= 0x7FF) {
text[0] = 0xC0 | (char) ((codepoint >> 6) & 0x1F);
text[1] = 0x80 | (char) (codepoint & 0x3F);
text[2] = '\0';
} else if (codepoint <= 0xFFFF) {
text[0] = 0xE0 | (char) ((codepoint >> 12) & 0x0F);
text[1] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
text[2] = 0x80 | (char) (codepoint & 0x3F);
text[3] = '\0';
} else if (codepoint <= 0x10FFFF) {
text[0] = 0xF0 | (char) ((codepoint >> 18) & 0x0F);
text[1] = 0x80 | (char) ((codepoint >> 12) & 0x3F);
text[2] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
text[3] = 0x80 | (char) (codepoint & 0x3F);
text[4] = '\0';
} else {
return SDL_FALSE;
}
return SDL_TRUE;
}
static EM_BOOL
Emscripten_HandlePointerLockChange(int eventType, const EmscriptenPointerlockChangeEvent *changeEvent, void *userData)
{
SDL_WindowData *window_data = (SDL_WindowData *) userData;
window_data->has_pointer_lock = changeEvent->isActive;
return 0;
}
static EM_BOOL
Emscripten_HandleMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
{
SDL_WindowData *window_data = userData;
const int isPointerLocked = window_data->has_pointer_lock;
int mx, my;
static double residualx = 0, residualy = 0;
double client_w, client_h, xscale, yscale;
emscripten_get_element_css_size(window_data->canvas_id, &client_w, &client_h);
xscale = window_data->window->w / client_w;
yscale = window_data->window->h / client_h;
if (isPointerLocked) {
residualx += mouseEvent->movementX * xscale;
residualy += mouseEvent->movementY * yscale;
mx = residualx;
residualx -= mx;
my = residualy;
residualy -= my;
} else {
mx = mouseEvent->targetX * xscale;
my = mouseEvent->targetY * yscale;
}
SDL_SendMouseMotion(window_data->window, 0, isPointerLocked, mx, my);
return 0;
}
static EM_BOOL
Emscripten_HandleMouseButton(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
{
SDL_WindowData *window_data = userData;
Uint8 sdl_button;
Uint8 sdl_button_state;
SDL_EventType sdl_event_type;
double css_w, css_h;
switch (mouseEvent->button) {
case 0:
sdl_button = SDL_BUTTON_LEFT;
break;
case 1:
sdl_button = SDL_BUTTON_MIDDLE;
break;
case 2:
sdl_button = SDL_BUTTON_RIGHT;
break;
default:
return 0;
}
if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) {
if (SDL_GetMouse()->relative_mode && !window_data->has_pointer_lock) {
emscripten_request_pointerlock(window_data->canvas_id, 0);
}
sdl_button_state = SDL_PRESSED;
sdl_event_type = SDL_MOUSEBUTTONDOWN;
} else {
sdl_button_state = SDL_RELEASED;
sdl_event_type = SDL_MOUSEBUTTONUP;
}
SDL_SendMouseButton(window_data->window, 0, sdl_button_state, sdl_button);
emscripten_get_element_css_size(window_data->canvas_id, &css_w, &css_h);
if (mouseEvent->targetX < 0 || mouseEvent->targetX >= css_w ||
mouseEvent->targetY < 0 || mouseEvent->targetY >= css_h) {
return 0;
}
return SDL_GetEventState(sdl_event_type) == SDL_ENABLE;
}
static EM_BOOL
Emscripten_HandleMouseFocus(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
{
SDL_WindowData *window_data = userData;
int mx = mouseEvent->targetX, my = mouseEvent->targetY;
const int isPointerLocked = window_data->has_pointer_lock;
if (!isPointerLocked) {
double client_w, client_h;
emscripten_get_element_css_size(window_data->canvas_id, &client_w, &client_h);
mx = mx * (window_data->window->w / client_w);
my = my * (window_data->window->h / client_h);
SDL_SendMouseMotion(window_data->window, 0, isPointerLocked, mx, my);
}
SDL_SetMouseFocus(eventType == EMSCRIPTEN_EVENT_MOUSEENTER ? window_data->window : NULL);
return SDL_GetEventState(SDL_WINDOWEVENT) == SDL_ENABLE;
}
static EM_BOOL
Emscripten_HandleWheel(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
{
SDL_WindowData *window_data = userData;
float deltaY = wheelEvent->deltaY;
switch (wheelEvent->deltaMode) {
case DOM_DELTA_PIXEL:
deltaY /= 100;
break;
case DOM_DELTA_LINE:
deltaY /= 3;
break;
case DOM_DELTA_PAGE:
deltaY *= 80;
break;
}
SDL_SendMouseWheel(window_data->window, 0, (float)wheelEvent->deltaX, -deltaY, SDL_MOUSEWHEEL_NORMAL);
return SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE;
}
static EM_BOOL
Emscripten_HandleFocus(int eventType, const EmscriptenFocusEvent *wheelEvent, void *userData)
{
SDL_WindowData *window_data = userData;
if (eventType == EMSCRIPTEN_EVENT_BLUR) {
SDL_ResetKeyboard();
}
SDL_SendWindowEvent(window_data->window, eventType == EMSCRIPTEN_EVENT_FOCUS ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
return SDL_GetEventState(SDL_WINDOWEVENT) == SDL_ENABLE;
}
static EM_BOOL
Emscripten_HandleTouch(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
{
SDL_WindowData *window_data = (SDL_WindowData *) userData;
int i;
double client_w, client_h;
int preventDefault = 0;
SDL_TouchID deviceId = 1;
if (SDL_AddTouch(deviceId, SDL_TOUCH_DEVICE_DIRECT, "") < 0) {
return 0;
}
emscripten_get_element_css_size(window_data->canvas_id, &client_w, &client_h);
for (i = 0; i < touchEvent->numTouches; i++) {
SDL_FingerID id;
float x, y;
if (!touchEvent->touches[i].isChanged)
continue;
id = touchEvent->touches[i].identifier;
x = touchEvent->touches[i].targetX / client_w;
y = touchEvent->touches[i].targetY / client_h;
if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) {
SDL_SendTouch(deviceId, id, window_data->window, SDL_TRUE, x, y, 1.0f);
if (!preventDefault && SDL_GetEventState(SDL_FINGERDOWN) == SDL_ENABLE) {
preventDefault = 1;
}
} else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) {
SDL_SendTouchMotion(deviceId, id, window_data->window, x, y, 1.0f);
} else {
SDL_SendTouch(deviceId, id, window_data->window, SDL_FALSE, x, y, 1.0f);
preventDefault = 1;
}
}
return preventDefault;
}
static EM_BOOL
Emscripten_HandleKey(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
{
const SDL_Keycode keycode = Emscripten_MapKeyCode(keyEvent);
SDL_Scancode scancode = Emscripten_MapScanCode(keyEvent->code);
SDL_bool prevent_default = SDL_TRUE;
SDL_bool is_nav_key = SDL_FALSE;
if (scancode == SDL_SCANCODE_UNKNOWN) {
if (SDL_strncmp(keyEvent->key, "SoftLeft", 9) == 0) {
scancode = SDL_SCANCODE_AC_FORWARD;
} else if (SDL_strncmp(keyEvent->key, "SoftRight", 10) == 0) {
scancode = SDL_SCANCODE_AC_BACK;
}
}
if (scancode != SDL_SCANCODE_UNKNOWN) {
SDL_SendKeyboardKeyAndKeycode(eventType == EMSCRIPTEN_EVENT_KEYDOWN ? SDL_PRESSED : SDL_RELEASED, scancode, keycode);
}
if ( (scancode == SDL_SCANCODE_BACKSPACE) ||
(scancode == SDL_SCANCODE_TAB) ||
(scancode == SDL_SCANCODE_LEFT) ||
(scancode == SDL_SCANCODE_UP) ||
(scancode == SDL_SCANCODE_RIGHT) ||
(scancode == SDL_SCANCODE_DOWN) ||
((scancode >= SDL_SCANCODE_F1) && (scancode <= SDL_SCANCODE_F15)) ||
keyEvent->ctrlKey ) {
is_nav_key = SDL_TRUE;
}
if ((eventType == EMSCRIPTEN_EVENT_KEYDOWN) && (SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE) && !is_nav_key) {
prevent_default = SDL_FALSE;
}
return prevent_default;
}
static EM_BOOL
Emscripten_HandleKeyPress(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
{
char text[5];
if (Emscripten_ConvertUTF32toUTF8(keyEvent->charCode, text)) {
SDL_SendKeyboardText(text);
}
return SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE;
}
static EM_BOOL
Emscripten_HandleFullscreenChange(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData)
{
SDL_WindowData *window_data = userData;
SDL_VideoDisplay *display;
if(fullscreenChangeEvent->isFullscreen)
{
window_data->window->flags |= window_data->requested_fullscreen_mode;
window_data->requested_fullscreen_mode = 0;
}
else
{
window_data->window->flags &= ~FULLSCREEN_MASK;
display = SDL_GetDisplayForWindow(window_data->window);
if (display->fullscreen_window == window_data->window) {
display->fullscreen_window = NULL;
}
}
return 0;
}
static EM_BOOL
Emscripten_HandleResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
{
SDL_WindowData *window_data = userData;
SDL_bool force = SDL_FALSE;
if (window_data->window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
if (window_data->pixel_ratio != emscripten_get_device_pixel_ratio()) {
window_data->pixel_ratio = emscripten_get_device_pixel_ratio();
force = SDL_TRUE;
}
}
if(!(window_data->window->flags & FULLSCREEN_MASK))
{
if(window_data->window->flags & SDL_WINDOW_RESIZABLE)
{
double w = window_data->window->w;
double h = window_data->window->h;
if(window_data->external_size) {
emscripten_get_element_css_size(window_data->canvas_id, &w, &h);
}
emscripten_set_canvas_element_size(window_data->canvas_id, w * window_data->pixel_ratio, h * window_data->pixel_ratio);
if (!window_data->external_size && window_data->pixel_ratio != 1.0f) {
emscripten_set_element_css_size(window_data->canvas_id, w, h);
}
if (force) {
window_data->window->w = 0;
window_data->window->h = 0;
}
SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, w, h);
}
}
return 0;
}
EM_BOOL
Emscripten_HandleCanvasResize(int eventType, const void *reserved, void *userData)
{
SDL_WindowData *window_data = userData;
if(window_data->fullscreen_resize)
{
double css_w, css_h;
emscripten_get_element_css_size(window_data->canvas_id, &css_w, &css_h);
SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, css_w, css_h);
}
return 0;
}
static EM_BOOL
Emscripten_HandleVisibilityChange(int eventType, const EmscriptenVisibilityChangeEvent *visEvent, void *userData)
{
SDL_WindowData *window_data = userData;
SDL_SendWindowEvent(window_data->window, visEvent->hidden ? SDL_WINDOWEVENT_HIDDEN : SDL_WINDOWEVENT_SHOWN, 0, 0);
return 0;
}
static const char*
Emscripten_HandleBeforeUnload(int eventType, const void *reserved, void *userData)
{
SDL_SendAppEvent(SDL_APP_TERMINATING);
return "";
}
void
Emscripten_RegisterEventHandlers(SDL_WindowData *data)
{
const char *keyElement;
emscripten_set_mousemove_callback(data->canvas_id, data, 0, Emscripten_HandleMouseMove);
emscripten_set_mousedown_callback(data->canvas_id, data, 0, Emscripten_HandleMouseButton);
emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandleMouseButton);
emscripten_set_mouseenter_callback(data->canvas_id, data, 0, Emscripten_HandleMouseFocus);
emscripten_set_mouseleave_callback(data->canvas_id, data, 0, Emscripten_HandleMouseFocus);
emscripten_set_wheel_callback(data->canvas_id, data, 0, Emscripten_HandleWheel);
emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleFocus);
emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleFocus);
emscripten_set_touchstart_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
emscripten_set_touchend_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
emscripten_set_touchmove_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
emscripten_set_touchcancel_callback(data->canvas_id, data, 0, Emscripten_HandleTouch);
emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandlePointerLockChange);
keyElement = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
if (!keyElement) keyElement = EMSCRIPTEN_EVENT_TARGET_WINDOW;
emscripten_set_keydown_callback(keyElement, data, 0, Emscripten_HandleKey);
emscripten_set_keyup_callback(keyElement, data, 0, Emscripten_HandleKey);
emscripten_set_keypress_callback(keyElement, data, 0, Emscripten_HandleKeyPress);
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, data, 0, Emscripten_HandleFullscreenChange);
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, data, 0, Emscripten_HandleResize);
emscripten_set_visibilitychange_callback(data, 0, Emscripten_HandleVisibilityChange);
emscripten_set_beforeunload_callback(data, Emscripten_HandleBeforeUnload);
}
void
Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
{
const char *target;
emscripten_set_mousemove_callback(data->canvas_id, NULL, 0, NULL);
emscripten_set_mousedown_callback(data->canvas_id, NULL, 0, NULL);
emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
emscripten_set_mouseenter_callback(data->canvas_id, NULL, 0, NULL);
emscripten_set_mouseleave_callback(data->canvas_id, NULL, 0, NULL);
emscripten_set_wheel_callback(data->canvas_id, NULL, 0, NULL);
emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
emscripten_set_touchstart_callback(data->canvas_id, NULL, 0, NULL);
emscripten_set_touchend_callback(data->canvas_id, NULL, 0, NULL);
emscripten_set_touchmove_callback(data->canvas_id, NULL, 0, NULL);
emscripten_set_touchcancel_callback(data->canvas_id, NULL, 0, NULL);
emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
target = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
if (!target) {
target = EMSCRIPTEN_EVENT_TARGET_WINDOW;
}
emscripten_set_keydown_callback(target, NULL, 0, NULL);
emscripten_set_keyup_callback(target, NULL, 0, NULL);
emscripten_set_keypress_callback(target, NULL, 0, NULL);
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
emscripten_set_visibilitychange_callback(NULL, 0, NULL);
emscripten_set_beforeunload_callback(NULL, NULL);
}
#endif