I've tested this change with Mount & Blade II: Bannerlord - the mouse works and feels right.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=36873 Signed-off-by: Arkadiusz Hiler ahiler@codeweavers.com --- dlls/user32/input.c | 74 ++++++++++++++++++++++++++++--- dlls/user32/tests/input.c | 79 +++++++++++++++++++++++++++++++++- include/wine/server_protocol.h | 30 ++++++++++--- server/protocol.def | 28 +++++++++--- server/queue.c | 27 ++++++++++++ server/trace.c | 30 +++++++++++++ 6 files changed, 250 insertions(+), 18 deletions(-)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 1dd43a36a11..b8c6ef6d682 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -1271,22 +1271,84 @@ TrackMouseEvent (TRACKMOUSEEVENT *ptme) * Success: count of point set in the buffer * Failure: -1 */ -int WINAPI GetMouseMovePointsEx(UINT size, LPMOUSEMOVEPOINT ptin, LPMOUSEMOVEPOINT ptout, int count, DWORD res) { +int WINAPI GetMouseMovePointsEx(UINT size, LPMOUSEMOVEPOINT ptin, LPMOUSEMOVEPOINT ptout, int count, DWORD res) +{ + cursor_pos_history_t history; + cursor_pos_t *pos; + int copied, current; + NTSTATUS ret; + BOOL found;
- if((size != sizeof(MOUSEMOVEPOINT)) || (count < 0) || (count > 64)) { + TRACE("(%d %p %p %d %d)\n", size, ptin, ptout, count, res); + + if ((size != sizeof(MOUSEMOVEPOINT)) || (count < 0) || (count > 64)) + { SetLastError(ERROR_INVALID_PARAMETER); return -1; }
- if(!ptin || (!ptout && count)) { + if (!ptin || (!ptout && count)) + { SetLastError(ERROR_NOACCESS); return -1; }
- FIXME("(%d %p %p %d %d) stub\n", size, ptin, ptout, count, res); + if (res != GMMP_USE_DISPLAY_POINTS) + { + FIXME("only GMMP_USE_DISPLAY_POINTS is supported for now\n"); + SetLastError(ERROR_POINT_NOT_FOUND); + return -1; + }
- SetLastError(ERROR_POINT_NOT_FOUND); - return -1; + SERVER_START_REQ( set_cursor ) + { + req->flags = SET_CURSOR_GET_HISTORY; + wine_server_set_reply( req, &history, sizeof(history) ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + + if (ret) + { + SetLastError(ret); + return -1; + } + + found = FALSE; + current = history.last; + while (history.size) + { + pos = &history.positions[current]; + + if (ptin->x == pos->x && ptin->y == pos->y && (!ptin->time || ptin->time == pos->time)) + { + found = TRUE; + break; + } + + current = (current + ARRAY_SIZE(history.positions) - 1) % ARRAY_SIZE(history.positions); + history.size--; + } + + if (!found) + { + SetLastError(ERROR_POINT_NOT_FOUND); + return -1; + } + + copied = 0; + while (history.size-- && copied < count) + { + cursor_pos_t *pos = &history.positions[current]; + ptout[copied].x = pos->x; + ptout[copied].y = pos->y; + ptout[copied].time = pos->time; + ptout[copied].dwExtraInfo = pos->info; + current = (current + ARRAY_SIZE(history.positions) - 1) % ARRAY_SIZE(history.positions); + copied++; + } + + return copied; }
/*********************************************************************** diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 1809c147cbd..998e0d585fe 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1481,9 +1481,11 @@ static void test_GetMouseMovePointsEx(void) MOUSEMOVEPOINT in; MOUSEMOVEPOINT out[200]; POINT point; + TEST_INPUT input;
/* Get a valid content for the input struct */ - if(!GetCursorPos(&point)) { + if (!GetCursorPos(&point)) + { win_skip("GetCursorPos() failed with error %u\n", GetLastError()); return; } @@ -1605,6 +1607,81 @@ static void test_GetMouseMovePointsEx(void) ok(GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == MYERROR, "expected error ERROR_INVALID_PARAMETER, got %u\n", GetLastError());
+ /* more than 64 to be sure we wrap around */ + for (int i = 0; i < 67; i++) + { + in.x = i; + in.y = i*2; + SetCursorPos(in.x, in.y); + } + + retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS); + ok(retval == 64, "expected to get 64 mouse move points but got %d\n", retval); + + for (int i = 0; i < retval; i++) + { + ok(out[i].x == in.x && out[i].y == in.y, "wrong position %d, expected %dx%d got %dx%d\n", i, in.x, in.y, out[i].x, out[i].y); + in.x--; + in.y -= 2; + } + + /* make there's no deduplication */ + SetCursorPos(6, 6); + SetCursorPos(6, 6); + in.x = 6; + in.y = 6; + retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS); + ok(retval == 64, "expected to get 64 mouse move points but got %d\n", retval); + ok(out[0].x == 6 && out[0].y == 6, "expected cursor position to be 6x6 but got %d %d\n", out[0].x, out[0].y); + ok(out[1].x == 6 && out[1].y == 6, "expected cursor position to be 6x6 but got %d %d\n", out[1].x, out[1].y); + + /* make sure 2 events are distinguishable by their timestamps */ + in.x = 150; + in.y = 75; + SetCursorPos(30, 30); + SetCursorPos(in.x, in.y); + SetCursorPos(150, 150); + Sleep(3); + SetCursorPos(in.x, in.y); + + retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS); + ok(retval == 64, "expected to get 64 mouse move points but got %d\n", retval); + ok(out[0].x == 150 && out[0].y == 75, "expected cursor position to be 150x75 but got %d %d\n", out[0].x, out[0].y); + ok(out[1].x == 150 && out[1].y == 150, "expected cursor position to be 150x150 but got %d %d\n", out[1].x, out[1].y); + ok(out[2].x == 150 && out[2].y == 75, "expected cursor position to be 150x75 but got %d %d\n", out[2].x, out[2].y); + + in.time = out[2].time; + retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS); + ok(retval == 62, "expected to get 62 mouse move points but got %d\n", retval); + ok(out[0].x == 150 && out[0].y == 75, "expected cursor position to be 150x75 but got %d %d\n", out[0].x, out[0].y); + ok(out[1].x == 30 && out[1].y == 30, "expected cursor position to be 30x30 but got %d %d\n", out[1].x, out[1].y); + + /* events created through other means should also be on the list with correct extra info */ + mouse_event(MOUSEEVENTF_MOVE, -13, 17, 0, 0xcafecafe); + ok(GetCursorPos(&point), "failed to get cursor positon\n"); + ok(in.x != point.x && in.y != point.y, "cursor didn't change position after moue_event()\n"); + in.time = 0; + in.x = point.x; + in.y = point.y; + retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS); + ok(retval == 64, "expected to get 64 mouse move points but got %d\n", retval); + ok(out[0].dwExtraInfo == 0xcafecafe, "wrong extra info, got 0x%lx expected 0xcafecafe\n", out[0].dwExtraInfo); + + input.type = INPUT_MOUSE; + memset(&input, 0, sizeof(input)); + input.u.mi.dwFlags = MOUSEEVENTF_MOVE; + input.u.mi.dwExtraInfo = 0xdeadbeef; + input.u.mi.dx = -17; + input.u.mi.dy = 13; + SendInput(1, (INPUT*) &input, sizeof(INPUT)); + ok(GetCursorPos(&point), "failed to get cursor positon\n"); + ok(in.x != point.x && in.y != point.y, "cursor didn't change position after moue_event()\n"); + in.time = 0; + in.x = point.x; + in.y = point.y; + retval = pGetMouseMovePointsEx(sizeof(MOUSEMOVEPOINT), &in, out, BUFLIM, GMMP_USE_DISPLAY_POINTS); + ok(retval == 64, "expected to get 64 mouse move points but got %d\n", retval); + ok(out[0].dwExtraInfo == 0xdeadbeef, "wrong extra info, got 0x%lx expected 0xdeadbeef\n", out[0].dwExtraInfo); #undef BUFLIM #undef MYERROR } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 5259b5a60ce..e7cb6a2518c 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -766,6 +766,22 @@ struct rawinput_device user_handle_t target; };
+typedef struct +{ + int x; + int y; + unsigned int time; + int __pad; + lparam_t info; +} cursor_pos_t; + +typedef struct +{ + unsigned int size; + unsigned int last; + cursor_pos_t positions[64]; +} cursor_pos_history_t; +
@@ -5264,13 +5280,15 @@ struct set_cursor_reply int new_y; rectangle_t new_clip; unsigned int last_change; + /* VARARG(history,cursor_pos_history); */ char __pad_52[4]; }; -#define SET_CURSOR_HANDLE 0x01 -#define SET_CURSOR_COUNT 0x02 -#define SET_CURSOR_POS 0x04 -#define SET_CURSOR_CLIP 0x08 -#define SET_CURSOR_NOCLIP 0x10 +#define SET_CURSOR_HANDLE 0x01 +#define SET_CURSOR_COUNT 0x02 +#define SET_CURSOR_POS 0x04 +#define SET_CURSOR_CLIP 0x08 +#define SET_CURSOR_NOCLIP 0x10 +#define SET_CURSOR_GET_HISTORY 0x20
@@ -6320,7 +6338,7 @@ union generic_reply
/* ### protocol_version begin ### */
-#define SERVER_PROTOCOL_VERSION 641 +#define SERVER_PROTOCOL_VERSION 642
/* ### protocol_version end ### */
diff --git a/server/protocol.def b/server/protocol.def index 92290af701c..7d278653414 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -782,6 +782,22 @@ struct rawinput_device user_handle_t target; };
+typedef struct +{ + int x; + int y; + unsigned int time; + int __pad; + lparam_t info; +} cursor_pos_t; + +typedef struct +{ + unsigned int size; + unsigned int last; + cursor_pos_t positions[64]; +} cursor_pos_history_t; + /****************************************************************/ /* Request declarations */
@@ -3606,12 +3622,14 @@ struct handle_info int new_y; rectangle_t new_clip; /* new clip rectangle */ unsigned int last_change; /* time of last position change */ + VARARG(history,cursor_pos_history); /* history of cursor positions */ @END -#define SET_CURSOR_HANDLE 0x01 -#define SET_CURSOR_COUNT 0x02 -#define SET_CURSOR_POS 0x04 -#define SET_CURSOR_CLIP 0x08 -#define SET_CURSOR_NOCLIP 0x10 +#define SET_CURSOR_HANDLE 0x01 +#define SET_CURSOR_COUNT 0x02 +#define SET_CURSOR_POS 0x04 +#define SET_CURSOR_CLIP 0x08 +#define SET_CURSOR_NOCLIP 0x10 +#define SET_CURSOR_GET_HISTORY 0x20
/* Batch read rawinput message data */ diff --git a/server/queue.c b/server/queue.c index c1016016051..7afb1f22bae 100644 --- a/server/queue.c +++ b/server/queue.c @@ -225,6 +225,8 @@ static const struct object_ops thread_input_ops = /* pointer to input structure of foreground thread */ static unsigned int last_input_time;
+static cursor_pos_history_t cursor_history; + static void queue_hardware_message( struct desktop *desktop, struct message *msg, int always_queue ); static void free_message( struct message *msg );
@@ -1518,14 +1520,34 @@ static void update_rawinput_device(const struct rawinput_device *device) e->device.target = get_user_full_handle( e->device.target ); }
+static void append_cursor_history( int x, int y, unsigned int time, lparam_t info ) +{ + cursor_pos_t *pos; + cursor_history.last = (cursor_history.last + 1) % ARRAY_SIZE( cursor_history.positions ); + pos = &cursor_history.positions[cursor_history.last]; + + pos->x = x; + pos->y = y; + pos->time = time; + pos->info = info; + + if (cursor_history.size < ARRAY_SIZE( cursor_history.positions )) + cursor_history.size++; +} + /* queue a hardware message into a given thread input */ static void queue_hardware_message( struct desktop *desktop, struct message *msg, int always_queue ) { user_handle_t win; struct thread *thread; struct thread_input *input; + struct hardware_msg_data *msg_data; unsigned int msg_code;
+ msg_data = msg->data; + if (msg->msg == WM_MOUSEMOVE) + append_cursor_history( msg->x, msg->y, msg->time, msg_data->info ); + update_input_key_state( desktop, desktop->keystate, msg->msg, msg->wparam ); last_input_time = get_tick_count(); if (msg->msg != WM_MOUSEMOVE) always_queue = 1; @@ -3213,6 +3235,11 @@ DECL_HANDLER(set_cursor)
set_clip_rectangle( desktop, (req->flags & SET_CURSOR_NOCLIP) ? NULL : &req->clip, 0 ); } + if (req->flags & SET_CURSOR_GET_HISTORY) + { + if (sizeof(cursor_history) <= get_reply_max_size()) + set_reply_data( &cursor_history, min( sizeof(cursor_history), get_reply_max_size() ) ); + }
reply->new_x = input->desktop->cursor.x; reply->new_y = input->desktop->cursor.y; diff --git a/server/trace.c b/server/trace.c index c93e4fe3b4e..9bb76163c52 100644 --- a/server/trace.c +++ b/server/trace.c @@ -885,6 +885,35 @@ static void dump_varargs_rectangles( const char *prefix, data_size_t size ) remove_data( size ); }
+static void dump_varargs_cursor_pos_history( const char *prefix, data_size_t size) +{ + data_size_t len; + const cursor_pos_history_t *history = cur_data; + const cursor_pos_t *pos; + if (size != sizeof(*history)) + { + fprintf( stderr, "%s{}", prefix ); + return; + } + + fprintf( stderr, "%s{", prefix ); + fprintf( stderr, "size=%u,", history->size); + fprintf( stderr, "last=%u,", history->last); + fprintf( stderr, "positions={" ); + len = ARRAY_SIZE( history->positions ); + pos = history->positions; + while (len > 0) + { + fprintf( stderr, "{x=%d,y=%d,time=%u", pos->x, pos->y, pos->time ); + dump_uint64( ",info=", &pos->info ); + fputc( '}', stderr ); + pos++; + if (--len) fputc( ',', stderr ); + } + fputc( '}', stderr ); + fputc( '}', stderr ); +} + static void dump_varargs_message_data( const char *prefix, data_size_t size ) { /* FIXME: dump the structured data */ @@ -4321,6 +4350,7 @@ static void dump_set_cursor_reply( const struct set_cursor_reply *req ) fprintf( stderr, ", new_y=%d", req->new_y ); dump_rectangle( ", new_clip=", &req->new_clip ); fprintf( stderr, ", last_change=%08x", req->last_change ); + dump_varargs_cursor_pos_history( ", history=", cur_size ); }
static void dump_get_rawinput_buffer_request( const struct get_rawinput_buffer_request *req )