From bac7d92cf66437e1555ba1634c2b59b068c7be7c Mon Sep 17 00:00:00 2001 From: Fabio Scotto di Santolo Date: Sun, 21 Jun 2026 20:45:11 +0200 Subject: [PATCH] Add optional KDE desktop profile --- AGENTS.md | 13 +- README.md | 32 +- ansible/inventory/group_vars/desktop.yml | 53 ++- ansible/inventory/group_vars/void.yml | 71 ++-- ansible/roles/packages_void/tasks/main.yml | 55 ++- .../profile_desktop_common/tasks/main.yml | 58 ++- .../roles/profile_desktop_host/tasks/main.yml | 2 + .../profile_desktop_hyprland/tasks/main.yml | 28 +- .../roles/profile_desktop_kde/tasks/main.yml | 54 +++ .../roles/profile_desktop_sway/tasks/main.yml | 24 +- ansible/roles/services_runit/tasks/main.yml | 76 ++++ ansible/site.yml | 1 + dotfiles/desktop/.config/mimeapps.kde.list | 338 ++++++++++++++++++ 13 files changed, 718 insertions(+), 87 deletions(-) create mode 100644 ansible/roles/profile_desktop_kde/tasks/main.yml create mode 100644 dotfiles/desktop/.config/mimeapps.kde.list diff --git a/AGENTS.md b/AGENTS.md index f3cb80d..c49adaa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,7 +52,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_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. +- `desktop_environment` selects the mutually exclusive `minimal` (default) or `kde` desktop mode. `profile_desktop_common` owns shared bootstrap; the minimal mode uses `profile_desktop_sway` and `profile_desktop_hyprland`, while `profile_desktop_kde` owns KDE-specific defaults and cleanup. `desktop_sessions_enabled` and `desktop_default_session` apply only to the minimal mode. - `.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/`. @@ -60,16 +60,17 @@ Ansible-driven personal infrastructure repo for Void desktops, Linux workstation - Critical session entrypoints: - `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. +- Do not switch or restart a display manager during a playbook run from an active graphical session. Changing between `emptty` and SDDM requires an explicit run from another TTY/SSH session with `desktop_allow_display_manager_switch=true`. - `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 sway 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: +The 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_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. +- `desktop_common_packages` — GUI infrastructure shared by minimal and KDE modes. +- `desktop_minimal_packages` / `desktop_kde_packages` — mutually exclusive applications, integration components, and display managers. +- `desktop_sway_packages` / `desktop_hyprland_packages` — binaries specific to each session. Cross-WM packages used by both (such as `dunst`, `rofi`, and `foot`) 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`). +The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-independent content, while `desktop_minimal_dotfiles` carries Thunar, Udiskie, and minimal MIME defaults. KDE uses its own MIME defaults. `desktop_void_dotfiles` remains reserved for files that need the Void runtime. ## Workstation Notes - Native Linux workstation hosts can combine `workstation_host_linux` with an OS-specific dev group. diff --git a/README.md b/README.md index e268af0..8f62a4d 100644 --- a/README.md +++ b/README.md @@ -70,34 +70,36 @@ Sistema operativo: - Void Linux -Sessioni desktop: - -- `ikaros`: sway (Wayland) + Hyprland (Wayland), default sway -- `nymph`: sway (Wayland) + Hyprland (Wayland), default sway +Modalita desktop: + +- `minimal`: sway (Wayland) + Hyprland (Wayland), gestite da `emptty` +- `kde`: KDE Plasma, gestito da SDDM Macchine: - `ikaros` - `nymph` -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. +Queste macchine condividono la stessa configurazione base desktop e vengono mantenute allineate tramite Ansible. `desktop_environment` seleziona in modo esclusivo `minimal` (default) oppure `kde`. Nella modalita minimale 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 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` +- sessioni sway e Hyprland su entrambi gli host in modalita `minimal` +- KDE Plasma con applicazioni KDE curate e SDDM disponibile come alternativa esclusiva +- `emptty` con default host-specific in modalita `minimal`, con session file Wayland esposti 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_common_packages` per l'infrastruttura condivisa dalle due modalita + - `desktop_minimal_packages` per applicazioni GTK/XFCE e `emptty` + - `desktop_kde_packages` per Plasma, SDDM e le applicazioni KDE equivalenti - `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` - `tmux` con plugin gestiti da TPM al bootstrap del profilo desktop - Flatpak con remoto Flathub -- GNOME Keyring e bootstrap della posta via script dedicato -- `udiskie` come backend per automount/LUKS +- GNOME Keyring e `udiskie` nella modalita minimale; KWallet e integrazione UDisks di Plasma nella modalita KDE - multi-monitor: sotto sway è 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 @@ -395,6 +397,14 @@ Per vedere l'elenco reale aggiornato dei tag disponibili: ansible-playbook ansible/site.yml --list-tags ``` +Per attivare KDE su un host, impostare `desktop_environment: kde` nei relativi `host_vars` ed eseguire prima un dry run. Il primo play configura KDE ma, se `emptty` e ancora attivo, rinvia il cambio del display manager. Da una TTY o sessione SSH separata, applicare quindi il passaggio esplicito: + +```bash +ansible-playbook ansible/site.yml --limit -e desktop_allow_display_manager_switch=true +``` + +La stessa procedura vale in senso inverso per tornare a `minimal`. + Allo stato attuale `ansible/site.yml` espone questi tag: | Tag | Scopo | Ambito principale | @@ -407,6 +417,8 @@ Allo stato attuale `ansible/site.yml` espone questi tag: | `dotfiles:server` | dotfiles dedicati al profilo server | server | | `dotfiles:workstation` | dotfiles dedicati alle workstation | workstation Linux, WSL | | `emptty` | gestione display manager `emptty` | desktop Void | +| `display-manager` | selezione protetta tra `emptty` e SDDM | desktop Void | +| `kde` | profilo KDE Plasma e relativa pulizia | desktop Void | | `gnome` | configurazione host GNOME | workstation host Linux, parte desktop | | `sway` | sessione/configurazione sway / SwayFX (Wayland) | desktop Void | | `hyprland` | sessione/configurazione Hyprland (Wayland) | desktop Void | diff --git a/ansible/inventory/group_vars/desktop.yml b/ansible/inventory/group_vars/desktop.yml index e69eb98..d21fc67 100644 --- a/ansible/inventory/group_vars/desktop.yml +++ b/ansible/inventory/group_vars/desktop.yml @@ -1,6 +1,9 @@ --- desktop_protonmail_bridge_cert_path: ~/.var/app/ch.protonmail.protonmail-bridge/config/protonmail/bridge-v3/cert.pem +desktop_environment: minimal +desktop_allow_display_manager_switch: false + desktop_sessions_enabled: - sway - hyprland @@ -60,22 +63,6 @@ desktop_common_dotfiles: src: .local/bin/setup-gtk-theme dest: .local/bin/setup-gtk-theme mode: "0755" - - name: MIME application defaults - src: .config/mimeapps.list - dest: .config/mimeapps.list - mode: "0644" - - name: Thunar config - src: .config/Thunar/ - dest: .config/Thunar/ - mode: preserve - - name: Udiskie config - src: .config/udiskie/ - dest: .config/udiskie/ - mode: preserve - - name: Udiskie password helper - src: .local/bin/udiskie-password - dest: .local/bin/udiskie-password - mode: "0755" - name: WirePlumber config src: .config/wireplumber/ dest: .config/wireplumber/ @@ -145,6 +132,40 @@ desktop_common_dotfiles: dest: .ssh/config mode: "0600" +desktop_minimal_dotfiles: + - name: Minimal desktop MIME defaults + src: .config/mimeapps.list + dest: .config/mimeapps.list + mode: "0644" + - name: Thunar config + src: .config/Thunar/ + dest: .config/Thunar/ + mode: preserve + - name: Udiskie config + src: .config/udiskie/ + dest: .config/udiskie/ + mode: preserve + - name: Udiskie password helper + src: .local/bin/udiskie-password + dest: .local/bin/udiskie-password + mode: "0755" + +desktop_minimal_managed_paths: + - .config/Thunar + - .config/alacritty + - .config/dunst + - .config/flameshot + - .config/foot + - .config/gtklock + - .config/hypr + - .config/kanshi + - .config/rofi + - .config/sway + - .config/udiskie + - .config/waybar + - .config/wlogout + - .local/bin/udiskie-password + desktop_emacs_dotfiles: - name: Emacs config src: .emacs.d/ diff --git a/ansible/inventory/group_vars/void.yml b/ansible/inventory/group_vars/void.yml index 26e65f4..2cd2638 100644 --- a/ansible/inventory/group_vars/void.yml +++ b/ansible/inventory/group_vars/void.yml @@ -64,7 +64,6 @@ enabled_services: - cupsd - dbus - elogind - - emptty - libvirtd - nanoklogd - socklog-unix @@ -99,32 +98,64 @@ desktop_void_dotfiles: mode: "0755" desktop_common_packages: - - blueman - - brightnessctl - cups - cups-filters - cups-pk-helper - - dex - - emptty - flatpak - - gnome-keyring - - gvfs - libsecret - - network-manager-applet - - pavucontrol - pinentry-emacs - - pinentry-gtk - sane - sane-airscan + - turnstile + - xdg-desktop-portal + - xdg-user-dirs + +desktop_minimal_packages: + - alacritty + - blueman + - brightnessctl + - dex + - emptty + - exo + - ffmpegthumbnailer + - gnome-keyring + - gvfs + - gvfs-cdda + - gvfs-mtp + - gvfs-smb + - network-manager-applet + - pavucontrol + - pinentry-gtk + - ristretto - seahorse + - shotwell - simple-scan - system-config-printer - - turnstile + - Thunar + - thunar-archive-plugin + - thunar-volman + - tumbler - udiskie - - xdg-desktop-portal + - xarchiver - xdg-desktop-portal-gtk - - xdg-user-dirs - xfce-polkit + - xournalpp + +desktop_kde_packages: + - ark + - dolphin + - ffmpegthumbs + - gwenview + - kate + - kde-plasma + - kleopatra + - konsole + - okular + - polkit-kde-agent + - print-manager + - sddm + - skanpage + - spectacle desktop_sway_packages: - cliphist @@ -169,7 +200,6 @@ desktop_hyprland_packages: - xorg-server-xwayland profile_packages: - - alacritty - bash-language-server - bluez - bridge-utils @@ -181,12 +211,8 @@ profile_packages: - emacs-pgtk - poppler-glib - poppler-utils - - exo - fontconfig-devel - freetype-devel - - gvfs-cdda - - gvfs-mtp - - gvfs-smb - gufw - golangci-lint - gopls @@ -211,25 +237,16 @@ profile_packages: - remmina - ripgrep - rustup - - ristretto - rsync - - shotwell - shfmt - ruff - terminus-font - texlive - ty - - tumbler - uv - - Thunar - - thunar-archive-plugin - - thunar-volman - - ffmpegthumbnailer - virt-manager - virt-manager-tools - wireplumber - - xarchiver - - xournalpp - yaru - yaru-plus - zstd diff --git a/ansible/roles/packages_void/tasks/main.yml b/ansible/roles/packages_void/tasks/main.yml index 39d7fb4..facab6b 100644 --- a/ansible/roles/packages_void/tasks/main.yml +++ b/ansible/roles/packages_void/tasks/main.yml @@ -19,7 +19,7 @@ loop: "{{ void_xbps_repositories | default([]) + host_xbps_repositories | default([]) }}" loop_control: label: "{{ item.name }}" - register: void_xbps_repository_configs + register: packages_void_xbps_repository_configs when: (void_xbps_repositories | default([]) + host_xbps_repositories | default([])) | length > 0 - name: Install Void nonfree repository if needed @@ -28,7 +28,7 @@ name: void-repo-nonfree state: present update_cache: false - register: void_nonfree_repository + register: packages_void_nonfree_repository when: "'void-repo-nonfree' in (host_packages | default([]) + void_packages_base | default([]))" - name: Refresh XBPS cache after configuring repositories @@ -38,7 +38,7 @@ accept_pubkey: true when: >- (void_xbps_repositories | default([]) + host_xbps_repositories | default([])) | length > 0 - or (void_nonfree_repository is defined and void_nonfree_repository is changed) + or (packages_void_nonfree_repository is defined and packages_void_nonfree_repository is changed) - name: Synchronize and upgrade Void Linux packages tags: [packages] @@ -56,14 +56,25 @@ + (void_packages_base | default([])) + (profile_packages | default([])) + (desktop_common_packages | default([])) + + ( + (desktop_minimal_packages | default([])) + if (desktop_environment | default('minimal')) == 'minimal' + else (desktop_kde_packages | default([])) + ) + ( (desktop_sway_packages | default([])) - if 'sway' in (desktop_sessions_enabled | default([])) + if ( + (desktop_environment | default('minimal')) == 'minimal' + and 'sway' in (desktop_sessions_enabled | default([])) + ) else [] ) + ( (desktop_hyprland_packages | default([])) - if 'hyprland' in (desktop_sessions_enabled | default([])) + if ( + (desktop_environment | default('minimal')) == 'minimal' + and 'hyprland' in (desktop_sessions_enabled | default([])) + ) else [] ) + (host_packages | default([])) @@ -73,6 +84,40 @@ state: present update_cache: false +- name: Check whether the selected display manager is active + tags: [packages, display-manager] + ansible.builtin.stat: + path: >- + /var/service/{{ 'sddm' if (desktop_environment | default('minimal')) == 'kde' else 'emptty' }} + register: packages_void_selected_display_manager + +- name: Remove packages managed by the inactive desktop environment + tags: [packages] + community.general.xbps: + name: >- + {{ + ( + ( + (desktop_minimal_packages | default([])) + | reject('equalto', 'emptty') + | list + ) + + (desktop_sway_packages | default([])) + + (desktop_hyprland_packages | default([])) + ) + if (desktop_environment | default('minimal')) == 'kde' + else ( + (desktop_kde_packages | default([])) + | reject('equalto', 'sddm') + | list + ) + }} + state: absent + update_cache: false + when: >- + packages_void_selected_display_manager.stat.exists + or (desktop_allow_display_manager_switch | default(false)) + - name: Remove host-absent packages on Void Linux tags: [packages] community.general.xbps: diff --git a/ansible/roles/profile_desktop_common/tasks/main.yml b/ansible/roles/profile_desktop_common/tasks/main.yml index 45d9929..8b50e51 100644 --- a/ansible/roles/profile_desktop_common/tasks/main.yml +++ b/ansible/roles/profile_desktop_common/tasks/main.yml @@ -1,4 +1,22 @@ --- +- name: Validate desktop environment selection + tags: [always] + ansible.builtin.assert: + that: + - (desktop_environment | default('minimal')) in ['minimal', 'kde'] + - >- + (desktop_environment | default('minimal')) == 'kde' + or (desktop_sessions_enabled | default([]) | length > 0) + - >- + (desktop_environment | default('minimal')) == 'kde' + or (desktop_sessions_enabled | default([]) | difference(['sway', 'hyprland']) | length == 0) + - >- + (desktop_environment | default('minimal')) == 'kde' + or desktop_default_session in (desktop_sessions_enabled | default([])) + fail_msg: >- + desktop_environment must be minimal or kde. Minimal desktops also require + desktop_default_session to be included in desktop_sessions_enabled. + - name: Configure elogind to suspend on lid close tags: [packages] ansible.builtin.lineinfile: @@ -34,32 +52,33 @@ group: "{{ user_group }}" mode: "0755" -- name: Enable gnome-keyring PAM auth hook +- name: Manage 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 + state: "{{ 'present' if (desktop_environment | default('minimal')) == 'minimal' else 'absent' }}" when: "'void' in group_names" -- name: Enable gnome-keyring PAM session hook +- name: Manage 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 + state: "{{ 'present' if (desktop_environment | default('minimal')) == 'minimal' else 'absent' }}" when: "'void' in group_names" -- name: Enable gnome-keyring PAM password hook +- name: Manage 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 - when: "'void' in group_names" + state: "{{ 'present' if (desktop_environment | default('minimal')) == 'minimal' else 'absent' }}" + when: + - "'void' in group_names" - name: Check whether SSH host ed25519 key exists tags: [services] @@ -234,7 +253,9 @@ owner: root group: root mode: "0755" - when: "'void' in group_names" + when: + - "'void' in group_names" + - (desktop_environment | default('minimal')) == 'minimal' - name: Ensure emptty session directories exist tags: [packages, services, emptty] @@ -247,7 +268,9 @@ loop: - /etc/emptty/xsessions - /etc/emptty/wayland-sessions - when: "'void' in group_names" + when: + - "'void' in group_names" + - (desktop_environment | default('minimal')) == 'minimal' - name: Configure emptty tags: [packages, services, emptty] @@ -258,7 +281,9 @@ group: root mode: "0644" notify: Restart emptty - when: "'void' in group_names" + when: + - "'void' in group_names" + - (desktop_environment | default('minimal')) == 'minimal' - name: Copy common desktop dotfiles tags: [dotfiles, dotfiles:desktop] @@ -271,6 +296,11 @@ loop: >- {{ (desktop_common_dotfiles | default([])) + + ( + (desktop_minimal_dotfiles | default([])) + if (desktop_environment | default('minimal')) == 'minimal' + else [] + ) + ((desktop_void_dotfiles | default([])) if 'void' in group_names else []) }} loop_control: @@ -445,6 +475,14 @@ owner: root group: root mode: "0644" + when: (desktop_environment | default('minimal')) == 'minimal' + +- name: Remove minimal Qt theme override for KDE + tags: [theme] + ansible.builtin.file: + path: /etc/profile.d/10-qt-theme.sh + state: absent + when: (desktop_environment | default('minimal')) == 'kde' - name: Ensure flathub remote is configured tags: [packages] diff --git a/ansible/roles/profile_desktop_host/tasks/main.yml b/ansible/roles/profile_desktop_host/tasks/main.yml index 05a866a..7c7488a 100644 --- a/ansible/roles/profile_desktop_host/tasks/main.yml +++ b/ansible/roles/profile_desktop_host/tasks/main.yml @@ -16,6 +16,7 @@ loop_control: label: "{{ item.dest }}" when: + - (desktop_environment | default('minimal')) == 'minimal' - "'sway' in (desktop_sessions_enabled | default([]))" - (host_sway_dotfiles | default([])) | length > 0 @@ -32,5 +33,6 @@ loop_control: label: "{{ item.dest }}" when: + - (desktop_environment | default('minimal')) == 'minimal' - "'hyprland' in (desktop_sessions_enabled | default([]))" - (host_hyprland_dotfiles | default([])) | length > 0 diff --git a/ansible/roles/profile_desktop_hyprland/tasks/main.yml b/ansible/roles/profile_desktop_hyprland/tasks/main.yml index 8badcf3..1df1a99 100644 --- a/ansible/roles/profile_desktop_hyprland/tasks/main.yml +++ b/ansible/roles/profile_desktop_hyprland/tasks/main.yml @@ -15,7 +15,9 @@ - "{{ user_home }}/.config/hypr" - "{{ user_home }}/.config/rofi" - "{{ user_home }}/.config/waybar" - when: "'hyprland' in (desktop_sessions_enabled | default([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'hyprland' in (desktop_sessions_enabled | default([]))" - name: Install allowed emptty Hyprland Wayland session tags: [packages, services, emptty, hyprland] @@ -25,14 +27,18 @@ owner: root group: root mode: "0644" - when: "'hyprland' in (desktop_sessions_enabled | default([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'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([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'hyprland' in (desktop_sessions_enabled | default([]))" - name: Install Hyprland session launcher in /usr/local/bin tags: [packages, services, hyprland] @@ -42,7 +48,9 @@ owner: root group: root mode: "0755" - when: "'hyprland' in (desktop_sessions_enabled | default([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'hyprland' in (desktop_sessions_enabled | default([]))" - name: Ensure /etc/xdg/xdg-desktop-portal exists for Hyprland tags: [packages, services, hyprland, portal] @@ -52,7 +60,9 @@ owner: root group: root mode: "0755" - when: "'hyprland' in (desktop_sessions_enabled | default([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'hyprland' in (desktop_sessions_enabled | default([]))" - name: Configure xdg-desktop-portal backend preferences for Hyprland tags: [packages, services, hyprland, portal] @@ -62,7 +72,9 @@ owner: root group: root mode: "0644" - when: "'hyprland' in (desktop_sessions_enabled | default([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'hyprland' in (desktop_sessions_enabled | default([]))" - name: Copy Hyprland desktop dotfiles tags: [dotfiles, dotfiles:desktop, hyprland] @@ -75,4 +87,6 @@ loop: "{{ desktop_hyprland_dotfiles | default([]) }}" loop_control: label: "{{ item.dest }}" - when: "'hyprland' in (desktop_sessions_enabled | default([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'hyprland' in (desktop_sessions_enabled | default([]))" diff --git a/ansible/roles/profile_desktop_kde/tasks/main.yml b/ansible/roles/profile_desktop_kde/tasks/main.yml new file mode 100644 index 0000000..2891991 --- /dev/null +++ b/ansible/roles/profile_desktop_kde/tasks/main.yml @@ -0,0 +1,54 @@ +--- +- name: Check whether SDDM is the active runit display manager + tags: [packages, services, kde, display-manager] + ansible.builtin.stat: + path: /var/service/sddm + register: profile_desktop_kde_sddm_service + when: (desktop_environment | default('minimal')) == 'kde' + +- name: Copy KDE MIME application defaults + tags: [dotfiles, dotfiles:desktop, kde] + ansible.builtin.copy: + src: "{{ playbook_dir }}/../dotfiles/desktop/.config/mimeapps.kde.list" + dest: "{{ user_home }}/.config/mimeapps.list" + owner: "{{ username }}" + group: "{{ user_group }}" + mode: "0644" + when: (desktop_environment | default('minimal')) == 'kde' + +- name: Remove minimal desktop user configuration managed by Ansible + tags: [dotfiles, dotfiles:desktop, kde] + ansible.builtin.file: + path: "{{ user_home }}/{{ item }}" + state: absent + loop: "{{ desktop_minimal_managed_paths | default([]) }}" + loop_control: + label: "{{ item }}" + when: + - (desktop_environment | default('minimal')) == 'kde' + - profile_desktop_kde_sddm_service.stat.exists + +- name: Remove minimal desktop portal preferences + tags: [packages, services, kde, portal] + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /etc/xdg/xdg-desktop-portal/hyprland-portals.conf + - /etc/xdg/xdg-desktop-portal/sway-portals.conf + when: + - (desktop_environment | default('minimal')) == 'kde' + - profile_desktop_kde_sddm_service.stat.exists + +- name: Remove inactive emptty and minimal session launchers + tags: [packages, services, kde, display-manager] + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - /etc/emptty + - /usr/local/bin/start-hyprland-session + - /usr/local/bin/start-sway + when: + - (desktop_environment | default('minimal')) == 'kde' + - profile_desktop_kde_sddm_service.stat.exists diff --git a/ansible/roles/profile_desktop_sway/tasks/main.yml b/ansible/roles/profile_desktop_sway/tasks/main.yml index 6d76dca..b92f3a2 100644 --- a/ansible/roles/profile_desktop_sway/tasks/main.yml +++ b/ansible/roles/profile_desktop_sway/tasks/main.yml @@ -15,7 +15,9 @@ - "{{ user_home }}/.config/sway" - "{{ user_home }}/.config/waybar" - "{{ user_home }}/.config/wlogout" - when: "'sway' in (desktop_sessions_enabled | default([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'sway' in (desktop_sessions_enabled | default([]))" - name: Install allowed emptty Wayland sessions tags: [packages, services, emptty, sway] @@ -25,7 +27,9 @@ owner: root group: root mode: "0644" - when: "'sway' in (desktop_sessions_enabled | default([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'sway' in (desktop_sessions_enabled | default([]))" - name: Install Sway session launcher in /usr/local/bin tags: [packages, services, sway] @@ -35,7 +39,9 @@ owner: root group: root mode: "0755" - when: "'sway' in (desktop_sessions_enabled | default([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'sway' in (desktop_sessions_enabled | default([]))" - name: Ensure /etc/xdg/xdg-desktop-portal exists tags: [packages, services, sway, portal] @@ -45,7 +51,9 @@ owner: root group: root mode: "0755" - when: "'sway' in (desktop_sessions_enabled | default([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'sway' in (desktop_sessions_enabled | default([]))" - name: Configure xdg-desktop-portal backend preferences for sway tags: [packages, services, sway, portal] @@ -55,7 +63,9 @@ owner: root group: root mode: "0644" - when: "'sway' in (desktop_sessions_enabled | default([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'sway' in (desktop_sessions_enabled | default([]))" - name: Copy sway desktop dotfiles tags: [dotfiles, dotfiles:desktop, sway] @@ -68,4 +78,6 @@ loop: "{{ desktop_sway_dotfiles | default([]) }}" loop_control: label: "{{ item.dest }}" - when: "'sway' in (desktop_sessions_enabled | default([]))" + when: + - (desktop_environment | default('minimal')) == 'minimal' + - "'sway' in (desktop_sessions_enabled | default([]))" diff --git a/ansible/roles/services_runit/tasks/main.yml b/ansible/roles/services_runit/tasks/main.yml index ed6572a..c2cd538 100644 --- a/ansible/roles/services_runit/tasks/main.yml +++ b/ansible/roles/services_runit/tasks/main.yml @@ -14,3 +14,79 @@ dest: "/var/service/{{ item }}" state: link loop: "{{ host_enabled_services | default([]) }}" + +- name: Define desktop display manager services + tags: [services, packages, display-manager] + ansible.builtin.set_fact: + services_runit_desktop_display_manager_service: >- + {{ 'sddm' if (desktop_environment | default('minimal')) == 'kde' else 'emptty' }} + services_runit_inactive_display_manager_service: >- + {{ 'emptty' if (desktop_environment | default('minimal')) == 'kde' else 'sddm' }} + when: "'desktop' in group_names" + +- name: Inspect desktop display manager services + tags: [services, packages, display-manager] + ansible.builtin.stat: + path: "/var/service/{{ item }}" + loop: + - "{{ services_runit_desktop_display_manager_service }}" + - "{{ services_runit_inactive_display_manager_service }}" + loop_control: + label: "{{ item }}" + register: services_runit_display_manager_links + when: "'desktop' in group_names" + +- name: Report deferred desktop display manager switch + tags: [services, packages, display-manager] + ansible.builtin.debug: + msg: >- + {{ desktop_environment }} is configured, but {{ services_runit_inactive_display_manager_service }} + is still active. Re-run from a TTY or SSH session with + -e desktop_allow_display_manager_switch=true to switch safely to + {{ services_runit_desktop_display_manager_service }}. + when: + - "'desktop' in group_names" + - services_runit_display_manager_links.results[1].stat.exists + - not (services_runit_display_manager_links.results[0].stat.exists) + - not (desktop_allow_display_manager_switch | default(false)) + +- name: Disable inactive desktop display manager + tags: [services, packages, display-manager] + ansible.builtin.file: + path: "/var/service/{{ services_runit_inactive_display_manager_service }}" + state: absent + register: services_runit_display_manager_disabled + when: + - "'desktop' in group_names" + - services_runit_display_manager_links.results[1].stat.exists + - not (services_runit_display_manager_links.results[0].stat.exists) + - desktop_allow_display_manager_switch | default(false) + +- name: Enable selected desktop display manager + tags: [services, packages, display-manager] + ansible.builtin.file: + src: "/etc/sv/{{ services_runit_desktop_display_manager_service }}" + dest: "/var/service/{{ services_runit_desktop_display_manager_service }}" + state: link + register: services_runit_display_manager_enabled + when: + - "'desktop' in group_names" + - not (services_runit_display_manager_links.results[0].stat.exists) + - >- + not (services_runit_display_manager_links.results[1].stat.exists) + or (desktop_allow_display_manager_switch | default(false)) + +- name: Remove inactive desktop display manager package after switching + tags: [services, packages, display-manager] + community.general.xbps: + name: "{{ services_runit_inactive_display_manager_service }}" + state: absent + update_cache: false + when: + - "'desktop' in group_names" + - >- + services_runit_display_manager_links.results[0].stat.exists + or (services_runit_display_manager_enabled is defined and services_runit_display_manager_enabled is changed) + - >- + not (services_runit_display_manager_links.results[1].stat.exists) + or (services_runit_display_manager_disabled is defined and services_runit_display_manager_disabled is changed) diff --git a/ansible/site.yml b/ansible/site.yml index a9e89f9..53314dc 100644 --- a/ansible/site.yml +++ b/ansible/site.yml @@ -26,6 +26,7 @@ - profile_desktop_common - profile_desktop_sway - profile_desktop_hyprland + - profile_desktop_kde - profile_desktop_host - hosts: workstation_dev_fedora diff --git a/dotfiles/desktop/.config/mimeapps.kde.list b/dotfiles/desktop/.config/mimeapps.kde.list new file mode 100644 index 0000000..58140aa --- /dev/null +++ b/dotfiles/desktop/.config/mimeapps.kde.list @@ -0,0 +1,338 @@ +[Default Applications] +text/html=userapp-Firefox-91DVN3.desktop +x-scheme-handler/http=userapp-Firefox-91DVN3.desktop +x-scheme-handler/https=userapp-Firefox-91DVN3.desktop +x-scheme-handler/about=firefox.desktop +x-scheme-handler/unknown=firefox.desktop +application/pdf=org.kde.okular.desktop +application/json=emacs.desktop +application/json5=emacs.desktop +application/xml=emacs.desktop +application/xml-external-parsed-entity=emacs.desktop +application/x-shellscript=emacs.desktop +application/yaml=emacs.desktop +inode/directory=org.kde.dolphin.desktop +image/bmp=org.kde.gwenview.desktop +image/gif=org.kde.gwenview.desktop +image/jpeg=org.kde.gwenview.desktop +image/png=org.kde.gwenview.desktop +image/svg+xml=org.kde.gwenview.desktop +image/tiff=org.kde.gwenview.desktop +image/webp=org.kde.gwenview.desktop +image/x-pixmap=org.kde.gwenview.desktop +image/x-xpixmap=org.kde.gwenview.desktop +audio/aac=mpv.desktop +audio/flac=mpv.desktop +audio/m4a=mpv.desktop +audio/mp3=mpv.desktop +audio/mpeg=mpv.desktop +audio/mp4=mpv.desktop +audio/ogg=mpv.desktop +audio/opus=mpv.desktop +audio/vnd.wave=mpv.desktop +audio/wav=mpv.desktop +audio/webm=mpv.desktop +audio/x-aac=mpv.desktop +audio/x-m4a=mpv.desktop +audio/x-matroska=mpv.desktop +audio/x-mp3=mpv.desktop +audio/x-mpegurl=mpv.desktop +audio/x-ms-wma=mpv.desktop +audio/x-ogg=mpv.desktop +audio/x-pn-wav=mpv.desktop +audio/x-scpls=mpv.desktop +audio/x-wav=mpv.desktop +video/3gp=mpv.desktop +video/3gpp=mpv.desktop +video/3gpp2=mpv.desktop +video/avi=mpv.desktop +video/mp2t=mpv.desktop +video/mp4=mpv.desktop +video/mp4v-es=mpv.desktop +video/mpeg=mpv.desktop +video/ogg=mpv.desktop +video/quicktime=mpv.desktop +video/vnd.avi=mpv.desktop +video/webm=mpv.desktop +video/x-avi=mpv.desktop +video/x-flc=mpv.desktop +video/x-flic=mpv.desktop +video/x-flv=mpv.desktop +video/x-m4v=mpv.desktop +video/x-matroska=mpv.desktop +video/x-mpeg2=mpv.desktop +video/x-ms-asf=mpv.desktop +video/x-ms-wmv=mpv.desktop +video/x-msvideo=mpv.desktop +video/x-ogm+ogg=mpv.desktop +video/x-theora=mpv.desktop +video/x-theora+ogg=mpv.desktop +application/msword=libreoffice-writer.desktop +application/rtf=libreoffice-writer.desktop +application/vnd.ms-word=libreoffice-writer.desktop +application/vnd.ms-word.document.macroEnabled.12=libreoffice-writer.desktop +application/vnd.ms-word.template.macroEnabled.12=libreoffice-writer.desktop +application/vnd.oasis.opendocument.text=libreoffice-writer.desktop +application/vnd.oasis.opendocument.text-template=libreoffice-writer.desktop +application/vnd.openxmlformats-officedocument.wordprocessingml.document=libreoffice-writer.desktop +application/vnd.openxmlformats-officedocument.wordprocessingml.template=libreoffice-writer.desktop +application/x-doc=libreoffice-writer.desktop +text/rtf=libreoffice-writer.desktop +application/csv=libreoffice-calc.desktop +application/excel=libreoffice-calc.desktop +application/msexcel=libreoffice-calc.desktop +application/tab-separated-values=libreoffice-calc.desktop +application/vnd.ms-excel=libreoffice-calc.desktop +application/vnd.ms-excel.sheet.binary.macroEnabled.12=libreoffice-calc.desktop +application/vnd.ms-excel.sheet.macroEnabled.12=libreoffice-calc.desktop +application/vnd.ms-excel.template.macroEnabled.12=libreoffice-calc.desktop +application/vnd.oasis.opendocument.spreadsheet=libreoffice-calc.desktop +application/vnd.oasis.opendocument.spreadsheet-template=libreoffice-calc.desktop +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet=libreoffice-calc.desktop +application/vnd.openxmlformats-officedocument.spreadsheetml.template=libreoffice-calc.desktop +application/x-dos_ms_excel=libreoffice-calc.desktop +application/x-excel=libreoffice-calc.desktop +application/x-ms-excel=libreoffice-calc.desktop +application/x-msexcel=libreoffice-calc.desktop +text/comma-separated-values=libreoffice-calc.desktop +text/csv=libreoffice-calc.desktop +text/tab-separated-values=libreoffice-calc.desktop +text/x-comma-separated-values=libreoffice-calc.desktop +text/x-csv=libreoffice-calc.desktop +application/mspowerpoint=libreoffice-impress.desktop +application/vnd.ms-powerpoint=libreoffice-impress.desktop +application/vnd.ms-powerpoint.presentation.macroEnabled.12=libreoffice-impress.desktop +application/vnd.ms-powerpoint.slideshow.macroEnabled.12=libreoffice-impress.desktop +application/vnd.ms-powerpoint.template.macroEnabled.12=libreoffice-impress.desktop +application/vnd.oasis.opendocument.presentation=libreoffice-impress.desktop +application/vnd.oasis.opendocument.presentation-template=libreoffice-impress.desktop +application/vnd.openxmlformats-officedocument.presentationml.presentation=libreoffice-impress.desktop +application/vnd.openxmlformats-officedocument.presentationml.slide=libreoffice-impress.desktop +application/vnd.openxmlformats-officedocument.presentationml.slideshow=libreoffice-impress.desktop +application/vnd.openxmlformats-officedocument.presentationml.template=libreoffice-impress.desktop +application/gzip=org.kde.ark.desktop +application/vnd.debian.binary-package=org.kde.ark.desktop +application/vnd.ms-cab-compressed=org.kde.ark.desktop +application/vnd.rar=org.kde.ark.desktop +application/x-7z-compressed=org.kde.ark.desktop +application/x-archive=org.kde.ark.desktop +application/x-arj=org.kde.ark.desktop +application/x-bzip=org.kde.ark.desktop +application/x-bzip-compressed-tar=org.kde.ark.desktop +application/x-bzip2=org.kde.ark.desktop +application/x-bzip2-compressed-tar=org.kde.ark.desktop +application/x-cd-image=org.kde.ark.desktop +application/x-compressed-tar=org.kde.ark.desktop +application/x-cpio=org.kde.ark.desktop +application/x-cpio-compressed=org.kde.ark.desktop +application/x-lha=org.kde.ark.desktop +application/x-lrzip=org.kde.ark.desktop +application/x-lrzip-compressed-tar=org.kde.ark.desktop +application/x-lzip=org.kde.ark.desktop +application/x-lzip-compressed-tar=org.kde.ark.desktop +application/x-lzma=org.kde.ark.desktop +application/x-lzma-compressed-tar=org.kde.ark.desktop +application/x-rar=org.kde.ark.desktop +application/x-rpm=org.kde.ark.desktop +application/x-rzip=org.kde.ark.desktop +application/x-rzip-compressed-tar=org.kde.ark.desktop +application/x-source-rpm=org.kde.ark.desktop +application/x-tar=org.kde.ark.desktop +application/x-tarz=org.kde.ark.desktop +application/x-xz=org.kde.ark.desktop +application/x-xz-compressed-tar=org.kde.ark.desktop +application/x-zpaq=org.kde.ark.desktop +application/x-zstd-compressed-tar=org.kde.ark.desktop +application/zip=org.kde.ark.desktop +application/zstd=org.kde.ark.desktop +text/markdown=emacs.desktop +text/org=emacs.desktop +text/plain=emacs.desktop +text/rust=emacs.desktop +text/x-c++hdr=emacs.desktop +text/x-c++src=emacs.desktop +text/x-chdr=emacs.desktop +text/x-csrc=emacs.desktop +text/x-emacs-lisp=emacs.desktop +text/x-go=emacs.desktop +text/x-java=emacs.desktop +text/x-makefile=emacs.desktop +text/x-patch=emacs.desktop +text/x-python=emacs.desktop +text/x-python3=emacs.desktop +text/x-readme=emacs.desktop +text/x-rst=emacs.desktop +text/x-tex=emacs.desktop +text/x-texinfo=emacs.desktop +x-scheme-handler/chrome=userapp-Firefox-91DVN3.desktop +application/x-extension-htm=userapp-Firefox-91DVN3.desktop +application/x-extension-html=userapp-Firefox-91DVN3.desktop +application/x-extension-shtml=userapp-Firefox-91DVN3.desktop +application/xhtml+xml=userapp-Firefox-91DVN3.desktop +application/x-extension-xhtml=userapp-Firefox-91DVN3.desktop +application/x-extension-xht=userapp-Firefox-91DVN3.desktop + +[Added Associations] +application/json=emacs.desktop; +application/json5=emacs.desktop; +application/pdf=org.kde.okular.desktop; +application/xml=emacs.desktop; +application/xml-external-parsed-entity=emacs.desktop; +application/x-shellscript=emacs.desktop; +application/yaml=emacs.desktop; +text/plain=emacs.desktop; +inode/directory=org.kde.dolphin.desktop; +image/bmp=org.kde.gwenview.desktop; +image/gif=org.kde.gwenview.desktop; +image/jpeg=org.kde.gwenview.desktop; +image/png=org.kde.gwenview.desktop; +image/svg+xml=org.kde.gwenview.desktop; +image/tiff=org.kde.gwenview.desktop; +image/webp=org.kde.gwenview.desktop; +image/x-pixmap=org.kde.gwenview.desktop; +image/x-xpixmap=org.kde.gwenview.desktop; +audio/aac=mpv.desktop; +audio/flac=mpv.desktop; +audio/m4a=mpv.desktop; +audio/mp3=mpv.desktop; +audio/mpeg=mpv.desktop; +audio/mp4=mpv.desktop; +audio/ogg=mpv.desktop; +audio/opus=mpv.desktop; +audio/vnd.wave=mpv.desktop; +audio/wav=mpv.desktop; +audio/webm=mpv.desktop; +audio/x-aac=mpv.desktop; +audio/x-m4a=mpv.desktop; +audio/x-matroska=mpv.desktop; +audio/x-mp3=mpv.desktop; +audio/x-mpegurl=mpv.desktop; +audio/x-ms-wma=mpv.desktop; +audio/x-ogg=mpv.desktop; +audio/x-pn-wav=mpv.desktop; +audio/x-scpls=mpv.desktop; +audio/x-wav=mpv.desktop; +video/3gp=mpv.desktop; +video/3gpp=mpv.desktop; +video/3gpp2=mpv.desktop; +video/avi=mpv.desktop; +video/mp2t=mpv.desktop; +video/mp4=mpv.desktop; +video/mp4v-es=mpv.desktop; +video/mpeg=mpv.desktop; +video/ogg=mpv.desktop; +video/quicktime=mpv.desktop; +video/vnd.avi=mpv.desktop; +video/webm=mpv.desktop; +video/x-avi=mpv.desktop; +video/x-flc=mpv.desktop; +video/x-flic=mpv.desktop; +video/x-flv=mpv.desktop; +video/x-m4v=mpv.desktop; +video/x-matroska=mpv.desktop; +video/x-mpeg2=mpv.desktop; +video/x-ms-asf=mpv.desktop; +video/x-ms-wmv=mpv.desktop; +video/x-msvideo=mpv.desktop; +video/x-ogm+ogg=mpv.desktop; +video/x-theora=mpv.desktop; +video/x-theora+ogg=mpv.desktop; +application/msword=libreoffice-writer.desktop; +application/rtf=libreoffice-writer.desktop; +application/vnd.ms-word=libreoffice-writer.desktop; +application/vnd.ms-word.document.macroEnabled.12=libreoffice-writer.desktop; +application/vnd.ms-word.template.macroEnabled.12=libreoffice-writer.desktop; +application/vnd.oasis.opendocument.text=libreoffice-writer.desktop; +application/vnd.oasis.opendocument.text-template=libreoffice-writer.desktop; +application/vnd.openxmlformats-officedocument.wordprocessingml.document=libreoffice-writer.desktop; +application/vnd.openxmlformats-officedocument.wordprocessingml.template=libreoffice-writer.desktop; +application/x-doc=libreoffice-writer.desktop; +text/rtf=libreoffice-writer.desktop; +application/csv=libreoffice-calc.desktop; +application/excel=libreoffice-calc.desktop; +application/msexcel=libreoffice-calc.desktop; +application/tab-separated-values=libreoffice-calc.desktop; +application/vnd.ms-excel=libreoffice-calc.desktop; +application/vnd.ms-excel.sheet.binary.macroEnabled.12=libreoffice-calc.desktop; +application/vnd.ms-excel.sheet.macroEnabled.12=libreoffice-calc.desktop; +application/vnd.ms-excel.template.macroEnabled.12=libreoffice-calc.desktop; +application/vnd.oasis.opendocument.spreadsheet=libreoffice-calc.desktop; +application/vnd.oasis.opendocument.spreadsheet-template=libreoffice-calc.desktop; +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet=libreoffice-calc.desktop; +application/vnd.openxmlformats-officedocument.spreadsheetml.template=libreoffice-calc.desktop; +application/x-dos_ms_excel=libreoffice-calc.desktop; +application/x-excel=libreoffice-calc.desktop; +application/x-ms-excel=libreoffice-calc.desktop; +application/x-msexcel=libreoffice-calc.desktop; +text/comma-separated-values=libreoffice-calc.desktop; +text/csv=libreoffice-calc.desktop; +text/tab-separated-values=libreoffice-calc.desktop; +text/x-comma-separated-values=libreoffice-calc.desktop; +text/x-csv=libreoffice-calc.desktop; +application/mspowerpoint=libreoffice-impress.desktop; +application/vnd.ms-powerpoint=libreoffice-impress.desktop; +application/vnd.ms-powerpoint.presentation.macroEnabled.12=libreoffice-impress.desktop; +application/vnd.ms-powerpoint.slideshow.macroEnabled.12=libreoffice-impress.desktop; +application/vnd.ms-powerpoint.template.macroEnabled.12=libreoffice-impress.desktop; +application/vnd.oasis.opendocument.presentation=libreoffice-impress.desktop; +application/vnd.oasis.opendocument.presentation-template=libreoffice-impress.desktop; +application/vnd.openxmlformats-officedocument.presentationml.presentation=libreoffice-impress.desktop; +application/vnd.openxmlformats-officedocument.presentationml.slide=libreoffice-impress.desktop; +application/vnd.openxmlformats-officedocument.presentationml.slideshow=libreoffice-impress.desktop; +application/vnd.openxmlformats-officedocument.presentationml.template=libreoffice-impress.desktop; +application/gzip=org.kde.ark.desktop; +application/vnd.debian.binary-package=org.kde.ark.desktop; +application/vnd.ms-cab-compressed=org.kde.ark.desktop; +application/vnd.rar=org.kde.ark.desktop; +application/x-7z-compressed=org.kde.ark.desktop; +application/x-archive=org.kde.ark.desktop; +application/x-arj=org.kde.ark.desktop; +application/x-bzip=org.kde.ark.desktop; +application/x-bzip-compressed-tar=org.kde.ark.desktop; +application/x-bzip2=org.kde.ark.desktop; +application/x-bzip2-compressed-tar=org.kde.ark.desktop; +application/x-cd-image=org.kde.ark.desktop; +application/x-compressed-tar=org.kde.ark.desktop; +application/x-cpio=org.kde.ark.desktop; +application/x-cpio-compressed=org.kde.ark.desktop; +application/x-lha=org.kde.ark.desktop; +application/x-lrzip=org.kde.ark.desktop; +application/x-lrzip-compressed-tar=org.kde.ark.desktop; +application/x-lzip=org.kde.ark.desktop; +application/x-lzip-compressed-tar=org.kde.ark.desktop; +application/x-lzma=org.kde.ark.desktop; +application/x-lzma-compressed-tar=org.kde.ark.desktop; +application/x-rar=org.kde.ark.desktop; +application/x-rpm=org.kde.ark.desktop; +application/x-rzip=org.kde.ark.desktop; +application/x-rzip-compressed-tar=org.kde.ark.desktop; +application/x-source-rpm=org.kde.ark.desktop; +application/x-tar=org.kde.ark.desktop; +application/x-tarz=org.kde.ark.desktop; +application/x-xz=org.kde.ark.desktop; +application/x-xz-compressed-tar=org.kde.ark.desktop; +application/x-zpaq=org.kde.ark.desktop; +application/x-zstd-compressed-tar=org.kde.ark.desktop; +application/zip=org.kde.ark.desktop; +application/zstd=org.kde.ark.desktop; +text/markdown=emacs.desktop; +text/org=emacs.desktop; +text/rust=emacs.desktop; +text/x-c++hdr=emacs.desktop; +text/x-c++src=emacs.desktop; +text/x-chdr=emacs.desktop; +text/x-csrc=emacs.desktop; +text/x-emacs-lisp=emacs.desktop; +text/x-go=emacs.desktop; +text/x-java=emacs.desktop; +text/x-makefile=emacs.desktop; +text/x-patch=emacs.desktop; +text/x-python=emacs.desktop; +text/x-python3=emacs.desktop; +text/x-readme=emacs.desktop; +text/x-rst=emacs.desktop; +text/x-tex=emacs.desktop; +text/x-texinfo=emacs.desktop; +x-scheme-handler/http=userapp-Firefox-91DVN3.desktop; +x-scheme-handler/https=userapp-Firefox-91DVN3.desktop; +x-scheme-handler/chrome=userapp-Firefox-91DVN3.desktop;