Reorganize desktop roles: extract common bootstrap and host-specific layers

- Add profile_desktop_common with shared desktop bootstrap (emptty, PAM,
  dotfiles, templates, GPG, Maildir, Flatpak, st, external tools)
- Add profile_desktop_host with host-specific tasks (NVIDIA/PRIME on nymph)
- Reduce profile_desktop_i3 to i3/X11-only tasks
- Create profile_desktop_hyprland for Hyprland Wayland session
- Add dual-session support (i3 + Hyprland) on nymph with session choice
- Create shared Hyprland/Waybar dotfiles under dotfiles/desktop/
- Fix Waybar: bottom position, no persistent workspaces, sort by number
- Rename host_dotfiles to host_i3_dotfiles for clarity
- Make emptty restart manual by default to avoid session drops
This commit is contained in:
Fabio Scotto di Santolo
2026-03-30 12:43:41 +02:00
parent 56d634c64e
commit 0a80706ea2
34 changed files with 1342 additions and 1013 deletions

View File

@@ -20,7 +20,7 @@ Project type: Ansible-driven infrastructure, workstation/server provisioning, an
- Ubuntu server: `prometheus` - Ubuntu server: `prometheus`
- Most hosts use `ansible_connection: local` - Most hosts use `ansible_connection: local`
- `all -> dotfiles_common` - `all -> dotfiles_common`
- `void -> packages_void, services_runit, profile_desktop_i3` - `void -> packages_void, services_runit, profile_desktop_common, profile_desktop_i3, profile_desktop_hyprland, profile_desktop_host`
- `ubuntu_workstation -> packages_ubuntu, services_systemd, profile_workstation_gnome` - `ubuntu_workstation -> packages_ubuntu, services_systemd, profile_workstation_gnome`
- `ubuntu_server -> packages_ubuntu, services_systemd, profile_server` - `ubuntu_server -> packages_ubuntu, services_systemd, profile_server`
- Present but unwired roles: `base`, `dotfiles` - Present but unwired roles: `base`, `dotfiles`
@@ -107,7 +107,7 @@ There is no pytest, Molecule, or unit-test suite. Use the narrowest command matc
### Variables, Types, And Naming ### Variables, Types, And Naming
- Use `snake_case` for vars, facts, and registered values - Use `snake_case` for vars, facts, and registered values
- Follow existing families such as `common_packages`, `profile_packages`, `host_packages`, `desktop_dotfiles`, `host_dotfiles`, `enabled_services`, and `host_enabled_services` - Follow existing families such as `common_packages`, `profile_packages`, `host_packages`, `desktop_common_packages`, `desktop_i3_packages`, `desktop_hyprland_packages`, `desktop_common_dotfiles`, `desktop_i3_dotfiles`, `desktop_hyprland_dotfiles`, `host_i3_dotfiles`, `host_hyprland_dotfiles`, `enabled_services`, and `host_enabled_services`
- Keep booleans as booleans, not quoted strings - Keep booleans as booleans, not quoted strings
- Keep structured values as YAML lists/maps, not comma-separated strings - Keep structured values as YAML lists/maps, not comma-separated strings
- Guard optional lists with `default([])`, mappings with `default({})`, and strings with `default('')` - Guard optional lists with `default([])`, mappings with `default({})`, and strings with `default('')`
@@ -134,8 +134,10 @@ There is no pytest, Molecule, or unit-test suite. Use the narrowest command matc
- Be careful with display-manager/session changes on desktop hosts; validate on one host first - Be careful with display-manager/session changes on desktop hosts; validate on one host first
## Area-Specific Notes ## Area-Specific Notes
- `profile_desktop_i3` now manages `emptty` on Void desktops; keep session changes coordinated with `.xinitrc` - `profile_desktop_common` manages `emptty` and the shared Void desktop bootstrap; `profile_desktop_i3` adds the X11/i3 session; `profile_desktop_hyprland` adds the optional Wayland session on hosts that enable it; `profile_desktop_host` carries host-specific desktop overrides such as NVIDIA or host session dotfiles
- Do not auto-restart `emptty` during playbook runs on active desktop hosts; prefer a manual restart from SSH or another TTY after the run
- `dotfiles/desktop/.xinitrc` is part of the X11 session bootstrap path; changes there affect login behavior - `dotfiles/desktop/.xinitrc` is part of the X11 session bootstrap path; changes there affect login behavior
- `dotfiles/desktop/.local/bin/start-hyprland-session` is the Wayland session bootstrap path; keep it aligned with DBus and keyring expectations
- `nymph` has special NVIDIA/PRIME handling; keep host-specific logic guarded by hostname or host vars - `nymph` has special NVIDIA/PRIME handling; keep host-specific logic guarded by hostname or host vars
- `ikaros` is treated as the more stable personal desktop; prefer validating risky desktop changes elsewhere first - `ikaros` is treated as the more stable personal desktop; prefer validating risky desktop changes elsewhere first

View File

@@ -65,9 +65,10 @@ Sistema operativo:
- Void Linux - Void Linux
Window manager: Sessioni desktop:
- i3 - `ikaros`: i3
- `nymph`: i3 + Hyprland con scelta sessione a login
Macchine: Macchine:
@@ -79,7 +80,8 @@ Queste macchine condividono la stessa configurazione base desktop e vengono mant
Lo stato attuale del profilo desktop include, tra le altre cose: Lo stato attuale del profilo desktop include, tra le altre cose:
- dotfiles comuni e desktop - dotfiles comuni e desktop
- sessione i3 e servizi desktop correlati - sessione i3 su tutti i desktop Void e sessione Hyprland opzionale su `nymph`
- `emptty` con scelta sessione a login su `nymph` e default host-specific sugli altri desktop
- pacchetti Void Linux e servizi runit - pacchetti Void Linux e servizi runit
- Flatpak con remoto Flathub - Flatpak con remoto Flathub
- GNOME Keyring e bootstrap della posta via script dedicato - GNOME Keyring e bootstrap della posta via script dedicato
@@ -183,7 +185,10 @@ I principali ruoli attualmente presenti sono:
| packages_ubuntu | installazione pacchetti su Ubuntu | | packages_ubuntu | installazione pacchetti su Ubuntu |
| services_runit | gestione servizi runit | | services_runit | gestione servizi runit |
| services_systemd | gestione servizi systemd | | services_systemd | gestione servizi systemd |
| profile_desktop_i3 | configurazione desktop i3 | | profile_desktop_common | bootstrap desktop Void condiviso |
| profile_desktop_i3 | sessione desktop i3 |
| profile_desktop_hyprland | sessione desktop Hyprland |
| profile_desktop_host | override desktop specifici per host |
| profile_workstation_gnome | configurazione workstation GNOME | | profile_workstation_gnome | configurazione workstation GNOME |
| profile_server | configurazione server | | profile_server | configurazione server |
| dotfiles_common | distribuzione dotfiles comuni | | dotfiles_common | distribuzione dotfiles comuni |
@@ -197,7 +202,7 @@ Il playbook `ansible/site.yml` e attualmente composto da quattro blocchi:
```text ```text
all -> dotfiles_common all -> dotfiles_common
void -> packages_void + services_runit + profile_desktop_i3 void -> packages_void + services_runit + profile_desktop_common + profile_desktop_i3 + profile_desktop_hyprland + profile_desktop_host
ubuntu_workstation -> packages_ubuntu + services_systemd + profile_workstation_gnome ubuntu_workstation -> packages_ubuntu + services_systemd + profile_workstation_gnome
ubuntu_server -> packages_ubuntu + services_systemd + profile_server ubuntu_server -> packages_ubuntu + services_systemd + profile_server
``` ```
@@ -267,9 +272,10 @@ ansible-playbook ansible/site.yml
Allo stato attuale questo comando: Allo stato attuale questo comando:
- distribuisce i dotfiles comuni a tutti gli host - distribuisce i dotfiles comuni a tutti gli host
- per gli host Void applica pacchetti, servizi runit e profilo desktop i3 - per gli host Void applica bootstrap desktop condiviso, sessioni i3/Hyprland e override specifici per host
- per gli host `ubuntu_workstation` applica pacchetti Ubuntu, servizi systemd, profilo workstation GNOME, UFW, dotfiles, Snap e template dedicati - per gli host `ubuntu_workstation` applica pacchetti Ubuntu, servizi systemd, profilo workstation GNOME, UFW, dotfiles, Snap e template dedicati
- per gli host `ubuntu_server` applica pacchetti Ubuntu, servizi systemd, profilo server, UFW, dotfiles e template dedicati - per gli host `ubuntu_server` applica pacchetti Ubuntu, servizi systemd, profilo server, UFW, dotfiles e template dedicati
- non riavvia automaticamente `emptty`; le modifiche al display manager vanno applicate manualmente da SSH o da una TTY separata
- carica `secrets/vault.yml` solo se presente - carica `secrets/vault.yml` solo se presente
Per validare prima di applicare: Per validare prima di applicare:
@@ -277,6 +283,7 @@ Per validare prima di applicare:
```bash ```bash
ansible-playbook ansible/site.yml --syntax-check ansible-playbook ansible/site.yml --syntax-check
ansible-playbook ansible/site.yml --limit ikaros --check --diff ansible-playbook ansible/site.yml --limit ikaros --check --diff
ansible-playbook ansible/site.yml --limit nymph --check --diff
ansible-playbook ansible/site.yml --limit deadalus --check --diff ansible-playbook ansible/site.yml --limit deadalus --check --diff
ansible-playbook ansible/site.yml --limit prometheus --check --diff ansible-playbook ansible/site.yml --limit prometheus --check --diff
``` ```

View File

