Skip to content

Fix game resuming in Steam Big Picture mode (#276) - #283

Open
rapheljr wants to merge 1 commit into
NotProtonNot:mainfrom
rapheljr:feat/big-picture-resume
Open

rapheljr wants to merge 1 commit into
NotProtonNot:mainfrom
rapheljr:feat/big-picture-resume

Conversation

@rapheljr

@rapheljr rapheljr commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Resolves #276

Root Cause Analysis

In Steam Big Picture mode on macOS, clicking Resume Game deactivates the Big Picture UI and attempts to reactivate the game by calling ActivateApplication(pid) / ActivateProcess(pid) (in steamui.dylib and steamclient.dylib):

  1. Steam executes:
    [NSRunningApplication runningApplicationWithProcessIdentifier:tracked_pid] activateWithOptions:NSApplicationActivateIgnoringOtherApps
  2. For native macOS games, Steam tracks the game's GUI application process directly, which has an NSRunningApplication, so window activation succeeds.
  3. For NotProton games, Steam spawns and tracks /bin/sh -c '...compat_run.sh waitforexitandrun ...'. Because /bin/sh is a non-GUI CLI process (not an application bundle), runningApplicationWithProcessIdentifier:sh_pid returns nil.
  4. Steam then dispatches [nil activateWithOptions:2], which silently returns NO. Steam deactivates Big Picture, but WindowServer never brings the game forward, dropping focus to whatever inactive background window was behind Big Picture ("switching to the non-active application").

Solution

  1. dylib/hooks/hook_activate.c:
    • Hook +[NSRunningApplication runningApplicationWithProcessIdentifier:].
    • If orig_running_app returns a valid NSRunningApplication, return it immediately (zero impact on native apps or already-recognized processes).
    • If orig_running_app returns nil for pid > 1:
      • Process Tree Resolution: Search descendant processes via proc_listchildpids() breadth-first to find child GUI processes like wine.app / wine64-preloader.
      • Launcher Bundle Fallback: When games are launched via open -n -W -a "$loader_app" (where launchd reparents the app to PID 1), inspect [[NSWorkspace sharedWorkspace] runningApplications] and resolve the newest, non-terminated application matching com.notproton.launcher.*.
  2. overlay-shim/overlay_shim.m:
    • When the overlay deactivates (np_overlay_shim_set_active(0)), dispatch [[NSApplication sharedApplication] activateIgnoringOtherApps:YES] on the main thread inside Wine so the Cocoa game window regains key focus.
  3. dylib/tests/activatecheck.c:
    • Add unit test coverage verifying:
      • Hook installation on NSRunningApplication.
      • Direct PID lookups returning original application.
      • Rejection of invalid PIDs (0, negative).
      • Child and multi-level descendant resolution via proc_listchildpids.
      • Fallback resolution of com.notproton.launcher.* applications with terminated filter and launch timestamp comparison.
      • Proper return of nil for unrelated processes.

Verification

All existing and new test suites pass with 0 failures:

  • make -s activatecheck: passed (100%)
  • make -s overlay-shim-tests: 10 passed, 0 skipped (100%)
  • make -s launchercheck: all passed
  • make -s envcheck: all passed
  • make -s defaults-check: all passed
  • make -s runcheck: all passed
  • make -s scriptcheck: 28 scripts lint and parse clean
  • Clean full rebuild (make clean && make -s all) succeeds and codesigns properly.

@NotProtonNot

Copy link
Copy Markdown
Owner

Thank you for this! This is getting merged!

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.

resuming broken in steam big picture mode

2 participants