OSX: check if the screen is locked OSX: check if the screen is locked shell shell

OSX: check if the screen is locked


First, there's a bit of confusion in your question. Both Shift+Control+Eject and Energy Saver put the screens to sleep, which isn't the same thing as locking them. Depending on your other settings, this may also entail locking the screen, but that's a separate issue. IIRC, on Lion, by default, neither one will ever lock the screen—but if you leave the screen asleep for longer than the time set in Security & Privacy, that will lock it.

Anyway, the API CGSessionCopyCurrentDictionary allows you to get information about both screen sleep and screen lock, for your GUI session. If you don't have a GUI session (e.g., because you're running in an ssh shell), or your session doesn't own the console (e.g., because someone has fast-user-switched you out), you won't be able to get this information, but you will at least be able to detect those cases.

This is the only mechanism I know of that works for all OS's from 10.5 (actually 10.3) to 10.8 (but that doesn't mean it's the only one there actually is…).

There's no direct way to call this from bash or AppleScript. However, you can use your favorite bridge (PyObjC, MacRuby, ASOC, etc.) to call it indirectly. Here's an example using Python:

#!/usr/bin/pythonimport Quartzd = Quartz.CGSessionCopyCurrentDictionary()print d

Here's how to interpret the response:

  • If you get nothing back, then you don't have a UI session.
  • If the dictionary has kCGSSessionOnConsoleKey = 0, or not present, either your GUI session doesn't own the console, or the console's screens are asleep.
  • If the dictionary has CGSSessionScreenIsLocked = 1, the screens are locked.

The one problem case is where kCGSSessionOnConsoleKey is 0 (or missing) and CGSSessionScreenIsLocked is 1. In that case, either you've put the screens to sleep and locked them, or someone else has taken the console and locked the screens (with or without putting them to sleep). And I'm not sure if there's a way to distinguish between these cases. But if you're looking for "don't try to display a dialog because the user will have to unlock the screen first", both of those cases mean "don't display a dialog".

So, this should give you what you want:

#!/usr/bin/pythonimport sysimport Quartzd=Quartz.CGSessionCopyCurrentDictionary()sys.exit(d and          d.get("CGSSessionScreenIsLocked", 0) == 0 and          d.get("kCGSSessionOnConsoleKey", 0) == 1)

Or, turning it into a one-liner you can put directly in a shell script:

python -c 'import sys,Quartz; d=Quartz.CGSessionCopyCurrentDictionary(); sys.exit(d and d.get("CGSSessionScreenIsLocked", 0) == 0 and d.get("kCGSSessionOnConsoleKey", 0) == 1)'

Now, what if you've ssh'd into a Mac, and you're also currently logged into that Mac's GUI console (as the same user)? In that case, your ssh login session can communicate with the console login session in exactly the same way that a local Terminal login session would. So, CGSessionCopyCurrentDictionary is going to get the same values.

The bootstrap server that mediates that connection will apply some restrictions (e.g., security authorize -u foo should work from the Terminal but not over ssh), but those aren't fully documented, and change from version to version, so that's probably not something you want to rely on. Instead, you want to actually read your login session information

If you want to go further with this, start with reading Multiple User Environments Programming Topics. But some of the information isn't really documented anywhere (e.g., how the Mach-level sessions referenced by SessionGetInfo and the BSD-level sessions referenced by utmpx are tied together). Many of the relevant tools and libraries are open source, which may help. Even if reading up on all of that doesn't tell you how to do what you want, it will tell you exactly what you want, and the right terms to use to search and ask questions, which may be good enough.


The answer from @LevB is great but with the advent of Python "deprecation" in macOS (which has not yet seen its removal as of Big Sur) I wondered if there was another way to get CGSSessionScreenIsLocked, Applecare Enterprise Support pointed me to ioreg where these CoreGraphics Session values also live

Below are a couple shell function one-liners, in the shell style they return zero (0) in the affirmative (success) and non-zero (1) in the negative so they can be used as expected in if statements.

function screenIsLocked { [ "$(/usr/libexec/PlistBuddy -c "print :IOConsoleUsers:0:CGSSessionScreenIsLocked" /dev/stdin 2>/dev/null <<< "$(ioreg -n Root -d1 -a)")" = "true" ] && return 0 || return 1; }function screenIsUnlocked { [ "$(/usr/libexec/PlistBuddy -c "print :IOConsoleUsers:0:CGSSessionScreenIsLocked" /dev/stdin 2>/dev/null <<< "$(ioreg -n Root -d1 -a)")" != "true" ] && return 0 || return 1; }

Note these keys only exist when the screen is locked: kCGSSessionSecureInputPID, CGSSessionScreenLockedTime, CGSSessionScreenIsLocked. When the screen is unlocked CGSSessionScreenIsLocked is not present, thus the != "true" comparison, since it will never be "false"

Example usage (since the simplicity of if evaluating exit codes is sometimes not realized):

#!/bin/shfunction screenIsLocked { [ "$(/usr/libexec/PlistBuddy -c "print :IOConsoleUsers:0:CGSSessionScreenIsLocked" /dev/stdin 2>/dev/null <<< "$(ioreg -n Root -d1 -a)")" = "true" ] && return 0 || return 1; }function screenIsUnlocked { [ "$(/usr/libexec/PlistBuddy -c "print :IOConsoleUsers:0:CGSSessionScreenIsLocked" /dev/stdin 2>/dev/null <<< "$(ioreg -n Root -d1 -a)")" != "true" ] && return 0 || return 1; }if screenIsLocked; then    echo "Screen locked"fiif screenIsUnlocked; then    echo "Screen unlocked"fiif ! screenIsLocked; then    echo "Screen unlocked (inverse logic)"fiif ! screenIsUnlocked; then    echo "Screen locked (inverse logic)"fi


With jdk9 you can use

java.awt.DesktopDesktop tempDesktop = Desktop.getDesktop();        tempDesktop.addAppEventListener(new UserSessionListener() {            @Override            public void userSessionDeactivated(UserSessionEvent aE) {                LOG.info("Desktop:userSessionDeactivated Reason=" + aE.getReason() + " Source=" + aE.getSource());                // Windows Key L:                 // Tue Aug 31 11:22:49 CEST 2021:info:MainController:userSessionDeactivated Reason=LOCK                // Close Lid:                // Tue Aug 31 11:24:38 CEST 2021:info:MainController:userSessionDeactivated Reason=LOCK                // Tue Aug 31 11:24:39 CEST 2021:info:MainController:systemAboutToSleep Source=java.awt.Desktop@741f67cd                ptcUserStatus = PtcUserStatus.AWAY;            }            @Override            public void userSessionActivated(UserSessionEvent aE) {                LOG.info("Desktop:userSessionActivated Reason=" + aE.getReason() + " Source=" + aE.getSource());                // Logon after Windows Key L                // Tue Aug 31 11:22:53 CEST 2021:info:MainController:userSessionActivated Reason=LOCK                // Open Lid:                // Tue Aug 31 11:24:56 CEST 2021:info:MainController:systemAwoke Source=java.awt.Desktop@741f67cd                // Tue Aug 31 11:25:06 CEST 2021:info:MainController:userSessionActivated Reason=LOCK                ptcUserStatus = PtcUserStatus.BACK;            }        });