@@ -2,36 +2,57 @@
desktop_manage_icloud_keyring: false desktop_manage_icloud_keyring: false
desktop_protonmail_bridge_cert_path: ~/.var/app/ch.protonmail.protonmail-bridge/config/protonmail/bridge-v3/cert.pem desktop_protonmail_bridge_cert_path: ~/.var/app/ch.protonmail.protonmail-bridge/config/protonmail/bridge-v3/cert.pem
desktop_x11_packages: desktop_sessions_enabled:
- arandr - i3
- autorandr
desktop_default_session: i3
desktop_default_session_env: xorg
desktop_restart_emptty_automatically: false
desktop_emptty_session_error_logging: disabled
desktop_common_packages:
- brightnessctl - brightnessctl
- dex - dex
- dunst - dunst
- emptty - emptty
- network-manager-applet
- rofi
- udiskie
- xfce-polkit
- xdg-desktop-portal
- xdg-desktop-portal-gtk
desktop_i3_packages:
- arandr
- autorandr
- feh - feh
- i3 - i3
- i3blocks - i3blocks
- i3blocks-blocklets - i3blocks-blocklets
- i3lock-color - i3lock-color
- i3status - i3status
- network-manager-applet
- rofi
- scrot - scrot
- setxkbmap - setxkbmap
- udiskie
- volumeicon - volumeicon
- xclip - xclip
- xfce-polkit
- xfce4-clipman-plugin - xfce4-clipman-plugin
- xfce4-screenshooter - xfce4-screenshooter
- xkbutils - xkbutils
- xdg-desktop-portal
- xdg-desktop-portal-gtk
- xorg-fonts - xorg-fonts
- xorg-minimal - xorg-minimal
- xss-lock - xss-lock
desktop_hyprland_packages:
- grim
- hypridle
- hyprland
- hyprlock
- hyprpaper
- slurp
- Waybar
- wl-clipboard
- xdg-desktop-portal-hyprland
profile_packages: profile_packages:
- alacritty - alacritty
- bluez - bluez
@@ -68,19 +89,11 @@ profile_packages:
- yaru - yaru
- yaru-plus - yaru-plus
desktop_dotfiles: desktop_common_dotfiles:
- name: XDG autostart entries - name: XDG autostart entries
src: .config/autostart/ src: .config/autostart/
dest: .config/autostart/ dest: .config/autostart/
mode: preserve mode: preserve
- name: i3 config
src: .config/i3/
dest: .config/i3/
mode: preserve
- name: i3blocks config
src: .config/i3blocks/
dest: .config/i3blocks/
mode: preserve
- name: dunst config - name: dunst config
src: .config/dunst/ src: .config/dunst/
dest: .config/dunst/ dest: .config/dunst/
@@ -113,10 +126,6 @@ desktop_dotfiles:
src: .config/yt-dlp/ src: .config/yt-dlp/
dest: .config/yt-dlp/ dest: .config/yt-dlp/
mode: preserve mode: preserve
- name: .xinitrc
src: .xinitrc
dest: .xinitrc
mode: "0644"
- name: .gitignore_global - name: .gitignore_global
src: .gitignore_global src: .gitignore_global
dest: .gitignore_global dest: .gitignore_global
@@ -129,6 +138,50 @@ desktop_dotfiles:
src: .emacs.d/ src: .emacs.d/
dest: .emacs.d/ dest: .emacs.d/
mode: preserve mode: preserve
- name: GTK theme setup script
src: .local/bin/setup-gtk-theme
dest: .local/bin/setup-gtk-theme
mode: "0755"
- name: Lock session script
src: .local/bin/lock-session
dest: .local/bin/lock-session
mode: "0755"
- name: Powermenu script
src: .local/bin/powermenu
dest: .local/bin/powermenu
mode: "0755"
desktop_i3_dotfiles:
- name: i3 config
src: .config/i3/
dest: .config/i3/
mode: preserve
- name: i3blocks config
src: .config/i3blocks/
dest: .config/i3blocks/
mode: preserve
- name: .xinitrc
src: .xinitrc
dest: .xinitrc
mode: "0644"
desktop_hyprland_dotfiles:
- name: Hyprland config
src: .config/hypr/
dest: .config/hypr/
mode: preserve
- name: Waybar config
src: .config/waybar/
dest: .config/waybar/
mode: preserve
- name: Hyprland session wrapper
src: .local/bin/start-hyprland-session
dest: .local/bin/start-hyprland-session
mode: "0755"
- name: Wayland screenshot script
src: .local/bin/screenshot-wayland
dest: .local/bin/screenshot-wayland
mode: "0755"
desktop_flatpak_packages: desktop_flatpak_packages:
- be.alexandervanhee.gradia - be.alexandervanhee.gradia

View File

@@ -1,6 +1,12 @@
--- ---
hostname: ikaros hostname: ikaros
desktop_sessions_enabled:
- i3
desktop_default_session: i3
desktop_default_session_env: xorg
host_packages: host_packages:
- mesa-dri - mesa-dri
- vulkan-loader - vulkan-loader
@@ -8,7 +14,7 @@ host_packages:
- mesa-vaapi - mesa-vaapi
- xf86-video-amdgpu - xf86-video-amdgpu
host_dotfiles: host_i3_dotfiles:
- src: .config/autorandr/ - src: .config/autorandr/
dest: .config/autorandr/ dest: .config/autorandr/
mode: preserve mode: preserve

View File

@@ -1,13 +1,17 @@
--- ---
hostname: nymph hostname: nymph
desktop_sessions_enabled:
- i3
- hyprland
desktop_prompt_for_session: true
desktop_emptty_session_error_logging: rotate
host_xbps_repositories: host_xbps_repositories:
- name: hyprland-void - name: hyprland-void
url: "https://raw.githubusercontent.com/Makrennel/hyprland-void/repository-x86_64-glibc" url: "https://raw.githubusercontent.com/Makrennel/hyprland-void/repository-x86_64-glibc"
host_emptty_wayland_sessions:
- Hyprland.desktop
host_packages: host_packages:
- nvidia - nvidia
- mesa-dri - mesa-dri
@@ -16,22 +20,16 @@ host_packages:
- intel-video-accel - intel-video-accel
- tlp - tlp
- tlp-rdw - tlp-rdw
- hyprland
- hyprpaper
- xdg-desktop-portal-hyprland
- Waybar
- wlogout
host_enabled_services: host_enabled_services:
- tlp - tlp
host_dotfiles: host_i3_dotfiles:
- src: .config/autorandr/ - src: .config/autorandr/
dest: .config/autorandr/ dest: .config/autorandr/
mode: preserve mode: preserve
- src: .config/hypr/
dest: .config/hypr/ host_hyprland_dotfiles:
mode: preserve - src: .config/hypr/host.conf
- src: .config/waybar/ dest: .config/hypr/host.conf
dest: .config/waybar/ mode: "0644"
mode: preserve

View File

@@ -86,12 +86,24 @@
ansible.builtin.apt: ansible.builtin.apt:
name: >- name: >-
{{ {{
(common_packages | default([])) (
(common_packages | default([]))
+ (ubuntu_packages_base | default([])) + (ubuntu_packages_base | default([]))
+ (ubuntu_docker_packages | default([])) + (ubuntu_docker_packages | default([]))
+ (profile_packages | default([])) + (profile_packages | default([]))
+ (desktop_x11_packages | default([])) + (desktop_common_packages | default([]))
+ (
(desktop_i3_packages | default([]))
if 'i3' in (desktop_sessions_enabled | default([]))
else []
)
+ (
(desktop_hyprland_packages | default([]))
if 'hyprland' in (desktop_sessions_enabled | default([]))
else []
)
+ (host_packages | default([])) + (host_packages | default([]))
) | unique
}} }}
state: present state: present

View File

@@ -51,11 +51,23 @@
community.general.xbps: community.general.xbps:
name: >- name: >-
{{ {{
(common_packages | default([])) (
(common_packages | default([]))
+ (void_packages_base | default([])) + (void_packages_base | default([]))
+ (profile_packages | default([])) + (profile_packages | default([]))
+ (desktop_x11_packages | default([])) + (desktop_common_packages | default([]))
+ (
(desktop_i3_packages | default([]))
if 'i3' in (desktop_sessions_enabled | default([]))
else []
)
+ (
(desktop_hyprland_packages | default([]))
if 'hyprland' in (desktop_sessions_enabled | default([]))
else []
)
+ (host_packages | default([])) + (host_packages | default([]))
) | unique
}} }}
state: present state: present
update_cache: false update_cache: false

View File

@@ -0,0 +1,19 @@
---
- name: Restart emptty service
listen: Restart emptty
ansible.builtin.command: sv restart emptty
changed_when: true
when:
- not ansible_check_mode
- desktop_restart_emptty_automatically | default(false)
- name: Report manual emptty restart requirement
listen: Restart emptty
ansible.builtin.debug:
msg: >-
Emptty configuration changed but automatic restart is disabled.
Restart it manually from SSH or another TTY with `sudo sv restart emptty`
to avoid dropping the active graphical session.
when:
- not ansible_check_mode
- not (desktop_restart_emptty_automatically | default(false))

View File

