Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51357
Now that we're tracking window position changes as they arrive, they should always be consistent with the received mouse input events.
Absolute rawinput is useful for tablets, touchpad or touchscreen when they are exposed as absolute mouse devices.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/mouse.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 9d25b71c992..f6386918c09 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1693,19 +1693,17 @@ static BOOL map_raw_event_coords( XIRawEvent *event, INPUT *input ) values++; }
- input->mi.dx = round( x->value ); - input->mi.dy = round( y->value ); - - TRACE( "event %f,%f value %f,%f input %d,%d\n", x_value, y_value, x->value, y->value, - (int)input->mi.dx, (int)input->mi.dy ); - - x->value -= input->mi.dx; - y->value -= input->mi.dy; - - if (!input->mi.dx && !input->mi.dy) + if (!(input->mi.dx = round( x->value )) && !(input->mi.dy = round( y->value ))) { - TRACE( "accumulating motion\n" ); - return FALSE; + TRACE( "event %f,%f value %f,%f, accumulating motion\n", x_value, y_value, x->value, y->value ); + input->mi.dwFlags &= ~MOUSEEVENTF_MOVE; + } + else + { + TRACE( "event %f,%f value %f,%f, input %d,%d\n", x_value, y_value, x->value, y->value, + (int)input->mi.dx, (int)input->mi.dy ); + x->value -= input->mi.dx; + y->value -= input->mi.dy; }
return TRUE; @@ -1733,6 +1731,7 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev ) input.mi.dx = 0; input.mi.dy = 0; if (!map_raw_event_coords( event, &input )) return FALSE; + if (!(input.mi.dwFlags & MOUSEEVENTF_MOVE)) return FALSE;
NtUserSendHardwareInput( 0, 0, &input, 0 ); return TRUE;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/mouse.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index f6386918c09..d504a68e6ba 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -240,12 +240,18 @@ static void update_relative_valuators( XIAnyClassInfo **classes, int num_classes { valuator = (XIValuatorClassInfo *)classes[num_classes]; if (classes[num_classes]->type != XIValuatorClass) continue; - if (valuator->number == 0 && valuator->mode == XIModeRelative) thread_data->x_valuator = *valuator; - if (valuator->number == 1 && valuator->mode == XIModeRelative) thread_data->y_valuator = *valuator; + if (valuator->number == 0) thread_data->x_valuator = *valuator; + if (valuator->number == 1) thread_data->y_valuator = *valuator; }
if (thread_data->x_valuator.number < 0 || thread_data->y_valuator.number < 0) WARN( "X/Y axis valuators not found, ignoring RawMotion events\n" ); + else if (thread_data->x_valuator.mode != thread_data->y_valuator.mode) + { + WARN( "Relative/Absolute mismatch between X/Y axis, ignoring RawMotion events\n" ); + thread_data->x_valuator.number = -1; + thread_data->y_valuator.number = -1; + }
thread_data->x_valuator.value = 0; thread_data->y_valuator.value = 0; @@ -1660,6 +1666,7 @@ static BOOL map_raw_event_coords( XIRawEvent *event, INPUT *input ) { struct x11drv_thread_data *thread_data = x11drv_thread_data(); XIValuatorClassInfo *x = &thread_data->x_valuator, *y = &thread_data->y_valuator; + const UINT absolute_flags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK; double x_value = 0, y_value = 0, x_scale, y_scale; const double *values = event->valuators.values; RECT virtual_rect; @@ -1670,7 +1677,15 @@ static BOOL map_raw_event_coords( XIRawEvent *event, INPUT *input ) if (!xinput2_available) return FALSE; if (event->deviceid != thread_data->xinput2_pointer) return FALSE;
- virtual_rect = NtUserGetVirtualScreenRect( MDT_RAW_DPI ); + if (x->mode == XIModeRelative && y->mode == XIModeRelative) + input->mi.dwFlags &= ~absolute_flags; + else if (x->mode == XIModeAbsolute && y->mode == XIModeAbsolute) + input->mi.dwFlags |= absolute_flags; + else + FIXME( "Unsupported relative/absolute X/Y axis mismatch\n." ); + + if (input->mi.dwFlags & MOUSEEVENTF_VIRTUALDESK) SetRect( &virtual_rect, 0, 0, 65535, 65535 ); + else virtual_rect = NtUserGetVirtualScreenRect( MDT_RAW_DPI );
if (x->max <= x->min) x_scale = 1; else x_scale = (virtual_rect.right - virtual_rect.left) / (x->max - x->min); @@ -1683,17 +1698,26 @@ static BOOL map_raw_event_coords( XIRawEvent *event, INPUT *input ) if (i == x->number) { x_value = *values; - x->value += x_value * x_scale; + if (x->mode == XIModeRelative) x->value += x_value * x_scale; + else x->value = (x_value - x->min) * x_scale; } if (i == y->number) { y_value = *values; - y->value += y_value * y_scale; + if (y->mode == XIModeRelative) y->value += y_value * y_scale; + else y->value = (y_value - y->min) * y_scale; } values++; }
- if (!(input->mi.dx = round( x->value )) && !(input->mi.dy = round( y->value ))) + if (input->mi.dwFlags & MOUSEEVENTF_ABSOLUTE) + { + input->mi.dx = round( x->value ); + input->mi.dy = round( y->value ); + TRACE( "event %f,%f value %f,%f absolute input %d,%d\n", x_value, y_value, x->value, y->value, + (int)input->mi.dx, (int)input->mi.dy ); + } + else if (!(input->mi.dx = round( x->value )) && !(input->mi.dy = round( y->value ))) { TRACE( "event %f,%f value %f,%f, accumulating motion\n", x_value, y_value, x->value, y->value ); input->mi.dwFlags &= ~MOUSEEVENTF_MOVE;
From: Rémi Bernon rbernon@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51357 --- dlls/winex11.drv/mouse.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index d504a68e6ba..f118d7f52a3 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -504,7 +504,6 @@ static void map_event_coords( HWND hwnd, Window window, Window event_root, int x x_root, y_root, input );
if (window == root_window) pt = root_to_virtual_screen( pt.x, pt.y ); - else if (event_root == root_window) pt = root_to_virtual_screen( x_root, y_root ); else if (!hwnd) { thread_data = x11drv_thread_data();
Hmm, cursor position test failures might be relevant.
If you don't mind, I think this also affects https://bugs.winehq.org/show_bug.cgi?id=57603 which was working until 9.22. Deleting the same line as the patch gets mouse events to land properly, although other users report that menus still open in the wrong location.
I'll provide any logs you need as you flesh out this MR.
I tested 10.2 + this patch. The position of the cursor and the actual location where it clicks are still quite different.
Situation 1: the "Spire/Spire" window (see movie) is located at the top left of the screen. Result: the location of the pointer and where it clicks are offset downwards.
Situation 2: I put the "Spire/Spire" window in the center of the screen. Result: the offset between the cursor and where it clicks is increased substantially both down and to the right.
I added a short movie where you can see it happen. If you need the output of WINEDEBUG can you please let me know which options need to be enabled?

On Wed Feb 26 18:15:07 2025 +0000, Patrick Laimbock wrote:
I tested 10.2 + this patch. The position of the cursor and the actual location where it clicks are still quite different. Situation 1: the "Spire/Spire" window (see movie) is located at the top left of the screen. Result: the location of the pointer and where it clicks are offset downwards. Situation 2: I put the "Spire/Spire" window in the center of the screen. Result: the offset between the cursor and where it clicks is increased substantially both down and to the right. I added a short movie where you can see it happen. If you need the output of WINEDEBUG can you please let me know which options need to be enabled? 
I don't know what is missing between 10.2 and HEAD. I just tested…
```sh git fetch origin merge-requests/7429/head git checkout FETCH_HEAD ./configure --prefix="$HOME/Source/wine-build" --enable-archs=i386,x86_64 make -j`nproc` make install ```
…and the mouse events are registered **correctly**. The only thing that _might_ be unusual is the amount of duplicate/repetitive calls, like `:win:GetWindowRect` (I'm guessing this is one event cascaded for each window: host, parent, wrapper, wine)
<details> <summary>very small snippet from log</summary> <pre> :win:NtUserTrackMouseEvent size 24, flags 0x2, hwnd 0x10076, time 0 :message:spy_enter_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST sent from self wp=00000000 lp=00f60169 :message:spy_exit_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST returned 00000001 :win:window_from_point scope 0x10076 (361,246) returning 0x10076 :win:NtUserTrackMouseEvent point (361,246) hwnd 0x10076 hittest 1 :msg:NtUserSetSystemTimer window 0x10076, id 0xfffa, timeout 400 :cursor:LoadCursorW 0000000000000000, #7f00 :cursor:CURSORICON_Load 0000000000000000, #7f00, 0x0, depth 32, fCursor 1, flags 0x8040 :cursor:NtUserSetCursor 0x20054 :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :message:spy_enter_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST sent from self wp=00000000 lp=00f60169 :message:spy_exit_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST returned 00000001 :win:window_from_point scope 0x10020 (361,246) returning 0x10076 :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :message:spy_exit_message (0x10076) L"{JUCE_195448f5}" [0200] WM_MOUSEMOVE returned 00000000 :msg:peek_message got type 6 msg 113 (WM_TIMER) hwnd 0x10072 wp 539 lp 0 :message:spy_enter_message (0x10072) L"yabridge plugin" [0113] WM_TIMER dispatched wp=00000539 lp=00000000 :message:spy_exit_message (0x10072) L"yabridge plugin" [0113] WM_TIMER returned 00000000 :event:merge_events discarding duplicate MotionNotify for window 2600003 :event:call_event_handler 207 MotionNotify for hwnd/window 0x10072/2600003 :cursor:X11DRV_MotionNotify hwnd 0x10072/2600003 pos 354,243 is_hint 0 serial 207 :cursor:map_event_coords hwnd 0x10072, window 2600003, event_root 402, x_root 1189, y_root 720, input 0x7ffffe0ff8f0 :cursor:map_event_coords mapped (354,243) to (354,243) :event:process_events processed 2 events, returning 1 :msg:peek_message got type 7 msg 200 (WM_MOUSEMOVE) hwnd 0x10072 wp 0 lp 0 :message:spy_enter_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST sent from self wp=00000000 lp=00f30162 :message:spy_exit_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST returned 00000001 :win:window_from_point scope 0x10072 (354,243) returning 0x10076 :message:spy_enter_message (0x10076) L"{JUCE_195448f5}" [0020] WM_SETCURSOR sent from self wp=00010076 lp=02000001 :message:spy_enter_message (0x10076) DefWindowProc:[0020] WM_SETCURSOR wp=00010076 lp=02000001 :message:spy_enter_message (0x10072) L"yabridge plugin" [0020] WM_SETCURSOR sent from self wp=00010076 lp=02000001 :msg:WINPROC_CallProcWtoA (hwnd=0000000000010072,msg=WM_SETCURSOR,wp=00010076,lp=02000001) :message:spy_enter_message (0x10072) DefWindowProc:[0020] WM_SETCURSOR wp=00010076 lp=02000001 :message:spy_exit_message (0x10072) DefWindowProc: [0020] WM_SETCURSOR returned 00000000 :message:spy_exit_message (0x10072) L"yabridge plugin" [0020] WM_SETCURSOR returned 00000000 :message:spy_exit_message (0x10076) DefWindowProc: [0020] WM_SETCURSOR returned 00000000 :message:spy_exit_message (0x10076) L"{JUCE_195448f5}" [0020] WM_SETCURSOR returned 00000000 :message:spy_enter_message (0x10076) L"{JUCE_195448f5}" [0200] WM_MOUSEMOVE dispatched wp=00000000 lp=00f30162 :msg:WINPROC_CallProcAtoW (hwnd=0000000000010076,msg=WM_MOUSEMOVE,wp=00000000,lp=00f30162) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :message:spy_enter_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST sent from self wp=00000000 lp=00f30162 :message:spy_exit_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST returned 00000001 :win:window_from_point scope 0x10020 (354,243) returning 0x10076 :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) </pre> </details>
I'll eventually get this log into the bug tracker, since—unless I built or called the wrong Wine source/binary—it looks like this will resolve mouse clicks.
I'd have to check my plugins to see if any have context menus as that was another yabridgectl complaint not resolved by removing `else if (event_root == root_window) pt = root_to_virtual_screen( x_root, y_root );`. Valhalla DSP doesn't have menus, and the rest are installed on a different computer.
Env vars: ```txt WINEDEBUG=+x11drv,+event,+cursor,+win,+msg,+message,+hid YABRIDGE_DEBUG_FILE=yabridge-log.txt YABRIDGE_DEBUG_LEVEL=1+editor ```
Thanks for this MR!
On Wed Feb 26 23:51:58 2025 +0000, Brian Wright wrote:
I don't know what is missing between 10.2 and HEAD. I just tested…
git fetch origin merge-requests/7429/head git checkout FETCH_HEAD ./configure --prefix="$HOME/Source/wine-build" --enable-archs=i386,x86_64 make -j`nproc` make install
…and the mouse events are registered **correctly**. The only thing that _might_ be unusual is the amount of duplicate/repetitive calls, like `:win:GetWindowRect` (I'm guessing this is one event cascaded for each window: host, parent, wrapper, wine)
<details> <summary>very small snippet from log</summary> <pre> :win:NtUserTrackMouseEvent size 24, flags 0x2, hwnd 0x10076, time 0 :message:spy_enter_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST sent from self wp=00000000 lp=00f60169 :message:spy_exit_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST returned 00000001 :win:window_from_point scope 0x10076 (361,246) returning 0x10076 :win:NtUserTrackMouseEvent point (361,246) hwnd 0x10076 hittest 1 :msg:NtUserSetSystemTimer window 0x10076, id 0xfffa, timeout 400 :cursor:LoadCursorW 0000000000000000, #7f00 :cursor:CURSORICON_Load 0000000000000000, #7f00, 0x0, depth 32, fCursor 1, flags 0x8040 :cursor:NtUserSetCursor 0x20054 :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :message:spy_enter_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST sent from self wp=00000000 lp=00f60169 :message:spy_exit_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST returned 00000001 :win:window_from_point scope 0x10020 (361,246) returning 0x10076 :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :message:spy_exit_message (0x10076) L"{JUCE_195448f5}" [0200] WM_MOUSEMOVE returned 00000000 :msg:peek_message got type 6 msg 113 (WM_TIMER) hwnd 0x10072 wp 539 lp 0 :message:spy_enter_message (0x10072) L"yabridge plugin" [0113] WM_TIMER dispatched wp=00000539 lp=00000000 :message:spy_exit_message (0x10072) L"yabridge plugin" [0113] WM_TIMER returned 00000000 :event:merge_events discarding duplicate MotionNotify for window 2600003 :event:call_event_handler 207 MotionNotify for hwnd/window 0x10072/2600003 :cursor:X11DRV_MotionNotify hwnd 0x10072/2600003 pos 354,243 is_hint 0 serial 207 :cursor:map_event_coords hwnd 0x10072, window 2600003, event_root 402, x_root 1189, y_root 720, input 0x7ffffe0ff8f0 :cursor:map_event_coords mapped (354,243) to (354,243) :event:process_events processed 2 events, returning 1 :msg:peek_message got type 7 msg 200 (WM_MOUSEMOVE) hwnd 0x10072 wp 0 lp 0 :message:spy_enter_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST sent from self wp=00000000 lp=00f30162 :message:spy_exit_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST returned 00000001 :win:window_from_point scope 0x10072 (354,243) returning 0x10076 :message:spy_enter_message (0x10076) L"{JUCE_195448f5}" [0020] WM_SETCURSOR sent from self wp=00010076 lp=02000001 :message:spy_enter_message (0x10076) DefWindowProc:[0020] WM_SETCURSOR wp=00010076 lp=02000001 :message:spy_enter_message (0x10072) L"yabridge plugin" [0020] WM_SETCURSOR sent from self wp=00010076 lp=02000001 :msg:WINPROC_CallProcWtoA (hwnd=0000000000010072,msg=WM_SETCURSOR,wp=00010076,lp=02000001) :message:spy_enter_message (0x10072) DefWindowProc:[0020] WM_SETCURSOR wp=00010076 lp=02000001 :message:spy_exit_message (0x10072) DefWindowProc: [0020] WM_SETCURSOR returned 00000000 :message:spy_exit_message (0x10072) L"yabridge plugin" [0020] WM_SETCURSOR returned 00000000 :message:spy_exit_message (0x10076) DefWindowProc: [0020] WM_SETCURSOR returned 00000000 :message:spy_exit_message (0x10076) L"{JUCE_195448f5}" [0020] WM_SETCURSOR returned 00000000 :message:spy_enter_message (0x10076) L"{JUCE_195448f5}" [0200] WM_MOUSEMOVE dispatched wp=00000000 lp=00f30162 :msg:WINPROC_CallProcAtoW (hwnd=0000000000010076,msg=WM_MOUSEMOVE,wp=00000000,lp=00f30162) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :message:spy_enter_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST sent from self wp=00000000 lp=00f30162 :message:spy_exit_message (0x10076) L"{JUCE_195448f5}" [0084] WM_NCHITTEST returned 00000001 :win:window_from_point scope 0x10020 (354,243) returning 0x10076 :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) :win:GetWindowRect hwnd 0000000000010076 (0,0)-(805,460) </pre> </details> I'll eventually get this log into the bug tracker, since—unless I built or called the wrong Wine source/binary—it looks like this will resolve mouse clicks. I'd have to check my plugins to see if any have context menus as that was another yabridgectl complaint not resolved by removing `else if (event_root == root_window) pt = root_to_virtual_screen( x_root, y_root );`. Valhalla DSP doesn't have menus, and the rest are installed on a different computer. Env vars: ```txt WINEDEBUG=+x11drv,+event,+cursor,+win,+msg,+message,+hid YABRIDGE_DEBUG_FILE=yabridge-log.txt YABRIDGE_DEBUG_LEVEL=1+editor ``` Thanks for this MR!
Context menus show up as if the window were located at (0, 0) on my entire screen. The plugin I'm using always loads its context menu the same (regardless of cursor position) so I have no idea if cursor plays a role.
I'll post a bug report and log but I believe it would be independent of this MR, which fixes where mouse clicks are landing in my test.
On Thu Feb 27 00:34:48 2025 +0000, Brian Wright wrote:
Context menus show up as if the window were located at (0, 0) on my entire screen. The plugin I'm using always loads its context menu the same (regardless of cursor position) so I have no idea if cursor plays a role. I'll post a bug report and log but I believe it would be independent of this MR, which fixes where mouse clicks are landing in my test.
At the very least, context menus typically open as a result of a mouse event, and they are still being positioned incorrectly after this patch (although in the opposite wrong direction as mouse clicks) so I'll link to the bug report: https://bugs.winehq.org/show_bug.cgi?id=57886
@Patrick If you're using yabridge, the PR @ https://github.com/robbert-vdh/yabridge/pull/405 should fix mouse event locations.