Skip to main content

Lockscreen somewm-only

This guide covers setting up and customizing session locking, from basic usage to building a fully custom lockscreen.

Using the Default Lockscreen

The default configuration includes a lockscreen out of the box. In rc.lua:

-- After beautiful.init()
require("lockscreen").init()

Press Mod4 + Shift + Escape to lock. Enter your password and press Return to unlock.

Customizing Colors and Fonts

Via theme.lua

Add lockscreen_* variables to your theme:

theme.lockscreen_bg_color = "#000000"
theme.lockscreen_fg_color = "#ffffff"
theme.lockscreen_input_bg = "#1a1a1a"
theme.lockscreen_border_color = "#333333"
theme.lockscreen_error_color = "#ff4444"
theme.lockscreen_font = "monospace 14"
theme.lockscreen_font_large = "monospace bold 64"

Via the opts table

Pass options directly to override theme values:

require("lockscreen").init({
bg_color = "#1e1e2e",
fg_color = "#cdd6f4",
error_color = "#f38ba8",
clock_format = "%I:%M %p",
date_format = "%a, %b %d",
})

Choosing the Lock Screen

By default, the interactive UI appears on screen.primary. Use lock_screen to change this:

-- Always use screen 1
require("lockscreen").init({
lock_screen = screen[1],
})

-- Dynamic: use the currently focused screen
require("lockscreen").init({
lock_screen = function()
return awful.screen.focused()
end,
})

Setting Up Idle Timeouts

Use awesome.set_idle_timeout() to trigger actions after periods of inactivity:

-- Dim displays after 2 minutes
awesome.set_idle_timeout("dim", 120, function()
awesome.dpms_off()
end)

-- Lock after 5 minutes
awesome.set_idle_timeout("lock", 300, function()
awesome.lock()
end)

Multiple timeouts run independently. Each fires once per idle period and resets on activity.

To remove a timeout:

awesome.clear_idle_timeout("dim")

-- Or remove all
awesome.clear_all_idle_timeouts()

Inhibiting Idle

Set awesome.idle_inhibit = true to suppress idle timeouts from Lua. This is OR-ed with Wayland protocol inhibitors (e.g., from media players), so both sources must be inactive for idle to resume.

Inhibit while fullscreen

client.connect_signal("property::fullscreen", function()
local dominated = false
for _, c in ipairs(client.get()) do
if c.fullscreen then dominated = true; break end
end
awesome.idle_inhibit = dominated
end)

Inhibit for specific apps

client.connect_signal("property::fullscreen", function()
local dominated = false
for _, c in ipairs(client.get()) do
if c.fullscreen and (c.class == "firefox" or c.class == "okular") then
dominated = true
break
end
end
awesome.idle_inhibit = dominated
end)

Keybinding toggle

awful.key({ modkey }, "F8", function()
awesome.idle_inhibit = not awesome.idle_inhibit
end, {description = "toggle idle inhibition", group = "screen"})

Wibar idle status widget

Use property::idle_inhibited to build a reactive widget that shows whether idle is inhibited and by what:

local idle_widget = wibox.widget.textbox()

local function update_idle_widget()
if not awesome.idle_inhibited then
idle_widget.text = " "
return
end

-- Show which app is inhibiting (if any)
local inhibitors = awesome.inhibitors
if #inhibitors > 0 and inhibitors[1].client then
idle_widget.text = " " .. inhibitors[1].client.class
else
idle_widget.text = " "
end
end

update_idle_widget()
awesome.connect_signal("property::idle_inhibited", update_idle_widget)

DPMS Control

Keybinding

awful.key({ modkey }, "F7", function()
awesome.dpms_off()
end, {description = "turn off displays", group = "screen"})

Combining with Idle

-- Turn off displays after 3 minutes, lock after 5
awesome.set_idle_timeout("dpms", 180, function()
awesome.dpms_off()
end)

awesome.set_idle_timeout("lock", 300, function()
awesome.lock()
end)

Displays automatically wake on user activity.

Locking from the Command Line

somewm-client lock

This triggers awesome.lock() via IPC. Requires a lock surface to be registered (i.e., lockscreen.init() must have been called).

Locking on Suspend

Connect to the logind::prepare_sleep signal to lock before the system suspends:

awesome.connect_signal("logind::prepare_sleep", function(going_to_sleep)
if going_to_sleep then
awesome.lock()
end
end)

Building a Custom Lockscreen

For full control over the lock UI, use the raw Lock API instead of the default module:

local wibox = require("wibox")
local awful = require("awful")

-- Create lock surfaces
local lock_wb = wibox({
visible = false,
ontop = true,
type = "desktop",
})

-- Register as lock surface
awesome.set_lock_surface(lock_wb)

-- Handle multi-monitor: add covers for other screens
for s in screen do
if s ~= screen.primary then
local cover = wibox({
screen = s,
visible = false,
ontop = true,
bg = "#000000",
x = s.geometry.x,
y = s.geometry.y,
width = s.geometry.width,
height = s.geometry.height,
})
awesome.add_lock_cover(cover)
end
end

-- React to lock activation
awesome.connect_signal("lock::activate", function()
-- Show your surfaces, start keygrabber
lock_wb.visible = true
local password = ""

awful.keygrabber {
autostart = true,
keypressed_callback = function(_, _, key)
if key == "Return" then
awesome.authenticate(password)
awesome.unlock()
password = ""
elseif key == "BackSpace" then
password = password:sub(1, -2)
elseif #key == 1 then
password = password .. key
end
end,
}
end)

awesome.connect_signal("lock::deactivate", function()
lock_wb.visible = false
end)

Using an External Lock Client

SomeWM supports the ext-session-lock-v1 Wayland protocol, which lets external lockers like swaylock manage the lock session:

swaylock -c 000000

When an ext-session-lock client is active, awesome.lock() is blocked to prevent conflicts. Only one lock mechanism can be active at a time.

Troubleshooting

Lock fails silently

awesome.lock() returns false if:

  • No lock surface is registered (call lockscreen.init() first)
  • Already locked
  • An ext-session-lock client is active

Check with: somewm-client eval "return awesome.locked, awesome.lock_mechanism"

Wrong password accepted or rejected

Authentication uses PAM with the "login" service. Verify your PAM configuration:

# Test PAM outside SomeWM
su - $USER

Displays won't wake

Activity should auto-wake displays. If not:

  • Check DPMS state: somewm-client eval "return awesome.dpms_state"
  • Force wake: somewm-client eval "awesome.dpms_on()"

Idle timeouts not firing

  • Check Lua inhibition: somewm-client eval "return awesome.idle_inhibit"
  • Check overall state: somewm-client eval "return awesome.idle_inhibited"
  • Fullscreen clients with protocol inhibitors also suppress idle
  • List active timeouts: somewm-client eval "return awesome.idle_timeouts"

See Also