Search code examples
macosappkitmacos-big-sur

NSRunningApplication activateWithOptions method behavior change with Big Sur


I am using an application to select windows on MacOS Big Sur. (I have a slight hand tremor that causes me to often miss the intended target with mouse clicks.)

The app:

  • uses the AppKit "Accessibility for macOS" API to select windows. It calls the AXUIElementSetAttributeValue function to set the attribute NSAccessibilityMainAttribute to bring a particular window to the front and then
  • calls NSRunningAppplication's method activateWithOptions: passing NSApplicationActivateIgnoringOtherApps to activate the app associated with that window.

Unfortunately, I see a behavior change under Big Sur. Specifically, imagine I have two apps open, each with two windows:

  • AppX has windows:
    • X1 covering left half of my screen
    • X2 covering the right half of my screen
  • AppY has windows:
    • Y1 covering the left half of my screen
    • Y2 covering the right half of my screen

Starting with both X1 and X2 visible (on top):

  • before Big Sur I could select window Y2 which would making AppY the current app and leave me with windows X1 and Y2 visible
  • since upgrading to Big Sur when I select window Y2, the call to activateWithOptions: brings all of application AppY's windows forwards, showing only Y1 and Y2.

The same thing happens in the reverse situation (starting with Y1 and Y2 visible (on top), then trying to bring X1 forward and still see Y2).

The documentation for NSRunningApplication's activateWithOptions: method says:

By default, activation brings only the main and key windows forward. If you specify NSApplicationActivateAllWindows, all of the application's windows are brought forward." My application is not specifying NSApplicationActivateAllWindows.

Any suggestions on how the app should be handling this? (It's perfectly fine if you say, "The app is already doing the right thing," but I'd love to know if there is a reasonable work-around.)

[Aside: I'd also welcome any tips on the best way to bring this to Apple's attention.]


Solution

  • I've just managed to make my app work correctly again using the following code. It uses deprecated functions (as of 10.9) so I don't expect those functions to disappear soon. Not ideal, but at least it does not result in a weird user experience.

    - (OSStatus)makeApplicationFront:(NSRunningApplication*)application
    {
        if(!application || application.processIdentifier == -1) {
            return procNotFound; // Previous front most application is nil or does not have a process identifier.
        }
        ProcessSerialNumber process;
        OSStatus error = GetProcessForPID(application.processIdentifier, &process); // Deprecated, but replacement (NSRunningApplication:activateWithOptions:] does not work properly on Big Sur.
        if(error) {
            return error; // Process could not be obtained. Evaluate error (e.g. using osstatus.com) to understand why.
        }
        return SetFrontProcessWithOptions(&process, kSetFrontProcessFrontWindowOnly); // Deprecated, but replacement (NSRunningApplication:activateWithOptions:] does not work properly on Big Sur.
    }
    

    Hope it also helps you.