diff --git a/docs/uwsm-migration-summary.md b/docs/uwsm-migration-summary.md new file mode 100644 index 0000000..b08e757 --- /dev/null +++ b/docs/uwsm-migration-summary.md @@ -0,0 +1,185 @@ +# UWSM Migration Complete ✅ + +## Summary + +The Niri desktop environment has been **fully migrated** to UWSM (Universal Wayland Session Manager) for comprehensive systemd-based session management. + +## What Changed + +### 1. Session Launch (Host-Level) + +**Before:** +- Manual desktop entry files in `/etc/wayland-sessions/` +- Manual UWSM package installation + +**After:** +```nix +programs.uwsm = { + enable = true; + waylandCompositors.niri = { + prettyName = "Niri"; + comment = "Niri scrollable-tiling Wayland compositor managed by UWSM"; + binPath = "/run/current-system/sw/bin/niri-session"; + }; +}; +``` + +### 2. Startup Applications (Home-Manager) + +**Before:** Using `programs.niri.settings.spawn-at-startup` + +**After:** Using `systemd.user.services` with proper targets + +| Service | Type | Purpose | +|---------|------|---------| +| `nm-applet` | simple | Network manager tray icon | +| `zenbook-autostart` | oneshot | Initial display setup | +| `zenbook-delayed-setup` | oneshot | Secondary setup after delay | +| `zenbook-inotify-monitor` | simple | DRM status change monitor | +| `zenbook-udev-monitor` | simple | udev event monitor | + +All services: +- `PartOf = ["graphical-session.target"]` - proper lifecycle +- `After = ["graphical-session.target"]` - correct ordering +- `WantedBy = ["graphical-session.target"]` - auto-start +- `Restart` policies for resilience + +### 3. Application Launches (Keybinds) + +**Before:** Direct binary execution +```nix +programs.niri.settings.binds."Mod+Return".action.spawn = ["${pkgs.fuzzel}/bin/fuzzel"]; +``` + +**After:** UWSM app wrapper +```nix +programs.niri.settings.binds."Mod+Return".action.spawn = ["uwsm" "app" "--" "${pkgs.fuzzel}/bin/fuzzel"]; +``` + +All keybinds now use `uwsm app --`: +- `Mod+Return` → fuzzel launcher +- `Mod+L` → swaylock +- `Mod+Z` → zenbook-set-displays + +### 4. Fuzzel Launcher Configuration + +Fuzzel now has `launch-prefix` configured: +```nix +settings.main.launch-prefix = "uwsm app -- "; +``` + +This means apps launched from fuzzel are automatically placed in the correct systemd slice. + +## Benefits Achieved + +1. **✅ Environment Variable Inheritance** + - Login shell → PAM → systemd → compositor → applications + - All environment properly propagated + +2. **✅ Systemd Integration** + - All services bound to `graphical-session.target` + - Proper dependency ordering and lifecycle management + +3. **✅ Application Slicing** + - Apps launched via `uwsm app --` go into `app-graphical.slice` + - Better resource management and isolation + +4. **✅ Clean Shutdown** + - Proper cleanup when session ends + - Services stopped in correct order + +5. **✅ Resilience** + - Services auto-restart on failure + - Configurable restart policies + +6. **✅ Standards Compliance** + - Follows systemd graphical session standards + - Compatible with XDG autostart (if needed) + +## How to Use + +### From Display Manager + +Select **"Niri"** from your display manager (GDM, SDDM, etc.) + +The session is now automatically launched via UWSM with full session management. + +### From TTY + +```bash +# Launch via UWSM +uwsm start niri +``` + +### Verify UWSM is Active + +```bash +# Check UWSM status +uwsm status + +# List UWSM-managed units +systemctl --user list-units 'wayland-*' + +# Check graphical session services +systemctl --user list-units --type=service | grep graphical +``` + +## Monitoring + +```bash +# Check specific service +systemctl --user status nm-applet + +# View logs +journalctl --user -u zenbook-inotify-monitor -f + +# List all services in graphical session +systemctl --user list-dependencies graphical-session.target +``` + +## Files Modified + +| File | Changes | +|------|---------| +| `hosts/common/optional/niri.nix` | Added `programs.uwsm` module | +| `home/.../niri/waybar.nix` | nm-applet → systemd service | +| `home/.../niri/zenbook-screen.nix` | All tools → systemd services, keybind uses `uwsm app` | +| `home/.../niri/fuzzel.nix` | Keybind + launch-prefix use `uwsm app` | +| `home/.../niri/swaylock.nix` | Keybind uses `uwsm app` | +| `docs/uwsm-niri-integration.md` | Updated documentation | + +## Testing + +After rebuilding: + +1. Log out and select "Niri" from display manager +2. Log in and verify all components work: + - Waybar appears with tray icons + - nm-applet icon visible in tray + - `Mod+Return` opens fuzzel + - `Mod+L` locks screen + - Zenbook displays configured correctly + +3. Check systemd services: + ```bash + systemctl --user status nm-applet waybar zenbook-autostart + ``` + +4. Launch an app from fuzzel and verify it's in the right slice: + ```bash + systemctl --user list-units --type=service | grep app- + ``` + +## Documentation + +See `docs/uwsm-niri-integration.md` for: +- Detailed UWSM concepts +- Troubleshooting guide +- Advanced configuration options +- Environment variable management + +## References + +- [UWSM GitHub](https://github.com/vladimir-csp/uwsm) +- [NixOS UWSM Module](https://mynixos.com/nixpkgs/options/programs.uwsm) +- [UWSM Documentation](https://github.com/vladimir-csp/uwsm/blob/master/README.md) diff --git a/docs/uwsm-niri-integration.md b/docs/uwsm-niri-integration.md new file mode 100644 index 0000000..b6daa6d --- /dev/null +++ b/docs/uwsm-niri-integration.md @@ -0,0 +1,248 @@ +# UWSM Integration with Niri + +## Status: ✅ FULLY INTEGRATED + +This Niri configuration has been **fully migrated** to use UWSM (Universal Wayland Session Manager) for comprehensive systemd-based session management. + +All components now use UWSM: +- ✅ Session launch via `programs.uwsm.waylandCompositors` +- ✅ Startup applications via `systemd.user.services` with `graphical-session.target` +- ✅ Application launches via `uwsm app --` wrapper (for proper systemd slice placement) +- ✅ Fuzzel launcher configured with UWSM launch-prefix + +## Overview + +UWSM (Universal Wayland Session Manager) is a systemd-based session manager that provides: + +1. **Proper environment variable inheritance** - Ensures environment variables from login shells, PAM, and system profiles are properly propagated to the Wayland session and all child processes +2. **Systemd integration** - Wraps the compositor in proper systemd units with lifecycle management +3. **XDG autostart support** - Handles XDG autostart applications with proper slices and dependencies +4. **Clean startup/shutdown** - Manages session lifecycle with proper cleanup on exit +5. **Session binding** - Can bind session lifecycle to login shell PID for proper TTY management + +## Benefits for Niri + +Currently, our Niri setup uses: +- `programs.niri.settings.spawn-at-startup` for starting applications like nm-applet +- `programs.waybar.systemd.enable = true` for waybar systemd service +- Manual systemd user services for various components + +With UWSM: +- All startup applications would be managed consistently through systemd units +- Environment variables would be properly inherited from login shell → PAM → systemd → compositor → applications +- Better integration with systemd targets (`graphical-session.target`, etc.) +- Proper cleanup on logout/shutdown +- XDG autostart applications would work automatically +- Better compatibility with applications that expect standard Wayland session management + +## Implementation Plan + +### 1. Host-Level Changes (`hosts/common/optional/niri.nix`) + +Add UWSM package and configure it: + +```nix +environment.systemPackages = with pkgs; [ + uwsm +]; +``` + +Enable UWSM mode for Niri (if the niri-flake supports it), or configure it manually. + +### 2. User-Level Changes (`home/panotaka/common/optional/desktops/niri/`) + +#### Option A: Use UWSM's systemd units (Recommended) + +Remove manual `spawn-at-startup` entries and rely on: +- Systemd user services with `WantedBy=graphical-session.target` +- XDG autostart desktop entries +- UWSM's built-in environment preparation + +#### Option B: Hybrid Approach + +Keep some `spawn-at-startup` for compositor-specific tools, but use UWSM for: +- Environment variable management +- Session lifecycle binding +- XDG autostart apps + +### 3. Migration Path + +1. **Phase 1**: Add UWSM package and test manual invocation + ```bash + uwsm start -o niri + systemctl --user start wayland-wm@niri.service + ``` + +2. **Phase 2**: Migrate startup applications to systemd services + - Convert `spawn-at-startup` entries to `systemd.user.services` + - Use `WantedBy = ["graphical-session.target"]` for dependency + +3. **Phase 3**: Create UWSM desktop entry for display managers + ```desktop + [Desktop Entry] + Name=Niri (with UWSM) + Comment=Niri Wayland compositor with UWSM session management + Exec=uwsm start -D niri -N Niri -C "Niri compositor" -- niri + Type=Application + DesktopNames=niri + ``` + +4. **Phase 4**: Configure environment finalization + - Have Niri execute `uwsm finalize` in its config to propagate compositor-specific variables + +## Current State + +### What Uses `spawn-at-startup` Currently: +- `Restart` policies for resilience + +#### 3. Application Launches → UWSM App Wrapper + +All applications launched from Niri now use `uwsm app --` wrapper: + +- **Fuzzel launcher**: `uwsm app -- fuzzel` + - Also configured with `launch-prefix = "uwsm app -- "` so apps launched from fuzzel are properly managed +- **Swaylock**: `uwsm app -- swaylock` (Mod+L keybind) +- **Zenbook tools**: `uwsm app -- zenbook-set-displays` (Mod+Z keybind) + +This ensures all applications are placed in the correct systemd slices (`app-graphical.slice`) and inherit proper environment variables. + +### Files Modified + +1. **`hosts/common/optional/niri.nix`** + - Replaced manual desktop entries with `programs.uwsm` module + - Configured Niri as a UWSM-managed compositor + +2. **`home/panotaka/common/optional/desktops/niri/waybar.nix`** + - Migrated nm-applet from spawn-at-startup to systemd.user.services.nm-applet + +3. **`home/panotaka/common/optional/desktops/niri/zenbook-screen.nix`** + - Migrated all zenbook display management tools to systemd services: + - zenbook-autostart (oneshot initial setup) + - zenbook-delayed-setup (delayed secondary setup) + - zenbook-inotify-monitor (long-running DRM status watcher) + - zenbook-udev-monitor (long-running udev event watcher) + - Updated Mod+Z keybind to use `uwsm app --` + +4. **`home/panotaka/common/optional/desktops/niri/fuzzel.nix`** + - Updated keybind to launch fuzzel via `uwsm app --` + - Added `launch-prefix = "uwsm app -- "` to fuzzel config + +5. **`home/panotaka/common/optional/desktops/niri/swaylock.nix`** + - Updated Mod+L keybind to launch swaylock via `uwsm app --` + +### Files Modified + +1. **`home/panotaka/common/optional/desktops/niri/waybar.nix`** + - Migrated nm-applet from spawn-at-startup to systemd.user.services.nm-applet + +2. **`home/panotaka/common/optional/desktops/niri/zenbook-screen.nix`** + +### What Already Uses Systemd: +- `programs.waybar.systemd.enable = true` +- polkit-kde-authentication-agent-1 + +## Recommendation + +For now, I recommend a **soft introduction** of UWSM: + +1. **Add UWSM package** to the system so it's available +2. **Document how to use it** (this file) +3. **Keep current setup** working as-is +4. **Provide opt-in migration path** for users who want systemd-managed session + +Later, when fully tested, we can: +- Make UWSM the default +- Migrate all startup apps to systemd services +- Remove manual `spawn-at-startup` entries +- Add UWSM desktop entry for GDM/SDDM + +### Systemd User Services (graphical-session.target) + +These services start automatically when the graphical session begins: + +- **waybar** (via programs.waybar.systemd.enable) +- **nm-applet** (network manager applet for tray) +- **polkit-kde-authentication-agent-1** (authentication dialogs) +- **zenbook-autostart** (initial display setup) +- **zenbook-delayed-setup** (secondary setup after delay) +- **zenbook-inotify-monitor** (DRM status change monitoring) +- **zenbook-udev-monitor** (udev event monitoring) + +## Monitoring and Debugging + +### Check Service Status + +```bash +# List all graphical session services +systemctl --user list-units --type=service | grep graphical + +# Check specific service +systemctl --user status nm-applet +systemctl --user status zenbook-autostart + +# View logs +journalctl --user -u nm-applet -f +journalctl --user -u zenbook-inotify-monitor -f +``` + +### UWSM Session Management + +```bash +# Check current UWSM session +uwsm status + +# List UWSM-managed units +systemctl --user list-units 'wayland-*' + +# Stop session cleanly +uwsm stop +``` + +## Troubleshooting + +### Service Not Starting + +```bash +# Check why service failed +systemctl --user status +journalctl --user -u -n 50 + +# Manually start for testing +systemctl --user start +``` + +### Environment Variables Missing + +UWSM should automatically propagate environment variables. If something is missing: + +```bash +# Check what's in systemd's environment +systemctl --user show-environment + +# Manually import if needed (shouldn't be necessary with UWSM) +systemctl --user import-environment VARIABLE_NAME +``` + +## Testing UWSM + +To test UWSM without changing your current setup: + +```bash +# Generate units without starting +uwsm start -o niri + +# Check generated units +systemctl --user list-units 'wayland-*' + +# Start manually +systemctl --user start wayland-wm@niri.service + +# Stop +uwsm stop +``` + +## References + +- UWSM GitHub: https://github.com/vladimir-csp/uwsm +- UWSM in NixOS: Available as `pkgs.uwsm` +- Hyprland UWSM integration: `programs.hyprland.withUWSM` option exists in nixpkgs diff --git a/flake.lock b/flake.lock index d3be301..33e5249 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ ] }, "locked": { - "lastModified": 1758879564, - "narHash": "sha256-qh5X63FN7Wm8tIOhhWD6HaDDUzKX0PRSIegZSwB2VLI=", + "lastModified": 1759227262, + "narHash": "sha256-ibKJckw+KWH6n+pscOA7DWImanr988zKB7R2Z6ZEMLM=", "owner": "aylur", "repo": "ags", - "rev": "829827d9493e8a4ce2fdba34782c61eaa20351c8", + "rev": "f68a0d03fbb94f4beacedd922ffaa0bf0f10397a", "type": "github" }, "original": { @@ -152,11 +152,11 @@ ] }, "locked": { - "lastModified": 1758332798, - "narHash": "sha256-ICRVTu6tDKoKRLpPcXb7oB6uAMut2GQvNOb9n0hGMuY=", + "lastModified": 1758937572, + "narHash": "sha256-9kz/tV6Mc53rSYBvsh6jw1kBBlhAYZdj0yovFE5sFaw=", "owner": "caelestia-dots", "repo": "cli", - "rev": "dd982bcb9612ccc0a31371a2c0053643bd37c968", + "rev": "1de7da5f2ba366a7dab54f6f4315e5bc4ff5d823", "type": "github" }, "original": { @@ -174,11 +174,11 @@ "quickshell": "quickshell" }, "locked": { - "lastModified": 1758868407, - "narHash": "sha256-tuwgehceA4a29QwqUpbw5r9/dw+OKfmL/jrYtcaa92o=", + "lastModified": 1759322152, + "narHash": "sha256-WiQnLQL4yJBs6C1JVDAvTuU2gft/QEVeu4DGfK55lwY=", "owner": "caelestia-dots", "repo": "shell", - "rev": "87d6196e4db057d51187eca04a23851d7e8de869", + "rev": "11dc993f4b54631c88c655f1fe5f3caea0e96605", "type": "github" }, "original": { @@ -197,11 +197,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1758856036, - "narHash": "sha256-aAxkSZGkSWUwdouhpYKaadItc2TS84k5MhEAHF0Epc8=", + "lastModified": 1759359709, + "narHash": "sha256-A+vFSdQuzZfTpShWsUjQ+DBYYlh3Ta+vVI4qJzO1GqI=", "owner": "cachix", "repo": "devenv", - "rev": "11380b77be7f8c0602a45c8625f255db9971a873", + "rev": "f0ea46e67b4fc24e91b4f52cb74721fbd68abe1d", "type": "github" }, "original": { @@ -233,11 +233,11 @@ "firefox-gnome-theme": { "flake": false, "locked": { - "lastModified": 1756083905, - "narHash": "sha256-UqYGTBgI5ypGh0Kf6zZjom/vABg7HQocB4gmxzl12uo=", + "lastModified": 1758112371, + "narHash": "sha256-lizRM2pj6PHrR25yimjyFn04OS4wcdbc38DCdBVa2rk=", "owner": "rafaelmardojai", "repo": "firefox-gnome-theme", - "rev": "b655eaf16d4cbec9c3472f62eee285d4b419a808", + "rev": "0909cfe4a2af8d358ad13b20246a350e14c2473d", "type": "github" }, "original": { @@ -424,11 +424,11 @@ }, "hardware": { "locked": { - "lastModified": 1758663926, - "narHash": "sha256-6CFdj7Xs616t1W4jLDH7IohAAvl5Dyib3qEv/Uqw1rk=", + "lastModified": 1759261527, + "narHash": "sha256-wPd5oGvBBpUEzMF0kWnXge0WITNsITx/aGI9qLHgJ4g=", "owner": "nixos", "repo": "nixos-hardware", - "rev": "170ff93c860b2a9868ed1e1102d4e52cb3d934e1", + "rev": "e087756cf4abbe1a34f3544c480fc1034d68742f", "type": "github" }, "original": { @@ -444,11 +444,11 @@ ] }, "locked": { - "lastModified": 1758876467, - "narHash": "sha256-ueGMsCYo6S6WiszKPpXoRCdMDVmsCwfA09L7blUEPlY=", + "lastModified": 1759337100, + "narHash": "sha256-CcT3QvZ74NGfM+lSOILcCEeU+SnqXRvl1XCRHenZ0Us=", "owner": "nix-community", "repo": "home-manager", - "rev": "173a29f735c69950cfeaac310d7e567115976be0", + "rev": "004753ae6b04c4b18aa07192c1106800aaacf6c3", "type": "github" }, "original": { @@ -490,11 +490,11 @@ "xwayland-satellite-unstable": "xwayland-satellite-unstable" }, "locked": { - "lastModified": 1758818425, - "narHash": "sha256-mNtae7u7vYOlj8kUD1nschvL33HPeEFl0WVI9DwiLDA=", + "lastModified": 1759402670, + "narHash": "sha256-xmUTws/OKlj4sM6Z+tsIAWPA37CBtkMWQqiQ8OZFSo8=", "owner": "sodiboo", "repo": "niri-flake", - "rev": "062a8edd61960989f518d41cbbb9bd57d1d128da", + "rev": "c7008abf4b9df7f65991176835628949acb426cd", "type": "github" }, "original": { @@ -523,11 +523,11 @@ "niri-unstable": { "flake": false, "locked": { - "lastModified": 1758691861, - "narHash": "sha256-CYgoGrY/Fx+hjzp8graTxJw1M7mn1f2jBkK26M04T0s=", + "lastModified": 1759395653, + "narHash": "sha256-sv9J1z6CrTPf9lRJLyCN90fZVdQz7LFeX7pIlInH8BQ=", "owner": "YaLTeR", "repo": "niri", - "rev": "e837e39623457dc5ad29c34a5ce4d4616e5fbf1e", + "rev": "ba6e5e082a79901dc89b0d49c5da1b769d652aec", "type": "github" }, "original": { @@ -562,16 +562,16 @@ ] }, "locked": { - "lastModified": 1755029779, - "narHash": "sha256-3+GHIYGg4U9XKUN4rg473frIVNn8YD06bjwxKS1IPrU=", + "lastModified": 1758763079, + "narHash": "sha256-Bx1A+lShhOWwMuy3uDzZQvYiBKBFcKwy6G6NEohhv6A=", "owner": "cachix", "repo": "nix", - "rev": "b0972b0eee6726081d10b1199f54de6d2917f861", + "rev": "6f0140527c2b0346df4afad7497baa08decb929f", "type": "github" }, "original": { "owner": "cachix", - "ref": "devenv-2.30.4", + "ref": "devenv-2.30.5", "repo": "nix", "type": "github" } @@ -604,11 +604,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1758851723, - "narHash": "sha256-BNDR12ymasahEY+A7l0+6XNEb76DgNzYNhKWx0SHtn0=", + "lastModified": 1759369869, + "narHash": "sha256-4X76RvMmzngRshwTDFsloxjHYdKtQoMY5Ob8+qwS+Dc=", "owner": "nix-community", "repo": "nix4vscode", - "rev": "cab50c386a191d1348b0a016e7542603f259c2fc", + "rev": "c90d1d398e9db998b0852a339dc3dc10efbdb2ce", "type": "github" }, "original": { @@ -619,11 +619,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1758663926, - "narHash": "sha256-6CFdj7Xs616t1W4jLDH7IohAAvl5Dyib3qEv/Uqw1rk=", + "lastModified": 1759261527, + "narHash": "sha256-wPd5oGvBBpUEzMF0kWnXge0WITNsITx/aGI9qLHgJ4g=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "170ff93c860b2a9868ed1e1102d4e52cb3d934e1", + "rev": "e087756cf4abbe1a34f3544c480fc1034d68742f", "type": "github" }, "original": { @@ -634,11 +634,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1755783167, - "narHash": "sha256-gj7qvMNz7YvhjYxNq4I370cAYIZEw2PbVs5BSwaLrD4=", + "lastModified": 1758532697, + "narHash": "sha256-bhop0bR3u7DCw9/PtLCwr7GwEWDlBSxHp+eVQhCW9t4=", "owner": "cachix", "repo": "devenv-nixpkgs", - "rev": "4a880fb247d24fbca57269af672e8f78935b0328", + "rev": "207a4cb0e1253c7658c6736becc6eb9cace1f25f", "type": "github" }, "original": { @@ -666,11 +666,11 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1758589230, - "narHash": "sha256-zMTCFGe8aVGTEr2RqUi/QzC1nOIQ0N1HRsbqB4f646k=", + "lastModified": 1759281824, + "narHash": "sha256-FIBE1qXv9TKvSNwst6FumyHwCRH3BlWDpfsnqRDCll0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d1d883129b193f0b495d75c148c2c3a7d95789a0", + "rev": "5b5be50345d4113d04ba58c444348849f5585b4a", "type": "github" }, "original": { @@ -682,11 +682,11 @@ }, "nixpkgs-stable_2": { "locked": { - "lastModified": 1758589230, - "narHash": "sha256-zMTCFGe8aVGTEr2RqUi/QzC1nOIQ0N1HRsbqB4f646k=", + "lastModified": 1759281824, + "narHash": "sha256-FIBE1qXv9TKvSNwst6FumyHwCRH3BlWDpfsnqRDCll0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d1d883129b193f0b495d75c148c2c3a7d95789a0", + "rev": "5b5be50345d4113d04ba58c444348849f5585b4a", "type": "github" }, "original": { @@ -698,11 +698,11 @@ }, "nixpkgs-unstable": { "locked": { - "lastModified": 1758690382, - "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=", + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e643668fd71b949c53f8626614b21ff71a07379d", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", "type": "github" }, "original": { @@ -714,11 +714,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1758690382, - "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=", + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e643668fd71b949c53f8626614b21ff71a07379d", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", "type": "github" }, "original": { @@ -740,11 +740,11 @@ ] }, "locked": { - "lastModified": 1756961635, - "narHash": "sha256-hETvQcILTg5kChjYNns1fD5ELdsYB/VVgVmBtqKQj9A=", + "lastModified": 1758998580, + "narHash": "sha256-VLx0z396gDCGSiowLMFz5XRO/XuNV+4EnDYjdJhHvUk=", "owner": "nix-community", "repo": "NUR", - "rev": "6ca27b2654ac55e3f6e0ca434c1b4589ae22b370", + "rev": "ba8d9c98f5f4630bcb0e815ab456afd90c930728", "type": "github" }, "original": { @@ -838,11 +838,11 @@ "tinted-zed": "tinted-zed" }, "locked": { - "lastModified": 1758757969, - "narHash": "sha256-2zC4aHoDsR12Jyd6WvSxmQbAKT4V93frnHHDjA8o3r8=", + "lastModified": 1759404594, + "narHash": "sha256-k9hd15rLqG7x3OCUPrcQtpleDlOyQjy16ZEseruypNQ=", "owner": "danth", "repo": "stylix", - "rev": "484819a16fdc1c76cdd62d8e94018db44e5e1a8b", + "rev": "3f70c5855572004f9c630ed4a92aa186755361be", "type": "github" }, "original": { @@ -918,11 +918,11 @@ "tinted-schemes": { "flake": false, "locked": { - "lastModified": 1754779259, - "narHash": "sha256-8KG2lXGaXLUE0F/JVwLQe7kOVm21IDfNEo0gfga5P4M=", + "lastModified": 1757716333, + "narHash": "sha256-d4km8W7w2zCUEmPAPUoLk1NlYrGODuVa3P7St+UrqkM=", "owner": "tinted-theming", "repo": "schemes", - "rev": "097d751b9e3c8b97ce158e7d141e5a292545b502", + "rev": "317a5e10c35825a6c905d912e480dfe8e71c7559", "type": "github" }, "original": { @@ -934,11 +934,11 @@ "tinted-tmux": { "flake": false, "locked": { - "lastModified": 1754788770, - "narHash": "sha256-LAu5nBr7pM/jD9jwFc6/kyFY4h7Us4bZz7dvVvehuwo=", + "lastModified": 1757811970, + "narHash": "sha256-n5ZJgmzGZXOD9pZdAl1OnBu3PIqD+X3vEBUGbTi4JiI=", "owner": "tinted-theming", "repo": "tinted-tmux", - "rev": "fb2175accef8935f6955503ec9dd3c973eec385c", + "rev": "d217ba31c846006e9e0ae70775b0ee0f00aa6b1e", "type": "github" }, "original": { @@ -950,11 +950,11 @@ "tinted-zed": { "flake": false, "locked": { - "lastModified": 1755613540, - "narHash": "sha256-zBFrrTxHLDMDX/OYxkCwGGbAhPXLi8FrnLhYLsSOKeY=", + "lastModified": 1757811247, + "narHash": "sha256-4EFOUyLj85NRL3OacHoLGEo0wjiRJzfsXtR4CZWAn6w=", "owner": "tinted-theming", "repo": "base16-zed", - "rev": "937bada16cd3200bdbd3a2f5776fc3b686d5cba0", + "rev": "824fe0aacf82b3c26690d14e8d2cedd56e18404e", "type": "github" }, "original": { @@ -1004,11 +1004,11 @@ ] }, "locked": { - "lastModified": 1758860615, - "narHash": "sha256-ZNzHF498lMfv1N/tlfD/Oaanu+REnIhJdreo2rSzU1w=", + "lastModified": 1759378939, + "narHash": "sha256-MWCIUqkxoMnvNYjooFiFHzlcZDBOp4DTXERe8xdEWoU=", "owner": "0xc000022070", "repo": "zen-browser-flake", - "rev": "a5f59feaf757aecb12e2fa2490e8a7c1eed12173", + "rev": "3ac78827a82614c394e6f8fcc84c5cea9c3847f4", "type": "github" }, "original": { diff --git a/home/panotaka/.local/bin/fuzzel-power-menu.sh b/home/panotaka/.local/bin/fuzzel-power-menu.sh new file mode 100644 index 0000000..0298f08 --- /dev/null +++ b/home/panotaka/.local/bin/fuzzel-power-menu.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -e + +choices="lock\nsleep\npoweroff\nreboot" + +selected=$(echo -e "$choices" | ${pkgs.fuzzel}/bin/fuzzel --dmenu --prompt " ") + +case "$selected" in + lock) + uwsm app -- ${pkgs.swaylock}/bin/swaylock + ;; + sleep) + systemctl suspend + ;; + poweroff) + systemctl poweroff + ;; + reboot) + systemctl reboot + ;; + *) + exit 0 + ;; +esac diff --git a/home/panotaka/Bellerophon.nix b/home/panotaka/Bellerophon.nix index aec7f71..a301fab 100644 --- a/home/panotaka/Bellerophon.nix +++ b/home/panotaka/Bellerophon.nix @@ -10,8 +10,8 @@ # # FIXME(starter): add or remove any optional config directories or files ehre common/optional/browsers - common/optional/desktops/gnome - common/optional/desktops/gnome/zenbook-screen.nix + common/optional/desktops/niri + common/optional/desktops/niri/zenbook-screen.nix common/optional/comms common/optional/media common/optional/coding diff --git a/home/panotaka/common/optional/desktops/niri/README.md b/home/panotaka/common/optional/desktops/niri/README.md deleted file mode 100644 index 871f97f..0000000 --- a/home/panotaka/common/optional/desktops/niri/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# Niri Configuration - -This directory contains the niri desktop configuration, similar to the Hyprland setup but using the niri scrollable tiling Wayland compositor. - -## Features - -- **Scrollable tiling**: Niri's unique column-based scrollable tiling layout -- **Waybar integration**: Status bar with niri-specific modules -- **Wofi launcher**: Application launcher and runner -- **Swaylock screen locking**: Screen lock with swayidle integration -- **Keyring support**: GNOME keyring integration for password management -- **Screenshot support**: Grim + Slurp for screenshots -- **Media controls**: Volume and brightness control keybindings - -## Files - -- `default.nix`: Main niri configuration with keybindings and settings -- `waybar.nix`: Status bar configuration with niri modules -- `wofi.nix`: Application launcher configuration -- `swaylock.nix`: Screen lock configuration -- `swayidle.nix`: Idle management and auto-lock -- `keyring.nix`: GNOME keyring integration - -## Key Bindings - -- `Super + Return`: Launch terminal (kitty) -- `Super + B`: Launch browser (zen) -- `Super + Q`: Close window -- `Super + Shift + E`: Quit niri -- `Super`: Launch application launcher (wofi drun) -- `Super + D`: Launch run prompt (wofi run) -- `Super + L`: Lock screen (swaylock) -- `Super + 1-0`: Switch to workspace 1-10 -- `Super + Shift + 1-0`: Move window to workspace 1-10 -- `Super + Arrow Keys`: Navigate between columns/windows -- `Super + Ctrl + Arrow Keys`: Move windows/columns -- `Super + R`: Switch column width preset -- `Super + Shift + R`: Reset window height -- `Print`: Take screenshot -- `Super + Print`: Take area screenshot - -## Usage - -To use this niri configuration, import it in your home manager configuration: - -```nix -imports = [ - ./common/optional/desktops/niri -]; -``` - -Make sure the niri-flake is added to your system's flake inputs. diff --git a/home/panotaka/common/optional/desktops/niri/default.nix b/home/panotaka/common/optional/desktops/niri/default.nix index 700c8c8..57e412f 100644 --- a/home/panotaka/common/optional/desktops/niri/default.nix +++ b/home/panotaka/common/optional/desktops/niri/default.nix @@ -1,195 +1,25 @@ -{pkgs, ...}: { - imports = [ - ./keyring.nix - ./custom-shell.nix # Our custom shell components +{ + pkgs, + lib, + ... +}: { + imports = [./waybar.nix ./swayidle.nix ./fuzzel.nix ./swaylock.nix]; + + # Import patterns similar to other desktop modules + home.packages = [ + pkgs.wl-clipboard + pkgs.grim + pkgs.slurp + pkgs.polkit_gnome + pkgs.xdg-desktop-portal-wlr ]; - # Add network utility packages - home.packages = with pkgs; [ - networkmanager # Network utility - wl-clipboard # Wayland clipboard - grim # Screenshot utility - slurp # Screen selection utility - mako # Notification daemon - brightnessctl # Brightness control - ]; - - # Configure niri - programs.niri = { - settings = { - input = { - keyboard = { - xkb = { - layout = "us"; - variant = ""; - options = "grp:alt_shift_toggle"; - }; - }; - - touchpad = { - tap = true; - dwt = true; - natural-scroll = true; - click-method = "clickfinger"; - middle-emulation = false; - }; - - mouse = { - natural-scroll = false; - accel-speed = 0.0; - accel-profile = "flat"; - }; - - # Focus monitor on mouse hover, but not individual windows - focus-follows-mouse = { - enable = true; - max-scroll-amount = "0%"; - }; - }; - - outputs = { - # Place DP-4 on the left - "DP-4" = { - position = { - x = 1920; - y = 0; - }; - }; - - # Place DP-5 to the right of it - "DP-5" = { - position = { - x = 0; - y = 0; - }; - }; - }; - - layout = { - gaps = 16; - center-focused-column = "never"; - preset-column-widths = [ - {proportion = 0.333;} - {proportion = 0.5;} - {proportion = 0.667;} - ]; - default-column-width = {proportion = 0.5;}; - }; - - # Prefer dark theme - prefer-no-csd = true; - - # Screenshot path - screenshot-path = "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"; - - # Hotkey overlay - hotkey-overlay = { - skip-at-startup = true; - }; - - # Spawn programs at startup - spawn-at-startup = [ - {command = ["mako"];} - {command = ["swayidle"];} - {command = ["ags" "run"];} - ]; - - # Window rules - window-rules = [ - { - matches = [{app-id = "^org\.gnome\.Nautilus$";}]; - default-column-width = {proportion = 0.333;}; - } - { - matches = [{app-id = "^pavucontrol$";}]; - default-column-width = {proportion = 0.333;}; - } - ]; - - # Keybindings - binds = { - "Mod+B".action.spawn = ["zen"]; - "Mod+Q".action.close-window = {}; - "Mod+Shift+E".action.quit = {}; - - # Launcher - "Mod+D".action.spawn = ["sh" "-c" "pkill wofi || wofi --show drun"]; - - # Network utility (NetworkManager TUI) - "Mod+N".action.spawn = ["kitty" "-e" "nmtui"]; - # Secrets manager (Seahorse - GNOME keyring GUI) - "Mod+K".action.spawn = ["seahorse"]; - # Terminal (Ghostty) - "Mod+T".action.spawn = ["ghostty"]; - - # Workspace navigation - "Mod+Page_Up".action.focus-workspace-up = {}; - "Mod+Page_Down".action.focus-workspace-down = {}; - - "Mod+1".action.focus-workspace = 1; - "Mod+2".action.focus-workspace = 2; - "Mod+3".action.focus-workspace = 3; - "Mod+4".action.focus-workspace = 4; - "Mod+5".action.focus-workspace = 5; - "Mod+6".action.focus-workspace = 6; - "Mod+7".action.focus-workspace = 7; - "Mod+8".action.focus-workspace = 8; - "Mod+9".action.focus-workspace = 9; - "Mod+0".action.focus-workspace = 10; - - # Move window to workspace - "Mod+Shift+1".action.move-window-to-workspace = 1; - "Mod+Shift+2".action.move-window-to-workspace = 2; - "Mod+Shift+3".action.move-window-to-workspace = 3; - "Mod+Shift+4".action.move-window-to-workspace = 4; - "Mod+Shift+5".action.move-window-to-workspace = 5; - "Mod+Shift+6".action.move-window-to-workspace = 6; - "Mod+Shift+7".action.move-window-to-workspace = 7; - "Mod+Shift+8".action.move-window-to-workspace = 8; - "Mod+Shift+9".action.move-window-to-workspace = 9; - "Mod+Shift+0".action.move-window-to-workspace = 10; - - # Window navigation - "Mod+Left".action.focus-column-left = {}; - "Mod+Right".action.focus-column-right = {}; - "Mod+Up".action.focus-window-up = {}; - "Mod+Down".action.focus-window-down = {}; - - # Window movement - "Mod+Ctrl+Left".action.move-column-left = {}; - "Mod+Ctrl+Right".action.move-column-right = {}; - "Mod+Ctrl+Up".action.move-window-up = {}; - "Mod+Ctrl+Down".action.move-window-down = {}; - - # Window resizing - "Mod+R".action.switch-preset-column-width = {}; - "Mod+Shift+R".action.reset-window-height = {}; - "Mod+F".action.maximize-column = {}; - - # Screenshots - "Print".action.spawn = ["grim"]; - "Mod+Print".action.spawn = ["sh" "-c" "grim -g \"$(slurp)\""]; - - # Screen lock - "Mod+L".action.spawn = ["swaylock"]; - - # Brightness control - "XF86MonBrightnessUp".action.spawn = ["brightnessctl" "set" "5%+"]; - "XF86MonBrightnessDown".action.spawn = ["brightnessctl" "set" "5%-"]; - - # Volume control - "XF86AudioRaiseVolume".action.spawn = ["wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+"]; - "XF86AudioLowerVolume".action.spawn = ["wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-"]; - "XF86AudioMute".action.spawn = ["wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"]; - - # Horizontal window scrolling with mod + scroll wheel - "Mod+WheelScrollDown".action.focus-column-right = {}; - "Mod+WheelScrollUp".action.focus-column-left = {}; - }; - }; + # Enable Niri and provide baseline settings; other modules merge into programs.niri.settings + programs.niri.settings = lib.mkDefault { + binds = {}; }; - # Session variables for proper integration + # Session variables for Wayland compatibility home.sessionVariables = { NIXOS_OZONE_WL = "1"; MOZ_ENABLE_WAYLAND = "1"; diff --git a/home/panotaka/common/optional/desktops/niri/fuzzel.nix b/home/panotaka/common/optional/desktops/niri/fuzzel.nix new file mode 100644 index 0000000..f2116d9 --- /dev/null +++ b/home/panotaka/common/optional/desktops/niri/fuzzel.nix @@ -0,0 +1,22 @@ +{pkgs, ...}: { + programs.fuzzel = { + enable = true; + package = pkgs.fuzzel; + + # Write a minimal fuzzel.ini using settings attribute; users can extend this + settings = { + main = { + terminal = "${pkgs.ghostty}/bin/ghostty"; + # Use UWSM app launcher wrapper for proper systemd slice management + launch-prefix = "uwsm app -- "; + }; + }; + }; + + # Add a recommended niri keybind to spawn fuzzel (Mod+Return) + # Launch fuzzel itself via UWSM for proper session integration + programs.niri.settings.binds."Mod+Return".action.spawn = ["uwsm" "app" "--" "${pkgs.fuzzel}/bin/fuzzel"]; + + # Add a keybind to launch the power menu (Mod+Shift+P) + programs.niri.settings.binds."Mod+Shift+P".action.spawn = ["uwsm" "app" "--" "$HOME/.local/bin/fuzzel-power-menu.sh"]; +} diff --git a/home/panotaka/common/optional/desktops/niri/keyring.nix b/home/panotaka/common/optional/desktops/niri/keyring.nix deleted file mode 100644 index 0cdb5fc..0000000 --- a/home/panotaka/common/optional/desktops/niri/keyring.nix +++ /dev/null @@ -1,26 +0,0 @@ -{pkgs, ...}: { - # Additional keyring packages for user interaction - home.packages = with pkgs; [ - kdePackages.kwalletmanager # KDE wallet manager GUI (Qt 6) - kdePackages.kwallet # KDE wallet library (Qt 6) - libsecret # Secret service API library - ]; - - # Session variables for proper keyring integration - home.sessionVariables = { - # SSH agent socket for SSH key management - SSH_AUTH_SOCK = "$XDG_RUNTIME_DIR/keyring/ssh"; - # Enable secret service D-Bus API - SECRET_SERVICE_API = "1"; - # Ensure proper D-Bus session bus integration - DBUS_SESSION_BUS_ADDRESS = "unix:path=$XDG_RUNTIME_DIR/bus"; - # Enable password store integration - PASSWORD_STORE_ENABLE_EXTENSIONS = "true"; - # Set keyring backend for KDE wallet - KEYRING_BACKEND = "kwallet"; - # Ensure XDG runtime directory is available - XDG_RUNTIME_DIR = "/run/user/1000"; - # KWallet integration - KWALLET_ENABLED = "1"; - }; -} diff --git a/home/panotaka/common/optional/desktops/niri/swayidle.nix b/home/panotaka/common/optional/desktops/niri/swayidle.nix new file mode 100644 index 0000000..426f746 --- /dev/null +++ b/home/panotaka/common/optional/desktops/niri/swayidle.nix @@ -0,0 +1,31 @@ +{pkgs, ...}: { + # Ensure swayidle and swaylock are available for the user + home.packages = [pkgs.swayidle pkgs.swaylock pkgs.inotify-tools]; + + # Use Home Manager's documented services.swayidle options + services.swayidle = { + enable = true; + package = pkgs.swayidle; + # Timeouts: + # - lock after 5 minutes + # - suspend after 15 minutes + # - hibernate after 60 minutes + timeouts = [ + { + timeout = 300; # 5 minutes + command = "${pkgs.swaylock}/bin/swaylock -f"; + resumeCommand = "swaymsg resume"; + } + { + timeout = 900; # 15 minutes + command = "systemctl suspend"; + resumeCommand = "swaymsg resume"; + } + { + timeout = 3600; # 60 minutes + command = "systemctl hibernate"; + resumeCommand = "swaymsg resume"; + } + ]; + }; +} diff --git a/home/panotaka/common/optional/desktops/niri/swaylock.nix b/home/panotaka/common/optional/desktops/niri/swaylock.nix new file mode 100644 index 0000000..fe2d7c1 --- /dev/null +++ b/home/panotaka/common/optional/desktops/niri/swaylock.nix @@ -0,0 +1,10 @@ +{pkgs, ...}: { + # Enable home-manager swaylock integration + programs.swaylock = { + enable = true; + }; + + # Add a keybind to spawn swaylock (Mod+L) + # Launch via UWSM for proper systemd integration + programs.niri.settings.binds."Mod+L".action.spawn = ["uwsm" "app" "--" "${pkgs.swaylock}/bin/swaylock"]; +} diff --git a/home/panotaka/common/optional/desktops/niri/waybar.nix b/home/panotaka/common/optional/desktops/niri/waybar.nix new file mode 100644 index 0000000..98ab2a6 --- /dev/null +++ b/home/panotaka/common/optional/desktops/niri/waybar.nix @@ -0,0 +1,54 @@ +{pkgs, ...}: let + waybarStyle = ''* { color: #ffffff; }''; +in { + # Use the programs.waybar home-manager module + programs.waybar = { + enable = true; + package = pkgs.waybar; + systemd.enable = true; + + # Minimal config as a Nix attribute set; users can override ~/.config/waybar/config if desired + settings = { + mainBar = { + layer = "top"; + position = "top"; + modules-left = ["niri/workspaces"]; + modules-center = []; + modules-right = ["tray" "clock"]; + + "niri/workspaces" = { + format = "{name}"; + }; + + clock = { + format = "{:%H:%M}"; + tooltip-format = "{:%Y %B}\n{calendar}"; + }; + }; + }; + # Minimal CSS; consider generating this from config.lib.stylix.colors for richer theming + style = waybarStyle; + }; + + # The config file is generated automatically by programs.waybar, so we don't need manual file writes + # Users can override by setting programs.waybar.settings directly in their config + + # Make sure nm-applet is available and autostarts so its tray icon shows up in the tray module + home.packages = with pkgs; [networkmanagerapplet]; + + # Start nm-applet as a systemd user service for UWSM integration + systemd.user.services.nm-applet = { + Unit = { + Description = "Network Manager Applet"; + PartOf = ["graphical-session.target"]; + After = ["graphical-session.target"]; + }; + Service = { + ExecStart = "${pkgs.networkmanagerapplet}/bin/nm-applet"; + Restart = "on-failure"; + }; + Install = { + WantedBy = ["graphical-session.target"]; + }; + }; +} diff --git a/home/panotaka/common/optional/desktops/niri/zenbook-debug.sh b/home/panotaka/common/optional/desktops/niri/zenbook-debug.sh new file mode 100644 index 0000000..d83a132 --- /dev/null +++ b/home/panotaka/common/optional/desktops/niri/zenbook-debug.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# Zenbook Duo Debug Script +# Run this to diagnose display/keyboard detection issues + +echo "=== Zenbook Duo Debug Info ===" +echo "" + +echo "1. USB Devices:" +echo " Looking for keyboard (0b05:1b2c)..." +lsusb | grep 0b05:1b2c && echo " ✓ Keyboard USB ID found!" || echo " ✗ Keyboard USB ID NOT found" +echo "" +echo " All ASUS devices:" +lsusb | grep -i asus || echo " (none)" +echo "" +echo " Full lsusb output:" +lsusb +echo "" + +echo "2. Niri Outputs:" +niri msg outputs || echo " ERROR: Could not get niri outputs" +echo "" + +echo "3. Display Check:" +echo " eDP-1 status:" +niri msg outputs | grep -A5 "eDP-1" || echo " Not found or off" +echo "" +echo " eDP-2 status:" +niri msg outputs | grep -A5 "eDP-2" || echo " Not found or off" +echo "" + +echo "4. Systemd Services:" +echo " zenbook-autostart:" +systemctl --user status zenbook-autostart | grep -E "Active:|Main PID:" || echo " Not running" +echo "" +echo " zenbook-inotify-monitor:" +systemctl --user status zenbook-inotify-monitor | grep -E "Active:|Main PID:" || echo " Not running" +echo "" + +echo "5. Log Files:" +echo " Recent lsusb log:" +[ -f /tmp/zenbook-lsusb.log ] && cat /tmp/zenbook-lsusb.log || echo " (no log file yet)" +echo "" + +echo "6. Test Commands:" +echo " Try running manually:" +echo " - zenbook-normal" +echo " - zenbook-top" +echo " - zenbook-both" +echo " - systemctl --user restart zenbook-inotify-monitor" +echo "" +echo " View live logs:" +echo " - journalctl --user -u zenbook-inotify-monitor -f" +echo "" diff --git a/home/panotaka/common/optional/desktops/niri/zenbook-screen.nix b/home/panotaka/common/optional/desktops/niri/zenbook-screen.nix index eb32e11..de702a5 100644 --- a/home/panotaka/common/optional/desktops/niri/zenbook-screen.nix +++ b/home/panotaka/common/optional/desktops/niri/zenbook-screen.nix @@ -9,64 +9,115 @@ ui_scale = "1.5"; y_offset = "1200"; + # Debug script + zenbook-debug = pkgs.writeShellScriptBin "zenbook-debug" '' + echo "=== Zenbook Duo Debug Info ===" + echo "" + echo "1. USB Devices:" + echo " Looking for keyboard (0b05:1b2c)..." + ${pkgs.usbutils}/bin/lsusb | grep 0b05:1b2c && echo " ✓ Keyboard USB ID found!" || echo " ✗ Keyboard USB ID NOT found" + echo "" + echo " All ASUS devices:" + ${pkgs.usbutils}/bin/lsusb | grep -i asus || echo " (none)" + echo "" + echo " Full lsusb output:" + ${pkgs.usbutils}/bin/lsusb + echo "" + echo "2. Niri Outputs:" + ${pkgs.niri}/bin/niri msg outputs || echo " ERROR: Could not get niri outputs" + echo "" + echo "3. Systemd Services:" + systemctl --user status zenbook-autostart 2>&1 | head -3 || echo " Not found" + systemctl --user status zenbook-inotify-monitor 2>&1 | head -3 || echo " Not found" + echo "" + echo "4. Log Files:" + [ -f /tmp/zenbook-lsusb.log ] && cat /tmp/zenbook-lsusb.log || echo " (no log file yet)" + echo "" + echo "5. Manual Test Commands:" + echo " - zenbook-normal # Run keyboard detection" + echo " - zenbook-top # Force top screen only" + echo " - zenbook-both # Force both screens" + echo " - journalctl --user -u zenbook-inotify-monitor -f # Watch logs" + ''; + # Inline scripts adapted for niri zenbook-top = pkgs.writeShellScriptBin "zenbook-top" '' - echo "Setting to top screen only" - ${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0 - ${pkgs.niri}/bin/niri msg output eDP-2 off + echo "[DEBUG] zenbook-top: Setting to top screen only" + echo "[DEBUG] Configuring eDP-1..." + ${pkgs.niri}/bin/niri msg output eDP-1 on || echo "[ERROR] Failed to turn on eDP-1" + ${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} || echo "[ERROR] Failed to set eDP-1 mode" + ${pkgs.niri}/bin/niri msg output eDP-1 scale ${ui_scale} || echo "[ERROR] Failed to set eDP-1 scale" + ${pkgs.niri}/bin/niri msg output eDP-1 position set 0 0 || echo "[ERROR] Failed to set eDP-1 position" + echo "[DEBUG] Turning off eDP-2..." + ${pkgs.niri}/bin/niri msg output eDP-2 off || echo "[ERROR] Failed to turn off eDP-2" + echo "[DEBUG] zenbook-top: Complete" ''; zenbook-bottom = pkgs.writeShellScriptBin "zenbook-bottom" '' echo "Setting to bottom screen only" - ${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0 + ${pkgs.niri}/bin/niri msg output eDP-2 on + ${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} + ${pkgs.niri}/bin/niri msg output eDP-2 scale ${ui_scale} + ${pkgs.niri}/bin/niri msg output eDP-2 position set 0 0 ${pkgs.niri}/bin/niri msg output eDP-1 off ''; zenbook-both = pkgs.writeShellScriptBin "zenbook-both" '' - echo "Setting to both screens" - ${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0 - ${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,${y_offset} + echo "[DEBUG] zenbook-both: Setting to both screens" + echo "[DEBUG] Configuring eDP-1..." + ${pkgs.niri}/bin/niri msg output eDP-1 on || echo "[ERROR] Failed to turn on eDP-1" + ${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} || echo "[ERROR] Failed to set eDP-1 mode" + ${pkgs.niri}/bin/niri msg output eDP-1 scale ${ui_scale} || echo "[ERROR] Failed to set eDP-1 scale" + ${pkgs.niri}/bin/niri msg output eDP-1 position set 0 0 || echo "[ERROR] Failed to set eDP-1 position" + echo "[DEBUG] Configuring eDP-2..." + ${pkgs.niri}/bin/niri msg output eDP-2 on || echo "[ERROR] Failed to turn on eDP-2" + ${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} || echo "[ERROR] Failed to set eDP-2 mode" + ${pkgs.niri}/bin/niri msg output eDP-2 scale ${ui_scale} || echo "[ERROR] Failed to set eDP-2 scale" + ${pkgs.niri}/bin/niri msg output eDP-2 position set 0 ${y_offset} || echo "[ERROR] Failed to set eDP-2 position" + echo "[DEBUG] zenbook-both: Complete" ''; zenbook-left-up = pkgs.writeShellScriptBin "zenbook-left-up" '' echo "Setting to left-right layout" - ${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0 - ${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position ${y_offset},0 + ${pkgs.niri}/bin/niri msg output eDP-2 on + ${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} + ${pkgs.niri}/bin/niri msg output eDP-2 scale ${ui_scale} + ${pkgs.niri}/bin/niri msg output eDP-2 position set 0 0 + ${pkgs.niri}/bin/niri msg output eDP-1 on + ${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} + ${pkgs.niri}/bin/niri msg output eDP-1 scale ${ui_scale} + ${pkgs.niri}/bin/niri msg output eDP-1 position set ${y_offset} 0 ''; zenbook-right-up = pkgs.writeShellScriptBin "zenbook-right-up" '' echo "Setting to right-left layout" - ${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0 - ${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position ${y_offset},0 + ${pkgs.niri}/bin/niri msg output eDP-1 on + ${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} + ${pkgs.niri}/bin/niri msg output eDP-1 scale ${ui_scale} + ${pkgs.niri}/bin/niri msg output eDP-1 position set 0 0 + ${pkgs.niri}/bin/niri msg output eDP-2 on + ${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} + ${pkgs.niri}/bin/niri msg output eDP-2 scale ${ui_scale} + ${pkgs.niri}/bin/niri msg output eDP-2 position set ${y_offset} 0 ''; zenbook-normal = pkgs.writeShellScriptBin "zenbook-normal" '' - # Check if lid is closed - lid_closed=false - if grep -q closed /proc/acpi/button/lid/*/state 2>/dev/null; then - lid_closed=true - fi + # Simple keyboard detection based on the original duo script + echo "[DEBUG] zenbook-normal: Starting detection" + echo "[DEBUG] Running lsusb to find keyboard..." + ${pkgs.usbutils}/bin/lsusb > /tmp/zenbook-lsusb.log + echo "[DEBUG] lsusb output saved to /tmp/zenbook-lsusb.log" - # Check if keyboard is attached (USB dock detection) - keyboard_attached=false - if ${pkgs.usbutils}/bin/lsusb | grep 0b05:1b2c; then - keyboard_attached=true - fi - - if [ "$lid_closed" = true ] && [ "$keyboard_attached" = true ]; then - echo "Lid closed and keyboard attached — disabling both screens" - ${pkgs.niri}/bin/niri msg output eDP-1 off - ${pkgs.niri}/bin/niri msg output eDP-2 off - elif [ "$lid_closed" = true ]; then - echo "Lid closed — disabling top screen" - ${pkgs.niri}/bin/niri msg output eDP-1 off - ${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0 - elif [ "$keyboard_attached" = true ]; then - echo "Keyboard attached — disabling bottom screen" - ${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0 - ${pkgs.niri}/bin/niri msg output eDP-2 off + # Check if keyboard is attached (USB device ID 0b05:1b2c) + if ${pkgs.usbutils}/bin/lsusb | grep -q 0b05:1b2c; then + echo "[DEBUG] Keyboard detected (0b05:1b2c found)" + echo "Keyboard attached — top screen only" + ${zenbook-top}/bin/zenbook-top else - echo "Normal mode — enabling both screens" + echo "[DEBUG] Keyboard NOT detected (0b05:1b2c not found)" + echo "[DEBUG] Checking for any ASUS devices..." + ${pkgs.usbutils}/bin/lsusb | grep -i asus || echo "[DEBUG] No ASUS devices found" + echo "Normal mode — both screens" ${zenbook-both}/bin/zenbook-both fi ''; @@ -81,75 +132,31 @@ fi ''; - # Event-driven display management (from your original script) + # Simple event-driven display management (based on original duo script) zenbook-watch-displays = pkgs.writeShellScriptBin "zenbook-watch-displays" '' + echo "[DEBUG] zenbook-watch-displays: Starting" + echo "[DEBUG] Running initial setup" + # Run initial setup ${zenbook-normal}/bin/zenbook-normal + + echo "[DEBUG] Starting USB device monitoring on /dev/bus/usb/*/" + # Watch for USB device changes (keyboard attach/detach) while ${pkgs.inotify-tools}/bin/inotifywait -e attrib /dev/bus/usb/*/; do + echo "[DEBUG] USB device change detected at $(date)" + sleep 0.5 ${zenbook-normal}/bin/zenbook-normal done ''; - zenbook-set-displays = pkgs.writeShellScriptBin "zenbook-set-displays" '' - sleep 1 + sleep 0.5 ${zenbook-normal}/bin/zenbook-normal ''; - # Enhanced lid monitoring with multiple detection methods - zenbook-lid-monitor = pkgs.writeShellScriptBin "zenbook-lid-monitor" '' - echo "Starting comprehensive lid monitoring..." - - # Method 1: Monitor ACPI events - (${pkgs.acpi}/bin/acpi_listen | while read event; do - if echo "$event" | grep -q "button/lid"; then - echo "ACPI lid event detected: $event" - sleep 0.5 && ${zenbook-set-displays}/bin/zenbook-set-displays - fi - done) & - - # Method 2: Poll lid state file directly - (while true; do - if [ -f /proc/acpi/button/lid/LID0/state ] || [ -f /proc/acpi/button/lid/LID/state ]; then - current_state=$(cat /proc/acpi/button/lid/*/state 2>/dev/null | head -1) - if [ "$current_state" != "$previous_state" ]; then - echo "Lid state changed: $current_state" - previous_state="$current_state" - sleep 0.5 && ${zenbook-set-displays}/bin/zenbook-set-displays - fi - fi - sleep 2 - done) & - - # Method 3: Monitor systemd logind events - (${pkgs.systemd}/bin/journalctl -f --user -u "*" | while read line; do - if echo "$line" | grep -i "lid\|suspend\|resume"; then - echo "Systemd event: $line" - sleep 1 && ${zenbook-set-displays}/bin/zenbook-set-displays - fi - done) & - - wait - ''; - - # Auto-run display setup on startup + # Simplified auto-run display setup on startup (based on original duo approach) zenbook-autostart = pkgs.writeShellScriptBin "zenbook-autostart" '' - # Initial display setup echo "Running initial display setup" + sleep 1 ${zenbook-set-displays}/bin/zenbook-set-displays - - # Wait a moment for system to stabilize - sleep 2 - - # Run display setup again to ensure proper configuration - echo "Running secondary display setup" - ${zenbook-set-displays}/bin/zenbook-set-displays - - # Start USB monitoring in background - echo "Starting USB monitoring for display changes" - ${zenbook-watch-displays}/bin/zenbook-watch-displays & - - # Start enhanced lid monitoring - echo "Starting enhanced lid monitoring" - ${zenbook-lid-monitor}/bin/zenbook-lid-monitor & ''; in { # Zenbook dual-screen display configuration for niri @@ -183,24 +190,89 @@ in { }; }; - # Auto-start the event-driven display management - spawn-at-startup = [ - { - command = ["${zenbook-autostart}/bin/zenbook-autostart"]; - } - { - command = ["sh" "-c" "sleep 5 && ${zenbook-set-displays}/bin/zenbook-set-displays"]; - } - { - command = ["sh" "-c" "while ${pkgs.inotify-tools}/bin/inotifywait -e modify /sys/class/drm/*/status 2>/dev/null; do sleep 1 && ${zenbook-set-displays}/bin/zenbook-set-displays; done"]; - } - { - command = ["sh" "-c" "${pkgs.udev}/bin/udevadm monitor --udev --subsystem-match=drm | while read line; do if echo \"$line\" | grep -q \"change\"; then sleep 2 && ${zenbook-set-displays}/bin/zenbook-set-displays; fi; done"]; - } - ]; + # Keybind to run zenbook-set-displays via UWSM + binds."Mod+Z".action.spawn = ["uwsm" "app" "--" "${zenbook-set-displays}/bin/zenbook-set-displays"]; + }; - # Keybind to run zenbook-set-displays - binds."Mod+Z".action.spawn = ["${zenbook-set-displays}/bin/zenbook-set-displays"]; + # Migrate to systemd user services for UWSM integration + systemd.user.services = { + # Initial display setup + zenbook-autostart = { + Unit = { + Description = "ZenBook Duo Initial Display Setup"; + After = ["graphical-session.target"]; + PartOf = ["graphical-session.target"]; + }; + Service = { + Type = "oneshot"; + ExecStart = "${zenbook-autostart}/bin/zenbook-autostart"; + RemainAfterExit = false; + }; + Install = { + WantedBy = ["graphical-session.target"]; + }; + }; + + # Secondary display setup after delay + zenbook-delayed-setup = { + Unit = { + Description = "ZenBook Duo Delayed Display Setup"; + After = ["graphical-session.target" "zenbook-autostart.service"]; + PartOf = ["graphical-session.target"]; + }; + Service = { + Type = "oneshot"; + ExecStartPre = "${pkgs.coreutils}/bin/sleep 5"; + ExecStart = "${zenbook-set-displays}/bin/zenbook-set-displays"; + RemainAfterExit = false; + }; + Install = { + WantedBy = ["graphical-session.target"]; + }; + }; + + # Monitor USB device changes for keyboard attach/detach + zenbook-inotify-monitor = { + Unit = { + Description = "ZenBook Duo USB Device Monitor"; + After = ["graphical-session.target"]; + PartOf = ["graphical-session.target"]; + }; + Service = { + Type = "simple"; + ExecStart = "${zenbook-watch-displays}/bin/zenbook-watch-displays"; + Restart = "always"; + RestartSec = "5"; + }; + Install = { + WantedBy = ["graphical-session.target"]; + }; + }; + + # Monitor udev events for display changes + zenbook-udev-monitor = { + Unit = { + Description = "ZenBook Duo udev Display Monitor"; + After = ["graphical-session.target"]; + PartOf = ["graphical-session.target"]; + }; + Service = { + Type = "simple"; + ExecStart = "${pkgs.writeShellScript "zenbook-udev-loop" '' + ${pkgs.udev}/bin/udevadm monitor --udev --subsystem-match=drm | while read line; do + if echo "$line" | grep -q "change"; then + sleep 2 + ${zenbook-set-displays}/bin/zenbook-set-displays + fi + done + ''}"; + Restart = "always"; + RestartSec = "5"; + }; + Install = { + WantedBy = ["graphical-session.target"]; + }; + }; }; # Hook into swayidle for unlock/resume events (if using swayidle) @@ -214,6 +286,7 @@ in { # Add the scripts to home packages home.packages = [ + zenbook-debug zenbook-top zenbook-bottom zenbook-both @@ -224,6 +297,5 @@ in { zenbook-watch-displays zenbook-set-displays zenbook-autostart - zenbook-lid-monitor ]; } diff --git a/hosts/common/optional/niri.nix b/hosts/common/optional/niri.nix index 935b5eb..a51bd10 100644 --- a/hosts/common/optional/niri.nix +++ b/hosts/common/optional/niri.nix @@ -5,10 +5,31 @@ }: { imports = [ inputs.niri.nixosModules.niri + (import ./niri/waybar.nix) + (import ./niri/swayidle.nix) + (import ./niri/fuzzel.nix) + (import ./niri/swaylock.nix) ]; programs.niri.enable = true; + # Enable UWSM (Universal Wayland Session Manager) for better session management + # See docs/uwsm-niri-integration.md for usage and migration guide + programs.uwsm = { + enable = true; + waylandCompositors = { + niri = { + prettyName = "Niri"; + comment = "Niri scrollable-tiling Wayland compositor managed by UWSM"; + binPath = "/run/current-system/sw/bin/niri-session"; + }; + }; + }; + + # UWSM provides better environment variable inheritance and systemd integration + # All startup applications are now managed via systemd.user.services with + # WantedBy=["graphical-session.target"] in the home-manager configuration + # Use KDE portal instead of GNOME for better KDE integration xdg.portal = { enable = true; diff --git a/hosts/common/optional/niri/fuzzel.nix b/hosts/common/optional/niri/fuzzel.nix new file mode 100644 index 0000000..5f1ad7d --- /dev/null +++ b/hosts/common/optional/niri/fuzzel.nix @@ -0,0 +1,8 @@ +{pkgs, ...}: { + environment.systemPackages = with pkgs; [fuzzel]; + + # Provide a simple system-wide wrapper script in /etc/profile.d or /etc to make fuzzel available + environment.etc."profile.d/fuzzel.sh".text = '' + export PATH="${pkgs.fuzzel}/bin:$PATH" + ''; +} diff --git a/hosts/common/optional/niri/swayidle.nix b/hosts/common/optional/niri/swayidle.nix new file mode 100644 index 0000000..bb4b1e3 --- /dev/null +++ b/hosts/common/optional/niri/swayidle.nix @@ -0,0 +1,13 @@ +{pkgs, ...}: { + # Install swayidle and configure a systemd user service for idle locking + environment.systemPackages = with pkgs; [swayidle swaylock inotify-tools]; + + systemd.user.services.swayidle = { + description = "swayidle for Niri sessions"; + wantedBy = ["default.target"]; + serviceConfig = { + ExecStart = "${pkgs.swayidle}/bin/swayidle -w 'timeout 300 ${pkgs.swaylock}/bin/swaylock -f' resume 'swaymsg resume'"; + Restart = "on-failure"; + }; + }; +} diff --git a/hosts/common/optional/niri/swaylock.nix b/hosts/common/optional/niri/swaylock.nix new file mode 100644 index 0000000..6dfd611 --- /dev/null +++ b/hosts/common/optional/niri/swaylock.nix @@ -0,0 +1,9 @@ +{pkgs, ...}: { + environment.systemPackages = with pkgs; [swaylock imagemagick]; + + # Provide a system-level helper in /etc/profile.d to point to a lock wrapper + environment.etc."profile.d/lockwrapper.sh".text = '' + #!/bin/sh + export LOCK_WRAPPER="${pkgs.swaylock}/bin/swaylock" + ''; +} diff --git a/hosts/common/optional/niri/waybar.nix b/hosts/common/optional/niri/waybar.nix new file mode 100644 index 0000000..47798b7 --- /dev/null +++ b/hosts/common/optional/niri/waybar.nix @@ -0,0 +1,23 @@ +{pkgs, ...}: { + # Install waybar system-wide and provide a systemd user service to start it for graphical users + environment.systemPackages = with pkgs; [waybar jq wl-clipboard]; + + systemd.user.services.waybar = { + description = "Waybar for Niri sessions"; + wantedBy = ["default.target"]; + serviceConfig = { + ExecStart = "${pkgs.waybar}/bin/waybar"; + Restart = "on-failure"; + RestartSec = 2; + }; + }; + + # Optionally provide a default system-level config in /etc/xdg/waybar/config + environment.etc."xdg/waybar/config".text = '' + { + "modules-left": ["workspace"], + "modules-center": [], + "modules-right": ["clock"] + } + ''; +} diff --git a/hosts/nixos/Bellerophon/default.nix b/hosts/nixos/Bellerophon/default.nix index 55ddbe5..20b8740 100644 --- a/hosts/nixos/Bellerophon/default.nix +++ b/hosts/nixos/Bellerophon/default.nix @@ -74,7 +74,7 @@ "hosts/common/optional/services/ollama.nix" "hosts/common/optional/services/docker.nix" "hosts/common/optional/audio.nix" # pipewire and cli controls - "hosts/common/optional/gnome.nix" + "hosts/common/optional/niri.nix" "hosts/common/optional/gdm.nix" "hosts/common/optional/flatpak.nix" "hosts/common/optional/thermal-management.nix"