@@ -0,0 +1,509 @@
---
- name: Configure elogind to suspend on lid close
tags: [packages]
ansible.builtin.lineinfile:
path: /etc/elogind/logind.conf
regexp: '^#?HandleLidSwitch='
line: 'HandleLidSwitch=suspend'
state: present
- name: Ensure common config directories exist
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "0755"
loop:
- "{{ user_home }}/.config"
- "{{ user_home }}/.config/autostart"
- "{{ user_home }}/.config/dunst"
- "{{ user_home }}/.config/alacritty"
- "{{ user_home }}/.config/Thunar"
- "{{ user_home }}/.config/rofi"
- name: Ensure user local bin directory exists
tags: [dotfiles, dotfiles:desktop, dotfiles:host]
ansible.builtin.file:
path: "{{ user_home }}/.local/bin"
state: directory
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "0755"
- name: Enable gnome-keyring PAM auth hook
tags: [packages, gnome]
ansible.builtin.lineinfile:
path: /etc/pam.d/login
insertafter: '^auth\s+include\s+system-local-login$'
line: "auth optional pam_gnome_keyring.so"
state: present
- name: Enable gnome-keyring PAM session hook
tags: [packages, gnome]
ansible.builtin.lineinfile:
path: /etc/pam.d/login
insertafter: '^session\s+include\s+system-local-login$'
line: "session optional pam_gnome_keyring.so auto_start"
state: present
- name: Enable gnome-keyring PAM password hook
tags: [packages, gnome]
ansible.builtin.lineinfile:
path: /etc/pam.d/login
insertafter: '^password\s+include\s+system-local-login$'
line: "password optional pam_gnome_keyring.so use_authtok"
state: present
- name: Ensure emptty log directory exists
tags: [packages, services, emptty]
ansible.builtin.file:
path: /var/log/emptty
state: directory
owner: root
group: root
mode: "0755"
- name: Ensure emptty session directories exist
tags: [packages, services, emptty]
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop:
- /etc/emptty/xsessions
- /etc/emptty/wayland-sessions
- name: Configure emptty
tags: [packages, services, emptty]
ansible.builtin.template:
src: emptty-conf.j2
dest: /etc/emptty/conf
owner: root
group: root
mode: "0644"
notify: Restart emptty
- name: Copy common desktop dotfiles
tags: [dotfiles, dotfiles:desktop]
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_common_dotfiles | default([]) }}"
loop_control:
label: "{{ item.dest }}"
- name: Render desktop templates with private values
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.template:
src: "{{ item.src }}"
dest: "{{ user_home }}/{{ item.dest }}"
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "{{ item.mode }}"
loop:
- src: desktop/.gitconfig.j2
dest: .gitconfig
mode: "0644"
- src: desktop/.mbsyncrc.j2
dest: .mbsyncrc
mode: "0600"
- src: desktop/.msmtprc.j2
dest: .msmtprc
mode: "0600"
- src: desktop/email.el.j2
dest: .emacs.d/lisp/misc/email.el
mode: "0644"
loop_control:
label: "{{ item.dest }}"
- name: Refresh user font cache
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.command: fc-cache -f
become_user: "{{ username }}"
environment:
HOME: "{{ user_home }}"
changed_when: false
- name: Ensure .gnupg directory exists
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.file:
path: "{{ user_home }}/.gnupg"
state: directory
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "0700"
- name: Copy gpg-agent.conf
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.copy:
src: "{{ playbook_dir }}/../dotfiles/desktop/.gnupg/gpg-agent.conf"
dest: "{{ user_home }}/.gnupg/gpg-agent.conf"
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "0600"
- name: Ensure local user directories exist
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "{{ item.mode }}"
loop:
- path: "{{ user_home }}/.local"
mode: "0755"
- path: "{{ user_home }}/.local/share"
mode: "0755"
- path: "{{ user_home }}/.local/share/keyrings"
mode: "0700"
- path: "{{ user_home }}/.local/src"
mode: "0755"
- name: Ensure maildir directories exist
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "0700"
loop:
- "{{ user_home }}/Maildir"
- "{{ user_home }}/Maildir/iCloudAccount"
- "{{ user_home }}/Maildir/ProtonMailAccount"
- name: Bootstrap iCloud keyring secret from Ansible vault
tags: [dotfiles, dotfiles:desktop, gnome]
when: desktop_manage_icloud_keyring | default(false)
block:
- name: Store iCloud mail password in GNOME Keyring
ansible.builtin.getent:
database: passwd
key: "{{ username }}"
- name: Set desktop user runtime UID
ansible.builtin.set_fact:
desktop_user_uid: "{{ ansible_facts.getent_passwd[username][1] }}"
- name: Check whether desktop user DBus session address file exists
ansible.builtin.stat:
path: "{{ user_home }}/.dbus-session-bus-address"
register: desktop_user_bus_address_file
- name: Read desktop user DBus session address
ansible.builtin.slurp:
src: "{{ user_home }}/.dbus-session-bus-address"
register: desktop_user_bus_address_raw
when:
- (vault_icloud_mail_password | default('')) | length > 0
- desktop_user_bus_address_file.stat.exists
- name: Set desktop user DBus session address
ansible.builtin.set_fact:
desktop_user_bus_address: >-
{{ desktop_user_bus_address_raw.content | b64decode | trim }}
when:
- (vault_icloud_mail_password | default('')) | length > 0
- desktop_user_bus_address_file.stat.exists
- name: Check whether GNOME Keyring default collection is available
ansible.builtin.command:
cmd: >-
gdbus call --session
--dest org.freedesktop.secrets
--object-path /org/freedesktop/secrets
--method org.freedesktop.Secret.Service.ReadAlias default
become: true
become_user: "{{ username }}"
environment:
HOME: "{{ user_home }}"
XDG_RUNTIME_DIR: "/run/user/{{ desktop_user_uid }}"
DBUS_SESSION_BUS_ADDRESS: "{{ desktop_user_bus_address }}"
register: icloud_keyring_default_alias
failed_when: false
changed_when: false
when:
- (vault_icloud_mail_password | default('')) | length > 0
- desktop_user_bus_address | default('') | length > 0
- name: Set GNOME Keyring default collection path
ansible.builtin.set_fact:
icloud_keyring_default_alias_path: >-
{{
(
icloud_keyring_default_alias.stdout
| default('')
| regex_findall("objectpath '([^']+)'")
| first
)
| default('')
}}
when:
- (vault_icloud_mail_password | default('')) | length > 0
- desktop_user_bus_address | default('') | length > 0
- icloud_keyring_default_alias.rc | default(1) == 0
- name: Store iCloud mail password in GNOME Keyring
ansible.builtin.command:
cmd: secret-tool store --label="iCloud Mail" icloud-mail icloud
stdin: "{{ vault_icloud_mail_password }}"
stdin_add_newline: false
become: true
become_user: "{{ username }}"
environment:
HOME: "{{ user_home }}"
XDG_RUNTIME_DIR: "/run/user/{{ desktop_user_uid }}"
DBUS_SESSION_BUS_ADDRESS: "{{ desktop_user_bus_address }}"
register: icloud_keyring_store
failed_when: false
changed_when: icloud_keyring_store.rc == 0
no_log: true
when:
- (vault_icloud_mail_password | default('')) | length > 0
- desktop_user_bus_address | default('') | length > 0
- icloud_keyring_default_alias.rc | default(1) == 0
- (icloud_keyring_default_alias_path | default('')) | length > 0
- (icloud_keyring_default_alias_path | default('')) != '/'
- name: Warn when iCloud keyring storage is skipped
ansible.builtin.debug:
msg: >-
Unable to store iCloud password in GNOME Keyring automatically.
{% if (desktop_user_bus_address | default('')) | length == 0 %}
No saved DBus session address was found in {{ user_home }}/.dbus-session-bus-address.
{% elif icloud_keyring_default_alias.rc | default(1) != 0 %}
The Secret Service default alias could not be queried for {{ username }}.
{% elif (icloud_keyring_default_alias_path | default('')) == '/' %}
The Secret Service default alias is unset, so the login keyring is not initialized.
{% endif %}
Ensure a graphical user session is active, the login keyring exists and is unlocked, then run:
secret-tool store --label="iCloud Mail" icloud-mail icloud
when:
- (vault_icloud_mail_password | default('')) | length > 0
- icloud_keyring_store.rc | default(1) != 0
- name: Clone st repository
tags: [packages]
ansible.builtin.git:
repo: https://codeberg.org/fscotto/st
dest: "{{ user_home }}/.local/src/st"
update: true
become_user: "{{ username }}"
environment:
HOME: "{{ user_home }}"
register: st_repo
- name: Check whether st binary is installed
tags: [packages]
ansible.builtin.stat:
path: /usr/local/bin/st
register: st_binary
- name: Build and install st
tags: [packages]
ansible.builtin.command:
cmd: make clean install
chdir: "{{ user_home }}/.local/src/st"
when: st_repo.changed or not st_binary.stat.exists
- name: Clean st build artifacts
tags: [packages]
ansible.builtin.command:
cmd: make clean
chdir: "{{ user_home }}/.local/src/st"
when: st_repo.changed or not st_binary.stat.exists
- name: Ensure flathub remote is configured
tags: [packages]
community.general.flatpak_remote:
name: "{{ desktop_flatpak_remote_name | default('flathub') }}"
state: present
flatpakrepo_url: "{{ desktop_flatpak_remote_url | default('https://dl.flathub.org/repo/flathub.flatpakrepo') }}"
when: (desktop_flatpak_packages | default([])) | length > 0
- name: Install desktop flatpak applications
tags: [packages]
community.general.flatpak:
name: "{{ desktop_flatpak_packages }}"
state: present
remote: "{{ desktop_flatpak_remote_name | default('flathub') }}"
method: system
when: (desktop_flatpak_packages | default([])) | length > 0
- name: Install Flatpak extensions
tags: [packages]
community.general.flatpak:
name: "{{ item }}"
state: present
remote: "{{ desktop_flatpak_remote_name | default('flathub') }}"
method: system
loop: "{{ desktop_flatpak_extensions | default([]) }}"
when:
- (desktop_flatpak_packages | default([])) | length > 0
- (desktop_flatpak_extensions | default([])) | length > 0
- item | length > 0
- name: Set desktop external tool release metadata
tags: [packages]
ansible.builtin.set_fact:
desktop_tools_tmp_dir: /tmp/desktop-tools
gitmux_version: v0.11.5
bw_version: 1.22.1
opencode_asset_name: >-
{{
'opencode-linux-x64-baseline.tar.gz' if ansible_facts['architecture'] == 'x86_64'
else 'opencode-linux-arm64.tar.gz' if ansible_facts['architecture'] in ['aarch64', 'arm64']
else ''
}}
gitmux_arch: >-
{{
'amd64' if ansible_facts['architecture'] == 'x86_64'
else 'arm64' if ansible_facts['architecture'] in ['aarch64', 'arm64']
else ''
}}
- name: Ensure architecture is supported for OpenCode binary
tags: [packages]
ansible.builtin.fail:
msg: "Unsupported architecture {{ ansible_facts['architecture'] }} for OpenCode release binary"
when: opencode_asset_name == ''
- name: Ensure architecture is supported for gitmux binary
tags: [packages]
ansible.builtin.fail:
msg: "Unsupported architecture {{ ansible_facts['architecture'] }} for gitmux release binary"
when: gitmux_arch == ''
- name: Ensure architecture is supported for bw binary
tags: [packages]
ansible.builtin.fail:
msg: "Unsupported architecture {{ ansible_facts['architecture'] }} for bw release binary"
when: ansible_facts['architecture'] != 'x86_64'
- name: Ensure temporary directory exists for external tools
tags: [packages]
ansible.builtin.file:
path: "{{ desktop_tools_tmp_dir }}"
state: directory
mode: "0755"
- name: Fetch latest OpenCode release metadata
tags: [packages]
ansible.builtin.uri:
url: https://api.github.com/repos/anomalyco/opencode/releases/latest
headers:
Accept: application/vnd.github+json
return_content: true
register: opencode_latest_release
changed_when: false
- name: Set OpenCode release asset metadata
tags: [packages]
ansible.builtin.set_fact:
opencode_version: "{{ opencode_latest_release.json.tag_name }}"
opencode_asset: >-
{{
opencode_latest_release.json.assets
| selectattr('name', 'equalto', opencode_asset_name)
| first
| default({})
}}
- name: Ensure latest OpenCode asset metadata is available
tags: [packages]
ansible.builtin.fail:
msg: "Could not find OpenCode asset {{ opencode_asset_name }} in release {{ opencode_version }}"
when: opencode_asset == {}
- name: Download OpenCode release archive
tags: [packages]
ansible.builtin.get_url:
url: "{{ opencode_asset.browser_download_url }}"
dest: "{{ desktop_tools_tmp_dir }}/{{ opencode_asset.name }}"
checksum: "{{ opencode_asset.digest | default(omit) }}"
mode: "0644"
- name: Extract OpenCode release archive
tags: [packages]
ansible.builtin.unarchive:
src: "{{ desktop_tools_tmp_dir }}/{{ opencode_asset.name }}"
dest: "{{ desktop_tools_tmp_dir }}"
remote_src: true
- name: Install OpenCode binary
tags: [packages]
ansible.builtin.copy:
src: "{{ desktop_tools_tmp_dir }}/opencode"
dest: /usr/local/bin/opencode
remote_src: true
owner: root
group: root
mode: "0755"
- name: Set gitmux asset metadata
tags: [packages]
ansible.builtin.set_fact:
gitmux_asset: "gitmux_{{ gitmux_version }}_linux_{{ gitmux_arch }}.tar.gz"
- name: Download gitmux release archive
tags: [packages]
ansible.builtin.get_url:
url: "https://github.com/arl/gitmux/releases/download/{{ gitmux_version }}/{{ gitmux_asset }}"
dest: "{{ desktop_tools_tmp_dir }}/{{ gitmux_asset }}"
checksum: "sha256:https://github.com/arl/gitmux/releases/download/{{ gitmux_version }}/checksums.txt"
mode: "0644"
- name: Extract gitmux release archive
tags: [packages]
ansible.builtin.unarchive:
src: "{{ desktop_tools_tmp_dir }}/{{ gitmux_asset }}"
dest: "{{ desktop_tools_tmp_dir }}"
remote_src: true
- name: Install gitmux binary
tags: [packages]
ansible.builtin.copy:
src: "{{ desktop_tools_tmp_dir }}/gitmux"
dest: /usr/local/bin/gitmux
remote_src: true
owner: root
group: root
mode: "0755"
- name: Set bw asset metadata
tags: [packages]
ansible.builtin.set_fact:
bw_asset: "bw-linux-{{ bw_version }}.zip"
- name: Download bw release archive
tags: [packages]
ansible.builtin.get_url:
url: "https://github.com/bitwarden/cli/releases/download/v{{ bw_version }}/{{ bw_asset }}"
dest: "{{ desktop_tools_tmp_dir }}/{{ bw_asset }}"
checksum: "sha256:https://github.com/bitwarden/cli/releases/download/v{{ bw_version }}/bw-linux-sha256-{{ bw_version }}.txt"
mode: "0644"
- name: Extract bw release archive
tags: [packages]
ansible.builtin.unarchive:
src: "{{ desktop_tools_tmp_dir }}/{{ bw_asset }}"
dest: "{{ desktop_tools_tmp_dir }}"
remote_src: true
- name: Install bw binary
tags: [packages]
ansible.builtin.copy:
src: "{{ desktop_tools_tmp_dir }}/bw"
dest: /usr/local/bin/bw
remote_src: true
owner: root
group: root
mode: "0755"

