[Spice-devel] [PATCH vdagent-win] vdagent: fix loss of mouse movement events
free.user.name
free.user.name at ya.ru
Fri Feb 16 12:05:39 UTC 2018
send_input() may not be immediately called from handle_mouse_event() on
movement. INPUT structure is generated and stored and a timer may be set
instead. If subsequent call to handle_mouse_event() occurs before timer
expires, prepared INPUT structure gets overwritten and MOUSEEVENTF_MOVE
bit is lost. Windows doesn't see updated mouse position as the result.
Make handle_mouse_event() merely store the new mouse state, and move
INPUT structure generation to send_input(). Shuffle new mouse state to
previous only after mouse events are submitted to SendInput() Windows
API function.
Signed-off-by: free.user.name <free.user.name at ya.ru>
---
This fixes a very annoying mouse input bug in Windows 7/10 guests for
me. Whenever vdagent-win is handling mouse input, mouse pointer position
seen by the guest sometimes lags behind the cursor drawn on screen,
which makes it impossible to reliably click on anything.
vdagent/vdagent.cpp | 132 +++++++++++++++++++++++++---------------------------
1 file changed, 63 insertions(+), 69 deletions(-)
diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp
index f00fbf5..89b3c6a 100644
--- a/vdagent/vdagent.cpp
+++ b/vdagent/vdagent.cpp
@@ -89,8 +89,7 @@ private:
void on_clipboard_grab();
void on_clipboard_request(UINT format);
void on_clipboard_release();
- DWORD get_buttons_change(DWORD last_buttons_state, DWORD new_buttons_state,
- DWORD mask, DWORD down_flag, DWORD up_flag);
+ DWORD get_buttons_change(DWORD mask, DWORD down_flag, DWORD up_flag);
static HGLOBAL utf8_alloc(LPCSTR data, int size);
static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
static DWORD WINAPI event_thread_proc(LPVOID param);
@@ -131,10 +130,8 @@ private:
int _system_version;
int _clipboard_owner;
DWORD _clipboard_tick;
- DWORD _buttons_state;
- ULONG _mouse_x;
- ULONG _mouse_y;
- INPUT _input;
+ VDAgentMouseState _new_mouse;
+ VDAgentMouseState _last_mouse;
DWORD _input_time;
HANDLE _control_event;
HANDLE _stop_event;
@@ -191,9 +188,6 @@ VDAgent::VDAgent()
, _remove_clipboard_listener (NULL)
, _clipboard_owner (owner_none)
, _clipboard_tick (0)
- , _buttons_state (0)
- , _mouse_x (0)
- , _mouse_y (0)
, _input_time (0)
, _control_event (NULL)
, _stop_event (NULL)
@@ -221,7 +215,8 @@ VDAgent::VDAgent()
swprintf_s(log_path, MAX_PATH, VD_AGENT_LOG_PATH, temp_path);
_log = VDLog::get(log_path);
}
- ZeroMemory(&_input, sizeof(_input));
+ ZeroMemory(&_new_mouse, sizeof(_new_mouse));
+ ZeroMemory(&_last_mouse, sizeof(_last_mouse));
ZeroMemory(&_read_overlapped, sizeof(_read_overlapped));
ZeroMemory(&_write_overlapped, sizeof(_write_overlapped));
ZeroMemory(_read_buf, sizeof(_read_buf));
@@ -522,13 +517,12 @@ void VDAgent::event_dispatcher(DWORD timeout, DWORD wake_mask)
}
}
-DWORD VDAgent::get_buttons_change(DWORD last_buttons_state, DWORD new_buttons_state,
- DWORD mask, DWORD down_flag, DWORD up_flag)
+DWORD VDAgent::get_buttons_change(DWORD mask, DWORD down_flag, DWORD up_flag)
{
DWORD ret = 0;
- if (!(last_buttons_state & mask) && (new_buttons_state & mask)) {
+ if (!(_last_mouse.buttons & mask) && (_new_mouse.buttons & mask)) {
ret = down_flag;
- } else if ((last_buttons_state & mask) && !(new_buttons_state & mask)) {
+ } else if ((_last_mouse.buttons & mask) && !(_new_mouse.buttons & mask)) {
ret = up_flag;
}
return ret;
@@ -536,101 +530,101 @@ DWORD VDAgent::get_buttons_change(DWORD last_buttons_state, DWORD new_buttons_st
bool VDAgent::send_input()
{
+ DisplayMode* mode = NULL;
+ DWORD mouse_move = 0;
+ DWORD buttons_change = 0;
+ DWORD mouse_wheel = 0;
bool ret = true;
- _desktop_layout->lock();
+ INPUT input;
+
if (_pending_input) {
if (KillTimer(_hwnd, VD_TIMER_ID)) {
_pending_input = false;
} else {
vd_printf("KillTimer failed: %lu", GetLastError());
_running = false;
- _desktop_layout->unlock();
return false;
}
}
- if (!SendInput(1, &_input, sizeof(INPUT))) {
- DWORD err = GetLastError();
- // Don't stop agent due to UIPI blocking, which is usually only for specific windows
- // of system security applications (anti-viruses etc.)
- if (err != ERROR_SUCCESS && err != ERROR_ACCESS_DENIED) {
- vd_printf("SendInput failed: %lu", err);
- ret = _running = false;
- }
- }
- _input_time = GetTickCount();
- _desktop_layout->unlock();
- return ret;
-}
-
-bool VDAgent::handle_mouse_event(VDAgentMouseState* state)
-{
- DisplayMode* mode = NULL;
- DWORD mouse_move = 0;
- DWORD buttons_change = 0;
- DWORD mouse_wheel = 0;
- bool ret = true;
ASSERT(_desktop_layout);
_desktop_layout->lock();
- if (state->display_id < _desktop_layout->get_display_count()) {
- mode = _desktop_layout->get_display(state->display_id);
+ if (_new_mouse.display_id < _desktop_layout->get_display_count()) {
+ mode = _desktop_layout->get_display(_new_mouse.display_id);
}
if (!mode || !mode->get_attached()) {
_desktop_layout->unlock();
return true;
}
- ZeroMemory(&_input, sizeof(INPUT));
- _input.type = INPUT_MOUSE;
- if (state->x != _mouse_x || state->y != _mouse_y) {
+ ZeroMemory(&input, sizeof(INPUT));
+ input.type = INPUT_MOUSE;
+ if (_new_mouse.x != _last_mouse.x || _new_mouse.y != _last_mouse.y) {
DWORD w = _desktop_layout->get_total_width();
DWORD h = _desktop_layout->get_total_height();
w = (w > 1) ? w-1 : 1; /* coordinates are 0..w-1, protect w==0 */
h = (h > 1) ? h-1 : 1; /* coordinates are 0..h-1, protect h==0 */
- _mouse_x = state->x;
- _mouse_y = state->y;
mouse_move = MOUSEEVENTF_MOVE;
- _input.mi.dx = (mode->get_pos_x() + _mouse_x) * 0xffff / w;
- _input.mi.dy = (mode->get_pos_y() + _mouse_y) * 0xffff / h;
+ input.mi.dx = (mode->get_pos_x() + _new_mouse.x) * 0xffff / w;
+ input.mi.dy = (mode->get_pos_y() + _new_mouse.y) * 0xffff / h;
}
- if (state->buttons != _buttons_state) {
- buttons_change = get_buttons_change(_buttons_state, state->buttons, VD_AGENT_LBUTTON_MASK,
+ if (_new_mouse.buttons != _last_mouse.buttons) {
+ buttons_change = get_buttons_change(VD_AGENT_LBUTTON_MASK,
MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP) |
- get_buttons_change(_buttons_state, state->buttons, VD_AGENT_MBUTTON_MASK,
+ get_buttons_change(VD_AGENT_MBUTTON_MASK,
MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP) |
- get_buttons_change(_buttons_state, state->buttons, VD_AGENT_RBUTTON_MASK,
+ get_buttons_change(VD_AGENT_RBUTTON_MASK,
MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP);
- mouse_wheel = get_buttons_change(_buttons_state, state->buttons,
- VD_AGENT_UBUTTON_MASK | VD_AGENT_DBUTTON_MASK,
+ mouse_wheel = get_buttons_change(VD_AGENT_UBUTTON_MASK | VD_AGENT_DBUTTON_MASK,
MOUSEEVENTF_WHEEL, 0);
if (mouse_wheel) {
- if (state->buttons & VD_AGENT_UBUTTON_MASK) {
- _input.mi.mouseData = WHEEL_DELTA;
- } else if (state->buttons & VD_AGENT_DBUTTON_MASK) {
- _input.mi.mouseData = (DWORD)(-WHEEL_DELTA);
+ if (_new_mouse.buttons & VD_AGENT_UBUTTON_MASK) {
+ input.mi.mouseData = WHEEL_DELTA;
+ } else if (_new_mouse.buttons & VD_AGENT_DBUTTON_MASK) {
+ input.mi.mouseData = (DWORD)(-WHEEL_DELTA);
}
}
- _buttons_state = state->buttons;
}
- _input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | mouse_move |
- mouse_wheel | buttons_change;
+ input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | mouse_move |
+ mouse_wheel | buttons_change;
- if ((mouse_move && GetTickCount() - _input_time > VD_INPUT_INTERVAL_MS) || buttons_change ||
- mouse_wheel) {
- ret = send_input();
- } else if (!_pending_input) {
- if (SetTimer(_hwnd, VD_TIMER_ID, VD_INPUT_INTERVAL_MS, NULL)) {
- _pending_input = true;
- } else {
- vd_printf("SetTimer failed: %lu", GetLastError());
- _running = false;
- ret = false;
+ if (!SendInput(1, &input, sizeof(INPUT))) {
+ DWORD err = GetLastError();
+ // Don't stop agent due to UIPI blocking, which is usually only for specific windows
+ // of system security applications (anti-viruses etc.)
+ if (err != ERROR_SUCCESS && err != ERROR_ACCESS_DENIED) {
+ vd_printf("SendInput failed: %lu", err);
+ ret = _running = false;
}
+ } else {
+ _last_mouse = _new_mouse;
}
+ _input_time = GetTickCount();
_desktop_layout->unlock();
return ret;
}
+bool VDAgent::handle_mouse_event(VDAgentMouseState* state)
+{
+ _new_mouse = *state;
+ if (_new_mouse.buttons != _last_mouse.buttons) {
+ return send_input();
+ } else if (_new_mouse.x != _last_mouse.x || _new_mouse.y != _last_mouse.y) {
+ if (GetTickCount() - _input_time > VD_INPUT_INTERVAL_MS) {
+ return send_input();
+ } else if (!_pending_input) {
+ if (SetTimer(_hwnd, VD_TIMER_ID, VD_INPUT_INTERVAL_MS, NULL)) {
+ _pending_input = true;
+ } else {
+ vd_printf("SetTimer failed: %lu", GetLastError());
+ _running = false;
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
bool VDAgent::handle_mon_config(VDAgentMonitorsConfig* mon_config, uint32_t port)
{
VDIChunk* reply_chunk;
--
2.15.1
More information about the Spice-devel
mailing list