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>
428 lines
11 KiB
Markdown
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`
|