View File

@@ -0,0 +1,18 @@
TTY_NUMBER=7
SWITCH_TTY=true
DEFAULT_ENV={{ desktop_default_session_env | default('xorg') }}
{% if not (desktop_prompt_for_session | default(false)) and (desktop_default_session | default('')) | length > 0 %}
DEFAULT_SESSION={{ desktop_default_session | default('') }}
{% if (desktop_default_session_env | default('')) | length > 0 %}
DEFAULT_SESSION_ENV={{ desktop_default_session_env | default('') }}
{% endif %}
{% endif %}
DBUS_LAUNCH=false
XINITRC_LAUNCH=true
XORG_SESSIONS_PATH=/etc/emptty/xsessions
WAYLAND_SESSIONS_PATH=/etc/emptty/wayland-sessions
VERTICAL_SELECTION=true
IDENTIFY_ENVS=true
SELECT_LAST_USER=global
LOGGING=rotate
SESSION_ERROR_LOGGING={{ desktop_emptty_session_error_logging | default('disabled') }}

View File

@@ -0,0 +1,35 @@
---
- name: Include nymph desktop host tasks
tags: [packages, nvidia, dotfiles, dotfiles:host]
ansible.builtin.include_tasks: nymph.yml
when: hostname == 'nymph'
- name: Copy host-specific i3 dotfiles
tags: [dotfiles, dotfiles:desktop, dotfiles:host, i3]
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_i3_dotfiles | default([]) }}"
loop_control:
label: "{{ item.dest }}"
when:
- "'i3' in (desktop_sessions_enabled | default([]))"
- (host_i3_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

View File

@@ -0,0 +1,41 @@
---
- name: Configure GRUB kernel parameters for NVIDIA hybrid graphics
tags: [packages, nvidia]
ansible.builtin.lineinfile:
path: /etc/default/grub
regexp: '^GRUB_CMDLINE_LINUX='
line: 'GRUB_CMDLINE_LINUX="rd.luks.uuid=1e15d159-5d05-4a1f-9639-ac200dff9f9c rootflags=subvol=@ apparmor=1 security=apparmor nouveau.modeset=0 nvidia-drm.modeset=1"'
state: present
- name: Regenerate GRUB configuration
tags: [packages, nvidia]
ansible.builtin.command: grub-mkconfig -o /boot/grub/grub.cfg
changed_when: true
- name: Configure NVIDIA power management for hybrid graphics
tags: [packages, nvidia]
ansible.builtin.copy:
dest: /etc/modprobe.d/nvidia-power-management.conf
content: |
options nvidia "NVreg_DynamicPowerManagement=0x02"
owner: root
group: root
mode: "0644"
- name: Install prime-run wrapper script for NVIDIA PRIME offload
tags: [nvidia, dotfiles, dotfiles:host]
ansible.builtin.copy:
src: "{{ playbook_dir }}/../dotfiles/nymph/.local/bin/prime-run"
dest: "{{ user_home }}/.local/bin/prime-run"
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "0755"
force: false
- name: Wrap alacritty with prime-run for NVIDIA PRIME offload
tags: [nvidia, dotfiles, dotfiles:desktop, dotfiles:host]
ansible.builtin.lineinfile:
path: "{{ user_home }}/.config/i3/config"
regexp: '^bindsym \$mod\+Return exec --no-startup-id /usr/bin/alacritty'
line: 'bindsym $mod+Return exec --no-startup-id ~/.local/bin/prime-run /usr/bin/alacritty'
when: "'i3' in (desktop_sessions_enabled | default([]))"

View File

@@ -0,0 +1,36 @@
---
- 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/hypr"
- "{{ user_home }}/.config/waybar"
when: "'hyprland' in (desktop_sessions_enabled | default([]))"
- name: Install Hyprland emptty session entry
tags: [packages, services, emptty, hyprland]
ansible.builtin.template:
src: Hyprland.desktop.j2
dest: /etc/emptty/wayland-sessions/Hyprland.desktop
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([]))"

View File

@@ -1,7 +1,7 @@
[Desktop Entry] [Desktop Entry]
Name=Hyprland Name=Hyprland
Comment=Hyprland Wayland compositor Comment=Hyprland Wayland compositor
Exec=Hyprland Exec={{ user_home }}/.local/bin/start-hyprland-session
TryExec=Hyprland TryExec=Hyprland
Type=Application Type=Application
DesktopNames=Hyprland DesktopNames=Hyprland

View File

@@ -1,5 +0,0 @@
---
- name: Restart emptty
ansible.builtin.command: sv restart emptty
changed_when: true
when: not ansible_check_mode

View File

