Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 61 additions & 53 deletions src/video/wayland/SDL_waylandevents.c
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,20 @@ static void pointer_handle_motion(void *data, struct wl_pointer *pointer,

static void pointer_dispatch_enter(SDL_WaylandSeat *seat)
{
SDL_WindowData *window = seat->pointer.pending_frame.enter_window;
SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(seat->pointer.pending_frame.enter_surface);
if (!window) {
// Entering a surface not managed by SDL; just set the cursor reset flag.
Wayland_SeatResetCursor(seat);
return;
}

if (window->surface != seat->pointer.pending_frame.enter_surface) {
/* This surface is part of the window managed by SDL, but it is not the main content
* surface and doesn't get focus. Just set the default cursor and leave.
*/
Wayland_SeatSetDefaultCursor(seat);
return;
}

seat->pointer.focus = window;
++window->pointer_focus_count;
Expand Down Expand Up @@ -834,14 +847,8 @@ static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
return;
}

SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface);
if (!window) {
// Not a surface owned by SDL.
return;
}

SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data;
seat->pointer.pending_frame.enter_window = window;
seat->pointer.pending_frame.enter_surface = surface;
seat->pointer.enter_serial = serial;

/* In the case of e.g. a pointer confine warp, we may receive an enter
Expand All @@ -860,32 +867,39 @@ static void pointer_handle_enter(void *data, struct wl_pointer *pointer,

static void pointer_dispatch_leave(SDL_WaylandSeat *seat, bool update_pointer)
{
SDL_WindowData *window = seat->pointer.pending_frame.leave_window;
SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(seat->pointer.pending_frame.leave_surface);

if (window) {
// Clear the capture flag and raise all buttons
window->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;

seat->pointer.focus = 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);
seat->pointer.buttons_pressed &= ~SDL_BUTTON_MASK(i);
}
}
if (seat->pointer.focus) {
if (seat->pointer.focus->surface == seat->pointer.pending_frame.leave_surface) {
// Clear the capture flag and raise all buttons
window->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;

/* A pointer leave event may be emitted if the compositor hides the pointer in response to receiving a touch event.
* Don't relinquish focus if the surface has active touches, as the compositor is just transitioning from mouse to touch mode.
*/
SDL_Window *mouse_focus = SDL_GetMouseFocus();
const bool had_focus = mouse_focus && window->sdlwindow == mouse_focus;
if (!--window->pointer_focus_count && had_focus && !window->active_touch_count) {
SDL_SetMouseFocus(NULL);
}
seat->pointer.focus = 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);
seat->pointer.buttons_pressed &= ~SDL_BUTTON_MASK(i);
}
}

if (update_pointer) {
Wayland_SeatUpdatePointerGrab(seat);
Wayland_SeatUpdatePointerCursor(seat);
/* A pointer leave event may be emitted if the compositor hides the pointer in response to receiving a touch event.
* Don't relinquish focus if the surface has active touches, as the compositor is just transitioning from mouse to touch mode.
*/
SDL_Window *mouse_focus = SDL_GetMouseFocus();
const bool had_focus = mouse_focus && window->sdlwindow == mouse_focus;
if (!--window->pointer_focus_count && had_focus && !window->active_touch_count) {
SDL_SetMouseFocus(NULL);
}

if (update_pointer) {
Wayland_SeatUpdatePointerGrab(seat);
Wayland_SeatUpdatePointerCursor(seat);
}
}
} else if (update_pointer) {
// Leaving a non-content surface managed by SDL; just set the cursor reset flag.
Wayland_SeatResetCursor(seat);
}
}
}
Expand All @@ -898,15 +912,9 @@ static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
return;
}

SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface);
if (!window) {
// Not a surface owned by SDL.
return;
}

SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data;
seat->pointer.pending_frame.leave_window = window;
if (wl_pointer_get_version(seat->pointer.wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION && window == seat->pointer.focus) {
seat->pointer.pending_frame.leave_surface = surface;
if (wl_pointer_get_version(seat->pointer.wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION) {
pointer_dispatch_leave(seat, true);
}
}
Expand Down Expand Up @@ -1277,11 +1285,13 @@ static void pointer_handle_frame(void *data, struct wl_pointer *pointer)
{
SDL_WaylandSeat *seat = data;

if (seat->pointer.pending_frame.enter_window) {
if (seat->pointer.focus && seat->pointer.pending_frame.leave_window == seat->pointer.focus) {
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;
}

pointer_dispatch_enter(seat);
}

Expand Down Expand Up @@ -1309,7 +1319,7 @@ static void pointer_handle_frame(void *data, struct wl_pointer *pointer)
pointer_dispatch_axis(seat);
}

if (seat->pointer.focus && seat->pointer.pending_frame.leave_window == seat->pointer.focus) {
if (seat->pointer.pending_frame.leave_surface) {
pointer_dispatch_leave(seat, true);
}

Expand Down Expand Up @@ -1435,7 +1445,7 @@ static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t seri
Wayland_UpdateImplicitGrabSerial(seat, serial);
window_data = Wayland_GetWindowDataForOwnedSurface(surface);

if (window_data) {
if (window_data && window_data->surface == surface) {
float x, y;

if (window_data->current.logical_width <= 1) {
Expand All @@ -1457,8 +1467,7 @@ static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t seri
}
}

static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial,
uint32_t timestamp, int id)
static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial, uint32_t timestamp, int id)
{
SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data;
wl_fixed_t fx = 0, fy = 0;
Expand All @@ -1469,7 +1478,7 @@ static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial
if (surface) {
SDL_WindowData *window_data = Wayland_GetWindowDataForOwnedSurface(surface);

if (window_data) {
if (window_data && window_data->surface == surface) {
const float x = (float)wl_fixed_to_double(fx) / window_data->current.logical_width;
const float y = (float)wl_fixed_to_double(fy) / window_data->current.logical_height;

Expand All @@ -1489,8 +1498,7 @@ static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial
}
}

static void touch_handler_motion(void *data, struct wl_touch *touch, uint32_t timestamp,
int id, wl_fixed_t fx, wl_fixed_t fy)
static void touch_handler_motion(void *data, struct wl_touch *touch, uint32_t timestamp, int id, wl_fixed_t fx, wl_fixed_t fy)
{
SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data;
struct wl_surface *surface = NULL;
Expand All @@ -1500,7 +1508,7 @@ static void touch_handler_motion(void *data, struct wl_touch *touch, uint32_t ti
if (surface) {
SDL_WindowData *window_data = Wayland_GetWindowDataForOwnedSurface(surface);

if (window_data) {
if (window_data && window_data->surface == surface) {
const float x = (float)wl_fixed_to_double(fx) / window_data->current.logical_width;
const float y = (float)wl_fixed_to_double(fy) / window_data->current.logical_height;

Expand Down Expand Up @@ -2395,9 +2403,9 @@ static void Wayland_SeatDestroyPointer(SDL_WaylandSeat *seat)

// Make sure focus is removed from a surface before the pointer is destroyed.
if (seat->pointer.focus) {
seat->pointer.pending_frame.leave_window = seat->pointer.focus;
seat->pointer.pending_frame.leave_surface = seat->pointer.focus->surface;
pointer_dispatch_leave(seat, false);
seat->pointer.pending_frame.leave_window = NULL;
seat->pointer.pending_frame.leave_surface = NULL;
}

SDL_RemoveMouse(seat->pointer.sdl_id);
Expand Down Expand Up @@ -3349,7 +3357,7 @@ static void tablet_tool_handle_proximity_in(void *data, struct zwp_tablet_tool_v
{
SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
SDL_WindowData *windowdata = surface ? Wayland_GetWindowDataForOwnedSurface(surface) : NULL;
sdltool->focus = windowdata;
sdltool->focus = windowdata && windowdata->surface == surface ? windowdata : NULL;
sdltool->proximity_serial = serial;
sdltool->frame.have_proximity = true;
sdltool->frame.in_proximity = true;
Expand Down Expand Up @@ -3664,9 +3672,9 @@ void Wayland_DisplayRemoveWindowReferencesFromSeats(SDL_VideoData *display, SDL_
}

if (seat->pointer.focus == window) {
seat->pointer.pending_frame.leave_window = seat->pointer.focus;
seat->pointer.pending_frame.leave_surface = seat->pointer.focus->surface;
pointer_dispatch_leave(seat, true);
seat->pointer.pending_frame.leave_window = NULL;
seat->pointer.pending_frame.leave_surface = NULL;
}

// Need the safe loop variant here as cancelling a touch point removes it from the list.
Expand Down
4 changes: 2 additions & 2 deletions src/video/wayland/SDL_waylandevents_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@ typedef struct SDL_WaylandSeat
SDL_MouseWheelDirection direction;
} axis;

SDL_WindowData *enter_window;
SDL_WindowData *leave_window;
struct wl_surface *enter_surface;
struct wl_surface *leave_surface;

// Event timestamp in nanoseconds
Uint64 timestamp_ns;
Expand Down
17 changes: 17 additions & 0 deletions src/video/wayland/SDL_waylandmouse.c
Original file line number Diff line number Diff line change
Expand Up @@ -1523,6 +1523,23 @@ void Wayland_FiniMouse(SDL_VideoData *data)
#endif
}

void Wayland_SeatResetCursor(SDL_WaylandSeat *seat)
{
Wayland_CursorStateResetCursor(&seat->pointer.cursor_state);
}

void Wayland_SeatSetDefaultCursor(SDL_WaylandSeat *seat)
{
SDL_Mouse *mouse = SDL_GetMouse();
SDL_WindowData *pointer_focus = seat->pointer.focus;
const Wayland_PointerObject obj = {
.wl_pointer = seat->pointer.wl_pointer,
.is_pointer = true
};

Wayland_CursorStateSetCursor(&seat->pointer.cursor_state, &obj, pointer_focus, seat->pointer.enter_serial, mouse->def_cursor);
}

void Wayland_SeatUpdatePointerCursor(SDL_WaylandSeat *seat)
{
SDL_Mouse *mouse = SDL_GetMouse();
Expand Down
2 changes: 2 additions & 0 deletions src/video/wayland/SDL_waylandmouse.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
extern void Wayland_InitMouse(SDL_VideoData *data);
extern void Wayland_FiniMouse(SDL_VideoData *data);
extern void Wayland_SeatUpdatePointerCursor(SDL_WaylandSeat *seat);
extern void Wayland_SeatSetDefaultCursor(SDL_WaylandSeat *seat);
extern void Wayland_SeatResetCursor(SDL_WaylandSeat *seat);
extern void Wayland_TabletToolUpdateCursor(SDL_WaylandPenTool *tool);
extern void Wayland_SeatWarpMouse(SDL_WaylandSeat *seat, SDL_WindowData *window, float x, float y);
extern void Wayland_CursorStateSetFrameCallback(SDL_WaylandCursorState *state, void *userdata);
Expand Down
25 changes: 25 additions & 0 deletions src/video/wayland/SDL_waylandshmbuffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include "SDL_waylandshmbuffer.h"
#include "SDL_waylandvideo.h"
#include "single-pixel-buffer-v1-client-protocol.h"

static bool SetTempFileSize(int fd, off_t size)
{
Expand Down Expand Up @@ -186,4 +187,28 @@ void Wayland_ReleaseSHMPool(Wayland_SHMPool *shmPool)
}
}

struct wl_buffer *Wayland_CreateSinglePixelBuffer(Uint32 r, Uint32 g, Uint32 b, Uint32 a)
{
SDL_VideoData *viddata = SDL_GetVideoDevice()->internal;

// The single-pixel buffer protocol is preferred, as the compositor can choose an optimal format.
if (viddata->single_pixel_buffer_manager) {
return wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(viddata->single_pixel_buffer_manager, r, g, b, a);
} else {
Wayland_SHMPool *pool = Wayland_AllocSHMPool(4);
if (!pool) {
return NULL;
}

void *mem;
struct wl_buffer *wl_buffer = Wayland_AllocBufferFromPool(pool, 1, 1, &mem);

const Uint8 pixel[4] = { r >> 24, g >> 24, b >> 24, a >> 24 };
SDL_memcpy(mem, pixel, sizeof(pixel));

Wayland_ReleaseSHMPool(pool);
return wl_buffer;
}
}

#endif
2 changes: 2 additions & 0 deletions src/video/wayland/SDL_waylandshmbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ extern Wayland_SHMPool *Wayland_AllocSHMPool(int size);
extern struct wl_buffer *Wayland_AllocBufferFromPool(Wayland_SHMPool *shmPool, int width, int height, void **data);
extern void Wayland_ReleaseSHMPool(Wayland_SHMPool *shmPool);

extern struct wl_buffer *Wayland_CreateSinglePixelBuffer(Uint32 r, Uint32 g, Uint32 b, Uint32 a);

#endif
16 changes: 16 additions & 0 deletions src/video/wayland/SDL_waylandvideo.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
#include "color-management-v1-client-protocol.h"
#include "pointer-warp-v1-client-protocol.h"
#include "pointer-gestures-unstable-v1-client-protocol.h"
#include "single-pixel-buffer-v1-client-protocol.h"

#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
Expand Down Expand Up @@ -653,6 +654,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
device->SetWindowResizable = Wayland_SetWindowResizable;
device->SetWindowPosition = Wayland_SetWindowPosition;
device->SetWindowSize = Wayland_SetWindowSize;
device->SetWindowAspectRatio = Wayland_SetWindowAspectRatio;
device->SetWindowMinimumSize = Wayland_SetWindowMinimumSize;
device->SetWindowMaximumSize = Wayland_SetWindowMaximumSize;
device->SetWindowParent = Wayland_SetWindowParent;
Expand Down Expand Up @@ -1278,6 +1280,8 @@ static void handle_registry_global(void *data, struct wl_registry *registry, uin

if (SDL_strcmp(interface, "wl_compositor") == 0) {
d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, SDL_min(SDL_WL_COMPOSITOR_VERSION, version));
} else if (SDL_strcmp(interface, "wl_subcompositor") == 0) {
d->subcompositor = wl_registry_bind(d->registry, id, &wl_subcompositor_interface, 1);
} else if (SDL_strcmp(interface, "wl_output") == 0) {
Wayland_add_display(d, id, SDL_min(version, SDL_WL_OUTPUT_VERSION));
} else if (SDL_strcmp(interface, "wl_seat") == 0) {
Expand Down Expand Up @@ -1344,6 +1348,8 @@ static void handle_registry_global(void *data, struct wl_registry *registry, uin
} else if (SDL_strcmp(interface, "zwp_pointer_gestures_v1") == 0) {
d->zwp_pointer_gestures = wl_registry_bind(d->registry, id, &zwp_pointer_gestures_v1_interface, SDL_min(version, 3));
Wayland_DisplayInitPointerGestureManager(d);
} else if (SDL_strcmp(interface, "wp_single_pixel_buffer_manager_v1") == 0) {
d->single_pixel_buffer_manager = wl_registry_bind(d->registry, id, &wp_single_pixel_buffer_manager_v1_interface, 1);
}
#ifdef SDL_WL_FIXES_VERSION
else if (SDL_strcmp(interface, "wl_fixes") == 0) {
Expand Down Expand Up @@ -1692,6 +1698,16 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
data->zwp_pointer_gestures = NULL;
}

if (data->single_pixel_buffer_manager) {
wp_single_pixel_buffer_manager_v1_destroy(data->single_pixel_buffer_manager);
data->single_pixel_buffer_manager = NULL;
}

if (data->subcompositor) {
wl_subcompositor_destroy(data->subcompositor);
data->subcompositor = NULL;
}

if (data->compositor) {
wl_compositor_destroy(data->compositor);
data->compositor = NULL;
Expand Down
2 changes: 2 additions & 0 deletions src/video/wayland/SDL_waylandvideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ struct SDL_VideoData
struct libdecor *libdecor;
#endif
} shell;
struct wl_subcompositor *subcompositor;
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
struct zwp_pointer_constraints_v1 *pointer_constraints;
struct wp_pointer_warp_v1 *wp_pointer_warp_v1;
Expand All @@ -85,6 +86,7 @@ struct SDL_VideoData
struct zwp_tablet_manager_v2 *tablet_manager;
struct wl_fixes *wl_fixes;
struct zwp_pointer_gestures_v1 *zwp_pointer_gestures;
struct wp_single_pixel_buffer_manager_v1 *single_pixel_buffer_manager;

struct xkb_context *xkb_context;

Expand Down
Loading
Loading