24

Is there a way to run a specified script or program when the screen is locked or unlocked? Ideally a bash function or an AppleScript.

Is there an event that I could subscribe to that would allow me to do that? Or an existing app that supports this?

4
  • 4
    For the lockscreen, there's notification sent which can be handled in Your Cocoa app. You'd need to listen to notificaiton called: "com.apple.screenIsLocked". Code: [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"com.apple.screenIsLocked" object:nil]; Commented Dec 3, 2014 at 13:48
  • Is there a way to get a hold of this through AppleScript?
    – nwinkler
    Commented Dec 3, 2014 at 14:32
  • Sorry I don't know of Apple Script way of doing this. But creating Your own app for this task is not so difficult. Let me know if I can help. Commented Dec 3, 2014 at 14:49
  • 1
    As an addition notification sent when screen is unlocked is named: "com.apple.screenIsUnlocked" Commented Dec 4, 2014 at 8:24

7 Answers 7

17

After some searching, I found SleepWatcher, a free tool that allows to run a script when the screen goes to sleep or wakes up.

After installing it, you can link a shell script that is run on one of the available events (which include dim screen, wake up screen, computer sleep, wake up computer and some more). From the shell script, I can run a command, or call an AppleScript that I want to run on these events.

Since my screen goes to sleep when I lock my computer, this is exactly what I was looking for.

You can see it in action here: https://github.com/nwinkler/sleepwatcher-lync-status

5
  • 2
    This program could be installed with brew install sleepwatcher you can find more information about configuration in plist files. The exact location brew info sleepwatcher. Default files are ~/.sleep and ~/.wakeup
    – A B
    Commented Mar 18, 2018 at 4:49
  • I've a problem, it seems that if my mac locks before sleeping the script is not called. what parameters have you used?
    – EsseTi
    Commented Oct 7, 2019 at 9:15
  • Sorry, haven't used this in a while, TBH...
    – nwinkler
    Commented Oct 7, 2019 at 10:05
  • 1
    Under Monterey 12.5, I cannot get sleepwatcher to fire on wake/sleep. When I install it (brew install sleepwatcher) and then start it as a service (brew services start sleepwatcher or as global daemon with SUDO brew services start sleepwatcher) it will not fire on wakeup nor sleep. My .sleep and .wakeup scripts are OK and can be run manually. Also when I looked at the service within the 3rd party LaunchControl app, both instances of it are misconfigured which may mean the brew formula is broken. Also the sleepwatcher github has not been updated since 2014 so might not work with recent MacOS. Commented Aug 30, 2022 at 16:56
  • since Monterey update, each time I reboot, I get a dialog saying MacOs cant run unverified files, with 2 options, to uninstall sleepwatcher, or to cancel. Commented Nov 8, 2022 at 22:40
4

EventScript has a few options that might work for you. It's a neat little utility that can trigger .sh/applescript/automator workflows and trigger them on a host of different options.

Here's some screen locking/unlocking options: EventScripts

Check it out here.

2
  • 1
    That looks pretty useful, thanks! It's $3.99 in the App Store.
    – nwinkler
    Commented Dec 5, 2014 at 7:09
  • I have issues with EventScript crashing silently now, so just keep an eye on it...
    – Pylinux
    Commented May 4, 2018 at 7:50
4

Here's a simple script that I use when Karabiner fails on the lock screen. It turns the On Screen Keyboard on when the mac locks.

import Foundation

class ScreenLockObserver {
    init() {
        let dnc = DistributedNotificationCenter.default()

        // listen for screen lock
        let _ = dnc.addObserver(forName: NSNotification.Name("com.apple.screenIsLocked"), object: nil, queue: .main) { _ in
            NSLog("Screen Locked")
            self.runShortcut(shortcutName: "Set Accessibility Keyboard On")
        }

        // listen for screen unlock
        let _ = dnc.addObserver(forName: NSNotification.Name("com.apple.screenIsUnlocked"), object: nil, queue: .main) { _ in
            NSLog("Screen Unlocked")
            self.runShortcut(shortcutName: "Set Accessibility Keyboard Off")
        }

        RunLoop.main.run()
    }

