From c5491474de1696962853f0dc321463e4f3eff261 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 18 Aug 2026 15:57:46 -0400 Subject: [PATCH] pen: If a pen leaves proximity, force all its pressed buttons to released. Some platforms do this for us, but just in case there is a badly-behaved one, this will help keep application state correct. Reference Issue #16125. --- src/events/SDL_pen.c | 107 ++++++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 43 deletions(-) diff --git a/src/events/SDL_pen.c b/src/events/SDL_pen.c index 849f12be53..7202559ffb 100644 --- a/src/events/SDL_pen.c +++ b/src/events/SDL_pen.c @@ -562,6 +562,35 @@ void SDL_SendPenMotion(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *wind } } +static void SendPenButtonEvents(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *window, Uint8 button, bool down, SDL_PenInputFlags input_state, float x, float y) +{ + const SDL_EventType evtype = down ? SDL_EVENT_PEN_BUTTON_DOWN : SDL_EVENT_PEN_BUTTON_UP; + if (SDL_EventEnabled(evtype)) { + SDL_Event event; + SDL_zero(event); + event.pbutton.type = evtype; + event.pbutton.timestamp = timestamp; + event.pbutton.windowID = window ? window->id : 0; + event.pbutton.which = instance_id; + event.pbutton.pen_state = input_state; + event.pbutton.x = x; + event.pbutton.y = y; + event.pbutton.button = button; + event.pbutton.down = down; + SDL_PushEvent(&event); + + if (window && (!pen_touching || (pen_touching == instance_id))) { + SDL_Mouse *mouse = SDL_GetMouse(); + if (mouse && mouse->pen_mouse_events) { + static const Uint8 mouse_buttons[] = { SDL_BUTTON_LEFT, SDL_BUTTON_RIGHT, SDL_BUTTON_MIDDLE, SDL_BUTTON_X1, SDL_BUTTON_X2 }; + if (button < SDL_arraysize(mouse_buttons)) { + SDL_SendMouseButton(timestamp, window, SDL_PEN_MOUSEID, mouse_buttons[button], down); + } + } + } + } +} + void SDL_SendPenButton(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *window, Uint8 button, bool down) { bool send_event = false; @@ -583,7 +612,7 @@ void SDL_SendPenButton(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *wind EnsurePenProximity(timestamp, pen, window); input_state = pen->input_state; - const Uint32 flag = (Uint32) (1u << button); + const SDL_PenInputFlags flag = (SDL_PenInputFlags) (1u << button); const bool current = ((input_state & flag) != 0); x = pen->x; y = pen->y; @@ -599,37 +628,7 @@ void SDL_SendPenButton(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *wind SDL_UnlockRWLock(pen_device_rwlock); if (send_event) { - const SDL_EventType evtype = down ? SDL_EVENT_PEN_BUTTON_DOWN : SDL_EVENT_PEN_BUTTON_UP; - if (SDL_EventEnabled(evtype)) { - SDL_Event event; - SDL_zero(event); - event.pbutton.type = evtype; - event.pbutton.timestamp = timestamp; - event.pbutton.windowID = window ? window->id : 0; - event.pbutton.which = instance_id; - event.pbutton.pen_state = input_state; - event.pbutton.x = x; - event.pbutton.y = y; - event.pbutton.button = button; - event.pbutton.down = down; - SDL_PushEvent(&event); - - if (window && (!pen_touching || (pen_touching == instance_id))) { - SDL_Mouse *mouse = SDL_GetMouse(); - if (mouse && mouse->pen_mouse_events) { - static const Uint8 mouse_buttons[] = { - SDL_BUTTON_LEFT, - SDL_BUTTON_RIGHT, - SDL_BUTTON_MIDDLE, - SDL_BUTTON_X1, - SDL_BUTTON_X2 - }; - if (button < SDL_arraysize(mouse_buttons)) { - SDL_SendMouseButton(timestamp, window, SDL_PEN_MOUSEID, mouse_buttons[button], down); - } - } - } - } + SendPenButtonEvents(timestamp, instance_id, window, button, down, input_state, x, y); } } @@ -637,6 +636,9 @@ void SDL_SendPenProximity(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *w { bool send_event = false; SDL_PenInputFlags input_state = 0; + SDL_PenInputFlags orig_input_state = 0; + float x = 0.0f; + float y = 0.0f; // note that this locks for _reading_ because the lock protects the // pen_devices array from being reallocated from under us, not the data in it; @@ -646,16 +648,22 @@ void SDL_SendPenProximity(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *w SDL_Pen *pen = FindPenByInstanceId(instance_id); if (pen) { if (in || immediate) { - input_state = pen->input_state; + orig_input_state = input_state = pen->input_state; const bool in_proximity = ((input_state & SDL_PEN_INPUT_IN_PROXIMITY) != 0); if (in_proximity != in) { if (in) { input_state |= SDL_PEN_INPUT_IN_PROXIMITY; } else { input_state &= ~SDL_PEN_INPUT_IN_PROXIMITY; + // drop all still-pressed buttons, too. + for (int button = 1; button <= 5; button++) { + input_state &= ~(1u << button); + } } send_event = true; pen->input_state = input_state; // we could do an SDL_SetAtomicInt here if we run into trouble... + x = pen->x; + y = pen->y; } pen->pending_proximity_out = false; } else { @@ -666,16 +674,29 @@ void SDL_SendPenProximity(Uint64 timestamp, SDL_PenID instance_id, SDL_Window *w } SDL_UnlockRWLock(pen_device_rwlock); - const Uint32 event_type = in ? SDL_EVENT_PEN_PROXIMITY_IN : SDL_EVENT_PEN_PROXIMITY_OUT; - if (send_event && SDL_EventEnabled(event_type)) { - SDL_Event event; - SDL_zero(event); - event.pproximity.type = event_type; - event.pproximity.timestamp = timestamp; - event.pproximity.windowID = window ? window->id : 0; - event.pproximity.which = instance_id; - event.pproximity.pen_state = input_state; - SDL_PushEvent(&event); + if (send_event) { + // report any currently-pressed buttons as released if we're leaving proximity. + if (!in) { + for (int button = 1; button <= 5; button++) { + const SDL_PenInputFlags flag = (SDL_PenInputFlags) (1u << button); + if (orig_input_state & flag) { // orig_input_state continues to report we're in proximity. + orig_input_state &= ~flag; // drop the button we're reporting on. + SendPenButtonEvents(timestamp, instance_id, window, button, false, orig_input_state, x, y); + } + } + } + + const Uint32 event_type = in ? SDL_EVENT_PEN_PROXIMITY_IN : SDL_EVENT_PEN_PROXIMITY_OUT; + if (SDL_EventEnabled(event_type)) { + SDL_Event event; + SDL_zero(event); + event.pproximity.type = event_type; + event.pproximity.timestamp = timestamp; + event.pproximity.windowID = window ? window->id : 0; + event.pproximity.which = instance_id; + event.pproximity.pen_state = input_state; + SDL_PushEvent(&event); + } } }