garlock
Screen Locker
A secure PAM-authenticated screen locker with blurred screenshot backgrounds and visual ring feedback. garlock provides comprehensive input protection with keyboard/pointer grabs and secure password handling using memory zeroing.
Features
- PAM authentication with configurable service
- Secure password handling with zeroize memory clearing
- Blurred screenshot background for privacy
- Animated ring indicator with 5 visual states
- Multi-monitor support via RandR
- Caps Lock and failed attempt warnings
- Configurable cooldown after failed attempts
- Daemon mode with IPC socket control
- Time display with strftime formatting
- Full Unicode password input support
- Keyboard and pointer grab for security
- TOML configuration at ~/.config/garlock/config.toml
Security Model
Memory Security
Passwords zeroed from memory using zeroize crate. No password remnants after authentication.
Input Grabbing
X11 keyboard and mouse grabbed. No input reaches other applications during lock.
PAM Integration
Uses system PAM stack. Same authentication as system login, supports 2FA and custom modules.
Quick Start Guide
Get garlock installed and configured for secure screen locking.
1. Installation
Install garlock using the unified installer or build from source:
2. PAM Configuration
Create a PAM service file for garlock (requires root):
#%PAM-1.0
# PAM configuration for garlock screen locker
auth required pam_unix.so 3. Running garlock
Lock your screen directly or run in daemon mode:
4. Integration with gar
Bind a key in gar to lock the screen:
-- Lock screen with Super+L
gar.bind("mod+l", "exec garlock")
-- Or lock via shell command
gar.bind("mod+l", function()
gar.exec("garlock lock")
end) 5. Configuration
Create a configuration file at ~/.config/garlock/config.toml:
6. Testing
Test garlock safely with Xephyr:
Warning: When testing on your main display, ensure you have TTY access (Ctrl+Alt+F2) as a backup in case of issues.
Configuration Reference
garlock uses TOML configuration at ~/.config/garlock/config.toml.
Minimal Configuration
[background]
blur_radius = 25.0
brightness = 0.6
[indicator]
show_time = true
time_format = "%H:%M" Full Configuration Example
# ═══════════════════════════════════════════
# General Settings
# ═══════════════════════════════════════════
[general]
grace_period = 0 # Reserved for future use
pam_service = "garlock" # Reserved for future use (currently always "garlock")
max_attempts = 3 # Failed attempts before cooldown starts
cooldown_seconds = 5 # Base cooldown duration (multiplied by excess attempts)
# ═══════════════════════════════════════════
# Background Settings
# ═══════════════════════════════════════════
[background]
blur_radius = 25.0 # Gaussian blur radius (0 = disabled, higher = more blur)
brightness = 0.6 # Brightness factor (0.0 = black, 1.0 = original)
fallback_color = "#1a1a2e" # Solid color if screenshot capture fails
# ═══════════════════════════════════════════
# Ring Indicator
# ═══════════════════════════════════════════
[ring]
# Ring geometry
radius_outer = 90.0 # Outer ring radius in pixels
radius_inner = 75.0 # Inner circle radius
line_width = 6.0 # Ring stroke width
# State colors (hex with optional alpha: #RRGGBB or #RRGGBBAA)
color_idle = "#1e90ffcc" # Blue: waiting for input
color_typing = "#00ff00cc" # Green: receiving password
color_verifying = "#ffa500cc" # Orange: checking with PAM
color_wrong = "#ff0000cc" # Red: authentication failed
color_clear = "#ffff00cc" # Yellow: backspace/clear
# Background colors
color_inside = "#00000088" # Inner circle fill (dark transparent)
color_ring_bg = "#00000055" # Ring background track
# ═══════════════════════════════════════════
# Text Overlays
# ═══════════════════════════════════════════
[indicator]
# Caps Lock warning
show_caps_lock = true # Display "Caps Lock" when active
caps_lock_text = "Caps Lock" # Custom warning text
# Failed attempts
show_failed_attempts = true # Display failed attempt count
# Time display
show_time = true # Display current time above ring
time_format = "%H:%M" # strftime format (24-hour default)
# ═══════════════════════════════════════════
# Font Settings
# ═══════════════════════════════════════════
[font]
family = "Sans" # Font family (Pango format)
size = 14 # Font size in points Configuration Options Reference
General Options
| Option | Type | Default | Description |
|---|---|---|---|
| grace_period | u32 | 0 | Reserved for future use |
| pam_service | string | "garlock" | Reserved for future use |
| max_attempts | u32 | 3 | Failed attempts before cooldown |
| cooldown_seconds | u32 | 5 | Base cooldown duration (multiplied) |
Background Options
| Option | Type | Default | Description |
|---|---|---|---|
| blur_radius | f32 | 25.0 | Gaussian blur amount (0 = disabled) |
| brightness | f32 | 0.6 | Brightness multiplier (0.0-1.0) |
| fallback_color | hex | #1a1a2e | Solid color if screenshot fails |
Ring Options
| Option | Type | Default | Description |
|---|---|---|---|
| radius_outer | f64 | 90.0 | Outer ring radius (pixels) |
| radius_inner | f64 | 75.0 | Inner circle radius |
| line_width | f64 | 6.0 | Ring stroke width |
| color_idle | hex | #1e90ffcc | Waiting for input (blue) |
| color_typing | hex | #00ff00cc | Receiving password (green) |
| color_verifying | hex | #ffa500cc | PAM check in progress (orange) |
| color_wrong | hex | #ff0000cc | Authentication failed (red) |
| color_clear | hex | #ffff00cc | Backspace/clear (yellow) |
| color_inside | hex | #00000088 | Inner circle fill |
| color_ring_bg | hex | #00000055 | Ring background track |
Indicator Options
| Option | Type | Default | Description |
|---|---|---|---|
| show_caps_lock | bool | true | Display caps lock warning |
| caps_lock_text | string | "Caps Lock" | Warning text |
| show_failed_attempts | bool | true | Show attempt counter |
| show_time | bool | false | Display current time |
| time_format | string | "%H:%M" | strftime pattern |
Font Options
| Option | Type | Default | Description |
|---|---|---|---|
| family | string | "Sans" | Font family (Pango) |
| size | u32 | 14 | Font size in points |
Overview
garlock is a secure screen locker built with PAM authentication, X11 input grabs, and Cairo/Pango rendering. It captures a blurred screenshot of your desktop and displays a ring indicator for password feedback.
Architecture
- Authentication: PAM with async threading, zeroize for password security
- Rendering: Cairo for graphics, Pango for text layout
- X11 Backend: x11rb with keyboard/pointer grabs
- Input: xkbcommon for proper Unicode handling
- IPC: JSON over Unix domain sockets (daemon mode)
- Config: TOML at ~/.config/garlock/config.toml
File Locations
| ~/.config/garlock/config.toml | User configuration |
| /etc/pam.d/garlock | PAM service configuration |
| $XDG_RUNTIME_DIR/garlock.sock | IPC socket (daemon mode) |
Lock Screen Layout
The lock screen displays elements centered on the primary monitor:
Time Display (optional)
150px above ring
[Circular Ring]
(12-segment indicator)
Caps Lock Warning (optional)
130px below ring
Failed Attempts Counter (optional)
160px below ring
Cooldown Timer (when active)
190px below ring Authentication
garlock uses PAM (Pluggable Authentication Modules) for secure password verification. Authentication runs asynchronously to keep the UI responsive.
Authentication Flow
- User types password (ring turns green)
- User presses Enter (ring turns orange)
- PAM verifies password in background thread
- Success: Screen unlocks
- Failure: Ring turns red, attempt counter increments
Async Model
PAM verification runs in a separate thread to prevent UI blocking. The main event loop continues processing keyboard input and rendering updates while authentication is in progress.
PAM Integration
PAM allows flexible authentication configuration, supporting passwords, fingerprints, or other methods.
PAM Service File
The service file at /etc/pam.d/garlock defines authentication rules:
#%PAM-1.0
auth required pam_unix.so #%PAM-1.0
auth required pam_systemd_user.so #%PAM-1.0
auth sufficient pam_fprintd.so
auth required pam_unix.so PAM Service
garlock always uses the garlock PAM service. Ensure the corresponding file exists at /etc/pam.d/garlock.
Security
garlock implements multiple security measures to protect your session.
Password Memory Zeroing
Passwords are stored in a secure buffer that uses the zeroize crate:
- Memory is explicitly zeroed when password is dropped
- Compiler cannot optimize away the zeroing
- Vec capacity is zeroed even if unused
- Manual
zeroize()available for explicit clearing
Input Grabs
garlock grabs both keyboard and pointer to prevent input from reaching other applications:
- Keyboard grab: Prevents Alt+Tab, Super key, all shortcuts
- Pointer grab: Confines mouse to lock window
- Retry mechanism: Up to 100 attempts with 50ms delay
Screenshot Timing
The screenshot is captured before the lock window is created, then immediately blurred and dimmed to prevent screen content from being visible.
Note: Password input is never logged, even in debug mode. The password buffer only stores bytes, never printing or exposing the content.
Cooldown System
After too many failed attempts, garlock enforces a cooldown period before allowing more attempts.
Cooldown Trigger
Cooldown activates when: failed_attempts >= max_attempts
Duration: cooldown_seconds (fixed)
Example with max_attempts=3, cooldown_seconds=5:
3 failures → 5 second cooldown
4 failures → 5 second cooldown
(cooldown triggers after each failed attempt beyond the threshold) Cooldown Behavior
- Password input is disabled during cooldown
- Countdown timer displayed below ring
- Enter key has no effect until cooldown expires
- Counter resets after successful authentication
Ring Indicator
The ring indicator provides visual feedback for authentication state and password input progress.
Ring Structure
- Outer ring: 12 segments for keystroke feedback
- Inner circle: Dark fill for contrast
- Background track: Subtle ring outline
Segment Highlighting
Each keystroke advances the highlight to the next segment (clockwise from top):
- Character typed → advance to next segment
- Backspace → retreat to previous segment
- Ctrl+U (clear) → reset to no highlight
- Active segment is brightened (+0.3 per color component)
Visual States
The ring color indicates the current authentication state:
State Timeouts
- Input idle: 300ms → ring returns to idle blue
- Auth error: 1.5s → ring returns to idle blue
Ring Customization
Customize the ring appearance in the [ring] section:
[ring]
# Make a larger ring
radius_outer = 120.0
radius_inner = 100.0
line_width = 8.0
# Custom color scheme (purple/pink theme)
color_idle = "#9b59b6cc" # Purple
color_typing = "#e91e63cc" # Pink
color_verifying = "#f39c12cc" # Gold
color_wrong = "#e74c3ccc" # Red
color_clear = "#3498dbcc" # Blue
# Darker backgrounds
color_inside = "#000000aa"
color_ring_bg = "#00000077" Color Format
Colors use hex format with optional alpha:
#RRGGBB— RGB (fully opaque)#RRGGBBAA— RGBA with alpha (cc = 80%, 88 = 53%)
Background
garlock captures a screenshot of your desktop before locking, then applies blur and brightness adjustments for privacy.
Processing Pipeline
- Connect to X11 and capture root window
- Downsample to 1/4 size (16x speedup)
- Apply Gaussian blur at reduced resolution
- Scale back to original size
- Apply brightness adjustment
- Use as lock screen background
Fallback Behavior
If screenshot capture fails (e.g., compositor issues), garlock uses the fallback_color as a solid background.
Blur Effect
The blur radius controls how much the screenshot is blurred. Higher values provide more privacy but take longer to process.
[background]
blur_radius = 25.0 # Default: good privacy/speed balance
# Examples:
blur_radius = 0 # No blur (fast, but shows desktop)
blur_radius = 10 # Light blur (fast)
blur_radius = 25 # Medium blur (default)
blur_radius = 50 # Heavy blur (slower)
blur_radius = 100 # Maximum privacy (slowest) Performance
Blur processing typically takes 50-150ms thanks to the downsample optimization. With blur disabled (blur_radius = 0), processing takes ~10ms.
Brightness
Brightness adjustment dims the background to improve ring indicator visibility and reduce screen glare.
[background]
brightness = 0.6 # Default: 60% brightness
# Examples:
brightness = 0.0 # Completely black
brightness = 0.3 # Very dim
brightness = 0.6 # Default
brightness = 0.8 # Slightly dimmed
brightness = 1.0 # Original brightness Lower values are easier on the eyes in dark environments and make the ring indicator more visible.
Text Overlays
garlock can display several text overlays around the ring indicator.
Time Display
[indicator]
show_time = true
time_format = "%H:%M" # 24-hour (14:30)
# time_format = "%I:%M %p" # 12-hour (02:30 PM)
# time_format = "%H:%M:%S" # With seconds (14:30:45)
# time_format = "%a %H:%M" # Day + time (Mon 14:30) Common Time Formats
| Format | Example |
|---|---|
| %H:%M | 14:30 |
| %I:%M %p | 02:30 PM |
| %H:%M:%S | 14:30:45 |
| %l:%M %p | 2:30 PM (no leading zero) |
| %a %H:%M | Mon 14:30 |
| %A, %B %d | Monday, January 17 |
Caps Lock Warning
[indicator]
show_caps_lock = true
caps_lock_text = "Caps Lock" # Or localized: "MAYÚSCULAS" Failed Attempts
[indicator]
show_failed_attempts = true # Shows "1 failed attempt" or "3 failed attempts" Keyboard Input
garlock uses xkbcommon for proper keyboard handling, supporting full Unicode input including international characters.
Key Bindings
| Key(s) | Action |
|---|---|
| Any printable | Add character to password |
| Backspace | Remove last character |
| Return / KP_Enter | Submit password |
| Ctrl+U / Ctrl+C | Clear password buffer |
| Escape | Exit (dev builds only, compiled with --features dev) |
| Modifiers | Update state (no visual change) |
Unicode Support
garlock correctly handles multi-byte UTF-8 characters:
- Japanese: 日本語
- Accented: café, naïve, señor
- Symbols: € £ © ™
- Emoji: Not recommended in passwords, but supported
Multi-Monitor
garlock uses RandR to detect monitors and centers the ring indicator on the primary monitor.
Monitor Detection
- Query RandR for screen resources
- Find all connected outputs with active CRTCs
- Identify primary monitor
- Calculate center point for ring placement
Fallback Behavior
If RandR is unavailable or no primary monitor is set, garlock centers the ring on the overall screen center.
Window Coverage
The lock window covers the entire virtual screen (all monitors combined), not just the primary monitor.
Daemon Mode
Run garlock as a daemon to accept lock commands via IPC socket.
Starting the Daemon
Daemon Benefits
- Faster locking: No startup overhead when locking
- State queries: Check if screen is locked
- Remote control: Lock via IPC from scripts
- Systemd integration: Run as user service
Systemd Service
[Unit]
Description=garlock - Screen Locker Daemon
After=graphical-session-pre.target
PartOf=graphical-session.target
[Service]
Type=simple
ExecStart=/usr/bin/garlock daemon
Restart=on-failure
RestartSec=1
[Install]
WantedBy=graphical-session.target Common Workflows
Practical examples of using garlock with gar and other tools.
Lock on Keybind
Bind Super+L to lock the screen in gar:
-- Lock screen with Super+L
gar.bind("mod+l", "exec garlock") Lock on Suspend
Use systemd to lock before suspend:
[Unit]
Description=Lock screen before suspend
Before=sleep.target
[Service]
User=%i
Environment=DISPLAY=:0
ExecStart=/usr/bin/garlock lock
Type=forking
[Install]
WantedBy=sleep.target Auto-Lock on Idle
Use xidlehook or xautolock to lock after inactivity:
Lock with DPMS
Turn off monitors and lock simultaneously:
#!/bin/bash
# Lock and turn off monitors
garlock lock &
sleep 0.5
xset dpms force off Minimal Privacy Lock
Maximum blur for privacy-conscious users:
[background]
blur_radius = 100.0 # Maximum blur
brightness = 0.3 # Very dim Solid Color Lock
Skip the screenshot for fastest locking:
[background]
blur_radius = 0
brightness = 0.0 # Forces solid fallback
fallback_color = "#1a1a2e" Custom Theme Example
A complete custom theme with different colors:
# Nord-inspired theme
[background]
blur_radius = 30.0
brightness = 0.5
fallback_color = "#2e3440"
[ring]
radius_outer = 100.0
radius_inner = 85.0
line_width = 5.0
color_idle = "#5e81accc" # Nord blue
color_typing = "#a3be8ccc" # Nord green
color_verifying = "#ebcb8bcc" # Nord yellow
color_wrong = "#bf616acc" # Nord red
color_clear = "#b48eadcc" # Nord purple
color_inside = "#2e344088"
color_ring_bg = "#3b425255"
[indicator]
show_time = true
time_format = "%H:%M"
show_caps_lock = true
show_failed_attempts = true
[font]
family = "Inter"
size = 16 IPC Protocol
In daemon mode, garlock exposes a Unix domain socket for control and status queries.
Socket Location
CLI Commands
| Command | Description |
|---|---|
| garlock daemon | Start daemon mode |
| garlock lock | Lock screen (direct or via daemon) |
| garlock status | Query current state |
| garlock shutdown | Stop the daemon |
Global Flags
| Flag | Description |
|---|---|
| -c, --config <PATH> | Custom config file path (default: ~/.config/garlock/config.toml) |
| -d, --debug | Enable debug logging (also logs to /tmp/garlock.log) |
JSON Protocol
Commands are JSON objects, one per line:
{"command": "lock"}
{"command": "query-state"}
{"command": "shutdown"} // Success
{"status": "ok"}
{"status": "ok", "message": "Locking screen"}
// State query response
{
"status": "state",
"locked": true,
"failed_attempts": 2,
"in_cooldown": false
}
// Error
{"status": "error", "message": "Screen is already locked"} Scripting Example
#!/bin/bash
# Lock only if not already locked
SOCK="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/garlock.sock"
if [ -S "$SOCK" ]; then
# Daemon mode: query state first
STATE=$(echo '{"command": "query-state"}' | nc -U "$SOCK")
if echo "$STATE" | grep -q '"locked":false'; then
echo '{"command": "lock"}' | nc -U "$SOCK"
fi
else
# Direct mode
garlock lock
fi Events (Planned)
Future versions will support event subscriptions:
// Planned event types
{"event": "Locked"}
{"event": "Unlocked"}
{"event": "AuthFailed", "attempt": 3}
{"event": "CooldownStarted", "seconds": 15}
{"event": "Shutdown"} Troubleshooting
PAM authentication fails
Ensure the PAM service file exists:
If missing, create /etc/pam.d/garlock with auth required pam_unix.so
Keyboard grab fails
Another application may have grabbed the keyboard:
garlock retries up to 100 times. If it still fails, check for modal dialogs or fullscreen apps.
Screenshot capture fails
This can happen with some compositors:
garlock will use the fallback solid color. Set fallback_color in config if you want a different color.
Config not loading
Verify config file syntax:
Locked out of system
Use a TTY to access your system:
- Press
Ctrl+Alt+F2to switch to TTY2 - Log in with your username and password
- Kill garlock:
pkill -9 garlock - Return to GUI:
Ctrl+Alt+F7(or F1)
Ring not centered on correct monitor
garlock uses the primary monitor. Set your primary:
Daemon not responding
Check if the daemon is running:
Slow blur processing
Reduce blur radius or disable blur:
[background]
blur_radius = 10.0 # Lower = faster
# blur_radius = 0 # Disable completely Password with special characters fails
Check keyboard layout:
garlock uses xkbcommon for keyboard handling. Ensure your keyboard layout matches what you used to set your password.
Debug mode
garlock logs diagnostic information to /tmp/garlock.log automatically.
Enable verbose logging for more detail:
The log file includes keyboard grab status, XKB key events, and authentication attempts (passwords are never logged).