If the lock is deactivated, mouselook won't work properly, and the user probably doesn't want mouselook to be active. For example:
* if the user is making their compositor inhibit constraints to temporarily prevent applications from disrupting pointer movement.
* if the keyboard is unfocused on the window, the lock is almost certainly deactivated.
From: Attila Fidan dev@print0.net
If the lock is deactivated, mouselook won't work properly, and the user probably doesn't want mouselook to be active. For example:
* if the user is making their compositor inhibit constraints to temporarily prevent applications from disrupting pointer movement.
* if the keyboard is unfocused on the window, the lock is almost certainly deactivated.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56681 --- dlls/winewayland.drv/wayland_pointer.c | 34 ++++++++++++++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 1 + 2 files changed, 35 insertions(+)
diff --git a/dlls/winewayland.drv/wayland_pointer.c b/dlls/winewayland.drv/wayland_pointer.c index 52aaa337aac..6b389700e28 100644 --- a/dlls/winewayland.drv/wayland_pointer.c +++ b/dlls/winewayland.drv/wayland_pointer.c @@ -251,17 +251,44 @@ static const struct wl_pointer_listener pointer_listener = pointer_handle_axis_discrete };
+static void locked_pointer_v1_handle_locked(void *data, + struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) +{ + struct wayland_pointer *pointer = &process_wayland.pointer; + pthread_mutex_lock(&pointer->mutex); + pointer->lock_active = TRUE; + pthread_mutex_unlock(&pointer->mutex); +} + +static void locked_pointer_v1_handle_unlocked(void *data, + struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) +{ + struct wayland_pointer *pointer = &process_wayland.pointer; + pthread_mutex_lock(&pointer->mutex); + pointer->lock_active = FALSE; + pthread_mutex_unlock(&pointer->mutex); +} + +static const struct zwp_locked_pointer_v1_listener locked_pointer_v1_listener = +{ + locked_pointer_v1_handle_locked, + locked_pointer_v1_handle_unlocked +}; + static void relative_pointer_v1_relative_motion(void *private, struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1, uint32_t utime_hi, uint32_t utime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) { + struct wayland_pointer *pointer = &process_wayland.pointer; INPUT input = {0}; HWND hwnd; POINT screen; struct wayland_win_data *data;
+ if (!pointer->lock_active) return; + if (!(hwnd = wayland_pointer_get_focused_hwnd())) return; if (!(data = wayland_win_data_get(hwnd))) return;
@@ -316,6 +343,7 @@ void wayland_pointer_deinit(void) { zwp_locked_pointer_v1_destroy(pointer->zwp_locked_pointer_v1); pointer->zwp_locked_pointer_v1 = NULL; + pointer->lock_active = FALSE; } if (pointer->zwp_relative_pointer_v1) { @@ -775,6 +803,7 @@ static void wayland_pointer_update_constraint(struct wl_surface *wl_surface, TRACE("Unlocking from hwnd=%p\n", pointer->constraint_hwnd); zwp_locked_pointer_v1_destroy(pointer->zwp_locked_pointer_v1); pointer->zwp_locked_pointer_v1 = NULL; + pointer->lock_active = FALSE; pointer->constraint_hwnd = NULL; }
@@ -822,7 +851,10 @@ static void wayland_pointer_update_constraint(struct wl_surface *wl_surface, if (!pointer->zwp_locked_pointer_v1 || pointer->constraint_hwnd != hwnd) { if (pointer->zwp_locked_pointer_v1) + { zwp_locked_pointer_v1_destroy(pointer->zwp_locked_pointer_v1); + pointer->lock_active = FALSE; + } pointer->zwp_locked_pointer_v1 = zwp_pointer_constraints_v1_lock_pointer( process_wayland.zwp_pointer_constraints_v1, @@ -830,6 +862,8 @@ static void wayland_pointer_update_constraint(struct wl_surface *wl_surface, pointer->wl_pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + zwp_locked_pointer_v1_add_listener(pointer->zwp_locked_pointer_v1, + &locked_pointer_v1_listener, NULL); pointer->constraint_hwnd = hwnd; TRACE("Locking to hwnd=%p\n", pointer->constraint_hwnd); } diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index c389167c3b7..67982dd57ba 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -110,6 +110,7 @@ struct wayland_pointer uint32_t enter_serial; uint32_t button_serial; struct wayland_cursor cursor; + BOOL lock_active; pthread_mutex_t mutex; };
The problem described in the linked bug is that the app is reacting to mouse events sent while the pointer is moving over it when it does not have the keyboard focus. However, in general, this is acceptable behavior for both Wayland and win32: keyboard focus is not a requirement to receive mouse events. It's up to the application to react (or not) to such mouse events, a decision that may or may not take into account the current keyboard focus.
My guess is that the fundamental problem at play in this particular case is that since winewayland does not clear the win32 focus state when a surface loses the Wayland keyboard focus, the game thinks it's still focused and acts accordingly (instead of, e.g., having dropped the confinement and shown the cursor when focus was lost).
I don't know of any good-for-all-cases answer to the more general question about what to do when the lock is disabled by the system itself, since AFAIK this is not a possible scenario under win32. All of the following are viable options depending on the application:
1. Do nothing (current code, i.e., keep relative motion enabled) 2. Block all pointer motion events (this MR, but what about mouse button/axis events?) 3. Switch to absolute events (e.g., if we are not dealing with a mouselook scenario)
One concern I have with option (2) is that the cursor will just disappear when entering the unfocused window and leave the user without any feedback about what's going on.
My personal preference is maintaining the current, more unopinionated "do nothing" approach and try to make some progress fixing the real underlying issue that's causing the bug.
My guess is that the fundamental problem at play in this particular case is that since winewayland does not clear the win32 focus state when a surface loses the Wayland keyboard focus, the game thinks it's still focused and acts accordingly (instead of, e.g., having dropped the confinement and shown the cursor when focus was lost).
Oh, right. I quickly added `NtUserSetForegroundWindow(NtUserGetDesktopWindow());` to the end of `keyboard_handle_leave` and that behaves more like winex11/xwayland.