Files
lenovo-gentoo/Lid-Automation-Implementation.md
Alexander Hinrichs 8de3f16ee6 chore: initialize gentoo-setup documentation repository
Add comprehensive documentation for Lenovo ThinkPad Gentoo Linux setup
including:
- Complete system configuration guides (power, Bluetooth, WiFi, audio)
- Hardware setup documentation (touchpad, touchscreen, DisplayLink)
- Management scripts with ZSH completions
- Kernel configuration (6.12.41-gentoo-x86_64)
- Lid automation and monitor management
- Battery conservation system
- User guides and troubleshooting

Repository includes .gitignore to exclude logs, temporary files, and
secrets.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 18:22:51 +01:00

11 KiB

Lid Automation with Suspend/Resume

Overview

Automated lid handling system that intelligently manages displays and power states based on whether external monitors are connected. Provides seamless docking/undocking experience with proper suspend/resume support.

Requirements Met

Based on Lid-Functionality.md specifications:

  • Lid closed + external monitors → Laptop screen turns off, external monitors stay active
  • Lid closed + no external monitors → System suspends to sleep
  • Lid opens from sleep → System resumes and restores monitors
  • Lid opens + external monitors → Three-monitor mode (2 external + laptop)
  • Dock disconnected → Automatically switches to mobile-only mode
  • Dock connected → Auto-detects and enables external monitors
  • Waybar restart → Automatically restarts on monitor configuration changes
  • Multi-location support → Handles different monitor configurations

Architecture

The system consists of three components:

ACPI Event (lid open/close)
    ↓
/etc/acpi/lid.sh (ACPI handler)
    ↓
/usr/local/bin/lid-handler.sh (Decision logic)
    ↓
    ├→ External monitors present → Monitor reconfiguration
    └→ No external monitors → System suspend
                                    ↓
                        /lib/elogind/system-sleep/hyprland-resume
                                    ↓
                        Monitor reconfiguration on wake

Components

1. ACPI Lid Event Handler

File: /etc/acpi/lid.sh

Purpose: Receives lid open/close events from ACPI daemon

Function: Minimal event handler that delegates to the comprehensive lid handler without blocking acpid

#!/bin/bash
# Simply delegate to comprehensive handler
/usr/local/bin/lid-handler.sh &
exit 0

Why this design:

  • ACPI handlers should return quickly
  • Running handler in background prevents blocking acpid
  • Keeps ACPI config simple and maintainable

2. Comprehensive Lid Handler

File: /usr/local/bin/lid-handler.sh

Purpose: Intelligent decision-making for lid events

