Issue was caused by TlsGetValue always resetting error to ERROR_SUCCESS. This change fixes GetCursorPos resetting last-error randomly.
Signed-off-by: Rafał Harabień rafalh1992@o2.pl --- dlls/user32/dialog.c | 1 + dlls/user32/menu.c | 3 +++ dlls/user32/tests/input.c | 11 ++++++++++- dlls/user32/tests/win.c | 7 +++++++ dlls/winex11.drv/x11drv.h | 7 ++++++- 5 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/dlls/user32/dialog.c b/dlls/user32/dialog.c index c2aad8d..46220b0 100644 --- a/dlls/user32/dialog.c +++ b/dlls/user32/dialog.c @@ -283,6 +283,7 @@ static BOOL DIALOG_CreateControls32( HWND hwnd, LPCSTR template, const DLG_TEMPL WARN("control %s %s creation failed\n", debugstr_w(info.className), debugstr_w(info.windowName)); if (dlgTemplate->style & DS_NOFAILCREATE) continue; + SetLastError(0); return FALSE; }
diff --git a/dlls/user32/menu.c b/dlls/user32/menu.c index a5f2317..1ba9638 100644 --- a/dlls/user32/menu.c +++ b/dlls/user32/menu.c @@ -3505,8 +3505,10 @@ BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y, GetClassLongW( hWnd, GCL_STYLE));
if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 )) + { ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpTpm ? &lpTpm->rcExclude : NULL ); + } MENU_ExitTracking(hWnd, TRUE);
if (menu->hWnd) @@ -3518,6 +3520,7 @@ BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y, SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu, MAKELPARAM(0, IS_SYSTEM_MENU(menu)) ); } + SetLastError(0); }
return ret; diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 8f2576d..6ce03f8 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -1928,8 +1928,17 @@ static void test_Input_mouse(void) DWORD thread_id; POINT pt, pt_org; MSG msg; + BOOL ret;
- GetCursorPos(&pt_org); + SetLastError(0xdeadbeef); + ret = GetCursorPos(NULL); + ok(!ret, "GetCursorPos succeed\n"); + ok(GetLastError() == 0xdeadbeef || GetLastError() == ERROR_NOACCESS, "error %lu\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = GetCursorPos(&pt_org); + ok(ret, "GetCursorPos failed\n"); + ok(GetLastError() == 0xdeadbeef, "error %lu\n", GetLastError());
button_win = CreateWindowA("button", "button", WS_VISIBLE | WS_POPUP, 100, 100, 100, 100, 0, NULL, NULL, NULL); diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index b529284..dae34e4 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -6261,6 +6261,13 @@ static void test_CreateWindow(void) ok( rc.bottom <= expected_cy, "invalid rect bottom %u\n", rc.bottom ); DestroyWindow(hwnd);
+ /* invalid class */ + SetLastError(0xdeadbeef); + hwnd = CreateWindowExA(0, "INVALID_CLASS", NULL, WS_CHILD, 10, 10, 100, 100, parent, 0, 0, NULL); + ok(hwnd == 0, "CreateWindowEx succeeded\n"); + ok(GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "CreateWindowEx error %d\n", GetLastError()); + DestroyWindow(hwnd); + if (pGetLayout && pSetLayout) { HDC hdc = GetDC( parent ); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 721c082..6786a08 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -345,7 +345,12 @@ extern DWORD thread_data_tls_index DECLSPEC_HIDDEN;
static inline struct x11drv_thread_data *x11drv_thread_data(void) { - return TlsGetValue( thread_data_tls_index ); + /* TlsGetValue always resets last error */ + unsigned long err = GetLastError(); + struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index ); + if (err != ERROR_SUCCESS && GetLastError() == ERROR_SUCCESS) + SetLastError(err); + return data; }
/* retrieve the thread display, or NULL if not created yet */
Signed-off-by: Rafał Harabień rafalh1992@o2.pl --- dlls/user32/input.c | 110 ++++++++++++++++++++++----------------------- dlls/user32/user_private.h | 8 ++++ 2 files changed, 61 insertions(+), 57 deletions(-)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index c475b19..a77127d 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -1243,45 +1243,38 @@ BOOL WINAPI UnloadKeyboardLayout(HKL hkl) return USER_Driver->pUnloadKeyboardLayout(hkl); }
-typedef struct __TRACKINGLIST { - TRACKMOUSEEVENT tme; - POINT pos; /* center of hover rectangle */ -} _TRACKINGLIST; - -/* FIXME: move tracking stuff into a per thread data */ -static _TRACKINGLIST tracking_info; -static UINT_PTR timer;
static void check_mouse_leave(HWND hwnd, int hittest) { - if (tracking_info.tme.hwndTrack != hwnd) + struct tracking_info *tracking_info = &get_user_thread_info()->tracking_info; + if (tracking_info->tme.hwndTrack != hwnd) { - if (tracking_info.tme.dwFlags & TME_NONCLIENT) - PostMessageW(tracking_info.tme.hwndTrack, WM_NCMOUSELEAVE, 0, 0); + if (tracking_info->tme.dwFlags & TME_NONCLIENT) + PostMessageW(tracking_info->tme.hwndTrack, WM_NCMOUSELEAVE, 0, 0); else - PostMessageW(tracking_info.tme.hwndTrack, WM_MOUSELEAVE, 0, 0); + PostMessageW(tracking_info->tme.hwndTrack, WM_MOUSELEAVE, 0, 0);
/* remove the TME_LEAVE flag */ - tracking_info.tme.dwFlags &= ~TME_LEAVE; + tracking_info->tme.dwFlags &= ~TME_LEAVE; } else { if (hittest == HTCLIENT) { - if (tracking_info.tme.dwFlags & TME_NONCLIENT) + if (tracking_info->tme.dwFlags & TME_NONCLIENT) { - PostMessageW(tracking_info.tme.hwndTrack, WM_NCMOUSELEAVE, 0, 0); + PostMessageW(tracking_info->tme.hwndTrack, WM_NCMOUSELEAVE, 0, 0); /* remove the TME_LEAVE flag */ - tracking_info.tme.dwFlags &= ~TME_LEAVE; + tracking_info->tme.dwFlags &= ~TME_LEAVE; } } else { - if (!(tracking_info.tme.dwFlags & TME_NONCLIENT)) + if (!(tracking_info->tme.dwFlags & TME_NONCLIENT)) { - PostMessageW(tracking_info.tme.hwndTrack, WM_MOUSELEAVE, 0, 0); + PostMessageW(tracking_info->tme.hwndTrack, WM_MOUSELEAVE, 0, 0); /* remove the TME_LEAVE flag */ - tracking_info.tme.dwFlags &= ~TME_LEAVE; + tracking_info->tme.dwFlags &= ~TME_LEAVE; } } } @@ -1292,6 +1285,7 @@ static void CALLBACK TrackMouseEventProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, { POINT pos; INT hoverwidth = 0, hoverheight = 0, hittest; + struct tracking_info *tracking_info = &get_user_thread_info()->tracking_info;
TRACE("hwnd %p, msg %04x, id %04lx, time %u\n", hwnd, uMsg, idEvent, dwTime);
@@ -1304,31 +1298,31 @@ static void CALLBACK TrackMouseEventProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, SystemParametersInfoW(SPI_GETMOUSEHOVERHEIGHT, 0, &hoverheight, 0);
TRACE("tracked pos %s, current pos %s, hover width %d, hover height %d\n", - wine_dbgstr_point(&tracking_info.pos), wine_dbgstr_point(&pos), + wine_dbgstr_point(&tracking_info->pos), wine_dbgstr_point(&pos), hoverwidth, hoverheight);
/* see if this tracking event is looking for TME_LEAVE and that the */ /* mouse has left the window */ - if (tracking_info.tme.dwFlags & TME_LEAVE) + if (tracking_info->tme.dwFlags & TME_LEAVE) { check_mouse_leave(hwnd, hittest); }
- if (tracking_info.tme.hwndTrack != hwnd) + if (tracking_info->tme.hwndTrack != hwnd) { /* mouse is gone, stop tracking mouse hover */ - tracking_info.tme.dwFlags &= ~TME_HOVER; + tracking_info->tme.dwFlags &= ~TME_HOVER; }
/* see if we are tracking hovering for this hwnd */ - if (tracking_info.tme.dwFlags & TME_HOVER) + if (tracking_info->tme.dwFlags & TME_HOVER) { /* has the cursor moved outside the rectangle centered around pos? */ - if ((abs(pos.x - tracking_info.pos.x) > (hoverwidth / 2)) || - (abs(pos.y - tracking_info.pos.y) > (hoverheight / 2))) + if ((abs(pos.x - tracking_info->pos.x) > (hoverwidth / 2)) || + (abs(pos.y - tracking_info->pos.y) > (hoverheight / 2))) { /* record this new position as the current position */ - tracking_info.pos = pos; + tracking_info->pos = pos; } else { @@ -1337,29 +1331,29 @@ static void CALLBACK TrackMouseEventProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, ScreenToClient(hwnd, &pos); TRACE("client cursor pos %s\n", wine_dbgstr_point(&pos));
- PostMessageW(tracking_info.tme.hwndTrack, WM_MOUSEHOVER, + PostMessageW(tracking_info->tme.hwndTrack, WM_MOUSEHOVER, get_key_state(), MAKELPARAM( pos.x, pos.y )); } else { - if (tracking_info.tme.dwFlags & TME_NONCLIENT) - PostMessageW(tracking_info.tme.hwndTrack, WM_NCMOUSEHOVER, + if (tracking_info->tme.dwFlags & TME_NONCLIENT) + PostMessageW(tracking_info->tme.hwndTrack, WM_NCMOUSEHOVER, hittest, MAKELPARAM( pos.x, pos.y )); }
/* stop tracking mouse hover */ - tracking_info.tme.dwFlags &= ~TME_HOVER; + tracking_info->tme.dwFlags &= ~TME_HOVER; } }
/* stop the timer if the tracking list is empty */ - if (!(tracking_info.tme.dwFlags & (TME_HOVER | TME_LEAVE))) + if (!(tracking_info->tme.dwFlags & (TME_HOVER | TME_LEAVE))) { - KillSystemTimer(tracking_info.tme.hwndTrack, timer); - timer = 0; - tracking_info.tme.hwndTrack = 0; - tracking_info.tme.dwFlags = 0; - tracking_info.tme.dwHoverTime = 0; + KillSystemTimer(tracking_info->tme.hwndTrack, tracking_info->timer); + tracking_info->timer = 0; + tracking_info->tme.hwndTrack = 0; + tracking_info->tme.dwFlags = 0; + tracking_info->tme.dwHoverTime = 0; } }
@@ -1393,6 +1387,7 @@ TrackMouseEvent (TRACKMOUSEEVENT *ptme) POINT pos; DWORD hover_time; INT hittest; + struct tracking_info *tracking_info = &get_user_thread_info()->tracking_info;
TRACE("%x, %x, %p, %u\n", ptme->cbSize, ptme->dwFlags, ptme->hwndTrack, ptme->dwHoverTime);
@@ -1405,7 +1400,7 @@ TrackMouseEvent (TRACKMOUSEEVENT *ptme) /* fill the TRACKMOUSEEVENT struct with the current tracking for the given hwnd */ if (ptme->dwFlags & TME_QUERY ) { - *ptme = tracking_info.tme; + *ptme = tracking_info->tme; /* set cbSize in the case it's not initialized yet */ ptme->cbSize = sizeof(TRACKMOUSEEVENT);
@@ -1434,52 +1429,53 @@ TrackMouseEvent (TRACKMOUSEEVENT *ptme)
if (ptme->dwFlags & TME_CANCEL) { - if (tracking_info.tme.hwndTrack == ptme->hwndTrack) + if (tracking_info->tme.hwndTrack == ptme->hwndTrack) { - tracking_info.tme.dwFlags &= ~(ptme->dwFlags & ~TME_CANCEL); + tracking_info->tme.dwFlags &= ~(ptme->dwFlags & ~TME_CANCEL);
/* if we aren't tracking on hover or leave remove this entry */ - if (!(tracking_info.tme.dwFlags & (TME_HOVER | TME_LEAVE))) + if (!(tracking_info->tme.dwFlags & (TME_HOVER | TME_LEAVE))) { - KillSystemTimer(tracking_info.tme.hwndTrack, timer); - timer = 0; - tracking_info.tme.hwndTrack = 0; - tracking_info.tme.dwFlags = 0; - tracking_info.tme.dwHoverTime = 0; + KillSystemTimer(tracking_info->tme.hwndTrack, tracking_info->timer); + tracking_info->timer = 0; + tracking_info->tme.hwndTrack = 0; + tracking_info->tme.dwFlags = 0; + tracking_info->tme.dwHoverTime = 0; } } } else { /* In our implementation it's possible that another window will receive a * WM_MOUSEMOVE and call TrackMouseEvent before TrackMouseEventProc is * called. In such a situation post the WM_MOUSELEAVE now */ - if (tracking_info.tme.dwFlags & TME_LEAVE && tracking_info.tme.hwndTrack != NULL) + if (tracking_info->tme.dwFlags & TME_LEAVE && tracking_info->tme.hwndTrack != NULL) check_mouse_leave(hwnd, hittest);
- if (timer) + if (tracking_info->timer) { - KillSystemTimer(tracking_info.tme.hwndTrack, timer); - timer = 0; - tracking_info.tme.hwndTrack = 0; - tracking_info.tme.dwFlags = 0; - tracking_info.tme.dwHoverTime = 0; + KillSystemTimer(tracking_info->tme.hwndTrack, tracking_info->timer); + tracking_info->timer = 0; + tracking_info->tme.hwndTrack = 0; + tracking_info->tme.dwFlags = 0; + tracking_info->tme.dwHoverTime = 0; }
if (ptme->hwndTrack == hwnd) { /* Adding new mouse event to the tracking list */ - tracking_info.tme = *ptme; - tracking_info.tme.dwHoverTime = hover_time; + tracking_info->tme = *ptme; + tracking_info->tme.dwHoverTime = hover_time;
/* Initialize HoverInfo variables even if not hover tracking */ - tracking_info.pos = pos; + tracking_info->pos = pos;
- timer = SetSystemTimer(tracking_info.tme.hwndTrack, (UINT_PTR)&tracking_info.tme, hover_time, TrackMouseEventProc); + tracking_info->timer = SetSystemTimer(tracking_info->tme.hwndTrack, (UINT_PTR)&tracking_info->tme, hover_time, TrackMouseEventProc); } }
return TRUE; }
+ /*********************************************************************** * GetMouseMovePointsEx [USER32] * diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index 052fdd8..d7cfada 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -163,6 +163,13 @@ struct wm_char_mapping_data MSG get_msg; };
+/* data for TrackMouseEvent */ +struct tracking_info { + TRACKMOUSEEVENT tme; + POINT pos; /* center of hover rectangle */ + UINT_PTR timer; +}; + /* this is the structure stored in TEB->Win32ClientInfo */ /* no attempt is made to keep the layout compatible with the Windows one */ struct user_thread_info @@ -185,6 +192,7 @@ struct user_thread_info HWND top_window; /* Desktop window */ HWND msg_window; /* HWND_MESSAGE parent window */ RAWINPUT *rawinput; + struct tracking_info tracking_info; };
C_ASSERT( sizeof(struct user_thread_info) <= sizeof(((TEB *)0)->Win32ClientInfo) );
New code doesn't use timer (for TME_LEAVE) and reacts immediately on mouse motion. It improves user experience for controls depending on this API like toolbar.
Signed-off-by: Rafał Harabień rafalh1992@o2.pl --- dlls/user32/input.c | 70 ++++++------ dlls/user32/message.c | 2 + dlls/user32/tests/msg.c | 265 ++++++++++++++++++++++++++++++++------------- dlls/user32/user_private.h | 1 + dlls/winex11.drv/event.c | 2 +- dlls/winex11.drv/mouse.c | 29 +++++ dlls/winex11.drv/window.c | 2 +- dlls/winex11.drv/x11drv.h | 1 + 8 files changed, 262 insertions(+), 110 deletions(-)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index a77127d..303d7b5 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -1244,9 +1244,25 @@ BOOL WINAPI UnloadKeyboardLayout(HKL hkl) }
-static void check_mouse_leave(HWND hwnd, int hittest) +void maybe_clean_tracking_info(struct tracking_info *tracking_info) +{ + if (!(tracking_info->tme.dwFlags & (TME_HOVER | TME_LEAVE))) + { + tracking_info->tme.hwndTrack = 0; + tracking_info->tme.dwFlags = 0; + tracking_info->tme.dwHoverTime = 0; + } +} + +void check_mouse_leave(HWND hwnd, int hittest) { struct tracking_info *tracking_info = &get_user_thread_info()->tracking_info; + + TRACE("hwnd %p hittest %d\n", hwnd, hittest); + + if (!(tracking_info->tme.dwFlags & TME_LEAVE)) + return; + if (tracking_info->tme.hwndTrack != hwnd) { if (tracking_info->tme.dwFlags & TME_NONCLIENT) @@ -1278,6 +1294,7 @@ static void check_mouse_leave(HWND hwnd, int hittest) } } } + maybe_clean_tracking_info(tracking_info); }
static void CALLBACK TrackMouseEventProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, @@ -1301,13 +1318,6 @@ static void CALLBACK TrackMouseEventProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, wine_dbgstr_point(&tracking_info->pos), wine_dbgstr_point(&pos), hoverwidth, hoverheight);
- /* see if this tracking event is looking for TME_LEAVE and that the */ - /* mouse has left the window */ - if (tracking_info->tme.dwFlags & TME_LEAVE) - { - check_mouse_leave(hwnd, hittest); - } - if (tracking_info->tme.hwndTrack != hwnd) { /* mouse is gone, stop tracking mouse hover */ @@ -1347,14 +1357,12 @@ static void CALLBACK TrackMouseEventProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, }
/* stop the timer if the tracking list is empty */ - if (!(tracking_info->tme.dwFlags & (TME_HOVER | TME_LEAVE))) + if (!(tracking_info->tme.dwFlags & TME_HOVER)) { KillSystemTimer(tracking_info->tme.hwndTrack, tracking_info->timer); tracking_info->timer = 0; - tracking_info->tme.hwndTrack = 0; - tracking_info->tme.dwFlags = 0; - tracking_info->tme.dwHoverTime = 0; } + maybe_clean_tracking_info(tracking_info); }
@@ -1398,7 +1406,7 @@ TrackMouseEvent (TRACKMOUSEEVENT *ptme) }
/* fill the TRACKMOUSEEVENT struct with the current tracking for the given hwnd */ - if (ptme->dwFlags & TME_QUERY ) + if (ptme->dwFlags & TME_QUERY) { *ptme = tracking_info->tme; /* set cbSize in the case it's not initialized yet */ @@ -1413,13 +1421,16 @@ TrackMouseEvent (TRACKMOUSEEVENT *ptme) return FALSE; }
- hover_time = (ptme->dwFlags & TME_HOVER) ? ptme->dwHoverTime : HOVER_DEFAULT; - - /* if HOVER_DEFAULT was specified replace this with the system's current value. - * TME_LEAVE doesn't need to specify hover time so use default */ - if (hover_time == HOVER_DEFAULT || hover_time == 0) - SystemParametersInfoW(SPI_GETMOUSEHOVERTIME, 0, &hover_time, 0); - + if (ptme->dwFlags & TME_HOVER) + { + hover_time = ptme->dwHoverTime; + /* if HOVER_DEFAULT was specified replace this with the system's current value. + * TME_LEAVE doesn't need to specify hover time so use default */ + if (hover_time == HOVER_DEFAULT || hover_time == 0) + SystemParametersInfoW(SPI_GETMOUSEHOVERTIME, 0, &hover_time, 0); + } + else + hover_time = 0; GetCursorPos(&pos); hwnd = WINPOS_WindowFromPoint(ptme->hwndTrack, pos, &hittest); TRACE("point %s hwnd %p hittest %d\n", wine_dbgstr_point(&pos), hwnd, hittest); @@ -1434,22 +1445,14 @@ TrackMouseEvent (TRACKMOUSEEVENT *ptme) tracking_info->tme.dwFlags &= ~(ptme->dwFlags & ~TME_CANCEL);
/* if we aren't tracking on hover or leave remove this entry */ - if (!(tracking_info->tme.dwFlags & (TME_HOVER | TME_LEAVE))) + if (!(tracking_info->tme.dwFlags & TME_HOVER) && tracking_info->timer) { KillSystemTimer(tracking_info->tme.hwndTrack, tracking_info->timer); tracking_info->timer = 0; - tracking_info->tme.hwndTrack = 0; - tracking_info->tme.dwFlags = 0; - tracking_info->tme.dwHoverTime = 0; } + maybe_clean_tracking_info(tracking_info); } } else { - /* In our implementation it's possible that another window will receive a - * WM_MOUSEMOVE and call TrackMouseEvent before TrackMouseEventProc is - * called. In such a situation post the WM_MOUSELEAVE now */ - if (tracking_info->tme.dwFlags & TME_LEAVE && tracking_info->tme.hwndTrack != NULL) - check_mouse_leave(hwnd, hittest); - if (tracking_info->timer) { KillSystemTimer(tracking_info->tme.hwndTrack, tracking_info->timer); @@ -1459,7 +1462,9 @@ TrackMouseEvent (TRACKMOUSEEVENT *ptme) tracking_info->tme.dwHoverTime = 0; }
- if (ptme->hwndTrack == hwnd) + if (ptme->hwndTrack != hwnd) + PostMessageW(ptme->hwndTrack, WM_MOUSELEAVE, 0, 0); + else if (WIN_IsCurrentThread(ptme->hwndTrack)) { /* Adding new mouse event to the tracking list */ tracking_info->tme = *ptme; @@ -1468,7 +1473,8 @@ TrackMouseEvent (TRACKMOUSEEVENT *ptme) /* Initialize HoverInfo variables even if not hover tracking */ tracking_info->pos = pos;
- tracking_info->timer = SetSystemTimer(tracking_info->tme.hwndTrack, (UINT_PTR)&tracking_info->tme, hover_time, TrackMouseEventProc); + if (ptme->dwFlags & TME_HOVER) + tracking_info->timer = SetSystemTimer(tracking_info->tme.hwndTrack, (UINT_PTR)&tracking_info->tme, hover_time, TrackMouseEventProc); } }
diff --git a/dlls/user32/message.c b/dlls/user32/message.c index 406eff3..035f98a 100644 --- a/dlls/user32/message.c +++ b/dlls/user32/message.c @@ -2503,6 +2503,8 @@ static BOOL process_mouse_message( MSG *msg, UINT hw_id, ULONG_PTR extra_info, H else { msg->hwnd = WINPOS_WindowFromPoint( msg->hwnd, msg->pt, &hittest ); + /* TrackMouseEvent support */ + check_mouse_leave(msg->hwnd, hittest); }
if (!msg->hwnd || !WIN_IsCurrentThread( msg->hwnd )) diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 64c7967..483cba1 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -11938,6 +11938,11 @@ static const struct message WmMouseHoverSeq[] = { { 0 } };
+static const struct message WmMouseLeaveSeq[] = { + { WM_MOUSELEAVE, sent|wparam, 0 }, + { 0 } +}; + static void pump_msg_loop_timeout(DWORD timeout, BOOL inject_mouse_move) { MSG msg; @@ -11989,6 +11994,9 @@ static void test_TrackMouseEvent(void) HWND hwnd, hchild; RECT rc_parent, rc_child; UINT default_hover_time, hover_width = 0, hover_height = 0; + struct wnd_event wnd_event; + HANDLE hthread; + DWORD tid;
#define track_hover(track_hwnd, track_hover_time) \ tme.cbSize = sizeof(tme); \ @@ -11999,6 +12007,15 @@ static void test_TrackMouseEvent(void) ret = pTrackMouseEvent(&tme); \ ok(ret, "TrackMouseEvent(TME_HOVER) error %d\n", GetLastError())
+#define track_leave(track_hwnd) \ + tme.cbSize = sizeof(tme); \ + tme.dwFlags = TME_LEAVE; \ + tme.hwndTrack = track_hwnd; \ + tme.dwHoverTime = 0xdeadbeef; \ + SetLastError(0xdeadbeef); \ + ret = pTrackMouseEvent(&tme); \ + ok(ret, "TrackMouseEvent(TME_LEAVE) error %d\n", GetLastError()); + #define track_query(expected_track_flags, expected_track_hwnd, expected_hover_time) \ tme.cbSize = sizeof(tme); \ tme.dwFlags = TME_QUERY; \ @@ -12024,6 +12041,15 @@ static void test_TrackMouseEvent(void) ret = pTrackMouseEvent(&tme); \ ok(ret, "TrackMouseEvent(TME_HOVER | TME_CANCEL) error %d\n", GetLastError())
+#define track_leave_cancel(track_hwnd) \ + tme.cbSize = sizeof(tme); \ + tme.dwFlags = TME_LEAVE | TME_CANCEL; \ + tme.hwndTrack = track_hwnd; \ + tme.dwHoverTime = 0xdeadbeef; \ + SetLastError(0xdeadbeef); \ + ret = pTrackMouseEvent(&tme); \ + ok(ret, "TrackMouseEvent(TME_LEAVE | TME_CANCEL) error %d\n", GetLastError()) + default_hover_time = 0xdeadbeef; SetLastError(0xdeadbeef); ret = SystemParametersInfoA(SPI_GETMOUSEHOVERTIME, 0, &default_hover_time, 0); @@ -12120,8 +12146,8 @@ static void test_TrackMouseEvent(void) if (!tme.dwFlags) { skip( "Cursor not inside window, skipping TrackMouseEvent tests\n" ); - DestroyWindow( hwnd ); - return; + //DestroyWindow( hwnd ); + //return; } ok(tme.dwFlags == TME_HOVER, "wrong tme.dwFlags %08x, expected TME_HOVER\n", tme.dwFlags); ok(tme.hwndTrack == hwnd, "wrong tme.hwndTrack %p, expected %p\n", tme.hwndTrack, hwnd); @@ -12158,12 +12184,99 @@ static void test_TrackMouseEvent(void) track_hover(hwnd, HOVER_DEFAULT); track_query(TME_HOVER, hwnd, default_hover_time); track_hover_cancel(hwnd); + + mouse_event(MOUSEEVENTF_MOVE, 20, 20, 0, 0); // rc_child.left + 10, rc_child.top + 10 + //SetCursorPos(rc_child.left + 10, rc_child.top + 10); + flush_events(); + flush_sequence(); + + /* cancel TME_LEAVE */ + track_leave(hchild); + ok(GetLastError() == 0xdeadbeef, "not expected error %u\n", GetLastError()); + track_query(TME_LEAVE, hchild, 0); + track_leave_cancel(hchild); + track_query(0, NULL, 0); + flush_events(); + ok_sequence(WmEmptySeq, "WmEmptySeq", FALSE); + + /* track window under cursor */ + track_leave(hchild); + ok(GetLastError() == 0xdeadbeef, "not expected error %u\n", GetLastError()); + track_query(TME_LEAVE, hchild, 0); + ok(!GetCapture(), "expected NULL\n"); + mouse_event(MOUSEEVENTF_MOVE, -20, 0, 0, 0); // rc_child.left - 10, rc_child.top + 10 + //SetCursorPos(rc_child.left - 10, rc_child.top + 10); + flush_events(); + ok_sequence(WmMouseLeaveSeq, "WmMouseLeaveSeq", FALSE); + track_query(0, NULL, 0); + + /* window is not under cursor - immediate WM_MOUSELEAVE is expected */ + track_leave(hchild); + ok(GetLastError() == 0xdeadbeef, "not expected error %u\n", GetLastError()); + track_query(0, NULL, 0); + flush_events(); + ok_sequence(WmMouseLeaveSeq, "WmMouseLeaveSeq", FALSE); +return; + //SetCursorPos(rc_child.left + 10, rc_child.top + 10); + mouse_event(MOUSEEVENTF_MOVE, 20, 0, 0, 0); // rc_child.left + 10, rc_child.top + 10 + flush_events(); + flush_sequence(); + + /* move cursor outside top-window */ + track_leave(hchild); + track_query(TME_LEAVE, hchild, 0); + mouse_event(MOUSEEVENTF_MOVE, 500, 0, 0, 0); // rc_child.left + 510, rc_child.top + 10 + //SetCursorPos(rc_parent.left - 10, rc_parent.top - 10); + flush_events(); + ok_sequence(WmMouseLeaveSeq, "WmMouseLeaveSeq", FALSE); + track_query(0, NULL, 0);
DestroyWindow(hwnd);
+ /* Try tracking cursor over window from other thread - immediate WM_MOUSELEAVE */ + wnd_event.start_event = CreateEventW(NULL, 0, 0, NULL); + if (!wnd_event.start_event) + { + win_skip("CreateEventW failed\n"); + return; + } + hthread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid); + ok(hthread != NULL, "CreateThread failed, error %d\n", GetLastError()); + ok(WaitForSingleObject(wnd_event.start_event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); + CloseHandle(wnd_event.start_event); + SetCursorPos(150, 150); + ShowWindow(wnd_event.hwnd, SW_SHOW); + flush_events(); + flush_sequence(); + + /* window is under cursor */ + track_leave(wnd_event.hwnd); + ok(GetLastError() == 0xdeadbeef, "not expected error %u\n", GetLastError()); + track_query(0, 0, 0); + flush_events(); + ok_sequence(WmEmptySeq, "WmEmptySeq", FALSE); + + /* window is not under cursor */ + SetCursorPos(600, 150); + flush_events(); + flush_sequence(); + + track_leave(wnd_event.hwnd); + ok(GetLastError() == 0xdeadbeef, "not expected error %u\n", GetLastError()); + track_query(0, 0, 0); + flush_events(); + ok_sequence(WmMouseLeaveSeq, "WmMouseLeaveSeq", FALSE); + + ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0); + ok( ret, "PostMessageA(WM_QUIT) error %d\n", GetLastError()); + ok(WaitForSingleObject(hthread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); + CloseHandle(hthread); + #undef track_hover #undef track_query +#undef track_leave #undef track_hover_cancel +#undef track_leave_cancel }
@@ -16685,86 +16798,86 @@ START_TEST(msg) hCBT_hook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, 0, GetCurrentThreadId()); if (!hCBT_hook) win_skip( "cannot set global hook, will skip hook tests\n" );
- test_winevents(); - - /* Fix message sequences before removing 4 lines below */ - if (pUnhookWinEvent && hEvent_hook) - { - ret = pUnhookWinEvent(hEvent_hook); - ok( ret, "UnhookWinEvent error %d\n", GetLastError()); - pUnhookWinEvent = 0; - } - hEvent_hook = 0; - - test_SendMessage_other_thread(1); - test_SendMessage_other_thread(2); - test_InSendMessage(); - test_SetFocus(); - test_SetParent(); - test_PostMessage(); - test_broadcast(); - test_ShowWindow(); - test_PeekMessage(); - test_PeekMessage2(); - test_PeekMessage3(); - test_WaitForInputIdle( test_argv[0] ); - test_scrollwindowex(); - test_messages(); - test_setwindowpos(); - test_showwindow(); - invisible_parent_tests(); - test_mdi_messages(); - test_button_messages(); - test_static_messages(); - test_listbox_messages(); - test_combobox_messages(); - test_wmime_keydown_message(); - test_paint_messages(); - test_interthread_messages(); - test_message_conversion(); - test_accelerators(); - test_timers(); - test_timers_no_wnd(); - test_timers_exceptions(); - if (hCBT_hook) - { - test_set_hook(); - test_recursive_hook(); - } - test_DestroyWindow(); - test_DispatchMessage(); - test_SendMessageTimeout(); - test_edit_messages(); - test_quit_message(); - test_notify_message(); - test_SetActiveWindow(); + // test_winevents(); + + // /* Fix message sequences before removing 4 lines below */ + // if (pUnhookWinEvent && hEvent_hook) + // { + // ret = pUnhookWinEvent(hEvent_hook); + // ok( ret, "UnhookWinEvent error %d\n", GetLastError()); + // pUnhookWinEvent = 0; + // } + // hEvent_hook = 0; + + // test_SendMessage_other_thread(1); + // test_SendMessage_other_thread(2); + // test_InSendMessage(); + // test_SetFocus(); + // test_SetParent(); + // test_PostMessage(); + // test_broadcast(); + // test_ShowWindow(); + // test_PeekMessage(); + // test_PeekMessage2(); + // test_PeekMessage3(); + // test_WaitForInputIdle( test_argv[0] ); + // test_scrollwindowex(); + // test_messages(); + // test_setwindowpos(); + // test_showwindow(); + // invisible_parent_tests(); + // test_mdi_messages(); + // test_button_messages(); + // test_static_messages(); + // test_listbox_messages(); + // test_combobox_messages(); + // test_wmime_keydown_message(); + // test_paint_messages(); + // test_interthread_messages(); + // test_message_conversion(); + // test_accelerators(); + // test_timers(); + // test_timers_no_wnd(); + // test_timers_exceptions(); + // if (hCBT_hook) + // { + // test_set_hook(); + // test_recursive_hook(); + // } + // test_DestroyWindow(); + // test_DispatchMessage(); + // test_SendMessageTimeout(); + // test_edit_messages(); + // test_quit_message(); + // test_notify_message(); + // test_SetActiveWindow();
if (!pTrackMouseEvent) win_skip("TrackMouseEvent is not available\n"); else test_TrackMouseEvent();
- test_SetWindowRgn(); - test_sys_menu(); - test_dialog_messages(); - test_EndDialog(); - test_nullCallback(); - test_dbcs_wm_char(); - test_unicode_wm_char(); - test_menu_messages(); - test_paintingloop(); - test_defwinproc(); - test_clipboard_viewers(); - test_keyflags(); - test_hotkey(); - test_layered_window(); - test_TrackPopupMenu(); - test_TrackPopupMenuEmpty(); - test_DoubleSetCapture(); - /* keep it the last test, under Windows it tends to break the tests - * which rely on active/foreground windows being correct. - */ - test_SetForegroundWindow(); + // test_SetWindowRgn(); + // test_sys_menu(); + // test_dialog_messages(); + // test_EndDialog(); + // test_nullCallback(); + // test_dbcs_wm_char(); + // test_unicode_wm_char(); + // test_menu_messages(); + // test_paintingloop(); + // test_defwinproc(); + // test_clipboard_viewers(); + // test_keyflags(); + // test_hotkey(); + // test_layered_window(); + // test_TrackPopupMenu(); + // test_TrackPopupMenuEmpty(); + // test_DoubleSetCapture(); + // /* keep it the last test, under Windows it tends to break the tests + // * which rely on active/foreground windows being correct. + // */ + // test_SetForegroundWindow();
UnhookWindowsHookEx(hCBT_hook); if (pUnhookWinEvent && hEvent_hook) diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index d7cfada..ad1d9c6 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -233,6 +233,7 @@ struct tagWND; extern void CLIPBOARD_ReleaseOwner( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL FOCUS_MouseActivate( HWND hwnd ) DECLSPEC_HIDDEN; extern BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ) DECLSPEC_HIDDEN; +extern void check_mouse_leave(HWND hwnd, int hittest); extern void free_dce( struct dce *dce, HWND hwnd ) DECLSPEC_HIDDEN; extern void invalidate_dce( struct tagWND *win, const RECT *rect ) DECLSPEC_HIDDEN; extern void erase_now( HWND hwnd, UINT rdw_flags ) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index a0bfe05..cc8dac8 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -114,7 +114,7 @@ static x11drv_event_handler handlers[MAX_EVENT_HANDLERS] = X11DRV_ButtonRelease, /* 5 ButtonRelease */ X11DRV_MotionNotify, /* 6 MotionNotify */ X11DRV_EnterNotify, /* 7 EnterNotify */ - NULL, /* 8 LeaveNotify */ + X11DRV_LeaveNotify, /* 8 LeaveNotify */ X11DRV_FocusIn, /* 9 FocusIn */ X11DRV_FocusOut, /* 10 FocusOut */ X11DRV_KeymapNotify, /* 11 KeymapNotify */ diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 5ace405..092c381 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1447,6 +1447,7 @@ BOOL CDECL X11DRV_GetCursorPos(LPPOINT pos) *pos = root_to_virtual_screen( winX, winY ); TRACE( "pointer at %s server pos %s\n", wine_dbgstr_point(pos), wine_dbgstr_point(&old) ); } + return ret; }
@@ -1690,6 +1691,34 @@ BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *xev ) return TRUE; }
+/*********************************************************************** + * X11DRV_LeaveNotify + */ +BOOL X11DRV_LeaveNotify( HWND hwnd, XEvent *xev ) +{ + XCrossingEvent *event = &xev->xcrossing; + INPUT input; + + TRACE( "hwnd %p/%lx pos %d,%d detail %d\n", hwnd, event->window, event->x, event->y, event->detail ); + + if (event->detail == NotifyVirtual) return FALSE; + if (hwnd == x11drv_thread_data()->grab_hwnd) return FALSE; + + /* simulate a mouse motion event - needed for TrackMouseEvent */ + input.u.mi.dx = event->x; + input.u.mi.dy = event->y; + input.u.mi.mouseData = 0; + input.u.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; + input.u.mi.time = EVENT_x11_time_to_win32_time( event->time ); + input.u.mi.dwExtraInfo = 0; + + /* Note: not calling is_old_motion_event because leave message is not simulated when warping cursor */ + + send_mouse_input( hwnd, event->window, event->state, &input ); + return TRUE; +} + + #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
/*********************************************************************** diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index d35328c..2626c93 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -326,7 +326,7 @@ static int get_window_attributes( struct x11drv_win_data *data, XSetWindowAttrib attr->backing_store = NotUseful; attr->border_pixel = 0; attr->event_mask = (ExposureMask | PointerMotionMask | - ButtonPressMask | ButtonReleaseMask | EnterWindowMask | + ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | KeyPressMask | KeyReleaseMask | FocusChangeMask | KeymapStateMask | StructureNotifyMask); if (data->managed) attr->event_mask |= PropertyChangeMask; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 6786a08..1e65682 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -510,6 +510,7 @@ extern BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_ButtonRelease( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_MotionNotify( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; +extern BOOL X11DRV_LeaveNotify( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_DestroyNotify( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN;
Hi,
While running your changed tests on Windows, 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=34199
Your paranoid android.
=== wxppro (32 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== w2003std (32 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== wvistau64 (32 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== w2008s64 (32 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== w7u (32 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== w7pro64 (32 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== w8 (32 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== w864 (32 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== w1064 (32 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== wvistau64 (64 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== w2008s64 (64 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== w7pro64 (64 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== w864 (64 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407
=== w1064 (64 bit win) === win.c:6268: Test failed: CreateWindowEx error 1407