http://bugs.winehq.org/show_bug.cgi?id=59972 Bug ID: 59972 Summary: TrackMouseEvent(TME_LEAVE) fails to post WM_MOUSELEAVE when the tracked window is obscured by another application (Z-order flaw) Product: Wine Version: 11.12 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: win32u Assignee: wine-bugs@list.winehq.org Reporter: lldmakhbfcrmqyluns@nesopf.com Target Milestone: --- Distribution: --- When an application relies on TrackMouseEvent(TME_LEAVE) to hide custom tooltips or UI elements, the WM_MOUSELEAVE message is never generated if the application loses focus and the tracked window is obscured by another top-level window (e.g., via Alt+Tab), provided the mouse cursor does not geometrically leave the original window's rectangle. This causes hint/tooltip windows (which often have WS_EX_TOPMOST and HTTRANSPARENT) to remain permanently stuck on the screen, floating above native Linux applications or other Wine applications. This bug most prominently affects applications written in Delphi/VCL (like SAS.Planet), as VCL strictly relies on WM_MOUSELEAVE to trigger HideHint() and does not use the standard comctl32.dll tooltip class. Steps to Reproduce: Open a Delphi/VCL application that uses custom hint windows. Hover the mouse over a map object to trigger the custom tooltip. Without moving the mouse, press Alt+Tab to switch to another application (e.g., a native Linux web browser) so that it completely covers the Wine application. Move the mouse around inside the native Linux application. Expected Result: The tooltip should disappear. In native Windows, TrackMouseEvent evaluates the global Z-order. If another application obscures the tracked window, the mouse is no longer considered to be "over" the tracked window, and WM_MOUSELEAVE is posted. Actual Result: The tooltip stays visible indefinitely on top of the newly active application. Moving the mouse over the tooltip itself does nothing because it usually has HTTRANSPARENT. Deep Technical Analysis & Root Cause: We reverse-engineered the SYSTEM_TIMER_TRACK_MOUSE behavior and found an architectural flaw in how Wine determines if the mouse is still over the tracked window. The call chain is: TrackMouseEventProc (timer) -> update_mouse_tracking_info(hwnd) get_mouse_window(hwnd, ...) in dlls/win32u/input.c. window_from_point(hwnd, pos, ...) -> list_children_from_point(hwnd, ...) This triggers the wineserver request get_window_children_from_point with parent = tracked_hwnd. In server/window.c, all_windows_from_point calls is_point_in_window. The Flaw: is_point_in_window only checks 2D geometry (visible_rect). Because get_mouse_window restricts the scope to the tracked_hwnd instead of the desktop (0), the server completely ignores global Z-order. It does not check if a window from another process is currently obscuring this point. Since the cursor geometrically remains inside the original window's rect, get_mouse_window returns the tracked hwnd. Consequently, check_mouse_leave sees tracking->info.hwndTrack == hwnd and decides not to post WM_MOUSELEAVE. Important Note Regarding comctl32.dll (Notepad++ etc.): Please do not use applications like Notepad++ to test this specific TrackMouseEvent bug. Notepad++ uses TOOLTIPS_CLASS (comctl32.dll), which relies on TTM_RELAYEVENT and WM_MOUSEMOVE rather than TrackMouseEvent. comctl32 tooltips get stuck after Alt+Tab to a native Linux app for a entirely different reason: Wine stops receiving X11/Wayland mouse events when focus is lost, so comctl32 assumes the mouse hasn't moved. However, Delphi/VCL apps get stuck purely due to the TrackMouseEvent Z-order logic flaw described above. Proposed Workaround / Lightweight Fix: Ideally, TrackMouseEvent should check global Z-order (e.g., window_from_point(0, pos)). However, doing a full desktop-level Z-order server request inside a high-frequency timer loop causes performance issues and recursive bugs. A highly effective, lightweight user-space heuristic is to check if the foreground window has changed. If the foreground window is not an ancestor of the tracked window, we can safely assume the tracked window is obscured and fire WM_MOUSELEAVE. Here is a working patch for dlls/win32u/input.c that completely resolves the issue without server overhead: --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1711,6 +1711,24 @@ NtUserGetCursorPos( &pos ); ret = window_from_point( hwnd, pos, hittest, FALSE ); + + /* If another application is now in the foreground, the tracked window is + * likely covered. Fire WM_MOUSELEAVE so apps can hide tooltips etc. + * This fixes hint windows staying visible after Alt+Tab in Wine. */ + if (ret) + { + HWND foreground = NtUserGetForegroundWindow(); + if (foreground) + { + HWND tracked_top = NtUserGetAncestor( ret, GA_ROOT ); + if (tracked_top && foreground != tracked_top) + { + ret = NULL; + *hittest = HTNOWHERE; + } + } + } + if (ret && ret == tracking->last_mouse_message_hwnd) { *hittest = tracking->last_mouse_message_hittest; -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.