From: Paul Gofman pgofman@codeweavers.com
--- dlls/user32/tests/msg.c | 51 ++++++++++++++++++++++++++++++++++++++++- dlls/win32u/window.c | 3 +++ 2 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 0347f0d73b0..590f50aa6b7 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -73,6 +73,8 @@ #define ARCH "none" #endif
+static void pump_msg_loop(HWND hwnd, HACCEL hAccel); + /* encoded DRAWITEMSTRUCT into an LPARAM */ typedef struct { @@ -108,6 +110,7 @@ typedef struct
static BOOL test_DestroyWindow_flag; static BOOL test_context_menu; +static BOOL ignore_mouse_messages = TRUE; static HWINEVENTHOOK hEvent_hook; static HHOOK hKBD_hook; static HHOOK hCBT_hook; @@ -6160,6 +6163,30 @@ static const struct message WmFrameChanged_move[] = { { 0 } };
+static const struct message WmMove_mouse[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOSIZE }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOCLIENTSIZE }, + { WM_MOVE, sent|defwinproc, 0 }, + { WM_GETTEXT, sent|optional }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam|optional, 0, 0 }, /* Win7 seems to send this twice. */ + { WM_SETCURSOR, sent|lparam, 0, HTCLIENT | (WM_MOUSEMOVE << 16) }, + { WM_MOUSEMOVE, sent }, + /* WM_MOUSEMOVE with accompanying WM_SETCURSOR may sometimes appear a few extra times on Windows 7 with the + * same parameters. */ + { WM_SETCURSOR, sent|lparam|optional, 0, HTCLIENT | (WM_MOUSEMOVE << 16) }, + { WM_MOUSEMOVE, sent|optional }, + { WM_SETCURSOR, sent|lparam|optional, 0, HTCLIENT | (WM_MOUSEMOVE << 16) }, + { WM_MOUSEMOVE, sent|optional }, + { WM_SETCURSOR, sent|lparam|optional, 0, HTCLIENT | (WM_MOUSEMOVE << 16) }, + { WM_MOUSEMOVE, sent|optional }, + { WM_SETCURSOR, sent|lparam|optional, 0, HTCLIENT | (WM_MOUSEMOVE << 16) }, + { WM_MOUSEMOVE, sent|optional }, + { WM_SETCURSOR, sent|lparam|optional, 0, HTCLIENT | (WM_MOUSEMOVE << 16) }, + { WM_MOUSEMOVE, sent|optional }, + { 0 } +}; + static void test_setwindowpos(void) { HWND hwnd; @@ -6198,6 +6225,7 @@ static void test_setwindowpos(void) expect(sysX + X, rc.right); expect(winY + Y, rc.bottom);
+ GetWindowRect(hwnd, &rc); res = SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); ok_sequence(WmFrameChanged_move, "FrameChanged", FALSE); @@ -6207,6 +6235,26 @@ static void test_setwindowpos(void) expect(sysX, rc.right); expect(winY, rc.bottom);
+ /* get away from possible menu bar to avoid spurious position changed induced by WM. */ + res = SetWindowPos( hwnd, HWND_TOPMOST, 200, 200, 200, 200, SWP_SHOWWINDOW ); + ok(res == TRUE, "SetWindowPos expected TRUE, got %Id.\n", res); + SetForegroundWindow( hwnd ); + SetActiveWindow( hwnd ); + flush_events(); + pump_msg_loop( hwnd, 0 ); + flush_sequence(); + GetWindowRect( hwnd, &rc ); + SetCursorPos( rc.left + 100, rc.top + 100 ); + flush_events(); + pump_msg_loop( hwnd, 0 ); + flush_sequence(); + ignore_mouse_messages = FALSE; + res = SetWindowPos( hwnd, 0, 205, 205, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE ); + ok(res == TRUE, "SetWindowPos expected TRUE, got %Id.\n", res); + flush_events(); + ok_sequence(WmMove_mouse, "MouseMove", FALSE); + ignore_mouse_messages = TRUE; + DestroyWindow(hwnd); }
@@ -10853,7 +10901,8 @@ static LRESULT MsgCheckProc (BOOL unicode, HWND hwnd, UINT message, case WM_NCMOUSEMOVE: case WM_SETCURSOR: case WM_IME_SELECT: - return 0; + if (ignore_mouse_messages) return 0; + break; }
msg.hwnd = hwnd; diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 48a7304b6b0..82cc0a1e90e 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -3787,6 +3787,8 @@ BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y ) if (((winpos->flags & SWP_AGG_STATUSFLAGS) != SWP_AGG_NOPOSCHANGE) && !((orig_flags & SWP_AGG_NOCLIENTCHANGE) && (orig_flags & SWP_SHOWWINDOW))) { + POINT pt; + /* WM_WINDOWPOSCHANGED is sent even if SWP_NOSENDCHANGING is set and always contains final window position. */ @@ -3795,6 +3797,7 @@ BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y ) winpos->cx = new_rects.window.right - new_rects.window.left; winpos->cy = new_rects.window.bottom - new_rects.window.top; send_message( winpos->hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)winpos ); + if (get_cursor_pos( &pt )) NtUserSetCursorPos( pt.x, pt.y ); }
if ((winpos->flags & (SWP_NOSIZE|SWP_NOMOVE|SWP_FRAMECHANGED)) != (SWP_NOSIZE|SWP_NOMOVE))
This helps VPet-Simulator game which currently may voluntary move its window while mouse is pressed.
The issue comes not from the game itself but from .Net WPF (native, not related to Wine-Mono). WPF may generate move events for its Grid class during some internal mouse position sync. The information on the mouse position comes from the last relative mouse coordinates reveived from WINAPI mouse event. When the window is moved with SetWindowPos() without actual mouse movement those are not updated and WPF ends up with wrong mouse coordinates (as relative mouse coordinates are wrong after window move).
I separately tested that WM_MOUSEMOVE is delievered to the window receving mouse input even if another window from the same thread was moved.
Thinking of it, sending WM_MOUSEMOVE after window position change makes sense. WM_MOUSE... messages have relative coordinates and if an application tracks the mouse position solely based on that (which is probably a valid behaviour) WM_MOUSEMOVE with new relative coordinates is necessary to keep it consitent.
Calling SetCursorPos doesn't seem like a good idea, it will forcefully move the host cursor to wherever Wine believe the cursor is, which may be off, causing spurious cursor jumps whenever a window is moved, even if it doesn't have focus (and we can't rely on Wine focus tracking either because it's often wrong). I also expect it to cause a lot of problems when a window is dragged around as it'll generate SetWindowPos calls, while the host cursor position itself is being used to move the window.
We should not try to change the host cursor position, but rather simply artificially generate whatever messages we need.