#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_EMSCRIPTEN
#include <emscripten/html5.h>
#include <emscripten/dom_pk_codes.h>
#include "../../events/SDL_dropevents_c.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"
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_MEDIA_PREVIOUS_TRACK;
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_MEDIA_NEXT_TRACK;
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_MUTE;
case DOM_PK_MEDIA_PLAY_PAUSE:
return SDL_SCANCODE_MEDIA_PLAY_PAUSE;
case DOM_PK_MEDIA_STOP:
return SDL_SCANCODE_MEDIA_STOP;
case DOM_PK_EJECT:
return SDL_SCANCODE_MEDIA_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_MEDIA_SELECT:
return SDL_SCANCODE_MEDIA_SELECT;
}
return SDL_SCANCODE_UNKNOWN;
}
static SDL_Window *Emscripten_GetFocusedWindow(SDL_VideoDevice *device)
{
SDL_Window *window;
for (window = device->windows; window; window = window->next) {
SDL_WindowData *wdata = window->internal;
const int focused = MAIN_THREAD_EM_ASM_INT({
var id = UTF8ToString($0);
try
{
var canvas = document.querySelector(id);
if (canvas) {
return canvas === document.activeElement;
}
}
catch (e)
{
}
return false;
}, wdata->canvas_id);
if (focused) {
break;
}
}
if (!window) {
const int focused = MAIN_THREAD_EM_ASM_INT({
return document.hasFocus();
});
if (focused) {
window = device->windows;
}
}
return window;
}
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_HandlePointerLockChangeGlobal(int eventType, const EmscriptenPointerlockChangeEvent *changeEvent, void *userData)
{
SDL_VideoDevice *device = userData;
bool prevent_default = false;
SDL_Window *window;
for (window = device->windows; window; window = window->next) {
prevent_default |= Emscripten_HandlePointerLockChange(eventType, changeEvent, window->internal);
}
return prevent_default;
}
static EM_BOOL Emscripten_HandleWheel(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
{
SDL_WindowData *window_data = userData;
float deltaY = wheelEvent->deltaY;
float deltaX = wheelEvent->deltaX;
switch (wheelEvent->deltaMode) {
case DOM_DELTA_PIXEL:
deltaX /= 100; deltaY /= 100; break;
case DOM_DELTA_LINE:
deltaX /= 3; deltaY /= 3; break;
case DOM_DELTA_PAGE:
deltaX *= 80; deltaY *= 80; break;
}
SDL_SendMouseWheel(0, window_data->window, SDL_DEFAULT_MOUSE_ID, deltaX, -deltaY, SDL_MOUSEWHEEL_NORMAL);
return SDL_EventEnabled(SDL_EVENT_MOUSE_WHEEL);
}
static EM_BOOL Emscripten_HandleFocus(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData)
{
SDL_VideoDevice *device = userData;
SDL_Window *window = Emscripten_GetFocusedWindow(device);
SDL_EventType sdl_event_type;
if (eventType == EMSCRIPTEN_EVENT_BLUR) {
SDL_ResetKeyboard();
}
sdl_event_type = (eventType == EMSCRIPTEN_EVENT_FOCUS) ? SDL_EVENT_WINDOW_FOCUS_GAINED : SDL_EVENT_WINDOW_FOCUS_LOST;
SDL_SetKeyboardFocus(sdl_event_type == SDL_EVENT_WINDOW_FOCUS_GAINED ? window : NULL);
return SDL_EventEnabled(sdl_event_type);
}
static bool IsFunctionKey(SDL_Scancode scancode)
{
if (scancode >= SDL_SCANCODE_F1 && scancode <= SDL_SCANCODE_F12) {
return true;
}
if (scancode >= SDL_SCANCODE_F13 && scancode <= SDL_SCANCODE_F24) {
return true;
}
return false;
}
static EM_BOOL Emscripten_HandleKey(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
{
SDL_WindowData *window_data = (SDL_WindowData *)userData;
SDL_Scancode scancode = Emscripten_MapScanCode(keyEvent->code);
SDL_Keycode keycode = SDLK_UNKNOWN;
bool prevent_default = false;
bool is_nav_key = false;
if (scancode == SDL_SCANCODE_UNKNOWN) {
if (SDL_strcmp(keyEvent->key, "Sleep") == 0) {
scancode = SDL_SCANCODE_SLEEP;
} else if (SDL_strcmp(keyEvent->key, "ChannelUp") == 0) {
scancode = SDL_SCANCODE_CHANNEL_INCREMENT;
} else if (SDL_strcmp(keyEvent->key, "ChannelDown") == 0) {
scancode = SDL_SCANCODE_CHANNEL_DECREMENT;
} else if (SDL_strcmp(keyEvent->key, "MediaPlay") == 0) {
scancode = SDL_SCANCODE_MEDIA_PLAY;
} else if (SDL_strcmp(keyEvent->key, "MediaPause") == 0) {
scancode = SDL_SCANCODE_MEDIA_PAUSE;
} else if (SDL_strcmp(keyEvent->key, "MediaRecord") == 0) {
scancode = SDL_SCANCODE_MEDIA_RECORD;
} else if (SDL_strcmp(keyEvent->key, "MediaFastForward") == 0) {
scancode = SDL_SCANCODE_MEDIA_FAST_FORWARD;
} else if (SDL_strcmp(keyEvent->key, "MediaRewind") == 0) {
scancode = SDL_SCANCODE_MEDIA_REWIND;
} else if (SDL_strcmp(keyEvent->key, "Close") == 0) {
scancode = SDL_SCANCODE_AC_CLOSE;
} else if (SDL_strcmp(keyEvent->key, "New") == 0) {
scancode = SDL_SCANCODE_AC_NEW;
} else if (SDL_strcmp(keyEvent->key, "Open") == 0) {
scancode = SDL_SCANCODE_AC_OPEN;
} else if (SDL_strcmp(keyEvent->key, "Print") == 0) {
scancode = SDL_SCANCODE_AC_PRINT;
} else if (SDL_strcmp(keyEvent->key, "Save") == 0) {
scancode = SDL_SCANCODE_AC_SAVE;
} else if (SDL_strcmp(keyEvent->key, "Props") == 0) {
scancode = SDL_SCANCODE_AC_PROPERTIES;
}
}
if (scancode == SDL_SCANCODE_UNKNOWN) {
if (SDL_strcmp(keyEvent->key, "SoftLeft") == 0) {
scancode = SDL_SCANCODE_AC_FORWARD;
} else if (SDL_strcmp(keyEvent->key, "SoftRight") == 0) {
scancode = SDL_SCANCODE_AC_BACK;
}
}
if (keyEvent->location == 0 && SDL_utf8strlen(keyEvent->key) == 1) {
const char *key = keyEvent->key;
keycode = SDL_StepUTF8(&key, NULL);
if (keycode == SDL_INVALID_UNICODE_CODEPOINT) {
keycode = SDLK_UNKNOWN;
}
}
if (keycode != SDLK_UNKNOWN) {
prevent_default = SDL_SendKeyboardKeyAndKeycode(0, SDL_DEFAULT_KEYBOARD_ID, 0, scancode, keycode, (eventType == EMSCRIPTEN_EVENT_KEYDOWN));
} else {
prevent_default = SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, 0, scancode, (eventType == EMSCRIPTEN_EVENT_KEYDOWN));
}
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) ||
IsFunctionKey(scancode) ||
keyEvent->ctrlKey) {
is_nav_key = true;
}
if ((eventType == EMSCRIPTEN_EVENT_KEYDOWN) && SDL_TextInputActive(window_data->window) && !is_nav_key) {
prevent_default = false;
}
return prevent_default;
}
static EM_BOOL Emscripten_HandleKeyPress(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
{
SDL_WindowData *window_data = (SDL_WindowData *)userData;
if (SDL_TextInputActive(window_data->window)) {
char text[5];
char *end = SDL_UCS4ToUTF8(keyEvent->charCode, text);
*end = '\0';
SDL_SendKeyboardText(text);
return EM_TRUE;
}
return EM_FALSE;
}
static EM_BOOL Emscripten_HandleFullscreenChange(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData)
{
SDL_WindowData *window_data = userData;
window_data->fullscreen_change_in_progress = false;
if (fullscreenChangeEvent->isFullscreen) {
SDL_SendWindowEvent(window_data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0);
window_data->fullscreen_mode_flags = 0;
} else {
SDL_SendWindowEvent(window_data->window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
}
SDL_UpdateFullscreenMode(window_data->window, fullscreenChangeEvent->isFullscreen, false);
return 0;
}
static EM_BOOL Emscripten_HandleFullscreenChangeGlobal(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData)
{
SDL_VideoDevice *device = userData;
SDL_Window *window = NULL;
for (window = device->windows; window != NULL; window = window->next) {
const char *canvas_id = window->internal->canvas_id;
if (*canvas_id == '#') {
canvas_id++;
}
if (SDL_strcmp(fullscreenChangeEvent->id, canvas_id) == 0) {
break; }
}
if (window) {
return Emscripten_HandleFullscreenChange(eventType, fullscreenChangeEvent, window->internal);
}
return EM_FALSE;
}
static EM_BOOL Emscripten_HandleResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
{
SDL_WindowData *window_data = userData;
bool force = false;
if (window_data->window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
if (window_data->pixel_ratio != emscripten_get_device_pixel_ratio()) {
window_data->pixel_ratio = emscripten_get_device_pixel_ratio();
force = true;
}
}
const bool fill_document = (Emscripten_fill_document_window == window_data->window);
const bool fullscreen = (window_data->window->flags & SDL_WINDOW_FULLSCREEN) != 0; const bool resizable = (window_data->window->flags & SDL_WINDOW_RESIZABLE) != 0;
if (fill_document || fullscreen || resizable) {
double w, h;
if (fill_document || fullscreen) {
w = (double) uiEvent->windowInnerWidth;
h = (double) uiEvent->windowInnerHeight;
} else {
SDL_assert(window_data->window->flags & SDL_WINDOW_RESIZABLE);
w = window_data->window->w;
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, SDL_lroundf(w * window_data->pixel_ratio), SDL_lroundf(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_EVENT_WINDOW_RESIZED, SDL_lroundf(w), SDL_lroundf(h));
}
return 0;
}
static EM_BOOL Emscripten_HandleResizeGlobal(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
{
SDL_VideoDevice *device = userData;
bool prevent_default = false;
SDL_Window *window;
for (window = device->windows; window; window = window->next) {
prevent_default |= Emscripten_HandleResize(eventType, uiEvent, window->internal);
}
return prevent_default;
}
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_EVENT_WINDOW_RESIZED, SDL_lroundf(css_w), SDL_lroundf(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_EVENT_WINDOW_HIDDEN : SDL_EVENT_WINDOW_SHOWN, 0, 0);
return 0;
}
static const char *Emscripten_HandleBeforeUnload(int eventType, const void *reserved, void *userData)
{
SDL_SendAppEvent(SDL_EVENT_TERMINATING);
return ""; }
static EM_BOOL Emscripten_HandleOrientationChange(int eventType, const EmscriptenOrientationChangeEvent *orientationChangeEvent, void *userData)
{
SDL_DisplayOrientation orientation;
switch (orientationChangeEvent->orientationIndex) {
#define CHECK_ORIENTATION(emsdk, sdl) case EMSCRIPTEN_ORIENTATION_##emsdk: orientation = SDL_ORIENTATION_##sdl; break
CHECK_ORIENTATION(LANDSCAPE_PRIMARY, LANDSCAPE);
CHECK_ORIENTATION(LANDSCAPE_SECONDARY, LANDSCAPE_FLIPPED);
CHECK_ORIENTATION(PORTRAIT_PRIMARY, PORTRAIT);
CHECK_ORIENTATION(PORTRAIT_SECONDARY, PORTRAIT_FLIPPED);
#undef CHECK_ORIENTATION
default: orientation = SDL_ORIENTATION_UNKNOWN; break;
}
SDL_WindowData *window_data = (SDL_WindowData *) userData;
SDL_SendDisplayEvent(SDL_GetVideoDisplayForWindow(window_data->window), SDL_EVENT_DISPLAY_ORIENTATION, orientation, 0);
EmscriptenUiEvent uiEvent;
SDL_zero(uiEvent);
uiEvent.documentBodyClientWidth = MAIN_THREAD_EM_ASM_INT( { return document.body.clientWidth; } );
uiEvent.documentBodyClientHeight = MAIN_THREAD_EM_ASM_INT( { return document.body.clientHeight; } );
uiEvent.windowInnerWidth = MAIN_THREAD_EM_ASM_INT( { return window.innerWidth; } );
uiEvent.windowInnerHeight = MAIN_THREAD_EM_ASM_INT( { return window.innerHeight; } );
uiEvent.windowOuterWidth = MAIN_THREAD_EM_ASM_INT( { return window.outerWidth; } );
uiEvent.windowOuterHeight = MAIN_THREAD_EM_ASM_INT( { return window.outerHeight; } );
uiEvent.scrollTop = MAIN_THREAD_EM_ASM_INT( { return window.pageXOffset; } );
uiEvent.scrollLeft = MAIN_THREAD_EM_ASM_INT( { return window.pageYOffset; } );
Emscripten_HandleResize(EMSCRIPTEN_EVENT_RESIZE, &uiEvent, userData);
return 0;
}
#define PTRTYPE_MOUSE 1
#define PTRTYPE_TOUCH 2
#define PTRTYPE_PEN 3
typedef struct Emscripten_PointerEvent
{
int pointer_type;
int pointerid;
int button;
int buttons;
int down;
float movementX;
float movementY;
float targetX;
float targetY;
float pressure;
float tangential_pressure;
float tiltx;
float tilty;
float rotation;
} Emscripten_PointerEvent;
static void Emscripten_HandleMouseButton(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
{
Uint8 sdl_button;
bool down = false;
switch (event->button) {
#define CHECK_MOUSE_BUTTON(jsbutton, downflag, sdlbutton) case jsbutton: sdl_button = SDL_BUTTON_##sdlbutton; down = (event->down != 0) || ((event->buttons & downflag) != 0); break
CHECK_MOUSE_BUTTON(0, 1, LEFT);
CHECK_MOUSE_BUTTON(1, 4, MIDDLE);
CHECK_MOUSE_BUTTON(2, 2, RIGHT);
CHECK_MOUSE_BUTTON(3, 8, X1);
CHECK_MOUSE_BUTTON(4, 16, X2);
#undef CHECK_MOUSE_BUTTON
default: sdl_button = 0; break;
}
if (sdl_button) {
const SDL_Mouse *mouse = SDL_GetMouse();
SDL_assert(mouse != NULL);
if (down) {
if (mouse->relative_mode && !window_data->has_pointer_lock) {
emscripten_request_pointerlock(window_data->canvas_id, 0); }
}
SDL_SendMouseButton(0, window_data->window, SDL_DEFAULT_MOUSE_ID, sdl_button, down);
if (mouse->auto_capture) {
if (SDL_GetMouseState(NULL, NULL) != 0) {
window_data->window->flags |= SDL_WINDOW_MOUSE_CAPTURE;
} else {
window_data->window->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
}
}
if (!down && window_data->mouse_focus_loss_pending) {
window_data->mouse_focus_loss_pending = (window_data->window->flags & SDL_WINDOW_MOUSE_CAPTURE) != 0;
if (!window_data->mouse_focus_loss_pending) {
SDL_SetMouseFocus(NULL);
}
}
}
}
static void Emscripten_UpdateMouseFromEvent(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
{
SDL_assert(event->pointer_type == PTRTYPE_MOUSE);
double client_w, client_h;
emscripten_get_element_css_size(window_data->canvas_id, &client_w, &client_h);
const double xscale = window_data->window->w / client_w;
const double yscale = window_data->window->h / client_h;
const bool isPointerLocked = window_data->has_pointer_lock;
float mx, my;
if (isPointerLocked) {
mx = (float)(event->movementX * xscale);
my = (float)(event->movementY * yscale);
} else {
mx = (float)(event->targetX * xscale);
my = (float)(event->targetY * yscale);
}
SDL_SendMouseMotion(0, window_data->window, SDL_DEFAULT_MOUSE_ID, isPointerLocked, mx, my);
Emscripten_HandleMouseButton(window_data, event);
}
static void Emscripten_UpdateTouchFromEvent(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
{
SDL_assert(event->pointer_type == PTRTYPE_TOUCH);
const SDL_TouchID deviceId = 1;
if (SDL_AddTouch(deviceId, SDL_TOUCH_DEVICE_DIRECT, "") < 0) {
return;
}
double client_w, client_h;
emscripten_get_element_css_size(window_data->canvas_id, &client_w, &client_h);
const SDL_FingerID id = event->pointerid + 1;
float x, y;
if (client_w <= 1) {
x = 0.5f;
} else {
x = event->targetX / (client_w - 1);
}
if (client_h <= 1) {
y = 0.5f;
} else {
y = event->targetY / (client_h - 1);
}
const bool down = (event->buttons & 1) != 0;
if (event->button == 0) { if (down) {
SDL_SendTouch(0, deviceId, id, window_data->window, SDL_EVENT_FINGER_DOWN, x, y, 1.0f);
}
}
SDL_SendTouchMotion(0, deviceId, id, window_data->window, x, y, 1.0f);
if (event->button == 0) { if (!down) {
SDL_SendTouch(0, deviceId, id, window_data->window, SDL_EVENT_FINGER_UP, x, y, 1.0f);
}
}
}
static void Emscripten_UpdatePenFromEvent(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
{
SDL_assert(event->pointer_type == PTRTYPE_PEN);
const SDL_PenID pen = SDL_FindPenByHandle((void *) (size_t) 1); if (pen) {
double client_w, client_h;
emscripten_get_element_css_size(window_data->canvas_id, &client_w, &client_h);
const double xscale = window_data->window->w / client_w;
const double yscale = window_data->window->h / client_h;
const bool isPointerLocked = window_data->has_pointer_lock;
float mx, my;
if (isPointerLocked) {
mx = (float)(event->movementX * xscale);
my = (float)(event->movementY * yscale);
} else {
mx = (float)(event->targetX * xscale);
my = (float)(event->targetY * yscale);
}
SDL_SendPenMotion(0, pen, window_data->window, mx, my);
if (event->button == 0) { bool down = ((event->buttons & 1) != 0);
SDL_SendPenTouch(0, pen, window_data->window, false, down);
} else if (event->button == 5) { bool down = ((event->buttons & 32) != 0);
SDL_SendPenTouch(0, pen, window_data->window, true, down);
} else if (event->button == 1) {
bool down = ((event->buttons & 4) != 0);
SDL_SendPenButton(0, pen, window_data->window, 2, down);
} else if (event->button == 2) {
bool down = ((event->buttons & 2) != 0);
SDL_SendPenButton(0, pen, window_data->window, 1, down);
}
SDL_SendPenAxis(0, pen, window_data->window, SDL_PEN_AXIS_PRESSURE, event->pressure);
SDL_SendPenAxis(0, pen, window_data->window, SDL_PEN_AXIS_TANGENTIAL_PRESSURE, event->tangential_pressure);
SDL_SendPenAxis(0, pen, window_data->window, SDL_PEN_AXIS_XTILT, event->tiltx);
SDL_SendPenAxis(0, pen, window_data->window, SDL_PEN_AXIS_YTILT, event->tilty);
SDL_SendPenAxis(0, pen, window_data->window, SDL_PEN_AXIS_ROTATION, event->rotation);
}
}
static void Emscripten_UpdatePointerFromEvent(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
{
SDL_assert(event != NULL);
if (event->pointer_type == PTRTYPE_MOUSE) {
Emscripten_UpdateMouseFromEvent(window_data, event);
} else if (event->pointer_type == PTRTYPE_TOUCH) {
Emscripten_UpdateTouchFromEvent(window_data, event);
} else if (event->pointer_type == PTRTYPE_PEN) {
Emscripten_UpdatePenFromEvent(window_data, event);
} else {
SDL_assert(!"Unexpected pointer event type");
}
}
static void Emscripten_HandleMouseFocus(SDL_WindowData *window_data, const Emscripten_PointerEvent *event, bool isenter)
{
SDL_assert(event->pointer_type == PTRTYPE_MOUSE);
const bool isPointerLocked = window_data->has_pointer_lock;
if (!isPointerLocked) {
float mx, my;
double client_w, client_h;
emscripten_get_element_css_size(window_data->canvas_id, &client_w, &client_h);
mx = (float)(event->targetX * (window_data->window->w / client_w));
my = (float)(event->targetY * (window_data->window->h / client_h));
SDL_SendMouseMotion(0, window_data->window, SDL_GLOBAL_MOUSE_ID, isPointerLocked, mx, my);
}
if (isenter && window_data->mouse_focus_loss_pending) {
window_data->mouse_focus_loss_pending = false; } else if (!isenter && (window_data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
window_data->mouse_focus_loss_pending = true; } else {
SDL_SetMouseFocus(isenter ? window_data->window : NULL);
}
}
static void Emscripten_HandlePenEnter(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
{
SDL_assert(event->pointer_type == PTRTYPE_PEN);
SDL_PenID pen = SDL_FindPenByHandle((void *) (size_t) 1); if (pen) {
SDL_SendPenProximity(0, pen, window_data->window, true, true);
} else {
SDL_PenInfo peninfo;
SDL_zero(peninfo);
peninfo.capabilities = SDL_PEN_CAPABILITY_PRESSURE | SDL_PEN_CAPABILITY_ROTATION | SDL_PEN_CAPABILITY_XTILT | SDL_PEN_CAPABILITY_YTILT | SDL_PEN_CAPABILITY_TANGENTIAL_PRESSURE | SDL_PEN_CAPABILITY_ERASER;
peninfo.max_tilt = 90.0f;
peninfo.num_buttons = 2;
peninfo.subtype = SDL_PEN_TYPE_PEN;
SDL_AddPenDevice(0, NULL, window_data->window, &peninfo, (void *) (size_t) 1, true);
}
Emscripten_UpdatePenFromEvent(window_data, event);
}
EMSCRIPTEN_KEEPALIVE void Emscripten_HandlePointerEnter(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
{
SDL_assert(event != NULL);
if (event->pointer_type == PTRTYPE_MOUSE) {
Emscripten_HandleMouseFocus(window_data, event, true);
} else if (event->pointer_type == PTRTYPE_PEN) {
Emscripten_HandlePenEnter(window_data, event);
} else if (event->pointer_type == PTRTYPE_TOUCH) {
} else {
SDL_assert(!"Unexpected pointer event type");
}
}
static void Emscripten_HandlePenLeave(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
{
const SDL_PenID pen = SDL_FindPenByHandle((void *) (size_t) 1); if (pen) {
Emscripten_UpdatePointerFromEvent(window_data, event); SDL_SendPenProximity(0, pen, window_data->window, false, false);
}
}
static void Emscripten_HandleTouchCancel(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
{
SDL_assert(event->pointer_type == PTRTYPE_TOUCH);
const SDL_TouchID deviceId = 1;
if (SDL_AddTouch(deviceId, SDL_TOUCH_DEVICE_DIRECT, "") < 0) {
return;
}
double client_w, client_h;
emscripten_get_element_css_size(window_data->canvas_id, &client_w, &client_h);
const SDL_FingerID id = event->pointerid + 1;
float x, y;
if (client_w <= 1) {
x = 0.5f;
} else {
x = event->targetX / (client_w - 1);
}
if (client_h <= 1) {
y = 0.5f;
} else {
y = event->targetY / (client_h - 1);
}
SDL_SendTouch(0, deviceId, id, window_data->window, SDL_EVENT_FINGER_CANCELED, x, y, 1.0f);
}
EMSCRIPTEN_KEEPALIVE void Emscripten_HandlePointerLeave(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
{
SDL_assert(event != NULL);
if (event->pointer_type == PTRTYPE_MOUSE) {
Emscripten_HandleMouseFocus(window_data, event, false);
} else if (event->pointer_type == PTRTYPE_PEN) {
Emscripten_HandlePenLeave(window_data, event);
} else if (event->pointer_type == PTRTYPE_TOUCH) {
Emscripten_HandleTouchCancel(window_data, event);
} else {
SDL_assert(!"Unexpected pointer event type");
}
}
EMSCRIPTEN_KEEPALIVE void Emscripten_HandlePointerGeneric(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
{
SDL_assert(event != NULL);
Emscripten_UpdatePointerFromEvent(window_data, event);
}
static void Emscripten_prep_pointer_event_callbacks(void)
{
MAIN_THREAD_EM_ASM({
var SDL3 = Module['SDL3'];
if (SDL3.makePointerEventCStruct === undefined) {
SDL3.makePointerEventCStruct = function(left, top, event) {
var ptrtype = 0;
if (event.pointerType == "mouse") {
ptrtype = 1;
} else if (event.pointerType == "touch") {
ptrtype = 2;
} else if (event.pointerType == "pen") {
ptrtype = 3;
} else {
return 0;
}
var ptr = _SDL_malloc($0);
if (ptr != 0) {
var idx = SDL3.CPtrToHeap32Index(ptr);
HEAP32[idx++] = ptrtype;
HEAP32[idx++] = event.pointerId;
HEAP32[idx++] = (typeof(event.button) !== "undefined") ? event.button : -1;
HEAP32[idx++] = event.buttons;
HEAP32[idx++] = (event.type == "pointerdown") ? 1 : 0;
HEAPF32[idx++] = event.movementX;
HEAPF32[idx++] = event.movementY;
HEAPF32[idx++] = event.clientX - left;
HEAPF32[idx++] = event.clientY - top;
if (ptrtype == 3) {
HEAPF32[idx++] = event.pressure;
HEAPF32[idx++] = event.tangentialPressure;
HEAPF32[idx++] = event.tiltX;
HEAPF32[idx++] = event.tiltY;
HEAPF32[idx++] = event.twist;
}
}
return ptr;
};
}
}, sizeof (Emscripten_PointerEvent));
}
static void Emscripten_set_pointer_event_callbacks(SDL_WindowData *data)
{
Emscripten_prep_pointer_event_callbacks();
MAIN_THREAD_EM_ASM({
var target = document.querySelector(UTF8ToString($1));
if (target) {
var SDL3 = Module['SDL3'];
var data = $0;
target.sdlEventHandlerPointerEnter = function(event) {
var rect = target.getBoundingClientRect();
var d = SDL3.makePointerEventCStruct(rect.left, rect.top, event);
if (d != 0)
{
_Emscripten_HandlePointerEnter(SDL3.JSVarToCPtr(data), d);
_SDL_free(d);
}
};
target.sdlEventHandlerPointerLeave = function(event) {
var rect = target.getBoundingClientRect();
var d = SDL3.makePointerEventCStruct(rect.left, rect.top, event);
if (d != 0)
{
_Emscripten_HandlePointerLeave(SDL3.JSVarToCPtr(data), d);
_SDL_free(d);
}
};
target.sdlEventHandlerPointerGeneric = function(event) {
var rect = target.getBoundingClientRect();
var d = SDL3.makePointerEventCStruct(rect.left, rect.top, event);
if (d != 0)
{
_Emscripten_HandlePointerGeneric(SDL3.JSVarToCPtr(data), d);
_SDL_free(d);
}
};
target.style.touchAction = "none"; target.addEventListener("pointerenter", target.sdlEventHandlerPointerEnter);
target.addEventListener("pointerleave", target.sdlEventHandlerPointerLeave);
target.addEventListener("pointercancel", target.sdlEventHandlerPointerLeave);
target.addEventListener("pointerdown", target.sdlEventHandlerPointerGeneric);
target.addEventListener("pointermove", target.sdlEventHandlerPointerGeneric);
target.addEventListener("pointerup", target.sdlEventHandlerPointerGeneric);
}
}, data, data->canvas_id);
}
static void Emscripten_unset_pointer_event_callbacks(SDL_WindowData *data)
{
MAIN_THREAD_EM_ASM({
var target = document.querySelector(UTF8ToString($0));
if (target) {
target.removeEventListener("pointerenter", target.sdlEventHandlerPointerEnter);
target.removeEventListener("pointerleave", target.sdlEventHandlerPointerLeave);
target.removeEventListener("pointercancel", target.sdlEventHandlerPointerLeave);
target.removeEventListener("pointerdown", target.sdlEventHandlerPointerGeneric);
target.removeEventListener("pointermove", target.sdlEventHandlerPointerGeneric);
target.removeEventListener("pointerup", target.sdlEventHandlerPointerGeneric);
target.style.touchAction = ""; target.sdlEventHandlerPointerEnter = undefined;
target.sdlEventHandlerPointerLeave = undefined;
target.sdlEventHandlerPointerGeneric = undefined;
}
}, data->canvas_id);
}
EMSCRIPTEN_KEEPALIVE void Emscripten_HandleMouseButtonUpGlobal(SDL_VideoDevice *device, const Emscripten_PointerEvent *event)
{
SDL_assert(device != NULL);
SDL_assert(event != NULL);
if (event->pointer_type == PTRTYPE_MOUSE) {
for (SDL_Window *window = device->windows; window; window = window->next) {
Emscripten_HandleMouseButton(window->internal, event);
}
}
}
static void Emscripten_set_global_mouseup_callback(SDL_VideoDevice *device)
{
Emscripten_prep_pointer_event_callbacks();
MAIN_THREAD_EM_ASM({
var target = document;
if (target) {
target.sdlEventHandlerMouseButtonUpGlobal = function(event) {
var SDL3 = Module['SDL3'];
var d = SDL3.makePointerEventCStruct(0, 0, event);
if (d != 0)
{
_Emscripten_HandleMouseButtonUpGlobal(SDL3.JSVarToCPtr($0), d);
_SDL_free(d);
}
};
target.addEventListener("pointerup", target.sdlEventHandlerMouseButtonUpGlobal);
}
}, device);
}
static void Emscripten_unset_global_mouseup_callback(SDL_VideoDevice *device)
{
MAIN_THREAD_EM_ASM({
var target = document;
if (target) {
target.removeEventListener("pointerup", target.sdlEventHandlerMouseButtonUpGlobal);
target.sdlEventHandlerMouseButtonUpGlobal = undefined;
}
});
}
typedef struct Emscripten_DropEvent
{
int x;
int y;
} Emscripten_DropEvent;
EMSCRIPTEN_KEEPALIVE void Emscripten_SendDragEvent(SDL_WindowData *window_data, const Emscripten_DropEvent *event)
{
SDL_SendDropPosition(window_data->window, event->x, event->y);
}
EMSCRIPTEN_KEEPALIVE void Emscripten_SendDragCompleteEvent(SDL_WindowData *window_data)
{
SDL_SendDropComplete(window_data->window);
}
EMSCRIPTEN_KEEPALIVE void Emscripten_SendDragTextEvent(SDL_WindowData *window_data, char *text)
{
SDL_SendDropText(window_data->window, text);
}
EMSCRIPTEN_KEEPALIVE void Emscripten_SendDragFileEvent(SDL_WindowData *window_data, char *filename)
{
SDL_SendDropFile(window_data->window, NULL, filename);
}
EM_JS_DEPS(dragndrop, "$writeArrayToMemory");
static void Emscripten_set_drag_event_callbacks(SDL_WindowData *data)
{
MAIN_THREAD_EM_ASM({
var target = document.querySelector(UTF8ToString($1));
if (target) {
var data = $0;
var SDL3 = Module['SDL3'];
var makeDropEventCStruct = function(event) {
var ptr = 0;
ptr = _SDL_malloc($2);
if (ptr != 0) {
var idx = ptr >> 2;
var rect = target.getBoundingClientRect();
HEAP32[idx++] = event.clientX - rect.left;
HEAP32[idx++] = event.clientY - rect.top;
}
return ptr;
};
SDL3.eventHandlerDropDragover = function(event) {
event.preventDefault();
var d = makeDropEventCStruct(event); if (d != 0) { _Emscripten_SendDragEvent(data, d); _SDL_free(d); }
};
target.addEventListener("dragover", SDL3.eventHandlerDropDragover);
SDL3.drop_count = 0;
try { FS.mkdir("/tmp/filedrop"); } catch (e) {}
SDL3.eventHandlerDropDrop = function(event) {
event.preventDefault();
if (event.dataTransfer.types.includes("text/plain")) {
let plain_text = stringToNewUTF8(event.dataTransfer.getData("text/plain"));
_Emscripten_SendDragTextEvent(data, plain_text);
_Emscripten_force_free(plain_text);
} else if (event.dataTransfer.types.includes("Files")) {
let files_read = 0;
const files_to_read = event.dataTransfer.files.length;
for (let i = 0; i < files_to_read; i++) {
const file = event.dataTransfer.files.item(i);
const file_reader = new FileReader();
file_reader.readAsArrayBuffer(file);
file_reader.onload = function(event) {
const fs_dropdir = `/tmp/filedrop/${SDL3.drop_count}`;
SDL3.drop_count += 1;
const fs_filepath = `${fs_dropdir}/${file.name}`;
const c_fs_filepath = stringToNewUTF8(fs_filepath);
const contents_array8 = new Uint8Array(event.target.result);
try {
FS.mkdir(fs_dropdir);
var stream = FS.open(fs_filepath, "w");
FS.write(stream, contents_array8, 0, contents_array8.length, 0);
FS.close(stream);
_Emscripten_SendDragFileEvent(data, c_fs_filepath);
} catch (e) {
}
_Emscripten_force_free(c_fs_filepath);
onFileRead();
};
file_reader.onerror = function(event) {
onFileRead();
};
}
function onFileRead() {
++files_read;
if (files_read === files_to_read) {
_Emscripten_SendDragCompleteEvent(data);
}
}
}
_Emscripten_SendDragCompleteEvent(data);
};
target.addEventListener("drop", SDL3.eventHandlerDropDrop);
SDL3.eventHandlerDropDragend = function(event) {
event.preventDefault();
_Emscripten_SendDragCompleteEvent(data);
};
target.addEventListener("dragend", SDL3.eventHandlerDropDragend);
target.addEventListener("dragleave", SDL3.eventHandlerDropDragend);
}
}, data, data->canvas_id, sizeof (Emscripten_DropEvent));
}
static void Emscripten_unset_drag_event_callbacks(SDL_WindowData *data)
{
MAIN_THREAD_EM_ASM({
var target = document.querySelector(UTF8ToString($0));
if (target) {
var SDL3 = Module['SDL3'];
target.removeEventListener("dragleave", SDL3.eventHandlerDropDragend);
target.removeEventListener("dragend", SDL3.eventHandlerDropDragend);
target.removeEventListener("drop", SDL3.eventHandlerDropDrop);
SDL3.drop_count = undefined;
function recursive_remove(dirpath) {
FS.readdir(dirpath).forEach((filename) => {
const p = `${dirpath}/${filename}`;
const p_s = FS.stat(p);
if (FS.isFile(p_s.mode)) {
FS.unlink(p);
} else if (FS.isDir(p)) {
recursive_remove(p);
}
});
FS.rmdir(dirpath);
}("/tmp/filedrop");
FS.rmdir("/tmp/filedrop");
target.removeEventListener("dragover", SDL3.eventHandlerDropDragover);
SDL3.eventHandlerDropDragover = undefined;
SDL3.eventHandlerDropDrop = undefined;
SDL3.eventHandlerDropDragend = undefined;
}
}, data->canvas_id);
}
static const char *Emscripten_GetKeyboardTargetElement(const char *target)
{
if (SDL_strcmp(target, "#none") == 0) {
return NULL;
} else if (SDL_strcmp(target, "#window") == 0) {
return EMSCRIPTEN_EVENT_TARGET_WINDOW;
} else if (SDL_strcmp(target, "#document") == 0) {
return EMSCRIPTEN_EVENT_TARGET_DOCUMENT;
} else if (SDL_strcmp(target, "#screen") == 0) {
return EMSCRIPTEN_EVENT_TARGET_SCREEN;
}
return target;
}
void Emscripten_RegisterGlobalEventHandlers(SDL_VideoDevice *device)
{
Emscripten_set_global_mouseup_callback(device);
emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, device, 0, Emscripten_HandleFocus);
emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, device, 0, Emscripten_HandleFocus);
emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, device, 0, Emscripten_HandlePointerLockChangeGlobal);
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, device, 0, Emscripten_HandleFullscreenChangeGlobal);
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, device, 0, Emscripten_HandleResizeGlobal);
}
void Emscripten_UnregisterGlobalEventHandlers(SDL_VideoDevice *device)
{
Emscripten_unset_global_mouseup_callback(device);
emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, NULL);
emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 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_KEEPALIVE void Emscripten_HandleLockKeysCheck(SDL_WindowData *window_data, EM_BOOL capslock, EM_BOOL numlock, EM_BOOL scrolllock)
{
const SDL_Keymod new_mods = (capslock ? SDL_KMOD_CAPS : 0) | (numlock ? SDL_KMOD_NUM : 0) | (scrolllock ? SDL_KMOD_SCROLL : 0);
SDL_Keymod modstate = SDL_GetModState();
if ((modstate & (SDL_KMOD_CAPS|SDL_KMOD_NUM|SDL_KMOD_SCROLL)) != new_mods) {
modstate &= ~(SDL_KMOD_CAPS|SDL_KMOD_NUM|SDL_KMOD_SCROLL);
modstate |= new_mods;
SDL_SetModState(modstate);
}
}
void Emscripten_RegisterEventHandlers(SDL_WindowData *data)
{
const char *keyElement;
emscripten_set_wheel_callback(data->canvas_id, data, 0, Emscripten_HandleWheel);
emscripten_set_orientationchange_callback(data, 0, Emscripten_HandleOrientationChange);
keyElement = Emscripten_GetKeyboardTargetElement(data->keyboard_element);
if (keyElement) {
MAIN_THREAD_EM_ASM_INT({
var data = $0;
document.sdlEventHandlerLockKeysCheck = function(event) {
if ((event.key != "CapsLock") && (event.key != "NumLock") && (event.key != "ScrollLock"))
{
_Emscripten_HandleLockKeysCheck(Module['SDL3'].JSVarToCPtr(data), event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("ScrollLock"));
}
};
document.addEventListener("keydown", document.sdlEventHandlerLockKeysCheck);
}, data);
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_visibilitychange_callback(data, 0, Emscripten_HandleVisibilityChange);
emscripten_set_beforeunload_callback(data, Emscripten_HandleBeforeUnload);
Emscripten_set_pointer_event_callbacks(data);
Emscripten_set_drag_event_callbacks(data);
}
void Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
{
const char *keyElement;
Emscripten_unset_drag_event_callbacks(data);
Emscripten_unset_pointer_event_callbacks(data);
emscripten_set_wheel_callback(data->canvas_id, NULL, 0, NULL);
emscripten_set_orientationchange_callback(NULL, 0, NULL);
keyElement = Emscripten_GetKeyboardTargetElement(data->keyboard_element);
if (keyElement) {
emscripten_set_keydown_callback(keyElement, NULL, 0, NULL);
emscripten_set_keyup_callback(keyElement, NULL, 0, NULL);
emscripten_set_keypress_callback(keyElement, NULL, 0, NULL);
MAIN_THREAD_EM_ASM_INT({
document.removeEventListener("keydown", document.sdlEventHandlerLockKeysCheck);
});
}
emscripten_set_visibilitychange_callback(NULL, 0, NULL);
emscripten_set_beforeunload_callback(NULL, NULL);
}
#endif