# 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`