@@ -1,14 +1,6 @@
--- ---
- name: Configure elogind to suspend on lid close - name: Ensure i3 config directories exist
tags: [packages] tags: [dotfiles, dotfiles:desktop, i3]
ansible.builtin.lineinfile:
path: /etc/elogind/logind.conf
regexp: '^#?HandleLidSwitch='
line: 'HandleLidSwitch=suspend'
state: present
- name: Ensure config directories exist
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.file: ansible.builtin.file:
path: "{{ item }}" path: "{{ item }}"
state: directory state: directory
@@ -16,577 +8,29 @@
group: "{{ user_group }}" group: "{{ user_group }}"
mode: "0755" mode: "0755"
loop: loop:
- "{{ user_home }}/.config"
- "{{ user_home }}/.config/autostart"
- "{{ user_home }}/.config/i3" - "{{ user_home }}/.config/i3"
- "{{ user_home }}/.config/i3blocks" - "{{ user_home }}/.config/i3blocks"
- "{{ user_home }}/.config/dunst" when: "'i3' in (desktop_sessions_enabled | default([]))"
- "{{ user_home }}/.config/alacritty"
- "{{ user_home }}/.config/Thunar"
- "{{ user_home }}/.config/rofi"
- name: Configure GRUB kernel parameters for NVIDIA hybrid graphics
tags: [packages, nvidia]
ansible.builtin.lineinfile:
path: /etc/default/grub
regexp: '^GRUB_CMDLINE_LINUX='
line: 'GRUB_CMDLINE_LINUX="rd.luks.uuid=1e15d159-5d05-4a1f-9639-ac200dff9f9c rootflags=subvol=@ apparmor=1 security=apparmor nouveau.modeset=0 nvidia-drm.modeset=1"'
state: present
when: ansible_facts['hostname'] == 'nymph'
- name: Regenerate GRUB configuration
tags: [packages, nvidia]
ansible.builtin.command: grub-mkconfig -o /boot/grub/grub.cfg
changed_when: true
when: ansible_facts['hostname'] == 'nymph'
- name: Configure NVIDIA power management for hybrid graphics
tags: [packages, nvidia]
ansible.builtin.copy:
dest: /etc/modprobe.d/nvidia-power-management.conf
content: |
options nvidia "NVreg_DynamicPowerManagement=0x02"
owner: root
group: root
mode: "0644"
when: ansible_facts['hostname'] == 'nymph'
- name: Ensure user local bin directory exists
tags: [nvidia, dotfiles, dotfiles:host]
ansible.builtin.file:
path: "{{ user_home }}/.local/bin"
state: directory
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "0755"
when: ansible_facts['hostname'] == 'nymph'
- name: Install prime-run wrapper script for NVIDIA PRIME offload
tags: [nvidia, dotfiles, dotfiles:host]
ansible.builtin.copy:
src: "{{ playbook_dir }}/../dotfiles/nymph/.local/bin/prime-run"
dest: "{{ user_home }}/.local/bin/prime-run"
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "0755"
force: false
when: ansible_facts['hostname'] == 'nymph'
- name: Enable gnome-keyring PAM auth hook
tags: [packages, gnome]
ansible.builtin.lineinfile:
path: /etc/pam.d/login
insertafter: '^auth\s+include\s+system-local-login$'
line: "auth optional pam_gnome_keyring.so"
state: present
- name: Enable gnome-keyring PAM session hook
tags: [packages, gnome]
ansible.builtin.lineinfile:
path: /etc/pam.d/login
insertafter: '^session\s+include\s+system-local-login$'
line: "session optional pam_gnome_keyring.so auto_start"
state: present
- name: Enable gnome-keyring PAM password hook
tags: [packages, gnome]
ansible.builtin.lineinfile:
path: /etc/pam.d/login
insertafter: '^password\s+include\s+system-local-login$'
line: "password optional pam_gnome_keyring.so use_authtok"
state: present
- name: Ensure emptty log directory exists
tags: [packages, services, emptty]
ansible.builtin.file:
path: /var/log/emptty
state: directory
owner: root
group: root
mode: "0755"
- name: Ensure emptty session directories exist
tags: [packages, services, emptty]
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop:
- /etc/emptty/xsessions
- /etc/emptty/wayland-sessions
- name: Install allowed emptty X11 sessions - name: Install allowed emptty X11 sessions
tags: [packages, services, emptty] tags: [packages, services, emptty, i3]
ansible.builtin.copy: ansible.builtin.copy:
src: i3.desktop src: i3.desktop
dest: /etc/emptty/xsessions/i3.desktop dest: /etc/emptty/xsessions/i3.desktop
owner: root owner: root
group: root group: root
mode: "0644" mode: "0644"
when: "'i3' in (desktop_sessions_enabled | default([]))"
- name: Install host-specific Wayland sessions for emptty - name: Copy i3 desktop dotfiles
tags: [packages, services, emptty, hyprland] tags: [dotfiles, dotfiles:desktop, i3]
ansible.builtin.copy:
src: "{{ item }}"
dest: "/etc/emptty/wayland-sessions/{{ item }}"
owner: root
group: root
mode: "0644"
loop: "{{ host_emptty_wayland_sessions | default([]) }}"
loop_control:
label: "{{ item }}"
when: (host_emptty_wayland_sessions | default([])) | length > 0
- name: Configure emptty
tags: [packages, services, emptty]
ansible.builtin.template:
src: emptty-conf.j2
dest: /etc/emptty/conf
owner: root
group: root
mode: "0644"
notify: Restart emptty
- name: Copy desktop dotfiles
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.copy: ansible.builtin.copy:
src: "{{ playbook_dir }}/../dotfiles/desktop/{{ item.src }}" src: "{{ playbook_dir }}/../dotfiles/desktop/{{ item.src }}"
dest: "{{ user_home }}/{{ item.dest }}" dest: "{{ user_home }}/{{ item.dest }}"
owner: "{{ username }}" owner: "{{ username }}"
group: "{{ user_group }}" group: "{{ user_group }}"
mode: "{{ item.mode }}" mode: "{{ item.mode }}"
loop: "{{ desktop_dotfiles | default([]) }}" loop: "{{ desktop_i3_dotfiles | default([]) }}"
loop_control: loop_control:
label: "{{ item.dest }}" label: "{{ item.dest }}"
when: "'i3' in (desktop_sessions_enabled | default([]))"
- name: Wrap alacritty with prime-run for NVIDIA PRIME offload
tags: [nvidia, dotfiles, dotfiles:desktop, dotfiles:host]
ansible.builtin.lineinfile:
path: "{{ user_home }}/.config/i3/config"
regexp: '^bindsym \$mod\+Return exec --no-startup-id /usr/bin/alacritty'
line: 'bindsym $mod+Return exec --no-startup-id ~/.local/bin/prime-run /usr/bin/alacritty'
when: ansible_facts['hostname'] == 'nymph'
- name: Copy host-specific dotfiles
tags: [dotfiles, dotfiles:desktop, dotfiles:host]
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_dotfiles | default([]) }}"
loop_control:
label: "{{ item.dest }}"
when: host_dotfiles is defined and host_dotfiles | length > 0
- name: Render desktop templates with private values
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.template:
src: "{{ item.src }}"
dest: "{{ user_home }}/{{ item.dest }}"
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "{{ item.mode }}"
loop:
- src: desktop/.gitconfig.j2
dest: .gitconfig
mode: "0644"
- src: desktop/.mbsyncrc.j2
dest: .mbsyncrc
mode: "0600"
- src: desktop/.msmtprc.j2
dest: .msmtprc
mode: "0600"
- src: desktop/email.el.j2
dest: .emacs.d/lisp/misc/email.el
mode: "0644"
loop_control:
label: "{{ item.dest }}"
- name: Refresh user font cache
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.command: fc-cache -f
become_user: "{{ username }}"
environment:
HOME: "{{ user_home }}"
changed_when: false
- name: Ensure .gnupg directory exists
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.file:
path: "{{ user_home }}/.gnupg"
state: directory
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "0700"
- name: Copy gpg-agent.conf
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.copy:
src: "{{ playbook_dir }}/../dotfiles/desktop/.gnupg/gpg-agent.conf"
dest: "{{ user_home }}/.gnupg/gpg-agent.conf"
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "0600"
- name: Ensure local user directories exist
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "{{ item.mode }}"
loop:
- path: "{{ user_home }}/.local"
mode: "0755"
- path: "{{ user_home }}/.local/share"
mode: "0755"
- path: "{{ user_home }}/.local/share/keyrings"
mode: "0700"
- path: "{{ user_home }}/.local/src"
mode: "0755"
- name: Ensure maildir directories exist
tags: [dotfiles, dotfiles:desktop]
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ username }}"
group: "{{ user_group }}"
mode: "0700"
loop:
- "{{ user_home }}/Maildir"
- "{{ user_home }}/Maildir/iCloudAccount"
- "{{ user_home }}/Maildir/ProtonMailAccount"
- name: Bootstrap iCloud keyring secret from Ansible vault
tags: [dotfiles, dotfiles:desktop, gnome]
when: desktop_manage_icloud_keyring | default(false)
block:
- name: Store iCloud mail password in GNOME Keyring
ansible.builtin.getent:
database: passwd
key: "{{ username }}"
- name: Set desktop user runtime UID
ansible.builtin.set_fact:
desktop_user_uid: "{{ ansible_facts.getent_passwd[username][1] }}"
- name: Check whether desktop user DBus session address file exists
ansible.builtin.stat:
path: "{{ user_home }}/.dbus-session-bus-address"
register: desktop_user_bus_address_file
- name: Read desktop user DBus session address
ansible.builtin.slurp:
src: "{{ user_home }}/.dbus-session-bus-address"
register: desktop_user_bus_address_raw
when:
- (vault_icloud_mail_password | default('')) | length > 0
- desktop_user_bus_address_file.stat.exists
- name: Set desktop user DBus session address
ansible.builtin.set_fact:
desktop_user_bus_address: >-
{{ desktop_user_bus_address_raw.content | b64decode | trim }}
when:
- (vault_icloud_mail_password | default('')) | length > 0
- desktop_user_bus_address_file.stat.exists
- name: Check whether GNOME Keyring default collection is available
ansible.builtin.command:
cmd: >-
gdbus call --session
--dest org.freedesktop.secrets
--object-path /org/freedesktop/secrets
--method org.freedesktop.Secret.Service.ReadAlias default
become: true
become_user: "{{ username }}"
environment:
HOME: "{{ user_home }}"
XDG_RUNTIME_DIR: "/run/user/{{ desktop_user_uid }}"
DBUS_SESSION_BUS_ADDRESS: "{{ desktop_user_bus_address }}"
register: icloud_keyring_default_alias
failed_when: false
changed_when: false
when:
- (vault_icloud_mail_password | default('')) | length > 0
- desktop_user_bus_address | default('') | length > 0
- name: Set GNOME Keyring default collection path
ansible.builtin.set_fact:
icloud_keyring_default_alias_path: >-
{{
(
icloud_keyring_default_alias.stdout
| default('')
| regex_findall("objectpath '([^']+)'")
| first
)
| default('')
}}
when:
- (vault_icloud_mail_password | default('')) | length > 0
- desktop_user_bus_address | default('') | length > 0
- icloud_keyring_default_alias.rc | default(1) == 0
- name: Store iCloud mail password in GNOME Keyring
ansible.builtin.command:
cmd: secret-tool store --label="iCloud Mail" icloud-mail icloud
stdin: "{{ vault_icloud_mail_password }}"
stdin_add_newline: false
become: true
become_user: "{{ username }}"
environment:
HOME: "{{ user_home }}"
XDG_RUNTIME_DIR: "/run/user/{{ desktop_user_uid }}"
DBUS_SESSION_BUS_ADDRESS: "{{ desktop_user_bus_address }}"
register: icloud_keyring_store
failed_when: false
changed_when: icloud_keyring_store.rc == 0
no_log: true
when:
- (vault_icloud_mail_password | default('')) | length > 0
- desktop_user_bus_address | default('') | length > 0
- icloud_keyring_default_alias.rc | default(1) == 0
- (icloud_keyring_default_alias_path | default('')) | length > 0
- (icloud_keyring_default_alias_path | default('')) != '/'
- name: Warn when iCloud keyring storage is skipped
ansible.builtin.debug:
msg: >-
Unable to store iCloud password in GNOME Keyring automatically.
{% if (desktop_user_bus_address | default('')) | length == 0 %}
No saved DBus session address was found in {{ user_home }}/.dbus-session-bus-address.
{% elif icloud_keyring_default_alias.rc | default(1) != 0 %}
The Secret Service default alias could not be queried for {{ username }}.
{% elif (icloud_keyring_default_alias_path | default('')) == '/' %}
The Secret Service default alias is unset, so the login keyring is not initialized.
{% endif %}
Ensure a graphical user session is active, the login keyring exists and is unlocked, then run:
secret-tool store --label="iCloud Mail" icloud-mail icloud
when:
- (vault_icloud_mail_password | default('')) | length > 0
- icloud_keyring_store.rc | default(1) != 0
- name: Clone st repository
tags: [packages]
ansible.builtin.git:
repo: https://codeberg.org/fscotto/st
dest: "{{ user_home }}/.local/src/st"
update: true
become_user: "{{ username }}"
environment:
HOME: "{{ user_home }}"
register: st_repo
- name: Check whether st binary is installed
tags: [packages]
ansible.builtin.stat:
path: /usr/local/bin/st
register: st_binary
- name: Build and install st
tags: [packages]
ansible.builtin.command:
cmd: make clean install
chdir: "{{ user_home }}/.local/src/st"
when: st_repo.changed or not st_binary.stat.exists
- name: Clean st build artifacts
tags: [packages]
ansible.builtin.command:
cmd: make clean
chdir: "{{ user_home }}/.local/src/st"
when: st_repo.changed or not st_binary.stat.exists
- name: Ensure flathub remote is configured
tags: [packages]
community.general.flatpak_remote:
name: "{{ desktop_flatpak_remote_name | default('flathub') }}"
state: present
flatpakrepo_url: "{{ desktop_flatpak_remote_url | default('https://dl.flathub.org/repo/flathub.flatpakrepo') }}"
when: (desktop_flatpak_packages | default([])) | length > 0
- name: Install desktop flatpak applications
tags: [packages]
community.general.flatpak:
name: "{{ desktop_flatpak_packages }}"
state: present
remote: "{{ desktop_flatpak_remote_name | default('flathub') }}"
method: system
when: (desktop_flatpak_packages | default([])) | length > 0
- name: Install Flatpak extensions
tags: [packages]
community.general.flatpak:
name: "{{ item }}"
state: present
remote: "{{ desktop_flatpak_remote_name | default('flathub') }}"
method: system
loop: "{{ desktop_flatpak_extensions | default([]) }}"
when:
- (desktop_flatpak_packages | default([])) | length > 0
- (desktop_flatpak_extensions | default([])) | length > 0
- item | length > 0
- name: Set desktop external tool release metadata
tags: [packages]
ansible.builtin.set_fact:
desktop_tools_tmp_dir: /tmp/desktop-tools
gitmux_version: v0.11.5
bw_version: 1.22.1
opencode_asset_name: >-
{{
'opencode-linux-x64-baseline.tar.gz' if ansible_facts['architecture'] == 'x86_64'
else 'opencode-linux-arm64.tar.gz' if ansible_facts['architecture'] in ['aarch64', 'arm64']
else ''
}}
gitmux_arch: >-
{{
'amd64' if ansible_facts['architecture'] == 'x86_64'
else 'arm64' if ansible_facts['architecture'] in ['aarch64', 'arm64']
else ''
}}
- name: Ensure architecture is supported for OpenCode binary
tags: [packages]
ansible.builtin.fail:
msg: "Unsupported architecture {{ ansible_facts['architecture'] }} for OpenCode release binary"
when: opencode_asset_name == ''
- name: Ensure architecture is supported for gitmux binary
tags: [packages]
ansible.builtin.fail:
msg: "Unsupported architecture {{ ansible_facts['architecture'] }} for gitmux release binary"
when: gitmux_arch == ''
- name: Ensure architecture is supported for bw binary
tags: [packages]
ansible.builtin.fail:
msg: "Unsupported architecture {{ ansible_facts['architecture'] }} for bw release binary"
when: ansible_facts['architecture'] != 'x86_64'
- name: Ensure temporary directory exists for external tools
tags: [packages]
ansible.builtin.file:
path: "{{ desktop_tools_tmp_dir }}"
state: directory
mode: "0755"
- name: Fetch latest OpenCode release metadata
tags: [packages]
ansible.builtin.uri:
url: https://api.github.com/repos/anomalyco/opencode/releases/latest
headers:
Accept: application/vnd.github+json
return_content: true
register: opencode_latest_release
changed_when: false
- name: Set OpenCode release asset metadata
tags: [packages]
ansible.builtin.set_fact:
opencode_version: "{{ opencode_latest_release.json.tag_name }}"
opencode_asset: >-
{{
opencode_latest_release.json.assets
| selectattr('name', 'equalto', opencode_asset_name)
| first
| default({})
}}
- name: Ensure latest OpenCode asset metadata is available
tags: [packages]
ansible.builtin.fail:
msg: "Could not find OpenCode asset {{ opencode_asset_name }} in release {{ opencode_version }}"
when: opencode_asset == {}
- name: Download OpenCode release archive
tags: [packages]
ansible.builtin.get_url:
url: "{{ opencode_asset.browser_download_url }}"
dest: "{{ desktop_tools_tmp_dir }}/{{ opencode_asset.name }}"
checksum: "{{ opencode_asset.digest | default(omit) }}"
mode: "0644"
- name: Extract OpenCode release archive
tags: [packages]
ansible.builtin.unarchive:
src: "{{ desktop_tools_tmp_dir }}/{{ opencode_asset.name }}"
dest: "{{ desktop_tools_tmp_dir }}"
remote_src: true
- name: Install OpenCode binary
tags: [packages]
ansible.builtin.copy:
src: "{{ desktop_tools_tmp_dir }}/opencode"
dest: /usr/local/bin/opencode
remote_src: true
owner: root
group: root
mode: "0755"
- name: Set gitmux asset metadata
tags: [packages]
ansible.builtin.set_fact:
gitmux_asset: "gitmux_{{ gitmux_version }}_linux_{{ gitmux_arch }}.tar.gz"
- name: Download gitmux release archive
tags: [packages]
ansible.builtin.get_url:
url: "https://github.com/arl/gitmux/releases/download/{{ gitmux_version }}/{{ gitmux_asset }}"
dest: "{{ desktop_tools_tmp_dir }}/{{ gitmux_asset }}"
checksum: "sha256:https://github.com/arl/gitmux/releases/download/{{ gitmux_version }}/checksums.txt"
mode: "0644"
- name: Extract gitmux release archive
tags: [packages]
ansible.builtin.unarchive:
src: "{{ desktop_tools_tmp_dir }}/{{ gitmux_asset }}"
dest: "{{ desktop_tools_tmp_dir }}"
remote_src: true
- name: Install gitmux binary
tags: [packages]
ansible.builtin.copy:
src: "{{ desktop_tools_tmp_dir }}/gitmux"
dest: /usr/local/bin/gitmux
remote_src: true
owner: root
group: root
mode: "0755"
- name: Set bw asset metadata
tags: [packages]
ansible.builtin.set_fact:
bw_asset: "bw-linux-{{ bw_version }}.zip"
- name: Download bw release archive
tags: [packages]
ansible.builtin.get_url:
url: "https://github.com/bitwarden/cli/releases/download/v{{ bw_version }}/{{ bw_asset }}"
dest: "{{ desktop_tools_tmp_dir }}/{{ bw_asset }}"
checksum: "sha256:https://github.com/bitwarden/cli/releases/download/v{{ bw_version }}/bw-linux-sha256-{{ bw_version }}.txt"
mode: "0644"
- name: Extract bw release archive
tags: [packages]
ansible.builtin.unarchive:
src: "{{ desktop_tools_tmp_dir }}/{{ bw_asset }}"
dest: "{{ desktop_tools_tmp_dir }}"
remote_src: true
- name: Install bw binary
tags: [packages]
ansible.builtin.copy:
src: "{{ desktop_tools_tmp_dir }}/bw"
dest: /usr/local/bin/bw
remote_src: true
owner: root
group: root
mode: "0755"

