From 536126fdcf6b507074f9a9240c0630e748658e1f Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Sun, 13 Jul 2025 14:57:48 -0400
Subject: [PATCH] emscripten: Move over to using Pointer Events for all mouse,
pen, and touch.
This allows us to avoid browser mouse emulation for touches, since we provide
our own anyhow.
The other option is to "prevent default" in the legacy touch event handlers we
historically used, to tell the browser not to supply emulation, but we can't
currently tell Emscripten to mark those handlers as not "passive," so as it
stands they are unable to prevent default. Using Pointer Events bypasses this
problem entirely.
Fixes #13161.
---
src/video/emscripten/SDL_emscriptenevents.c | 593 +++++++++++---------
1 file changed, 329 insertions(+), 264 deletions(-)
diff --git a/src/video/emscripten/SDL_emscriptenevents.c b/src/video/emscripten/SDL_emscriptenevents.c
index cc999a74ddb02..69c685ba6fc8b 100644
--- a/src/video/emscripten/SDL_emscriptenevents.c
+++ b/src/video/emscripten/SDL_emscriptenevents.c
@@ -310,135 +310,6 @@ static EM_BOOL Emscripten_HandlePointerLockChangeGlobal(int eventType, const Ems
return prevent_default;
}
-static EM_BOOL Emscripten_HandleMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
-{
- SDL_WindowData *window_data = userData;
- const bool isPointerLocked = window_data->has_pointer_lock;
- float mx, my;
-
- // rescale (in case canvas is being scaled)
- 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) {
- mx = (float)(mouseEvent->movementX * xscale);
- my = (float)(mouseEvent->movementY * yscale);
- } else {
- mx = (float)(mouseEvent->targetX * xscale);
- my = (float)(mouseEvent->targetY * yscale);
- }
-
- SDL_SendMouseMotion(0, window_data->window, SDL_DEFAULT_MOUSE_ID, 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;
- bool sdl_button_state;
- double css_w, css_h;
- bool prevent_default = false; // needed for iframe implementation in Chrome-based browsers.
-
- 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;
- }
-
- const SDL_Mouse *mouse = SDL_GetMouse();
- SDL_assert(mouse != NULL);
-
- if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) {
- if (mouse->relative_mode && !window_data->has_pointer_lock) {
- emscripten_request_pointerlock(window_data->canvas_id, 0); // try to regrab lost pointer lock.
- }
- sdl_button_state = true;
- } else {
- sdl_button_state = false;
- prevent_default = SDL_EventEnabled(SDL_EVENT_MOUSE_BUTTON_UP);
- }
-
- SDL_SendMouseButton(0, window_data->window, SDL_DEFAULT_MOUSE_ID, sdl_button, sdl_button_state);
-
- // We have an imaginary mouse capture, because we need SDL to not drop our imaginary mouse focus when we leave the canvas.
- 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 ((eventType == EMSCRIPTEN_EVENT_MOUSEUP) && 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);
- }
- } else {
- // Do not consume the event if the mouse is outside of the canvas.
- 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 prevent_default;
-}
-
-static EM_BOOL Emscripten_HandleMouseButtonGlobal(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
-{
- SDL_VideoDevice *device = userData;
- bool prevent_default = false;
- SDL_Window *window;
-
- for (window = device->windows; window; window = window->next) {
- prevent_default |= Emscripten_HandleMouseButton(eventType, mouseEvent, window->internal);
- }
-
- return prevent_default;
-}
-
-static EM_BOOL Emscripten_HandleMouseFocus(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
-{
- SDL_WindowData *window_data = userData;
-
- const bool isPointerLocked = window_data->has_pointer_lock;
-
- if (!isPointerLocked) {
- // rescale (in case canvas is being scaled)
- float mx, my;
- double client_w, client_h;
- emscripten_get_element_css_size(window_data->canvas_id, &client_w, &client_h);
-
- mx = (float)(mouseEvent->targetX * (window_data->window->w / client_w));
- my = (float)(mouseEvent->targetY * (window_data->window->h / client_h));
- SDL_SendMouseMotion(0, window_data->window, SDL_GLOBAL_MOUSE_ID, isPointerLocked, mx, my);
- }
-
- const bool isenter = (eventType == EMSCRIPTEN_EVENT_MOUSEENTER);
- if (isenter && window_data->mouse_focus_loss_pending) {
- window_data->mouse_focus_loss_pending = false; // just drop the state, but don't send the enter event.
- } else if (!isenter && (window_data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
- window_data->mouse_focus_loss_pending = true; // waiting on a mouse button to let go before we send the mouse focus update.
- } else {
- SDL_SetMouseFocus(isenter ? window_data->window : NULL);
- }
-
- return SDL_EventEnabled(SDL_EVENT_MOUSE_MOTION); // !!! FIXME: should this be MOUSE_MOTION or something else?
-}
-
static EM_BOOL Emscripten_HandleWheel(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
{
SDL_WindowData *window_data = userData;
@@ -483,62 +354,6 @@ static EM_BOOL Emscripten_HandleFocus(int eventType, const EmscriptenFocusEvent
return SDL_EventEnabled(sdl_event_type);
}
-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;
-
- const 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 + 1;
- if (client_w <= 1) {
- x = 0.5f;
- } else {
- x = touchEvent->touches[i].targetX / (client_w - 1);
- }
- if (client_h <= 1) {
- y = 0.5f;
- } else {
- y = touchEvent->touches[i].targetY / (client_h - 1);
- }
-
- if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) {
- SDL_SendTouch(0, deviceId, id, window_data->window, SDL_EVENT_FINGER_DOWN, x, y, 1.0f);
-
- // disable browser scrolling/pinch-to-zoom if app handles touch events
- if (!preventDefault && SDL_EventEnabled(SDL_EVENT_FINGER_DOWN)) {
- preventDefault = 1;
- }
- } else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) {
- SDL_SendTouchMotion(0, deviceId, id, window_data->window, x, y, 1.0f);
- } else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) {
- SDL_SendTouch(0, deviceId, id, window_data->window, SDL_EVENT_FINGER_UP, x, y, 1.0f);
-
- // block browser's simulated mousedown/mouseup on touchscreen devices
- preventDefault = 1;
- } else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) {
- SDL_SendTouch(0, deviceId, id, window_data->window, SDL_EVENT_FINGER_CANCELED, x, y, 1.0f);
- }
- }
-
- return preventDefault;
-}
-
static bool IsFunctionKey(SDL_Scancode scancode)
{
if (scancode >= SDL_SCANCODE_F1 && scancode <= SDL_SCANCODE_F12) {
@@ -783,9 +598,13 @@ static EM_BOOL Emscripten_HandleOrientationChange(int eventType, const Emscripte
return 0;
}
-// IF YOU CHANGE THIS STRUCTURE, YOU NEED TO UPDATE THE JAVASCRIPT THAT FILLS IT IN: makePointerEventCStruct, below.
+// IF YOU CHANGE THIS STRUCTURE, YOU NEED TO UPDATE THE JAVASCRIPT THAT FILLS IT IN: SDL3.makePointerEventCStruct, below.
+#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;
@@ -800,8 +619,120 @@ typedef struct Emscripten_PointerEvent
float rotation;
} Emscripten_PointerEvent;
-static void Emscripten_UpdatePointerFromEvent(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
+static void Emscripten_HandleMouseButton(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
{
+ Uint8 sdl_button;
+ bool down;
+ switch (event->button) {
+ #define CHECK_MOUSE_BUTTON(jsbutton, downflag, sdlbutton) case jsbutton: down = (event->buttons & downflag) != 0; ; sdl_button = SDL_BUTTON_##sdlbutton; 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); // try to regrab lost pointer lock.
+ }
+ }
+
+ SDL_SendMouseButton(0, window_data->window, SDL_DEFAULT_MOUSE_ID, sdl_button, down);
+
+ // We have an imaginary mouse capture, because we need SDL to not drop our imaginary mouse focus when we leave the canvas.
+ 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);
+
+ // rescale (in case canvas is being scaled)
+ 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) { // touch is starting or ending if this is zero (-1 means "no change").
+ 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) { // touch is starting or ending if this is zero (-1 means "no change").
+ 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) event->pointerid);
if (pen) {
// rescale (in case canvas is being scaled)
@@ -844,8 +775,49 @@ static void Emscripten_UpdatePointerFromEvent(SDL_WindowData *window_data, const
}
}
-EMSCRIPTEN_KEEPALIVE void Emscripten_HandlePointerEnter(SDL_WindowData *window_data, const Emscripten_PointerEvent *event)
+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) {
+ // rescale (in case canvas is being scaled)
+ 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; // just drop the state, but don't send the enter event.
+ } else if (!isenter && (window_data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
+ window_data->mouse_focus_loss_pending = true; // waiting on a mouse button to let go before we send the mouse focus update.
+ } 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);
// Web browsers offer almost none of this information as specifics, but can without warning offer any of these specific things.
SDL_PenInfo peninfo;
SDL_zero(peninfo);
@@ -854,10 +826,24 @@ EMSCRIPTEN_KEEPALIVE void Emscripten_HandlePointerEnter(SDL_WindowData *window_d
peninfo.num_buttons = 2;
peninfo.subtype = SDL_PEN_TYPE_PEN;
SDL_AddPenDevice(0, NULL, &peninfo, (void *) (size_t) event->pointerid);
- Emscripten_UpdatePointerFromEvent(window_data, event);
+ Emscripten_UpdatePenFromEvent(window_data, event);
}
-EMSCRIPTEN_KEEPALIVE void Emscripten_HandlePointerLeave(SDL_WindowData *window_data, const Emscripten_PointerEvent *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) {
+ // do nothing.
+ } 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) event->pointerid);
if (pen) {
@@ -866,37 +852,87 @@ EMSCRIPTEN_KEEPALIVE void Emscripten_HandlePointerLeave(SDL_WindowData *window_d
}
}
+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_set_pointer_event_callbacks(SDL_WindowData *data)
+static void Emscripten_prep_pointer_event_callbacks(void)
{
MAIN_THREAD_EM_ASM({
- var target = document.querySelector(UTF8ToString($1));
- if (target) {
- var data = $0;
-
- if (typeof(Module['SDL3']) === 'undefined') {
- Module['SDL3'] = {};
- }
- var SDL3 = Module['SDL3'];
+ if (typeof(Module['SDL3']) === 'undefined') {
+ Module['SDL3'] = {};
+ }
+ 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 makePointerEventCStruct = function(event) {
- var ptr = 0;
- if (event.pointerType == "pen") {
- ptr = _SDL_malloc($2);
- if (ptr != 0) {
- var rect = target.getBoundingClientRect();
- var idx = ptr >> 2;
- HEAP32[idx++] = event.pointerId;
- HEAP32[idx++] = (typeof(event.button) !== "undefined") ? event.button : -1;
- HEAP32[idx++] = event.buttons;
- HEAPF32[idx++] = event.movementX;
- HEAPF32[idx++] = event.movementY;
- HEAPF32[idx++] = event.clientX - rect.left;
- HEAPF32[idx++] = event.clientY - rect.top;
+ var ptr = _SDL_malloc($0);
+ if (ptr != 0) {
+ var idx = ptr >> 2;
+ HEAP32[idx++] = ptrtype;
+ HEAP32[idx++] = event.pointerId;
+ HEAP32[idx++] = (typeof(event.button) !== "undefined") ? event.button : -1;
+ HEAP32[idx++] = event.buttons;
+ 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;
@@ -906,26 +942,41 @@ static void Emscripten_set_pointer_event_callbacks(SDL_WindowData *data)
}
return ptr;
};
+ }
+ }, sizeof (Emscripten_PointerEvent));
+}
- SDL3.eventHandlerPointerEnter = function(event) {
- var d = makePointerEventCStruct(event); if (d != 0) { _Emscripten_HandlePointerEnter(data, d); _SDL_free(d); }
- };
- target.addEventListener("pointerenter", SDL3.eventHandlerPointerEnter);
+static void Emscripten_set_pointer_event_callbacks(SDL_WindowData *data)
+{
+ Emscripten_prep_pointer_event_callbacks();
- SDL3.eventHandlerPointerLeave = function(event) {
- var d = makePointerEventCStruct(event); if (d != 0) { _Emscripten_HandlePointerLeave(data, d); _SDL_free(d); }
+ 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(data, d); _SDL_free(d); }
};
- target.addEventListener("pointerleave", SDL3.eventHandlerPointerLeave);
- target.addEventListener("pointercancel", SDL3.eventHandlerPointerLeave); // catch this, just in case.
-
- SDL3.eventHandlerPointerGeneric = function(event) {
- var d = makePointerEventCStruct(event); if (d != 0) { _Emscripten_HandlePointerGeneric(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(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(data, d); _SDL_free(d); }
};
- target.addEventListener("pointerdown", SDL3.eventHandlerPointerGeneric);
- target.addEventListener("pointerup", SDL3.eventHandlerPointerGeneric);
- target.addEventListener("pointermove", SDL3.eventHandlerPointerGeneric);
+
+ target.style.touchAction = "none"; // or mobile devices will scroll as your touch moves across the element.
+ 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, sizeof (Emscripten_PointerEvent));
+ }, data, data->canvas_id);
}
static void Emscripten_unset_pointer_event_callbacks(SDL_WindowData *data)
@@ -933,20 +984,58 @@ static void Emscripten_unset_pointer_event_callbacks(SDL_WindowData *data)
MAIN_THREAD_EM_ASM({
var target = document.querySelector(UTF8ToString($0));
if (target) {
- var SDL3 = Module['SDL3'];
- target.removeEventListener("pointerenter", SDL3.eventHandlerPointerEnter);
- target.removeEventListener("pointerleave", SDL3.eventHandlerPointerLeave);
- target.removeEventListener("pointercancel", SDL3.eventHandlerPointerLeave);
- target.removeEventListener("pointerdown", SDL3.eventHandlerPointerGeneric);
- target.removeEventListener("pointerup", SDL3.eventHandlerPointerGeneric);
- target.removeEventListener("pointermove", SDL3.eventHandlerPointerGeneric);
- SDL3.eventHandlerPointerEnter = undefined;
- SDL3.eventHandlerPointerLeave = undefined;
- SDL3.eventHandlerPointerGeneric = undefined;
+ 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 = ""; // let mobile devices scroll again as your touch moves across the element.
+ 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($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;
+ }
+ });
+}
+
// IF YOU CHANGE THIS STRUCTURE, YOU NEED TO UPDATE THE JAVASCRIPT THAT FILLS IT IN: makeDropEventCStruct, below.
typedef struct Emscripten_DropEvent
{
@@ -1102,7 +1191,7 @@ static const char *Emscripten_GetKeyboardTargetElement(const char *target)
void Emscripten_RegisterGlobalEventHandlers(SDL_VideoDevice *device)
{
- emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, device, 0, Emscripten_HandleMouseButtonGlobal);
+ 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);
@@ -1116,7 +1205,7 @@ void Emscripten_RegisterGlobalEventHandlers(SDL_VideoDevice *device)
void Emscripten_UnregisterGlobalEventHandlers(SDL_VideoDevice *device)
{
- emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, 0, NULL);
+ 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);
@@ -1133,22 +1222,10 @@ void Emscripten_RegisterEventHandlers(SDL_WindowData *data)
const char *keyElement;
// There is only one window and that window is the canvas
- emscripten_set_mousemove_callback(data->canvas_id, data, 0, Emscripten_HandleMouseMove);
-
- emscripten_set_mousedown_callback(data->canvas_id, 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_orientationchange_callback(data, 0, Emscripten_HandleOrientationChange);
- 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);
-
keyElement = Emscripten_GetKeyboardTargetElement(data->keyboard_element);
if (keyElement) {
emscripten_set_keydown_callback(keyElement, data, 0, Emscripten_HandleKey);
@@ -1180,22 +1257,10 @@ void Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
Emscripten_unset_pointer_event_callbacks(data);
// only works due to having one window
- emscripten_set_mousemove_callback(data->canvas_id, NULL, 0, NULL);
-
- emscripten_set_mousedown_callback(data->canvas_id, 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_orien
(Patch may be truncated, please check the link at the top of this post.)