Skip to content

Instantly share code, notes, and snippets.

@memreflect
Last active April 8, 2024 03:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save memreflect/96a823cce272c829f2956b7999f85dbd to your computer and use it in GitHub Desktop.
Save memreflect/96a823cce272c829f2956b7999f85dbd to your computer and use it in GitHub Desktop.
Sway/Wayland screen locker

Requirements

  • swayidle
  • swaylock
  • any shell supporting sh syntax and execution of tasks in the background (job control is not necessary); just make sure that you fix the path to the shell at the top of the script if it isn't /bin/sh

Installation

Install the script somewhere in your PATH such as $HOME/bin and ensure it is executable. Then you just need to modify your compositor's config to execute it on a key press and/or automatically or whatever you want.

Examples

Here's a modified version of my personal Sway config:

# Run screen locker on $mod+Ctrl+l
bindsym $mod+Ctrl+l exec way_lock

# Turn off screen after 5 minutes of inactivity.
# Screen is not automatically locked except when going to sleep.
exec swayidle -w \
        timeout 300  'swaymsg output \* dpms off' \
        resume       'swaymsg output \* dpms on' \
        before-sleep 'swaymsg exec way_lock'

If you prefer automatic locking, you can still take advantage of the different timeouts in your own config if you want:

# Lock screen after 5 minutes of inactivity and when going to sleep.
# way_lock already handles DPMS on/off, so there's no need for it here.
exec swayidle -w \
        timeout 300  'swaymsg exec way_lock' \
        before-sleep 'swaymsg exec way_lock'

Why not just use swayidle on its own?

I have been using XSecureLock for a couple of years on my PCs prior to trying Sway or anything else to do with Wayland, and I have not needed an automatic screen locker. If I am using the machine, there is no need for the screen to be locked. If I am at home and away for a bit making some tea or whatever, the screen does not need to be locked. Sometimes I even stream video to my TV, so I'm not actively using the machine, but I still want to be able to use it for controlling media playback without the need to unlock the machine just to tap a key.

However, just because my machine may remain unlocked, that does not mean I want my screen to be on all the time. As a result, I have a DPMS timeout of 5 minutes set using xset s on; xset s 300 in my $HOME/.xinitrc.

But when I manually lock my screen, there's no reason to leave the screen on for the full 5 minutes, especially when XSecureLock allows me to specify a much shorter time limit before the screen turns off. When I decided to try Sway, this behavior was one thing I quickly missed.

swayidle is simple enough to understand how to use, but I essentially wanted a second DPMS timeout associated specifically with screen locking, which swayidle can only do by executing a second instance of swayidle with the desired timeout. And because swayidle acts like a daemon, this temporary swayidle instance needs to be stopped.

The daemon-like behavior, combined with my requirements, makes the problem complex enough that adding the functionality into my Sway config directly becomes even more of a problem due to nested quotation marks. It's much easier to customize and work with an individual script as there is no need to reload my Sway config to see the result of the changes.

#!/bin/sh
###
# way_lock
# Lock Wayland session
#
# Run-Depends
# x11/swayidle
# x11/swaylock
# {any /bin/sh capable of background execution}
#
# Note: swayidle implements KDE's idle protocol[1], so the compositor
# acting as a window manager must support it. Swayidle will not
# work as intended otherwise.
# [1] https://github.com/swaywm/sway/blob/57d6f6f19e3088dcb8e202acade8c39a80075b4a/protocols/idle.xml
###
# Number of seconds before screen turns off
timeout=10
# Turn screen off after $timeout seconds of inactivity.
# Turn it on again when there is activity.
#
# swayidle acts like a daemon, meaning it continues execution even after
# the script is terminated, so we need to execute it in the background
# to be able to terminate it later (unless you like your screen turning
# off every 10 seconds for some reason?)
swayidle \
timeout $timeout 'swaymsg output \* dpms off' \
resume 'swaymsg output \* dpms on' \
&
# Lock the screen and wait for it to be unlocked.
swaylock
# Screen unlocked: terminate swayidle and clean up PID
kill -TERM $!
wait
@fritzrehde
Copy link

Thanks a lot, this was very helpful!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment