Search code examples
macosapplescriptautomator

Trigger an apple script / automator when an application comes to focus in mac


I want to automate the keyboard layout based on the application. I am using an Windows VM over remote I wan't to create a script which can:

  1. Change the Keyboard layout when the application switches to Windows VM
  2. Change it back to normal layout with all other apps.

I have hard time finding a trigger event on application focus change. Is it possible to do it over automator?

Or else kindly suggest another solution to resolve the issue.


Solution

  • Assuming that you only have two keyboard layouts, you can use the following script application.

    First, you'll need to open System Preferences and look at Keyboard➔Shortcuts➔Input Sources; see what the what the keyboard shortcut for 'selecting previous input source' is, make sure it's clicked on, and copy it into the script at the marked places. I have mine set to comd-ctrl-space (^⌘ ), but yours is likely different. You should also check that "Windows VM" is the correct name for the app: find the application in the Finder, select it and type ⌘I to open a Get Info window, and look in the Name & Extension text box.

    Now do the following

    1. Copy the following script in the Script Editor
    2. Save it as a stay-open application
      • select 'Application' from the 'Format' menu at the bottom left
      • click the 'Stay open after run handler' checkbox
    3. Open System Preferences and do the following
      • add the app to the Accessibility section in Security & Privacy➔Privacy (so it can send keyboard shortcuts)
      • add the app to the Login Items section for your account in Users & Groups (so it starts automatically at login)

    You can then run it and test it out. The app waits in the background to receive notifications that an app has activated or deactivated, then it checks the app name and triggers the keyboard shortcut for the switching the input source when the Window VM app comes or goes.

    use AppleScript version "2.4"
    use framework "AppKit"
    use scripting additions
    
    property NSWorkspace : class "NSWorkspace"
    
    on run
        set workSp to NSWorkspace's sharedWorkspace()
        set notifCent to workSp's notificationCenter()
        tell notifCent to addObserver:me selector:"appHasActivated:" |name|:"NSWorkspaceDidActivateApplicationNotification" object:(missing value)
        tell notifCent to addObserver:me selector:"appHasDeactivated:" |name|:"NSWorkspaceDidDeactivateApplicationNotification" object:(missing value)
    end run
    
    on idle
        -- we don't use the idle loop, so tell the system to check in once an hour
        return 3600
    end idle
    
    on appHasActivated:notif
        set targetApp to (notif's userInfo's valueForKey:"NSWorkspaceApplicationKey")
        set targetAppName to (targetApp's localizedName) as text
        if targetAppName is "Windows VM" then
            tell application "System Events"
                tell process "Windows VM"
                    -- change this line to match your 'Select previous input source' keyboard shortcut
                    keystroke space using {command down, control down}
                end tell
            end tell
        end if
    end appHasActivated:
    
    on appHasDeactivated:notif
        set targetApp to (notif's userInfo's valueForKey:"NSWorkspaceApplicationKey")
        set targetAppName to (targetApp's localizedName) as text
        if targetAppName is "Windows VM" then
            tell application "System Events"
                tell process targetAppName
                    -- change this line to match your 'Select previous input source' keyboard shortcut
                    keystroke space using {command down, control down}
                end tell
            end tell
        end if
    end appHasDeactivated:
    

    Once you are satisfied it works, you can run the following command in terminal:

    defaults write '/path/to/$name.app/Contents/Info.plist' LSUIElement -bool yes

    This will turn the app into a background agent, which never takes the foreground and is invisible in the dock. That way you can just forget about it.