View File

@@ -1,12 +0,0 @@
TTY_NUMBER=7
SWITCH_TTY=true
DEFAULT_ENV=xorg
DBUS_LAUNCH=false
XINITRC_LAUNCH=true
XORG_SESSIONS_PATH=/etc/emptty/xsessions
WAYLAND_SESSIONS_PATH=/etc/emptty/wayland-sessions
VERTICAL_SELECTION=true
IDENTIFY_ENVS=true
SELECT_LAST_USER=global
LOGGING=rotate
SESSION_ERROR_LOGGING=disabled

View File

@@ -17,7 +17,10 @@
roles: roles:
- packages_void - packages_void
- services_runit - services_runit
- profile_desktop_common
- profile_desktop_i3 - profile_desktop_i3
- profile_desktop_hyprland
- profile_desktop_host
- hosts: ubuntu_workstation - hosts: ubuntu_workstation
become: true become: true

View File

View File

@@ -0,0 +1,16 @@
general {
lock_cmd = pidof hyprlock || hyprlock
before_sleep_cmd = pidof hyprlock || hyprlock
after_sleep_cmd = hyprctl dispatch dpms on
}
listener {
timeout = 600
on-timeout = pidof hyprlock || hyprlock
}
listener {
timeout = 900
on-timeout = hyprctl dispatch dpms off
on-resume = hyprctl dispatch dpms on
}

View File