Logic Flow:

  1. Detect lid state from /proc/acpi/button/lid/*/state

  2. Check for Hyprland and set up environment

  3. Count external monitors using hyprctl monitors

  4. Make decision:

    If lid is CLOSED:

    • External monitors present → Disable laptop screen only
    • No external monitors → Trigger suspend via loginctl suspend

    If lid is OPEN:

    • Reconfigure all monitors (handled by monitor-setup.sh)

Key Features:

  • Properly sets Hyprland environment variables for hyprctl to work
  • Logs all actions to /tmp/lid-handler.log
  • Uses loginctl suspend (elogind) for proper suspend handling
  • Non-blocking execution

Environment Setup:

export HYPRLAND_INSTANCE_SIGNATURE=$(ls -t /tmp/hypr/ | head -n1)
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
export WAYLAND_DISPLAY="wayland-0"

3. elogind Resume Hook

File: /lib/elogind/system-sleep/hyprland-resume

Purpose: Restores monitor configuration after waking from suspend

Execution: Automatically called by elogind with two arguments:

  • $1 = "pre" before suspend, "post" after resume
  • $2 = "suspend", "hibernate", or "hybrid-sleep"

On Resume (post):

  1. Wait 2 seconds for system to stabilize
  2. Find user running Hyprland
  3. Execute monitor-setup.sh as that user
  4. Log all actions

Why this is needed:

  • Monitors may disconnect/reconnect during suspend
  • DisplayLink dock needs re-initialization after resume
  • Waybar needs restart to update status bar for new monitor layout

Logs: /tmp/elogind-sleep.log

4. Monitor Setup Script

File: /home/alexander/.config/hypr/scripts/monitor-setup.sh

Purpose: Central script for all monitor configuration

Called by:

  • Lid handler (when lid state changes)
  • Resume hook (when waking from suspend)
  • DisplayLink hotplug (when dock connects/disconnects)

Already implemented and working correctly.

Installation

All scripts are prepared in /tmp/. To install:

su -
/tmp/install-lid-automation.sh
exit

What the installer does:

  1. Copies lid handler to /usr/local/bin/
  2. Creates elogind sleep hook directory if needed
  3. Installs resume hook in /lib/elogind/system-sleep/
  4. Replaces ACPI lid handler in /etc/acpi/lid.sh
  5. Restarts acpid service to activate changes

Testing

Test 1: Lid Close with External Monitors

Procedure:

  1. Connect USB-C dock with external monitors
  2. Verify external monitors are working
  3. Close laptop lid

Expected Result:

  • Laptop screen turns off immediately
  • External monitors continue working
  • No system suspend

Verify:

tail -f /tmp/lid-handler.log

Should show: "External monitors detected" and "Disable laptop screen"

Test 2: Lid Close without External Monitors

Procedure:

  1. Disconnect USB-C dock
  2. Verify only laptop screen is active
  3. Close laptop lid

Expected Result:

  • System suspends within 1 second
  • Power LED should blink (suspend indicator)

Verify: After waking, check logs:

tail /tmp/lid-handler.log

Should show: "No external monitors" and "suspending system"

Test 3: Resume from Suspend

Procedure:

  1. With laptop suspended (from Test 2)
  2. Open laptop lid

Expected Result:

  • System wakes immediately
  • Laptop screen turns on
  • Login prompt appears
  • Monitors restore to correct configuration

Verify:

tail /tmp/elogind-sleep.log

Should show: "Waking from sleep" and "Monitor reconfiguration triggered"

Test 4: Three-Monitor Mode

Procedure:

  1. Connect USB-C dock with external monitors
  2. Open laptop lid (if closed)

Expected Result:

  • Two external monitors: 2560x1440@60Hz side-by-side at top
  • Laptop monitor: 2880x1800@120Hz centered below
  • All three monitors active simultaneously

Verify:

hyprctl monitors

Should show 3 active monitors: eDP-1, DVI-I-1, DVI-I-2

Test 5: Dock Hotplug

Procedure:

  1. With laptop lid open, disconnect USB-C dock
  2. Wait 2-3 seconds
  3. Reconnect USB-C dock

Expected Result:

  • On disconnect: Switches to laptop-only mode
  • On reconnect: External monitors appear and configure automatically
  • Waybar restarts on each change

Verify:

tail -f /tmp/displaylink-hotplug.log

Troubleshooting

Lid close doesn't suspend

Check:

cat /etc/elogind/logind.conf | grep HandleLidSwitch

Should show:

HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore

Our script handles lid logic, not elogind. This is correct.

Test suspend manually:

loginctl suspend

If this doesn't work, check:

cat /sys/power/state

Should include mem or s2idle.

Monitors don't restore after resume

Check logs:

tail -20 /tmp/elogind-sleep.log
tail -20 /tmp/hyprland-monitor-setup.log

Common issues:

  • DisplayLink dock needs 2-3 seconds to re-initialize after resume
  • Increase sleep time in resume hook if needed

Manual test: After resume, run:

~/.config/hypr/scripts/monitor-setup.sh

ACPI lid events not triggering

Check acpid is running:

rc-status | grep acpid

Test ACPI events:

# Clear log
> /tmp/lid-handler.log

# Close lid, wait, open lid

# Check log
cat /tmp/lid-handler.log

If no entries, check ACPI event:

acpi_listen
# Close and open lid - should see events

Logs

All components log their actions:

Log File What It Contains
/tmp/lid-handler.log Lid event decisions and actions
/tmp/elogind-sleep.log Suspend/resume events
/tmp/hyprland-monitor-setup.log Monitor configuration details
/tmp/displaylink-hotplug.log Dock connection/disconnection

Monitor all logs simultaneously:

tail -f /tmp/lid-handler.log /tmp/elogind-sleep.log /tmp/hyprland-monitor-setup.log

Configuration

Single User System

The system is designed for single-user operation (alexander). The scripts automatically detect the user running Hyprland.

Different Monitor Configurations

The monitor setup script uses preferred resolution and auto-positioning. To support different locations (work/home) with different monitors:

Option 1: Let it auto-configure (current approach)

  • Works for most setups
  • Monitors use their preferred resolution

Option 2: Create location profiles (future enhancement)

  • Detect location by monitor serial numbers
  • Load specific configuration per location
  • Requires extending monitor-setup.sh

Dependencies

  • elogind: Session management and suspend/resume
  • acpid: ACPI event handling
  • hyprland: Window manager with hyprctl
  • jq: JSON parsing for monitor queries
  • loginctl: elogind control utility

All dependencies are already installed and working.

Files Modified/Created

File Action Purpose
/usr/local/bin/lid-handler.sh Created Main lid event logic
/lib/elogind/system-sleep/hyprland-resume Created Resume hook
/etc/acpi/lid.sh Replaced ACPI event entry point
/home/alexander/.config/hypr/scripts/monitor-setup.sh Already exists Monitor configuration

Design Decisions

Why not let elogind handle lid events?

Problem: elogind's lid handling is all-or-nothing:

  • HandleLidSwitch=suspend → Always suspends (even with external monitors)
  • HandleLidSwitch=ignore → Never suspends (manual suspend only)

Solution: Custom logic that considers external monitor state.

Why use loginctl suspend instead of writing to /sys/power/state?

Reasons:

  • elogind handles pre-suspend tasks (session locking, service notifications)
  • Proper resume hook execution
  • Better integration with session management
  • Logs in journal/syslog

Why run handler in background from ACPI?

Reasons:

  • ACPI handlers should return quickly (< 1 second)
  • Suspend takes time, would block acpid
  • Other ACPI events (power button, dock) need to be handled concurrently

Why wait 2 seconds on resume?

Reasons:

  • DisplayLink USB devices need time to re-enumerate
  • Kernel drivers need to re-initialize
  • Too fast = monitors not detected yet
  • Too slow = user sees blank screens longer

Tunable: Adjust sleep time in /lib/elogind/system-sleep/hyprland-resume if needed.

Future Enhancements

1. Monitor Configuration Profiles

  • Detect location by monitor serial numbers
  • Load specific layouts per location
  • Store in ~/.config/hypr/monitor-profiles/

2. Lock Screen on Suspend

  • Integrate with swaylock or gtklock
  • Lock before suspend, unlock on resume

3. Notification on Dock Connect/Disconnect

  • Use mako to show notifications
  • "Dock connected: 2 monitors"
  • "Switched to mobile mode"

4. Faster Resume

  • Pre-load DisplayLink configuration
  • Reduce sleep time by detecting device readiness

5. Hibernation Support

  • Similar to suspend but saves to disk
  • Requires swap configuration
  • Useful for extended battery life

References