This is a rebase/resend of:
https://www.winehq.org/pipermail/wine-devel/2019-September/151347.html
It should fix several issues related to mouse cursor position sometimes jumping around in some games as well as improving the performance with high polling rate mouses by avoiding a rountrip to the server in the dinput8 implementation.
The first patches are in common with the HID rawinput implementation patches that have been previously sent here:
https://www.winehq.org/pipermail/wine-devel/2019-October/152735.html
Rémi Bernon (9): server: Add send_hardware_message flags for rawinput translation. server: Implement rawinput hardware message broadcast. user32: Add __wine_send_input flags to hint raw input translation. user32: Add support for RIDEV_INPUTSINK flag in RegisterRawInputDevices. user32: Implement GetRegisteredRawInputDevices. dinput8: Add support for dinput devices that use raw input interface. dinput8: Use raw input interface for dinput8 mouse device. winex11.drv: Advertise XInput2 version 2.1 support. winex11.drv: Listen to RawMotion and RawButton* events in the desktop thread.
dlls/dinput/device_private.h | 3 + dlls/dinput/dinput_main.c | 84 +++++++++++++++++++- dlls/dinput/mouse.c | 117 ++++++++++++++++++++++++++- dlls/dinput8/tests/device.c | 23 +----- dlls/user32/input.c | 4 +- dlls/user32/rawinput.c | 64 ++++++++++++++- 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 | 10 ++- dlls/winex11.drv/keyboard.c | 2 +- dlls/winex11.drv/mouse.c | 128 +++++++++++++++++------------- dlls/winex11.drv/x11drv.h | 6 +- dlls/winex11.drv/x11drv_main.c | 4 + include/winuser.h | 2 +- server/protocol.def | 9 +++ server/queue.c | 135 ++++++++++++++++++++++++++------ 21 files changed, 489 insertions(+), 120 deletions(-)
-- 2.24.0.rc2
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- 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 6af0ae0cff8..ab3af90545b 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 b5e17be18fb..418cd7e524e 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 );
Hello Rémi,
On 11/4/19 6:17 AM, Rémi Bernon wrote:
Signed-off-by: Rémi Bernon rbernon@codeweavers.com
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 6af0ae0cff8..ab3af90545b 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 */
In lieu of other feedback, I still feel like this is kind of awkward.
Supposedly (though in practice I still don't see why this needs to be true) raw input and window-message input should be sending two different sets of data. If that's the case, it feels more sensible to me to actually split them into two separate calls to __wine_send_input() in every case, not just when we actually have native raw input support. That is, "no native raw input support" is where we have to work around an insufficiency in the lower level, and we should design the code around the "correct" case.
I don't know what the actual performance penalty of making two separate wineserver calls is, or whether it's worth caring about for systems that don't support the Xinput extension.
If nothing else, I'd suggest to change these flags to something more like SEND_HWMSG_WINDOW and SEND_HWMSG_RAW, and then use the combination of both in the cases where we have only one source of input.
ἔρρωσο, Zeb
diff --git a/server/queue.c b/server/queue.c index b5e17be18fb..418cd7e524e 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 )
{ const struct rawinput_device *device; struct hardware_msg_data *msg_data;unsigned int origin, struct msg_queue *sender, unsigned int req_flags )
@@ -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 )
{ struct hw_msg_source source = { IMDT_KEYBOARD, origin }; const struct rawinput_device *device;unsigned int origin, struct msg_queue *sender, unsigned int req_flags )
@@ -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 );
On 11/4/19 9:43 PM, Zebediah Figura wrote:
Hello Rémi,
On 11/4/19 6:17 AM, Rémi Bernon wrote:
Signed-off-by: Rémi Bernon rbernon@codeweavers.com
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 6af0ae0cff8..ab3af90545b 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 */
In lieu of other feedback, I still feel like this is kind of awkward.
Supposedly (though in practice I still don't see why this needs to be true) raw input and window-message input should be sending two different sets of data.
From my understanding, raw input could be sufficient on its own for all input events, and wine could implement the translation to filtered and accelerated input from it, providing the raw values to the subsystems which use it directly - such as rawinput and dinput on top of it. However this is a huge amount of work.
In the meantime we have to deal with the native interfaces that provide the data, and I don't think they can be reconciliated safely as a uniform source.
The transformed input in X11 comes in absolute position, is already filtered by window, but may not be sent if the cursor is confined and may be the result of some X11 requests, without any user action. The raw input comes from XInput2 in relative or absolute mode depending on many things, but is guaranteed to always come from the devices. And ofc there's no ordering guarantee between the two.
If that's the case, it feels more sensible to me to
actually split them into two separate calls to __wine_send_input() in every case, not just when we actually have native raw input support. That is, "no native raw input support" is where we have to work around an insufficiency in the lower level, and we should design the code around the "correct" case.
I don't know what the actual performance penalty of making two separate wineserver calls is, or whether it's worth caring about for systems that don't support the Xinput extension.
If nothing else, I'd suggest to change these flags to something more like SEND_HWMSG_WINDOW and SEND_HWMSG_RAW, and then use the combination of both in the cases where we have only one source of input.
I'm fine with that. I was mostly trying to minimize the changes for the other drivers and the "non raw input support" case.
ἔρρωσο, Zeb--
Rémi Bernon rbernon@codeweavers.com
On 11/4/19 3:54 PM, Rémi Bernon wrote:
On 11/4/19 9:43 PM, Zebediah Figura wrote:
Hello Rémi,
On 11/4/19 6:17 AM, Rémi Bernon wrote:
Signed-off-by: Rémi Bernon rbernon@codeweavers.com
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 6af0ae0cff8..ab3af90545b 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 */
In lieu of other feedback, I still feel like this is kind of awkward.
Supposedly (though in practice I still don't see why this needs to be true) raw input and window-message input should be sending two different sets of data.
From my understanding, raw input could be sufficient on its own for all input events, and wine could implement the translation to filtered and accelerated input from it, providing the raw values to the subsystems which use it directly - such as rawinput and dinput on top of it. However this is a huge amount of work.
Well, maybe? It's not immediately clear to me that the scope really is that big.
Setting aside for a moment the problem of whether or not to ignore transformations done by the window manager, how much work would it actually be to do? We're already passing mostly or even entirely correct values in WM_INPUT messages, even if we're not sending them at the right times. I'm probably forgetting things, but the only significant thing left that I see is judging which windows to send raw input to. (Does Xinput2 alone give us enough information to accurately determine that?)
I think as long as we're contemplating moving in this direction, we really should get a good idea of what the design would look like (and whether it would indeed work).
In the meantime we have to deal with the native interfaces that provide the data, and I don't think they can be reconciliated safely as a uniform source.
The transformed input in X11 comes in absolute position, is already filtered by window, but may not be sent if the cursor is confined and may be the result of some X11 requests, without any user action. The raw input comes from XInput2 in relative or absolute mode depending on many things, but is guaranteed to always come from the devices. And ofc there's no ordering guarantee between the two.
I can understand that when raw and WM input is sent differs. I'm still less than sanguine about why that needs to be a concern for us with respect to how we send it, i.e. why we even need to take raw input from Xinput2 in the first place. Notably, I don't think it's ever been fully established why bug 45882 is really a bug and worth fixing—I mean, sure, it mirrors Windows behaviour and is thus more correct in some sense, but:
(a) why would this cause problems for the applications mentioned, if indeed it does?
(b) do similar native X11 applications also bypass transformations that the WM performs, and, if not, are we going to cause a confusing difference in behaviour by doing so ourselves? Put more simply, if the user has e.g. a constant acceleration applied to their mouse at the WM level, don't they expect managed windows to respect that? Ultimately "behave like the application on Windows" and "behave like other applications on X11" are potentially acting at cross purposes here, and I'm not claiming that either one is more salient, but I want to make sure this is at least considered before being thrown away.
If that's the case, it feels more sensible to me to actually split them into two separate calls to __wine_send_input() in every case, not just when we actually have native raw input support. That is, "no native raw input support" is where we have to work around an insufficiency in the lower level, and we should design the code around the "correct" case.
I don't know what the actual performance penalty of making two separate wineserver calls is, or whether it's worth caring about for systems that don't support the Xinput extension.
If nothing else, I'd suggest to change these flags to something more like SEND_HWMSG_WINDOW and SEND_HWMSG_RAW, and then use the combination of both in the cases where we have only one source of input.
I'm fine with that. I was mostly trying to minimize the changes for the other drivers and the "non raw input support" case.
Understandable. My intuition, though, is to make the interface more like the ideal case—which, as far as I can tell, is "with full raw input support". It's also to make the server agnostic as to the details of how messages are produced—i.e. the server shouldn't care about whether we're producing both messages from one source, or from different sources, and along those lines it makes more sense to have only one way to send that information to the server.
But that's only my intuition as to what Alexandre wants. As always, I invite comments from the upstream maintainer.
ἔρρωσο, Zeb--
Rémi Bernon rbernon@codeweavers.com
On 11/4/19 11:22 PM, Zebediah Figura wrote:
On 11/4/19 3:54 PM, Rémi Bernon wrote:
On 11/4/19 9:43 PM, Zebediah Figura wrote:
Hello Rémi,
On 11/4/19 6:17 AM, Rémi Bernon wrote:
Signed-off-by: Rémi Bernon rbernon@codeweavers.com
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 6af0ae0cff8..ab3af90545b 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 */
In lieu of other feedback, I still feel like this is kind of awkward.
Supposedly (though in practice I still don't see why this needs to be true) raw input and window-message input should be sending two different sets of data.
From my understanding, raw input could be sufficient on its own for all input events, and wine could implement the translation to filtered and accelerated input from it, providing the raw values to the subsystems which use it directly - such as rawinput and dinput on top of it. However this is a huge amount of work.
Well, maybe? It's not immediately clear to me that the scope really is that big.
Setting aside for a moment the problem of whether or not to ignore transformations done by the window manager, how much work would it actually be to do? We're already passing mostly or even entirely correct values in WM_INPUT messages, even if we're not sending them at the right times. I'm probably forgetting things, but the only significant thing left that I see is judging which windows to send raw input to. (Does Xinput2 alone give us enough information to accurately determine that?)
Maybe not huge then, but clearly a larger change than these few patches. We could work towards it and these patches are be a first step, at least by providing a way for drivers to send raw input only.
I think as long as we're contemplating moving in this direction, we really should get a good idea of what the design would look like (and whether it would indeed work).
In the meantime we have to deal with the native interfaces that provide the data, and I don't think they can be reconciliated safely as a uniform source.
The transformed input in X11 comes in absolute position, is already filtered by window, but may not be sent if the cursor is confined and may be the result of some X11 requests, without any user action. The raw input comes from XInput2 in relative or absolute mode depending on many things, but is guaranteed to always come from the devices. And ofc there's no ordering guarantee between the two.
I can understand that when raw and WM input is sent differs. I'm still less than sanguine about why that needs to be a concern for us with respect to how we send it, i.e. why we even need to take raw input from Xinput2 in the first place. Notably, I don't think it's ever been fully established why bug 45882 is really a bug and worth fixing—I mean, sure, it mirrors Windows behaviour and is thus more correct in some sense, but:
(a) why would this cause problems for the applications mentioned, if indeed it does?
(b) do similar native X11 applications also bypass transformations that the WM performs, and, if not, are we going to cause a confusing difference in behaviour by doing so ourselves? Put more simply, if the user has e.g. a constant acceleration applied to their mouse at the WM level, don't they expect managed windows to respect that? Ultimately "behave like the application on Windows" and "behave like other applications on X11" are potentially acting at cross purposes here, and I'm not claiming that either one is more salient, but I want to make sure this is at least considered before being thrown away.
It's not very much about the raw values, and as you could see in these patches I'm still using "transformed" values from XInput2. But more about the reliability of the data source.
Because games are very often messing with cursor confining windows, the basic mouse motion messages are plagued with events caused by warps when confining is enabled -and we try to ignore these- and it also doesn't provide reliable movement information -that still expected by the games- when the cursor is moving against the confining borders.
Wine enables XInput2 when the cursor is confined to address these, and tries to make it coherent with basic messages, but when some games continuously enable/disable cursor confining it then creates another set of issues.
Regarding (b), it clearly would be a problem if Wine implements its own cursor speed and acceleration and generate cursor positions different from the native one.
Applications can use raw values natively and I think native games already do for the same reason as on Windows -to have their own internal mouse speed for example- and libraries like SDL use these events to provide relative mouse mouvement. They would behave the same as on Windows and in general it is only to control a camera rotation speed, not a visible cursor that would feel indeed weird.
If that's the case, it feels more sensible to me to actually split them into two separate calls to __wine_send_input() in every case, not just when we actually have native raw input support. That is, "no native raw input support" is where we have to work around an insufficiency in the lower level, and we should design the code around the "correct" case.
I don't know what the actual performance penalty of making two separate wineserver calls is, or whether it's worth caring about for systems that don't support the Xinput extension.
If nothing else, I'd suggest to change these flags to something more like SEND_HWMSG_WINDOW and SEND_HWMSG_RAW, and then use the combination of both in the cases where we have only one source of input.
I'm fine with that. I was mostly trying to minimize the changes for the other drivers and the "non raw input support" case.
Understandable. My intuition, though, is to make the interface more like the ideal case—which, as far as I can tell, is "with full raw input support". It's also to make the server agnostic as to the details of how messages are produced—i.e. the server shouldn't care about whether we're producing both messages from one source, or from different sources, and along those lines it makes more sense to have only one way to send that information to the server.
But that's only my intuition as to what Alexandre wants. As always, I invite comments from the upstream maintainer.
ἔρρωσο, Zeb--
Rémi Bernon rbernon@codeweavers.com
--
Rémi Bernon rbernon@codeweavers.com
On 11/5/19 2:47 AM, Rémi Bernon wrote:
On 11/4/19 11:22 PM, Zebediah Figura wrote:
On 11/4/19 3:54 PM, Rémi Bernon wrote:
On 11/4/19 9:43 PM, Zebediah Figura wrote:
Hello Rémi,
On 11/4/19 6:17 AM, Rémi Bernon wrote:
Signed-off-by: Rémi Bernon rbernon@codeweavers.com
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 6af0ae0cff8..ab3af90545b 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 */
In lieu of other feedback, I still feel like this is kind of awkward.
Supposedly (though in practice I still don't see why this needs to be true) raw input and window-message input should be sending two different sets of data.
From my understanding, raw input could be sufficient on its own for all input events, and wine could implement the translation to filtered and accelerated input from it, providing the raw values to the subsystems which use it directly - such as rawinput and dinput on top of it. However this is a huge amount of work.
Well, maybe? It's not immediately clear to me that the scope really is that big.
Setting aside for a moment the problem of whether or not to ignore transformations done by the window manager, how much work would it actually be to do? We're already passing mostly or even entirely correct values in WM_INPUT messages, even if we're not sending them at the right times. I'm probably forgetting things, but the only significant thing left that I see is judging which windows to send raw input to. (Does Xinput2 alone give us enough information to accurately determine that?)
Maybe not huge then, but clearly a larger change than these few patches. We could work towards it and these patches are be a first step, at least by providing a way for drivers to send raw input only.
Sure, no objection. Like I said, I'm just mostly concerned with having the end result in mind so that intermediate steps aren't working against that.
I think as long as we're contemplating moving in this direction, we really should get a good idea of what the design would look like (and whether it would indeed work).
In the meantime we have to deal with the native interfaces that provide the data, and I don't think they can be reconciliated safely as a uniform source.
The transformed input in X11 comes in absolute position, is already filtered by window, but may not be sent if the cursor is confined and may be the result of some X11 requests, without any user action. The raw input comes from XInput2 in relative or absolute mode depending on many things, but is guaranteed to always come from the devices. And ofc there's no ordering guarantee between the two.
I can understand that when raw and WM input is sent differs. I'm still less than sanguine about why that needs to be a concern for us with respect to how we send it, i.e. why we even need to take raw input from Xinput2 in the first place. Notably, I don't think it's ever been fully established why bug 45882 is really a bug and worth fixing—I mean, sure, it mirrors Windows behaviour and is thus more correct in some sense, but:
(a) why would this cause problems for the applications mentioned, if indeed it does?
(b) do similar native X11 applications also bypass transformations that the WM performs, and, if not, are we going to cause a confusing difference in behaviour by doing so ourselves? Put more simply, if the user has e.g. a constant acceleration applied to their mouse at the WM level, don't they expect managed windows to respect that? Ultimately "behave like the application on Windows" and "behave like other applications on X11" are potentially acting at cross purposes here, and I'm not claiming that either one is more salient, but I want to make sure this is at least considered before being thrown away.
It's not very much about the raw values, and as you could see in these patches I'm still using "transformed" values from XInput2. But more about the reliability of the data source.
Because games are very often messing with cursor confining windows, the basic mouse motion messages are plagued with events caused by warps when confining is enabled -and we try to ignore these- and it also doesn't provide reliable movement information -that still expected by the games- when the cursor is moving against the confining borders.
Wine enables XInput2 when the cursor is confined to address these, and tries to make it coherent with basic messages, but when some games continuously enable/disable cursor confining it then creates another set of issues.
Okay, thanks for the clarification. That does help significantly.
(I didn't actually read patch 9/9 to notice that you weren't using the raw input patches. I know that Derek Lesho did in his original patch set, and I had bug 45882 in mind. I guess after this bug 45882 is still up in the air, then, but I'm happy enough to leave it that way for now.)
Regarding (b), it clearly would be a problem if Wine implements its own cursor speed and acceleration and generate cursor positions different from the native one.
Applications can use raw values natively and I think native games already do for the same reason as on Windows -to have their own internal mouse speed for example- and libraries like SDL use these events to provide relative mouse mouvement. They would behave the same as on Windows and in general it is only to control a camera rotation speed, not a visible cursor that would feel indeed weird.
Sure, makes sense enough. It also raises the question of how anyone could possibly tell that the acceleration is wrong, aside from just reading the documentation around raw input...
If that's the case, it feels more sensible to me to actually split them into two separate calls to __wine_send_input() in every case, not just when we actually have native raw input support. That is, "no native raw input support" is where we have to work around an insufficiency in the lower level, and we should design the code around the "correct" case.
I don't know what the actual performance penalty of making two separate wineserver calls is, or whether it's worth caring about for systems that don't support the Xinput extension.
If nothing else, I'd suggest to change these flags to something more like SEND_HWMSG_WINDOW and SEND_HWMSG_RAW, and then use the combination of both in the cases where we have only one source of input.
I'm fine with that. I was mostly trying to minimize the changes for the other drivers and the "non raw input support" case.
Understandable. My intuition, though, is to make the interface more like the ideal case—which, as far as I can tell, is "with full raw input support". It's also to make the server agnostic as to the details of how messages are produced—i.e. the server shouldn't care about whether we're producing both messages from one source, or from different sources, and along those lines it makes more sense to have only one way to send that information to the server.
But that's only my intuition as to what Alexandre wants. As always, I invite comments from the upstream maintainer.
ἔρρωσο, Zeb--
Rémi Bernon rbernon@codeweavers.com
--
Rémi Bernon rbernon@codeweavers.com
On the topic of how convey the raw/wnd input origin information.
Currently I use flags passed through __wine_send_input / send_hardware_message. Regardless of their name or meaning, I feel it may not be the rightest way to do it.
Instead, I'm thinking or decoupling the server hw_input_t type flags from the user32 INPUT structure type flags, and use some internal flags instead -that would be used to support HID rawinput as well [1]- to provide the nature of the input (raw, window, both).
Then, as __wine_send_input only exposes the INPUT structure, instead of going through some translation in user32, the graphics drivers could do the send_hardware_message request directly, filling the hw_input_t structure themselves with all the custom information we need, and we could get rid of this internal function.
Would that go more in the right direction, or is it too much of a change to be acceptable?
[1] https://source.winehq.org/patches/data/171760
We now broadcast rawinput messages to all listening processes when a driver sends input with the SEND_HWMSG_BCAST_RAW flag, instead of looking for registered rawdevices in the current process.
For now, only the foreground window thread will receive the rawinput messages.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- server/protocol.def | 1 + server/queue.c | 95 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 75 insertions(+), 21 deletions(-)
diff --git a/server/protocol.def b/server/protocol.def index ab3af90545b..3b5da8dcaed 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 418cd7e524e..73174bd01ea 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1596,12 +1596,64 @@ static int send_hook_ll_message( struct desktop *desktop, struct message *hardwa return 1; }
+struct rawinput_message +{ + struct desktop *desktop; + struct hw_msg_source source; + unsigned int time; + struct hardware_msg_data data; +}; + +static int queue_rawinput_message( struct process* process, void* user ) +{ + const struct rawinput_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 )) || + (raw_msg->desktop && desktop != raw_msg->desktop)) + goto done; + + if (!(thread = get_window_thread( device->target ? device->target : desktop->foreground_input->active )) || + process != thread->process) + goto done; + + if (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; + + 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 rawinput_message raw_msg; struct message *msg; unsigned int i, time, flags; struct hw_msg_source source = { IMDT_MOUSE, origin }; @@ -1651,24 +1703,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_rawinput_message, &raw_msg ); + else + queue_rawinput_message( current->process, &raw_msg ); }
if (req_flags & SEND_HWMSG_ONLY_RAW) @@ -1708,8 +1760,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 rawinput_message raw_msg; struct message *msg; unsigned char vkey = input->kbd.vkey; unsigned int message_code, time; @@ -1781,23 +1833,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_rawinput_message, &raw_msg ); + else + queue_rawinput_message( current->process, &raw_msg ); }
if (req_flags & SEND_HWMSG_ONLY_RAW)
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- 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 15e5c04a41e..23424ed44d2 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -620,7 +620,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; }
@@ -681,7 +681,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 @@ -1627,7 +1627,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 )) @@ -1854,7 +1854,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=59017
Your paranoid android.
=== debian10 (32 bit Chinese:China report) ===
user32: msg.c:8774: Test failed: WaitForSingleObject failed 102 msg.c:8780: Test failed: destroy child on thread exit: 0: the msg 0x0082 was expected, but got msg 0x000f instead msg.c:8780: Test failed: destroy child on thread exit: 1: the msg 0x000f was expected, but got msg 0x0014 instead msg.c:8780: Test failed: destroy child on thread exit: 2: the msg sequence is not complete: expected 0014 - actual 0000
This flag allows applications to receive rawinput messages while in background. They have to specify a target hwnd in which queue to receive them and the messages will carry a RIM_INPUTSINK wparam in this case.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/rawinput.c | 9 ++++++++- dlls/user32/tests/rawinput.c | 2 -- server/queue.c | 8 ++++++-- 3 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/dlls/user32/rawinput.c b/dlls/user32/rawinput.c index 94cf7a9a5d2..182ee1e9eb7 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) + if (devices[i].dwFlags & ~(RIDEV_REMOVE|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 73174bd01ea..767207fa6c3 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1611,6 +1611,7 @@ static int queue_rawinput_message( struct process* process, void* user ) struct desktop *desktop = NULL; struct thread *thread = NULL; struct message *msg; + int wparam = RIM_INPUT;
if (raw_msg->data.rawinput.type == RIM_TYPEMOUSE) device = process->rawinput_mouse; @@ -1629,14 +1630,17 @@ static int queue_rawinput_message( struct process* process, void* user ) goto done;
if (thread->queue->input != desktop->foreground_input) - goto done; + { + if (!(device->flags & RIDEV_INPUTSINK)) goto done; + wparam = RIM_INPUTSINK; + }
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->wparam = wparam; msg->lparam = 0; memcpy( msg->data, &raw_msg->data, sizeof(raw_msg->data) );
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput8/tests/device.c | 13 --------- dlls/user32/rawinput.c | 55 +++++++++++++++++++++++++++++++++++-- server/protocol.def | 6 ++++ server/queue.c | 24 ++++++++++++++++ 4 files changed, 83 insertions(+), 15 deletions(-)
diff --git a/dlls/dinput8/tests/device.c b/dlls/dinput8/tests/device.c index f3e7b542355..72a59878d8a 100644 --- a/dlls/dinput8/tests/device.c +++ b/dlls/dinput8/tests/device.c @@ -602,7 +602,6 @@ static void test_mouse_keyboard(void)
raw_devices_count = ARRAY_SIZE(raw_devices); GetRegisteredRawInputDevices(NULL, &raw_devices_count, sizeof(RAWINPUTDEVICE)); - todo_wine ok(raw_devices_count == 0, "Unexpected raw devices registered: %d\n", raw_devices_count);
hr = IDirectInputDevice8_Acquire(di_keyboard); @@ -624,7 +623,6 @@ static void test_mouse_keyboard(void) ok(SUCCEEDED(hr), "IDirectInputDevice8_Acquire failed: %08x\n", hr); raw_devices_count = ARRAY_SIZE(raw_devices); GetRegisteredRawInputDevices(NULL, &raw_devices_count, sizeof(RAWINPUTDEVICE)); - todo_wine ok(raw_devices_count == 0, "Unexpected raw devices registered: %d\n", raw_devices_count);
if (raw_devices[0].hwndTarget != NULL) @@ -662,7 +660,6 @@ static void test_mouse_keyboard(void) ok(SUCCEEDED(hr), "IDirectInputDevice8_Acquire failed: %08x\n", hr); raw_devices_count = ARRAY_SIZE(raw_devices); GetRegisteredRawInputDevices(NULL, &raw_devices_count, sizeof(RAWINPUTDEVICE)); - todo_wine ok(raw_devices_count == 0, "Unexpected raw devices registered: %d\n", raw_devices_count);
/* expect dinput8 to take over any activated raw input devices */ @@ -689,26 +686,18 @@ static void test_mouse_keyboard(void) raw_devices_count = ARRAY_SIZE(raw_devices); memset(raw_devices, 0, sizeof(raw_devices)); hr = GetRegisteredRawInputDevices(raw_devices, &raw_devices_count, sizeof(RAWINPUTDEVICE)); - todo_wine ok(hr == 3, "GetRegisteredRawInputDevices returned %d, raw_devices_count: %d\n", hr, raw_devices_count); - todo_wine ok(raw_devices[0].usUsagePage == 1, "Unexpected raw device usage page: %x\n", raw_devices[0].usUsagePage); - todo_wine ok(raw_devices[0].usUsage == 2, "Unexpected raw device usage: %x\n", raw_devices[0].usUsage); todo_wine ok(raw_devices[0].dwFlags == RIDEV_INPUTSINK, "Unexpected raw device flags: %x\n", raw_devices[0].dwFlags); todo_wine ok(raw_devices[0].hwndTarget == di_hwnd, "Unexpected raw device target: %p\n", raw_devices[0].hwndTarget); - todo_wine ok(raw_devices[1].usUsagePage == 1, "Unexpected raw device usage page: %x\n", raw_devices[1].usUsagePage); - todo_wine ok(raw_devices[1].usUsage == 5, "Unexpected raw device usage: %x\n", raw_devices[1].usUsage); ok(raw_devices[1].dwFlags == 0, "Unexpected raw device flags: %x\n", raw_devices[1].dwFlags); - todo_wine ok(raw_devices[1].hwndTarget == hwnd, "Unexpected raw device target: %p\n", raw_devices[1].hwndTarget); - todo_wine ok(raw_devices[2].usUsagePage == 1, "Unexpected raw device usage page: %x\n", raw_devices[1].usUsagePage); - todo_wine ok(raw_devices[2].usUsage == 6, "Unexpected raw device usage: %x\n", raw_devices[1].usUsage); todo_wine ok(raw_devices[2].dwFlags == RIDEV_INPUTSINK, "Unexpected raw device flags: %x\n", raw_devices[1].dwFlags); @@ -727,12 +716,10 @@ static void test_mouse_keyboard(void) hr = GetRegisteredRawInputDevices(raw_devices, &raw_devices_count, sizeof(RAWINPUTDEVICE)); todo_wine ok(hr == 1, "GetRegisteredRawInputDevices returned %d, raw_devices_count: %d\n", hr, raw_devices_count); - todo_wine ok(raw_devices[0].usUsagePage == 1, "Unexpected raw device usage page: %x\n", raw_devices[0].usUsagePage); todo_wine ok(raw_devices[0].usUsage == 5, "Unexpected raw device usage: %x\n", raw_devices[0].usUsage); ok(raw_devices[0].dwFlags == 0, "Unexpected raw device flags: %x\n", raw_devices[0].dwFlags); - todo_wine ok(raw_devices[0].hwndTarget == hwnd, "Unexpected raw device target: %p\n", raw_devices[0].hwndTarget);
IDirectInputDevice8_Release(di_mouse); diff --git a/dlls/user32/rawinput.c b/dlls/user32/rawinput.c index 182ee1e9eb7..e279ca2f06d 100644 --- a/dlls/user32/rawinput.c +++ b/dlls/user32/rawinput.c @@ -487,14 +487,65 @@ UINT WINAPI GetRawInputDeviceInfoW(HANDLE device, UINT command, void *data, UINT return s; }
+static int compare_raw_input_devices(const void *ap, const void *bp) +{ + const RAWINPUTDEVICE a = *(const RAWINPUTDEVICE *)ap; + const RAWINPUTDEVICE b = *(const RAWINPUTDEVICE *)bp; + + if (a.usUsagePage != b.usUsagePage) return a.usUsagePage - b.usUsagePage; + if (a.usUsage != b.usUsage) return a.usUsage - b.usUsage; + return 0; +} + /*********************************************************************** * GetRegisteredRawInputDevices (USER32.@) */ UINT WINAPI DECLSPEC_HOTPATCH GetRegisteredRawInputDevices(RAWINPUTDEVICE *devices, UINT *device_count, UINT size) { - FIXME("devices %p, device_count %p, size %u stub!\n", devices, device_count, size); + struct rawinput_device *d = NULL; + unsigned int count = ~0U;
- return 0; + TRACE("devices %p, device_count %p, size %u\n", devices, device_count, size); + + if (!device_count) + { + SetLastError(ERROR_INVALID_PARAMETER); + return ~0U; + } + + if (devices && !(d = HeapAlloc( GetProcessHeap(), 0, *device_count * sizeof(*d) ))) + return ~0U; + + SERVER_START_REQ( get_rawinput_devices ) + { + if (d) + wine_server_set_reply( req, d, *device_count * sizeof(*d) ); + + if (wine_server_call( req )) + goto done; + + if (!d || reply->device_count > *device_count) + { + *device_count = reply->device_count; + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + goto done; + } + + for (count = 0; count < reply->device_count; ++count) + { + devices[count].usUsagePage = d[count].usage_page; + devices[count].usUsage = d[count].usage; + devices[count].dwFlags = d[count].flags; + devices[count].hwndTarget = wine_server_ptr_handle(d[count].target); + } + } + SERVER_END_REQ; + + qsort(devices, count, sizeof(*devices), compare_raw_input_devices); + +done: + if (d) HeapFree( GetProcessHeap(), 0, d ); + return count; }
diff --git a/server/protocol.def b/server/protocol.def index 3b5da8dcaed..7854e31b2b1 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3867,6 +3867,12 @@ struct handle_info VARARG(devices,rawinput_devices); @END
+/* Retrieve the list of registered rawinput devices */ +@REQ(get_rawinput_devices) +@REPLY + unsigned int device_count; + VARARG(devices,rawinput_devices); +@END
/* Retrieve the suspended context of a thread */ @REQ(get_suspend_context) diff --git a/server/queue.c b/server/queue.c index 767207fa6c3..86e3535e58a 100644 --- a/server/queue.c +++ b/server/queue.c @@ -3194,3 +3194,27 @@ DECL_HANDLER(update_rawinput_devices) e = find_rawinput_device( 1, 6 ); current->process->rawinput_kbd = e ? &e->device : NULL; } + +DECL_HANDLER(get_rawinput_devices) +{ + unsigned int device_count = list_count(¤t->process->rawinput_devices); + struct rawinput_device *devices; + struct rawinput_device_entry *e; + unsigned int i; + + reply->device_count = device_count; + if (get_reply_max_size() / sizeof (*devices) < device_count) + return; + + if (!(devices = mem_alloc( device_count * sizeof (*devices) ))) + { + set_error( STATUS_NO_MEMORY ); + return; + } + + i = 0; + LIST_FOR_EACH_ENTRY( e, ¤t->process->rawinput_devices, struct rawinput_device_entry, entry ) + devices[i++] = e->device; + + set_reply_data_ptr( devices, device_count * sizeof (*devices) ); +}
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=59019
Your paranoid android.
=== debian10 (64 bit WoW report) ===
user32: 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 win.c:10210: Test failed: Expected foreground window 000E0120, got 00E300D4 win.c:10212: Test failed: GetActiveWindow() = 00000000 win.c:10212: Test failed: GetFocus() = 00000000 win.c:10213: Test failed: Received WM_ACTIVATEAPP(1), did not expect it.
This adds a global message window that will receive WM_INPUT messages, dispatched to every raw input device event_proc.
Devices that use raw input interface will not register low-level hooks anymore. They will also conflict with any raw input device registered outside of dinput, as exposed by the unit tests.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/device_private.h | 3 ++ dlls/dinput/dinput_main.c | 84 +++++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/dlls/dinput/device_private.h b/dlls/dinput/device_private.h index 27e9c262869..8d4d6a0b5bf 100644 --- a/dlls/dinput/device_private.h +++ b/dlls/dinput/device_private.h @@ -70,6 +70,9 @@ struct IDirectInputDeviceImpl int acquired; DI_EVENT_PROC event_proc; /* function to receive mouse & keyboard events */
+ BOOL use_raw_input; /* use raw input instead of low-level messages */ + RAWINPUTDEVICE raw_device; /* raw device to (un)register */ + LPDIDEVICEOBJECTDATA data_queue; /* buffer for 'GetDeviceData'. */ int queue_len; /* size of the queue - set in 'SetProperty' */ int queue_head; /* position to write new event into queue */ diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index 0855cb41cd5..fc001d77d83 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -97,6 +97,10 @@ static const struct dinput_device *dinput_devices[] =
HINSTANCE DINPUT_instance;
+static ATOM di_em_win_class; +static const WCHAR di_em_winW[] = {'D','I','E','m','W','i','n',0}; +static HWND di_em_win; + static BOOL check_hook_thread(void); static CRITICAL_SECTION dinput_hook_crit; static struct list direct_input_list = LIST_INIT( direct_input_list ); @@ -611,6 +615,59 @@ static HRESULT WINAPI IDirectInputWImpl_QueryInterface(LPDIRECTINPUT7W iface, RE return IDirectInputAImpl_QueryInterface( &This->IDirectInput7A_iface, riid, ppobj ); }
+static LRESULT WINAPI di_em_win_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + IDirectInputImpl *dinput; + + TRACE( "%p %d %lx %lx\n", hwnd, msg, wparam, lparam ); + + if (msg == WM_INPUT) + { + EnterCriticalSection( &dinput_hook_crit ); + LIST_FOR_EACH_ENTRY( dinput, &direct_input_list, IDirectInputImpl, entry ) + { + IDirectInputDeviceImpl *dev; + + EnterCriticalSection( &dinput->crit ); + LIST_FOR_EACH_ENTRY( dev, &dinput->devices_list, IDirectInputDeviceImpl, entry ) + { + if (dev->acquired && dev->event_proc && dev->use_raw_input) + { + TRACE("calling %p->%p (%lx %lx)\n", dev, dev->event_proc, wparam, lparam); + dev->event_proc( &dev->IDirectInputDevice8A_iface, GET_RAWINPUT_CODE_WPARAM(wparam), lparam ); + } + } + LeaveCriticalSection( &dinput->crit ); + } + LeaveCriticalSection( &dinput_hook_crit ); + } + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + +static void register_di_em_win_class(void) +{ + static WNDCLASSEXW class; + + ZeroMemory(&class, sizeof(class)); + class.cbSize = sizeof(class); + class.lpfnWndProc = di_em_win_wndproc; + class.hInstance = DINPUT_instance; + class.lpszClassName = di_em_winW; + + if (!(di_em_win_class = RegisterClassExW( &class ))) + WARN( "Unable to register message window class\n" ); +} + +static void unregister_di_em_win_class(void) +{ + if (!di_em_win_class) + return; + + if (!UnregisterClassW( MAKEINTRESOURCEW( di_em_win_class ), DINPUT_instance )) + WARN( "Unable to unregister message window class\n" ); +} + static HRESULT initialize_directinput_instance(IDirectInputImpl *This, DWORD dwVersion) { if (!This->initialized) @@ -1706,6 +1763,9 @@ static DWORD WINAPI hook_thread_proc(void *param) static HHOOK kbd_hook, mouse_hook; MSG msg;
+ di_em_win = CreateWindowW( MAKEINTRESOURCEW(di_em_win_class), di_em_winW, + 0, 0, 0, 0, 0, HWND_MESSAGE, 0, DINPUT_instance, NULL ); + /* Force creation of the message queue */ PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ); SetEvent(param); @@ -1738,7 +1798,7 @@ static DWORD WINAPI hook_thread_proc(void *param) EnterCriticalSection( &dinput->crit ); LIST_FOR_EACH_ENTRY( dev, &dinput->devices_list, IDirectInputDeviceImpl, entry ) { - if (!dev->acquired || !dev->event_proc) continue; + if (!dev->acquired || !dev->event_proc || dev->use_raw_input) continue;
if (IsEqualGUID( &dev->guid, &GUID_SysKeyboard )) kbd_cnt++; @@ -1770,6 +1830,9 @@ static DWORD WINAPI hook_thread_proc(void *param) DispatchMessageW(&msg); }
+ DestroyWindow( di_em_win ); + di_em_win = NULL; + FreeLibraryAndExitThread(DINPUT_instance, 0); }
@@ -1851,6 +1914,23 @@ void check_dinput_hooks(LPDIRECTINPUTDEVICE8W iface, BOOL acquired) hook_thread_event = NULL; }
+ if (dev->use_raw_input) + { + if (acquired) + { + dev->raw_device.dwFlags = RIDEV_INPUTSINK; + dev->raw_device.hwndTarget = di_em_win; + } + else + { + dev->raw_device.dwFlags = RIDEV_REMOVE; + dev->raw_device.hwndTarget = NULL; + } + + if (!RegisterRawInputDevices( &dev->raw_device, 1, sizeof(RAWINPUTDEVICE) )) + WARN( "Unable to (un)register raw device %x:%x\n", dev->raw_device.usUsagePage, dev->raw_device.usUsage ); + } + PostThreadMessageW( hook_thread_id, WM_USER+0x10, 1, 0 );
LeaveCriticalSection(&dinput_hook_crit); @@ -1877,9 +1957,11 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved) case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(inst); DINPUT_instance = inst; + register_di_em_win_class(); break; case DLL_PROCESS_DETACH: if (reserved) break; + unregister_di_em_win_class(); DeleteCriticalSection(&dinput_hook_crit); break; }
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=59020
Your paranoid android.
=== debian10 (32 bit WoW report) ===
user32: msg.c:8774: Test failed: WaitForSingleObject failed 102 msg.c:8780: Test failed: destroy child on thread exit: 0: the msg 0x0082 was expected, but got msg 0x000f instead msg.c:8780: Test failed: destroy child on thread exit: 1: the msg 0x000f was expected, but got msg 0x0014 instead msg.c:8780: Test failed: destroy child on thread exit: 2: the msg sequence is not complete: expected 0014 - actual 0000
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/mouse.c | 117 +++++++++++++++++++++++++++++++++++- dlls/dinput8/tests/device.c | 10 +-- 2 files changed, 119 insertions(+), 8 deletions(-)
diff --git a/dlls/dinput/mouse.c b/dlls/dinput/mouse.c index 52a766b2a1a..a94fff0670b 100644 --- a/dlls/dinput/mouse.c +++ b/dlls/dinput/mouse.c @@ -246,6 +246,13 @@ static SysMouseImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput) list_add_tail(&dinput->devices_list, &newDevice->base.entry); LeaveCriticalSection(&dinput->crit);
+ if (dinput->dwVersion >= 0x0800) + { + newDevice->base.use_raw_input = TRUE; + newDevice->base.raw_device.usUsagePage = 1; /* HID generic device page */ + newDevice->base.raw_device.usUsage = 2; /* HID generic mouse */ + } + return newDevice;
failed: @@ -318,7 +325,115 @@ static int dinput_mouse_hook( LPDIRECTINPUTDEVICE8A iface, WPARAM wparam, LPARAM { MSLLHOOKSTRUCT *hook = (MSLLHOOKSTRUCT *)lparam; SysMouseImpl* This = impl_from_IDirectInputDevice8A(iface); - int wdata = 0, inst_id = -1, ret = 0; + int wdata = 0, inst_id = -1, ret = 0, i; + + if (wparam == RIM_INPUT || wparam == RIM_INPUTSINK) + { + RAWINPUTHEADER raw_header; + RAWINPUT raw_input; + UINT size; + POINT rel, pt; + + static const USHORT mouse_button_flags[] = + { + RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP, + RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP, + RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP, + RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP, + RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP + }; + + TRACE("(%p) wp %08lx, lp %08lx\n", iface, wparam, lparam); + + size = sizeof(raw_header); + if (GetRawInputData( (HRAWINPUT)lparam, RID_HEADER, &raw_header, &size, sizeof(RAWINPUTHEADER) ) != sizeof(raw_header)) + { + WARN( "Unable to read raw input data header\n" ); + return 0; + } + + if (raw_header.dwType != RIM_TYPEMOUSE) + return 0; + + if (raw_header.dwSize > sizeof(raw_input)) + { + WARN( "Unexpected size for mouse raw input data\n" ); + return 0; + } + + size = raw_header.dwSize; + if (GetRawInputData( (HRAWINPUT)lparam, RID_INPUT, &raw_input, &size, sizeof(RAWINPUTHEADER) ) != raw_header.dwSize ) + { + WARN( "Unable to read raw input data\n" ); + return 0; + } + + if (raw_input.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) + FIXME( "Unimplemented MOUSE_VIRTUAL_DESKTOP flag\n" ); + if (raw_input.data.mouse.usFlags & MOUSE_ATTRIBUTES_CHANGED) + FIXME( "Unimplemented MOUSE_ATTRIBUTES_CHANGED flag\n" ); + + EnterCriticalSection(&This->base.crit); + + rel.x = raw_input.data.mouse.lLastX; + rel.y = raw_input.data.mouse.lLastY; + if (raw_input.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) + { + GetCursorPos(&pt); + rel.x -= pt.x; + rel.y -= pt.y; + } + + This->m_state.lX += rel.x; + This->m_state.lY += rel.y; + + if (This->base.data_format.user_df->dwFlags & DIDF_ABSAXIS) + { + pt.x = This->m_state.lX; + pt.y = This->m_state.lY; + } + else + { + pt = rel; + } + + if (rel.x) + queue_event(iface, DIDFT_MAKEINSTANCE(WINE_MOUSE_X_AXIS_INSTANCE) | DIDFT_RELAXIS, + pt.x, GetCurrentTime(), This->base.dinput->evsequence); + + if (rel.y) + queue_event(iface, DIDFT_MAKEINSTANCE(WINE_MOUSE_Y_AXIS_INSTANCE) | DIDFT_RELAXIS, + pt.y, GetCurrentTime(), This->base.dinput->evsequence); + + if (rel.x || rel.y) + { + if ((This->warp_override == WARP_FORCE_ON) || + (This->warp_override != WARP_DISABLE && (This->base.dwCoopLevel & DISCL_EXCLUSIVE))) + This->need_warp = TRUE; + } + + if (raw_input.data.mouse.usButtonFlags & RI_MOUSE_WHEEL) + { + This->m_state.lZ += (wdata = (SHORT)raw_input.data.mouse.usButtonData); + queue_event(iface, DIDFT_MAKEINSTANCE(WINE_MOUSE_Z_AXIS_INSTANCE) | DIDFT_RELAXIS, + wdata, GetCurrentTime(), This->base.dinput->evsequence); + ret = This->clipped; + } + + for (i = 0; i < ARRAY_SIZE(mouse_button_flags); ++i) + { + if (raw_input.data.mouse.usButtonFlags & mouse_button_flags[i]) + { + This->m_state.rgbButtons[i / 2] = 0x80 - (i % 2) * 0x80; + queue_event(iface, DIDFT_MAKEINSTANCE(WINE_MOUSE_BUTTONS_INSTANCE + (i / 2)) | DIDFT_PSHBUTTON, + This->m_state.rgbButtons[i / 2], GetCurrentTime(), This->base.dinput->evsequence); + } + } + + LeaveCriticalSection(&This->base.crit); + + return ret; + }
TRACE("msg %lx @ (%d %d)\n", wparam, hook->pt.x, hook->pt.y);
diff --git a/dlls/dinput8/tests/device.c b/dlls/dinput8/tests/device.c index 72a59878d8a..2aadf5ddf9b 100644 --- a/dlls/dinput8/tests/device.c +++ b/dlls/dinput8/tests/device.c @@ -646,13 +646,9 @@ static void test_mouse_keyboard(void) raw_devices_count = ARRAY_SIZE(raw_devices); memset(raw_devices, 0, sizeof(raw_devices)); hr = GetRegisteredRawInputDevices(raw_devices, &raw_devices_count, sizeof(RAWINPUTDEVICE)); - todo_wine ok(hr == 1, "GetRegisteredRawInputDevices returned %d, raw_devices_count: %d\n", hr, raw_devices_count); - todo_wine ok(raw_devices[0].usUsagePage == 1, "Unexpected raw device usage page: %x\n", raw_devices[0].usUsagePage); - todo_wine ok(raw_devices[0].usUsage == 2, "Unexpected raw device usage: %x\n", raw_devices[0].usUsage); - todo_wine ok(raw_devices[0].dwFlags == RIDEV_INPUTSINK, "Unexpected raw device flags: %x\n", raw_devices[0].dwFlags); todo_wine ok(raw_devices[0].hwndTarget == di_hwnd, "Unexpected raw device target: %p\n", raw_devices[0].hwndTarget); @@ -662,6 +658,9 @@ static void test_mouse_keyboard(void) GetRegisteredRawInputDevices(NULL, &raw_devices_count, sizeof(RAWINPUTDEVICE)); ok(raw_devices_count == 0, "Unexpected raw devices registered: %d\n", raw_devices_count);
+ if (raw_devices[0].hwndTarget != NULL) + di_hwnd = raw_devices[0].hwndTarget; + /* expect dinput8 to take over any activated raw input devices */ raw_devices[0].usUsagePage = 0x01; raw_devices[0].usUsage = 0x05; @@ -689,9 +688,7 @@ static void test_mouse_keyboard(void) ok(hr == 3, "GetRegisteredRawInputDevices returned %d, raw_devices_count: %d\n", hr, raw_devices_count); ok(raw_devices[0].usUsagePage == 1, "Unexpected raw device usage page: %x\n", raw_devices[0].usUsagePage); ok(raw_devices[0].usUsage == 2, "Unexpected raw device usage: %x\n", raw_devices[0].usUsage); - todo_wine ok(raw_devices[0].dwFlags == RIDEV_INPUTSINK, "Unexpected raw device flags: %x\n", raw_devices[0].dwFlags); - todo_wine ok(raw_devices[0].hwndTarget == di_hwnd, "Unexpected raw device target: %p\n", raw_devices[0].hwndTarget); ok(raw_devices[1].usUsagePage == 1, "Unexpected raw device usage page: %x\n", raw_devices[1].usUsagePage); ok(raw_devices[1].usUsage == 5, "Unexpected raw device usage: %x\n", raw_devices[1].usUsage); @@ -717,7 +714,6 @@ static void test_mouse_keyboard(void) todo_wine ok(hr == 1, "GetRegisteredRawInputDevices returned %d, raw_devices_count: %d\n", hr, raw_devices_count); ok(raw_devices[0].usUsagePage == 1, "Unexpected raw device usage page: %x\n", raw_devices[0].usUsagePage); - todo_wine ok(raw_devices[0].usUsage == 5, "Unexpected raw device usage: %x\n", raw_devices[0].usUsage); ok(raw_devices[0].dwFlags == 0, "Unexpected raw device flags: %x\n", raw_devices[0].dwFlags); ok(raw_devices[0].hwndTarget == hwnd, "Unexpected raw device target: %p\n", raw_devices[0].hwndTarget);
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=59021
Your paranoid android.
=== debian10 (64 bit WoW report) ===
user32: clipboard.c:833: Test failed: 0: gle 5 clipboard.c:838: Test failed: 0.0: got 0000 instead of 0001 clipboard.c:868: Test failed: 0: gle 1418 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
Under XInput2 protocol version < 2.1, RawEvents are not supposed to be sent if a pointer grab is active. However slave device events are still received regardless of this specification and Wine implemented a workaround to receive RawEvents during pointer grabs by listening to these slave device events.
However as soon as a mouse button is pressed, only the grabbing client will receive the raw motion events.
By advertising the support of XInput2 version >= 2.1, where rawevents are sent even during pointer grabs, we ensure to receive the RawMotion events from the desktop window thread, even if a mouse grab is active.
It is also possible to simplify the code by listening to master device events only and get rid of slave device id tracking.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/mouse.c | 49 ++++++++------------------------------- dlls/winex11.drv/x11drv.h | 3 --- 2 files changed, 10 insertions(+), 42 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 23424ed44d2..7e010fa2ada 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; + 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 }
@@ -1771,7 +1762,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; }
@@ -1792,26 +1782,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 35e27334db5..fec19dc9a78 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -338,12 +338,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;
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.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winex11.drv/event.c | 10 ++++- dlls/winex11.drv/mouse.c | 79 ++++++++++++++++++++++++++++------ dlls/winex11.drv/x11drv.h | 3 ++ dlls/winex11.drv/x11drv_main.c | 4 ++ 4 files changed, 80 insertions(+), 16 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 25730192d3d..4e89cb843dd 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -321,6 +321,10 @@ static enum event_merge_action merge_raw_motion_events( XIRawEvent *prev, XIRawE */ static enum event_merge_action merge_events( XEvent *prev, XEvent *next ) { +#ifdef HAVE_X11_EXTENSIONS_XINPUT2_H + struct x11drv_thread_data *thread_data = x11drv_thread_data(); +#endif + switch (prev->type) { case ConfigureNotify: @@ -352,19 +356,21 @@ static enum event_merge_action merge_events( XEvent *prev, XEvent *next ) case GenericEvent: if (next->xcookie.extension != xinput2_opcode) break; if (next->xcookie.evtype != XI_RawMotion) break; - if (x11drv_thread_data()->warp_serial) break; + if (thread_data->xi2_rawinput_only) break; + if (thread_data->warp_serial) break; return MERGE_KEEP; } break; case GenericEvent: if (prev->xcookie.extension != xinput2_opcode) break; if (prev->xcookie.evtype != XI_RawMotion) break; + if (thread_data->xi2_rawinput_only) break; switch (next->type) { case GenericEvent: if (next->xcookie.extension != xinput2_opcode) break; if (next->xcookie.evtype != XI_RawMotion) break; - if (x11drv_thread_data()->warp_serial) break; + if (thread_data->warp_serial) break; return merge_raw_motion_events( prev->xcookie.data, next->xcookie.data ); #endif } diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 7e010fa2ada..29efb57d3d8 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(); @@ -400,7 +412,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 +442,7 @@ static BOOL grab_clipping_window( const RECT *clip )
if (!clipping_cursor) { - disable_xinput2(); + X11DRV_XInput2_Disable(); DestroyWindow( msg_hwnd ); return FALSE; } @@ -511,7 +523,7 @@ LRESULT clip_cursor_notify( HWND hwnd, HWND prev_clip_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 */ @@ -611,7 +623,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; }
@@ -672,7 +684,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 @@ -1618,7 +1630,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 )) @@ -1787,6 +1799,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 ); @@ -1822,10 +1835,44 @@ 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 ) +{ + struct x11drv_thread_data *thread_data = x11drv_thread_data(); + XIRawEvent *event = cookie->data; + int button = event->detail - 1; + INPUT input;
- input.type = INPUT_MOUSE; - __wine_send_input( 0, &input, 0 ); + if (button >= NB_BUTTONS) return FALSE; + if (thread_data->xi2_state != xi_enabled) return FALSE; + if (event->deviceid != thread_data->xi2_core_pointer) return FALSE; + + TRACE( "raw button %u %s\n", button, event->evtype == XI_RawButtonRelease ? "up" : "down" ); + + 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[button] : button_down_data[button]; + input.u.mi.dwFlags = event->evtype == XI_RawButtonRelease ? button_up_flags[button] : button_down_flags[button]; + input.u.mi.time = EVENT_x11_time_to_win32_time(event->time); + input.u.mi.dwExtraInfo = 0; + + __wine_send_input( 0, &input, SEND_HWMSG_BCAST_RAW | SEND_HWMSG_ONLY_RAW ); return TRUE; }
@@ -1893,6 +1940,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 fec19dc9a78..4e5908f5aac 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -196,6 +196,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, @@ -341,6 +343,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 4f611f5faa6..aedc0faad53 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -634,6 +634,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 ); @@ -703,6 +705,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=59023
Your paranoid android.
=== debian10 (32 bit WoW report) ===
user32: menu.c:2354: Test failed: test 27
=== debian10 (64 bit WoW report) ===
user32: win.c:10172: Test failed: GetActiveWindow() = 00000000 win.c:10172: Test failed: GetFocus() = 00000000 win.c:10174: Test failed: Expected foreground window 000E0120, got 00E300D4 win.c:10177: Test failed: Received WM_ACTIVATEAPP(0), did not expect it. win.c:10184: Test failed: Expected foreground window 000E0120, got 00000000 win.c:10186: Test failed: GetActiveWindow() = 00000000 win.c:10186: Test failed: GetFocus() = 00000000 win.c:10191: Test failed: Expected foreground window 000E0120, got 00E300D4 win.c:10194: Test failed: Received WM_ACTIVATEAPP(1), did not expect it.