diff --git a/AGENTS.md b/AGENTS.md index 69fc9e6..8284f36 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,7 +54,7 @@ Ansible-driven personal infrastructure repo for Void desktops, Linux workstation - Use `no_log: true` for secret-bearing task inputs or outputs. ## Desktop Notes -- `profile_desktop_common` owns the shared desktop bootstrap; `profile_desktop_i3` (X11) and `profile_desktop_sway` (Wayland/SwayFX) install their respective session in parallel and are both wired into the `void` play in `ansible/site.yml`. `desktop_sessions_enabled` and `desktop_default_session` in host vars decide which sessions are exposed in `emptty` and which is preselected. +- `profile_desktop_common` owns the shared desktop bootstrap; `profile_desktop_i3` (X11), `profile_desktop_sway` (Wayland/SwayFX), and `profile_desktop_hyprland` (Wayland) install their respective sessions in parallel and are wired into the `void` play in `ansible/site.yml`. `desktop_sessions_enabled` and `desktop_default_session` in host vars decide which sessions are exposed in `emptty` and which is preselected. - `.emacs.d` is deployed by a dedicated `profile_desktop_common` task tagged `emacs`. - NTFS filesystem support is provided by `ntfs-3g` in `ansible/inventory/group_vars/void.yml`. - Void user services are managed by `turnstile` and live under `dotfiles/desktop/.config/service/`. @@ -62,14 +62,15 @@ Ansible-driven personal infrastructure repo for Void desktops, Linux workstation - Critical session entrypoints: - `dotfiles/desktop/.xinitrc` (i3 / X11) - `dotfiles/desktop/.config/sway/config` plus `host.conf` and `session-env` deployed via `host_sway_dotfiles` (sway / Wayland) + - `dotfiles/desktop/.config/hypr/hyprland.conf` plus `host.conf` and `session-env` deployed via `host_hyprland_dotfiles` (Hyprland / Wayland) - Do not auto-restart `emptty` during playbook runs on active Void desktop hosts; restart it manually from another TTY/SSH session if needed. -- `nymph` is a Void laptop with NVIDIA Optimus, running both i3 (X11) and sway (Wayland); host-specific tasks in `profile_desktop_host/tasks/nymph.yml` handle GRUB NVIDIA cmdline params, `prime-run` wrapper, and the WirePlumber camera priority config. Multi-monitor on X11 is driven by `autorandr`; on sway it is driven by `kanshi` (config deployed via `host_sway_dotfiles`). +- `nymph` is a Void laptop with NVIDIA Optimus, running sway (Wayland) and Hyprland (Wayland); host-specific tasks in `profile_desktop_host/tasks/nymph.yml` handle GRUB NVIDIA cmdline params, `prime-run` wrapper, and the WirePlumber camera priority config. Multi-monitor on X11 is driven by `autorandr`; on sway it is driven by `kanshi` (config deployed via `host_sway_dotfiles`), and on Hyprland by host-specific Hyprland monitor/workspace rules. ## Void Package And Dotfile Bucket Rules The four Void desktop package lists in `ansible/inventory/group_vars/void.yml` are kept disjoint by role: - `void_packages_base` — system runtime only (init/services, kernel, audio core, networking, filesystem, firewall, hardware daemons, runit logging). - `desktop_common_packages` — WM-agnostic GUI infra (GTK theme, polkit, keyring, NM-applet, blueman, audio GUI, file manager backend + GVFS, portal, flatpak, printing/scanning). -- `desktop_i3_packages` / `desktop_sway_packages` — binaries specific to each session. Cross-WM packages used by both (`dunst`, `rofi`, `alacritty`) are intentionally duplicated in the two lists. +- `desktop_i3_packages` / `desktop_sway_packages` / `desktop_hyprland_packages` — binaries specific to each session. Cross-WM packages used by both (`dunst`, `rofi`, `alacritty`) are intentionally duplicated in the two lists. `profile_packages` in the same file is cross-distro and is overridden by `group_vars/server.yml` and the workstation group vars; do not move desktop-specific Void entries through it. The dotfile vars follow the same split: `desktop_common_dotfiles` (in `group_vars/desktop.yml`) carries WM-agnostic GUI config and user scripts (Thunar, mimeapps, GTK theme setup, Udiskie, fontconfig, WirePlumber, mpv, …); `desktop_void_dotfiles` (in `group_vars/void.yml`) is reserved for files that need Void runtime (turnstile services, bash runit/dbus/ssh-agent fragments, `update-turnstile-env`). diff --git a/README.md b/README.md index 8ac7cd4..b2b9b39 100644 --- a/README.md +++ b/README.md @@ -72,25 +72,25 @@ Sistema operativo: Sessioni desktop: -- `ikaros`: i3 (X11) + sway (Wayland), default i3 -- `nymph`: i3 (X11) + sway (Wayland), default i3 +- `ikaros`: sway (Wayland) + Hyprland (Wayland), default sway +- `nymph`: sway (Wayland) + Hyprland (Wayland), default sway Macchine: - `ikaros` - `nymph` -Queste macchine condividono la stessa configurazione base desktop e vengono mantenute allineate tramite Ansible. Sia i3 sia sway/SwayFX sono installate in parallelo e selezionabili da `emptty`; `desktop_default_session` decide quale sessione viene preselezionata al login. +Queste macchine condividono la stessa configurazione base desktop e vengono mantenute allineate tramite Ansible. Sway/SwayFX e Hyprland sono installate in parallelo e selezionabili da `emptty`; `desktop_default_session` decide quale sessione viene preselezionata al login. Lo stato attuale del profilo desktop include, tra le altre cose: - dotfiles comuni e desktop -- sessioni i3 e sway su entrambi gli host (default i3, sway disponibile via `emptty`) -- `emptty` con default host-specific per il desktop attivo, con `XORG_SESSIONS_PATH` puntato a `/etc/emptty/xsessions` e session file Wayland esposti su `WAYLAND_SESSIONS_PATH` per `sway` +- sessioni sway e Hyprland su entrambi gli host (default sway, Hyprland disponibile via `emptty`) +- `emptty` con default host-specific per il desktop attivo, con `XORG_SESSIONS_PATH` puntato a `/etc/emptty/xsessions` e session file Wayland esposti su `WAYLAND_SESSIONS_PATH` per `sway` e `hyprland` - pacchetti Void Linux e servizi runit; le liste pacchetti Void desktop sono separate per criterio: - `void_packages_base` per il runtime sistema (init, kernel, audio core, networking, firewall, hw daemons) - `desktop_common_packages` per l'infrastruttura GUI WM-agnostic (tema, polkit, keyring, NM-applet, blueman, audio GUI, file manager backend, portal, flatpak, printing/scanning) - - `desktop_i3_packages` e `desktop_sway_packages` per i binari specifici di ciascuna sessione + - `desktop_i3_packages`, `desktop_sway_packages` e `desktop_hyprland_packages` per i binari specifici di ciascuna sessione - `turnstile` per i servizi utente, inclusi `emacs` e `ssh-agent` - `ssh-agent` con socket stabile condiviso tra shell, SSH ed Emacs in `~/.local/state/ssh-agent/socket` - `.emacs.d` distribuito da un task dedicato Ansible con tag `emacs` @@ -98,7 +98,7 @@ Lo stato attuale del profilo desktop include, tra le altre cose: - Flatpak con remoto Flathub - GNOME Keyring e bootstrap della posta via script dedicato - `udiskie` come backend per automount/LUKS -- `autorandr` per profili monitor X11 host-specifici (`nymph` ha profili `dual` e `solo`); sotto sway il multi-monitor è gestito da `kanshi` (config host-specifica in `host_sway_dotfiles` su `nymph`) +- `autorandr` per profili monitor X11 host-specifici (`nymph` ha profili `dual` e `solo`); sotto sway il multi-monitor è gestito da `kanshi` (config host-specifica in `host_sway_dotfiles` su `nymph`); sotto Hyprland gli override monitor vivono in `host_hyprland_dotfiles` - override NVIDIA Optimus su `nymph`: parametri kernel GRUB iniettati in modo idempotente in `GRUB_CMDLINE_LINUX`, wrapper `prime-run` e config WirePlumber per priorità telecamera --- @@ -276,6 +276,7 @@ I principali ruoli attualmente presenti sono: | profile_desktop_common | bootstrap desktop Void condiviso | | profile_desktop_i3 | sessione desktop i3 (X11) | | profile_desktop_sway | sessione desktop sway / SwayFX (Wayland) | +| profile_desktop_hyprland | sessione desktop Hyprland (Wayland) | | profile_desktop_host | override desktop specifici per host | | profile_workstation_dev_common | configurazione dev workstation condivisa | | profile_workstation_gnome | configurazione host workstation GNOME | @@ -293,7 +294,7 @@ Il playbook `ansible/site.yml` e attualmente composto da sette blocchi: ```text all:!workstation_host_windows -> dotfiles_common -void -> packages_void + services_runit + profile_desktop_common + profile_desktop_i3 + profile_desktop_sway + profile_desktop_host +void -> packages_void + services_runit + profile_desktop_common + profile_desktop_i3 + profile_desktop_sway + profile_desktop_hyprland + profile_desktop_host workstation_dev_ubuntu -> packages_ubuntu + services_systemd + profile_workstation_dev_common workstation_dev_fedora -> packages_fedora + services_systemd + profile_workstation_dev_common workstation_host_linux -> profile_workstation_gnome @@ -445,6 +446,7 @@ Allo stato attuale `ansible/site.yml` espone questi tag: | `gnome` | configurazione host GNOME | workstation host Linux, parte desktop | | `i3` | sessione/configurazione i3 (X11) | desktop Void | | `sway` | sessione/configurazione sway / SwayFX (Wayland) | desktop Void | +| `hyprland` | sessione/configurazione Hyprland (Wayland) | desktop Void | | `npm` | installazione pacchetti npm globali | desktop Void, workstation Linux, WSL | | `nvidia` | componenti NVIDIA desktop | desktop Void | | `packages` | installazione e aggiornamento pacchetti | tutti i profili | @@ -457,6 +459,7 @@ Esempi pratici: ```bash ansible-playbook ansible/site.yml --limit nymph --tags dotfiles:desktop,i3 --check --diff ansible-playbook ansible/site.yml --limit ikaros --tags sway,portal --check --diff +ansible-playbook ansible/site.yml --limit ikaros --tags hyprland,portal --check --diff ansible-playbook ansible/site.yml --limit deadalus-fedora --tags packages,vscode --check --diff ansible-playbook ansible/site.yml --limit prometheus --tags services,dotfiles:server --check --diff ``` diff --git a/ansible/inventory/group_vars/desktop.yml b/ansible/inventory/group_vars/desktop.yml index a660b12..552bbe0 100644 --- a/ansible/inventory/group_vars/desktop.yml +++ b/ansible/inventory/group_vars/desktop.yml @@ -3,6 +3,7 @@ desktop_protonmail_bridge_cert_path: ~/.var/app/ch.protonmail.protonmail-bridge/ desktop_sessions_enabled: - sway + - hyprland desktop_default_session: sway desktop_default_session_env: wayland @@ -215,6 +216,36 @@ desktop_sway_dotfiles: dest: .config/gtklock/ mode: preserve +desktop_hyprland_dotfiles: + - name: alacritty config + src: .config/alacritty/ + dest: .config/alacritty/ + mode: preserve + - name: dunst config + src: .config/dunst/ + dest: .config/dunst/ + mode: preserve + - name: flameshot config + src: .config/flameshot/ + dest: .config/flameshot/ + mode: preserve + - name: rofi config + src: .config/rofi/ + dest: .config/rofi/ + mode: preserve + - name: hyprland config + src: .config/hypr/ + dest: .config/hypr/ + mode: preserve + - name: waybar config + src: .config/waybar/ + dest: .config/waybar/ + mode: preserve + - name: foot config + src: .config/foot/ + dest: .config/foot/ + mode: preserve + desktop_flatpak_packages: - be.alexandervanhee.gradia - ch.protonmail.protonmail-bridge diff --git a/ansible/inventory/group_vars/void.yml b/ansible/inventory/group_vars/void.yml index e187b72..6c66d28 100644 --- a/ansible/inventory/group_vars/void.yml +++ b/ansible/inventory/group_vars/void.yml @@ -1,4 +1,8 @@ --- +void_xbps_repositories: + - name: hyprland-void + url: https://raw.githubusercontent.com/sofijacom/hyprland-void/repository-x86_64-glibc + desktop_void_source_tools: - name: st repo: https://codeberg.org/fscotto/st @@ -175,6 +179,25 @@ desktop_sway_packages: - xdg-desktop-portal-wlr - xorg-server-xwayland +desktop_hyprland_packages: + - cliphist + - dunst + - foot + - foot-terminfo + - grim + - hypridle + - hyprland + - hyprland-guiutils + - hyprlock + - hyprpaper + - rofi + - slurp + - SwayOSD + - Waybar + - wl-clipboard + - xdg-desktop-portal-hyprland + - xorg-server-xwayland + profile_packages: - alacritty - bluez diff --git a/ansible/inventory/host_vars/ikaros.yml b/ansible/inventory/host_vars/ikaros.yml index 6f424f5..bf15bc6 100644 --- a/ansible/inventory/host_vars/ikaros.yml +++ b/ansible/inventory/host_vars/ikaros.yml @@ -3,6 +3,7 @@ hostname: ikaros desktop_sessions_enabled: - sway + - hyprland desktop_default_session: sway desktop_default_session_env: wayland diff --git a/ansible/inventory/host_vars/nymph.yml b/ansible/inventory/host_vars/nymph.yml index a031cbf..44e30fd 100644 --- a/ansible/inventory/host_vars/nymph.yml +++ b/ansible/inventory/host_vars/nymph.yml @@ -3,6 +3,7 @@ hostname: nymph desktop_sessions_enabled: - sway + - hyprland desktop_default_session: sway desktop_default_session_env: wayland @@ -44,3 +45,14 @@ host_sway_dotfiles: - src: .config/wireplumber/wireplumber.conf.d/60-camera-priority.conf dest: .config/wireplumber/wireplumber.conf.d/60-camera-priority.conf mode: "0644" + +host_hyprland_dotfiles: + - src: .config/hypr/host.conf + dest: .config/hypr/host.conf + mode: "0644" + - src: .config/hypr/session-env + dest: .config/hypr/session-env + mode: "0644" + - src: .config/wireplumber/wireplumber.conf.d/60-camera-priority.conf + dest: .config/wireplumber/wireplumber.conf.d/60-camera-priority.conf + mode: "0644" diff --git a/ansible/roles/packages_void/tasks/main.yml b/ansible/roles/packages_void/tasks/main.yml index 4f0f422..1caa5c7 100644 --- a/ansible/roles/packages_void/tasks/main.yml +++ b/ansible/roles/packages_void/tasks/main.yml @@ -5,9 +5,9 @@ path: /etc/xbps.d state: directory mode: "0755" - when: (host_xbps_repositories | default([])) | length > 0 + when: (void_xbps_repositories | default([]) + host_xbps_repositories | default([])) | length > 0 -- name: Configure host-specific XBPS repositories +- name: Configure Void XBPS repositories tags: [packages] ansible.builtin.copy: dest: "/etc/xbps.d/{{ item.name }}.conf" @@ -16,11 +16,11 @@ owner: root group: root mode: "0644" - loop: "{{ host_xbps_repositories | default([]) }}" + loop: "{{ void_xbps_repositories | default([]) + host_xbps_repositories | default([]) }}" loop_control: label: "{{ item.name }}" - register: host_xbps_repository_configs - when: (host_xbps_repositories | default([])) | length > 0 + register: void_xbps_repository_configs + when: (void_xbps_repositories | default([]) + host_xbps_repositories | default([])) | length > 0 - name: Install Void nonfree repository if needed tags: [packages] @@ -37,7 +37,7 @@ update_cache: true accept_pubkey: true when: >- - (host_xbps_repositories | default([])) | length > 0 + (void_xbps_repositories | default([]) + host_xbps_repositories | default([])) | length > 0 or (void_nonfree_repository is defined and void_nonfree_repository is changed) - name: Synchronize and upgrade Void Linux packages @@ -66,6 +66,11 @@ if 'sway' in (desktop_sessions_enabled | default([])) else [] ) + + ( + (desktop_hyprland_packages | default([])) + if 'hyprland' in (desktop_sessions_enabled | default([])) + else [] + ) + (host_packages | default([])) ) | unique diff --git a/ansible/roles/profile_desktop_host/tasks/main.yml b/ansible/roles/profile_desktop_host/tasks/main.yml index 8bc0567..65ca82e 100644 --- a/ansible/roles/profile_desktop_host/tasks/main.yml +++ b/ansible/roles/profile_desktop_host/tasks/main.yml @@ -34,3 +34,18 @@ - "'sway' in (desktop_sessions_enabled | default([]))" - (host_sway_dotfiles | default([])) | length > 0 + +- name: Copy host-specific Hyprland dotfiles + tags: [dotfiles, dotfiles:desktop, dotfiles:host, hyprland] + ansible.builtin.copy: + src: "{{ playbook_dir }}/../dotfiles/{{ hostname }}/{{ item.src }}" + dest: "{{ user_home }}/{{ item.dest }}" + owner: "{{ username }}" + group: "{{ user_group }}" + mode: "{{ item.mode }}" + loop: "{{ host_hyprland_dotfiles | default([]) }}" + loop_control: + label: "{{ item.dest }}" + when: + - "'hyprland' in (desktop_sessions_enabled | default([]))" + - (host_hyprland_dotfiles | default([])) | length > 0 diff --git a/ansible/roles/profile_desktop_hyprland/files/hyprland-portals.conf b/ansible/roles/profile_desktop_hyprland/files/hyprland-portals.conf new file mode 100644 index 0000000..9ba090c --- /dev/null +++ b/ansible/roles/profile_desktop_hyprland/files/hyprland-portals.conf @@ -0,0 +1,4 @@ +[preferred] +default=gtk +org.freedesktop.impl.portal.Screenshot=hyprland +org.freedesktop.impl.portal.ScreenCast=hyprland diff --git a/ansible/roles/profile_desktop_hyprland/files/hyprland.desktop b/ansible/roles/profile_desktop_hyprland/files/hyprland.desktop new file mode 100644 index 0000000..c48504b --- /dev/null +++ b/ansible/roles/profile_desktop_hyprland/files/hyprland.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=Hyprland +Comment=Hyprland (Wayland) +Exec=/usr/local/bin/start-hyprland-session +Type=Application +DesktopNames=Hyprland diff --git a/ansible/roles/profile_desktop_hyprland/tasks/main.yml b/ansible/roles/profile_desktop_hyprland/tasks/main.yml new file mode 100644 index 0000000..8badcf3 --- /dev/null +++ b/ansible/roles/profile_desktop_hyprland/tasks/main.yml @@ -0,0 +1,78 @@ +--- +- name: Ensure Hyprland config directories exist + tags: [dotfiles, dotfiles:desktop, hyprland] + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: "{{ username }}" + group: "{{ user_group }}" + mode: "0755" + loop: + - "{{ user_home }}/.config/alacritty" + - "{{ user_home }}/.config/dunst" + - "{{ user_home }}/.config/flameshot" + - "{{ user_home }}/.config/foot" + - "{{ user_home }}/.config/hypr" + - "{{ user_home }}/.config/rofi" + - "{{ user_home }}/.config/waybar" + when: "'hyprland' in (desktop_sessions_enabled | default([]))" + +- name: Install allowed emptty Hyprland Wayland session + tags: [packages, services, emptty, hyprland] + ansible.builtin.copy: + src: hyprland.desktop + dest: /etc/emptty/wayland-sessions/hyprland.desktop + owner: root + group: root + mode: "0644" + when: "'hyprland' in (desktop_sessions_enabled | default([]))" + +- name: Remove legacy Hyprland session launcher shadowing package binary + tags: [packages, services, hyprland] + ansible.builtin.file: + path: /usr/local/bin/start-hyprland + state: absent + when: "'hyprland' in (desktop_sessions_enabled | default([]))" + +- name: Install Hyprland session launcher in /usr/local/bin + tags: [packages, services, hyprland] + ansible.builtin.copy: + src: "{{ playbook_dir }}/../dotfiles/desktop/.local/bin/start-hyprland-session" + dest: /usr/local/bin/start-hyprland-session + owner: root + group: root + mode: "0755" + when: "'hyprland' in (desktop_sessions_enabled | default([]))" + +- name: Ensure /etc/xdg/xdg-desktop-portal exists for Hyprland + tags: [packages, services, hyprland, portal] + ansible.builtin.file: + path: /etc/xdg/xdg-desktop-portal + state: directory + owner: root + group: root + mode: "0755" + when: "'hyprland' in (desktop_sessions_enabled | default([]))" + +- name: Configure xdg-desktop-portal backend preferences for Hyprland + tags: [packages, services, hyprland, portal] + ansible.builtin.copy: + src: hyprland-portals.conf + dest: /etc/xdg/xdg-desktop-portal/hyprland-portals.conf + owner: root + group: root + mode: "0644" + when: "'hyprland' in (desktop_sessions_enabled | default([]))" + +- name: Copy Hyprland desktop dotfiles + tags: [dotfiles, dotfiles:desktop, hyprland] + ansible.builtin.copy: + src: "{{ playbook_dir }}/../dotfiles/desktop/{{ item.src }}" + dest: "{{ user_home }}/{{ item.dest }}" + owner: "{{ username }}" + group: "{{ user_group }}" + mode: "{{ item.mode }}" + loop: "{{ desktop_hyprland_dotfiles | default([]) }}" + loop_control: + label: "{{ item.dest }}" + when: "'hyprland' in (desktop_sessions_enabled | default([]))" diff --git a/ansible/site.yml b/ansible/site.yml index 3a754ae..627dfbf 100644 --- a/ansible/site.yml +++ b/ansible/site.yml @@ -26,6 +26,7 @@ - profile_desktop_common - profile_desktop_i3 - profile_desktop_sway + - profile_desktop_hyprland - profile_desktop_host - hosts: workstation_dev_ubuntu diff --git a/dotfiles/desktop/.config/hypr/host.conf b/dotfiles/desktop/.config/hypr/host.conf new file mode 100644 index 0000000..f31af54 --- /dev/null +++ b/dotfiles/desktop/.config/hypr/host.conf @@ -0,0 +1,3 @@ +# Per-host Hyprland overrides. Empty by default; hosts that need tuning +# ship their own version via host_hyprland_dotfiles, which overwrites this +# placeholder during deploy. diff --git a/dotfiles/desktop/.config/hypr/hypridle.conf b/dotfiles/desktop/.config/hypr/hypridle.conf new file mode 100644 index 0000000..2411e9c --- /dev/null +++ b/dotfiles/desktop/.config/hypr/hypridle.conf @@ -0,0 +1,16 @@ +general { + lock_cmd = pidof hyprlock || hyprlock + before_sleep_cmd = loginctl lock-session + after_sleep_cmd = hyprctl dispatch dpms on +} + +listener { + timeout = 300 + on-timeout = loginctl lock-session +} + +listener { + timeout = 600 + on-timeout = hyprctl dispatch dpms off + on-resume = hyprctl dispatch dpms on +} diff --git a/dotfiles/desktop/.config/hypr/hyprland.conf b/dotfiles/desktop/.config/hypr/hyprland.conf new file mode 100644 index 0000000..776e492 --- /dev/null +++ b/dotfiles/desktop/.config/hypr/hyprland.conf @@ -0,0 +1,226 @@ +# Hyprland config, aligned with the Sway desktop profile. + +$mod = SUPER +$terminal = alacritty +$browser = firefox + +monitor = ,preferred,auto,1 + +env = XCURSOR_THEME,Yaru +env = XCURSOR_SIZE,24 + +input { + kb_layout = us + kb_variant = intl + follow_mouse = 0 +} + +cursor { + no_hardware_cursors = false +} + +general { + gaps_in = 10 + gaps_out = 4 + border_size = 2 + col.active_border = rgba(4a90d9ff) + col.inactive_border = rgba(2b2b36ff) + layout = master +} + +decoration { + rounding = 8 + + blur { + enabled = true + size = 5 + passes = 1 + } + + shadow { + enabled = true + } + + dim_inactive = true + dim_strength = 0.1 +} + +animations { + enabled = true + bezier = easeOutQuint,0.23,1,0.32,1 + animation = windows,1,2,easeOutQuint,popin 85% + animation = border,1,2,easeOutQuint + animation = fade,1,2,easeOutQuint + animation = workspaces,1,2,easeOutQuint,slidevert + animation = layers,1,2,easeOutQuint,fade +} + +master { + new_status = slave + orientation = left +} + +misc { + disable_hyprland_logo = true + disable_splash_rendering = true + force_default_wallpaper = 0 + focus_on_activate = true +} + +xwayland { + enabled = true +} + +# Per-host overrides (terminal wrapper, env, monitors, workspace rules, etc.). +source = ~/.config/hypr/host.conf + +# Window rules +windowrule = float on, match:class (?i)(polkit) +windowrule = float on, match:title ^Authentication Required$ +windowrule = float on, match:title ^Open File$ +windowrule = float on, match:title ^Save File$ +windowrule = float on, match:class (?i)(blueman-manager) +windowrule = float on, match:class (?i)(nm-connection-editor) +windowrule = float on, match:class (?i)(pinentry) +windowrule = float on, match:class (?i)(firefox), match:title ^Library$ +windowrule = float on, match:class (?i)(firefox), match:title ^About Mozilla Firefox$ +windowrule = float on, match:class org.freedesktop.impl.portal.desktop.gtk +windowrule = idle_inhibit fullscreen, match:fullscreen true +windowrule = idle_inhibit focus, match:content 2 + +# Autostart — XDG and session services +exec-once = dbus-update-activation-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP XDG_SESSION_TYPE XDG_SESSION_DESKTOP +exec-once = dex --autostart --environment Hyprland +exec-once = gnome-keyring-daemon --start --components=secrets +exec-once = ~/.local/bin/setup-gtk-theme +exec-once = /usr/libexec/xdg-desktop-portal +exec-once = /usr/libexec/xdg-desktop-portal-hyprland +exec-once = /usr/libexec/xdg-desktop-portal-gtk + +# Audio/session services +exec-once = pipewire +exec-once = pipewire-pulse +exec-once = wireplumber + +# Desktop services +exec-once = hyprctl setcursor Yaru 24 +exec-once = hyprpaper +exec-once = waybar -c ~/.config/waybar/hyprland.jsonc +exec-once = swayosd-server +exec-once = dunst +exec-once = /usr/libexec/xfce-polkit +exec-once = nm-applet +exec-once = blueman-applet +exec-once = udiskie +exec-once = hypridle + +# Clipboard manager +exec-once = wl-paste --type text --watch cliphist store +exec-once = wl-paste --type image --watch cliphist store + +# Applications +bind = $mod, Return, exec, $terminal +bind = $mod SHIFT, Return, exec, foot +bind = $mod SHIFT, E, exec, emacsclient -c -n +bind = $mod, D, exec, rofi -show drun -theme ~/.config/rofi/config.rasi +bind = $mod, C, exec, cliphist list | rofi -dmenu -theme ~/.config/rofi/config.rasi | cliphist decode | wl-copy +bind = $mod SHIFT, V, exec, pavucontrol +bind = $mod SHIFT, F, exec, thunar +bind = $mod SHIFT, D, exec, dunstctl set-paused toggle +bind = $mod SHIFT, X, exec, hyprlock +bind = $mod SHIFT, Q, killactive + +# Screenshots (Hyprland-native: grim/slurp/wl-copy; Flameshot is unstable here) +bind = , Print, exec, ~/.config/hypr/scripts/screenshot region +bind = SHIFT, Print, exec, ~/.config/hypr/scripts/screenshot full +bind = ALT, Print, exec, ~/.config/hypr/scripts/screenshot output + +# Focus +bind = $mod, H, movefocus, l +bind = $mod, J, movefocus, d +bind = $mod, K, movefocus, u +bind = $mod, L, movefocus, r +bind = $mod, left, movefocus, l +bind = $mod, down, movefocus, d +bind = $mod, up, movefocus, u +bind = $mod, right, movefocus, r + +# Move windows +bind = $mod SHIFT, H, movewindow, l +bind = $mod SHIFT, J, movewindow, d +bind = $mod SHIFT, K, movewindow, u +bind = $mod SHIFT, L, movewindow, r +bind = $mod SHIFT, left, movewindow, l +bind = $mod SHIFT, down, movewindow, d +bind = $mod SHIFT, up, movewindow, u +bind = $mod SHIFT, right, movewindow, r + +# Layouts / floating +bind = $mod, F, fullscreen +bind = $mod SHIFT, Space, togglefloating +bind = $mod, Space, cyclenext +bind = $mod, A, layoutmsg, swapwithmaster +bindm = $mod, mouse:272, movewindow +bindm = $mod, mouse:273, resizewindow + +# Scratchpad-like special workspace +bind = $mod SHIFT, minus, movetoworkspace, special +bind = $mod, minus, togglespecialworkspace + +# Volume / brightness / media +bind = , XF86AudioRaiseVolume, exec, swayosd-client --output-volume raise +bind = , XF86AudioLowerVolume, exec, swayosd-client --output-volume lower +bind = , XF86AudioMute, exec, swayosd-client --output-volume mute-toggle +bind = , XF86AudioMicMute, exec, swayosd-client --input-volume mute-toggle +bind = , XF86MonBrightnessUp, exec, swayosd-client --brightness raise +bind = , XF86MonBrightnessDown, exec, swayosd-client --brightness lower +bind = , XF86AudioPlay, exec, playerctl play-pause +bind = , XF86AudioNext, exec, playerctl next +bind = , XF86AudioPrev, exec, playerctl previous + +# Workspaces +bind = $mod, 1, workspace, 1 +bind = $mod, 2, workspace, 2 +bind = $mod, 3, workspace, 3 +bind = $mod, 4, workspace, 4 +bind = $mod, 5, workspace, 5 +bind = $mod, 6, workspace, 6 +bind = $mod, 7, workspace, 7 +bind = $mod, 8, workspace, 8 +bind = $mod, 9, workspace, 9 +bind = $mod, 0, workspace, 10 +bind = $mod, comma, exec, ~/.config/hypr/scripts/workspace-cycle prev +bind = $mod, period, exec, ~/.config/hypr/scripts/workspace-cycle next + +bind = $mod SHIFT, 1, movetoworkspace, 1 +bind = $mod SHIFT, 2, movetoworkspace, 2 +bind = $mod SHIFT, 3, movetoworkspace, 3 +bind = $mod SHIFT, 4, movetoworkspace, 4 +bind = $mod SHIFT, 5, movetoworkspace, 5 +bind = $mod SHIFT, 6, movetoworkspace, 6 +bind = $mod SHIFT, 7, movetoworkspace, 7 +bind = $mod SHIFT, 8, movetoworkspace, 8 +bind = $mod SHIFT, 9, movetoworkspace, 9 +bind = $mod SHIFT, 0, movetoworkspace, 10 + +# Reload / exit +bind = $mod SHIFT, C, exec, hyprctl reload +bind = $mod SHIFT, R, exec, hyprctl reload +bind = $mod SHIFT, BackSpace, exit +bind = $mod SHIFT, Escape, exec, ~/.config/hypr/scripts/powermenu.sh + +# Resize submap +bind = $mod, R, submap, resize +submap = resize +bind = , H, resizeactive, -10 0 +bind = , J, resizeactive, 0 10 +bind = , K, resizeactive, 0 -10 +bind = , L, resizeactive, 10 0 +bind = , left, resizeactive, -10 0 +bind = , down, resizeactive, 0 10 +bind = , up, resizeactive, 0 -10 +bind = , right, resizeactive, 10 0 +bind = , Return, submap, reset +bind = , Escape, submap, reset +bind = $mod, R, submap, reset +submap = reset diff --git a/dotfiles/desktop/.config/hypr/hyprlock.conf b/dotfiles/desktop/.config/hypr/hyprlock.conf new file mode 100644 index 0000000..e2000c6 --- /dev/null +++ b/dotfiles/desktop/.config/hypr/hyprlock.conf @@ -0,0 +1,26 @@ +general { + hide_cursor = true +} + +background { + monitor = + path = ~/.config/hypr/wallpapers/maxresdefault.jpg + blur_passes = 2 + blur_size = 4 +} + +input-field { + monitor = + size = 300, 48 + outline_thickness = 2 + dots_size = 0.25 + dots_spacing = 0.20 + outer_color = rgba(4a90d9ff) + inner_color = rgba(1f1f28dd) + font_color = rgba(ffffffff) + fade_on_empty = false + placeholder_text = Password + position = 0, -80 + halign = center + valign = center +} diff --git a/dotfiles/desktop/.config/hypr/hyprpaper.conf b/dotfiles/desktop/.config/hypr/hyprpaper.conf new file mode 100644 index 0000000..413db7b --- /dev/null +++ b/dotfiles/desktop/.config/hypr/hyprpaper.conf @@ -0,0 +1,7 @@ +splash = false + +wallpaper { + monitor = + path = ~/.config/hypr/wallpapers/darth-vader-sam.jpg + fit_mode = contain +} diff --git a/dotfiles/desktop/.config/hypr/scripts/powermenu.sh b/dotfiles/desktop/.config/hypr/scripts/powermenu.sh new file mode 100755 index 0000000..98223d7 --- /dev/null +++ b/dotfiles/desktop/.config/hypr/scripts/powermenu.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +choice="$(printf "⏻ Shutdown\n Reboot\n Logout\n Lock\n⏾ Suspend" \ +| rofi -dmenu \ +-i \ +-p "Power" \ +-theme ~/.config/rofi/config.rasi \ +-theme-str 'window { width: 20%; location: center; anchor: center; } listview { columns: 1; spacing: 6px; }')" + +[ -z "$choice" ] && exit 0 + +case "$choice" in + *Lock) + hyprlock + ;; + *Logout) + hyprctl dispatch exit + ;; + *Suspend) + hyprlock & + loginctl suspend + ;; + *Reboot) + loginctl reboot + ;; + *Shutdown) + loginctl poweroff + ;; +esac diff --git a/dotfiles/desktop/.config/hypr/scripts/screenshot b/dotfiles/desktop/.config/hypr/scripts/screenshot new file mode 100755 index 0000000..209380f --- /dev/null +++ b/dotfiles/desktop/.config/hypr/scripts/screenshot @@ -0,0 +1,38 @@ +#!/bin/sh +# Quick screenshot helpers for Hyprland. Captures via grim, saves to disk and +# also copies to the Wayland clipboard. Use the flameshot binding for +# annotations / fancy capture. +# +# Usage: screenshot {full|region|output|window} + +set -eu + +mode="${1:-full}" +dir="$HOME/Pictures/Screenshots" +mkdir -p "$dir" +file="$dir/$(date +%Y%m%d_%H%M%S).png" + +case "$mode" in + full) + grim "$file" + ;; + region) + grim -g "$(slurp)" "$file" + ;; + output) + out=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name') + grim -o "$out" "$file" + ;; + window) + geom=$(hyprctl activewindow -j \ + | jq -r '"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"') + grim -g "$geom" "$file" + ;; + *) + printf 'Usage: %s {full|region|output|window}\n' "$0" >&2 + exit 1 + ;; +esac + +wl-copy < "$file" +notify-send "Screenshot saved" "$file" -i image-x-generic >/dev/null 2>&1 || true diff --git a/dotfiles/desktop/.config/hypr/scripts/workspace-cycle b/dotfiles/desktop/.config/hypr/scripts/workspace-cycle new file mode 100755 index 0000000..fc41400 --- /dev/null +++ b/dotfiles/desktop/.config/hypr/scripts/workspace-cycle @@ -0,0 +1,34 @@ +#!/bin/sh +# Cycle Hyprland workspaces within the fixed 1..10 desktop set. + +set -eu + +direction="${1:-next}" +current="$(hyprctl activeworkspace -j | jq -r '.id')" + +case "$current" in + ''|*[!0-9-]*) current=1 ;; +esac + +case "$direction" in + next) + if [ "$current" -ge 10 ] || [ "$current" -lt 1 ]; then + target=1 + else + target=$((current + 1)) + fi + ;; + prev) + if [ "$current" -le 1 ] || [ "$current" -gt 10 ]; then + target=10 + else + target=$((current - 1)) + fi + ;; + *) + printf 'Usage: %s {next|prev}\n' "$0" >&2 + exit 1 + ;; +esac + +exec hyprctl dispatch workspace "$target" diff --git a/dotfiles/desktop/.config/hypr/wallpapers/darth-vader-sam.jpg b/dotfiles/desktop/.config/hypr/wallpapers/darth-vader-sam.jpg new file mode 100755 index 0000000..22b3f3d Binary files /dev/null and b/dotfiles/desktop/.config/hypr/wallpapers/darth-vader-sam.jpg differ diff --git a/dotfiles/desktop/.config/hypr/wallpapers/maxresdefault.jpg b/dotfiles/desktop/.config/hypr/wallpapers/maxresdefault.jpg new file mode 100755 index 0000000..b0da946 Binary files /dev/null and b/dotfiles/desktop/.config/hypr/wallpapers/maxresdefault.jpg differ diff --git a/dotfiles/desktop/.config/waybar/hyprland.jsonc b/dotfiles/desktop/.config/waybar/hyprland.jsonc new file mode 100644 index 0000000..b8abce5 --- /dev/null +++ b/dotfiles/desktop/.config/waybar/hyprland.jsonc @@ -0,0 +1,115 @@ +{ + "layer": "top", + "position": "top", + "height": 26, + "spacing": 4, + + "modules-left": ["hyprland/workspaces"], + "modules-center": [], + "modules-right": ["cpu", "memory", "disk", "temperature", "backlight", "network", "pulseaudio", "battery", "tray", "clock", "custom/dnd", "custom/power"], + + "hyprland/workspaces": { + "disable-scroll": true, + "all-outputs": false, + "format": "{icon}", + "format-icons": { + "1": "I", + "2": "II", + "3": "III", + "4": "IV", + "5": "V", + "6": "VI", + "7": "VII", + "8": "VIII", + "9": "IX", + "10": "X" + }, + "persistent-workspaces": { + "*": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + } + }, + + "clock": { + "interval": 60, + "format": "{:%Y-%m-%d %H:%M}", + "tooltip-format": "{calendar}" + }, + + "cpu": { + "interval": 5, + "format": " {usage}%" + }, + + "memory": { + "interval": 10, + "format": " {used:0.1f}G / {total:0.1f}G" + }, + + "disk": { + "interval": 60, + "path": "/", + "format": " {used} / {total}" + }, + + "temperature": { + "interval": 10, + "critical-threshold": 80, + "format": " {temperatureC}°C", + "format-critical": " {temperatureC}°C" + }, + + "backlight": { + "format": " {percent}%", + "on-scroll-up": "brightnessctl set +5%", + "on-scroll-down": "brightnessctl set 5%-" + }, + + "network": { + "interval": 5, + "format-wifi": " {essid} {signalStrength}%", + "format-ethernet": " {ifname}", + "format-disconnected": " offline", + "tooltip-format": "{ifname} {ipaddr}" + }, + + "pulseaudio": { + "format": "{icon} {volume}%", + "format-muted": " muted", + "format-bluetooth": "{icon} {volume}% ", + "format-icons": { "default": ["", "", ""] }, + "on-click": "swayosd-client --output-volume mute-toggle", + "on-click-right": "pavucontrol" + }, + + "battery": { + "bat": "BAT0", + "interval": 30, + "states": { + "warning": 30, + "critical": 15 + }, + "format": "{icon} {capacity}%", + "format-charging": " {capacity}%", + "format-full": " full", + "format-icons": ["", "", "", "", ""] + }, + + "tray": { + "icon-size": 16, + "spacing": 6 + }, + + "custom/dnd": { + "exec": "dunstctl is-paused | grep -q true && echo '{\"text\":\"󰂛\",\"class\":\"paused\"}' || echo '{\"text\":\"󰂚\",\"class\":\"\"}'", + "return-type": "json", + "interval": 2, + "on-click": "dunstctl set-paused toggle", + "tooltip": false + }, + + "custom/power": { + "format": "⏻", + "tooltip": false, + "on-click": "~/.config/hypr/scripts/powermenu.sh" + } +} diff --git a/dotfiles/desktop/.local/bin/start-hyprland-session b/dotfiles/desktop/.local/bin/start-hyprland-session new file mode 100755 index 0000000..9b1dcbd --- /dev/null +++ b/dotfiles/desktop/.local/bin/start-hyprland-session @@ -0,0 +1,27 @@ +#!/bin/sh + +[ -r /etc/profile ] && . /etc/profile +[ -r "$HOME/.profile" ] && . "$HOME/.profile" + +export XDG_CURRENT_DESKTOP=Hyprland +export XDG_SESSION_DESKTOP=Hyprland +export XDG_SESSION_TYPE=wayland + +export MOZ_ENABLE_WAYLAND=1 +export QT_QPA_PLATFORM='wayland;xcb' +export _JAVA_AWT_WM_NONREPARENTING=1 +export SDL_VIDEODRIVER=wayland +export GDK_BACKEND='wayland,x11' + +export SSH_AUTH_SOCK="$HOME/.local/state/ssh-agent/socket" + +# Per-host session environment (GPU / Wayland tuning, etc.) +[ -r "$HOME/.config/hypr/session-env" ] && . "$HOME/.config/hypr/session-env" + +exec dbus-run-session sh -eu -c ' + umask 077 + printf "%s\n" "$DBUS_SESSION_BUS_ADDRESS" > "$HOME/.dbus-session-bus-address" + gpgconf --launch gpg-agent + "$HOME/.local/bin/update-turnstile-env" + exec /usr/bin/start-hyprland +' diff --git a/dotfiles/nymph/.config/hypr/host.conf b/dotfiles/nymph/.config/hypr/host.conf new file mode 100644 index 0000000..2ba43a2 --- /dev/null +++ b/dotfiles/nymph/.config/hypr/host.conf @@ -0,0 +1,24 @@ +$terminal = ~/.local/bin/prime-run /usr/bin/alacritty + +# Prefer the integrated panel as primary, and place the docked display to its right. +monitor = eDP-1,1920x1080,0x0,1 +monitor = DP-1,3440x1440@100,1920x0,1 +monitor = DP-2,disable +monitor = HDMI-A-1,disable +monitor = HDMI-A-2,disable + +workspace = 1, monitor:eDP-1 +workspace = 2, monitor:DP-1 +workspace = 3, monitor:DP-1 +workspace = 4, monitor:DP-1 +workspace = 5, monitor:DP-1 +workspace = 6, monitor:DP-1 +workspace = 7, monitor:DP-1 +workspace = 8, monitor:DP-1 +workspace = 9, monitor:DP-1 +workspace = 10, monitor:DP-1 + +env = LIBVA_DRIVER_NAME,iHD +env = QT_QPA_PLATFORM,wayland;xcb +env = WLR_DRM_DEVICES,/dev/dri/card0:/dev/dri/card1 +env = WLR_NO_HARDWARE_CURSORS,1 diff --git a/dotfiles/nymph/.config/hypr/session-env b/dotfiles/nymph/.config/hypr/session-env new file mode 100644 index 0000000..bf3fda7 --- /dev/null +++ b/dotfiles/nymph/.config/hypr/session-env @@ -0,0 +1,4 @@ +export LIBVA_DRIVER_NAME=iHD +export QT_QPA_PLATFORM='wayland;xcb' +export WLR_DRM_DEVICES=/dev/dri/card0:/dev/dri/card1 +export WLR_NO_HARDWARE_CURSORS=1