Skip to content

Commit 85fea77

Browse files
authored
fix(menubar): ignore clicks while the system menu bar is auto-hidden offscreen (#574)
When another application is running in macOS fullscreen mode and the system menu bar is auto-hidden, fast clicks at the top of the screen (before the menu bar visually reveals on hover) were hijacked by Thaw: with `Show on click` enabled the Thaw Bar popped open, and regardless of that setting the always-hidden section could be expanded offscreen. The two affected click paths in `HIDEventManager.handleShowOnClick` and `ControlItem.performAction` both lacked any visibility guard, and `NSScreen.getMenuBarHeight()` returns a cached value that keeps the existing geometry check in `isMouseInsideMenuBar` passing even when the menu bar window is no longer rendered. This change introduces a new uncached `NSScreen.isSystemMenuBarVisible()` helper that asks the Window Server whether any menu bar status items are currently on-screen for the active space via `Bridging.getMenuBarWindowList(option: [.onScreen, .activeSpace, .itemsOnly])`, and short-circuits both click handlers when the answer is no. The items list was chosen over `WindowInfo.menuBarWindow(for:)` after empirical testing on a fullscreen repro showed the menu bar window flips to `kCGWindowIsOnscreen` at the start of the reveal animation, well before the items become visible to the user; the items list more closely tracks the perceived reveal state. No precondition on `MenuBarManager.isMenuBarHiddenBySystem` is applied because `NSApp.currentSystemPresentationOptions` is per-app and reports `0` even when another app is fullscreen, so the visibility check is both necessary and sufficient. Clicks fall through normally once the user hovers and the menu bar fully repopulates, preserving expected behaviour inside fullscreen spaces. Fixes #481 Signed-off-by: Amir Zarrinkafsh <3339418+nightah@users.noreply.github.com>
1 parent 121a9c9 commit 85fea77

3 files changed

Lines changed: 37 additions & 0 deletions

File tree

Thaw/Events/HIDEventManager.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,18 @@ extension HIDEventManager {
741741
return
742742
}
743743

744+
// Suppress show-on-click when no menu bar status items are currently
745+
// rendered on-screen for the active space. This catches the case where
746+
// a fullscreen app has auto-hidden the menu bar and the click lands in
747+
// the top-of-screen trigger zone before the menu bar visually reveals.
748+
// NSApp.currentSystemPresentationOptions is per-app and does not
749+
// reflect another app's fullscreen state, so the items-list signal is
750+
// used directly without a precondition.
751+
if !screen.isSystemMenuBarVisible() {
752+
Self.diagLog.debug("handleShowOnClick: suppressing, no menu bar items on-screen for active space")
753+
return
754+
}
755+
744756
guard isMouseInsideEmptyMenuBarSpace(appState: appState, screen: screen) else {
745757
return
746758
}

Thaw/MenuBar/ControlItem/ControlItem.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,18 @@ final class ControlItem {
557557
}
558558
let menuBarManager = appState.menuBarManager
559559

560+
// Suppress phantom clicks delivered to the status item button while
561+
// no menu bar items are rendered on-screen for the active space. This
562+
// catches fast top-of-screen clicks during the menu bar reveal
563+
// sequence under a fullscreen app, which would otherwise expand the
564+
// hidden section offscreen. NSApp.currentSystemPresentationOptions is
565+
// per-app and does not reflect another app's fullscreen state, so the
566+
// items-list signal is used directly.
567+
let screenForCheck = window?.screen ?? NSScreen.main
568+
if let screen = screenForCheck, !screen.isSystemMenuBarVisible() {
569+
return
570+
}
571+
560572
switch event.type {
561573
case .leftMouseDown:
562574
// Capture modifier flags from the event to ensure we have the state

Thaw/Utilities/Extensions.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,19 @@ extension NSScreen {
749749
return fallback
750750
}
751751

752+
/// Returns true when at least one menu bar status item is currently
753+
/// rendered on-screen for the active space.
754+
///
755+
/// Returns false when the menu bar is auto-hidden behind a fullscreen app
756+
/// and not yet visually revealed. The menu bar window itself flips to
757+
/// kCGWindowIsOnscreen at the start of the reveal sequence, well before
758+
/// the status items become visible to the user; gating on the items list
759+
/// more closely matches the perceived reveal state, which is what click
760+
/// suppression needs.
761+
func isSystemMenuBarVisible() -> Bool {
762+
!Bridging.getMenuBarWindowList(option: [.onScreen, .activeSpace, .itemsOnly]).isEmpty
763+
}
764+
752765
/// Returns the raw frame of the application menu on this screen, as
753766
/// reported by the Accessibility API, without any notch-capping applied.
754767
///

0 commit comments

Comments
 (0)