All Articles

Creating a Custom Auto-Locker

Background

Window Managers like i3, Awesome, Xmonad, Sway etc etc are great. But it does feel like reinventing the wheel sometimes with basic features that come included with full desktop environments.

As life starts to show hints that it might finally be returning to relative normal soon and we all brush up on our social skills and personal hygiene. I find myself thinking that it might be useful to have my screen auto-lock after a set period whilst I’m out and about.

There are already a couple of options for this, such as Xscreensaver but I don’t want a bloated screensaver, I just want to trigger a custom lock screen I’ve already built in AwesomeWM.

I figured I may as well document how I did it, so here we go.

Step 1: Check idle time

First thing to do is to ascertain how long the user has been idle. Fortunately this is relatively easy with Xprintidle, so let’s use that:

#!/bin/bash
IDLE_TIME="$(xprintidle)"
MAX_IDLE="180000" # 3 minutes

echo "Idle time is: $IDLE_TIME"

[ "$IDLE_TIME" -gt "$MAX_IDLE" ] && $(echo "lock_screen_show()" | awesome-client) || exit 0

This nice simple script I’ve called idle-check checks whether the idle time (in ms) is greater than the timeout. If it is, it calls the lock_screen_show function I have defined in my Awesome config.

Good start, but the big problem with this is that when I’m watching a video for example, I don’t want the script to trigger. We’ll solve that in the next step.

Step 2: Check if a fullscreen app is running.

Let’s create another script to check if a fullscreen application is running. We can do this using a combination of xwininfo and xdotool.

#!/bin/bash
root_geo="$(xwininfo -root | grep geometry)"
[ "$(xwininfo -id $(xdotool getactivewindow) | grep geometry)" = "$root_geo" ] && echo "1" || echo "0"

This script gets the resolution of the screen (root_geo) and uses xdotool to get the currently active window before comparing the geometry of said window to the max resolution. If they match, we know the currently active application is in fullscreen mode.

We can simply return 1 to represent fullscreen and 0 otherwise.

Now that we have this set up, we can add it to the check in our initial script:

#!/bin/bash
IDLE_TIME="$(xprintidle)"
MAX_IDLE="180000" # 3 minutes

echo "Idle time is: $IDLE_TIME"
+ echo "Fullscreen status is: $(is_fullscreen)"

- [ "$IDLE_TIME" -gt "$MAX_IDLE" ] && $(echo "lock_screen_show()" | awesome-client) || exit 0
+ [ "$IDLE_TIME" -gt "$MAX_IDLE" ] && [ "$(is_fullscreen)" -eq "0" ] && $(echo "lock_screen_show()" | awesome-client) || exit 0

Here, is_fullscreen is the name of our other script.

Almost there. We now have a script which, when run will check the idle time and, if greater than the specified timeout (and no fullscreen app is running) it will call the lock function.

The only thing left to do is run this script on an interval to constantly poll the status. To do this with cron just append * * * * * /path/to/your/script using crontab-e. However I’m going to be using Systemd timers, which I’ll detail in the next step.

Running the script every minute using Systemd

Create a service file in ~/.config/systemd/user called something like idle-check.service with the following:

[Unit]
Description=Check if user has been idle for more than 3 minutes and lock the screen
After=multi-user.target

[Service]
Environment=DISPLAY=:0
ExecStart=/path/to/your/idle-check/script

[Install]
WantedBy=basic.target

Then, create a timer to activate this service at the same location (e.g. idle-check.timer);

[Unit]
Description=Run idle check every minute

[Timer]
OnBootSec=2min
OnCalendar=*-*-* *:*:00
Unit=idle-check.service

[Install]
WantedBy=basic.target

Enable it with systemd --user enable idle-check.timer and you’re good to go!