wayland: Enable relative pointer mode based on the window flag

This can be toggled per-window, so use the individual window flags instead of the global toggle to selectively enable it only for the relevant window in a multi-seat scenario, as is already done with keyboard and pointer grabs.
This commit is contained in:
Frank Praznik
2025-05-07 13:10:24 -04:00
parent 968222e74f
commit b0a282e31f
7 changed files with 55 additions and 73 deletions

View File

@@ -1314,8 +1314,9 @@ static void SDL_MaybeEnableWarpEmulation(SDL_Window *window, float x, float y)
// Require two consecutive warps to the center within a certain timespan to enter warp emulation mode.
const Uint64 now = SDL_GetTicksNS();
if (now - mouse->last_center_warp_time_ns < WARP_EMULATION_THRESHOLD_NS) {
if (SDL_SetRelativeMouseMode(true)) {
mouse->warp_emulation_active = true;
if (!SDL_SetRelativeMouseMode(true)) {
mouse->warp_emulation_active = false;
}
}

View File

@@ -912,7 +912,7 @@ static void pointer_handle_button_common(SDL_WaylandSeat *seat, uint32_t serial,
*
* The mouse is not captured in relative mode.
*/
if (!seat->display->relative_mode_enabled || !Wayland_SeatHasRelativePointerFocus(seat)) {
if (!seat->pointer.relative_pointer) {
if (seat->pointer.buttons_pressed != 0) {
window->sdlwindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
} else {
@@ -1164,12 +1164,7 @@ static void relative_pointer_handle_relative_motion(void *data,
wl_fixed_t dy_unaccel_w)
{
SDL_WaylandSeat *seat = data;
if (seat->display->relative_mode_enabled) {
SDL_WindowData *window = seat->pointer.focus;
// Relative motion follows keyboard focus.
if (Wayland_SeatHasRelativePointerFocus(seat)) {
SDL_Mouse *mouse = SDL_GetMouse();
// Relative pointer event times are in microsecond granularity.
@@ -1187,8 +1182,6 @@ static void relative_pointer_handle_relative_motion(void *data,
SDL_SendMouseMotion(timestamp, window->sdlwindow, seat->pointer.sdl_id, true, (float)dx, (float)dy);
}
}
}
static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
relative_pointer_handle_relative_motion,
@@ -2077,26 +2070,6 @@ static const struct wl_keyboard_listener keyboard_listener = {
keyboard_handle_repeat_info, // Version 4
};
static void Wayland_SeatCreateRelativePointer(SDL_WaylandSeat *seat)
{
if (seat->display->relative_pointer_manager) {
if (seat->pointer.wl_pointer && !seat->pointer.relative_pointer) {
seat->pointer.relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(seat->display->relative_pointer_manager, seat->pointer.wl_pointer);
zwp_relative_pointer_v1_add_listener(seat->pointer.relative_pointer,
&relative_pointer_listener,
seat);
}
}
}
void Wayland_DisplayInitRelativePointerManager(SDL_VideoData *display)
{
SDL_WaylandSeat *seat;
wl_list_for_each(seat, &display->seat_list, link) {
Wayland_SeatCreateRelativePointer(seat);
}
}
static void Wayland_SeatDestroyPointer(SDL_WaylandSeat *seat, bool send_event)
{
// Make sure focus is removed from a surface before the pointer is destroyed.
@@ -2239,8 +2212,6 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum w
wl_pointer_set_user_data(seat->pointer.wl_pointer, seat);
wl_pointer_add_listener(seat->pointer.wl_pointer, &pointer_listener, seat);
Wayland_SeatCreateRelativePointer(seat);
seat->pointer.sdl_id = SDL_GetNextObjectID();
if (seat->name) {
@@ -3490,16 +3461,36 @@ void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool send_events)
SDL_free(seat);
}
bool Wayland_SeatHasRelativePointerFocus(SDL_WaylandSeat *seat)
static void Wayland_SeatUpdateRelativePointer(SDL_WaylandSeat *seat)
{
if (seat->display->relative_pointer_manager) {
bool relative_focus = false;
if (seat->pointer.focus) {
/* If a seat has both keyboard and pointer capabilities, relative focus will follow the keyboard
* attached to that seat. Otherwise, relative focus will be gained if any other seat has keyboard
* focus on the window with pointer focus.
*/
if (seat->pointer.focus->sdlwindow->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE) {
if (seat->keyboard.wl_keyboard) {
return seat->keyboard.focus && seat->keyboard.focus == seat->pointer.focus;
relative_focus = seat->keyboard.focus == seat->pointer.focus;
} else {
return seat->pointer.focus && seat->pointer.focus->keyboard_focus_count != 0;
relative_focus = seat->pointer.focus->keyboard_focus_count != 0;
}
} else {
relative_focus = SDL_GetMouse()->warp_emulation_active;
}
}
if (relative_focus) {
if (!seat->pointer.relative_pointer) {
seat->pointer.relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(seat->display->relative_pointer_manager, seat->pointer.wl_pointer);
zwp_relative_pointer_v1_add_listener(seat->pointer.relative_pointer, &relative_pointer_listener, seat);
}
} else if (seat->pointer.relative_pointer) {
zwp_relative_pointer_v1_destroy(seat->pointer.relative_pointer);
seat->pointer.relative_pointer = NULL;
}
}
}
@@ -3532,11 +3523,10 @@ static void Wayland_SeatUpdateKeyboardGrab(SDL_WaylandSeat *seat)
void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat)
{
SDL_VideoData *display = seat->display;
Wayland_SeatUpdateRelativePointer(seat);
if (display->pointer_constraints) {
const bool has_relative_focus = Wayland_SeatHasRelativePointerFocus(seat);
if (seat->pointer.locked_pointer && (!display->relative_mode_enabled || !has_relative_focus)) {
if (seat->pointer.locked_pointer && !seat->pointer.relative_pointer) {
zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer);
seat->pointer.locked_pointer = NULL;
@@ -3546,7 +3536,7 @@ void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat)
if (seat->pointer.wl_pointer) {
// If relative mode is active, and the pointer focus matches the keyboard focus, lock it.
if (seat->display->relative_mode_enabled && has_relative_focus) {
if (seat->pointer.relative_pointer) {
if (!seat->pointer.locked_pointer) {
// Creating a lock on a surface with an active confinement region on the same seat is a protocol error.
if (seat->pointer.confined_pointer) {

View File

@@ -191,7 +191,6 @@ extern int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS);
extern void Wayland_DisplayInitInputTimestampManager(SDL_VideoData *display);
extern void Wayland_DisplayInitCursorShapeManager(SDL_VideoData *display);
extern void Wayland_DisplayInitRelativePointerManager(SDL_VideoData *display);
extern void Wayland_DisplayInitTabletManager(SDL_VideoData *display);
extern void Wayland_DisplayInitDataDeviceManager(SDL_VideoData *display);
extern void Wayland_DisplayInitPrimarySelectionDeviceManager(SDL_VideoData *display);
@@ -201,7 +200,6 @@ extern void Wayland_DisplayCreateTextInputManager(SDL_VideoData *d, uint32_t id)
extern void Wayland_DisplayCreateSeat(SDL_VideoData *display, struct wl_seat *wl_seat, Uint32 id);
extern void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool send_events);
extern bool Wayland_SeatHasRelativePointerFocus(SDL_WaylandSeat *seat);
extern void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat);
extern void Wayland_DisplayUpdatePointerGrabs(SDL_VideoData *display, SDL_WindowData *window);
extern void Wayland_DisplayUpdateKeyboardGrabs(SDL_VideoData *display, SDL_WindowData *window);

View File

@@ -881,8 +881,7 @@ static bool Wayland_WarpMouseRelative(SDL_Window *window, float x, float y)
if (d->pointer_constraints) {
wl_list_for_each (seat, &d->seat_list, link) {
if (wind == seat->pointer.focus ||
(!seat->pointer.focus && wind == seat->keyboard.focus)) {
if (wind == seat->pointer.focus) {
Wayland_SeatWarpMouse(seat, wind, x, y);
}
}
@@ -939,7 +938,7 @@ static bool Wayland_SetRelativeMouseMode(bool enabled)
return SDL_SetError("Failed to enable relative mode: compositor lacks support for the required zwp_pointer_constraints_v1 protocol");
}
data->relative_mode_enabled = enabled;
// Windows have a relative mode flag, so just update the grabs on a state change.
Wayland_DisplayUpdatePointerGrabs(data, NULL);
return true;
}
@@ -1122,13 +1121,10 @@ void Wayland_SeatUpdateCursor(SDL_WaylandSeat *seat)
SDL_WindowData *pointer_focus = seat->pointer.focus;
if (pointer_focus && mouse->cursor_visible) {
const bool has_relative_focus = Wayland_SeatHasRelativePointerFocus(seat);
if (!seat->display->relative_mode_enabled || !has_relative_focus || !mouse->relative_mode_hide_cursor) {
if (!seat->pointer.relative_pointer || !mouse->relative_mode_hide_cursor) {
const SDL_HitTestResult rc = pointer_focus->hit_test_result;
if ((seat->display->relative_mode_enabled && has_relative_focus) ||
rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) {
if (seat->pointer.relative_pointer || rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) {
Wayland_SeatSetCursor(seat, mouse->cur_cursor);
} else {
Wayland_SeatSetCursor(seat, sys_cursors[rc]);

View File

@@ -1267,7 +1267,6 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
} else if (SDL_strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
d->relative_pointer_manager = wl_registry_bind(d->registry, id, &zwp_relative_pointer_manager_v1_interface, 1);
Wayland_DisplayInitRelativePointerManager(d);
} else if (SDL_strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
d->pointer_constraints = wl_registry_bind(d->registry, id, &zwp_pointer_constraints_v1_interface, 1);
} else if (SDL_strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0) {

View File

@@ -96,9 +96,7 @@ struct SDL_VideoData
int output_count;
int output_max;
bool relative_mode_enabled;
bool display_externally_owned;
bool scale_to_display_enabled;
};

View File

@@ -2209,7 +2209,7 @@ static const struct xdg_activation_token_v1_listener activation_listener_xdg = {
*
* As you might expect from Wayland, the general policy is to go with #2 unless
* the client can prove to the compositor beyond a reasonable doubt that raising
* the window will not be malicuous behavior.
* the window will not be malicious behavior.
*
* For SDL this means RaiseWindow and FlashWindow both use the same protocol,
* but in different ways: RaiseWindow will provide as _much_ information as