Hi all,
I'm sending out this as follow-up of my previous e-mail [1] about dinput and background input processing, hopefully to get some comments and to discuss about the idea and the implementation.
The patch series also depends on some additional dinput8 patches I already sent to the mailing list, to implement dinput8 devices on top of raw input, but I think they can be independent.
There's a few points I believe that may be of concern, the main one being a privacy issue. Allowing Wine applications to listen to input while in background would allow keyloggers to work in Wine. One could argue though that this implementation does not use any priviledged method for that, and that Xlib allows any application to listen to raw input events in background. I guess there may be some ways to get an exclusive device access, to prevent other apps to get the events but I didn't investigate this part.
Also, this is currently only used for Wine dinput8 implementation, so we could also add some internal mechanism to enforce only Wine dinput8 to be able to access these events.
[1] https://www.winehq.org/pipermail/wine-devel/2019-August/149941.html
-- Rémi Bernon rbernon@codeweavers.com
Derek Lesho (1): user32: Add support for RIDEV_NOLEGACY flag.
Rémi Bernon (7): server: Add send_hardware_message flags to hint raw input translation. user32: Add __wine_send_input flags to hint raw input translation. server: Implement raw mouse/keyboard input message broadcast. user32: Add support for RIDEV_INPUTSINK flag. winex11.drv: Listen to XInput2 master devices only. winex11.drv: Listen to RawMotion and RawButton* events in the desktop thread. winex11.drv: Listen to RawKey* events in the desktop thread.
dlls/user32/input.c | 4 +- dlls/user32/rawinput.c | 9 ++- dlls/user32/tests/rawinput.c | 2 - dlls/user32/user32.spec | 2 +- dlls/wineandroid.drv/keyboard.c | 2 +- dlls/wineandroid.drv/window.c | 4 +- dlls/winemac.drv/ime.c | 4 +- dlls/winemac.drv/keyboard.c | 2 +- dlls/winemac.drv/mouse.c | 2 +- dlls/winex11.drv/event.c | 42 +--------- dlls/winex11.drv/keyboard.c | 42 +++++++++- dlls/winex11.drv/mouse.c | 131 +++++++++++++++++++------------- dlls/winex11.drv/x11drv.h | 7 +- dlls/winex11.drv/x11drv_main.c | 5 ++ include/winuser.h | 2 +- server/protocol.def | 3 + server/queue.c | 106 +++++++++++++++++++++----- 17 files changed, 236 insertions(+), 133 deletions(-)
-- 2.23.0
--- server/protocol.def | 2 ++ server/queue.c | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/server/protocol.def b/server/protocol.def index 2647e87f1fe..f7ce29977a2 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2315,6 +2315,8 @@ enum message_type VARARG(keystate,bytes); /* global state array for all the keys */ @END #define SEND_HWMSG_INJECTED 0x01 +#define SEND_HWMSG_ONLY_RAW 0x02 +#define SEND_HWMSG_SKIP_RAW 0x04
/* Get a message from the current queue */ diff --git a/server/queue.c b/server/queue.c index 6207fdb1f7c..a02ef10e742 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1598,7 +1598,7 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa
/* queue a hardware message for a mouse event */ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, const hw_input_t *input, - unsigned int origin, struct msg_queue *sender ) + unsigned int origin, struct msg_queue *sender, unsigned int req_flags ) { const struct rawinput_device *device; struct hardware_msg_data *msg_data; @@ -1651,7 +1651,8 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons y = desktop->cursor.y; }
- if ((device = current->process->rawinput_mouse)) + if ((device = current->process->rawinput_mouse) && + !(req_flags & SEND_HWMSG_SKIP_RAW)) { if (!(msg = alloc_hardware_message( input->mouse.info, source, time ))) return 0; msg_data = msg->data; @@ -1670,6 +1671,9 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons queue_hardware_message( desktop, msg, 0 ); }
+ if (req_flags & SEND_HWMSG_ONLY_RAW) + return 0; + for (i = 0; i < ARRAY_SIZE( messages ); i++) { if (!messages[i]) continue; @@ -1701,7 +1705,7 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons
/* queue a hardware message for a keyboard event */ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, const hw_input_t *input, - unsigned int origin, struct msg_queue *sender ) + unsigned int origin, struct msg_queue *sender, unsigned int req_flags ) { struct hw_msg_source source = { IMDT_KEYBOARD, origin }; const struct rawinput_device *device; @@ -1777,7 +1781,8 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c break; }
- if ((device = current->process->rawinput_kbd)) + if ((device = current->process->rawinput_kbd) && + !(req_flags & SEND_HWMSG_SKIP_RAW)) { if (!(msg = alloc_hardware_message( input->kbd.info, source, time ))) return 0; msg_data = msg->data; @@ -1795,6 +1800,9 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c queue_hardware_message( desktop, msg, 0 ); }
+ if (req_flags & SEND_HWMSG_ONLY_RAW) + return 0; + if (!(msg = alloc_hardware_message( input->kbd.info, source, time ))) return 0; msg_data = msg->data;
@@ -2351,10 +2359,10 @@ DECL_HANDLER(send_hardware_message) switch (req->input.type) { case INPUT_MOUSE: - reply->wait = queue_mouse_message( desktop, req->win, &req->input, origin, sender ); + reply->wait = queue_mouse_message( desktop, req->win, &req->input, origin, sender, req->flags ); break; case INPUT_KEYBOARD: - reply->wait = queue_keyboard_message( desktop, req->win, &req->input, origin, sender ); + reply->wait = queue_keyboard_message( desktop, req->win, &req->input, origin, sender, req->flags ); break; case INPUT_HARDWARE: queue_custom_hardware_message( desktop, req->win, origin, &req->input );
--- dlls/user32/input.c | 4 ++-- dlls/user32/user32.spec | 2 +- dlls/wineandroid.drv/keyboard.c | 2 +- dlls/wineandroid.drv/window.c | 4 ++-- dlls/winemac.drv/ime.c | 4 ++-- dlls/winemac.drv/keyboard.c | 2 +- dlls/winemac.drv/mouse.c | 2 +- dlls/winex11.drv/keyboard.c | 2 +- dlls/winex11.drv/mouse.c | 8 ++++---- include/winuser.h | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 8b2ae805aa7..b023963004e 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -122,9 +122,9 @@ BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ) * * Internal SendInput function to allow the graphics driver to inject real events. */ -BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input ) +BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input, UINT flags ) { - NTSTATUS status = send_hardware_message( hwnd, input, 0 ); + NTSTATUS status = send_hardware_message( hwnd, input, flags ); if (status) SetLastError( RtlNtStatusToDosError(status) ); return !status; } diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec index f9a4ae26df4..3b934dc4057 100644 --- a/dlls/user32/user32.spec +++ b/dlls/user32/user32.spec @@ -832,5 +832,5 @@ # All functions must be prefixed with '__wine_' (for internal functions) # or 'wine_' (for user-visible functions) to avoid namespace conflicts. # -@ cdecl __wine_send_input(long ptr) +@ cdecl __wine_send_input(long ptr long) @ cdecl __wine_set_pixel_format(long long) diff --git a/dlls/wineandroid.drv/keyboard.c b/dlls/wineandroid.drv/keyboard.c index 2c37c42e0d4..8400414b8d4 100644 --- a/dlls/wineandroid.drv/keyboard.c +++ b/dlls/wineandroid.drv/keyboard.c @@ -680,7 +680,7 @@ static void send_keyboard_input( HWND hwnd, WORD vkey, WORD scan, DWORD flags ) input.u.ki.time = 0; input.u.ki.dwExtraInfo = 0;
- __wine_send_input( hwnd, &input ); + __wine_send_input( hwnd, &input, 0 ); }
/*********************************************************************** diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c index eb05aaf2832..e95fb481972 100644 --- a/dlls/wineandroid.drv/window.c +++ b/dlls/wineandroid.drv/window.c @@ -521,7 +521,7 @@ static int process_events( DWORD mask ) } SERVER_END_REQ; } - __wine_send_input( capture ? capture : event->data.motion.hwnd, &event->data.motion.input ); + __wine_send_input( capture ? capture : event->data.motion.hwnd, &event->data.motion.input, 0 ); } break;
@@ -535,7 +535,7 @@ static int process_events( DWORD mask ) event->data.kbd.input.u.ki.wVk, event->data.kbd.input.u.ki.wVk, event->data.kbd.input.u.ki.wScan ); update_keyboard_lock_state( event->data.kbd.input.u.ki.wVk, event->data.kbd.lock_state ); - __wine_send_input( 0, &event->data.kbd.input ); + __wine_send_input( 0, &event->data.kbd.input, 0 ); break;
default: diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c index dabe6654f98..69c85a07842 100644 --- a/dlls/winemac.drv/ime.c +++ b/dlls/winemac.drv/ime.c @@ -1427,10 +1427,10 @@ void macdrv_im_set_text(const macdrv_event *event) { input.ki.wScan = chars[i]; input.ki.dwFlags = KEYEVENTF_UNICODE; - __wine_send_input(hwnd, &input); + __wine_send_input(hwnd, &input, 0);
input.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; - __wine_send_input(hwnd, &input); + __wine_send_input(hwnd, &input, 0); } }
diff --git a/dlls/winemac.drv/keyboard.c b/dlls/winemac.drv/keyboard.c index aed4ff0d6e4..39230c0809d 100644 --- a/dlls/winemac.drv/keyboard.c +++ b/dlls/winemac.drv/keyboard.c @@ -929,7 +929,7 @@ static void macdrv_send_keyboard_input(HWND hwnd, WORD vkey, WORD scan, DWORD fl input.ki.time = time; input.ki.dwExtraInfo = 0;
- __wine_send_input(hwnd, &input); + __wine_send_input(hwnd, &input, 0); }
diff --git a/dlls/winemac.drv/mouse.c b/dlls/winemac.drv/mouse.c index dd6443fe1ba..b510b512fc3 100644 --- a/dlls/winemac.drv/mouse.c +++ b/dlls/winemac.drv/mouse.c @@ -165,7 +165,7 @@ static void send_mouse_input(HWND hwnd, macdrv_window cocoa_window, UINT flags, input.mi.time = time; input.mi.dwExtraInfo = 0;
- __wine_send_input(top_level_hwnd, &input); + __wine_send_input(top_level_hwnd, &input, 0); }
diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 131c5f5442f..61a2881e60c 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1148,7 +1148,7 @@ static void X11DRV_send_keyboard_input( HWND hwnd, WORD vkey, WORD scan, DWORD f input.u.ki.time = time; input.u.ki.dwExtraInfo = 0;
- __wine_send_input( hwnd, &input ); + __wine_send_input( hwnd, &input, 0 ); }
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 8d1dc5e35d7..cedb488614d 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -612,7 +612,7 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU } input->u.mi.dx += clip_rect.left; input->u.mi.dy += clip_rect.top; - __wine_send_input( hwnd, input ); + __wine_send_input( hwnd, input, 0 ); return; }
@@ -673,7 +673,7 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU
input->u.mi.dx = pt.x; input->u.mi.dy = pt.y; - __wine_send_input( hwnd, input ); + __wine_send_input( hwnd, input, 0 ); }
#ifdef SONAME_LIBXCURSOR @@ -1619,7 +1619,7 @@ void move_resize_window( HWND hwnd, int dir ) input.u.mi.dwFlags = button_up_flags[button - 1] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; input.u.mi.time = GetTickCount(); input.u.mi.dwExtraInfo = 0; - __wine_send_input( hwnd, &input ); + __wine_send_input( hwnd, &input, 0 ); }
while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE )) @@ -1846,7 +1846,7 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev ) TRACE( "pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, dx, dy );
input.type = INPUT_MOUSE; - __wine_send_input( 0, &input ); + __wine_send_input( 0, &input, 0 ); return TRUE; }
diff --git a/include/winuser.h b/include/winuser.h index 51c73d25c2f..10cebfa97d0 100644 --- a/include/winuser.h +++ b/include/winuser.h @@ -4389,7 +4389,7 @@ static inline BOOL WINAPI SetRectEmpty(LPRECT rect) WORD WINAPI SYSTEM_KillSystemTimer( WORD );
#ifdef __WINESRC__ -WINUSERAPI BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input ); +WINUSERAPI BOOL CDECL __wine_send_input( HWND hwnd, const INPUT *input, UINT flags ); #endif
#ifdef __cplusplus
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=56433
Your paranoid android.
=== debian10 (32 bit report) ===
user32: msg.c:5145: Test succeeded inside todo block: ShowWindow(SW_SHOWMINIMIZED):overlapped: marked "todo_wine" but succeeds
Report errors: user32:msg prints too much data (35221 bytes)
=== debian10 (32 bit Chinese:China report) ===
user32: msg.c:5145: Test succeeded inside todo block: ShowWindow(SW_SHOWMINIMIZED):overlapped: marked "todo_wine" but succeeds
Report errors: user32:msg prints too much data (35221 bytes)
=== debian10 (32 bit WoW report) ===
user32: msg.c:5145: Test succeeded inside todo block: ShowWindow(SW_SHOWMINIMIZED):overlapped: marked "todo_wine" but succeeds
Report errors: user32:msg prints too much data (35221 bytes)
=== debian10 (64 bit WoW report) ===
user32: msg.c:5145: Test succeeded inside todo block: ShowWindow(SW_SHOWMINIMIZED):overlapped: marked "todo_wine" but succeeds
Report errors: user32:msg prints too much data (35221 bytes)
This implements broadcast of raw input messages to all listening processes when the user driver sends raw input with the BCAST flag.
This allows host drivers to listen to raw input events from a unique thread, avoiding duplicate input events and the induced computation cost, as well as the possibility to listen to raw events in background. --- server/protocol.def | 1 + server/queue.c | 85 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 65 insertions(+), 21 deletions(-)
diff --git a/server/protocol.def b/server/protocol.def index f7ce29977a2..7854e31b2b1 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2317,6 +2317,7 @@ enum message_type #define SEND_HWMSG_INJECTED 0x01 #define SEND_HWMSG_ONLY_RAW 0x02 #define SEND_HWMSG_SKIP_RAW 0x04 +#define SEND_HWMSG_BCAST_RAW 0x08
/* Get a message from the current queue */ diff --git a/server/queue.c b/server/queue.c index a02ef10e742..5f8b82c864c 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1596,12 +1596,54 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa return 1; }
+struct raw_input_message +{ + struct desktop *desktop; + struct hw_msg_source source; + unsigned int time; + struct hardware_msg_data data; +}; + +static int queue_raw_input_message( struct process* process, void* user ) +{ + const struct raw_input_message* raw_msg = user; + const struct rawinput_device *device = NULL; + struct desktop *desktop = NULL; + struct thread *thread = NULL; + struct message *msg; + + if (raw_msg->data.rawinput.type == RIM_TYPEMOUSE) + device = process->rawinput_mouse; + else if (raw_msg->data.rawinput.type == RIM_TYPEKEYBOARD) + device = process->rawinput_kbd; + + if (!device) + return 0; + if (!(desktop = get_desktop_obj( process, process->desktop, 0 )) || desktop != raw_msg->desktop) + goto done; + if (!(msg = alloc_hardware_message( raw_msg->data.info, raw_msg->source, raw_msg->time ))) + goto done; + + msg->win = device->target; + msg->msg = WM_INPUT; + msg->wparam = RIM_INPUT; + msg->lparam = 0; + memcpy( msg->data, &raw_msg->data, sizeof(raw_msg->data) ); + + queue_hardware_message( desktop, msg, 0 ); + +done: + if (thread) release_object( thread ); + if (desktop) release_object( desktop ); + return 0; +} + /* queue a hardware message for a mouse event */ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, const hw_input_t *input, unsigned int origin, struct msg_queue *sender, unsigned int req_flags ) { - const struct rawinput_device *device; struct hardware_msg_data *msg_data; + struct raw_input_message raw_msg; struct message *msg; unsigned int i, time, flags; struct hw_msg_source source = { IMDT_MOUSE, origin }; @@ -1651,24 +1693,24 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons y = desktop->cursor.y; }
- if ((device = current->process->rawinput_mouse) && - !(req_flags & SEND_HWMSG_SKIP_RAW)) + if (!(req_flags & SEND_HWMSG_SKIP_RAW)) { - if (!(msg = alloc_hardware_message( input->mouse.info, source, time ))) return 0; - msg_data = msg->data; - - msg->win = device->target; - msg->msg = WM_INPUT; - msg->wparam = RIM_INPUT; - msg->lparam = 0; + raw_msg.desktop = desktop; + raw_msg.source = source; + raw_msg.time = time;
+ msg_data = &raw_msg.data; + msg_data->info = input->mouse.info; msg_data->flags = flags; msg_data->rawinput.type = RIM_TYPEMOUSE; msg_data->rawinput.mouse.x = x - desktop->cursor.x; msg_data->rawinput.mouse.y = y - desktop->cursor.y; msg_data->rawinput.mouse.data = input->mouse.data;
- queue_hardware_message( desktop, msg, 0 ); + if (req_flags & SEND_HWMSG_BCAST_RAW) + enum_processes( queue_raw_input_message, &raw_msg ); + else + queue_raw_input_message( current->process, &raw_msg ); }
if (req_flags & SEND_HWMSG_ONLY_RAW) @@ -1708,8 +1750,8 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c unsigned int origin, struct msg_queue *sender, unsigned int req_flags ) { struct hw_msg_source source = { IMDT_KEYBOARD, origin }; - const struct rawinput_device *device; struct hardware_msg_data *msg_data; + struct raw_input_message raw_msg; struct message *msg; unsigned char vkey = input->kbd.vkey; unsigned int message_code, time; @@ -1781,23 +1823,24 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c break; }
- if ((device = current->process->rawinput_kbd) && - !(req_flags & SEND_HWMSG_SKIP_RAW)) + if (!(req_flags & SEND_HWMSG_SKIP_RAW)) { - if (!(msg = alloc_hardware_message( input->kbd.info, source, time ))) return 0; - msg_data = msg->data; - - msg->win = device->target; - msg->msg = WM_INPUT; - msg->wparam = RIM_INPUT; + raw_msg.desktop = desktop; + raw_msg.source = source; + raw_msg.time = time;
+ msg_data = &raw_msg.data; + msg_data->info = input->kbd.info; msg_data->flags = input->kbd.flags; msg_data->rawinput.type = RIM_TYPEKEYBOARD; msg_data->rawinput.kbd.message = message_code; msg_data->rawinput.kbd.vkey = vkey; msg_data->rawinput.kbd.scan = input->kbd.scan;
- queue_hardware_message( desktop, msg, 0 ); + if (req_flags & SEND_HWMSG_BCAST_RAW) + enum_processes( queue_raw_input_message, &raw_msg ); + else + queue_raw_input_message( current->process, &raw_msg ); }
if (req_flags & SEND_HWMSG_ONLY_RAW)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=56434
Your paranoid android.
=== debian10 (32 bit report) ===
user32: menu.c:2354: Test failed: test 25 msg.c:5145: Test succeeded inside todo block: ShowWindow(SW_SHOWMINIMIZED):overlapped: marked "todo_wine" but succeeds
Report errors: user32:msg prints too much data (35221 bytes)
=== debian10 (32 bit Chinese:China report) ===
user32: clipboard.c:717: Test failed: 1: gle 5 clipboard.c:719: Test failed: 1: gle 1418 clipboard.c:746: Test failed: 1: count 4 clipboard.c:749: Test failed: 1: gle 1418 clipboard.c:760: Test failed: 1: gle 5 clipboard.c:765: Test failed: 1.0: got 0000 instead of 0007 clipboard.c:805: Test failed: 1: gle 1418 clipboard.c:815: Test failed: 1: count 4 clipboard.c:818: Test failed: 1: gle 1418 clipboard.c:833: Test failed: 1: gle 5 clipboard.c:838: Test failed: 1.0: got 0000 instead of 0007 clipboard.c:868: Test failed: 1: gle 1418 clipboard.c:717: Test failed: 2: gle 5 clipboard.c:719: Test failed: 2: gle 1418 clipboard.c:746: Test failed: 2: count 4 clipboard.c:749: Test failed: 2: gle 1418 clipboard.c:760: Test failed: 2: gle 5 clipboard.c:765: Test failed: 2.0: got 0000 instead of 000d clipboard.c:805: Test failed: 2: gle 1418 clipboard.c:815: Test failed: 2: count 4 clipboard.c:818: Test failed: 2: gle 1418 clipboard.c:833: Test failed: 2: gle 5 clipboard.c:838: Test failed: 2.0: got 0000 instead of 000d clipboard.c:868: Test failed: 2: gle 1418 clipboard.c:717: Test failed: 3: gle 5 clipboard.c:719: Test failed: 3: gle 1418 clipboard.c:746: Test failed: 3: count 5 clipboard.c:749: Test failed: 3: gle 1418 clipboard.c:755: Test failed: 3: 0003 not available clipboard.c:757: Test failed: 3: count 5 instead of 2 clipboard.c:760: Test failed: 3: gle 5 clipboard.c:765: Test failed: 3.0: got 0000 instead of 000e clipboard.c:805: Test failed: 3: gle 1418 clipboard.c:815: Test failed: 3: count 5 clipboard.c:818: Test failed: 3: gle 1418 clipboard.c:826: Test failed: 3: 0003 not available clipboard.c:828: Test failed: 3: count 5 instead of 2 clipboard.c:833: Test failed: 3: gle 5 clipboard.c:838: Test failed: 3.0: got 0000 instead of 000e clipboard.c:868: Test failed: 3: gle 1418 clipboard.c:717: Test failed: 4: gle 5 clipboard.c:719: Test failed: 4: gle 1418 clipboard.c:746: Test failed: 4: count 6 clipboard.c:749: Test failed: 4: gle 1418 clipboard.c:757: Test failed: 4: count 6 instead of 2 clipboard.c:760: Test failed: 4: gle 5 clipboard.c:765: Test failed: 4.0: got 0000 instead of 0003 clipboard.c:805: Test failed: 4: gle 1418 clipboard.c:815: Test failed: 4: count 6 clipboard.c:818: Test failed: 4: gle 1418 clipboard.c:828: Test failed: 4: count 6 instead of 2 clipboard.c:833: Test failed: 4: gle 5 clipboard.c:838: Test failed: 4.0: got 0000 instead of 0003 clipboard.c:868: Test failed: 4: gle 1418 clipboard.c:717: Test failed: 5: gle 5 clipboard.c:719: Test failed: 5: gle 1418 clipboard.c:746: Test failed: 5: count 7 clipboard.c:749: Test failed: 5: gle 1418 clipboard.c:755: Test failed: 5: 0008 not available clipboard.c:755: Test failed: 5: 0011 not available clipboard.c:757: Test failed: 5: count 7 instead of 3 clipboard.c:760: Test failed: 5: gle 5 clipboard.c:765: Test failed: 5.0: got 0000 instead of 0002 clipboard.c:805: Test failed: 5: gle 1418 clipboard.c:815: Test failed: 5: count 7 clipboard.c:818: Test failed: 5: gle 1418 clipboard.c:826: Test failed: 5: 0008 not available clipboard.c:826: Test failed: 5: 0011 not available clipboard.c:828: Test failed: 5: count 7 instead of 3 clipboard.c:833: Test failed: 5: gle 5 clipboard.c:838: Test failed: 5.0: got 0000 instead of 0002 clipboard.c:868: Test failed: 5: gle 1418 clipboard.c:717: Test failed: 6: gle 5 clipboard.c:719: Test failed: 6: gle 1418 clipboard.c:746: Test failed: 6: count 8 clipboard.c:749: Test failed: 6: gle 1418 clipboard.c:755: Test failed: 6: 0011 not available clipboard.c:757: Test failed: 6: count 8 instead of 3 clipboard.c:760: Test failed: 6: gle 5 clipboard.c:765: Test failed: 6.0: got 0000 instead of 0008 clipboard.c:805: Test failed: 6: gle 1418 clipboard.c:815: Test failed: 6: count 8 clipboard.c:818: Test failed: 6: gle 1418 clipboard.c:826: Test failed: 6: 0011 not available clipboard.c:828: Test failed: 6: count 8 instead of 3 clipboard.c:833: Test failed: 6: gle 5 clipboard.c:838: Test failed: 6.0: got 0000 instead of 0008 clipboard.c:868: Test failed: 6: gle 1418 clipboard.c:717: Test failed: 7: gle 5 clipboard.c:719: Test failed: 7: gle 1418 clipboard.c:746: Test failed: 7: count 9 clipboard.c:749: Test failed: 7: gle 1418 clipboard.c:757: Test failed: 7: count 9 instead of 3 clipboard.c:760: Test failed: 7: gle 5 clipboard.c:765: Test failed: 7.0: got 0000 instead of 0011 clipboard.c:805: Test failed: 7: gle 1418 clipboard.c:815: Test failed: 7: count 9 clipboard.c:818: Test failed: 7: gle 1418 clipboard.c:828: Test failed: 7: count 9 instead of 3 clipboard.c:833: Test failed: 7: gle 5 clipboard.c:838: Test failed: 7.0: got 0000 instead of 0011 clipboard.c:868: Test failed: 7: gle 1418 clipboard.c:874: Test failed: gle 5 clipboard.c:876: Test failed: gle 1418 clipboard.c:878: Test failed: gle 1418 msg.c:5145: Test succeeded inside todo block: ShowWindow(SW_SHOWMINIMIZED):overlapped: marked "todo_wine" but succeeds
Report errors: user32:msg prints too much data (35221 bytes)
=== debian10 (32 bit WoW report) ===
user32: msg.c:5145: Test succeeded inside todo block: ShowWindow(SW_SHOWMINIMIZED):overlapped: marked "todo_wine" but succeeds
Report errors: user32:msg prints too much data (35221 bytes)
=== debian10 (64 bit WoW report) ===
user32: msg.c:5145: Test succeeded inside todo block: ShowWindow(SW_SHOWMINIMIZED):overlapped: marked "todo_wine" but succeeds
Report errors: user32:msg prints too much data (35221 bytes)
From: Derek Lesho dereklesho52@Gmail.com
--- dlls/user32/rawinput.c | 2 +- server/queue.c | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/dlls/user32/rawinput.c b/dlls/user32/rawinput.c index a8409666f17..66dc9b55f12 100644 --- a/dlls/user32/rawinput.c +++ b/dlls/user32/rawinput.c @@ -282,7 +282,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH RegisterRawInputDevices(RAWINPUTDEVICE *devices, U TRACE("device %u: page %#x, usage %#x, flags %#x, target %p.\n", i, devices[i].usUsagePage, devices[i].usUsage, devices[i].dwFlags, devices[i].hwndTarget); - if (devices[i].dwFlags & ~RIDEV_REMOVE) + if (devices[i].dwFlags & ~(RIDEV_REMOVE|RIDEV_NOLEGACY)) FIXME("Unhandled flags %#x for device %u.\n", devices[i].dwFlags, i);
d[i].usage_page = devices[i].usUsagePage; diff --git a/server/queue.c b/server/queue.c index 5f8b82c864c..31b643d4c96 100644 --- a/server/queue.c +++ b/server/queue.c @@ -372,6 +372,9 @@ static void set_cursor_pos( struct desktop *desktop, int x, int y ) static const struct hw_msg_source source = { IMDT_UNAVAILABLE, IMO_SYSTEM }; struct message *msg;
+ if (current->process->rawinput_mouse && + current->process->rawinput_mouse->flags & RIDEV_NOLEGACY) return; + if (!(msg = alloc_hardware_message( 0, source, get_tick_count() ))) return;
msg->msg = WM_MOUSEMOVE; @@ -1642,6 +1645,7 @@ done: static int queue_mouse_message( struct desktop *desktop, user_handle_t win, const hw_input_t *input, unsigned int origin, struct msg_queue *sender, unsigned int req_flags ) { + const struct rawinput_device *device; struct hardware_msg_data *msg_data; struct raw_input_message raw_msg; struct message *msg; @@ -1715,6 +1719,8 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons
if (req_flags & SEND_HWMSG_ONLY_RAW) return 0; + if ((device = current->process->rawinput_mouse) && (device->flags & RIDEV_NOLEGACY)) + return 0;
for (i = 0; i < ARRAY_SIZE( messages ); i++) { @@ -1750,6 +1756,7 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c unsigned int origin, struct msg_queue *sender, unsigned int req_flags ) { struct hw_msg_source source = { IMDT_KEYBOARD, origin }; + const struct rawinput_device *device; struct hardware_msg_data *msg_data; struct raw_input_message raw_msg; struct message *msg; @@ -1845,6 +1852,8 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c
if (req_flags & SEND_HWMSG_ONLY_RAW) return 0; + if ((device = current->process->rawinput_kbd) && (device->flags & RIDEV_NOLEGACY)) + return 0;
if (!(msg = alloc_hardware_message( input->kbd.info, source, time ))) return 0; msg_data = msg->data;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=56437
Your paranoid android.
=== debian10 (32 bit report) ===
user32: msg.c:5145: Test succeeded inside todo block: ShowWindow(SW_SHOWMINIMIZED):overlapped: marked "todo_wine" but succeeds msg.c:8713: Test failed: WaitForSingleObject failed 102 msg.c:8719: Test failed: destroy child on thread exit: 0: the msg 0x0082 was expected, but got msg 0x000f instead msg.c:8719: Test failed: destroy child on thread exit: 1: the msg 0x000f was expected, but got msg 0x0014 instead msg.c:8719: Test failed: destroy child on thread exit: 2: the msg sequence is not complete: expected 0014 - actual 0000
Report errors: user32:msg prints too much data (35954 bytes)
=== debian10 (32 bit Chinese:China report) ===
user32: msg.c:5145: Test succeeded inside todo block: ShowWindow(SW_SHOWMINIMIZED):overlapped: marked "todo_wine" but succeeds
Report errors: user32:msg prints too much data (35222 bytes)
=== debian10 (32 bit WoW report) ===
user32: menu.c:2354: Test failed: test 25 msg.c:5145: Test succeeded inside todo block: ShowWindow(SW_SHOWMINIMIZED):overlapped: marked "todo_wine" but succeeds
Report errors: user32:msg prints too much data (35221 bytes)
=== debian10 (64 bit WoW report) ===
user32: msg.c:5145: Test succeeded inside todo block: ShowWindow(SW_SHOWMINIMIZED):overlapped: marked "todo_wine" but succeeds
Report errors: user32:msg prints too much data (35221 bytes)
--- dlls/user32/rawinput.c | 9 ++++++++- dlls/user32/tests/rawinput.c | 2 -- server/queue.c | 4 ++++ 3 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/dlls/user32/rawinput.c b/dlls/user32/rawinput.c index 66dc9b55f12..db2f7c0cc90 100644 --- a/dlls/user32/rawinput.c +++ b/dlls/user32/rawinput.c @@ -267,6 +267,13 @@ BOOL WINAPI DECLSPEC_HOTPATCH RegisterRawInputDevices(RAWINPUTDEVICE *devices, U
for (i = 0; i < device_count; ++i) { + if ((devices[i].dwFlags & RIDEV_INPUTSINK) && + (devices[i].hwndTarget == NULL)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + if ((devices[i].dwFlags & RIDEV_REMOVE) && (devices[i].hwndTarget != NULL)) { @@ -282,7 +289,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH RegisterRawInputDevices(RAWINPUTDEVICE *devices, U TRACE("device %u: page %#x, usage %#x, flags %#x, target %p.\n", i, devices[i].usUsagePage, devices[i].usUsage, devices[i].dwFlags, devices[i].hwndTarget); - if (devices[i].dwFlags & ~(RIDEV_REMOVE|RIDEV_NOLEGACY)) + if (devices[i].dwFlags & ~(RIDEV_REMOVE|RIDEV_NOLEGACY|RIDEV_INPUTSINK)) FIXME("Unhandled flags %#x for device %u.\n", devices[i].dwFlags, i);
d[i].usage_page = devices[i].usUsagePage; diff --git a/dlls/user32/tests/rawinput.c b/dlls/user32/tests/rawinput.c index f4c8eb6738b..3e459a53940 100644 --- a/dlls/user32/tests/rawinput.c +++ b/dlls/user32/tests/rawinput.c @@ -80,9 +80,7 @@ static void test_RegisterRawInputDevices(void)
SetLastError(0xdeadbeef); res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE)); - todo_wine ok(res == FALSE, "RegisterRawInputDevices failed\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER, "RegisterRawInputDevices returned %08x\n", GetLastError());
raw_devices[0].hwndTarget = hwnd; diff --git a/server/queue.c b/server/queue.c index 31b643d4c96..7ee17e9e8b6 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1624,6 +1624,10 @@ static int queue_raw_input_message( struct process* process, void* user ) return 0; if (!(desktop = get_desktop_obj( process, process->desktop, 0 )) || desktop != raw_msg->desktop) goto done; + if (!(device->flags & RIDEV_INPUTSINK) && + (thread = get_window_thread( device->target )) && + thread->queue->input != desktop->foreground_input) + goto done; if (!(msg = alloc_hardware_message( raw_msg->data.info, raw_msg->source, raw_msg->time ))) goto done;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=56438
Your paranoid android.
=== build (build log) ===
error: patch failed: dlls/user32/rawinput.c:267 Task: Patch failed to apply
=== debian10 (build log) ===
error: patch failed: dlls/user32/rawinput.c:267 Task: Patch failed to apply
=== debian10 (build log) ===
error: patch failed: dlls/user32/rawinput.c:267 Task: Patch failed to apply
Under XInput2 protocol version < 2.1, raw events are not supposed to be sent if pointer grab is active. However slave device events are still received regardless of this specification and Wine implemented a workaround to get raw events during pointer grabs by listening to these slave device events.
By advertising to support XInput2 protocol version >= 2.1, where raw events are sent even during pointer grabs, it is possible to simplify the code by listening to master device events only.
As we are going to listen to keyboard raw input as well this simplifies the task of discarding duplicate keyboard events between slave and master keyboard devices. --- dlls/winex11.drv/event.c | 42 +-------------------------------- dlls/winex11.drv/mouse.c | 49 ++++++++------------------------------- dlls/winex11.drv/x11drv.h | 3 --- 3 files changed, 11 insertions(+), 83 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index f79f40c2323..83160f7affd 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -274,46 +274,6 @@ enum event_merge_action MERGE_IGNORE /* ignore the new event, keep the old one */ };
-/*********************************************************************** - * merge_raw_motion_events - */ -#ifdef HAVE_X11_EXTENSIONS_XINPUT2_H -static enum event_merge_action merge_raw_motion_events( XIRawEvent *prev, XIRawEvent *next ) -{ - int i, j, k; - unsigned char mask; - - if (!prev->valuators.mask_len) return MERGE_HANDLE; - if (!next->valuators.mask_len) return MERGE_HANDLE; - - mask = prev->valuators.mask[0] | next->valuators.mask[0]; - if (mask == next->valuators.mask[0]) /* keep next */ - { - for (i = j = k = 0; i < 8; i++) - { - if (XIMaskIsSet( prev->valuators.mask, i )) - next->valuators.values[j] += prev->valuators.values[k++]; - if (XIMaskIsSet( next->valuators.mask, i )) j++; - } - TRACE( "merging duplicate GenericEvent\n" ); - return MERGE_DISCARD; - } - if (mask == prev->valuators.mask[0]) /* keep prev */ - { - for (i = j = k = 0; i < 8; i++) - { - if (XIMaskIsSet( next->valuators.mask, i )) - prev->valuators.values[j] += next->valuators.values[k++]; - if (XIMaskIsSet( prev->valuators.mask, i )) j++; - } - TRACE( "merging duplicate GenericEvent\n" ); - return MERGE_IGNORE; - } - /* can't merge events with disjoint masks */ - return MERGE_HANDLE; -} -#endif - /*********************************************************************** * merge_events * @@ -365,7 +325,7 @@ static enum event_merge_action merge_events( XEvent *prev, XEvent *next ) if (next->xcookie.extension != xinput2_opcode) break; if (next->xcookie.evtype != XI_RawMotion) break; if (x11drv_thread_data()->warp_serial) break; - return merge_raw_motion_events( prev->xcookie.data, next->xcookie.data ); + return MERGE_HANDLE; #endif } break; diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index cedb488614d..3783ad6dc21 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -304,12 +304,16 @@ static void enable_xinput2(void)
if (data->xi2_state == xi_unknown) { - int major = 2, minor = 0; - if (!pXIQueryVersion( data->display, &major, &minor )) data->xi2_state = xi_disabled; + static int major = 2, minor = 1; + if (!pXIQueryVersion( data->display, &major, &minor ) && major == 2 && minor > 0) + { + TRACE( "XInput2 v%d.%d available\n", major, minor ); + data->xi2_state = xi_disabled; + } else { data->xi2_state = xi_unavailable; - WARN( "X Input 2 not available\n" ); + WARN( "XInput v2.1 not available\n" ); } } if (data->xi2_state == xi_unavailable) return; @@ -317,7 +321,7 @@ static void enable_xinput2(void)
mask.mask = mask_bits; mask.mask_len = sizeof(mask_bits); - mask.deviceid = XIAllDevices; + mask.deviceid = XIAllMasterDevices; memset( mask_bits, 0, sizeof(mask_bits) ); XISetMask( mask_bits, XI_DeviceChanged ); XISetMask( mask_bits, XI_RawMotion ); @@ -329,16 +333,6 @@ static void enable_xinput2(void) update_relative_valuators( pointer_info->classes, pointer_info->num_classes ); pXIFreeDeviceInfo( pointer_info );
- /* This device info list is only used to find the initial current slave if - * no XI_DeviceChanged events happened. If any hierarchy change occurred that - * might be relevant here (eg. user switching mice after (un)plugging), a - * XI_DeviceChanged event will point us to the right slave. So this list is - * safe to be obtained statically at enable_xinput2() time. - */ - if (data->xi2_devices) pXIFreeDeviceInfo( data->xi2_devices ); - data->xi2_devices = pXIQueryDevice( data->display, XIAllDevices, &data->xi2_device_count ); - data->xi2_current_slave = 0; - data->xi2_state = xi_enabled; #endif } @@ -359,15 +353,12 @@ static void disable_xinput2(void)
mask.mask = NULL; mask.mask_len = 0; - mask.deviceid = XIAllDevices; + mask.deviceid = XIAllMasterDevices;
pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 ); - pXIFreeDeviceInfo( data->xi2_devices ); data->x_rel_valuator.number = -1; data->y_rel_valuator.number = -1; - data->xi2_devices = NULL; data->xi2_core_pointer = 0; - data->xi2_current_slave = 0; #endif }
@@ -1763,7 +1754,6 @@ static BOOL X11DRV_DeviceChanged( XGenericEventCookie *xev ) if (event->reason != XISlaveSwitch) return FALSE;
update_relative_valuators( event->classes, event->num_classes ); - data->xi2_current_slave = event->sourceid; return TRUE; }
@@ -1784,26 +1774,7 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev ) if (thread_data->x_rel_valuator.number < 0 || thread_data->y_rel_valuator.number < 0) return FALSE; if (!event->valuators.mask_len) return FALSE; if (thread_data->xi2_state != xi_enabled) return FALSE; - - /* If there is no slave currently detected, no previous motion nor device - * change events were received. Look it up now on the device list in this - * case. - */ - if (!thread_data->xi2_current_slave) - { - XIDeviceInfo *devices = thread_data->xi2_devices; - - for (i = 0; i < thread_data->xi2_device_count; i++) - { - if (devices[i].use != XISlavePointer) continue; - if (devices[i].deviceid != event->deviceid) continue; - if (devices[i].attachment != thread_data->xi2_core_pointer) continue; - thread_data->xi2_current_slave = event->deviceid; - break; - } - } - - if (event->deviceid != thread_data->xi2_current_slave) return FALSE; + if (event->deviceid != thread_data->xi2_core_pointer) return FALSE;
x_rel = &thread_data->x_rel_valuator; y_rel = &thread_data->y_rel_valuator; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index d4e476facb2..7dd8c158617 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -336,12 +336,9 @@ struct x11drv_thread_data DWORD clip_reset; /* time when clipping was last reset */ HKL kbd_layout; /* active keyboard layout */ enum { xi_unavailable = -1, xi_unknown, xi_disabled, xi_enabled } xi2_state; /* XInput2 state */ - void *xi2_devices; /* list of XInput2 devices (valid when state is enabled) */ - int xi2_device_count; struct x11drv_valuator_data x_rel_valuator; struct x11drv_valuator_data y_rel_valuator; int xi2_core_pointer; /* XInput2 core pointer id */ - int xi2_current_slave; /* Current slave driving the Core pointer */ };
extern struct x11drv_thread_data *x11drv_init_thread_data(void) DECLSPEC_HIDDEN;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=56439
Your paranoid android.
=== debian10 (build log) ===
error: patch failed: dlls/user32/rawinput.c:267 Task: Patch failed to apply
=== debian10 (build log) ===
error: patch failed: dlls/user32/rawinput.c:267 Task: Patch failed to apply
We still need to send "normal" input from the clipping window thread to trigger low-level hooks callbacks when clipping cursor. This is for instance used in our dinput < 8 implementation.
Merging them with the raw input from the desktop thread creates some confusion on the server side between the absolute position from the MotionNotify events and the relative movement from the RawMotion events. --- dlls/winex11.drv/mouse.c | 76 +++++++++++++++++++++++++++------- dlls/winex11.drv/x11drv.h | 3 ++ dlls/winex11.drv/x11drv_main.c | 4 ++ 3 files changed, 69 insertions(+), 14 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 3783ad6dc21..1074586f628 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -289,9 +289,9 @@ static void update_relative_valuators(XIAnyClassInfo **valuators, int n_valuator
/*********************************************************************** - * enable_xinput2 + * X11DRV_XInput2_Enable */ -static void enable_xinput2(void) +void X11DRV_XInput2_Enable(void) { #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H struct x11drv_thread_data *data = x11drv_thread_data(); @@ -323,9 +323,21 @@ static void enable_xinput2(void) mask.mask_len = sizeof(mask_bits); mask.deviceid = XIAllMasterDevices; memset( mask_bits, 0, sizeof(mask_bits) ); + XISetMask( mask_bits, XI_DeviceChanged ); XISetMask( mask_bits, XI_RawMotion ); - XISetMask( mask_bits, XI_ButtonPress ); + + if (GetWindowThreadProcessId( GetDesktopWindow(), NULL ) == GetCurrentThreadId()) + { + XISetMask( mask_bits, XI_RawButtonPress ); + XISetMask( mask_bits, XI_RawButtonRelease ); + data->xi2_rawinput_only = TRUE; + } + else + { + XISetMask( mask_bits, XI_ButtonPress ); + data->xi2_rawinput_only = FALSE; + }
pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 );
@@ -338,9 +350,9 @@ static void enable_xinput2(void) }
/*********************************************************************** - * disable_xinput2 + * X11DRV_XInput2_Disable */ -static void disable_xinput2(void) +void X11DRV_XInput2_Disable(void) { #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H struct x11drv_thread_data *data = x11drv_thread_data(); @@ -358,6 +370,7 @@ static void disable_xinput2(void) pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 ); data->x_rel_valuator.number = -1; data->y_rel_valuator.number = -1; + data->xi2_core_pointer = 0; #endif } @@ -400,7 +413,7 @@ static BOOL grab_clipping_window( const RECT *clip ) }
/* enable XInput2 unless we are already clipping */ - if (!data->clip_hwnd) enable_xinput2(); + if (!data->clip_hwnd) X11DRV_XInput2_Enable();
if (data->xi2_state != xi_enabled) { @@ -430,7 +443,7 @@ static BOOL grab_clipping_window( const RECT *clip )
if (!clipping_cursor) { - disable_xinput2(); + X11DRV_XInput2_Disable(); DestroyWindow( msg_hwnd ); return FALSE; } @@ -511,7 +524,7 @@ LRESULT clip_cursor_notify( HWND hwnd, HWND new_clip_hwnd ) TRACE( "clip hwnd reset from %p\n", hwnd ); data->clip_hwnd = 0; data->clip_reset = GetTickCount(); - disable_xinput2(); + X11DRV_XInput2_Disable(); DestroyWindow( hwnd ); } else if (hwnd == GetForegroundWindow()) /* request to clip */ @@ -603,7 +616,7 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU } input->u.mi.dx += clip_rect.left; input->u.mi.dy += clip_rect.top; - __wine_send_input( hwnd, input, 0 ); + __wine_send_input( hwnd, input, SEND_HWMSG_SKIP_RAW ); return; }
@@ -664,7 +677,7 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU
input->u.mi.dx = pt.x; input->u.mi.dy = pt.y; - __wine_send_input( hwnd, input, 0 ); + __wine_send_input( hwnd, input, SEND_HWMSG_SKIP_RAW ); }
#ifdef SONAME_LIBXCURSOR @@ -1610,7 +1623,7 @@ void move_resize_window( HWND hwnd, int dir ) input.u.mi.dwFlags = button_up_flags[button - 1] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; input.u.mi.time = GetTickCount(); input.u.mi.dwExtraInfo = 0; - __wine_send_input( hwnd, &input, 0 ); + __wine_send_input( hwnd, &input, SEND_HWMSG_SKIP_RAW ); }
while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE )) @@ -1779,6 +1792,7 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev ) x_rel = &thread_data->x_rel_valuator; y_rel = &thread_data->y_rel_valuator;
+ input.type = INPUT_MOUSE; input.u.mi.mouseData = 0; input.u.mi.dwFlags = MOUSEEVENTF_MOVE; input.u.mi.time = EVENT_x11_time_to_win32_time( event->time ); @@ -1814,10 +1828,40 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev ) return FALSE; }
- TRACE( "pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, dx, dy ); + if (!thread_data->xi2_rawinput_only) + { + TRACE( "pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, dx, dy ); + __wine_send_input( 0, &input, SEND_HWMSG_SKIP_RAW ); + } + else + { + TRACE( "raw pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, dx, dy ); + __wine_send_input( 0, &input, SEND_HWMSG_BCAST_RAW | SEND_HWMSG_ONLY_RAW ); + } + return TRUE; +} + +/*********************************************************************** + * X11DRV_RawButtonEvent + */ +static BOOL X11DRV_RawButtonEvent( XGenericEventCookie *cookie ) +{ + XIRawEvent *event = cookie->data; + INPUT input; + + input.type = INPUT_MOUSE; + input.u.mi.dx = 0; + input.u.mi.dy = 0; + input.u.mi.mouseData = event->evtype == XI_RawButtonRelease + ? button_up_data[event->detail - 1] + : button_down_data[event->detail - 1]; + input.u.mi.dwFlags = event->evtype == XI_RawButtonRelease + ? button_up_flags[event->detail - 1] + : button_down_flags[event->detail - 1]; + input.u.mi.time = EVENT_x11_time_to_win32_time(event->time); + input.u.mi.dwExtraInfo = 0;
- input.type = INPUT_MOUSE; - __wine_send_input( 0, &input, 0 ); + __wine_send_input( 0, &input, SEND_HWMSG_BCAST_RAW | SEND_HWMSG_ONLY_RAW ); return TRUE; }
@@ -1885,6 +1929,10 @@ BOOL X11DRV_GenericEvent( HWND hwnd, XEvent *xev ) case XI_RawMotion: ret = X11DRV_RawMotion( event ); break; + case XI_RawButtonPress: + case XI_RawButtonRelease: + ret = X11DRV_RawButtonEvent( event ); + break;
default: TRACE( "Unhandled event %#x\n", event->evtype ); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 7dd8c158617..b981beb6660 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -194,6 +194,8 @@ extern BOOL CDECL X11DRV_UnrealizePalette( HPALETTE hpal ) DECLSPEC_HIDDEN;
extern void X11DRV_Xcursor_Init(void) DECLSPEC_HIDDEN; extern void X11DRV_XInput2_Init(void) DECLSPEC_HIDDEN; +extern void X11DRV_XInput2_Enable(void) DECLSPEC_HIDDEN; +extern void X11DRV_XInput2_Disable(void) DECLSPEC_HIDDEN;
extern DWORD copy_image_bits( BITMAPINFO *info, BOOL is_r8g8b8, XImage *image, const struct gdi_image_bits *src_bits, struct gdi_image_bits *dst_bits, @@ -339,6 +341,7 @@ struct x11drv_thread_data struct x11drv_valuator_data x_rel_valuator; struct x11drv_valuator_data y_rel_valuator; int xi2_core_pointer; /* XInput2 core pointer id */ + int xi2_rawinput_only; };
extern struct x11drv_thread_data *x11drv_init_thread_data(void) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 21807af3f18..c3a2ef93db2 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -611,6 +611,8 @@ void CDECL X11DRV_ThreadDetach(void)
if (data) { + if (GetWindowThreadProcessId( GetDesktopWindow(), NULL ) == GetCurrentThreadId()) + X11DRV_XInput2_Disable(); if (data->xim) XCloseIM( data->xim ); if (data->font_set) XFreeFontSet( data->display, data->font_set ); XCloseDisplay( data->display ); @@ -680,6 +682,8 @@ struct x11drv_thread_data *x11drv_init_thread_data(void) TlsSetValue( thread_data_tls_index, data );
if (use_xim) X11DRV_SetupXIM(); + if (GetWindowThreadProcessId( GetDesktopWindow(), NULL ) == GetCurrentThreadId()) + X11DRV_XInput2_Enable();
return data; }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=56440
Your paranoid android.
=== debian10 (build log) ===
error: patch failed: dlls/user32/rawinput.c:267 Task: Patch failed to apply
=== debian10 (build log) ===
error: patch failed: dlls/user32/rawinput.c:267 Task: Patch failed to apply
This adds keyboard input support while Wine windows are in background, so the events can be translated to raw input messages as on Windows, which are required to keep dinput8 keyboard state up-to-date when Alt+Tab is used to switch to a non-Wine window. --- dlls/winex11.drv/keyboard.c | 42 +++++++++++++++++++++++++++++++++- dlls/winex11.drv/mouse.c | 6 +++++ dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/x11drv_main.c | 1 + 4 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 61a2881e60c..f336256c15d 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -33,6 +33,9 @@ #ifdef HAVE_X11_XKBLIB_H #include <X11/XKBlib.h> #endif +#ifdef HAVE_X11_EXTENSIONS_XINPUT2_H +#include <X11/extensions/XInput2.h> +#endif
#include <ctype.h> #include <stdarg.h> @@ -1148,7 +1151,7 @@ static void X11DRV_send_keyboard_input( HWND hwnd, WORD vkey, WORD scan, DWORD f input.u.ki.time = time; input.u.ki.dwExtraInfo = 0;
- __wine_send_input( hwnd, &input, 0 ); + __wine_send_input( hwnd, &input, SEND_HWMSG_SKIP_RAW ); }
@@ -1417,6 +1420,43 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) return TRUE; }
+ +#ifdef HAVE_X11_EXTENSIONS_XINPUT2_H +/*********************************************************************** + * X11DRV_KeyEvent + * + * Handle a raw XInput2 key event for background windows + */ +BOOL X11DRV_RawKeyEvent( XGenericEventCookie *cookie ) +{ + XIRawEvent *event = cookie->data; + DWORD flags; + WORD vkey, scan; + INPUT input; + + vkey = keyc2vkey[event->detail]; + scan = keyc2scan[event->detail]; + + flags = 0; + if ( event->evtype == XI_RawKeyRelease ) flags |= KEYEVENTF_KEYUP; + if ( vkey & 0x100 ) flags |= KEYEVENTF_EXTENDEDKEY; + + TRACE_(key)( "vkey=%04x scan=%04x flags=%04x\n", vkey, scan, flags ); + + input.type = INPUT_KEYBOARD; + input.u.ki.wVk = vkey & 0xff; + input.u.ki.wScan = scan & 0xff; + input.u.ki.dwFlags = flags; + input.u.ki.time = EVENT_x11_time_to_win32_time(event->time); + input.u.ki.dwExtraInfo = 0; + + __wine_send_input( 0, &input, SEND_HWMSG_BCAST_RAW | SEND_HWMSG_ONLY_RAW ); + + return TRUE; +} +#endif + + /********************************************************************** * X11DRV_KEYBOARD_DetectLayout * diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 1074586f628..03397a69e02 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -331,6 +331,8 @@ void X11DRV_XInput2_Enable(void) { XISetMask( mask_bits, XI_RawButtonPress ); XISetMask( mask_bits, XI_RawButtonRelease ); + XISetMask( mask_bits, XI_RawKeyPress ); + XISetMask( mask_bits, XI_RawKeyRelease ); data->xi2_rawinput_only = TRUE; } else @@ -1933,6 +1935,10 @@ BOOL X11DRV_GenericEvent( HWND hwnd, XEvent *xev ) case XI_RawButtonRelease: ret = X11DRV_RawButtonEvent( event ); break; + case XI_RawKeyPress: + case XI_RawKeyRelease: + ret = X11DRV_RawKeyEvent( event ); + break;
default: TRACE( "Unhandled event %#x\n", event->evtype ); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index b981beb6660..32fbfd38d2d 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -514,6 +514,7 @@ extern BOOL X11DRV_ButtonRelease( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_MotionNotify( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; +extern BOOL X11DRV_RawKeyEvent( XGenericEventCookie *cookie ) DECLSPEC_HIDDEN; extern BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_DestroyNotify( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_SelectionRequest( HWND hWnd, XEvent *event ) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index c3a2ef93db2..4f615611afe 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -532,6 +532,7 @@ static void init_visuals( Display *display, int screen ) default_visual.visualid, default_visual.class, argb_visual.visualid ); }
+ /*********************************************************************** * X11DRV process initialisation routine */
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=56441
Your paranoid android.
=== debian10 (build log) ===
error: patch failed: dlls/user32/rawinput.c:267 Task: Patch failed to apply
=== debian10 (build log) ===
error: patch failed: dlls/user32/rawinput.c:267 Task: Patch failed to apply
On 9/11/19 9:30 AM, Rémi Bernon wrote:
Hi all,
I'm sending out this as follow-up of my previous e-mail [1] about dinput and background input processing, hopefully to get some comments and to discuss about the idea and the implementation.
The patch series also depends on some additional dinput8 patches I already sent to the mailing list, to implement dinput8 devices on top of raw input, but I think they can be independent.
There's a few points I believe that may be of concern, the main one being a privacy issue. Allowing Wine applications to listen to input while in background would allow keyloggers to work in Wine. One could argue though that this implementation does not use any priviledged method for that, and that Xlib allows any application to listen to raw input events in background. I guess there may be some ways to get an exclusive device access, to prevent other apps to get the events but I didn't investigate this part.
Also, this is currently only used for Wine dinput8 implementation, so we could also add some internal mechanism to enforce only Wine dinput8 to be able to access these events.
[1] https://www.winehq.org/pipermail/wine-devel/2019-August/149941.html
-- Rémi Bernon rbernon@codeweavers.com
Derek Lesho (1): user32: Add support for RIDEV_NOLEGACY flag.
Rémi Bernon (7): server: Add send_hardware_message flags to hint raw input translation. user32: Add __wine_send_input flags to hint raw input translation. server: Implement raw mouse/keyboard input message broadcast. user32: Add support for RIDEV_INPUTSINK flag. winex11.drv: Listen to XInput2 master devices only. winex11.drv: Listen to RawMotion and RawButton* events in the desktop thread. winex11.drv: Listen to RawKey* events in the desktop thread.
dlls/user32/input.c | 4 +- dlls/user32/rawinput.c | 9 ++- dlls/user32/tests/rawinput.c | 2 - dlls/user32/user32.spec | 2 +- dlls/wineandroid.drv/keyboard.c | 2 +- dlls/wineandroid.drv/window.c | 4 +- dlls/winemac.drv/ime.c | 4 +- dlls/winemac.drv/keyboard.c | 2 +- dlls/winemac.drv/mouse.c | 2 +- dlls/winex11.drv/event.c | 42 +--------- dlls/winex11.drv/keyboard.c | 42 +++++++++- dlls/winex11.drv/mouse.c | 131 +++++++++++++++++++------------- dlls/winex11.drv/x11drv.h | 7 +- dlls/winex11.drv/x11drv_main.c | 5 ++ include/winuser.h | 2 +- server/protocol.def | 3 + server/queue.c | 106 +++++++++++++++++++++----- 17 files changed, 236 insertions(+), 133 deletions(-)
-- 2.23.0
Nice work, this patch-set looks much tidier than mine. I have a few concerns:
1) This patch-set breaks XWayland support, you can see how I worked around this in commit 6709734 on my rawinput tree on github. https://github.com/Guy1524/wine/commit/670973417d576dec16c610f2fde25350a9638dca
2) Without support for absolute raw-input, there's a regression in OSU on touch tablets. To fix this we need to add support for absolute raw-input. openglfreak on the VKx discord already did a bunch of testing for how this behaves on windows, and I've incorporated it into commit 2fade59 of my rawinput tree on github.
3) Without DeviceChanged being used, there may be some weird interactions with the ckb-next driver for corsair mice. (I am not able to reproduce this, you'll probably want https://github.com/sur3 to test this patch-set)
On 9/11/19 5:56 PM, Derek Lesho wrote:
On 9/11/19 9:30 AM, Rémi Bernon wrote:
Hi all,
I'm sending out this as follow-up of my previous e-mail [1] about dinput and background input processing, hopefully to get some comments and to discuss about the idea and the implementation.
The patch series also depends on some additional dinput8 patches I already sent to the mailing list, to implement dinput8 devices on top of raw input, but I think they can be independent.
There's a few points I believe that may be of concern, the main one being a privacy issue. Allowing Wine applications to listen to input while in background would allow keyloggers to work in Wine. One could argue though that this implementation does not use any priviledged method for that, and that Xlib allows any application to listen to raw input events in background. I guess there may be some ways to get an exclusive device access, to prevent other apps to get the events but I didn't investigate this part.
Also, this is currently only used for Wine dinput8 implementation, so we could also add some internal mechanism to enforce only Wine dinput8 to be able to access these events.
[1] https://www.winehq.org/pipermail/wine-devel/2019-August/149941.html
-- Rémi Bernon rbernon@codeweavers.com
Derek Lesho (1): user32: Add support for RIDEV_NOLEGACY flag.
Rémi Bernon (7): server: Add send_hardware_message flags to hint raw input translation. user32: Add __wine_send_input flags to hint raw input translation. server: Implement raw mouse/keyboard input message broadcast. user32: Add support for RIDEV_INPUTSINK flag. winex11.drv: Listen to XInput2 master devices only. winex11.drv: Listen to RawMotion and RawButton* events in the desktop thread. winex11.drv: Listen to RawKey* events in the desktop thread.
dlls/user32/input.c | 4 +- dlls/user32/rawinput.c | 9 ++- dlls/user32/tests/rawinput.c | 2 - dlls/user32/user32.spec | 2 +- dlls/wineandroid.drv/keyboard.c | 2 +- dlls/wineandroid.drv/window.c | 4 +- dlls/winemac.drv/ime.c | 4 +- dlls/winemac.drv/keyboard.c | 2 +- dlls/winemac.drv/mouse.c | 2 +- dlls/winex11.drv/event.c | 42 +--------- dlls/winex11.drv/keyboard.c | 42 +++++++++- dlls/winex11.drv/mouse.c | 131 +++++++++++++++++++------------- dlls/winex11.drv/x11drv.h | 7 +- dlls/winex11.drv/x11drv_main.c | 5 ++ include/winuser.h | 2 +- server/protocol.def | 3 + server/queue.c | 106 +++++++++++++++++++++----- 17 files changed, 236 insertions(+), 133 deletions(-)
-- 2.23.0
Nice work, this patch-set looks much tidier than mine. I have a few concerns:
- This patch-set breaks XWayland support, you can see how I worked
around this in commit 6709734 on my rawinput tree on github. https://github.com/Guy1524/wine/commit/670973417d576dec16c610f2fde25350a9638dca
I think this could go in a separate "absolute raw input support" patch somehow - although maybe environment-specific hacks aren't very welcome - and it doesn't necessarily require these patch series either.
- Without support for absolute raw-input, there's a regression in OSU
on touch tablets. To fix this we need to add support for absolute raw-input. openglfreak on the VKx discord already did a bunch of testing for how this behaves on windows, and I've incorporated it into commit 2fade59 of my rawinput tree on github.
I think that unless you add absolute RawMotion valuators support the events from the tablets will be discarded, and the input will come from the MotionNotify event codepath which should be unmodified. As far as I can tell, osu! is still working fine with these patches.
Absolute RawMotion events could be useful, but it's not currently implemented and I think it can be implemented separately.
- Without DeviceChanged being used, there may be some weird
interactions with the ckb-next driver for corsair mice. (I am not able to reproduce this, you'll probably want https://github.com/sur3 to test this patch-set)
Yes, I went into issues when dropping these as well, I believe they are still subscribed to, in these patches, or they should be.