@@ -0,0 +1,171 @@
$mod = SUPER
$terminal = alacritty
$fallback_terminal = st
$menu = rofi -show drun -theme ~/.config/rofi/config.rasi
$powermenu = ~/.local/bin/powermenu
$locker = ~/.local/bin/lock-session
$screenshot = ~/.local/bin/screenshot-wayland
exec-once = dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
exec-once = dex --autostart --environment Hyprland
exec-once = gnome-keyring-daemon --start --components=secrets
exec-once = ~/.local/bin/setup-gtk-theme
exec-once = hyprpaper
exec-once = waybar
exec-once = hypridle
exec-once = dunst
exec-once = pipewire
exec-once = pipewire-pulse
exec-once = wireplumber
exec-once = /usr/libexec/xfce-polkit
exec-once = nm-applet
exec-once = blueman-applet
exec-once = udiskie --tray --automount
env = XCURSOR_THEME,Yaru
env = XCURSOR_SIZE,24
env = GTK_THEME,Yaru-blue-dark
source = ~/.config/hypr/host.conf
monitor = ,preferred,auto,1
input {
kb_layout = us
kb_variant = intl
follow_mouse = 1
sensitivity = 0
touchpad {
natural_scroll = true
}
}
general {
layout = dwindle
gaps_in = 0
gaps_out = 0
border_size = 2
col.active_border = rgb(4a90d9)
col.inactive_border = rgb(3a3a46)
allow_tearing = false
}
dwindle {
preserve_split = yes
}
decoration {
rounding = 0
blur {
enabled = false
}
}
animations {
enabled = no
}
misc {
disable_hyprland_logo = true
disable_splash_rendering = true
}
windowrulev2 = float,class:^(Rofi)$
windowrulev2 = float,class:^(org.pulseaudio.pavucontrol)$
windowrulev2 = float,class:^(nm-connection-editor)$
windowrulev2 = float,class:^(blueman-manager)$
bind = $mod, Return, exec, $terminal
bind = $mod SHIFT, Return, exec, $fallback_terminal
bind = $mod, D, exec, $menu
bind = $mod SHIFT, V, exec, pavucontrol
bind = $mod SHIFT, F, exec, thunar
bind = $mod SHIFT, X, exec, $locker
bind = $mod SHIFT, Q, killactive
bind = $mod, F, fullscreen, 1
bind = $mod, SPACE, togglefloating
bind = $mod SHIFT, SPACE, togglefloating
bind = $mod, V, togglesplit
bind = $mod, B, togglesplit
bind = $mod, minus, togglespecialworkspace, scratchpad
bind = $mod SHIFT, minus, movetoworkspace, special:scratchpad
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
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
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 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
bind = $mod SHIFT, C, exec, hyprctl reload
bind = $mod SHIFT, R, exec, hyprctl reload
bind = $mod SHIFT, BackSpace, exit
bind = $mod SHIFT, Escape, exec, $powermenu
bind = , Print, exec, $screenshot full
bind = SHIFT, Print, exec, $screenshot region
bind = ALT, Print, exec, $screenshot output
bind = , XF86AudioRaiseVolume, exec, pactl set-sink-volume @DEFAULT_SINK@ +5%
bind = , XF86AudioLowerVolume, exec, pactl set-sink-volume @DEFAULT_SINK@ -5%
bind = , XF86AudioMute, exec, pactl set-sink-mute @DEFAULT_SINK@ toggle
bind = , XF86AudioMicMute, exec, pactl set-source-mute @DEFAULT_SOURCE@ toggle
bind = , XF86MonBrightnessUp, exec, brightnessctl set +10%
bind = , XF86MonBrightnessDown, exec, brightnessctl set 10%-
bind = , XF86AudioPlay, exec, playerctl play-pause
bind = , XF86AudioNext, exec, playerctl next
bind = , XF86AudioPrev, exec, playerctl previous
bind = $mod, R, submap, resize
submap = resize
bind = , H, resizeactive, -40 0
bind = , J, resizeactive, 0 40
bind = , K, resizeactive, 0 -40
bind = , L, resizeactive, 40 0
bind = , left, resizeactive, -40 0
bind = , down, resizeactive, 0 40
bind = , up, resizeactive, 0 -40
bind = , right, resizeactive, 40 0
bind = , escape, submap, reset
bind = , Return, submap, reset
bind = $mod, R, submap, reset
submap = reset
bindm = $mod, mouse:272, movewindow
bindm = $mod, mouse:273, resizewindow

View File

@@ -0,0 +1,46 @@
general {
hide_cursor = true
grace = 2
no_fade_in = false
}
background {
monitor =
color = rgba(1f1f28ff)
}
label {
monitor =
text = cmd[update:1000] echo "$(date +'%H:%M')"
color = rgba(d0d0d0ff)
font_size = 42
position = 0, 80
halign = center
valign = center
}
label {
monitor =
text = cmd[update:60000] echo "$(date +'%Y-%m-%d')"
color = rgba(9a9a9aff)
font_size = 18
position = 0, 35
halign = center
valign = center
}
input-field {
monitor =
size = 280, 56
outline_thickness = 2
dots_size = 0.2
dots_spacing = 0.2
outer_color = rgba(4a90d9ff)
inner_color = rgba(1f1f28ff)
font_color = rgba(d0d0d0ff)
fade_on_empty = false
placeholder_text = Password...
position = 0, -20
halign = center
valign = center
}

View File

@@ -0,0 +1,4 @@
preload = ~/.config/i3/wallpapers/dont_like_wallpapers.jpg
wallpaper = ,~/.config/i3/wallpapers/dont_like_wallpapers.jpg
splash = false
ipc = on

View File

@@ -0,0 +1,111 @@
{
"layer": "top",
"position": "bottom",
"height": 28,
"spacing": 4,
"modules-left": [
"hyprland/workspaces"
],
"modules-right": [
"pulseaudio",
"backlight",
"network",
"bluetooth",
"battery",
"temperature",
"memory",
"clock",
"custom/powermenu",
"tray"
],
"hyprland/workspaces": {
"disable-scroll": true,
"all-outputs": true,
"format": "{name}",
"on-click": "activate",
"sort-by-number": true
},
"pulseaudio": {
"scroll-step": 1,
"format": "VOL {volume}%",
"format-muted": "MUTE",
"on-click": "pavucontrol",
"tooltip": false
},
"backlight": {
"device": "intel_backlight",
"on-scroll-up": "brightnessctl set +5%",
"on-scroll-down": "brightnessctl set 5%-",
"format": "BL {percent}%"
},
"network": {
"interval": 1,
"tooltip": true,
"format-wifi": "WIFI",
"format-ethernet": "ETH",
"format-linked": "LINK",
"format-disconnected": "OFF",
"tooltip-format": "{ifname}",
"tooltip-format-wifi": "{essid} ({signalStrength}%)",
"tooltip-format-ethernet": "{ifname}",
"tooltip-format-disconnected": "Disconnected",
"on-click": "nm-connection-editor"
},
"bluetooth": {
"format": "BT",
"format-connected": "BT {device_alias}",
"format-off": "BT OFF",
"on-click": "blueman-manager"
},
"battery": {
"interval": 10,
"states": {
"warning": 20,
"critical": 10
},
"format": "BAT {capacity}%",
"format-charging": "BAT {capacity}% AC",
"format-plugged": "BAT {capacity}% AC",
"format-alt": "{time}"
},
"temperature": {
"critical-threshold": 80,
"format": "TMP {temperatureC}C",
"tooltip": false
},
"memory": {
"interval": 5,
"format": "MEM {percentage}%",
"states": {
"warning": 85
}
},
"clock": {
"interval": 60,
"timezone": "Europe/Rome",
"format": "{:%Y-%m-%d %H:%M}",
"format-alt": "{:%A %d/%m/%Y}"
},
"custom/powermenu": {
"format": "POWER",
"on-click": "~/.local/bin/powermenu",
"tooltip": false
},
"tray": {
"icon-size": 15,
"spacing": 6
}
}

View File

@@ -4,104 +4,87 @@
min-height: 0; min-height: 0;
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: "Liberation Mono", "Font Awesome 6 Free", "Font Awesome 6 Brands", monospace; font-family: "Liberation Mono", monospace;
font-size: 12px; font-size: 12px;
} }
window#waybar { window#waybar {
background: #000000; background: #1f1f28;
color: #dcdcdc; color: #d0d0d0;
border-bottom: 1px solid #101010; border-top: 2px solid #3a3a46;
} }
/* blocchi principali */
#workspaces, #workspaces,
#custom-music,
#temperature,
#memory,
#cpu,
#pulseaudio, #pulseaudio,
#backlight, #backlight,
#network, #network,
#bluetooth, #bluetooth,
#battery, #battery,
#temperature,
#memory,
#clock, #clock,
#custom-powermenu, #custom-powermenu,
#tray { #tray {
font-size: 16px;
padding: 0 8px; padding: 0 8px;
margin: 3px 0; margin: 3px 0;
} }
/* workspaces */
#workspaces { #workspaces {
padding-left: 10px; padding-left: 10px;
} }
#workspaces button { #workspaces button {
background: transparent; background: transparent;
color: #6f6f6f; color: #9a9a9a;
padding: 0 7px; padding: 0 7px;
margin: 0 2px; margin: 0 2px;
border-bottom: 1px solid transparent; border-top: 2px solid transparent;
} }
#workspaces button:hover { #workspaces button:hover {
color: #dcdcdc; color: #d0d0d0;
background: transparent; background: transparent;
} }
#workspaces button.active { #workspaces button.active {
color: #ffffff; color: #ffffff;
border-bottom: 1px solid #ffffff; border-top: 2px solid #4a90d9;
} }
#workspaces button.urgent { #workspaces button.urgent {
color: #ffffff; color: #ffffff;
background: #0f0f0f; background: #c7162b;
} }
/* centro */
#custom-music {
color: #bfbfbf;
}
/* moduli destra */
#temperature,
#memory,
#cpu,
#pulseaudio, #pulseaudio,
#backlight, #backlight,
#network, #network,
#bluetooth, #bluetooth,
#battery { #battery,
#temperature,
#memory {
color: #d0d0d0; color: #d0d0d0;
} }
/* clock e power leggermente più evidenti */
#clock, #clock,
#custom-powermenu { #custom-powermenu {
color: #ffffff; color: #ffffff;
} }
#clock { #clock {
letter-spacing: 0.5px;
padding: 0 12px; padding: 0 12px;
} }
#custom-powermenu { #custom-powermenu {
padding-left: 12px; padding: 0 12px;
padding-right: 12px;
} }
#tray { #tray {
padding-right: 12px; padding-right: 12px;
} }
/* tooltip */
tooltip { tooltip {
background: #000000; background: #1f1f28;
border: 1px solid #ffffff; border: 1px solid #4a90d9;
color: #e6e6e6; color: #d0d0d0;
padding: 10px;
} }

View File

@@ -0,0 +1,23 @@
#!/bin/sh
set -eu
case "${XDG_CURRENT_DESKTOP:-}" in
*Hyprland*|*hyprland*)
exec hyprlock
;;
*i3*|*I3*)
exec "$HOME/.config/i3/scripts/lockscreen"
;;
esac
if command -v hyprlock >/dev/null 2>&1; then
exec hyprlock
fi
if [ -x "$HOME/.config/i3/scripts/lockscreen" ]; then
exec "$HOME/.config/i3/scripts/lockscreen"
fi
printf '%s\n' 'No supported lock command found.' >&2
exit 1

View File

@@ -0,0 +1,42 @@
#!/bin/sh
set -eu
choice="$(printf 'Shutdown\nReboot\nLogout\nLock\nSuspend' \
| rofi -dmenu \
-i \
-p 'Power' \
-theme ~/.config/rofi/config.rasi \
-theme-str 'window { width: 20%; location: center; anchor: center; } listview { columns: 1; spacing: 6px; }')"
[ -n "$choice" ] || exit 0
case "$choice" in
Lock)
"$HOME/.local/bin/lock-session"
;;
Logout)
case "${XDG_CURRENT_DESKTOP:-}" in
*Hyprland*|*hyprland*)
hyprctl dispatch exit
;;
*i3*|*I3*)
i3-msg exit
;;
*)
printf '%s\n' 'Unsupported desktop session for logout.' >&2
exit 1
;;
esac
;;
Suspend)
"$HOME/.local/bin/lock-session" || true
loginctl suspend
;;
Reboot)
loginctl reboot
;;
Shutdown)
loginctl poweroff
;;
esac

