-- v2: dinput/tests: Add tests for mouse state tracking from pointer updates. win32u: Update driver cursor position when it is changed by pointer. win32u: Improve tracking mouse cursor based on pointer messages. server: Merge WM_POINTERUPDATE messages. server: Factor out queue_mouse_message_from_pointer(). server: Move find_pointer_from_id() up.
From: Paul Gofman pgofman@codeweavers.com
--- server/queue.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/server/queue.c b/server/queue.c index 520659d377c..1a75289787d 100644 --- a/server/queue.c +++ b/server/queue.c @@ -2539,6 +2539,22 @@ struct pointer union hw_input input; };
+static struct pointer *find_pointer_from_id( struct desktop *desktop, unsigned int id ) +{ + struct pointer *pointer; + + LIST_FOR_EACH_ENTRY( pointer, &desktop->pointers, struct pointer, entry ) + if (LOWORD(pointer->input.hw.wparam) == id) return pointer; + + pointer = mem_alloc( sizeof(struct pointer) ); + pointer->timeout = NULL; + pointer->desktop = desktop; + pointer->primary = list_empty( &desktop->pointers ); + list_add_tail( &desktop->pointers, &pointer->entry ); + + return pointer; +} + static void queue_pointer_message( struct pointer *pointer, int repeated );
static void pointer_message_timeout( void *private ) @@ -2616,22 +2632,6 @@ static void queue_pointer_message( struct pointer *pointer, int repeated ) } }
-static struct pointer *find_pointer_from_id( struct desktop *desktop, unsigned int id ) -{ - struct pointer *pointer; - - LIST_FOR_EACH_ENTRY( pointer, &desktop->pointers, struct pointer, entry ) - if (LOWORD(pointer->input.hw.wparam) == id) return pointer; - - pointer = mem_alloc( sizeof(struct pointer) ); - pointer->timeout = NULL; - pointer->desktop = desktop; - pointer->primary = list_empty( &desktop->pointers ); - list_add_tail( &desktop->pointers, &pointer->entry ); - - return pointer; -} - /* queue a hardware message for a custom type of event */ static void queue_custom_hardware_message( struct desktop *desktop, user_handle_t win, unsigned int origin, const union hw_input *input )
From: Paul Gofman pgofman@codeweavers.com
--- server/queue.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-)
diff --git a/server/queue.c b/server/queue.c index 1a75289787d..44d13122cfc 100644 --- a/server/queue.c +++ b/server/queue.c @@ -2555,6 +2555,25 @@ static struct pointer *find_pointer_from_id( struct desktop *desktop, unsigned i return pointer; }
+static void queue_mouse_message_from_pointer( struct desktop *desktop, unsigned int message, int x, int y, + user_handle_t pointer_win, unsigned int time ) +{ + const struct hw_msg_source source = { IMDT_TOUCH, IMO_HARDWARE }; + struct message *msg; + + if (!(msg = alloc_hardware_message( 0xff515700, source, time, 0 ))) return; + + msg->win = get_user_full_handle( pointer_win ); + msg->msg = message; + msg->wparam = 0; + msg->lparam = 0; + msg->x = x; + msg->y = y; + + if (!send_hook_ll_message( desktop, msg, WH_MOUSE_LL, 0, NULL )) + queue_hardware_message( desktop, msg, 1 ); +} + static void queue_pointer_message( struct pointer *pointer, int repeated );
static void pointer_message_timeout( void *private ) @@ -2602,21 +2621,13 @@ static void queue_pointer_message( struct pointer *pointer, int repeated ) queue_hardware_message( desktop, msg, 1 ); }
- if (!repeated && pointer->primary && (msg = alloc_hardware_message( 0xff515700, source, time, 0 ))) + if (!repeated && pointer->primary) { unsigned int message = WM_MOUSEMOVE; if (input->hw.msg == WM_POINTERDOWN) message = WM_LBUTTONDOWN; else if (input->hw.msg == WM_POINTERUP) message = WM_LBUTTONUP;
- msg->win = get_user_full_handle( win ); - msg->msg = message; - msg->wparam = 0; - msg->lparam = 0; - msg->x = x; - msg->y = y; - - if (!send_hook_ll_message( desktop, msg, WH_MOUSE_LL, 0, NULL )) - queue_hardware_message( desktop, msg, 0 ); + queue_mouse_message_from_pointer( desktop, message, x, y, win, time ); }
if (input->hw.msg != WM_POINTERUP)
From: Paul Gofman pgofman@codeweavers.com
--- server/queue.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
diff --git a/server/queue.c b/server/queue.c index 44d13122cfc..4ac6a670cd7 100644 --- a/server/queue.c +++ b/server/queue.c @@ -891,6 +891,35 @@ static int merge_unique_message( struct thread_input *input, unsigned int messag return 1; }
+/* try to merge pointer update message with the last in the list; return 1 if successful */ +static int merge_pointerupdate( struct thread_input *input, const struct message *msg ) +{ + struct message *prev; + + LIST_FOR_EACH_ENTRY_REV( prev, &input->msg_list, struct message, entry ) + { + if (prev->msg == msg->msg) break; + if (prev->msg >= WM_POINTERUPDATE && prev->msg <= WM_POINTERLEAVE) return 0; + } + + if (&prev->entry == &input->msg_list) return 0; + + if (prev->result) return 0; + if (prev->win != msg->win) return 0; + if (prev->type != msg->type) return 0; + if (prev->wparam != msg->wparam) return 0; + if (prev->lparam != msg->lparam) return 0; + + /* now we can merge it */ + prev->x = msg->x; + prev->y = msg->y; + prev->time = msg->time; + list_remove( &prev->entry ); + list_add_tail( &input->msg_list, &prev->entry ); + + return 1; +} + /* try to merge a message with the messages in the list; return 1 if successful */ static int merge_message( struct thread_input *input, const struct message *msg ) { @@ -898,6 +927,7 @@ static int merge_message( struct thread_input *input, const struct message *msg if (msg->msg == WM_MOUSEMOVE) return merge_mousemove( input, msg ); if (msg->msg == WM_WINE_CLIPCURSOR) return merge_unique_message( input, WM_WINE_CLIPCURSOR, msg ); if (msg->msg == WM_WINE_SETCURSOR) return merge_unique_message( input, WM_WINE_SETCURSOR, msg ); + if (msg->msg == WM_POINTERUPDATE) return merge_pointerupdate( input, msg ); return 0; }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/win32u/defwnd.c | 6 ++ dlls/win32u/input.c | 12 ++++ dlls/win32u/win32u_private.h | 1 + server/protocol.def | 8 +++ server/queue.c | 124 ++++++++++++++++++++++++++++++----- 5 files changed, 133 insertions(+), 18 deletions(-)
diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index 0cbb41d8604..83ad8fb983b 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -2732,6 +2732,12 @@ LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, f10_key = menu_sys_key = 0; break;
+ case WM_POINTERDOWN: + case WM_POINTERUP: + case WM_POINTERUPDATE: + update_mouse_state_from_pointer( hwnd, msg, GET_POINTERID_WPARAM( wparam ) ); + break; + case WM_KEYDOWN: if (wparam == VK_F10) f10_key = VK_F10; break; diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index ad4ba4d77c5..f464892cdc0 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2596,6 +2596,18 @@ BOOL WINAPI NtUserIsMouseInPointerEnabled(void) return FALSE; }
+void update_mouse_state_from_pointer( HWND hwnd, UINT msg, unsigned int pointer_id ) +{ + SERVER_START_REQ( track_mouse_from_pointer ) + { + req->win = wine_server_user_handle( hwnd ); + req->msg = msg; + req->pointer_id = pointer_id; + wine_server_call( req ); + } + SERVER_END_REQ; +} + static BOOL is_captured_by_system(void) { GUITHREADINFO info; diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index b4738ed6b16..7aec52ec964 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -104,6 +104,7 @@ extern void update_mouse_tracking_info( HWND hwnd ); extern BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset ); extern BOOL clip_fullscreen_window( HWND hwnd, BOOL reset ); extern USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout ); +void update_mouse_state_from_pointer( HWND hwnd, UINT msg, unsigned int pointer_id );
/* menu.c */ extern UINT draw_nc_menu_bar( HDC hdc, RECT *rect, HWND hwnd ); diff --git a/server/protocol.def b/server/protocol.def index 4f712b4e4e6..48f812b2e04 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2311,6 +2311,14 @@ enum message_type #define SEND_HWMSG_INJECTED 0x01
+/* Control generating of mouse events from pointer events. */ +@REQ(track_mouse_from_pointer) + user_handle_t win; /* window handle */ + unsigned int msg; /* message code */ + unsigned int pointer_id; /* pointer id */ +@END + + /* Get a message from the current queue */ @REQ(get_message) unsigned int flags; /* PM_* flags */ diff --git a/server/queue.c b/server/queue.c index 4ac6a670cd7..d6643072014 100644 --- a/server/queue.c +++ b/server/queue.c @@ -100,6 +100,15 @@ struct timer lparam_t lparam; /* lparam for message */ };
+struct pointer_state +{ + unsigned int pointer_id; /* id of pointer currently tracked, ~0u if none */ + user_handle_t pointer_win; /* pointer window */ + int button_msg_sent; /* mouse button message was already sent */ + int x; /* last seen pointer x coordinate */ + int y; /* last seen pointer y coordinate */ +}; + struct thread_input { struct object obj; /* object header */ @@ -108,6 +117,7 @@ struct thread_input int caret_state; /* caret on/off state */ struct list msg_list; /* list of hardware messages */ unsigned char desktop_keystate[256]; /* desktop keystate when keystate was synced */ + struct pointer_state pointer_state; const input_shm_t *shared; /* thread input in session shared memory */ };
@@ -250,6 +260,7 @@ static struct thread_input *create_thread_input( struct thread *thread ) { list_init( &input->msg_list ); input->shared = NULL; + input->pointer_state.pointer_id = ~0u;
if (!(input->desktop = get_thread_desktop( thread, 0 /* FIXME: access rights */ ))) { @@ -2567,19 +2578,25 @@ struct pointer user_handle_t win; int primary; union hw_input input; + int x; + int y; };
-static struct pointer *find_pointer_from_id( struct desktop *desktop, unsigned int id ) +static struct pointer *find_pointer_from_id( struct desktop *desktop, unsigned int id, int create ) { struct pointer *pointer;
LIST_FOR_EACH_ENTRY( pointer, &desktop->pointers, struct pointer, entry ) if (LOWORD(pointer->input.hw.wparam) == id) return pointer;
+ if (!create) return NULL; + pointer = mem_alloc( sizeof(struct pointer) ); pointer->timeout = NULL; pointer->desktop = desktop; pointer->primary = list_empty( &desktop->pointers ); + pointer->x = 0; + pointer->y = 0; list_add_tail( &desktop->pointers, &pointer->entry );
return pointer; @@ -2604,6 +2621,76 @@ static void queue_mouse_message_from_pointer( struct desktop *desktop, unsigned queue_hardware_message( desktop, msg, 1 ); }
+static void track_mouse_message_from_pointer( struct thread_input *input, unsigned int pointer_id, + unsigned int input_message ) +{ + struct pointer_state *state = &input->pointer_state; + struct desktop *desktop = input->desktop; + unsigned int time = get_tick_count(); + const desktop_shm_t *desktop_shm = desktop->shared; + struct pointer *pointer; + int pointer_moved; + + if (!input) return; + if (input_message == WM_POINTERUP) + { + /* hardware pointer might be destroyed at this moment so don't rely on its presence */ + if (pointer_id != state->pointer_id) return; + if (!state->button_msg_sent) + { + queue_mouse_message_from_pointer( desktop, WM_MOUSEMOVE, state->x, state->y, state->pointer_win, time ); + queue_mouse_message_from_pointer( desktop, WM_LBUTTONDOWN, state->x, state->y, state->pointer_win, time ); + } + else if (state->x != desktop_shm->cursor.x || state->y != desktop_shm->cursor.y) + { + queue_mouse_message_from_pointer( desktop, WM_MOUSEMOVE, state->x, state->y, state->pointer_win, time ); + } + queue_mouse_message_from_pointer( desktop, WM_LBUTTONUP, state->x, state->y, state->pointer_win, time ); + state->pointer_id = ~0u; + return; + } + + if (input_message != WM_POINTERDOWN && state->pointer_id != pointer_id) return; + + if (!(pointer = find_pointer_from_id( desktop, pointer_id, 0 ))) return; + if (input_message == WM_POINTERDOWN) + { + if (state->pointer_id != pointer_id && list_count( &desktop->pointers ) > 1) + { + if (!state->button_msg_sent) state->pointer_id = ~0u; + return; + } + state->pointer_id = pointer_id; + state->pointer_win = pointer->win; + state->x = pointer->x; + state->y = pointer->y; + state->button_msg_sent = 0; + return; + } + + if (input_message != WM_POINTERUPDATE) return; + state->pointer_win = pointer->win; + + pointer_moved = state->x != pointer->x || state->y != pointer->y; + + if (pointer_moved) + { + if (!state->button_msg_sent) + { + queue_mouse_message_from_pointer( desktop, WM_MOUSEMOVE, state->x, state->y, state->pointer_win, time ); + /* TODO: emulate right button and double clicks. */ + queue_mouse_message_from_pointer( desktop, WM_LBUTTONDOWN, state->x, state->y, state->pointer_win, time ); + state->button_msg_sent = 1; + } + state->x = pointer->x; + state->y = pointer->y; + } + if (pointer_moved || (state->button_msg_sent + && (state->x != desktop_shm->cursor.x || state->y != desktop_shm->cursor.y))) + queue_mouse_message_from_pointer( desktop, WM_MOUSEMOVE, state->x, state->y, state->pointer_win, time ); + return; +} + static void queue_pointer_message( struct pointer *pointer, int repeated );
static void pointer_message_timeout( void *private ) @@ -2625,40 +2712,33 @@ static void queue_pointer_message( struct pointer *pointer, int repeated ) const desktop_shm_t *desktop_shm = desktop->shared; const union hw_input *input = &pointer->input; unsigned int i, wparam = input->hw.wparam; - timeout_t time = get_tick_count(); - user_handle_t win = pointer->win; struct rectangle top_rect; struct message *msg; - int x, y;
get_virtual_screen_rect( desktop, &top_rect, 0 ); - x = LOWORD(input->hw.lparam) * (top_rect.right - top_rect.left) / 65535; - y = HIWORD(input->hw.lparam) * (top_rect.bottom - top_rect.top) / 65535; + pointer->x = LOWORD(input->hw.lparam) * (top_rect.right - top_rect.left) / 65535; + pointer->y = HIWORD(input->hw.lparam) * (top_rect.bottom - top_rect.top) / 65535;
if (pointer->primary) wparam |= POINTER_MESSAGE_FLAG_PRIMARY << 16;
for (i = 0; i < 2 && messages[input->hw.msg - WM_POINTERUPDATE][i]; i++) { - if (!(msg = alloc_hardware_message( 0, source, time, 0 ))) return; + if (!(msg = alloc_hardware_message( 0, source, get_tick_count(), 0 ))) return;
- msg->win = get_user_full_handle( win ); + msg->win = get_user_full_handle( pointer->win ); msg->msg = messages[input->hw.msg - WM_POINTERUPDATE][i]; msg->wparam = wparam; - msg->lparam = MAKELONG(x, y); + msg->lparam = MAKELONG(pointer->x, pointer->y); msg->x = desktop_shm->cursor.x; msg->y = desktop_shm->cursor.y;
queue_hardware_message( desktop, msg, 1 ); }
- if (!repeated && pointer->primary) - { - unsigned int message = WM_MOUSEMOVE; - if (input->hw.msg == WM_POINTERDOWN) message = WM_LBUTTONDOWN; - else if (input->hw.msg == WM_POINTERUP) message = WM_LBUTTONUP; - - queue_mouse_message_from_pointer( desktop, message, x, y, win, time ); - } + if (desktop->foreground_input + && desktop->foreground_input->pointer_state.pointer_id == LOWORD(pointer->input.hw.wparam) + && desktop->foreground_input->pointer_state.button_msg_sent) + track_mouse_message_from_pointer( desktop->foreground_input, LOWORD(pointer->input.hw.wparam), input->hw.msg );
if (input->hw.msg != WM_POINTERUP) { @@ -2707,7 +2787,7 @@ static void queue_custom_hardware_message( struct desktop *desktop, user_handle_
if (input->hw.msg == WM_POINTERDOWN || input->hw.msg == WM_POINTERUP || input->hw.msg == WM_POINTERUPDATE) { - pointer = find_pointer_from_id( desktop, LOWORD(input->hw.wparam) ); + pointer = find_pointer_from_id( desktop, LOWORD(input->hw.wparam), 1 ); if (pointer->timeout) remove_timeout_user( pointer->timeout ); pointer->input = *input; pointer->win = win; @@ -4270,3 +4350,11 @@ DECL_HANDLER(set_keyboard_repeat)
release_object( desktop ); } + +DECL_HANDLER(track_mouse_from_pointer) +{ + struct msg_queue *queue = get_current_queue(); + + if (queue && check_queue_input_window( queue, req->win )) + track_mouse_message_from_pointer( queue->input, req->pointer_id, req->msg ); +}
From: Paul Gofman pgofman@codeweavers.com
--- dlls/win32u/input.c | 36 +++++++++++++++++++++++++----------- server/protocol.def | 2 ++ server/queue.c | 36 ++++++++++++++++++++++++------------ 3 files changed, 51 insertions(+), 23 deletions(-)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index f464892cdc0..abd761972a0 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -729,27 +729,33 @@ BOOL WINAPI NtUserSetCursorPos( INT x, INT y ) return ret; }
+static BOOL get_shared_cursor_pos( POINT *pt, DWORD *last_change ) +{ + struct object_lock lock = OBJECT_LOCK_INIT; + const desktop_shm_t *desktop_shm; + NTSTATUS status; + + while ((status = get_shared_desktop( &lock, &desktop_shm )) == STATUS_PENDING) + { + pt->x = desktop_shm->cursor.x; + pt->y = desktop_shm->cursor.y; + *last_change = desktop_shm->cursor.last_change; + } + return !status; +} + /*********************************************************************** * get_cursor_pos */ BOOL get_cursor_pos( POINT *pt ) { - struct object_lock lock = OBJECT_LOCK_INIT; - const desktop_shm_t *desktop_shm; BOOL ret = TRUE; DWORD last_change = 0; - NTSTATUS status; RECT rect;
if (!pt) return FALSE;
- while ((status = get_shared_desktop( &lock, &desktop_shm )) == STATUS_PENDING) - { - pt->x = desktop_shm->cursor.x; - pt->y = desktop_shm->cursor.y; - last_change = desktop_shm->cursor.last_change; - } - if (status) return FALSE; + if (!get_shared_cursor_pos( pt, &last_change )) return FALSE;
/* query new position from graphics driver if we haven't updated recently */ if (NtGetTickCount() - last_change > 100) ret = user_driver->pGetCursorPos( pt ); @@ -2598,14 +2604,22 @@ BOOL WINAPI NtUserIsMouseInPointerEnabled(void)
void update_mouse_state_from_pointer( HWND hwnd, UINT msg, unsigned int pointer_id ) { + BOOL update_cursor_pos = FALSE; + DWORD last_change; + POINT pt = { 0 }; + SERVER_START_REQ( track_mouse_from_pointer ) { req->win = wine_server_user_handle( hwnd ); req->msg = msg; req->pointer_id = pointer_id; - wine_server_call( req ); + if (!wine_server_call( req )) update_cursor_pos = reply->cursor_pos_updated; } SERVER_END_REQ; + + if (!update_cursor_pos) return; + if (get_shared_cursor_pos( &pt, &last_change )) + user_driver->pSetCursorPos( pt.x, pt.y ); }
static BOOL is_captured_by_system(void) diff --git a/server/protocol.def b/server/protocol.def index 48f812b2e04..67c87d8afb5 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2316,6 +2316,8 @@ enum message_type user_handle_t win; /* window handle */ unsigned int msg; /* message code */ unsigned int pointer_id; /* pointer id */ +@REPLY + int cursor_pos_updated; /* cursor position updated */ @END
diff --git a/server/queue.c b/server/queue.c index d6643072014..8273e24d51d 100644 --- a/server/queue.c +++ b/server/queue.c @@ -107,6 +107,7 @@ struct pointer_state int button_msg_sent; /* mouse button message was already sent */ int x; /* last seen pointer x coordinate */ int y; /* last seen pointer y coordinate */ + int cursor_pos_updated; /* updated mouse cursor position since last client tracking request */ };
struct thread_input @@ -2621,7 +2622,7 @@ static void queue_mouse_message_from_pointer( struct desktop *desktop, unsigned queue_hardware_message( desktop, msg, 1 ); }
-static void track_mouse_message_from_pointer( struct thread_input *input, unsigned int pointer_id, +static int track_mouse_message_from_pointer( struct thread_input *input, unsigned int pointer_id, unsigned int input_message ) { struct pointer_state *state = &input->pointer_state; @@ -2631,44 +2632,47 @@ static void track_mouse_message_from_pointer( struct thread_input *input, unsign struct pointer *pointer; int pointer_moved;
- if (!input) return; + if (!input) return 0; if (input_message == WM_POINTERUP) { /* hardware pointer might be destroyed at this moment so don't rely on its presence */ - if (pointer_id != state->pointer_id) return; + if (pointer_id != state->pointer_id) return 0; if (!state->button_msg_sent) { queue_mouse_message_from_pointer( desktop, WM_MOUSEMOVE, state->x, state->y, state->pointer_win, time ); queue_mouse_message_from_pointer( desktop, WM_LBUTTONDOWN, state->x, state->y, state->pointer_win, time ); + state->cursor_pos_updated = 1; } else if (state->x != desktop_shm->cursor.x || state->y != desktop_shm->cursor.y) { queue_mouse_message_from_pointer( desktop, WM_MOUSEMOVE, state->x, state->y, state->pointer_win, time ); + state->cursor_pos_updated = 1; } queue_mouse_message_from_pointer( desktop, WM_LBUTTONUP, state->x, state->y, state->pointer_win, time ); state->pointer_id = ~0u; - return; + return 1; }
- if (input_message != WM_POINTERDOWN && state->pointer_id != pointer_id) return; + if (input_message != WM_POINTERDOWN && state->pointer_id != pointer_id) return 0;
- if (!(pointer = find_pointer_from_id( desktop, pointer_id, 0 ))) return; + if (!(pointer = find_pointer_from_id( desktop, pointer_id, 0 ))) return 0; if (input_message == WM_POINTERDOWN) { if (state->pointer_id != pointer_id && list_count( &desktop->pointers ) > 1) { if (!state->button_msg_sent) state->pointer_id = ~0u; - return; + return 0; } state->pointer_id = pointer_id; state->pointer_win = pointer->win; state->x = pointer->x; state->y = pointer->y; state->button_msg_sent = 0; - return; + state->cursor_pos_updated = 0; + return 1; }
- if (input_message != WM_POINTERUPDATE) return; + if (input_message != WM_POINTERUPDATE) return 0; state->pointer_win = pointer->win;
pointer_moved = state->x != pointer->x || state->y != pointer->y; @@ -2681,14 +2685,18 @@ static void track_mouse_message_from_pointer( struct thread_input *input, unsign /* TODO: emulate right button and double clicks. */ queue_mouse_message_from_pointer( desktop, WM_LBUTTONDOWN, state->x, state->y, state->pointer_win, time ); state->button_msg_sent = 1; + state->cursor_pos_updated = 1; } state->x = pointer->x; state->y = pointer->y; } if (pointer_moved || (state->button_msg_sent && (state->x != desktop_shm->cursor.x || state->y != desktop_shm->cursor.y))) + { queue_mouse_message_from_pointer( desktop, WM_MOUSEMOVE, state->x, state->y, state->pointer_win, time ); - return; + state->cursor_pos_updated = 1; + } + return 1; }
static void queue_pointer_message( struct pointer *pointer, int repeated ); @@ -4355,6 +4363,10 @@ DECL_HANDLER(track_mouse_from_pointer) { struct msg_queue *queue = get_current_queue();
- if (queue && check_queue_input_window( queue, req->win )) - track_mouse_message_from_pointer( queue->input, req->pointer_id, req->msg ); + if (queue && check_queue_input_window( queue, req->win ) + && track_mouse_message_from_pointer( queue->input, req->pointer_id, req->msg )) + { + reply->cursor_pos_updated = queue->input->pointer_state.cursor_pos_updated; + queue->input->pointer_state.cursor_pos_updated = 0; + } }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/dinput/tests/device8.c | 392 ++++++++++++++++++++++++++++++++++-- 1 file changed, 379 insertions(+), 13 deletions(-)
diff --git a/dlls/dinput/tests/device8.c b/dlls/dinput/tests/device8.c index 85c0b79d70e..52a4d9c0e25 100644 --- a/dlls/dinput/tests/device8.c +++ b/dlls/dinput/tests/device8.c @@ -50,6 +50,17 @@ struct enum_data }; };
+static void pump_messages(void) +{ + MSG msg; + + while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + DispatchMessageA( &msg ); + } +} + static void flush_events(void) { int min_timeout = 100, diff = 200; @@ -1769,13 +1780,121 @@ done:
static UINT pointer_enter_count; static UINT pointer_up_count; -static HANDLE touchdown_event, touchleave_event; +static HANDLE touchdown_event, touchmoved_event, touchleave_event; static WPARAM pointer_wparam[16]; static WPARAM pointer_lparam[16]; static UINT pointer_count; +static int touch_test_line; +static POINT last_cursor_pos; +static const BOOL *skip_defwnd_for_pointer_message; +BOOL skip_touch_mouse_tests, touch_missing_leave_todo; + +#define start_touch_test(a) start_touch_test_( __LINE__, a ) +static void start_touch_test_( int line, const BOOL *skip_defwnd ) +{ + pointer_enter_count = pointer_up_count = pointer_count = 0; + memset( pointer_wparam, 0, sizeof(pointer_wparam) ); + memset( pointer_lparam, 0, sizeof(pointer_lparam) ); + + skip_defwnd_for_pointer_message = skip_defwnd; + SetCursorPos( 0, 0 ); + GetCursorPos( &last_cursor_pos ); + touch_test_line = line; +} + +static BOOL get_touch_mouse_message( HWND hwnd, MSG *msg ) +{ + while (PeekMessageW( msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE )) + { + if (msg->message != WM_MOUSEMOVE || (GetMessageExtraInfo() & ~(ULONG_PTR)0xff) == 0xff515700) return TRUE; + } + return FALSE; +} + +static BOOL touch_mouse_message_matches( UINT msg, UINT expect_msg ) +{ + /* Pointer quick up / downs, holds are used to simulate right mouse button and double clicks on Windows. That + * is randomly triggered with the tests. Since currently that is neither avoided nor consciously emulated + * in tests just accept that all. */ + if (expect_msg == WM_LBUTTONDOWN) + return msg == WM_LBUTTONDOWN || msg == WM_RBUTTONDOWN || msg == WM_LBUTTONDBLCLK; + if (expect_msg == WM_LBUTTONUP) + return msg == WM_LBUTTONUP || msg == WM_RBUTTONUP; + return msg == expect_msg; +} + +#define expect_touch_mouse_message(a, b, c) expect_touch_mouse_message_( __LINE__, a, b, c ) +static BOOL expect_touch_mouse_message_( int line, HWND hwnd, UINT expect_msg, BOOL optional ) +{ + INPUT_MESSAGE_SOURCE source; + LPARAM extra; + DWORD pos; + BOOL bret; + POINT cp; + MSG msg; + + if (skip_touch_mouse_tests) return TRUE; + + if (!(bret = get_touch_mouse_message( hwnd, &msg )) && !optional) + { + MsgWaitForMultipleObjects( 0, NULL, FALSE, 100, QS_MOUSE ); + bret = get_touch_mouse_message( hwnd, &msg ); + } + + if (!optional) + ok_(__FILE__, line)( bret, "did not receive mouse message, expected %#x.\n", expect_msg ); + if (!bret) return FALSE; + + if (optional && !touch_mouse_message_matches( msg.message, expect_msg )) + { + trace_(__FILE__, line)( "got message %#x instead of optional %#x.\n", msg.message, expect_msg ); + return FALSE; + } + + ok_(__FILE__, line)( touch_mouse_message_matches( msg.message, expect_msg ), + "got message %#x, expected %#x.\n", msg.message, expect_msg ); + + bret = GetCurrentInputMessageSource( &source ); + ok_(__FILE__, line)( bret, "got error %ld.\n", GetLastError() ); + ok_(__FILE__, line)( source.deviceType == IMDT_TOUCH, "got deviceType %#x, expected %#x.\n", + source.deviceType, IMDT_TOUCH ); + ok_(__FILE__, line)( source.originId == IMO_HARDWARE, "got originId %#x, expected %#x.\n", + source.originId, IMO_HARDWARE ); + + cp = last_cursor_pos; + pos = msg.lParam; + ok_(__FILE__, line)( pos == MAKELPARAM(cp.x, cp.y), "got coords (%d, %d), expected (%ld, %ld).\n", + LOWORD(pos), HIWORD(pos), cp.x, cp.y ); + pos = GetMessagePos(); + ok_(__FILE__, line)( pos == MAKELPARAM(cp.x, cp.y), "got coords (%d, %d), expected (%ld, %ld).\n", + LOWORD(pos), HIWORD(pos), cp.x, cp.y ); + + extra = GetMessageExtraInfo(); + ok_(__FILE__, line)( (extra & ~(ULONG_PTR)0xff) == 0xff515700, "got message extra info %#Ix, expected %#x.\n", + extra, 0xff515700 ); + return TRUE; +}
static LRESULT CALLBACK touch_screen_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { + static struct + { + UINT pointerid; + BOOL tracking_started; + POINT curr_pos; + } + state[64]; + static unsigned int active_touch_count, total_touch_count; + + BOOL pointer_moved, curpos_changed; + UINT expect_message_after; + unsigned int i; + UINT pointerid; + LRESULT ret; + MSG message; + BOOL bret; + POINT pt; + if (msg == WM_POINTERENTER) { pointer_wparam[pointer_count] = wparam; @@ -1783,8 +1902,6 @@ static LRESULT CALLBACK touch_screen_wndproc( HWND hwnd, UINT msg, WPARAM wparam pointer_count++; pointer_enter_count++; } - if (msg == WM_POINTERDOWN) ReleaseSemaphore( touchdown_event, 1, NULL ); - if (msg == WM_POINTERUP) { pointer_wparam[pointer_count] = wparam; @@ -1792,9 +1909,182 @@ static LRESULT CALLBACK touch_screen_wndproc( HWND hwnd, UINT msg, WPARAM wparam pointer_count++; pointer_up_count++; } - if (msg == WM_POINTERLEAVE) ReleaseSemaphore( touchleave_event, 1, NULL );
- return DefWindowProcW( hwnd, msg, wparam, lparam ); + if (!skip_touch_mouse_tests && msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST) + { + INPUT_MESSAGE_SOURCE source; + GetCurrentInputMessageSource( &source ); + ok( source.deviceType != IMDT_TOUCH, "got touch mouse message %#x outside of state tracking.\n", msg ); + } + + if (!(msg >= WM_POINTERUPDATE && msg <= WM_POINTERLEAVE)) + return DefWindowProcW( hwnd, msg, wparam, lparam ); + + pointerid = GET_POINTERID_WPARAM( wparam ); + winetest_push_context( "line %d, pointer msg %#x, wp %#Ix, lp %#Ix, pointerid %#x", + touch_test_line, msg, wparam, lparam, pointerid ); + for (i = 0; i < ARRAY_SIZE(state); ++i) + { + if (state[i].pointerid == pointerid) break; + } + if (msg == WM_POINTERENTER) + { + todo_wine_if(touch_missing_leave_todo) ok( i == ARRAY_SIZE(state), "Duplicate WM_POINTERDOWN for id %#x.\n", pointerid ); + touch_missing_leave_todo = FALSE; + if (i == ARRAY_SIZE(state)) + { + for (i = 0; i < ARRAY_SIZE(state); ++i) + { + if (!state[i].pointerid) break; + } + } + else + { + ok( active_touch_count == 1, "got %u.\n", active_touch_count ); + ok( total_touch_count == 1, "got %u.\n", total_touch_count ); + if (!--active_touch_count) total_touch_count = 0; + } + ok( i < ARRAY_SIZE(state), "No free space in state table.\n" ); + state[i].pointerid = pointerid; + state[i].tracking_started = FALSE; + state[i].curr_pos.x = LOWORD(lparam); + state[i].curr_pos.y = HIWORD(lparam); + ++active_touch_count; + ++total_touch_count; + } + else if (msg != WM_POINTERLEAVE) + { + ok( i < ARRAY_SIZE(state), "Missed WM_POINTERDOWN for id %#x.\n", pointerid ); + } + + pointer_moved = LOWORD(lparam) != state[i].curr_pos.x || HIWORD(lparam) != state[i].curr_pos.y; + curpos_changed = LOWORD(lparam) != last_cursor_pos.x || HIWORD(lparam) != last_cursor_pos.y; + expect_message_after = 0; + + if (winetest_debug > 1) + trace( "msg %#x, msgpos (%d, %d), tracking %d, t %ld, pointer_moved %d, curpos_changed %d.\n", + msg, LOWORD(lparam), HIWORD(lparam), state[i].tracking_started, GetTickCount(), + pointer_moved, curpos_changed); + + if (state[i].tracking_started) + { + if (msg == WM_POINTERUPDATE && (pointer_moved || curpos_changed) + && active_touch_count && total_touch_count == 1) + { + state[i].curr_pos.x = LOWORD(lparam); + state[i].curr_pos.y = HIWORD(lparam); + last_cursor_pos = state[i].curr_pos; + expect_touch_mouse_message( hwnd, WM_MOUSEMOVE, FALSE ); + } + else if (msg == WM_POINTERUP) + { + if (pointer_moved || curpos_changed) + { + state[i].curr_pos.x = LOWORD(lparam); + state[i].curr_pos.y = HIWORD(lparam); + last_cursor_pos = state[i].curr_pos; + expect_touch_mouse_message( hwnd, WM_MOUSEMOVE, FALSE ); + } + if (!expect_touch_mouse_message( hwnd, WM_LBUTTONUP, TRUE )) + { + /* This message arrives here most of the time, but sometimes it is delayed until after DefWindowProc + * call, maybe based on timing between WM_POINTERUPDATE which changed the position first time + * and WM_POINTERUP. */ + expect_message_after = WM_LBUTTONUP; + } + } + } + else + { + if (!skip_touch_mouse_tests) do + { + bret = get_touch_mouse_message( hwnd, &message ); + + ok( !bret, "got mouse message before DefWindowProc, msg %#x, wp %#Ix, lp %#Ix.\n", + message.message, message.wParam, message.lParam ); + } while (bret); + } + + if (skip_defwnd_for_pointer_message && skip_defwnd_for_pointer_message[msg - WM_POINTERUPDATE]) + { + ret = 0; + } + else + { + /* The handling in DefWindowProc() doesn't depend on pointer flags in wparam and position in lparam */ + ret = DefWindowProcW( hwnd, msg, LOWORD(wparam), 0 ); + ok( !ret, "got %Id from DefWindowProcW.\n", ret ); + + if (skip_defwnd_for_pointer_message && skip_defwnd_for_pointer_message[WM_POINTERDOWN - WM_POINTERUPDATE]) + { + /* No mouse messages al all if DefWindowProcW() wasn't called for WM_POINTERDOWN. */ + } + else if (msg == WM_POINTERUPDATE && pointer_moved && !state[i].tracking_started && active_touch_count && total_touch_count == 1) + { + last_cursor_pos = state[i].curr_pos; + expect_touch_mouse_message( hwnd, WM_MOUSEMOVE, FALSE ); + state[i].curr_pos.x = LOWORD(lparam); + state[i].curr_pos.y = HIWORD(lparam); + if (!state[i].tracking_started) + { + state[i].tracking_started = TRUE; + expect_touch_mouse_message( hwnd, WM_LBUTTONDOWN, FALSE ); + last_cursor_pos = state[i].curr_pos; + expect_touch_mouse_message( hwnd, WM_MOUSEMOVE, FALSE ); + } + } + else if (msg == WM_POINTERUP && active_touch_count == 1 && total_touch_count == 1) + { + if (!state[i].tracking_started) + { + last_cursor_pos = state[i].curr_pos; + if (!expect_touch_mouse_message( hwnd, WM_MOUSEMOVE, TRUE )) + { + /* A lot of details go different before Win10, so don't perform the rest of mouse message tests. */ + win_skip( "Old behaviour detected, skipping remaining mouse tracking tests.\n" ); + skip_touch_mouse_tests = TRUE; + } + expect_touch_mouse_message( hwnd, WM_LBUTTONDOWN, FALSE ); + expect_touch_mouse_message( hwnd, WM_LBUTTONUP, FALSE ); + } + } + + if (!skip_touch_mouse_tests) + { + GetCursorPos( &pt ); + /* This is flaky because GetCursorPos() currently periodically updated from mouse position from + * display driver. While we try to update that on mouse position update from pointer that may + * still fail. */ + flaky_wine ok( pt.x == last_cursor_pos.x && pt.y == last_cursor_pos.y, "got (%ld, %ld), expected (%ld, %ld).\n", + pt.x, pt.y, last_cursor_pos.x, last_cursor_pos.y ); + } + } + + if (expect_message_after) expect_touch_mouse_message( hwnd, expect_message_after, FALSE ); + if (msg == WM_POINTERDOWN && total_touch_count > 1) + { + /* With double touch, depending on WM_POINTERDOWN / WM_POINTERUPDATE timing, we may get mouse down message + * here (both on Windows and Wine). Then we are going to get mouse up before DefWindowProc which will be + * handled by our test state tracker with 'tracking_started' set. */ + if (expect_touch_mouse_message( hwnd, WM_LBUTTONDOWN, TRUE )) + { + trace( "Got WM_LBUTTONDOWN on WM_POINTERDOWN.\n" ); + state[i].tracking_started = TRUE; + } + } + + if (msg == WM_POINTERUP) + { + state[i].pointerid = 0; + ok( active_touch_count, "got 0.\n" ); + if (!--active_touch_count) total_touch_count = 0; + } + + winetest_pop_context(); + if (msg == WM_POINTERUPDATE && pointer_moved) SetEvent( touchmoved_event ); + if (msg == WM_POINTERDOWN) ReleaseSemaphore( touchdown_event, 1, NULL ); + if (msg == WM_POINTERLEAVE) ReleaseSemaphore( touchleave_event, 1, NULL ); + return ret; }
static void test_hid_touch_screen(void) @@ -1910,6 +2200,16 @@ static void test_hid_touch_screen(void) .code = IOCTL_HID_READ_REPORT, .report_buf = {1, 1, 0x01,0x02,0x08,0x10}, }; + struct hid_expect touch_single2 = + { + .code = IOCTL_HID_READ_REPORT, + .report_buf = {1, 1, 0x01,0x02,0x18,0x20}, + }; + struct hid_expect touch_single3 = + { + .code = IOCTL_HID_READ_REPORT, + .report_buf = {1, 1, 0x01,0x02,0x40,0x50}, + }; struct hid_expect touch_multiple = { .code = IOCTL_HID_READ_REPORT, @@ -1928,11 +2228,26 @@ static void test_hid_touch_screen(void) .report_buf = {1,0x02}, };
+ static struct + { + BOOL skip_message[5]; + } + mouse_tests[] = + { + /* WM_POINTERUPDATE, WM_POINTERDOWN, WM_POINTERUP, WM_POINTERENTER, WM_POINTERLEAVE */ + {{ FALSE }}, + {{ FALSE, TRUE }}, + {{ TRUE }}, + {{ FALSE, FALSE, TRUE }}, + {{ TRUE, FALSE, TRUE }}, + }; + RAWINPUTDEVICE rawdevice = {.usUsagePage = HID_USAGE_PAGE_DIGITIZER, .usUsage = HID_USAGE_DIGITIZER_TOUCH_SCREEN}; UINT rawbuffer_count, rawbuffer_size, expect_flags, id, width, height; WCHAR device_path[MAX_PATH]; char rawbuffer[1024]; RAWINPUT *rawinput; + unsigned int i, j; HANDLE file; DWORD res; HWND hwnd; @@ -1943,6 +2258,8 @@ static void test_hid_touch_screen(void)
touchdown_event = CreateSemaphoreW( NULL, 0, LONG_MAX, NULL ); ok( !!touchdown_event, "CreateSemaphoreW failed, error %lu\n", GetLastError() ); + touchmoved_event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!touchmoved_event, "CreateEventW failed, error %lu\n", GetLastError() ); touchleave_event = CreateSemaphoreW( NULL, 0, LONG_MAX, NULL ); ok( !!touchleave_event, "CreateSemaphoreW failed, error %lu\n", GetLastError() );
@@ -1986,17 +2303,13 @@ static void test_hid_touch_screen(void)
/* check basic touch_screen input injection to window message */
- SetCursorPos( 0, 0 ); - hwnd = create_foreground_window( TRUE ); SetWindowLongPtrW( hwnd, GWLP_WNDPROC, (LONG_PTR)touch_screen_wndproc );
/* a single touch is automatically released if we don't send continuous updates */
- pointer_enter_count = pointer_up_count = pointer_count = 0; - memset( pointer_wparam, 0, sizeof(pointer_wparam) ); - memset( pointer_lparam, 0, sizeof(pointer_lparam) ); + start_touch_test( NULL ); bus_send_hid_input( file, &desc, &touch_single, sizeof(touch_single) );
res = MsgWaitForMultipleObjects( 0, NULL, FALSE, 500, QS_POINTER ); @@ -2033,6 +2346,8 @@ static void test_hid_touch_screen(void) todo_wine ok( HIWORD( pointer_lparam[1] ) * 128 / height == 0x10, "got lparam %#Ix\n", pointer_lparam[1] );
+ /* Wine is currently missing WM_POINTERUP generation in this case. */ + touch_missing_leave_todo = TRUE;
/* test that we receive HID rawinput type with the touchscreen */
@@ -2143,9 +2458,7 @@ static void test_hid_touch_screen(void)
/* now the touch is continuously updated */
- pointer_enter_count = pointer_up_count = pointer_count = 0; - memset( pointer_wparam, 0, sizeof(pointer_wparam) ); - memset( pointer_lparam, 0, sizeof(pointer_lparam) ); + start_touch_test( NULL ); bus_send_hid_input( file, &desc, &touch_single, sizeof(touch_single) );
res = msg_wait_for_events( 1, &touchdown_event, 1000 ); @@ -2194,12 +2507,14 @@ static void test_hid_touch_screen(void) pointer_enter_count = pointer_up_count = pointer_count = 0; memset( pointer_wparam, 0, sizeof(pointer_wparam) ); memset( pointer_lparam, 0, sizeof(pointer_lparam) ); + bus_send_hid_input( file, &desc, &touch_multiple, sizeof(touch_multiple) );
res = msg_wait_for_events( 1, &touchdown_event, 1000 ); ok( res == 0, "WaitForSingleObject returned %#lx\n", res ); res = msg_wait_for_events( 1, &touchdown_event, 1000 ); ok( res == 0, "WaitForSingleObject returned %#lx\n", res ); + res = msg_wait_for_events( 1, &touchleave_event, 10 ); ok( res == WAIT_TIMEOUT, "WaitForSingleObject returned %#lx\n", res ); ok( pointer_enter_count == 2, "got pointer_enter_count %u\n", pointer_enter_count ); @@ -2256,7 +2571,57 @@ static void test_hid_touch_screen(void) ok( LOWORD( pointer_lparam[1] ) * 128 / width == 0x18, "got lparam %#Ix\n", pointer_lparam[1] ); ok( HIWORD( pointer_lparam[1] ) * 128 / height == 0x20, "got lparam %#Ix\n", pointer_lparam[1] );
+ /* Test mouse messages generation. */ + SetCapture( hwnd ); + pump_messages(); + for (i = 0; i < ARRAY_SIZE(mouse_tests); ++i) + { + for (j = 0; j < 2; ++j) + { + winetest_push_context( "test %u, %u", i, j ); + + res = WaitForSingleObject( touchdown_event, 0 ); + ok( res == WAIT_TIMEOUT, "WaitForSingleObject returned %#lx\n", res ); + res = WaitForSingleObject( touchleave_event, 0 ); + ok( res == WAIT_TIMEOUT, "WaitForSingleObject returned %#lx\n", res ); + + start_touch_test( mouse_tests[i].skip_message ); + bus_send_hid_input( file, &desc, &touch_single, sizeof(touch_single) ); + ResetEvent( touchmoved_event ); + res = msg_wait_for_events( 1, &touchdown_event, 1000 ); + ok( res == 0, "WaitForSingleObject returned %#lx\n", res ); + + if (j) + { + /* Current cursor positon affects WM_MOUSEMOVE messages. */ + start_touch_test( mouse_tests[i].skip_message ); + } + ResetEvent( touchmoved_event ); + bus_send_hid_input( file, &desc, &touch_single2, sizeof(touch_single2) ); + + res = msg_wait_for_events( 1, &touchmoved_event, 1000 ); + ok( res == 0, "WaitForSingleObject returned %#lx\n", res ); + + if (j) start_touch_test( mouse_tests[i].skip_message ); + ResetEvent( touchmoved_event ); + bus_send_hid_input( file, &desc, &touch_single3, sizeof(touch_single3) ); + res = msg_wait_for_events( 1, &touchmoved_event, 1000 ); + ok( res == 0, "WaitForSingleObject returned %#lx\n", res ); + + if (j) start_touch_test( mouse_tests[i].skip_message ); + bus_send_hid_input( file, & desc, &touch_release, sizeof(touch_release) ); + res = msg_wait_for_events( 1, &touchleave_event, 1000 ); + ok( res == 0, "WaitForSingleObject returned %#lx\n", res ); + + start_touch_test( NULL ); + pump_messages(); + + winetest_pop_context(); + } + }
+ ret = ReleaseCapture(); + ok( ret, "ReleaseCapture() failed.\n" ); DestroyWindow( hwnd );
CloseHandle( file ); @@ -2265,6 +2630,7 @@ done: hid_device_stop( &desc, 1 );
CloseHandle( touchdown_event ); + CloseHandle( touchmoved_event ); CloseHandle( touchleave_event ); }
The central part here is that mouse messages from pointer updates are not generated unconditionally, that depends on whether the app is calling DefWindowProc for them. Also, as mentioned in [1], "If an application selectively consumes some pointer input and passes the rest to DefWindowProc, the resulting behavior is undefined.".
While the logic covered by tests, specifically, the "duality" of mouse messages generation, whether it is done during DefWindowProc or independently, may like a convoluted implementation detail, I think it is actually not and follows strict functional logic (well, as soon as design decision is not have an implicit call to say whether mouse emulation from pointer is needed or not but instead that is based on whether DefWindowProc is called).
I think the specifics covered by test and implementation deduces from the following:
- if the application processes pointer messages and doesn't call DefWindowProc at all for them (or, well, doesn't call it for WM_POINTERDOWN which starts emulation action), pointer doesn't influence mouse anyhow; - when WM_POINTERDOWN is called, WM_LBUTTONDOWN (or any mouse message) can't be immediately issued because depending on the details on touch (e. g., long touch) the mouse message may be also _RBUTTONDOWN or DBLCKICK (while we do not support it yet). So any mouse message is generated as soon as WM_POINTERUPDATE is received which changes pointer position. If no such WM_POINTERUPDATE is received until WM_POINTERUP the mouse message sequence is generated at that moment; - Mouse messages are interconnected with current cursor position. So as soon as WM_POINTERUPDATE triggered button click, and mouse pointer is being held, the difference with cursor position and WM_POINTERUPDATE should result in cursor position update and WM_MOUSEMOVE. So once pointer-to-mouse tracking is started it is not possible to do as the reaction on DefWindowProc calls anymore, that should reflect the independent differences to cursor position (e. g., from real mouse movements or explicit SetCursorPos, like in the tests).
Patch "server: Merge WM_POINTERUPDATE messages." is not strictly related, but I think constant generation of WM_POINTERUPDATE when the app doesn't consume those is also a problem. On Windows WM_POINTERUPDATE is likely "on-demand" generated similar to WM_MOUSEMOVE. Indeed, WM_POINTERUPDATE are received periodically as soon as the application peeks messages constantly. But if it doesn't then on Windows the app won't be left with the huge queue of identical WM_POINTERUPDATE messages.
1. https://learn.microsoft.com/en-us/windows/win32/inputmsg/wm-pointerdown
Rémi Bernon (@rbernon) commented about server/queue.c:
user_handle_t pointer_win, unsigned int time )
+{
- const struct hw_msg_source source = { IMDT_TOUCH, IMO_HARDWARE };
- struct message *msg;
- if (!(msg = alloc_hardware_message( 0xff515700, source, time, 0 ))) return;
- msg->win = get_user_full_handle( pointer_win );
- msg->msg = message;
- msg->wparam = 0;
- msg->lparam = 0;
- msg->x = x;
- msg->y = y;
- if (!send_hook_ll_message( desktop, msg, WH_MOUSE_LL, 0, NULL ))
queue_hardware_message( desktop, msg, 1 );
This changes always_queue from 0 to 1, is it intentional?
Rémi Bernon (@rbernon) commented about dlls/win32u/input.c:
return FALSE;
}
+void update_mouse_state_from_pointer( HWND hwnd, UINT msg, unsigned int pointer_id ) +{
- SERVER_START_REQ( track_mouse_from_pointer )
- {
req->win = wine_server_user_handle( hwnd );
req->msg = msg;
req->pointer_id = pointer_id;
wine_server_call( req );
- }
- SERVER_END_REQ;
Do we really need to call back into wineserver? Seems unfortunate, maybe there's a better way?