diff --git a/src/video/emscripten/SDL_emscriptenevents.c b/src/video/emscripten/SDL_emscriptenevents.c index cc999a74dd..69c685ba6f 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; }; - - SDL3.eventHandlerPointerEnter = function(event) { - var d = makePointerEventCStruct(event); if (d != 0) { _Emscripten_HandlePointerEnter(data, d); _SDL_free(d); } - }; - target.addEventListener("pointerenter", SDL3.eventHandlerPointerEnter); - - SDL3.eventHandlerPointerLeave = function(event) { - var d = makePointerEventCStruct(event); if (d != 0) { _Emscripten_HandlePointerLeave(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.addEventListener("pointerdown", SDL3.eventHandlerPointerGeneric); - target.addEventListener("pointerup", SDL3.eventHandlerPointerGeneric); - target.addEventListener("pointermove", SDL3.eventHandlerPointerGeneric); } - }, data, data->canvas_id, sizeof (Emscripten_PointerEvent)); + }, 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(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.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); } 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_orientationchange_callback(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); - keyElement = Emscripten_GetKeyboardTargetElement(data->keyboard_element); if (keyElement) { emscripten_set_keydown_callback(keyElement, NULL, 0, NULL);