View File

@@ -0,0 +1,33 @@
#!/bin/sh
set -eu
mode=${1:-full}
target_dir="$HOME/Pictures/Screenshots"
target_file="$target_dir/$(date +%Y-%m-%d-%H%M%S).png"
mkdir -p "$target_dir"
case "$mode" in
full)
grim "$target_file"
;;
region)
grim -g "$(slurp)" "$target_file"
;;
output)
grim -g "$(slurp -o)" "$target_file"
;;
*)
printf 'Usage: %s [full|region|output]\n' "$0" >&2
exit 1
;;
esac
if command -v wl-copy >/dev/null 2>&1; then
wl-copy < "$target_file"
fi
if command -v notify-send >/dev/null 2>&1; then
notify-send "Screenshot saved" "$target_file"
fi

View File

@@ -0,0 +1,38 @@
#!/bin/sh
THEME="Yaru-blue-dark"
ICONS="Yaru-blue-dark"
CURSOR="Yaru"
FONT_UI="Liberation Sans 10"
mkdir -p "$HOME/.config/gtk-3.0"
mkdir -p "$HOME/.config/gtk-4.0"
cat > "$HOME/.config/gtk-3.0/settings.ini" <<EOF
[Settings]
gtk-theme-name=$THEME
gtk-icon-theme-name=$ICONS
gtk-cursor-theme-name=$CURSOR
gtk-font-name=$FONT_UI
gtk-application-prefer-dark-theme=1
gtk-xft-antialias=1
gtk-xft-hinting=1
gtk-xft-hintstyle=hintslight
gtk-xft-rgba=rgb
EOF
cat > "$HOME/.config/gtk-4.0/settings.ini" <<EOF
[Settings]
gtk-theme-name=$THEME
gtk-icon-theme-name=$ICONS
gtk-cursor-theme-name=$CURSOR
gtk-font-name=$FONT_UI
gtk-application-prefer-dark-theme=1
EOF
if command -v gsettings >/dev/null 2>&1; then
gsettings set org.gnome.desktop.interface gtk-theme "$THEME" >/dev/null 2>&1 || true
gsettings set org.gnome.desktop.interface icon-theme "$ICONS" >/dev/null 2>&1 || true
gsettings set org.gnome.desktop.interface cursor-theme "$CURSOR" >/dev/null 2>&1 || true
gsettings set org.gnome.desktop.interface color-scheme prefer-dark >/dev/null 2>&1 || true
fi

View File

@@ -0,0 +1,22 @@
#!/bin/sh
[ $# -gt 0 ] || set -- Hyprland
[ -r /etc/profile ] && . /etc/profile
[ -r "$HOME/.profile" ] && . "$HOME/.profile"
set -e
session_name=${1##*/}
export XDG_CURRENT_DESKTOP="$session_name"
export XDG_SESSION_DESKTOP="$session_name"
export XDG_SESSION_TYPE=wayland
exec dbus-run-session sh -eu -c '
umask 077
printf "%s\n" "$DBUS_SESSION_BUS_ADDRESS" > "$HOME/.dbus-session-bus-address"
eval "$(ssh-agent -s)" >/dev/null
gpgconf --launch gpg-agent
exec "$@"
' sh "$@"

View File

@@ -0,0 +1,5 @@
env = AQ_DRM_DEVICES,/dev/dri/card0:/dev/dri/card1
env = __GLX_VENDOR_LIBRARY_NAME,nvidia
env = GBM_BACKEND,nvidia-drm
env = LIBVA_DRIVER_NAME,iHD
env = WLR_NO_HARDWARE_CURSORS,1

View File

@@ -1,193 +0,0 @@
#############################################
# HYPRLAND CONFIG
# ThinkPad T480 (Intel + NVIDIA)
# Intel primary GPU + NVIDIA offload
#############################################
################
# Variables
################
$mod = SUPER
#$terminal = WINIT_UNIX_BACKEND=x11 ~/.local/bin/nvidia-run alacritty
$terminal = alacritty
$menu = rofi -show drun -config ~/.config/rofi/config.rasi
$powermenu = ~/.local/bin/powermenu
################
# Autostart
################
exec-once = hyprpaper
exec-once = waybar
exec-once = dunst
################
# Environment
################
env = AQ_DRM_DEVICES,/dev/dri/card0:/dev/dri/card1
env = __GLX_VENDOR_LIBRARY_NAME,nvidia
env = GBM_BACKEND,nvidia-drm
env = LIBVA_DRIVER_NAME,iHD
env = WLR_NO_HARDWARE_CURSORS,1
################
# Monitor
################
monitor = ,preferred,auto,1
################
# Input
################
input {
kb_layout = us
kb_variant = alt-intl
follow_mouse = 1
sensitivity = 0
touchpad {
natural_scroll = true
}
}
################
# General
################
general {
layout = master
gaps_in = 6
gaps_out = 10
border_size = 1
col.active_border = rgb(ffffff)
col.inactive_border = rgb(1a1a1a)
allow_tearing = false
}
################
# Master layout
################
master {
new_status = master
mfact = 0.55
inherit_fullscreen = true
}
################
# Decoration
################
decoration {
rounding = 0
blur {
enabled = false
}
}
################
# Animations
################
animations {
enabled = yes
bezier = easeOut, 0.16, 1, 0.3, 1
animation = windows,1,4,easeOut
animation = fade,1,4,easeOut
animation = workspaces,1,4,easeOut
}
################
# Misc
################
misc {
disable_hyprland_logo = true
disable_splash_rendering = true
}
################
# Window rules
################
windowrulev2 = opacity 0.96 0.96,class:^(Alacritty)$
windowrulev2 = float,class:^(Rofi)$
################
# Keybinds
################
# Terminal
bind = $mod, Return, exec, $terminal
# Launcher
bind = $mod, D, exec, $menu
# Powermenu
bind = $mod, Escape, exec, $powermenu
# Exit Hyprland (default)
bind = $mod SHIFT, E, exit
# Close window
bind = $mod, Q, killactive
# Fullscreen
bind = $mod, F, fullscreen, 1
# Floating
bind = $mod, Space, togglefloating
################
# Focus movement
################
bind = $mod, H, movefocus, l
bind = $mod, L, movefocus, r
bind = $mod, K, movefocus, u
bind = $mod, J, movefocus, d
################
# Master navigation
################
bind = $mod, J, layoutmsg, cyclenext
bind = $mod, K, layoutmsg, cycleprev
################
# 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 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

View File

@@ -1,4 +0,0 @@
preload = ~/Pictures/wallpapers/gargantua-black-hyprland.png
wallpaper = ,~/Pictures/wallpapers/gargantua-black-hyprland.png
splash = false
ipc = on

View File

@@ -1,146 +0,0 @@
{
"layer": "top",
"position": "top",
"height": 28,
"spacing": 4,
"modules-left": [
"hyprland/workspaces"
],
"modules-center": [
"custom/music"
],
"modules-right": [
"temperature",
"memory",
"cpu",
//"pulseaudio",
"backlight",
"network",
"bluetooth",
"battery",
"clock",
"custom/powermenu",
"tray"
],
"hyprland/workspaces": {
"disable-scroll": true,
"all-outputs": true,
"format": "{name}",
"on-click": "activate",
"persistent-workspaces": {
"*": [1, 2, 3, 4, 5]
}
},
"backlight": {
"device": "intel_backlight",
"on-scroll-up": "light -A 5",
"on-scroll-down": "light -U 5",
"format": "{percent}% {icon}",
"format-icons": ["", "", "", "", "", "", "", "", ""]
},
"pulseaudio": {
"scroll-step": 1,
"format": "{volume}% {icon}",
"format-muted": "mute ",
"format-icons": {
"headphone": "",
"phone": "",
"portable": "",
"car": "",
"default": ["", "", ""]
},
"on-click": "pavucontrol",
"tooltip": false
},
"battery": {
"interval": 10,
"states": {
"warning": 20,
"critical": 10
},
"format": "{capacity}% {icon}",
"format-charging": "{capacity}% 󰂄",
"format-plugged": "{capacity}% ",
"format-alt": "{time} {icon}",
"format-icons": ["󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂"]
},
"clock": {
"interval": 1,
"timezone": "Europe/Rome",
"format": "{:%H:%M}",
"tooltip-format": "{:%Y %B}\n{calendar}",
"format-alt": "{:%A %d/%m/%Y}"
},
"memory": {
"interval": 1,
"format": "{percentage}% ",
"states": {
"warning": 85
}
},
"cpu": {
"interval": 1,
"format": "{usage}% ",
"tooltip": false
},
"network": {
"interval": 1,
"tooltip": true,
"format-wifi": "{icon}",
"format-ethernet": "{icon}",
"format-linked": "󰈁",
"format-icons": ["󰤟", "󰤢", "󰤥", "󰤨"],
"format-disconnected": "󰈂",
"tooltip-format": "{ifname}",
"tooltip-format-wifi": "{essid} ({signalStrength}%) ",
"tooltip-format-ethernet": "{ifname} ",
"tooltip-format-disconnected": "Disconnected",
"on-click": "nm-connection-editor"
},
"temperature": {
"critical-threshold": 80,
"format": "{temperatureC}°C ",
"tooltip": false
},
"custom/powermenu": {
"format": "",
"on-click": "~/.local/bin/powermenu",
"tooltip": false
},
"tray": {
"icon-size": 15,
"spacing": 6
},
"bluetooth": {
"format": "",
"format-connected": "{device_alias} ",
"format-connected-battery": "{device_alias} {device_battery_percentage}% ",
"format-off": "󰂲",
"tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected",
"tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}",
"tooltip-format-enumerate-connected": "{device_alias}\t{device_address}",
"tooltip-format-enumerate-connected-battery": "{device_alias}\t{device_address}\t{device_battery_percentage}%",
"on-click": "blueman-manager"
},
"custom/music": {
"format": "{}",
"return-type": "json",
"exec": "waybar-module-music"
}
}