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

wayland: Handle captured pointer movements over a subsurface

Some compositors will send pointer enter/leave event while moving between surfaces that are part of the same window while mouse capture is active. Maintain window focus in this case, and adjust the coordinates relative to the content surface by the subsurface offset, if necessary.
Frank Praznik 20 часов назад
Родитель
Сommit
5f8eb44541
2 измененных файлов с 29 добавлено и 6 удалено
  1. 28 6
      src/video/wayland/SDL_waylandevents.c
  2. 1 0
      src/video/wayland/SDL_waylandevents_c.h

+ 28 - 6
src/video/wayland/SDL_waylandevents.c

@@ -685,8 +685,16 @@ static void pointer_dispatch_absolute_motion(SDL_WaylandSeat *seat)
     SDL_Window *window = window_data ? window_data->sdlwindow : NULL;
 
     if (window_data) {
-        const float sx = (float)(wl_fixed_to_double(seat->pointer.pending_frame.absolute.sx) * window_data->pointer_scale.x);
-        const float sy = (float)(wl_fixed_to_double(seat->pointer.pending_frame.absolute.sy) * window_data->pointer_scale.y);
+        double sx = wl_fixed_to_double(seat->pointer.pending_frame.absolute.sx);
+        double sy = wl_fixed_to_double(seat->pointer.pending_frame.absolute.sy);
+
+        if (seat->pointer.focus_surface == window_data->mask.surface) {
+            sx += (double)window_data->mask.offset_x;
+            sy += (double)window_data->mask.offset_y;
+        }
+
+        sx *= window_data->pointer_scale.x;
+        sy *= window_data->pointer_scale.y;
         SDL_SendMouseMotion(seat->pointer.pending_frame.timestamp_ns, window_data->sdlwindow, seat->pointer.sdl_id, false, sx, sy);
 
         seat->pointer.last_motion.x = (int)SDL_floorf(sx);
@@ -816,6 +824,7 @@ static void pointer_dispatch_enter(SDL_WaylandSeat *seat)
     }
 
     seat->pointer.focus = window;
+    seat->pointer.focus_surface = seat->pointer.pending_frame.enter_surface;
     ++window->pointer_focus_count;
     SDL_SetMouseFocus(window->sdlwindow);
 
@@ -874,6 +883,7 @@ static void pointer_dispatch_leave(SDL_WaylandSeat *seat, bool update_pointer)
                 window->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
 
                 seat->pointer.focus = NULL;
+                seat->pointer.focus_surface = NULL;
                 for (Uint8 i = 1; seat->pointer.buttons_pressed; ++i) {
                     if (seat->pointer.buttons_pressed & SDL_BUTTON_MASK(i)) {
                         SDL_SendMouseButton(0, window->sdlwindow, seat->pointer.sdl_id, i, false);
@@ -1283,12 +1293,24 @@ static void pointer_handle_frame(void *data, struct wl_pointer *pointer)
 
     if (seat->pointer.pending_frame.enter_surface) {
         if (seat->pointer.pending_frame.leave_surface) {
-            // Leaving the previous surface before entering a new surface.
-            pointer_dispatch_leave(seat, false);
-            seat->pointer.pending_frame.leave_surface = NULL;
+            SDL_WindowData *window_data = seat->pointer.focus;
+            SDL_WindowData *new_focus = Wayland_GetWindowDataForOwnedSurface(seat->pointer.pending_frame.enter_surface);
+
+            if (window_data && (window_data->sdlwindow->flags & SDL_WINDOW_MOUSE_CAPTURE) && window_data == new_focus) {
+                // The mouse is captured and moving between owned window surfaces. Just change the focused surface.
+                seat->pointer.focus_surface = seat->pointer.pending_frame.enter_surface;
+                seat->pointer.pending_frame.enter_surface = NULL;
+                seat->pointer.pending_frame.leave_surface = NULL;
+            } else {
+                // Leaving the previous surface before entering a new surface.
+                pointer_dispatch_leave(seat, false);
+                seat->pointer.pending_frame.leave_surface = NULL;
+            }
         }
 
-        pointer_dispatch_enter(seat);
+        if (seat->pointer.pending_frame.enter_surface) {
+            pointer_dispatch_enter(seat);
+        }
     }
 
     if (seat->pointer.pending_frame.have_absolute) {

+ 1 - 0
src/video/wayland/SDL_waylandevents_c.h

@@ -189,6 +189,7 @@ typedef struct SDL_WaylandSeat
         struct zwp_pointer_gesture_pinch_v1 *gesture_pinch;
 
         SDL_WindowData *focus;
+        struct wl_surface *focus_surface;
 
         // According to the spec, a seat can only have one active gesture of any type at a time.
         SDL_WindowData *gesture_focus;