Skip to content

fix(reactor): drive auto-focus workspace switch from NSWorkspace activations#403

Closed
bhedavivek wants to merge 1 commit into
acsandmann:mainfrom
bhedavivek:fix/auto-focus-workspace-switch-on-globally-activated
Closed

fix(reactor): drive auto-focus workspace switch from NSWorkspace activations#403
bhedavivek wants to merge 1 commit into
acsandmann:mainfrom
bhedavivek:fix/auto-focus-workspace-switch-on-globally-activated

Conversation

@bhedavivek

@bhedavivek bhedavivek commented Jun 22, 2026

Copy link
Copy Markdown

Closes #402.

Change

Event::ApplicationGloballyActivated, sent by the wm_controller when NSWorkspace fires didActivateApplication, now also dispatches to AppEventHandler::handle_application_activated. The login-window short-circuit still runs first; the new call only fires for non-login activations. Quiet::No is passed.

Before this change, handle_application_activated is only invoked from Event::ApplicationActivated, which originates in actor::app::on_activation_changed. After this change, Cmd+Tab and dock activations of an app on another virtual workspace switch the active workspace.

The activated app's bundle id is now resolved from app_manager.apps instead of NSRunningApplication::with_process_id. The reactor already records every running app's AppInfo when ApplicationLaunched is processed, and NSRunningApplication is not reachable from unit tests. The early returns when the app is not tracked or has no CFBundleIdentifier preserve the existing observable behavior.

Tests

Six new unit tests in src/actor/reactor/tests.rs covering the positive switch path and each early-exit:

  • globally_activated_app_on_other_workspace_switches_to_apps_workspace — activating an app pinned to a different workspace switches the active workspace to that workspace.
  • globally_activated_app_on_visible_workspace_does_not_switch — exits at app_is_on_visible_workspace.
  • globally_activated_blacklisted_app_does_not_switch_workspace — exits at the auto_focus_blacklist check.
  • globally_activated_login_window_does_not_drive_auto_switch — login-window short-circuit fires; new dispatch is skipped.
  • globally_activated_app_without_bundle_id_does_not_switch — app tracked by rift but with no CFBundleIdentifier exits at the bundle-id check.
  • globally_activated_untracked_pid_does_not_switch — pid not present in app_manager.apps exits at the lookup.

cargo test --lib actor::reactor — 57 passing (was 51 before this PR).

Manual repro

macOS 26.5 / arm64, single display. Built cargo build --release, pointed ~/Library/LaunchAgents/git.acsandmann.rift.plist at the patched binary, granted Accessibility to the new binary path, restarted via launchctl bootout + bootstrap. With these app_rules:

app_rules = [
  { app_id = "com.tinyspeck.slackmacgap",  workspace = 1 },
  { app_id = "us.zoom.xos",                workspace = 2 },
  { app_id = "company.thebrowser.Browser", workspace = 4 },
  { app_id = "com.mitchellh.ghostty",      workspace = 5 },
]

Starting from workspace 0, Cmd+Tab switched the active workspace as expected for every app with a CFBundleIdentifier and a current AXStandardWindow:

  • Cmd+Tab to Slack → active workspace becomes 1.
  • Cmd+Tab to Arc → active workspace becomes 4.
  • Cmd+Tab to Ghostty → active workspace becomes 5.
  • Cmd+Tab back to OpenCode → active workspace becomes 0.

Two cases did not switch, matching the existing limits of handle_app_activation_workspace_switch:

  • An app without a CFBundleIdentifier (a Cargo debug binary). Exits at the bundle-id check.
  • An app with no current AXStandardWindow (Zoom between meetings). Exits at the app_window_id lookup.

Manual Alt+N switching, switch_to_last_workspace, and MoveWindowToWorkspace continue to work unchanged. Not tested on multi-display, scrolling, or master-stack.

@bhedavivek bhedavivek force-pushed the fix/auto-focus-workspace-switch-on-globally-activated branch 4 times, most recently from ce108af to 5e07f25 Compare June 22, 2026 22:35
…vations

Route Event::ApplicationGloballyActivated through
AppEventHandler::handle_application_activated after the login-window
short-circuit, so Cmd+Tab and dock activations drive the same
auto-switch logic as the per-app AXApplicationActivated path. The
auto-switch logic is idempotent via its existing guards
(active_workspace_switch, manual_switch_in_progress,
app_is_on_visible_workspace), so double-firing with the per-app path
is safe.

Resolve the activated app's bundle id from `app_manager.apps` instead
of `NSRunningApplication::with_process_id`. The reactor already records
every running app's `AppInfo` when `ApplicationLaunched` is processed,
and `NSRunningApplication` is not reachable from unit tests. The early
returns when the app is not tracked or has no `CFBundleIdentifier`
preserve the existing observable behavior.

Six new tests in `actor::reactor::tests`, covering each early-exit and
the positive switch path:

- globally_activated_app_on_other_workspace_switches_to_apps_workspace
- globally_activated_app_on_visible_workspace_does_not_switch
- globally_activated_blacklisted_app_does_not_switch_workspace
- globally_activated_login_window_does_not_drive_auto_switch
- globally_activated_app_without_bundle_id_does_not_switch
- globally_activated_untracked_pid_does_not_switch

Closes acsandmann#402.
@bhedavivek bhedavivek force-pushed the fix/auto-focus-workspace-switch-on-globally-activated branch from 5e07f25 to 727204c Compare June 22, 2026 22:40
@bhedavivek

Copy link
Copy Markdown
Author

closing as a dup of #400

@bhedavivek bhedavivek closed this Jun 23, 2026
@bhedavivek bhedavivek deleted the fix/auto-focus-workspace-switch-on-globally-activated branch June 23, 2026 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto-focus workspace switching never fires on AppGloballyActivated (v0.4.3)

1 participant