Search code examples
macosapplescriptfocusaccessibilityappkit

How do I determine the window with the active keyboard focus using ScriptingBridge (or AppleScript)?


From all the API documentation I can find, it seems that the right thing to do is to check the "frontmost" window as returned by System Events or the accessibility API, like so (example in Python here, but this is the same in ObjC or swift or ruby or whatever):

#!/usr/bin/env python
from ScriptingBridge import SBApplication
events = SBApplication.applicationWithBundleIdentifier_(
    "com.apple.systemevents")
for proc in events.applicationProcesses():
    if proc.frontmost():
        print(proc.name())

The value I get back from this is the same as from NSWorkspace.sharedWorkspace().frontmostApplication(). And it's usually correct. Except when a prompt dialog, especially one from the system, is actually what has the keyboard focus. For example, if Messages.app wants a password to my Jabber account, or if my iCloud password changes; these dialogs appear to be coming from the UserNotificationCenter process, which does not report itself as the frontmost application somehow, even though it definitely has keyboard focus.


Solution

  • "UserNotificationCenter" and "UserNotificationCenter" are background applications (the NSUIElement key is 1 in the info.plist).

    proc.frontmost() is always false on process which is in background (no menu and not in the Dock).

    And NSWorkspace.sharedWorkspace().frontmostApplication() doesn't work on background application.


    To get the active application, use the activeApplication method from the NSWorkspace class

    Here's the AppleScript:

    set pyScript to "from AppKit import NSWorkspace
    activeApp = NSWorkspace.sharedWorkspace().activeApplication()
    print activeApp['NSApplicationName'].encode('utf-8')
    print activeApp['NSApplicationProcessIdentifier']"
    
    set r to do shell script "/usr/bin/python -c " & quoted form of pyScript
    set {localizedAppName, procID} to paragraphs of r -- procID is the unix id
    

    Update with a not deprecated method:

    set pyScript to "from AppKit import NSWorkspace
    for app in NSWorkspace.sharedWorkspace().runningApplications():
            if app.isActive(): 
                    print app.localizedName().encode('utf-8')
                    print app.processIdentifier()
                    break"
    
    set r to do shell script "/usr/bin/python -c " & quoted form of pyScript
    set {localizedAppName, procID} to paragraphs of r -- procID is the unix id
    

    To get the front window from a process ID, use the procIDvariable, like this:

    tell application "System Events"
        tell (first process whose unix id = procID)
            log (get properties) -- properties of this process
            tell window 1 to if exists then log (get properties) -- properties of the front window of this process
        end tell
    end tell