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

428 lines
11 KiB
Markdown

# 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
```bash
#!/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:**
```bash
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:
```bash
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:**
```bash
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:
```bash
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:**
```bash
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:**
```bash
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:**
```bash
tail -f /tmp/displaylink-hotplug.log
```
## Troubleshooting
### Lid close doesn't suspend
**Check:**
```bash
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:**
```bash
loginctl suspend
```
If this doesn't work, check:
```bash
cat /sys/power/state
```
Should include `mem` or `s2idle`.
### Monitors don't restore after resume
**Check logs:**
```bash
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:
```bash
~/.config/hypr/scripts/monitor-setup.sh
```
### ACPI lid events not triggering
**Check acpid is running:**
```bash
rc-status | grep acpid
```
**Test ACPI events:**
```bash
# Clear log
> /tmp/lid-handler.log
# Close lid, wait, open lid
# Check log
cat /tmp/lid-handler.log
```
If no entries, check ACPI event:
```bash
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:**
```bash
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
- elogind documentation: `/usr/share/doc/elogind-*/`
- ACPI specification: https://www.kernel.org/doc/Documentation/acpi/
- Original requirements: `Lid-Functionality.md`
- Monitor configuration: `Monitor-Dock-via-DisplayLink.md`