    private func runShortcut(shortcutName: String) {
        let task = Process()
        task.launchPath = "/bin/bash"
        task.arguments = ["-c", "shortcuts run '\(shortcutName)'"]
        task.launch()
        task.waitUntilExit()
    }
}

let _ = ScreenLockObserver()

Save it to mac-lock.swift and run it with swift mac-lock.swift. Change the task.arguments to run a different command.

1
  • 2
    This was a great reminder that we can script stuff with Swift. Perfect. Commented Dec 1, 2023 at 14:13
1

Info for future googlers landing here:

I got this working using EventScripts.

I wanted my desk lamp, which is connected to a TP-link smart plug to turn on when I unlock my Mac, and to turn off when it locks.

I found this bash script which can turn the smart plug on and off. (I don't have nmap installed, so I had to figure out the IP address of the smart plug by looking at my router's config screens, then supply the IP address to the hs100.sh script via the -i option.)

Then it was just a matter of writing two simple scripts for "on" and "off", to call hs100.sh on and hs100.sh off. Since EventScripts is on the Mac App Store, it can only run scripts in its own folder, so I put my two scripts there.

And voilà! Works perfectly!

1

Check out this free and OSS project Hammerspoon (drop-in-app) and add this simple setup init.lua triggering:

  • ${HOME}/.lock
  • ${HOME}/.unlock

Note: used separate executable scripts for "portability" (but could have all-in init.lua).

    -- init.lua  
    local log = hs.logger.new("", "info")

    local function ok2str(ok)
        if ok then return "ok" else return "fail" end
    end

    hs.caffeinate.watcher.new(function(event)
        local eventName = hs.caffeinate.watcher[event]
        log.f("got caffeinate event:%s (id:%d)", eventName, event)
    
        local script

        if event == hs.caffeinate.watcher.screensDidLock then       
            script = "${HOME}/.lock"
        elseif event == hs.caffeinate.watcher.screensDidUnlock then
            script = "${HOME}/.unlock"
        else        
            log.f("ignored event:%s (id:%d)", eventName, event)
            return
        end
    
        local ok, st, n = os.execute(script)
        log.f("exec:%s -> %s, %s, %d", script, ok2str(ok), st, n)
    end
    ):start()

See docs at Hammerspoon docs: hs.caffeinate.watcher

1

Run arbitrary script/executable on lock/unlock on a Mac.

  1. Save this code1 as lockWatcher.swift2:
#!/usr/bin/env swift

import Foundation

class ScreenLockObserver {
    init() {
        let dnc = DistributedNotificationCenter.default()

        // listen for screen lock
        let _ = dnc.addObserver(forName: NSNotification.Name("com.apple.screenIsLocked"), object: nil, queue: .main) { _ in
            NSLog("Screen Locked")
            self.runBashScript(path: "~/bin/logout.sh")
        }

        // listen for screen unlock
        let _ = dnc.addObserver(forName: NSNotification.Name("com.apple.screenIsUnlocked"), object: nil, queue: .main) { _ in
            NSLog("Screen Unlocked")
            self.runBashScript(path: "~/bin/login.sh")
        }

        RunLoop.main.run()
    }

    private func runBashScript(path: String) {
        let task = Process()
        task.launchPath = "/bin/bash"
        task.arguments = ["-c", path]
        task.launch()
        task.waitUntilExit()
    }
}

let _ = ScreenLockObserver()
  1. Make lockWatcher.swift2 executable:
chmod +x lockWatcher.swift
  1. Run lockWatcher.swift2 on startup / login:
    1. Go to Settings -> General -> Login Items
    2. Click the + under the Open at Login list.
    3. And select the lockWatcher.swift2 file.

1 This answer is heavily based upon laktaks answer.
2 Change the file name to what ever you want

1
  • Clean and simple. Thank you.
    – Dave Land
    Commented Jun 10 at 22:14
-2

You can set it up like this:

sudo defaults write com.apple.loginwindow LoginHook /path/to/script
2
  • The name implies that this runs when you log in, not when you unlock the screen, though. Is that correct?
    – tripleee
    Commented Jun 11, 2019 at 10:45
  • stackoverflow.com/questions/22842016/… suggests that LoginHook is deprecated anyway. You can use launchd to run things at login, but that's not what this question is asking.
    – tripleee
    Commented Jun 12, 2019 at 5:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .