Просмотр исходного кода

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.
Ryan C. Gordon 1 месяц назад
Родитель
Сommit
c5491474de
1 измененных файлов с 64 добавлено и 43 удалено
  1. 64 43
      src/events/SDL_pen.c

+ 64 - 43
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);
+        }
     }
 }