fix(start): intercept Start clicks on secondary-monitor taskbars#71
Conversation
The WH_MOUSE_LL hook only tested clicks against the primary taskbar's Start rect (Shell_TrayWnd), so with "show my taskbar on all displays" on, clicking Start on a secondary-monitor taskbar (Shell_SecondaryTrayWnd) always opened the native Windows Start menu instead of MenYou. - StartButtonLocator.Get() becomes GetAll(List<RECT>): the primary rect (existing logic, split into a per-tray GetStartRect helper) plus one rect per Shell_SecondaryTrayWnd found via a FindWindowExW loop. Each rect scales by ITS OWN tray's GetDpiForWindow — mixed-DPI monitors are the main real-world case for secondary taskbars. - Secondary taskbars are treated as left-aligned regardless of TaskbarAl: Win 11 doesn't center the icon group on secondary taskbars, so Start is their leftmost element there. (Needs a live multi-monitor confirmation — dev machine has a single display.) - A tray hwnd that dies mid-query yields an empty rect and is skipped, matching the existing failure convention. - StartClickHook keeps its per-event path allocation-free: two double-buffered rect lists (fill scratch, swap references), refreshed under the existing 250 ms throttle. The swallow logic and the SystemStartMenu.IsOpen pass-through are unchanged. Single-monitor behavior verified unchanged: GetAll returns exactly one rect, byte-identical to the UIA-reported Start button bounding rect. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 49 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| /// first; leaves it empty if no taskbar is found (Explorer not up). A tray | ||
| /// window that dies mid-query is skipped, not treated as fatal. Takes the | ||
| /// caller's list so the click hook can refresh without allocating. | ||
| public static void GetAll(List<RECT> results) |
| // Secondary taskbars don't center their icon group on Win 11: Start is | ||
| // the leftmost element there even when TaskbarAl says centered. | ||
| var secondary = IntPtr.Zero; | ||
| while ((secondary = FindWindowExW(IntPtr.Zero, secondary, "Shell_SecondaryTrayWnd", null)) != IntPtr.Zero) |
| private static extern IntPtr FindWindowW(string? lpClassName, string? lpWindowName); | ||
|
|
||
| [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] | ||
| private static extern IntPtr FindWindowExW(IntPtr hWndParent, IntPtr hWndChildAfter, string? lpszClass, string? lpszWindow); |
| if (_startRect.IsEmpty) return CallNextHookEx(IntPtr.Zero, code, wParam, lParam); | ||
| EnsureRectsFresh(); | ||
| var rects = _startRects; | ||
| if (rects.Count == 0) return CallNextHookEx(IntPtr.Zero, code, wParam, lParam); |
Follow-up to #67 (originally stacked on it; rebased onto
mainafter it merged in v0.8.13).Problem
The
WH_MOUSE_LLhook only tested clicks against the primary taskbar's Start rect (Shell_TrayWnd). With "show my taskbar on all displays" enabled, each secondary monitor gets its own taskbar (Shell_SecondaryTrayWnd) with its own Start button — clicking it always opened the native Windows Start menu instead of MenYou.Change
StartButtonLocator.Get()→GetAll(List<RECT>): the primary rect (the fix(start): DPI-scale the Start-button rect and default to centered alignment #67 logic, split into a per-trayGetStartRecthelper) plus one rect perShell_SecondaryTrayWndfound via aFindWindowExWloop. Each rect scales by its own tray'sGetDpiForWindow— mixed-DPI monitors are the main real-world case for secondary taskbars.TaskbarAl(Win 11 doesn't center the icon group on secondary taskbars, so Start is their leftmost element).StartClickHookstays allocation-free per event: two double-buffered rect lists (fill scratch, swap references), refreshed under the existing 250 ms throttle. Swallow logic and theSystemStartMenu.IsOpenpass-through unchanged.Verification
main.TaskbarAl=0):GetAllreturns exactly one rect,(0,907)-(55,955), byte-identical to the Start button bounding rect reported by UI Automation — single-monitor behavior is unchanged.(x*dpi+95)/96ceiling matches trueceil()at 96/120/144/150/192/240 DPI.The dev machine has a single display, so two claims are code-reviewed but not live-verified:
TaskbarAl=1.Quickest check: on a multi-monitor machine with taskbar on all displays, click Start on each monitor — MenYou should open every time, with no native menu flash.
🤖 Generated with Claude Code