Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
20e0643a02
|
|||
|
071cc307b4
|
|||
|
1917a3321b
|
|||
|
19c16006fb
|
+2
-42
@@ -2,41 +2,15 @@
|
|||||||
Changelog
|
Changelog
|
||||||
#########
|
#########
|
||||||
|
|
||||||
[0.6.0] - 2026-07-07
|
[XX] - YYYY-MM-DD
|
||||||
====================
|
=================
|
||||||
|
|
||||||
Added
|
Added
|
||||||
-----
|
-----
|
||||||
|
|
||||||
- **Installateur Anaconda 45 porté sur Arch** (payload LiveOS) : page « Récapitulatif avant installation » avec bouton
|
|
||||||
Précédent, choix du bureau intégré (GNOME, KDE, XFCE, Hyprland, i3, CLI) avec compatibilité VR affichée, splash de
|
|
||||||
préparation alimenté en direct par les logs (diagnostic automatique en cas de blocage), garde-fou RAM (< 2 Go),
|
|
||||||
mécanisme de retry réseau pour l'installation du bureau (``fws-desktop-retry``).
|
|
||||||
- **Outillage FWS** : ``fws-hello`` (accueil 1er démarrage), ``fws-update`` + indicateur de mises à jour,
|
|
||||||
``fws-keys`` (raccourcis réels), ``fws-powermenu``, ``fws-about``, ``fws-vr-setup`` (SteamVR/ALVR/Monado),
|
|
||||||
``fws-proton-ge``, ``fws-setup-hardware`` (drivers GPU/VM/audio/Bluetooth), ``fws-dnd``.
|
|
||||||
- **Récupération système** (« point de restauration ») : application GTK ``fws-recovery-gui`` + moteur ``fws-recovery``
|
|
||||||
(GRUB, initramfs, pacman, chroot), accessible depuis le live (entrée de boot), le menu GRUB du système installé
|
|
||||||
(``fws-recovery.target``) et son menu d'applications (élévation polkit).
|
|
||||||
- **Swap zram** sur le live et le système installé (``zram-generator``).
|
|
||||||
- **firewalld** activé par défaut sur le système installé.
|
|
||||||
- Bureaux Hyprland et i3 à parité visuelle (waybar/polybar, wofi/rofi, Catppuccin Mocha), thème SDDM « fws »,
|
|
||||||
bannière GDM.
|
|
||||||
- **Choix du nom de machine à l'installation** : champ « Nom de machine » dans la page Utilisateur d'anaconda,
|
|
||||||
appliqué au système installé (``/etc/hostname``, repli ``fws``).
|
|
||||||
- **Script de build interactif** (``build-offi.sh``) : version (``major.minor.build``) et variante (``cli``, ``gnome``,
|
|
||||||
``kde``…) demandées sur l'hôte avant d'entrer dans le conteneur, ISO nommée
|
|
||||||
``fws-<version>-<variante>-<AAAA.MM.JJ>.iso``, logo FWS dans fastfetch.
|
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
-------
|
-------
|
||||||
|
|
||||||
- **Branding FWS complet** : l'installateur s'annonce « FWS 0.6 » (variables ``ANACONDA_PRODUCT*``), splash FWS au menu
|
|
||||||
de boot BIOS (l'image Arch d'archiso est remplacée), ``/etc/issue`` sans version de noyau, filtres d'affichage
|
|
||||||
Fedora/Arch sur le splash et le journal de récupération.
|
|
||||||
- Toutes les entrées de boot plafonnent les workers udev bloqués (``udev.event_timeout=45``) ; le menu BIOS démarre
|
|
||||||
explicitement l'entrée FWS (``DEFAULT fws``).
|
|
||||||
- ``systemd-timesyncd`` explicitement activé sur le système installé pour éviter la dérive d'horloge (TLS, pacman).
|
|
||||||
- Use xz compression for the baseline profile's bootstrap tarball so that it has more differences when compared to the
|
- Use xz compression for the baseline profile's bootstrap tarball so that it has more differences when compared to the
|
||||||
releng profile.
|
releng profile.
|
||||||
- Match network interfaces by their type, instead of name, in systemd-networkd configuration.
|
- Match network interfaces by their type, instead of name, in systemd-networkd configuration.
|
||||||
@@ -47,26 +21,12 @@ Deprecated
|
|||||||
Fixed
|
Fixed
|
||||||
-----
|
-----
|
||||||
|
|
||||||
- Watchdog du splash qui affichait son diagnostic PAR-DESSUS l'installateur (il se désarme désormais dès que la GUI
|
|
||||||
d'anaconda est à l'écran, et mesure l'inactivité en itérations — insensible aux sauts d'horloge des VM).
|
|
||||||
- Gel de l'installateur sur machines à faible RAM (1 Go) : zram + avertissement préalable.
|
|
||||||
- Profils ``baseline``/``releng`` synchronisés (la fusion baseline-écrase-releng avait fait perdre ``firewalld``,
|
|
||||||
le cap udev et les entrées de boot FWS d'ISO précédentes).
|
|
||||||
- DNS du système installé (NetworkManager + systemd-resolved), hook ``lvm2`` dans l'initramfs, GRUB de secours
|
|
||||||
``--removable`` en UEFI.
|
|
||||||
- Disposition clavier dérivée de la locale complète et non de la seule langue : ``fr_CH`` donne désormais ``ch(fr)``
|
|
||||||
au lieu de ``fr``, appliqué à la console (``vconsole.conf``) et à X11 (via langtable) ; anaconda écrivait par
|
|
||||||
ailleurs un ``/etc/hostname`` vide sur le système installé.
|
|
||||||
- DNS dans le conteneur de build : ``podman run --network=host`` pour passer par la pile réseau de l'hôte
|
|
||||||
(corrige les timeouts avec firewalld/NAT).
|
|
||||||
- Use ``systemd-run`` to invoke ``curl`` with a transient service that depends on ``network-online.target`` since no
|
- Use ``systemd-run`` to invoke ``curl`` with a transient service that depends on ``network-online.target`` since no
|
||||||
services are currently pulling in ``network_online.target``.
|
services are currently pulling in ``network_online.target``.
|
||||||
|
|
||||||
Removed
|
Removed
|
||||||
-------
|
-------
|
||||||
|
|
||||||
- ``configs/baseline/syslinux/syslinux-linux.cfg`` (label ``arch`` stock, orphelin et sans cap udev).
|
|
||||||
|
|
||||||
[87] - 2025-10-29
|
[87] - 2025-10-29
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
|||||||
+3
-7
@@ -85,12 +85,8 @@ projet (« inbound = outbound », `GPL-3.0-or-later`), avec attestation d'origin
|
|||||||
plus « Arch Linux ») ; `motd` ne renvoie plus vers `archlinux.org`
|
plus « Arch Linux ») ; `motd` ne renvoie plus vers `archlinux.org`
|
||||||
- [ ] **Dépôt rendu publiquement accessible** (sinon l'offre écrite n'est pas
|
- [ ] **Dépôt rendu publiquement accessible** (sinon l'offre écrite n'est pas
|
||||||
opposable) — action hors-code
|
opposable) — action hors-code
|
||||||
- [x] Branding Red Hat/Fedora retiré de l'UI Anaconda : nom de produit **FWS**
|
- [ ] Branding Red Hat/Fedora retiré de l'UI Anaconda ; installateur marqué
|
||||||
via les variables `ANACONDA_PRODUCTNAME`/`ANACONDA_PRODUCTVERSION`
|
« modifié » — à faire pendant la migration (config `product.d`)
|
||||||
(exportées par le `.xinitrc` du live, lues depuis l'os-release FWS —
|
|
||||||
l'en-tête affiche « FWS 0.6 INSTALLATION ») ; visuel **Arch** du menu de
|
|
||||||
boot BIOS remplacé par un splash FWS ; le caractère « version modifiée
|
|
||||||
d'Anaconda » reste documenté ici et dans TRADEMARKS.md
|
|
||||||
- [ ] Décision tranchée sur `python-iso639` (AGPL-3.0-only) : garder ou remplacer
|
- [ ] Décision tranchée sur `python-iso639` (AGPL-3.0-only) : garder ou remplacer
|
||||||
- [ ] Revue par un conseil FOSS (recommandé avant diffusion commerciale)
|
- [ ] Revue par un conseil FOSS (recommandé avant diffusion commerciale)
|
||||||
|
|
||||||
@@ -100,4 +96,4 @@ projet (« inbound = outbound », `GPL-3.0-or-later`), avec attestation d'origin
|
|||||||
> dépôt** (construits depuis l'AUR) : à corriger à la source AUR, pas ici.
|
> dépôt** (construits depuis l'AUR) : à corriger à la source AUR, pas ici.
|
||||||
|
|
||||||
> **Ceci n'est pas un avis juridique.** Base de conformité de bonne foi au
|
> **Ceci n'est pas un avis juridique.** Base de conformité de bonne foi au
|
||||||
> 2026-07-07.
|
> 2026-06-17.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# FWS
|
# FWS
|
||||||
|
|
||||||
**FWS** est une distribution Linux live/installable basée sur **Arch Linux**, pensée pour **remplacer Windows** : bureau prêt à l'emploi, jeux (Steam/Proton-GE), réalité virtuelle (SteamVR), compatibilité applicative (winboat/Wine) et outillage de maintenance intégré. L'installateur graphique est **Anaconda** (celui de Fedora/RHEL), **porté sur Arch** : payload LiveOS (copie du live sur le disque), spokes FWS (choix du bureau, récapitulatif avant installation), finalisation pacman/GRUB/mkinitcpio en `%post`.
|
**FWS** est une distribution Linux live/installable basée sur **Arch Linux**, livrée sous forme d'ISO bootable BIOS/UEFI. L'installateur graphique est **en cours de migration de Calamares vers Anaconda** (Calamares retiré au commit 05ae9c3, intégration d'Anaconda en cours — voir le chantier dédié).
|
||||||
|
|
||||||
Le projet est conçu pour être **construit depuis Windows via WSL** (les scripts détectent Git Bash et basculent automatiquement dans WSL), mais fonctionne aussi directement depuis une **Arch Linux native** ou depuis **toute distribution non-Arch (Fedora, Ubuntu…) via une bascule automatique dans un conteneur Arch podman**.
|
Le projet est conçu pour être **construit depuis Windows via WSL** (les scripts détectent Git Bash et basculent automatiquement dans WSL), mais fonctionne aussi directement depuis une **Arch Linux native** ou depuis **toute distribution non-Arch (Fedora, Ubuntu…) via une bascule automatique dans un conteneur Arch podman**.
|
||||||
|
|
||||||
@@ -9,40 +9,14 @@ Le projet est conçu pour être **construit depuis Windows via WSL** (les script
|
|||||||
## Aperçu
|
## Aperçu
|
||||||
|
|
||||||
- **Base** : Arch Linux (rolling release)
|
- **Base** : Arch Linux (rolling release)
|
||||||
- **Installateur** : **Anaconda 45 porté sur Arch** — payload LiveOS (installe FWS, pas Fedora), page « Récapitulatif avant installation » avec bouton Précédent, choix du bureau intégré, **brandé FWS** (en-tête « FWS 0.6 INSTALLATION », via les variables `ANACONDA_PRODUCT*` exportées par le `.xinitrc`)
|
- **Installateur** : en migration Calamares → Anaconda (Calamares retiré, Anaconda en préparation ; payload `liveimg` pour installer FWS et non Fedora)
|
||||||
- **Affichage live** : Xorg + Openbox + splash de préparation qui montre l'activité réelle d'anaconda (dernier log actif + chrono ; si rien ne bouge avant l'apparition de l'installateur, il le dit puis affiche un diagnostic complet — le watchdog se désarme dès que la GUI est à l'écran)
|
- **Affichage live** : Xorg + Openbox (session graphique conservée pour l'installateur)
|
||||||
- **Mémoire** : **4 Go recommandés** pour installer (2 Go minimum — swap **zram** intégré au live ET au système installé ; sous 2 Go le live avertit, à 1 Go sans zram l'installateur graphique gèle)
|
- **Bureaux installables** : XFCE, KDE Plasma, GNOME, Hyprland (choix à l'installation)
|
||||||
- **Bureaux installables** : GNOME, KDE Plasma, XFCE, **Hyprland** (Wayland — non compatible VR), **i3** (X11 — compatible VR/SteamVR), ou ligne de commande seule
|
- **Boot** : BIOS (syslinux) + UEFI (systemd-boot)
|
||||||
- **Boot** : UEFI (systemd-boot) + BIOS (syslinux, splash **FWS**, `DEFAULT fws`) — entrées *normale*, *safe graphics (nomodeset)*, *Réparer FWS (recovery)* et *speech*
|
|
||||||
- **Récupération** : accessible depuis le live (entrée de boot), depuis le **menu GRUB du système installé** (« Réparer FWS ») et depuis son **menu d'applications** (« Récupération FWS », élévation polkit)
|
|
||||||
- **Compression** : squashfs (xz)
|
- **Compression** : squashfs (xz)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Ce qu'il y a dans FWS
|
|
||||||
|
|
||||||
### À l'installation
|
|
||||||
- **Choix du bureau** dans Anaconda, avec compatibilité VR affichée (i3 = SteamVR ✔ ; Hyprland = ✘)
|
|
||||||
- **Page récapitulative** avant toute écriture disque : config choisie (langue, clavier, fuseau, comptes, partitionnement, bureau, nom de machine) + matériel détecté (GPU, disques, virtualisation, réseau)
|
|
||||||
- **Automatique en fin d'install** : drivers GPU (NVIDIA/AMD/Intel), outils invité VM (VMware/VirtualBox/QEMU/Hyper-V), PipeWire + pavucontrol, Bluetooth (BlueZ + blueman), luminosité (brightnessctl), **pare-feu firewalld activé**, Steam + Wine + Monado + MangoHud + gamemode, clavier/locale, hostname, sudo pour l'utilisateur créé, branding FWS
|
|
||||||
|
|
||||||
### Sur le système installé (outillage `fws-*`)
|
|
||||||
| Commande | Rôle | Accès |
|
|
||||||
|---|---|---|
|
|
||||||
| `fws-hello` | Bienvenue au 1er démarrage (mise à jour, codecs, Proton-GE, VR, raccourcis) | auto au 1er login |
|
|
||||||
| `fws-update` | Mise à jour complète (pacman + cache + orphelins + flatpak) | bouton de la barre |
|
|
||||||
| `fws-keys` | Panneau des **raccourcis clavier réels** (lit la config Hyprland/i3) | `Super+K`, bouton ⌨ |
|
|
||||||
| `fws-powermenu` | Verrouiller / déconnexion / suspendre / redémarrer / éteindre | `Super+X`, bouton |
|
|
||||||
| `fws-about` | « À propos » : le projet FWS + infos système | menu d'applications |
|
|
||||||
| `fws-vr-setup` | Drivers/runtimes VR (Vive/Index → SteamVR natif ; Quest → ALVR ; Monado) | `Super+Shift+V` (i3), menu |
|
|
||||||
| `fws-proton-ge` | Installe/actualise Proton-GE pour Steam (vérif sha512) | fws-hello, terminal |
|
|
||||||
| `fws-recovery` | **Application GTK de réparation** : GRUB, initramfs, pacman, terminal chroot (moteur bash + repli console) | menu « Récupération FWS » de l'OS installé, entrée GRUB « Réparer FWS », entrée de boot du live |
|
|
||||||
|
|
||||||
### Hyprland & i3 : parité visuelle
|
|
||||||
Même identité Catppuccin Mocha (nuances de bleu) sur les deux : barre flottante arrondie (**waybar** / **polybar**) avec date+heure, indicateur de mises à jour, volume/réseau/CPU/RAM/luminosité/Bluetooth cliquables ; menus assortis (**wofi** / **rofi**) ; mêmes raccourcis (déclarés avec descriptions → `Super+K`) ; **Dolphin** en gestionnaire de fichiers ; thème **SDDM « fws »** maison (pur QtQuick, accent adapté au bureau ; GNOME garde GDM avec bannière FWS).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Environnement requis — INSTALLATION ULTRA PRÉCISE
|
## Environnement requis — INSTALLATION ULTRA PRÉCISE
|
||||||
|
|
||||||
### Côté Windows (machine hôte)
|
### Côté Windows (machine hôte)
|
||||||
@@ -172,9 +146,9 @@ Détails utiles :
|
|||||||
Depuis **Git Bash sur Windows** (à la racine du repo) :
|
Depuis **Git Bash sur Windows** (à la racine du repo) :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Étape 1 : compilation des paquets AUR (winboat) + PKGBUILD locaux
|
# Étape 1 : compilation des paquets AUR (winboat) + deps locales
|
||||||
# (anaconda porté sur Arch + ses dépendances python)
|
|
||||||
# → crée local-repo/ avec les .pkg.tar.zst
|
# → crée local-repo/ avec les .pkg.tar.zst
|
||||||
|
# (Calamares retiré ; l'installateur Anaconda est un chantier en cours)
|
||||||
./setup-aur.sh
|
./setup-aur.sh
|
||||||
|
|
||||||
# Étape 2 : build de l'ISO via mkarchiso
|
# Étape 2 : build de l'ISO via mkarchiso
|
||||||
@@ -196,11 +170,11 @@ L'ISO finale est générée dans `./out/FWS-YYYY.MM.DD-x86_64.iso`.
|
|||||||
4. **`pacman -Syu`** complet (obligatoire — sinon `makepkg` plante sur les "conflicting files" lors des splits de paquets type `gcc-libs`).
|
4. **`pacman -Syu`** complet (obligatoire — sinon `makepkg` plante sur les "conflicting files" lors des splits de paquets type `gcc-libs`).
|
||||||
5. **Install des outils de build** : `base-devel git`.
|
5. **Install des outils de build** : `base-devel git`.
|
||||||
6. **Création d'un utilisateur `fwsbuild`** non-root avec `sudo` NOPASSWD (makepkg refuse de tourner en root).
|
6. **Création d'un utilisateur `fwsbuild`** non-root avec `sudo` NOPASSWD (makepkg refuse de tourner en root).
|
||||||
7. **Clone + build AUR** : `winboat-bin` via `makepkg -s --noconfirm`, plus les PKGBUILD locaux de `pkgbuilds/` (anaconda, anaconda-widgets, pykickstart, blivet, python-meh patché…). Le build est **incrémental** : un paquet déjà présent dans `local-repo/` à la bonne version est sauté.
|
7. **Clone + build AUR** : `winboat-bin` via `makepkg -s --noconfirm`, plus les PKGBUILD locaux de `pkgbuilds/` (deps Anaconda). Calamares n'est plus compilé.
|
||||||
8. **Création du repo local** : `repo-add local-repo/fws-local.db.tar.gz`.
|
8. **Création du repo local** : `repo-add local-repo/fws-local.db.tar.gz`.
|
||||||
9. **Cleanup** : suppression de l'utilisateur `fwsbuild`.
|
9. **Cleanup** : suppression de l'utilisateur `fwsbuild`.
|
||||||
|
|
||||||
**Durée** : quelques minutes en incrémental ; le premier build complet (anaconda + deps) est plus long.
|
**Durée** : quelques minutes (winboat-bin + deps locales ; Calamares et ses ~12 dépendances Qt6 ne sont plus compilés).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -246,12 +220,11 @@ L'ISO finale est générée dans `./out/FWS-YYYY.MM.DD-x86_64.iso`.
|
|||||||
|
|
||||||
## Séquence de boot de l'ISO
|
## Séquence de boot de l'ISO
|
||||||
|
|
||||||
1. Menu de boot : **installation** (défaut), **safe graphics** (`nomodeset`), **Réparer FWS** (`fws.recovery=1` → app GTK `fws-recovery-gui` via openbox, repli console `fws-recovery`), **speech**. Toutes les entrées plafonnent les workers udev bloqués (`udev.event_timeout=45`)
|
1. Démarrage de l'ISO FWS (BIOS ou UEFI)
|
||||||
2. `systemd` → autologin root sur `tty1`
|
2. `systemd` → autologin root sur `tty1`
|
||||||
3. `.zprofile` → mode recovery si demandé, sinon `startx`
|
3. `.zprofile` → `startx`
|
||||||
4. `.xinitrc` → Openbox + **garde-fou RAM** (avertissement sous 2 Go) + **splash « Préparation de l'installateur »** (label alimenté en direct par les logs anaconda ; à 4 min sans activité → diagnostic auto `/tmp/fws-stall.txt` ; le tout se désarme dès que la GUI d'anaconda apparaît), bind mount `/run/rootfsbase` (source du payload LiveOS), patch du hub Anaconda, **exports `ANACONDA_PRODUCT*`** (branding FWS), puis `dbus-run-session liveinst`
|
4. `.xinitrc` → Openbox (session graphique du live)
|
||||||
5. **Anaconda** : accueil → hub (langue, clavier, disque, utilisateur, **bureau FWS**) → « Commencer l'installation » → **page Récapitulatif** (bouton Précédent / Lancer l'installation) → installation → écran de réussite → extinction
|
5. **[Chantier C]** lancement de l'installateur Anaconda — pas encore câblé (Calamares retiré au commit 05ae9c3)
|
||||||
6. En cas d'échec : écran de debug avec tous les logs (liveinst, %post chrooté, anaconda)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -260,18 +233,8 @@ L'ISO finale est générée dans `./out/FWS-YYYY.MM.DD-x86_64.iso`.
|
|||||||
- **Paquets du live** : `configs/releng/packages.x86_64`
|
- **Paquets du live** : `configs/releng/packages.x86_64`
|
||||||
- **Paquets baseline** : `configs/baseline/packages.x86_64`
|
- **Paquets baseline** : `configs/baseline/packages.x86_64`
|
||||||
- **Overlay rootfs** : `configs/releng/airootfs/` ou `configs/baseline/airootfs/`
|
- **Overlay rootfs** : `configs/releng/airootfs/` ou `configs/baseline/airootfs/`
|
||||||
- **Kickstart de l'installateur** : `configs/releng/airootfs/usr/share/anaconda/interactive-defaults.ks` (payload LiveOS via `--liveinst` + tout le `%post` : bureaux, drivers, pare-feu, branding)
|
- **Kickstart de l'installateur** : `configs/releng/airootfs/usr/share/anaconda/interactive-defaults.ks` (payload liveimg + %post)
|
||||||
- **Spokes Anaconda FWS** : `configs/baseline/airootfs/usr/lib/python3.14/site-packages/pyanaconda/ui/gui/spokes/` (`fws_desktop.py`, `fws_recap.py`) + glades dans `usr/share/anaconda/ui/spokes/`
|
- **Branding ISO** : `configs/releng/profiledef.sh` (`iso_name`, `iso_label`, `iso_publisher`)
|
||||||
- **Configs bureaux & thèmes** : `configs/releng/airootfs/usr/local/share/fws/` (hyprland, i3, waybar, polybar, wofi, rofi, picom) et `usr/share/sddm/themes/fws/`
|
|
||||||
- **Outils fws-\*** : `configs/releng/airootfs/usr/local/bin/`
|
|
||||||
- **Récupération sur l'OS installé** : entrée GRUB générée par `configs/releng/airootfs/etc/grub.d/42_fws-recovery` → `fws-recovery.target` + `fws-recovery-boot.service` (`etc/systemd/system/`) ; lanceur `usr/local/share/applications/fws-recovery.desktop` + politique polkit `usr/share/polkit-1/actions/org.fws.recovery.policy`
|
|
||||||
- **Swap zram** (live + installé) : `configs/releng/airootfs/etc/systemd/zram-generator.conf` (paquet `zram-generator`)
|
|
||||||
- **Branding ISO** : `configs/releng/profiledef.sh` (`iso_name`, `iso_label`, `iso_publisher`) ; **visuel du menu BIOS** : `configs/releng/syslinux/splash.png` (PNG 8-bit) ; **nom/version affichés par l'installateur** : exports `ANACONDA_PRODUCTNAME`/`ANACONDA_PRODUCTVERSION` dans le `.xinitrc` (lus depuis l'os-release du live)
|
|
||||||
|
|
||||||
> ⚠ **Règle d'or de la fusion** : au build, `configs/baseline/*` est copié **par-dessus** `configs/releng/*` — tout fichier homonyme de baseline **écrase** celui de releng. Ça concerne notamment `profiledef.sh`, `packages.x86_64`, `pacman.conf`, `efiboot/loader/entries/01-archiso-linux.conf` et `syslinux/syslinux.cfg` : **toute modification d'un de ces fichiers doit être reportée dans les deux profils** (des divergences ont déjà fait perdre silencieusement `firewalld` et le cap `udev.event_timeout` d'une ISO ; un `syslinux-linux.cfg` stock orphelin côté baseline définissait aussi un label `arch` sans le cap — supprimé, et le menu BIOS pointe désormais explicitement sur `DEFAULT fws`). Vérifier avec :
|
|
||||||
> ```bash
|
|
||||||
> comm -12 <(cd configs/baseline && find . -type f | sort) <(cd configs/releng && find . -type f | sort)
|
|
||||||
> ```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -284,20 +247,7 @@ L'ISO finale est générée dans `./out/FWS-YYYY.MM.DD-x86_64.iso`.
|
|||||||
| `pacman: signature unknown trust` | Keyring périmé → `pacman -Sy --noconfirm archlinux-keyring` puis relancer. |
|
| `pacman: signature unknown trust` | Keyring périmé → `pacman -Sy --noconfirm archlinux-keyring` puis relancer. |
|
||||||
| `makepkg: conflicting files` | `pacman -Syu` incomplet avant le build. Relancer `setup-aur.sh`. |
|
| `makepkg: conflicting files` | `pacman -Syu` incomplet avant le build. Relancer `setup-aur.sh`. |
|
||||||
| `mkarchiso` plante sur `[fws-local]` | Le repo local n'a pas été généré. Relancer `setup-aur.sh`. |
|
| `mkarchiso` plante sur `[fws-local]` | Le repo local n'a pas été généré. Relancer `setup-aur.sh`. |
|
||||||
| Écran bleu 1-2 min au boot du live | Normal en VM : un worker udev traîne (plafonné à 45 s) — le splash « Préparation de l'installateur » l'indique. Attendre. |
|
| Écran noir après login Hyprland | Tu testes dans VirtualBox → pas d'accélération GPU. Utiliser QEMU/KVM avec `virtio-gpu` ou bare-metal. |
|
||||||
| L'installateur semble figé / fenêtre blanche | Rendu logiciel en VM : la fenêtre Anaconda se peint lentement. Patienter quelques secondes. |
|
|
||||||
| Session Hyprland/i3 : fenêtres au contenu noir en VM | Compositing sur GPU virtuel — picom est auto-désactivé en VM (config FWS) ; si tu l'as réactivé, `pkill picom`. |
|
|
||||||
| Hyprland crash au lancement en VM | Géré : le wrapper `fws-hyprland` force le rendu logiciel (`kms_swrast`/llvmpipe) sous hyperviseur. Sur vrai GPU, rendu matériel normal. |
|
|
||||||
| Steam plante à l'install de SteamVR | En VM : `steam -cef-disable-gpu -cef-disable-gpu-compositing` (et la VR ne peut PAS marcher en VM — GPU/USB inaccessibles). |
|
|
||||||
| Icône Bluetooth absente / blueman en erreur | Pas d'adaptateur Bluetooth (cas VM) : le module de barre se cache et l'applet ne se lance pas — comportement voulu. |
|
|
||||||
| Le système installé ne boote plus | Menu GRUB → « **Réparer FWS (récupération du système)** » (boote l'initramfs fallback + l'app de réparation). Si GRUB lui-même est mort : booter l'ISO → entrée « **Réparer FWS (recovery)** ». |
|
|
||||||
| Réparer depuis le système installé (il boote encore) | Menu d'applications → « **Récupération FWS** » (mot de passe admin demandé une fois — polkit). |
|
|
||||||
| Réinstallation par-dessus une install existante | Le kickstart nettoie tout (`zerombr` + `clearpart --all`), mais en cas de comportement étrange au 2ᵉ passage, effacer/recréer le disque virtuel. |
|
|
||||||
| Boot BIOS | Fonctionne (résolu) : syslinux est sous `/boot/syslinux` de l'ISO (pas `/syslinux`), El-Torito BIOS + UEFI présents ; install BIOS complète validée en VM. Le menu BIOS démarre l'entrée FWS par défaut (`DEFAULT fws`). |
|
|
||||||
| Splash « Préparation de l'installateur » figé | **RAM insuffisante** (reproduit : gel à 1 Go, OK à 2 Go, aucun swap sur le live avant zram). En VM : allouer **4 Go** (le wizard VMware « Other 64-bit » n'en met que 1 !). Le live avertit désormais sous 2 Go, embarque un swap zram, et le splash affiche l'activité réelle d'anaconda (+ diagnostic auto si les logs ne bougent plus). |
|
|
||||||
| Dialogue « préparation bloquée : diagnostic » PAR-DESSUS l'installateur | Bug corrigé (ISO ≤ 2026-07-07 01:04) : le watchdog du splash se déclenchait alors qu'anaconda était à l'écran (logs silencieux = normal quand il attend l'utilisateur). Il se désarme désormais dès l'apparition de la GUI ; ferme le dialogue et continue, ou rebuild. |
|
|
||||||
| « Possibly missing firmware for module … » pendant une réparation Recovery | Avertissements `mkinitcpio` **normaux** : firmware de matériel exotique (SCSI/fibre channel…) absent de `linux-firmware`. Sans effet, sauf si tu possèdes ce matériel précis. Le journal Recovery l'explique désormais. |
|
|
||||||
| Recovery : « tout est grisé » | Corrigé : seul le sélecteur était désactivé (une seule installation détectée) mais toute l'app semblait morte ; il reste actif désormais. Les boutons d'action, eux, ont toujours été fonctionnels. |
|
|
||||||
| Retours à la ligne CRLF | `build.sh` lance `dos2unix` automatiquement, mais si tu modifies pendant le build, relancer. |
|
| Retours à la ligne CRLF | `build.sh` lance `dos2unix` automatiquement, mais si tu modifies pendant le build, relancer. |
|
||||||
|
|
||||||
### Logs utiles
|
### Logs utiles
|
||||||
@@ -306,16 +256,11 @@ L'ISO finale est générée dans `./out/FWS-YYYY.MM.DD-x86_64.iso`.
|
|||||||
# Pendant le build
|
# Pendant le build
|
||||||
tail -f /tmp/fws-build/work/iso/arch/pkglist.x86_64.txt
|
tail -f /tmp/fws-build/work/iso/arch/pkglist.x86_64.txt
|
||||||
|
|
||||||
# Dans le LIVE (Ctrl+Alt+F2, login root) — pendant/après l'installation
|
# Dans l'ISO (autologin tty1)
|
||||||
journalctl -b # système
|
journalctl -b
|
||||||
cat /tmp/fws-liveinst.log # sortie complète de liveinst/anaconda
|
|
||||||
cat /tmp/anaconda.log # log principal anaconda
|
|
||||||
cat /tmp/fws-post-chroot.log # %post chrooté = grub-install/mkinitcpio
|
|
||||||
# En cas d'échec, l'écran de debug du live affiche déjà tout ça (zenity).
|
|
||||||
|
|
||||||
# Sur le système installé
|
# Hyprland post-install
|
||||||
cat /var/log/fws-post.log # trace complète du %post d'installation
|
cat ~/.local/share/hyprland/hyprland.log
|
||||||
journalctl -b # premier démarrage
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+4
-26
@@ -47,24 +47,12 @@ Le système live héritait de l'`os-release` du paquet `filesystem` d'Arch et
|
|||||||
donc sans conflit, et est retiré du système final par
|
donc sans conflit, et est retiré du système final par
|
||||||
`zzzz99-remove-custom-hooks-from-airootfs.hook`.
|
`zzzz99-remove-custom-hooks-from-airootfs.hook`.
|
||||||
- **`motd`** : ne référence plus `archlinux.org` ; pointe vers le dépôt FWS.
|
- **`motd`** : ne référence plus `archlinux.org` ; pointe vers le dépôt FWS.
|
||||||
- **Menu de boot BIOS (2026-07-07)** : l'image de fond `syslinux/splash.png`
|
|
||||||
héritée d'archiso affichait le **logo Arch Linux plein écran** — remplacée
|
|
||||||
par un visuel FWS original (générique, sans logo tiers).
|
|
||||||
- **Bannière de login (`/etc/issue`)** : `\S (\l)` — affiche le `PRETTY_NAME`
|
|
||||||
FWS, sans la version du noyau (dont le suffixe `-archN`).
|
|
||||||
- **Textes utilisateur** : `fws-about` dit « distribution Linux » ; le splash
|
|
||||||
de préparation et le journal de l'app Récupération remplacent à l'affichage
|
|
||||||
les mentions « Arch Linux »/« Fedora » issues des logs d'outils tiers (les
|
|
||||||
identifiants techniques réels — `archlinux-keyring`, miroirs — restent
|
|
||||||
intacts : usage nominatif fonctionnel).
|
|
||||||
|
|
||||||
> Note technique : `ID=fws` + `ID_LIKE=arch` préserve la détection « famille
|
> Note technique : `ID=fws` + `ID_LIKE=arch` préserve la détection « famille
|
||||||
> Arch » des outils tiers (et satisfait les checks `^ID(_LIKE)?=.*arch` de
|
> Arch » des outils tiers (et satisfait les checks `^ID(_LIKE)?=.*arch` de
|
||||||
> `build.sh`/`setup-aur.sh`, qui portent de toute façon sur l'hôte de build).
|
> `build.sh`/`setup-aur.sh`, qui portent de toute façon sur l'hôte de build).
|
||||||
> Si la migration Anaconda révèle un composant testant `ID=arch` en dur, basculer
|
> Si la migration Anaconda révèle un composant testant `ID=arch` en dur, basculer
|
||||||
> transitoirement le hook sur `ID=arch` tout en gardant `NAME`/`PRETTY_NAME` FWS.
|
> transitoirement le hook sur `ID=arch` tout en gardant `NAME`/`PRETTY_NAME` FWS.
|
||||||
> Le suffixe `-archN` de `uname -r` vient du binaire du noyau Arch : usage
|
|
||||||
> nominatif inévitable sans recompiler le kernel.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -72,7 +60,7 @@ Le système live héritait de l'`os-release` du paquet `filesystem` d'Arch et
|
|||||||
|
|
||||||
> **Red Hat®** et **Fedora®** sont des marques déposées de **Red Hat, Inc.**
|
> **Red Hat®** et **Fedora®** sont des marques déposées de **Red Hat, Inc.**
|
||||||
> L'installateur **Anaconda** est développé par Red Hat. FWS distribue une
|
> L'installateur **Anaconda** est développé par Red Hat. FWS distribue une
|
||||||
> **version MODIFIÉE** d'Anaconda (portée sur Arch, payload LiveOS), **non
|
> **version MODIFIÉE** d'Anaconda (portée sur Arch, payload `liveimg`), **non
|
||||||
> officielle**, **non fournie ni approuvée** par Red Hat ou le projet Fedora.
|
> officielle**, **non fournie ni approuvée** par Red Hat ou le projet Fedora.
|
||||||
> *« This product includes a modified version of Anaconda. Red Hat and Fedora
|
> *« This product includes a modified version of Anaconda. Red Hat and Fedora
|
||||||
> are trademarks of Red Hat, Inc. This is an unofficial build, not provided or
|
> are trademarks of Red Hat, Inc. This is an unofficial build, not provided or
|
||||||
@@ -86,19 +74,9 @@ Engagements FWS :
|
|||||||
passer une version modifiée pour officielle) ;
|
passer une version modifiée pour officielle) ;
|
||||||
- **recommandation béton :** retirer tout **logo / nom de produit Red Hat ou
|
- **recommandation béton :** retirer tout **logo / nom de produit Red Hat ou
|
||||||
Fedora** de l'interface de l'installateur si présent, et y afficher
|
Fedora** de l'interface de l'installateur si présent, et y afficher
|
||||||
« Installateur FWS (basé sur Anaconda) ».
|
« Installateur FWS (basé sur Anaconda) ». La configuration produit d'Anaconda
|
||||||
|
se règle via `product.d` / les données de branding — à auditer pendant la
|
||||||
### ✅ Remédiation appliquée (2026-07-07 — l'installateur s'annonce « FWS »)
|
migration.
|
||||||
|
|
||||||
Sans `/.buildstamp`, Anaconda s'annonçait « ANACONDA BLUESKY INSTALLATION » et
|
|
||||||
ses logs parlent de Fedora. `pyanaconda/core/product.py` donne priorité aux
|
|
||||||
variables d'environnement : le `.xinitrc` du live exporte
|
|
||||||
`ANACONDA_PRODUCTNAME`/`ANACONDA_PRODUCTVERSION` (depuis l'os-release FWS) et
|
|
||||||
`ANACONDA_ISFINAL=true` → l'en-tête et l'accueil affichent **« FWS 0.6 »**. Le
|
|
||||||
splash de préparation filtre en outre les mentions Fedora des lignes de log
|
|
||||||
affichées. Les mentions profondes dans les **logs techniques** (fichiers
|
|
||||||
`/tmp/*.log`, écrans de debug) sont conservées volontairement : identification
|
|
||||||
loyale du composant amont (version modifiée d'Anaconda), utile au diagnostic.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+30
-116
@@ -2,13 +2,10 @@
|
|||||||
# ============================================================
|
# ============================================================
|
||||||
# FWS — Build OFFICIEL versionné de l'ISO (archiso)
|
# FWS — Build OFFICIEL versionné de l'ISO (archiso)
|
||||||
#
|
#
|
||||||
# IDENTIQUE à build.sh (même bascule conteneur/WSL, --network=host, fusion
|
# Identique à build.sh dans sa structure, mais :
|
||||||
# baseline, MÊME injection local-repo → donc même paquet anaconda avec le champ
|
# - lit la version dans le fichier VERSION (format Major.Minor.Build)
|
||||||
# Hostname), avec EN PLUS uniquement le RENOMMAGE :
|
# - nomme l'ISO FWS-vMAJOR-MINOR-BUILD-DATE-x86_64.iso
|
||||||
# - demande la version (major.minor.build) + le nom de variante, le fichier
|
# - range l'ISO dans out/vMAJOR/vMAJOR.MINOR/
|
||||||
# VERSION servant de défaut,
|
|
||||||
# - nomme l'ISO fws-<version>-<nom>.iso (ex. fws-1.5.0-cli.iso),
|
|
||||||
# - range l'ISO dans out/vMAJOR/vMAJOR.MINOR/.
|
|
||||||
#
|
#
|
||||||
# Fonctionne sur :
|
# Fonctionne sur :
|
||||||
# - Windows 10 / 11 (Git Bash → bascule auto dans WSL Arch)
|
# - Windows 10 / 11 (Git Bash → bascule auto dans WSL Arch)
|
||||||
@@ -124,37 +121,6 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# La version + le nom sont DEMANDÉS sur l'hôte (avant le conteneur) et passés
|
|
||||||
# via -e, pour que le prompt s'affiche au bon endroit (terminal de l'hôte).
|
|
||||||
# (Définis FWS_ISO_VERSION / FWS_ISO_TAG ; lus plus bas, prompt sauté si déjà
|
|
||||||
# fournis — c.-à-d. dans le conteneur.)
|
|
||||||
ask_iso_meta_host() {
|
|
||||||
[ -n "$FWS_ISO_VERSION" ] && [ -n "$FWS_ISO_TAG" ] && return 0
|
|
||||||
local def=""
|
|
||||||
if [ -f "$(dirname "${BASH_SOURCE[0]}")/VERSION" ]; then
|
|
||||||
def="$(tr -d ' \t\r\n' < "$(dirname "${BASH_SOURCE[0]}")/VERSION")"
|
|
||||||
[[ "$def" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || def=""
|
|
||||||
fi
|
|
||||||
while :; do
|
|
||||||
if [ -n "$def" ]; then
|
|
||||||
read -rp "==> Version (major.minor.build) [Entrée = $def] : " FWS_ISO_VERSION || exit 1
|
|
||||||
[ -n "$FWS_ISO_VERSION" ] || FWS_ISO_VERSION="$def"
|
|
||||||
else
|
|
||||||
read -rp "==> Version (major.minor.build, ex. 1.5.0) : " FWS_ISO_VERSION || exit 1
|
|
||||||
fi
|
|
||||||
[[ "$FWS_ISO_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && break
|
|
||||||
echo -e "\e[31m Format attendu : major.minor.build (ex. 1.5.0)\e[0m" >&2
|
|
||||||
done
|
|
||||||
while :; do
|
|
||||||
read -rp "==> Nom de la variante (ex. cli, gnome, kde) : " FWS_ISO_TAG || exit 1
|
|
||||||
FWS_ISO_TAG="$(printf '%s' "$FWS_ISO_TAG" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9-')"
|
|
||||||
[ -n "$FWS_ISO_TAG" ] && break
|
|
||||||
echo -e "\e[31m Nom requis (lettres, chiffres, tirets)\e[0m" >&2
|
|
||||||
done
|
|
||||||
export FWS_ISO_VERSION FWS_ISO_TAG
|
|
||||||
}
|
|
||||||
ask_iso_meta_host
|
|
||||||
|
|
||||||
# Rootful : préfixe sudo si on n'est pas déjà root.
|
# Rootful : préfixe sudo si on n'est pas déjà root.
|
||||||
SUDO=(); [ "$EUID" -eq 0 ] || SUDO=(sudo)
|
SUDO=(); [ "$EUID" -eq 0 ] || SUDO=(sudo)
|
||||||
# -it seulement si on est dans un terminal (sinon podman casse en CI).
|
# -it seulement si on est dans un terminal (sinon podman casse en CI).
|
||||||
@@ -172,14 +138,8 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
|
|||||||
"${SUDO[@]}" podman pull "$IMAGE"
|
"${SUDO[@]}" podman pull "$IMAGE"
|
||||||
|
|
||||||
echo "==> Relance dans le conteneur ($SELF)..."
|
echo "==> Relance dans le conteneur ($SELF)..."
|
||||||
# --network=host : le réseau bridge de podman ne route plus vers l'extérieur
|
"${SUDO[@]}" podman run --rm "${TTY[@]}" --privileged \
|
||||||
# sur ce wifi (firewalld/NAT cassé après changement de réseau → « core.db :
|
|
||||||
# Resolving timed out », même avec --dns). En partageant la pile réseau de
|
|
||||||
# l'hôte (qui, lui, a internet + DNS via 127.0.0.53/systemd-resolved joignable
|
|
||||||
# dans son propre netns), le conteneur récupère une connectivité identique.
|
|
||||||
"${SUDO[@]}" podman run --rm "${TTY[@]}" --network=host --privileged \
|
|
||||||
-e FWS_IN_CONTAINER=1 \
|
-e FWS_IN_CONTAINER=1 \
|
||||||
-e FWS_ISO_VERSION -e FWS_ISO_TAG \
|
|
||||||
-v "$REPO_DIR":"$REPO_DIR" \
|
-v "$REPO_DIR":"$REPO_DIR" \
|
||||||
-v fws-pacman-cache:/var/cache/pacman/pkg \
|
-v fws-pacman-cache:/var/cache/pacman/pkg \
|
||||||
-w "$REPO_DIR" \
|
-w "$REPO_DIR" \
|
||||||
@@ -208,8 +168,7 @@ command -v pacman >/dev/null \
|
|||||||
# $0 est cité pour gérer les chemins à espaces (ex. "1. FWS").
|
# $0 est cité pour gérer les chemins à espaces (ex. "1. FWS").
|
||||||
if [ "$EUID" -ne 0 ]; then
|
if [ "$EUID" -ne 0 ]; then
|
||||||
if command -v sudo >/dev/null 2>&1; then
|
if command -v sudo >/dev/null 2>&1; then
|
||||||
# -E : conserve FWS_ISO_VERSION / FWS_ISO_TAG (demandés avant) à travers sudo.
|
exec sudo bash "$0" "$@"
|
||||||
exec sudo -E bash "$0" "$@"
|
|
||||||
else
|
else
|
||||||
exec su -c "bash \"$0\"" root
|
exec su -c "bash \"$0\"" root
|
||||||
fi
|
fi
|
||||||
@@ -229,48 +188,29 @@ WORK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
echo "==> Dossier de travail : $WORK_DIR"
|
echo "==> Dossier de travail : $WORK_DIR"
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Version (major.minor.build) + nom de la variante.
|
# Lecture et validation de la version (fichier VERSION)
|
||||||
# Déjà demandés sur l'hôte (FWS_ISO_VERSION/FWS_ISO_TAG passés via -e) ; sinon
|
|
||||||
# (exécution native Arch / WSL directe) on les demande ici. Le fichier VERSION
|
|
||||||
# sert de défaut (Entrée = le reprendre). Produit fws-<version>-<nom>.iso.
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
VERSION="$FWS_ISO_VERSION"
|
VERSION_FILE="$WORK_DIR/VERSION"
|
||||||
ISO_TAG="$FWS_ISO_TAG"
|
[ -f "$VERSION_FILE" ] \
|
||||||
VERSION_DEFAULT=""
|
|| { echo -e "\e[31mFichier VERSION introuvable : $VERSION_FILE\e[0m"; exit 1; }
|
||||||
if [ -f "$WORK_DIR/VERSION" ]; then
|
|
||||||
VERSION_DEFAULT="$(tr -d ' \t\r\n' < "$WORK_DIR/VERSION")"
|
# Trim espaces / tabulations / CR / LF
|
||||||
[[ "$VERSION_DEFAULT" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || VERSION_DEFAULT=""
|
VERSION="$(tr -d ' \t\r\n' < "$VERSION_FILE")"
|
||||||
|
|
||||||
|
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo -e "\e[31mVERSION invalide : '$VERSION' (attendu Major.Minor.Build, ex. 1.2.3)\e[0m"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
while [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; do
|
|
||||||
if [ -n "$VERSION_DEFAULT" ]; then
|
|
||||||
read -rp "==> Version (major.minor.build) [Entrée = $VERSION_DEFAULT] : " VERSION \
|
|
||||||
|| { echo -e "\e[31mEntrée requise — lance le script en interactif.\e[0m" >&2; exit 1; }
|
|
||||||
[ -n "$VERSION" ] || VERSION="$VERSION_DEFAULT"
|
|
||||||
else
|
|
||||||
read -rp "==> Version (major.minor.build, ex. 1.5.0) : " VERSION \
|
|
||||||
|| { echo -e "\e[31mEntrée requise — lance le script en interactif.\e[0m" >&2; exit 1; }
|
|
||||||
fi
|
|
||||||
[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] \
|
|
||||||
|| echo -e "\e[31m Format attendu : major.minor.build (ex. 1.5.0)\e[0m" >&2
|
|
||||||
done
|
|
||||||
while [ -z "$ISO_TAG" ]; do
|
|
||||||
read -rp "==> Nom de la variante (ex. cli, gnome, kde) : " ISO_TAG \
|
|
||||||
|| { echo -e "\e[31mEntrée requise — lance le script en interactif.\e[0m" >&2; exit 1; }
|
|
||||||
ISO_TAG="$(printf '%s' "$ISO_TAG" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9-')"
|
|
||||||
[ -n "$ISO_TAG" ] || echo -e "\e[31m Nom requis (lettres, chiffres, tirets)\e[0m" >&2
|
|
||||||
done
|
|
||||||
|
|
||||||
IFS='.' read -r MAJOR MINOR BUILD <<< "$VERSION"
|
IFS='.' read -r MAJOR MINOR BUILD <<< "$VERSION"
|
||||||
BUILD_DATE="$(date +%Y.%m.%d)"
|
BUILD_DATE="$(date +%Y.%m.%d)"
|
||||||
# iso_version interne (passé à mkarchiso) ; l'ISO finale est renommée plus bas.
|
ISO_VERSION="v${MAJOR}-${MINOR}-${BUILD}-${BUILD_DATE}"
|
||||||
ISO_VERSION="${VERSION}-${ISO_TAG}"
|
|
||||||
OUT_DIR="$WORK_DIR/out/v${MAJOR}/v${MAJOR}.${MINOR}"
|
OUT_DIR="$WORK_DIR/out/v${MAJOR}/v${MAJOR}.${MINOR}"
|
||||||
# Date ajoutée À LA FIN du nom, avant .iso → fws-<version>-<nom>-<AAAA.MM.JJ>.iso
|
ISO_FILE="$OUT_DIR/FWS-${ISO_VERSION}-x86_64.iso"
|
||||||
ISO_FILE="$OUT_DIR/fws-${VERSION}-${ISO_TAG}-${BUILD_DATE}.iso"
|
|
||||||
|
|
||||||
echo "==> Version : ${VERSION} · variante : ${ISO_TAG} · date : ${BUILD_DATE}"
|
echo "==> Version officielle : ${MAJOR}.${MINOR}.${BUILD}"
|
||||||
echo "==> ISO finale : fws-${VERSION}-${ISO_TAG}-${BUILD_DATE}.iso"
|
echo "==> Dossier de sortie : $OUT_DIR"
|
||||||
echo "==> Dossier : $OUT_DIR"
|
echo "==> ISO finale : FWS-${ISO_VERSION}-x86_64.iso"
|
||||||
|
|
||||||
# pacman ≥ 7 sandboxe les ops FS via landlock, non supporté par le kernel
|
# pacman ≥ 7 sandboxe les ops FS via landlock, non supporté par le kernel
|
||||||
# WSL ni (selon le noyau) par le conteneur → désactivation. Idempotent,
|
# WSL ni (selon le noyau) par le conteneur → désactivation. Idempotent,
|
||||||
@@ -309,8 +249,8 @@ if [ -d "$WORK_DIR/configs/baseline" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Injection de la version dans profiledef.sh (copie temporaire) ──
|
# ── Injection de la version dans profiledef.sh (copie temporaire) ──
|
||||||
# mkarchiso nomme l'ISO ${iso_name}-${iso_version}-${arch}.iso ; on force
|
# mkarchiso nomme l'ISO ${iso_name}-${iso_version}-${arch}.iso.
|
||||||
# iso_version pour obtenir le nom voulu (l'ISO est renommée fws-… plus bas).
|
# On force iso_version pour obtenir vMAJOR-MINOR-BUILD-DATE.
|
||||||
if [ -f /tmp/fws-build/releng/profiledef.sh ]; then
|
if [ -f /tmp/fws-build/releng/profiledef.sh ]; then
|
||||||
sed -i "s|^iso_version=.*|iso_version=\"${ISO_VERSION}\"|" \
|
sed -i "s|^iso_version=.*|iso_version=\"${ISO_VERSION}\"|" \
|
||||||
/tmp/fws-build/releng/profiledef.sh
|
/tmp/fws-build/releng/profiledef.sh
|
||||||
@@ -353,39 +293,13 @@ echo "==> Correction des retours à la ligne Windows (dos2unix)..."
|
|||||||
pacman -S --needed --noconfirm dos2unix
|
pacman -S --needed --noconfirm dos2unix
|
||||||
find /tmp/fws-build/releng -type f -exec dos2unix {} + 2>/dev/null
|
find /tmp/fws-build/releng -type f -exec dos2unix {} + 2>/dev/null
|
||||||
|
|
||||||
# ── Pré-vol : résolution de TOUS les paquets du profil ──
|
|
||||||
# « error: target not found » ne doit plus surgir en plein mkarchiso : on
|
|
||||||
# résout la liste complète (packages.x86_64 fusionné) contre le pacman.conf
|
|
||||||
# du profil (fws-local injecté inclus) dans une base temporaire. S'il manque
|
|
||||||
# un paquet, on échoue ICI avec la liste + le geste correctif.
|
|
||||||
echo "==> Pré-vol : résolution des paquets du profil..."
|
|
||||||
PREFLIGHT_DB="$(mktemp -d)"
|
|
||||||
pacman -Sy --config /tmp/fws-build/releng/pacman.conf --dbpath "$PREFLIGHT_DB" >/dev/null
|
|
||||||
mapfile -t PROFILE_PKGS < <(sed -e 's/#.*//' -e 's/[[:space:]]//g' -e '/^$/d' \
|
|
||||||
/tmp/fws-build/releng/packages.x86_64)
|
|
||||||
# -Sp n'installe rien (affiche les URLs) ; LC_ALL=C fige le motif d'erreur.
|
|
||||||
# stderr → capture, stdout → poubelle ; le pipeline sed sort toujours 0.
|
|
||||||
MISSING="$(LC_ALL=C pacman -Sp --config /tmp/fws-build/releng/pacman.conf \
|
|
||||||
--dbpath "$PREFLIGHT_DB" "${PROFILE_PKGS[@]}" 2>&1 >/dev/null \
|
|
||||||
| sed -n 's/^error: target not found:[[:space:]]*//p')"
|
|
||||||
rm -rf "$PREFLIGHT_DB"
|
|
||||||
if [ -n "$MISSING" ]; then
|
|
||||||
echo -e "\e[31mPaquets introuvables dans les dépôts du profil :\e[0m" >&2
|
|
||||||
printf ' \e[31m%s\e[0m\n' $MISSING >&2
|
|
||||||
echo -e "\e[33mDépôt fws-local absent/incomplet ? Lance ./setup-aur.sh (construit" >&2
|
|
||||||
echo -e "anaconda, winboat-bin, fws-dnd, …) puis relance ce script.\e[0m" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "==> Pré-vol OK : ${#PROFILE_PKGS[@]} paquets résolus."
|
|
||||||
|
|
||||||
echo "==> Lancement de mkarchiso..."
|
echo "==> Lancement de mkarchiso..."
|
||||||
mkdir -p "$OUT_DIR"
|
mkdir -p "$OUT_DIR"
|
||||||
# mkarchiso fait un « rm » interne de l'ISO cible avant de la (ré)écrire ; si
|
# mkarchiso supprime l'ISO cible avant de la réécrire : si elle est VERROUILLÉE
|
||||||
# une ISO du même nom est VERROUILLÉE par Windows (VM encore ouverte dessus,
|
# par Windows (VM ouverte dessus, ISO montée), ce rm casse tout le build à la
|
||||||
# ISO montée, antivirus), ce rm échoue et tout le build casse à la toute fin.
|
# fin. Parade : staging neuf puis déplacement ; si la cible (cette même
|
||||||
# Parade : mkarchiso écrit dans un dossier de staging neuf, puis on déplace
|
# version) est verrouillée, on bascule sur un nom horodaté. On ne touche
|
||||||
# l'ISO. Si la cible (cette même version) est verrouillée, on bascule sur un
|
# jamais aux ISO des autres versions.
|
||||||
# nom horodaté → on ne touche jamais aux ISO des autres versions.
|
|
||||||
STAGE="$OUT_DIR/.staging-$$"
|
STAGE="$OUT_DIR/.staging-$$"
|
||||||
rm -rf "$STAGE"; mkdir -p "$STAGE"
|
rm -rf "$STAGE"; mkdir -p "$STAGE"
|
||||||
mkarchiso -v -w /tmp/fws-build/work -o "$STAGE" /tmp/fws-build/releng
|
mkarchiso -v -w /tmp/fws-build/work -o "$STAGE" /tmp/fws-build/releng
|
||||||
@@ -405,4 +319,4 @@ echo "==> Nettoyage..."
|
|||||||
rm -rf /tmp/fws-build/work
|
rm -rf /tmp/fws-build/work
|
||||||
|
|
||||||
echo "==> Build officiel terminé ! ISO : $FINAL"
|
echo "==> Build officiel terminé ! ISO : $FINAL"
|
||||||
echo -e "\e[32mScript de build officiel (fws-${VERSION}-${ISO_TAG}-${BUILD_DATE}) terminé avec succès.\e[0m"
|
echo -e "\e[32mScript de build officiel (v${MAJOR}.${MINOR}.${BUILD}) terminé avec succès.\e[0m"
|
||||||
|
|||||||
@@ -133,12 +133,7 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
|
|||||||
"${SUDO[@]}" podman pull "$IMAGE"
|
"${SUDO[@]}" podman pull "$IMAGE"
|
||||||
|
|
||||||
echo "==> Relance dans le conteneur ($SELF)..."
|
echo "==> Relance dans le conteneur ($SELF)..."
|
||||||
# --network=host : le réseau bridge de podman ne route plus vers l'extérieur
|
"${SUDO[@]}" podman run --rm "${TTY[@]}" --privileged \
|
||||||
# sur ce wifi (firewalld/NAT cassé après changement de réseau → « core.db :
|
|
||||||
# Resolving timed out », même avec --dns). En partageant la pile réseau de
|
|
||||||
# l'hôte (qui, lui, a internet + DNS via 127.0.0.53/systemd-resolved joignable
|
|
||||||
# dans son propre netns), le conteneur récupère une connectivité identique.
|
|
||||||
"${SUDO[@]}" podman run --rm "${TTY[@]}" --network=host --privileged \
|
|
||||||
-e FWS_IN_CONTAINER=1 \
|
-e FWS_IN_CONTAINER=1 \
|
||||||
-v "$REPO_DIR":"$REPO_DIR" \
|
-v "$REPO_DIR":"$REPO_DIR" \
|
||||||
-v fws-pacman-cache:/var/cache/pacman/pkg \
|
-v fws-pacman-cache:/var/cache/pacman/pkg \
|
||||||
|
|||||||
@@ -20,146 +20,30 @@ _OB_PID=$!
|
|||||||
# boucle (symptôme « fond bleu qui clignote/reboote »). On NE rend jamais la main
|
# boucle (symptôme « fond bleu qui clignote/reboote »). On NE rend jamais la main
|
||||||
# à startx : on reste sur Openbox pour lire les logs / rebooter proprement.
|
# à startx : on reste sur Openbox pour lire les logs / rebooter proprement.
|
||||||
if command -v liveinst >/dev/null 2>&1; then
|
if command -v liveinst >/dev/null 2>&1; then
|
||||||
# Payload LiveOS : avec --liveinst (passé par liveinst), Anaconda IGNORE le
|
# Chantier D : RÉCAPITULATIF matériel AVANT de lancer l'installateur.
|
||||||
# payload du kickstart (anaconda.py teste opts.liveinst AVANT le type du
|
# fws-hw-detect est la MÊME source de vérité que le %post du kickstart
|
||||||
# payload DBus — un « liveimg » dans le ks serait du code mort) et détecte
|
# (mode --env) → ce que cet écran annonce = ce que l'install appliquera
|
||||||
# l'image live sur des chemins Fedora/dracut : /dev/mapper/live-base,
|
# (drivers GPU, guest tools de l'hyperviseur, effacement des disques…).
|
||||||
# /dev/mapper/live-osimg-min ou /run/rootfsbase — tous ABSENTS d'archiso →
|
# « Éteindre » est le seul moyen propre d'annuler : on ne rend jamais la
|
||||||
# « SourceSetupError: No Live OS image found! » = crash au démarrage
|
# main à startx (cf. plus bas), et liveinst n'a pas encore touché au disque.
|
||||||
# (reproduit hors VM dans un chroot de l'airootfs). On expose donc le
|
# Tout est best-effort : sans script ou sans zenity, on installe comme avant.
|
||||||
# squashfs archiso (monté ro sur /run/archiso/airootfs) là où Anaconda le
|
if command -v fws-hw-detect >/dev/null 2>&1; then
|
||||||
# cherche : findmnt y résout le loop device → montage → rsync vers la cible.
|
fws-hw-detect --text >/tmp/fws-hw-summary.txt 2>/dev/null || true
|
||||||
if [ -d /run/archiso/airootfs ] && [ ! -e /run/rootfsbase ]; then
|
if command -v zenity >/dev/null 2>&1 && [ -s /tmp/fws-hw-summary.txt ]; then
|
||||||
mkdir -p /run/rootfsbase
|
if ! zenity --text-info --title="FWS — Récapitulatif avant installation" \
|
||||||
mount --bind /run/archiso/airootfs /run/rootfsbase || rmdir /run/rootfsbase
|
--filename=/tmp/fws-hw-summary.txt \
|
||||||
fi
|
--ok-label="Lancer l'installateur" --cancel-label="Éteindre" \
|
||||||
|
--width=800 --height=700 2>/dev/null; then
|
||||||
# GARDE-FOU RAM : le live n'a que le zram comme marge — en dessous de
|
systemctl poweroff
|
||||||
# ~2 Go, Anaconda (GUI Python + squashfs) peut geler la machine en plein
|
|
||||||
# « Préparation de l'installateur ». On prévient AVANT plutôt que de
|
|
||||||
# laisser l'utilisateur croire à un bug.
|
|
||||||
_MEMKB=$(awk '/^MemTotal:/{print $2}' /proc/meminfo 2>/dev/null)
|
|
||||||
if [ -n "$_MEMKB" ] && [ "$_MEMKB" -lt 2000000 ] && command -v zenity >/dev/null 2>&1; then
|
|
||||||
zenity --warning --width=460 --title="FWS — mémoire insuffisante ?" \
|
|
||||||
--text="<b>Cette machine n'a que $(( _MEMKB / 1024 )) Mo de RAM.</b>\n\nL'installateur graphique peut geler en dessous de 2 Go.\nEn VM : augmente la mémoire (4 Go recommandés) puis redémarre.\n\nOK pour tenter quand même." 2>/dev/null
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ÉCRAN D'ATTENTE immédiat : le démarrage d'anaconda peut prendre 2 à
|
|
||||||
# 4 minutes (observé en VM VMware : un worker udev se bloque au boot et
|
|
||||||
# n'est tué par udevd qu'après 45 s — udev.event_timeout). Sans feedback,
|
|
||||||
# l'utilisateur croit à une panne alors que TOUT va bien. Le splash montre
|
|
||||||
# EN DIRECT ce que fait anaconda : un nourrisseur écrit sur stdin de
|
|
||||||
# zenity --progress (« # texte » = mise à jour du libellé) le log anaconda
|
|
||||||
# le plus récemment modifié + son dernier message + un chrono. Si les logs
|
|
||||||
# ne bougent plus (vrai blocage), le libellé le DIT (au lieu d'une barre
|
|
||||||
# qui pulse pour rien) et, au-delà de 4 min d'inactivité, on collecte
|
|
||||||
# journal+dmesg dans /tmp/fws-stall.txt et on l'affiche : l'écran devient
|
|
||||||
# lui-même le rapport de bug. Le nourrisseur meurt par SIGPIPE quand le
|
|
||||||
# splash est tué à la sortie de liveinst.
|
|
||||||
_SPLASH_PID=""
|
|
||||||
if command -v zenity >/dev/null 2>&1; then
|
|
||||||
(
|
|
||||||
_el=0; _quiet=0; _prevstamp=""
|
|
||||||
while :; do
|
|
||||||
sleep 3
|
|
||||||
# GUI d'anaconda à l'écran (marqueur ui.gui) → le splash n'a
|
|
||||||
# plus de raison d'être et le watchdog DOIT se désarmer : des
|
|
||||||
# logs silencieux pendant que l'utilisateur remplit les écrans
|
|
||||||
# sont NORMAUX (bug vécu : le rapport « préparation bloquée »
|
|
||||||
# s'affichait PAR-DESSUS l'installateur au bout de 4 min).
|
|
||||||
if grep -q 'ui\.gui' /tmp/anaconda.log 2>/dev/null; then
|
|
||||||
pkill -f 'zenity --progress' 2>/dev/null
|
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
# Compteurs en itérations (pas d'heure murale : l'horloge du
|
|
||||||
# guest SAUTE en VM — resynchro RTC — et gonflerait les âges,
|
|
||||||
# vu en vrai : « 242 s d'inactivité » 90 s après le boot).
|
|
||||||
_el=$(( _el + 3 ))
|
|
||||||
_last=$(ls -t /tmp/storage.log /tmp/anaconda.log /tmp/program.log /tmp/dbus.log 2>/dev/null | head -n1)
|
|
||||||
if [ -z "$_last" ]; then
|
|
||||||
printf '# Démarrage d'"'"'Anaconda… (%d min %02d s)\n' $(( _el / 60 )) $(( _el % 60 )) || exit 0
|
|
||||||
continue
|
|
||||||
fi
|
fi
|
||||||
_stamp=$(stat -c %Y "$_last" 2>/dev/null)
|
|
||||||
if [ "$_stamp" = "$_prevstamp" ]; then
|
|
||||||
_quiet=$(( _quiet + 3 ))
|
|
||||||
else
|
|
||||||
_quiet=0; _prevstamp="$_stamp"
|
|
||||||
fi
|
fi
|
||||||
# Rebranding d'affichage : anaconda (installateur Fedora) parle
|
|
||||||
# de Fedora/Arch dans ses logs — on ne montre pas ça à l'écran.
|
|
||||||
_msg=$(tail -n1 "$_last" 2>/dev/null | tr -d '\r' \
|
|
||||||
| sed -E 's/Fedora( Linux)?/FWS/g; s/Arch Linux/FWS/g; s/\<Arch\>/FWS/g' \
|
|
||||||
| cut -c1-90)
|
|
||||||
if [ "$_quiet" -ge 240 ] && [ ! -f /tmp/fws-stall.txt ]; then
|
|
||||||
{
|
|
||||||
echo "=== FWS : préparation bloquée (aucune activité log depuis ${_quiet}s) ==="
|
|
||||||
echo "--- dernier log actif : $_last"; tail -n 25 "$_last" 2>/dev/null
|
|
||||||
echo "--- udev (workers bloqués ?)"; udevadm settle --timeout=0 2>&1
|
|
||||||
echo "--- journal (fin)"; journalctl -b --no-pager 2>/dev/null | tail -n 60
|
|
||||||
echo "--- dmesg (fin)"; dmesg 2>/dev/null | tail -n 40
|
|
||||||
} > /tmp/fws-stall.txt 2>&1
|
|
||||||
zenity --text-info --title="FWS — préparation bloquée : diagnostic" \
|
|
||||||
--filename=/tmp/fws-stall.txt --width=1000 --height=650 2>/dev/null &
|
|
||||||
fi
|
|
||||||
if [ "$_quiet" -ge 60 ]; then
|
|
||||||
printf '# ⚠ Aucune activité depuis %d s (%s) — dernier message : %s\n' "$_quiet" "$(basename "$_last")" "$_msg" || exit 0
|
|
||||||
else
|
|
||||||
printf '# Préparation (%d min %02d s) — %s : %s\n' $(( _el / 60 )) $(( _el % 60 )) "$(basename "$_last")" "$_msg" || exit 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
) | zenity --progress --pulsate --no-cancel \
|
|
||||||
--title="FWS" \
|
|
||||||
--text="<b>Préparation de l'installateur FWS…</b>\n\nAnalyse du matériel et des disques en cours.\nEncore une à deux minutes, c'est normal." \
|
|
||||||
--width=560 2>/dev/null &
|
|
||||||
_SPLASH_PID=$!
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Hub Résumé SANS le libellé « We won't touch your disks… » : notre glade
|
|
||||||
# modifié NE PEUT PAS être livré directement à son chemin final —
|
|
||||||
# mkarchiso pose l'overlay airootfs AVANT pacstrap, et pacman refuse
|
|
||||||
# alors d'installer anaconda par-dessus (« exists in filesystem »). On le
|
|
||||||
# livre donc sous /usr/local/share/fws et on ÉCRASE ici, au boot du live
|
|
||||||
# (l'overlay du live est inscriptible), le fichier du paquet.
|
|
||||||
if [ -f /usr/local/share/fws/anaconda-summary-hub.glade ]; then
|
|
||||||
cp -f /usr/local/share/fws/anaconda-summary-hub.glade \
|
|
||||||
/usr/share/anaconda/ui/hubs/summary.glade 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# BRANDING : sans /.buildstamp, anaconda s'appelle « anaconda bluesky »
|
|
||||||
# (défauts de pyanaconda/core/product.py) et ses logs parlent de Fedora.
|
|
||||||
# Les variables d'environnement ANACONDA_* ont priorité → l'en-tête
|
|
||||||
# devient « FWS 1.0 INSTALLATION » et le log « anaconda … for FWS started ».
|
|
||||||
# (Les mentions Fedora PROFONDES dans les logs — GeoIP, profils — restent :
|
|
||||||
# ce sont des chaînes internes de l'installateur, sans effet sur l'install.)
|
|
||||||
export ANACONDA_PRODUCTNAME="$(. /etc/os-release 2>/dev/null; echo "${NAME:-FWS}")"
|
|
||||||
export ANACONDA_PRODUCTVERSION="$(. /etc/os-release 2>/dev/null; echo "${VERSION_ID:-0.6}")"
|
|
||||||
export ANACONDA_ISFINAL=true
|
|
||||||
|
|
||||||
LIVEINST_LOG=/tmp/fws-liveinst.log
|
LIVEINST_LOG=/tmp/fws-liveinst.log
|
||||||
# Bus D-Bus de SESSION : au démarrage (--liveinst), inhibit_screensaver()
|
|
||||||
# de pyanaconda se connecte au bus de session. S'il n'y en a AUCUN (notre
|
|
||||||
# startx root minimal n'en garantit pas), Gio lève une GLib.GError que
|
|
||||||
# pyanaconda n'attrape PAS (il n'attrape que DBusError) → crash immédiat
|
|
||||||
# (2e crash reproduit hors VM). dbus-run-session fournit un bus de session
|
|
||||||
# pour toute la durée de l'installateur ; avec un bus présent, l'absence du
|
|
||||||
# service ScreenSaver est attrapée (simple warning dans anaconda.log).
|
|
||||||
if command -v dbus-run-session >/dev/null 2>&1 && [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
|
|
||||||
dbus-run-session -- liveinst >"$LIVEINST_LOG" 2>&1
|
|
||||||
else
|
|
||||||
liveinst >"$LIVEINST_LOG" 2>&1
|
liveinst >"$LIVEINST_LOG" 2>&1
|
||||||
fi
|
|
||||||
rc=$?
|
rc=$?
|
||||||
|
|
||||||
# liveinst terminé (install finie ou échec) : le splash n'a plus de raison
|
|
||||||
# d'être — kill de zenity ; le nourrisseur meurt de SIGPIPE à son
|
|
||||||
# prochain printf. Idem pour un éventuel dialogue de diagnostic de stall.
|
|
||||||
if [ -n "$_SPLASH_PID" ]; then
|
|
||||||
pkill -P "$_SPLASH_PID" 2>/dev/null
|
|
||||||
kill "$_SPLASH_PID" 2>/dev/null
|
|
||||||
fi
|
|
||||||
pkill -f "fws-stall.txt" 2>/dev/null
|
|
||||||
|
|
||||||
# ATTENTION : liveinst NE propage PAS le code de sortie d'anaconda — il
|
# ATTENTION : liveinst NE propage PAS le code de sortie d'anaconda — il
|
||||||
# enchaîne son propre nettoyage et sort souvent 0 même quand anaconda a
|
# enchaîne son propre nettoyage et sort souvent 0 même quand anaconda a
|
||||||
# planté. On ne se fie donc PAS à $rc : en phase debug on regroupe TOUS les
|
# planté. On ne se fie donc PAS à $rc : en phase debug on regroupe TOUS les
|
||||||
@@ -203,8 +87,8 @@ if command -v liveinst >/dev/null 2>&1; then
|
|||||||
echo "########## %post --nochroot (/tmp/fws-post-nochroot.log) ##########"
|
echo "########## %post --nochroot (/tmp/fws-post-nochroot.log) ##########"
|
||||||
cat /tmp/fws-post-nochroot.log 2>/dev/null || echo "(absent)"
|
cat /tmp/fws-post-nochroot.log 2>/dev/null || echo "(absent)"
|
||||||
echo
|
echo
|
||||||
echo "########## Récapitulatif (page GUI fws_recap, avant install) ##########"
|
echo "########## Récapitulatif matériel montré avant l'install ##########"
|
||||||
cat /tmp/fws-hw-summary.txt 2>/dev/null || echo "(absent — page récap pas atteinte)"
|
cat /tmp/fws-hw-summary.txt 2>/dev/null || echo "(absent)"
|
||||||
for f in /tmp/anaconda.log /tmp/syslog /tmp/program.log \
|
for f in /tmp/anaconda.log /tmp/syslog /tmp/program.log \
|
||||||
/tmp/storage.log /tmp/dbus.log /tmp/packaging.log /tmp/X.log; do
|
/tmp/storage.log /tmp/dbus.log /tmp/packaging.log /tmp/X.log; do
|
||||||
echo
|
echo
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
# Démarrage automatique dans le live FWS uniquement.
|
# Démarrage automatique de Xorg uniquement dans le live FWS.
|
||||||
# Sur le système installé, /run/archiso n'existe pas, donc rien ne se lance.
|
# Sur le système installé, /run/archiso n'existe pas, donc rien ne se lance.
|
||||||
|
|
||||||
# Mode RÉPARATION (entrée de boot « Réparer FWS », paramètre fws.recovery=1) :
|
|
||||||
# on démarre X + l'application GTK (fws-recovery-session lance openbox puis
|
|
||||||
# fws-recovery-gui, qui pilote le moteur fws-recovery). Si X échoue à
|
|
||||||
# démarrer, startx rend la main → repli sur l'assistant console fws-recovery.
|
|
||||||
# (Sur le SYSTÈME INSTALLÉ, l'équivalent est fws-recovery-boot.service via
|
|
||||||
# l'entrée GRUB « Réparer FWS » — pas ce fichier.)
|
|
||||||
if [[ -d /run/archiso && "$(tty)" == "/dev/tty1" ]] \
|
|
||||||
&& grep -qw 'fws.recovery=1' /proc/cmdline 2>/dev/null; then
|
|
||||||
startx /usr/local/bin/fws-recovery-session -- vt1
|
|
||||||
exec fws-recovery
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -d /run/archiso && "$(tty)" == "/dev/tty1" && -z "$DISPLAY" ]]; then
|
if [[ -d /run/archiso && "$(tty)" == "/dev/tty1" && -z "$DISPLAY" ]]; then
|
||||||
exec startx -- vt1
|
exec startx -- vt1
|
||||||
fi
|
fi
|
||||||
|
|||||||
-106
@@ -1,106 +0,0 @@
|
|||||||
# FWS — Spoke « Desktop Environment » : nouvel onglet de choix du bureau.
|
|
||||||
#
|
|
||||||
# POURQUOI ICI (airootfs, pas dans le paquet anaconda) : Anaconda découvre les
|
|
||||||
# spokes au RUNTIME via collect()/collect_spokes (os.listdir du dossier des
|
|
||||||
# spokes puis import de chaque .py). Déposer ce fichier dans le dossier des
|
|
||||||
# spokes via l'overlay airootfs suffit donc à le faire apparaître — SANS rebuild
|
|
||||||
# du paquet anaconda (juste ./build.sh). Le glade associé est dans
|
|
||||||
# /usr/share/anaconda/ui/spokes/fws_desktop.glade (aussi via l'airootfs).
|
|
||||||
#
|
|
||||||
# ⚠ Le chemin /usr/lib/python3.14/... est couplé à la version de Python d'Arch
|
|
||||||
# (3.14 au moment du build d'anaconda). À mettre à jour si Python change de
|
|
||||||
# version majeure (en même temps que le rebuild d'anaconda de toute façon).
|
|
||||||
#
|
|
||||||
# FONCTION : afficher 5 choix (CLI/GNOME/KDE/Hyprland/XFCE) et écrire la clé
|
|
||||||
# choisie dans /tmp/fws-desktop. Le %post du kickstart lit ce fichier, fait
|
|
||||||
# « pacman -S » du bureau + d'un navigateur (Firefox), et active le display
|
|
||||||
# manager. « cli » (défaut) = aucun bureau, rien à installer.
|
|
||||||
|
|
||||||
from pyanaconda.core.i18n import CN_, _
|
|
||||||
from pyanaconda.ui.categories.software import SoftwareCategory
|
|
||||||
from pyanaconda.ui.gui.spokes import NormalSpoke
|
|
||||||
|
|
||||||
__all__ = ["FWSDesktopSpoke"]
|
|
||||||
|
|
||||||
# clé interne (écrite dans /tmp/fws-desktop) → libellé affiché dans le statut.
|
|
||||||
_CHOICES = [
|
|
||||||
("cli", _("Command line only (no desktop)")),
|
|
||||||
("gnome", "GNOME"),
|
|
||||||
("kde", "KDE Plasma"),
|
|
||||||
("hyprland", "Hyprland — Wayland (non compatible VR)"),
|
|
||||||
("i3", "i3 — X11 (compatible VR : SteamVR)"),
|
|
||||||
("xfce", "XFCE"),
|
|
||||||
]
|
|
||||||
_DESKTOP_FILE = "/tmp/fws-desktop"
|
|
||||||
|
|
||||||
|
|
||||||
class FWSDesktopSpoke(NormalSpoke):
|
|
||||||
"""Choix de l'environnement de bureau FWS (écrit /tmp/fws-desktop)."""
|
|
||||||
|
|
||||||
builderObjects = ["fwsDesktopWindow"]
|
|
||||||
mainWidgetName = "fwsDesktopWindow"
|
|
||||||
uiFile = "spokes/fws_desktop.glade"
|
|
||||||
category = SoftwareCategory
|
|
||||||
icon = "preferences-desktop-display-symbolic"
|
|
||||||
title = CN_("GUI|Spoke", "_Desktop Environment")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_screen_id():
|
|
||||||
"""Identifiant unique de cet écran."""
|
|
||||||
return "fws-desktop-selection"
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
NormalSpoke.__init__(self, *args)
|
|
||||||
self._choice = "cli"
|
|
||||||
self._radios = {}
|
|
||||||
|
|
||||||
def initialize(self):
|
|
||||||
NormalSpoke.initialize(self)
|
|
||||||
for key, _label in _CHOICES:
|
|
||||||
self._radios[key] = self.builder.get_object("radio_" + key)
|
|
||||||
# Reprend un éventuel choix déjà écrit (revisite du spoke).
|
|
||||||
try:
|
|
||||||
with open(_DESKTOP_FILE) as fh:
|
|
||||||
val = fh.read().strip()
|
|
||||||
if val in self._radios:
|
|
||||||
self._choice = val
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def refresh(self):
|
|
||||||
# Coche le bouton correspondant au choix courant.
|
|
||||||
rb = self._radios.get(self._choice) or self._radios.get("cli")
|
|
||||||
if rb is not None:
|
|
||||||
rb.set_active(True)
|
|
||||||
|
|
||||||
def apply(self):
|
|
||||||
# Relit le bouton actif et persiste le choix pour le %post du kickstart.
|
|
||||||
for key, rb in self._radios.items():
|
|
||||||
if rb is not None and rb.get_active():
|
|
||||||
self._choice = key
|
|
||||||
break
|
|
||||||
try:
|
|
||||||
with open(_DESKTOP_FILE, "w") as fh:
|
|
||||||
fh.write(self._choice)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self):
|
|
||||||
for key, label in _CHOICES:
|
|
||||||
if key == self._choice:
|
|
||||||
return label
|
|
||||||
return _("Command line only (no desktop)")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def completed(self):
|
|
||||||
# « cli » est un défaut valide → le spoke n'est jamais bloquant.
|
|
||||||
return True
|
|
||||||
|
|
||||||
@property
|
|
||||||
def mandatory(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ready(self):
|
|
||||||
return True
|
|
||||||
-348
@@ -1,348 +0,0 @@
|
|||||||
# FWS — Page « Récapitulatif » : écran À PART ENTIÈRE avant l'installation.
|
|
||||||
#
|
|
||||||
# POURQUOI ICI (airootfs, pas dans le paquet anaconda) : même mécanisme que la
|
|
||||||
# spoke « Desktop Environment » (fws_desktop.py) — Anaconda découvre les spokes
|
|
||||||
# au RUNTIME (collect) dans ce dossier → l'overlay airootfs suffit, AUCUN
|
|
||||||
# rebuild du paquet anaconda. Le glade associé est dans
|
|
||||||
# /usr/share/anaconda/ui/spokes/fws_recap.glade (aussi via l'airootfs).
|
|
||||||
#
|
|
||||||
# PLACEMENT DANS LE FLUX : standalone spoke « postForHub = SummaryHub » avec
|
|
||||||
# priority = -10. L'ordre des écrans est pre(SummaryHub) → SummaryHub →
|
|
||||||
# post(SummaryHub) trié par priority croissante (UserInterface.
|
|
||||||
# _orderActionClasses). ProgressSpoke — l'écran qui DÉMARRE l'installation
|
|
||||||
# dans son refresh() — est aussi post(SummaryHub), priority 0 par défaut →
|
|
||||||
# cette page s'intercale ENTRE les deux : l'utilisateur clique « Commencer
|
|
||||||
# l'installation » sur le hub, voit CE récapitulatif (le disque n'a encore
|
|
||||||
# subi AUCUNE écriture), et l'installation ne démarre qu'à son clic
|
|
||||||
# « Lancer l'installation ». Le bouton « Précédent » ramène au hub pour
|
|
||||||
# modifier les choix (cf. _on_back_clicked : la file d'actions du GUI est
|
|
||||||
# consommée à chaque écran, on y réinsère le hub).
|
|
||||||
#
|
|
||||||
# CONTENU : configuration choisie par l'utilisateur (via les modules D-Bus
|
|
||||||
# d'Anaconda : langue, clavier, fuseau, comptes, partitionnement + les
|
|
||||||
# fichiers /tmp des spokes FWS : bureau, hostname) ET matériel détecté
|
|
||||||
# (virtualisation, GPU, disques, réseau) — mêmes sources que ce que le
|
|
||||||
# %post du kickstart et fws-setup-hardware appliqueront.
|
|
||||||
#
|
|
||||||
# ⚠ « completed » doit rester False : _instantiateAction SAUTE les standalone
|
|
||||||
# spokes complétées (mécanisme prévu pour les installs kickstart).
|
|
||||||
# ⚠ Chemin couplé à la version Python d'Arch (3.14) — comme fws_desktop.py —
|
|
||||||
# et le bouton « Précédent » s'appuie sur les internes du GUI d'anaconda
|
|
||||||
# 45.8 (_actions/_currentAction) : à re-vérifier à chaque bump d'anaconda.
|
|
||||||
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
import gi
|
|
||||||
|
|
||||||
gi.require_version("Gtk", "3.0")
|
|
||||||
from gi.repository import Gtk
|
|
||||||
|
|
||||||
from pyanaconda.anaconda_loggers import get_module_logger
|
|
||||||
from pyanaconda.core import util
|
|
||||||
from pyanaconda.modules.common.constants.objects import DISK_SELECTION
|
|
||||||
from pyanaconda.modules.common.constants.services import (
|
|
||||||
LOCALIZATION,
|
|
||||||
STORAGE,
|
|
||||||
TIMEZONE,
|
|
||||||
USERS,
|
|
||||||
)
|
|
||||||
from pyanaconda.modules.common.structures.user import UserData
|
|
||||||
from pyanaconda.ui.gui import GraphicalUserInterface
|
|
||||||
from pyanaconda.ui.gui.hubs.summary import SummaryHub
|
|
||||||
from pyanaconda.ui.gui.spokes import StandaloneSpoke
|
|
||||||
|
|
||||||
log = get_module_logger(__name__)
|
|
||||||
|
|
||||||
__all__ = ["FWSRecapSpoke"]
|
|
||||||
|
|
||||||
# Doit rester aligné sur le %post du kickstart (étape « Bureau choisi ») et
|
|
||||||
# sur les libellés de la spoke fws_desktop.
|
|
||||||
_DESKTOP_LABELS = {
|
|
||||||
"cli": "ligne de commande uniquement (aucun bureau)",
|
|
||||||
"gnome": "GNOME (+ Firefox, GDM)",
|
|
||||||
"kde": "KDE Plasma (+ Firefox, SDDM)",
|
|
||||||
"hyprland": "Hyprland — Wayland, non compatible VR (+ Firefox, SDDM)",
|
|
||||||
"i3": "i3 — X11, compatible VR/SteamVR (+ Firefox, SDDM)",
|
|
||||||
"xfce": "XFCE (+ Firefox, SDDM)",
|
|
||||||
}
|
|
||||||
|
|
||||||
_PARTITIONING_LABELS = {
|
|
||||||
"AUTOMATIC": "automatique (recommandé)",
|
|
||||||
"INTERACTIVE": "personnalisé (assistant)",
|
|
||||||
"CUSTOM": "personnalisé",
|
|
||||||
"BLIVET": "avancé (blivet-gui)",
|
|
||||||
"MANUAL": "points de montage manuels",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _safe(getter, default):
|
|
||||||
"""Valeur d'un getter D-Bus/système, ou défaut : le récap ne doit JAMAIS
|
|
||||||
faire tomber l'installateur, quel que soit le module indisponible (d'où
|
|
||||||
le except large : dasbus lève des types variés selon la panne)."""
|
|
||||||
try:
|
|
||||||
return getter()
|
|
||||||
except Exception: # pylint: disable=broad-except
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
def _capture(*argv):
|
|
||||||
"""stdout d'une commande, chaîne vide si absente ou en échec."""
|
|
||||||
try:
|
|
||||||
return util.execWithCapture(argv[0], list(argv[1:])) or ""
|
|
||||||
except (OSError, RuntimeError):
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def _read_flag(path):
|
|
||||||
try:
|
|
||||||
with open(path) as fh:
|
|
||||||
return fh.read().strip()
|
|
||||||
except OSError:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def _config_lines():
|
|
||||||
"""Section « configuration choisie » : interroge les modules D-Bus
|
|
||||||
d'Anaconda (remplis par les spokes) + les fichiers /tmp des spokes FWS."""
|
|
||||||
lines = []
|
|
||||||
|
|
||||||
lang = _safe(lambda: LOCALIZATION.get_proxy().Language, "")
|
|
||||||
if lang:
|
|
||||||
lines.append(f" • Langue : {lang}")
|
|
||||||
|
|
||||||
layouts = _safe(lambda: list(LOCALIZATION.get_proxy().XLayouts), [])
|
|
||||||
if layouts:
|
|
||||||
lines.append(f" • Clavier (X11) : {', '.join(layouts)}")
|
|
||||||
|
|
||||||
tz = _safe(lambda: TIMEZONE.get_proxy().Timezone, "")
|
|
||||||
if tz:
|
|
||||||
ntp = _safe(lambda: TIMEZONE.get_proxy().NTPEnabled, None)
|
|
||||||
suffix = {True: " — synchronisation NTP activée",
|
|
||||||
False: " — synchronisation NTP désactivée"}.get(ntp, "")
|
|
||||||
lines.append(f" • Fuseau horaire : {tz}{suffix}")
|
|
||||||
|
|
||||||
users = _safe(
|
|
||||||
lambda: UserData.from_structure_list(USERS.get_proxy().Users), [])
|
|
||||||
if users:
|
|
||||||
names = ", ".join(u.name for u in users if u.name)
|
|
||||||
lines.append(f" • Utilisateur(s) : {names} (sudo via wheel)")
|
|
||||||
else:
|
|
||||||
lines.append(" • Utilisateur(s) : AUCUN — pensez-y avant de lancer !")
|
|
||||||
|
|
||||||
root_locked = _safe(lambda: USERS.get_proxy().IsRootAccountLocked, None)
|
|
||||||
root_pw = _safe(lambda: USERS.get_proxy().IsRootPasswordSet, None)
|
|
||||||
if root_locked:
|
|
||||||
root_txt = "verrouillé (recommandé)"
|
|
||||||
elif root_pw:
|
|
||||||
root_txt = "actif, mot de passe défini"
|
|
||||||
elif root_pw is not None:
|
|
||||||
# Même règle que l'étape « Sécurité » du %post : un root resté sans
|
|
||||||
# mot de passe sera verrouillé en fin d'installation.
|
|
||||||
root_txt = "sans mot de passe → sera VERROUILLÉ en fin d'install"
|
|
||||||
else:
|
|
||||||
root_txt = ""
|
|
||||||
if root_txt:
|
|
||||||
lines.append(f" • Compte root : {root_txt}")
|
|
||||||
|
|
||||||
# Hostname : écrit par le spoke Utilisateur (patch user.py du paquet
|
|
||||||
# anaconda). Même nettoyage / même défaut que le %post --nochroot.
|
|
||||||
hn = "".join(c for c in _read_flag("/tmp/fws-hostname")
|
|
||||||
if c.isalnum() or c in "._-") or "fws"
|
|
||||||
lines.append(f" • Nom de machine : {hn}")
|
|
||||||
|
|
||||||
# Bureau : écrit par fws_desktop.apply() (sortie de la spoke). Absent =
|
|
||||||
# spoke jamais visitée = « cli », exactement le défaut du %post.
|
|
||||||
desk = _read_flag("/tmp/fws-desktop") or "cli"
|
|
||||||
lines.append(f" • Bureau : {_DESKTOP_LABELS.get(desk, desk)}")
|
|
||||||
|
|
||||||
method = _safe(
|
|
||||||
lambda: STORAGE.get_proxy(
|
|
||||||
STORAGE.get_proxy().AppliedPartitioning).PartitioningMethod, "")
|
|
||||||
if method:
|
|
||||||
lines.append(" • Partitionnement : "
|
|
||||||
f"{_PARTITIONING_LABELS.get(method, method)}")
|
|
||||||
|
|
||||||
return lines, desk, method
|
|
||||||
|
|
||||||
|
|
||||||
def _build_summary():
|
|
||||||
config_lines, desk, method = _config_lines()
|
|
||||||
config_text = "\n".join(config_lines) \
|
|
||||||
or " (configuration non disponible)"
|
|
||||||
|
|
||||||
# Disques ciblés par l'installation (spoke « Destination »).
|
|
||||||
selected = set(_safe(
|
|
||||||
lambda: list(STORAGE.get_proxy(DISK_SELECTION).SelectedDisks), []))
|
|
||||||
disks = []
|
|
||||||
for line in _capture("lsblk", "-dno", "NAME,SIZE,MODEL,TYPE").splitlines():
|
|
||||||
fields = line.split()
|
|
||||||
if fields and fields[-1] == "disk":
|
|
||||||
mark = " ← CIBLE de l'installation" if fields[0] in selected else ""
|
|
||||||
disks.append(" • " + " ".join(fields[:-1]) + mark)
|
|
||||||
disk_text = "\n".join(disks) or " (aucun disque détecté)"
|
|
||||||
|
|
||||||
if method == "AUTOMATIC":
|
|
||||||
disk_warn = (
|
|
||||||
" ⚠ Rien n'a encore été écrit. Le partitionnement automatique\n"
|
|
||||||
" EFFACERA ENTIÈREMENT le(s) disque(s) cible(s) au lancement.")
|
|
||||||
else:
|
|
||||||
disk_warn = (
|
|
||||||
" ⚠ Rien n'a encore été écrit. Au lancement, ce sont vos choix\n"
|
|
||||||
" de partitionnement faits dans l'installateur qui s'appliqueront.")
|
|
||||||
|
|
||||||
virt = (_capture("systemd-detect-virt").strip().splitlines() or ["none"])[0]
|
|
||||||
if virt in ("", "none"):
|
|
||||||
virt = "aucune (machine physique)"
|
|
||||||
|
|
||||||
gpus = []
|
|
||||||
for line in _capture("lspci").splitlines():
|
|
||||||
low = line.lower()
|
|
||||||
if ("vga" in low or "3d" in low or "display" in low) and ":" in line:
|
|
||||||
gpus.append(" • " + line.split(":", 2)[-1].strip())
|
|
||||||
gpu_text = "\n".join(gpus) or " (aucune carte graphique détectée)"
|
|
||||||
|
|
||||||
try:
|
|
||||||
net_ok = util.execWithRedirect("ip", ["route", "get", "1.1.1.1"]) == 0
|
|
||||||
except (OSError, RuntimeError):
|
|
||||||
net_ok = False
|
|
||||||
if net_ok:
|
|
||||||
net = "disponible ✔"
|
|
||||||
else:
|
|
||||||
net = ("INDISPONIBLE ✖ — bureau, drivers et stack FWS ne\n"
|
|
||||||
" s'installeront PAS (rattrapable au 1er boot)")
|
|
||||||
|
|
||||||
extras = ("Steam, Wine, VR (Monado/OpenXR), winboat-bin"
|
|
||||||
if desk != "cli" else "rien de plus (choix CLI)")
|
|
||||||
|
|
||||||
return f"""\
|
|
||||||
══════════════════════════════════════════════════════════
|
|
||||||
FWS — RÉCAPITULATIF AVANT INSTALLATION
|
|
||||||
══════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
CONFIGURATION CHOISIE
|
|
||||||
{config_text}
|
|
||||||
|
|
||||||
DISQUES
|
|
||||||
{disk_warn}
|
|
||||||
{disk_text}
|
|
||||||
|
|
||||||
MATÉRIEL DÉTECTÉ
|
|
||||||
• Virtualisation : {virt}
|
|
||||||
• Réseau : {net}
|
|
||||||
Cartes graphiques (drivers installés automatiquement en fin d'install) :
|
|
||||||
{gpu_text}
|
|
||||||
|
|
||||||
DÉROULEMENT DE L'INSTALLATION
|
|
||||||
1. Partitionnement, puis copie du système live FWS (payload LiveOS)
|
|
||||||
2. Noyau + initramfs régénérés (mkinitcpio), GRUB installé
|
|
||||||
3. Bureau choisi + navigateur installés (réseau requis)
|
|
||||||
4. Matériel : drivers GPU, outils VM, son, Bluetooth, Podman
|
|
||||||
+ si bureau choisi : {extras}
|
|
||||||
|
|
||||||
« Précédent » pour revenir modifier vos choix —
|
|
||||||
« Lancer l'installation » pour démarrer réellement l'installation.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class FWSRecapSpoke(StandaloneSpoke):
|
|
||||||
"""Récapitulatif des choix, affiché entre le hub et l'installation."""
|
|
||||||
|
|
||||||
builderObjects = ["fwsRecapWindow"]
|
|
||||||
mainWidgetName = "fwsRecapWindow"
|
|
||||||
uiFile = "spokes/fws_recap.glade"
|
|
||||||
|
|
||||||
postForHub = SummaryHub
|
|
||||||
priority = -10
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_screen_id():
|
|
||||||
"""Identifiant unique de cet écran."""
|
|
||||||
return "fws-recap"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def completed(self):
|
|
||||||
# False, sinon _instantiateAction saute l'écran (cf. en-tête).
|
|
||||||
return False
|
|
||||||
|
|
||||||
def apply(self):
|
|
||||||
# Rien à persister : cette page ne fait que montrer l'état.
|
|
||||||
pass
|
|
||||||
|
|
||||||
def initialize(self):
|
|
||||||
StandaloneSpoke.initialize(self)
|
|
||||||
# Le bouton « continuer » de CETTE page est le vrai déclencheur :
|
|
||||||
# l'écran suivant (ProgressSpoke) démarre la file d'installation.
|
|
||||||
self.window.get_continue_button().set_label("_Lancer l'installation")
|
|
||||||
|
|
||||||
# Capture pour « Précédent » : initialize() est appelé DEPUIS
|
|
||||||
# GraphicalUserInterface._on_continue_clicked (clic « Commencer
|
|
||||||
# l'installation » du hub), AVANT la bascule de _currentAction → à
|
|
||||||
# cet instant ui._currentAction est ENCORE l'instance du hub Résumé,
|
|
||||||
# et le « self » de l'appelant est l'UI. Aucune API publique ne les
|
|
||||||
# expose ; on remonte la pile d'appels (déterministe — pas de scan
|
|
||||||
# mémoire, pas de variable globale).
|
|
||||||
self._gui = None
|
|
||||||
self._hub = None
|
|
||||||
frame = inspect.currentframe()
|
|
||||||
try:
|
|
||||||
frame = frame.f_back
|
|
||||||
while frame is not None:
|
|
||||||
candidate = frame.f_locals.get("self")
|
|
||||||
if isinstance(candidate, GraphicalUserInterface):
|
|
||||||
self._gui = candidate
|
|
||||||
current = getattr(candidate, "_currentAction", None)
|
|
||||||
if isinstance(current, SummaryHub):
|
|
||||||
self._hub = current
|
|
||||||
break
|
|
||||||
frame = frame.f_back
|
|
||||||
finally:
|
|
||||||
del frame
|
|
||||||
|
|
||||||
# Bouton « Précédent », placé avec les boutons de navigation
|
|
||||||
# (à côté de « Quitter » — leur parent est la GtkButtonBox du bas).
|
|
||||||
quit_button = self.window.get_quit_button()
|
|
||||||
button_box = quit_button.get_parent()
|
|
||||||
self._back_button = Gtk.Button.new_with_mnemonic("_Précédent")
|
|
||||||
self._back_button.connect("clicked", self._on_back_clicked)
|
|
||||||
button_box.add(self._back_button)
|
|
||||||
button_box.reorder_child(self._back_button, 0)
|
|
||||||
if self._gui is None or self._hub is None:
|
|
||||||
# Références introuvables (flux inattendu) : bouton grisé plutôt
|
|
||||||
# qu'une navigation cassée — l'install reste possible.
|
|
||||||
log.warning("FWS recap: hub/UI introuvables, « Précédent » désactivé.")
|
|
||||||
self._back_button.set_sensitive(False)
|
|
||||||
self._back_button.show()
|
|
||||||
|
|
||||||
def _on_back_clicked(self, _button):
|
|
||||||
ui = self._gui
|
|
||||||
hub = self._hub
|
|
||||||
if ui is None or hub is None:
|
|
||||||
return
|
|
||||||
log.info("FWS recap: retour au hub Résumé pour modification.")
|
|
||||||
# La file d'actions du GUI est CONSOMMÉE (pop(0)) à chaque écran :
|
|
||||||
# ici _actions = [FWSRecapSpoke, ProgressSpoke] et le hub en est déjà
|
|
||||||
# sorti. On le réinsère en tête pour que le prochain « Commencer
|
|
||||||
# l'installation » ré-enchaîne hub → NOUVELLE page récap (résumé
|
|
||||||
# recalculé avec les choix modifiés) → installation.
|
|
||||||
if not ui._actions or ui._actions[0] is not SummaryHub:
|
|
||||||
ui._actions.insert(0, SummaryHub)
|
|
||||||
# _on_continue_clicked a désactivé le « continuer » du hub en le
|
|
||||||
# quittant : on le réarme, sinon « Commencer l'installation » est mort.
|
|
||||||
hub.window.set_may_continue(True)
|
|
||||||
# Même séquence que la transition officielle (_on_continue_clicked).
|
|
||||||
self.exited.emit(self)
|
|
||||||
hub.refresh()
|
|
||||||
ui.mainWindow.setCurrentAction(hub)
|
|
||||||
hub.entered.emit(hub)
|
|
||||||
ui._currentAction = hub
|
|
||||||
|
|
||||||
def refresh(self):
|
|
||||||
StandaloneSpoke.refresh(self)
|
|
||||||
text = _build_summary()
|
|
||||||
self.builder.get_object("recapView").get_buffer().set_text(text)
|
|
||||||
# Copie pour le harnais de debug du .xinitrc (fws-debug.txt).
|
|
||||||
try:
|
|
||||||
with open("/tmp/fws-hw-summary.txt", "w") as fh:
|
|
||||||
fh.write(text)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================================================
|
||||||
|
# fws-hw-detect — détection matériel FWS (hyperviseur + GPU).
|
||||||
|
#
|
||||||
|
# SOURCE UNIQUE de vérité, consommée par DEUX endroits :
|
||||||
|
# • l'écran « Récapitulatif avant installation » du live (--text, affiché
|
||||||
|
# par /root/.xinitrc via zenity juste avant de lancer liveinst) ;
|
||||||
|
# • le %post chrooté du kickstart Anaconda (--env), qui APPLIQUE :
|
||||||
|
# activation des guest tools du bon hyperviseur, purge des autres,
|
||||||
|
# installation des drivers GPU.
|
||||||
|
# → ce que l'écran ANNONCE = ce que l'install FERA (aucune divergence
|
||||||
|
# possible, la détection vit ici et nulle part ailleurs).
|
||||||
|
#
|
||||||
|
# Fonctionne dans le LIVE comme dans le CHROOT d'Anaconda : celui-ci
|
||||||
|
# bind-monte /sys et /proc du live → même matériel visible. Le script est
|
||||||
|
# livré par l'airootfs (/usr/local/bin) → recopié dans la cible par le
|
||||||
|
# payload liveimg → présent dans le chroot du %post. Aucune dépendance
|
||||||
|
# dure : lspci (pciutils) et ip (iproute2) ne servent qu'aux libellés
|
||||||
|
# humains de --text, avec repli si absents.
|
||||||
|
#
|
||||||
|
# Usage : fws-hw-detect --env lignes FWS_*='…' à eval par le kickstart
|
||||||
|
# fws-hw-detect --text récapitulatif humain en français (zenity)
|
||||||
|
# ============================================================================
|
||||||
|
set -u
|
||||||
|
MODE="${1:---text}"
|
||||||
|
|
||||||
|
# --- Hyperviseur --------------------------------------------------------------
|
||||||
|
# systemd-detect-virt lit CPUID/DMI → OK en chroot, sans systemd actif.
|
||||||
|
# ATTENTION : hors VM il AFFICHE « none » ET sort en code ≠ 0 → surtout pas de
|
||||||
|
# « || echo none » DANS le $() (on récolterait « none » en double).
|
||||||
|
VIRT="$(systemd-detect-virt 2>/dev/null | head -n1)"
|
||||||
|
[ -n "$VIRT" ] || VIRT=none
|
||||||
|
|
||||||
|
# Les guest tools des 3 hyperviseurs sont DÉJÀ dans l'image live
|
||||||
|
# (packages.x86_64 : open-vm-tools, virtualbox-guest-utils-nox,
|
||||||
|
# qemu-guest-agent) → activer un service marche HORS LIGNE ; on retire ceux
|
||||||
|
# des autres hyperviseurs (inutiles sur cette machine).
|
||||||
|
VIRT_SVC="" VIRT_RM="" VIRT_LABEL=""
|
||||||
|
case "$VIRT" in
|
||||||
|
vmware)
|
||||||
|
VIRT_LABEL="VMware"
|
||||||
|
VIRT_SVC="vmtoolsd.service vmware-vmblock-fuse.service"
|
||||||
|
VIRT_RM="virtualbox-guest-utils-nox qemu-guest-agent" ;;
|
||||||
|
oracle) # = VirtualBox (nom renvoyé par systemd-detect-virt)
|
||||||
|
VIRT_LABEL="VirtualBox"
|
||||||
|
VIRT_SVC="vboxservice.service"
|
||||||
|
VIRT_RM="open-vm-tools qemu-guest-agent" ;;
|
||||||
|
kvm|qemu)
|
||||||
|
VIRT_LABEL="QEMU/KVM"
|
||||||
|
VIRT_SVC="qemu-guest-agent.service"
|
||||||
|
VIRT_RM="open-vm-tools virtualbox-guest-utils-nox" ;;
|
||||||
|
none)
|
||||||
|
VIRT_LABEL="aucune (machine physique)"
|
||||||
|
VIRT_RM="open-vm-tools virtualbox-guest-utils-nox qemu-guest-agent" ;;
|
||||||
|
*) # Hyperviseur non géré (hyper-v…) : aucun de nos guest tools ne sert.
|
||||||
|
VIRT_LABEL="$VIRT (intégration non gérée)"
|
||||||
|
VIRT_RM="open-vm-tools virtualbox-guest-utils-nox qemu-guest-agent" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# --- GPU ----------------------------------------------------------------------
|
||||||
|
# Classe PCI 0x03xxxx = contrôleur d'affichage ; lecture DIRECTE de /sys, pas
|
||||||
|
# besoin de lspci. Vendors : 0x10de NVIDIA, 0x1002 AMD, 0x8086 Intel,
|
||||||
|
# 0x15ad VMware (le VMSVGA de VirtualBox porte AUSSI 0x15ad → couvert).
|
||||||
|
# QEMU virtio-gpu/QXL et VBoxVGA : modesetting + mesa (déjà tirés par
|
||||||
|
# xorg-server dans l'image) suffisent → rien à installer.
|
||||||
|
DRV_PKGS="" GPU_TEXT=""
|
||||||
|
for _dev in /sys/bus/pci/devices/*; do
|
||||||
|
case "$(cat "$_dev/class" 2>/dev/null)" in 0x03*) ;; *) continue ;; esac
|
||||||
|
_vendor="$(cat "$_dev/vendor" 2>/dev/null)"
|
||||||
|
_pkgs=""
|
||||||
|
case "$_vendor" in
|
||||||
|
# NVIDIA : le module des dépôts est bâti pour le NOYAU des dépôts →
|
||||||
|
# « linux » DOIT partir dans la même transaction pacman (le noyau
|
||||||
|
# installé est celui du live, plus vieux), sinon module ≠ noyau au
|
||||||
|
# 1er boot. Les chemins /boot/* sont stables → grub.cfg reste bon.
|
||||||
|
0x10de) _pkgs="linux nvidia nvidia-utils" ;;
|
||||||
|
0x1002) _pkgs="mesa vulkan-radeon xf86-video-amdgpu" ;;
|
||||||
|
0x8086) _pkgs="mesa vulkan-intel" ;;
|
||||||
|
0x15ad) _pkgs="mesa xf86-video-vmware" ;;
|
||||||
|
esac
|
||||||
|
# Libellé humain : lspci si dispo, sinon adresse PCI + vendor brut.
|
||||||
|
_name=""
|
||||||
|
if command -v lspci >/dev/null 2>&1; then
|
||||||
|
_name="$(lspci -s "${_dev##*/}" 2>/dev/null | head -n1 \
|
||||||
|
| cut -d: -f3- | sed 's/^ //')"
|
||||||
|
fi
|
||||||
|
[ -n "$_name" ] || _name="GPU PCI ${_dev##*/} (vendor $_vendor)"
|
||||||
|
if [ -n "$_pkgs" ]; then
|
||||||
|
GPU_TEXT="${GPU_TEXT} • ${_name}\n → paquets : ${_pkgs}\n"
|
||||||
|
DRV_PKGS="$DRV_PKGS $_pkgs"
|
||||||
|
else
|
||||||
|
GPU_TEXT="${GPU_TEXT} • ${_name}\n → rien à installer (modesetting + mesa, déjà dans l'image)\n"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Dédoublonnage (machine multi-GPU : « mesa » listé 2× ferait échouer pacman
|
||||||
|
# sur « duplicate target »).
|
||||||
|
if [ -n "$DRV_PKGS" ]; then
|
||||||
|
DRV_PKGS="$(printf '%s\n' $DRV_PKGS | sort -u | tr '\n' ' ')"
|
||||||
|
DRV_PKGS="${DRV_PKGS% }"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Mode machine : sortie shell pour le kickstart ----------------------------
|
||||||
|
if [ "$MODE" = "--env" ]; then
|
||||||
|
printf "FWS_VIRT='%s'\n" "$VIRT"
|
||||||
|
printf "FWS_VIRT_SVC='%s'\n" "$VIRT_SVC"
|
||||||
|
printf "FWS_VIRT_RM='%s'\n" "$VIRT_RM"
|
||||||
|
printf "FWS_DRV_PKGS='%s'\n" "$DRV_PKGS"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Mode humain : récapitulatif --text (zenity, avant liveinst) ---------------
|
||||||
|
CPU="$(awk -F': +' '/^model name/{print $2; exit}' /proc/cpuinfo)"
|
||||||
|
[ -n "$CPU" ] || CPU="inconnu"
|
||||||
|
case "$(awk -F': +' '/^vendor_id/{print $2; exit}' /proc/cpuinfo)" in
|
||||||
|
GenuineIntel) UCODE="intel-ucode" ;;
|
||||||
|
AuthenticAMD) UCODE="amd-ucode" ;;
|
||||||
|
*) UCODE="" ;;
|
||||||
|
esac
|
||||||
|
RAM="$(awk '/^MemTotal/{printf "%.1f Gio", $2/1048576}' /proc/meminfo)"
|
||||||
|
if [ -d /sys/firmware/efi ]; then
|
||||||
|
BOOT="UEFI — GRUB x86_64-efi + entrée de secours EFI/BOOT/BOOTX64.EFI"
|
||||||
|
else
|
||||||
|
BOOT="BIOS hérité — GRUB i386-pc, table de partitions MBR forcée"
|
||||||
|
fi
|
||||||
|
if command -v ip >/dev/null 2>&1 && ip route get 1.1.1.1 >/dev/null 2>&1; then
|
||||||
|
NET="disponible ✔"
|
||||||
|
else
|
||||||
|
NET="INDISPONIBLE ✖ — drivers GPU et bureau ne s'installeront PAS (rattrapable au 1er boot)"
|
||||||
|
fi
|
||||||
|
# Type « disk » uniquement (exclut lecteur ISO sr0/loop). La clé USB du live,
|
||||||
|
# elle, EST un disque → il est juste qu'elle apparaisse sous l'avertissement
|
||||||
|
# d'effacement (clearpart --all du kickstart efface bien TOUS les disques).
|
||||||
|
DISKS="$(lsblk -dno NAME,SIZE,MODEL,TYPE 2>/dev/null \
|
||||||
|
| awk '$NF=="disk"{NF--; print " • " $0}')"
|
||||||
|
[ -n "$DISKS" ] || DISKS=" (aucun disque détecté)"
|
||||||
|
[ -n "$GPU_TEXT" ] || GPU_TEXT=" (aucune carte graphique détectée)\n"
|
||||||
|
|
||||||
|
printf '%b' \
|
||||||
|
"══════════════════════════════════════════════════════════
|
||||||
|
FWS — RÉCAPITULATIF AVANT INSTALLATION
|
||||||
|
══════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
MACHINE
|
||||||
|
• Démarrage : ${BOOT}
|
||||||
|
• Virtualisation : ${VIRT_LABEL}
|
||||||
|
• Processeur : ${CPU}"
|
||||||
|
[ -n "$UCODE" ] && printf '%b' "
|
||||||
|
• Microcode : ${UCODE} — déjà intégré à l'image, rien à faire"
|
||||||
|
printf '%b' "
|
||||||
|
• Mémoire : ${RAM}
|
||||||
|
• Réseau : ${NET}
|
||||||
|
|
||||||
|
DISQUES DÉTECTÉS
|
||||||
|
⚠ Par défaut, TOUS les disques ci-dessous seront ENTIÈREMENT
|
||||||
|
EFFACÉS (partitionnement automatique). Le partitionnement
|
||||||
|
manuel reste possible dans l'installateur.
|
||||||
|
${DISKS}
|
||||||
|
|
||||||
|
CARTES GRAPHIQUES → drivers installés AUTOMATIQUEMENT en fin d'install
|
||||||
|
${GPU_TEXT}
|
||||||
|
INTÉGRATION HYPERVISEUR (automatique, fonctionne hors ligne)
|
||||||
|
• Services activés : ${VIRT_SVC:-aucun}
|
||||||
|
• Guest tools retirés (inutiles) : ${VIRT_RM:-aucun}
|
||||||
|
|
||||||
|
DÉROULEMENT DE L'INSTALLATION
|
||||||
|
1. Réglages dans Anaconda : langue, clavier, fuseau, disque, utilisateur
|
||||||
|
2. Copie du système live FWS sur le disque (payload liveimg)
|
||||||
|
3. Noyau + initramfs régénérés (mkinitcpio), GRUB installé
|
||||||
|
4. Drivers GPU + intégration hyperviseur appliqués (voir ci-dessus)
|
||||||
|
5. Bureau choisi installé (réseau requis)
|
||||||
|
|
||||||
|
« Lancer l'installateur » pour continuer — « Éteindre » pour annuler.
|
||||||
|
"
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Generated with glade 3.18.3 -->
|
|
||||||
<interface>
|
|
||||||
<!-- FWS : copie de summary.glade d'anaconda 45.8 SANS le libellé
|
|
||||||
« We won't touch your disks… » du bas du hub. Livré ICI (chemin neutre)
|
|
||||||
car mkarchiso pose l'airootfs AVANT pacstrap : au chemin du paquet,
|
|
||||||
pacman refuserait d'installer anaconda (« exists in filesystem »).
|
|
||||||
C'est le .xinitrc du live qui le copie sur
|
|
||||||
/usr/share/anaconda/ui/hubs/summary.glade au boot. -->
|
|
||||||
<requires lib="gtk+" version="3.0"/>
|
|
||||||
<requires lib="AnacondaWidgets" version="3.0"/>
|
|
||||||
<object class="AnacondaHubWindow" id="summaryWindow">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="window_name" translatable="yes">INSTALLATION SUMMARY</property>
|
|
||||||
<property name="quit_button">quitButton</property>
|
|
||||||
<property name="continue_button">continueButton</property>
|
|
||||||
<child internal-child="main_box">
|
|
||||||
<object class="GtkBox" id="AnacondaHubWindow-main_box1">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child internal-child="nav_box">
|
|
||||||
<object class="GtkEventBox" id="AnacondaHubWindow-nav_box1">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child internal-child="nav_area">
|
|
||||||
<object class="GtkGrid" id="AnacondaHubWindow-nav_area1">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child internal-child="alignment">
|
|
||||||
<object class="GtkAlignment" id="AnacondaHubWindow-alignment1">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="yalign">0</property>
|
|
||||||
<property name="left_padding">12</property>
|
|
||||||
<property name="right_padding">6</property>
|
|
||||||
<child internal-child="action_area">
|
|
||||||
<object class="GtkBox" id="summaryWindow-actionArea">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">6</property>
|
|
||||||
<child internal-child="scrolled_window">
|
|
||||||
<object class="GtkScrolledWindow" id="summaryWindow-scroll">
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="is_focus">True</property>
|
|
||||||
<property name="hscrollbar_policy">never</property>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButtonBox" id="buttonbox1">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_left">6</property>
|
|
||||||
<property name="margin_right">6</property>
|
|
||||||
<property name="spacing">12</property>
|
|
||||||
<property name="layout_style">end</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="quitButton">
|
|
||||||
<property name="label" translatable="yes" context="GUI|Summary">_Quit</property>
|
|
||||||
<property name="use_action_appearance">False</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">True</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="use_underline">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="continueButton">
|
|
||||||
<property name="label" translatable="yes" context="GUI|Summary">_Begin Installation</property>
|
|
||||||
<property name="use_action_appearance">False</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="sensitive">False</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">True</property>
|
|
||||||
<property name="use_underline">True</property>
|
|
||||||
<style>
|
|
||||||
<class name="suggested-action"/>
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="pack_type">end</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child internal-child="accessible">
|
|
||||||
<object class="AtkObject" id="summaryWindow-atkobject">
|
|
||||||
<property name="AtkObject::accessible-name" translatable="yes">INSTALLATION SUMMARY</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</interface>
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- FWS — onglet « Desktop Environment » (choix du bureau à installer). -->
|
|
||||||
<interface>
|
|
||||||
<requires lib="gtk+" version="3.6"/>
|
|
||||||
<requires lib="AnacondaWidgets" version="1.0"/>
|
|
||||||
<object class="AnacondaSpokeWindow" id="fwsDesktopWindow">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="vexpand">True</property>
|
|
||||||
<property name="window-name" translatable="yes">DESKTOP ENVIRONMENT</property>
|
|
||||||
<signal name="button-clicked" handler="on_back_clicked" swapped="no"/>
|
|
||||||
<child internal-child="main_box">
|
|
||||||
<object class="GtkBox" id="AnacondaSpokeWindow-main_box1">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">6</property>
|
|
||||||
<child internal-child="nav_box">
|
|
||||||
<object class="GtkEventBox" id="AnacondaSpokeWindow-nav_box1">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<child internal-child="nav_area">
|
|
||||||
<object class="GtkGrid" id="AnacondaSpokeWindow-nav_area1">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="margin-left">6</property>
|
|
||||||
<property name="margin-right">6</property>
|
|
||||||
<property name="margin-top">6</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child internal-child="alignment">
|
|
||||||
<object class="GtkAlignment" id="AnacondaSpokeWindow-alignment1">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="margin-top">12</property>
|
|
||||||
<property name="yalign">0</property>
|
|
||||||
<property name="xscale">0</property>
|
|
||||||
<property name="yscale">0</property>
|
|
||||||
<property name="bottom-padding">48</property>
|
|
||||||
<property name="left-padding">24</property>
|
|
||||||
<property name="right-padding">24</property>
|
|
||||||
<child internal-child="action_area">
|
|
||||||
<object class="GtkBox" id="AnacondaSpokeWindow-action_area1">
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">8</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="fws_desktop_desc">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<property name="margin-bottom">12</property>
|
|
||||||
<property name="wrap">True</property>
|
|
||||||
<property name="max-width-chars">60</property>
|
|
||||||
<property name="label" translatable="yes">Choose the desktop environment to install. A web browser (Firefox) is installed automatically with any desktop.</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="radio_cli">
|
|
||||||
<property name="label" translatable="yes">Command line only (no desktop)</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">True</property>
|
|
||||||
<property name="receives-default">False</property>
|
|
||||||
<property name="active">True</property>
|
|
||||||
<property name="draw-indicator">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="radio_gnome">
|
|
||||||
<property name="label" translatable="yes">GNOME</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">True</property>
|
|
||||||
<property name="receives-default">False</property>
|
|
||||||
<property name="draw-indicator">True</property>
|
|
||||||
<property name="group">radio_cli</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="radio_kde">
|
|
||||||
<property name="label" translatable="yes">KDE Plasma</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">True</property>
|
|
||||||
<property name="receives-default">False</property>
|
|
||||||
<property name="draw-indicator">True</property>
|
|
||||||
<property name="group">radio_cli</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">3</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="radio_hyprland">
|
|
||||||
<property name="label" translatable="yes">Hyprland — Wayland (NOT VR compatible)</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">True</property>
|
|
||||||
<property name="receives-default">False</property>
|
|
||||||
<property name="draw-indicator">True</property>
|
|
||||||
<property name="group">radio_cli</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">4</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="radio_i3">
|
|
||||||
<property name="label" translatable="yes">i3 — X11 (VR compatible: SteamVR, HTC Vive, Valve Index…)</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">True</property>
|
|
||||||
<property name="receives-default">False</property>
|
|
||||||
<property name="draw-indicator">True</property>
|
|
||||||
<property name="group">radio_cli</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">5</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="radio_xfce">
|
|
||||||
<property name="label" translatable="yes">XFCE</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">True</property>
|
|
||||||
<property name="receives-default">False</property>
|
|
||||||
<property name="draw-indicator">True</property>
|
|
||||||
<property name="group">radio_cli</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">6</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child internal-child="accessible">
|
|
||||||
<object class="AtkObject" id="fwsDesktopWindow-atkobject">
|
|
||||||
<property name="AtkObject::accessible-name" translatable="yes">DESKTOP ENVIRONMENT</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</interface>
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- FWS — page « Récapitulatif avant installation » (cf. fws_recap.py).
|
|
||||||
Squelette calqué sur installation_progress.glade : AnacondaStandaloneWindow
|
|
||||||
avec ses enfants internes (nav_box/nav_area, alignment/action_area). -->
|
|
||||||
<interface>
|
|
||||||
<requires lib="gtk+" version="3.0"/>
|
|
||||||
<requires lib="AnacondaWidgets" version="3.0"/>
|
|
||||||
<object class="AnacondaStandaloneWindow" id="fwsRecapWindow">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="window_name" translatable="yes">RÉCAPITULATIF AVANT INSTALLATION</property>
|
|
||||||
<child internal-child="main_box">
|
|
||||||
<object class="GtkBox" id="AnacondaStandaloneWindow-main_box1">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child internal-child="nav_box">
|
|
||||||
<object class="GtkEventBox" id="AnacondaStandaloneWindow-nav_box1">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child internal-child="nav_area">
|
|
||||||
<object class="GtkGrid" id="AnacondaStandaloneWindow-nav_area1">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child internal-child="alignment">
|
|
||||||
<object class="GtkAlignment" id="AnacondaStandaloneWindow-alignment1">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="yalign">0</property>
|
|
||||||
<property name="left_padding">12</property>
|
|
||||||
<property name="right_padding">6</property>
|
|
||||||
<child internal-child="action_area">
|
|
||||||
<object class="GtkBox" id="fwsRecapWindow-actionArea">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">6</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow" id="recapScroll">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="shadow_type">in</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkTextView" id="recapView">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="editable">False</property>
|
|
||||||
<property name="cursor_visible">False</property>
|
|
||||||
<property name="monospace">True</property>
|
|
||||||
<property name="left_margin">12</property>
|
|
||||||
<property name="right_margin">12</property>
|
|
||||||
<property name="top_margin">12</property>
|
|
||||||
<property name="bottom_margin">12</property>
|
|
||||||
<property name="wrap_mode">word-char</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</interface>
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
title FWS install medium (%ARCH%, UEFI)
|
title Arch Linux (%ARCH%, UEFI)
|
||||||
sort-key 01
|
|
||||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||||
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% udev.event_timeout=45
|
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ zsh
|
|||||||
grml-zsh-config
|
grml-zsh-config
|
||||||
bash
|
bash
|
||||||
zenity
|
zenity
|
||||||
zram-generator
|
|
||||||
nano
|
nano
|
||||||
vim
|
vim
|
||||||
less
|
less
|
||||||
@@ -80,7 +79,6 @@ pacman-contrib
|
|||||||
dmidecode
|
dmidecode
|
||||||
usbutils
|
usbutils
|
||||||
pciutils
|
pciutils
|
||||||
firewalld
|
|
||||||
fastfetch
|
fastfetch
|
||||||
htop
|
htop
|
||||||
|
|
||||||
@@ -105,8 +103,6 @@ open-vm-tools
|
|||||||
|
|
||||||
# --- FWS (fws-local) ---
|
# --- FWS (fws-local) ---
|
||||||
winboat-bin
|
winboat-bin
|
||||||
# Gestionnaire D&D 5e (dés, personnages, grimoire, sessions, combat).
|
|
||||||
fws-dnd
|
|
||||||
|
|
||||||
# --- Installateur Anaconda (fws-local) ---
|
# --- Installateur Anaconda (fws-local) ---
|
||||||
# 'anaconda' tire automatiquement anaconda-widgets + toutes ses dépendances
|
# 'anaconda' tire automatiquement anaconda-widgets + toutes ses dépendances
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
[fws-local]
|
||||||
|
SigLevel = Optional TrustAll
|
||||||
|
Server = file:///mnt/c/Users/Murasaki/Documents/FWS/fws/local-repo
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# /etc/pacman.conf
|
# /etc/pacman.conf
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -24,21 +24,7 @@ file_permissions=(
|
|||||||
["/root/.automated_script.sh"]="0:0:755"
|
["/root/.automated_script.sh"]="0:0:755"
|
||||||
["/root/.gnupg"]="0:0:700"
|
["/root/.gnupg"]="0:0:700"
|
||||||
["/usr/local/bin/choose-mirror"]="0:0:755"
|
["/usr/local/bin/choose-mirror"]="0:0:755"
|
||||||
|
["/usr/local/bin/fws-hw-detect"]="0:0:755"
|
||||||
["/usr/local/bin/Installation_guide"]="0:0:755"
|
["/usr/local/bin/Installation_guide"]="0:0:755"
|
||||||
["/usr/local/bin/livecd-sound"]="0:0:755"
|
["/usr/local/bin/livecd-sound"]="0:0:755"
|
||||||
["/usr/bin/load_policy"]="0:0:755"
|
|
||||||
["/usr/bin/setstatus"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-setup-hardware"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-keys"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-vr-setup"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-hello"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-update"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-updates-check"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-proton-ge"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-recovery"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-recovery-session"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-recovery-gui"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-powermenu"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-about"]="0:0:755"
|
|
||||||
["/etc/grub.d/42_fws-recovery"]="0:0:755"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
LABEL arch
|
||||||
|
MENU LABEL Arch Linux (%ARCH%, BIOS)
|
||||||
|
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||||
|
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||||
|
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
DEFAULT select
|
SERIAL 0 115200
|
||||||
|
UI menu.c32
|
||||||
|
MENU TITLE Arch Linux
|
||||||
|
MENU CLEAR
|
||||||
|
|
||||||
LABEL select
|
DEFAULT arch
|
||||||
COM32 whichsys.c32
|
TIMEOUT 30
|
||||||
APPEND -pxe- pxe -sys- sys -iso- sys
|
|
||||||
|
|
||||||
LABEL pxe
|
INCLUDE syslinux-linux.cfg
|
||||||
CONFIG archiso_pxe.cfg
|
|
||||||
|
|
||||||
LABEL sys
|
|
||||||
CONFIG archiso_sys.cfg
|
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# ============================================================================
|
|
||||||
# FWS — entrée « Réparer FWS » dans le menu GRUB du SYSTÈME INSTALLÉ
|
|
||||||
# (l'équivalent de l'environnement de récupération de Windows).
|
|
||||||
#
|
|
||||||
# Ce script est exécuté par grub-mkconfig (à l'installation via le %post du
|
|
||||||
# kickstart, puis à chaque fws-recovery « Réinstaller GRUB »). Il démarre le
|
|
||||||
# MÊME système sur fws-recovery.target (+ marqueur fws.recovery=1) :
|
|
||||||
# fws-recovery-boot.service prend alors tty1 et lance l'application de
|
|
||||||
# réparation (GUI via startx, repli console).
|
|
||||||
#
|
|
||||||
# Sur le live, ce fichier est inerte (grub-mkconfig n'y tourne jamais).
|
|
||||||
# ============================================================================
|
|
||||||
set -e
|
|
||||||
. /usr/share/grub/grub-mkconfig_lib
|
|
||||||
|
|
||||||
kernel=/boot/vmlinuz-linux
|
|
||||||
# L'initramfs FALLBACK (sans autodetect) : c'est l'entrée de secours, elle
|
|
||||||
# doit démarrer même si le matériel a changé depuis le dernier mkinitcpio.
|
|
||||||
initrd=/boot/initramfs-linux-fallback.img
|
|
||||||
[ -f "$initrd" ] || initrd=/boot/initramfs-linux.img
|
|
||||||
[ -f "$kernel" ] && [ -f "$initrd" ] || exit 0
|
|
||||||
|
|
||||||
# Même logique racine que 10_linux : UUID sauf refus explicite.
|
|
||||||
if [ -n "${GRUB_DEVICE_UUID:-}" ] && [ "x${GRUB_DISABLE_LINUX_UUID:-}" != xtrue ]; then
|
|
||||||
rootarg="root=UUID=$GRUB_DEVICE_UUID"
|
|
||||||
else
|
|
||||||
rootarg="root=$GRUB_DEVICE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
rel_kernel="$(make_system_path_relative_to_its_root "$kernel")"
|
|
||||||
rel_initrd="$(make_system_path_relative_to_its_root "$initrd")"
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
menuentry 'Réparer FWS (récupération du système)' --class recovery {
|
|
||||||
$(prepare_grub_to_access_device "${GRUB_DEVICE_BOOT}" | grub_add_tab)
|
|
||||||
linux $rel_kernel $rootarg rw ${GRUB_CMDLINE_LINUX:-} systemd.unit=fws-recovery.target fws.recovery=1
|
|
||||||
initrd $rel_initrd
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
@@ -1 +1 @@
|
|||||||
fws
|
archiso
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
\S (\l)
|
|
||||||
|
|
||||||
@@ -5,14 +5,5 @@
|
|||||||
# On utilise donc un autre nom.
|
# On utilise donc un autre nom.
|
||||||
_fastfetch_rc=$?
|
_fastfetch_rc=$?
|
||||||
if [ "${_fastfetch_rc}" -eq 0 ]; then
|
if [ "${_fastfetch_rc}" -eq 0 ]; then
|
||||||
# Logo FWS maison (4 carrés + Tux) au lieu du logo Arch détecté par défaut.
|
fastfetch
|
||||||
fastfetch \
|
|
||||||
--logo-type file \
|
|
||||||
--logo /usr/local/share/fws/logo.txt \
|
|
||||||
--logo-color-1 '38;2;30;160;100' \
|
|
||||||
--logo-color-2 '38;2;45;125;210' \
|
|
||||||
--logo-color-3 '38;2;240;180;40' \
|
|
||||||
--logo-color-4 '38;2;25;85;145' \
|
|
||||||
--logo-color-5 '38;2;28;28;34' \
|
|
||||||
--logo-color-6 '38;2;245;165;35'
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
/usr/lib/systemd/system/systemd-timesyncd.service
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/usr/lib/systemd/system/systemd-timesyncd.service
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=FWS - Reparation du systeme (session de secours sur tty1)
|
|
||||||
# Demarre UNIQUEMENT via fws-recovery.target (entree GRUB « Reparer FWS » du
|
|
||||||
# systeme installe : systemd.unit=fws-recovery.target). PAS de [Install] :
|
|
||||||
# enrole dans aucun boot normal, donc son Conflicts=getty@tty1 ne peut jamais
|
|
||||||
# casser le login tty1 d'un boot ordinaire.
|
|
||||||
# Sur le live, c'est /root/.zprofile (autologin tty1) qui gere la reparation.
|
|
||||||
ConditionKernelCommandLine=fws.recovery=1
|
|
||||||
ConditionPathExists=!/run/archiso
|
|
||||||
Conflicts=getty@tty1.service display-manager.service
|
|
||||||
After=systemd-user-sessions.service getty@tty1.service display-manager.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=idle
|
|
||||||
# GUI via startx (openbox + fws-recovery-gui) ; si X ne demarre pas (pilote
|
|
||||||
# video casse — un cas de reparation typique), repli sur l'assistant CONSOLE.
|
|
||||||
ExecStart=/bin/sh -c '/usr/bin/startx /usr/local/bin/fws-recovery-session -- vt1 || exec /usr/local/bin/fws-recovery'
|
|
||||||
StandardInput=tty
|
|
||||||
StandardOutput=tty
|
|
||||||
StandardError=tty
|
|
||||||
TTYPath=/dev/tty1
|
|
||||||
TTYReset=yes
|
|
||||||
TTYVHangup=yes
|
|
||||||
Restart=no
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=FWS - Cible de reparation du systeme
|
|
||||||
# Demandee par l'entree GRUB « Reparer FWS » du systeme installe
|
|
||||||
# (kernel : systemd.unit=fws-recovery.target fws.recovery=1).
|
|
||||||
# Un multi-user complet (reseau pour pacman -Syu) + la session de reparation ;
|
|
||||||
# le Conflicts du service libere tty1 (son start est REQUIS ici, il gagne
|
|
||||||
# contre le Wants de getty@tty1).
|
|
||||||
Requires=multi-user.target fws-recovery-boot.service
|
|
||||||
After=multi-user.target fws-recovery-boot.service
|
|
||||||
AllowIsolate=yes
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
/usr/lib/systemd/system/systemd-time-wait-sync.service
|
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
/usr/lib/systemd/system/systemd-time-wait-sync.service
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
/usr/lib/systemd/system/systemd-timesyncd.service
|
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
/usr/lib/systemd/system/systemd-timesyncd.service
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# FWS — swap compressé en RAM (zram), activé automatiquement par
|
|
||||||
# zram-generator (paquet dans packages.x86_64, aucun service à activer).
|
|
||||||
#
|
|
||||||
# Le LIVE n'a AUCUN swap : sur une machine/VM à 2 Go, Anaconda + le rootfs
|
|
||||||
# squashfs saturent la mémoire → livelock, écran figé pendant « Préparation
|
|
||||||
# de l'installateur ». Le zram (compression ~3:1 sur les tas Python) donne
|
|
||||||
# la marge nécessaire. Copié tel quel sur le SYSTÈME INSTALLÉ par le payload
|
|
||||||
# LiveOS → FWS garde ce filet de sécurité après installation (esprit
|
|
||||||
# « remplaçant de Windows » sur des machines modestes).
|
|
||||||
[zram0]
|
|
||||||
zram-size = min(ram, 4096)
|
|
||||||
compression-algorithm = zstd
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Name=Bienvenue sur FWS
|
|
||||||
Comment=Fenêtre de bienvenue au premier démarrage
|
|
||||||
Exec=fws-hello --autostart
|
|
||||||
Icon=help-about
|
|
||||||
Terminal=false
|
|
||||||
Categories=System;
|
|
||||||
X-GNOME-Autostart-enabled=true
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# FWS — fenêtre « À propos » : deux onglets, « Le projet FWS » (ce qu'est FWS,
|
|
||||||
# version, liens) et « Système » (matériel + logiciel détectés). Remplace le
|
|
||||||
# « About Xfce » de libxfce4ui par une page de marque FWS. GTK3 (python-gobject
|
|
||||||
# installé avec le bureau).
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
import gi
|
|
||||||
|
|
||||||
gi.require_version("Gtk", "3.0")
|
|
||||||
gi.require_version("Gdk", "3.0") # sinon gi charge Gdk 4.0 (conflit avec Gtk3)
|
|
||||||
from gi.repository import Gdk, GLib, Gtk # noqa: E402
|
|
||||||
|
|
||||||
GLib.set_prgname("fws-about")
|
|
||||||
|
|
||||||
|
|
||||||
def _osrelease():
|
|
||||||
data = {}
|
|
||||||
for path in ("/etc/os-release", "/usr/lib/os-release"):
|
|
||||||
try:
|
|
||||||
with open(path) as fh:
|
|
||||||
for line in fh:
|
|
||||||
m = re.match(r'^([A-Z_]+)=(.*)$', line.strip())
|
|
||||||
if m:
|
|
||||||
data[m.group(1)] = m.group(2).strip('"')
|
|
||||||
break
|
|
||||||
except OSError:
|
|
||||||
continue
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def _cmd(*argv):
|
|
||||||
try:
|
|
||||||
return subprocess.check_output(argv, text=True,
|
|
||||||
stderr=subprocess.DEVNULL).strip()
|
|
||||||
except (OSError, subprocess.CalledProcessError):
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def _cpu():
|
|
||||||
try:
|
|
||||||
with open("/proc/cpuinfo") as fh:
|
|
||||||
for line in fh:
|
|
||||||
if line.startswith("model name"):
|
|
||||||
return line.split(":", 1)[1].strip()
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
return "inconnu"
|
|
||||||
|
|
||||||
|
|
||||||
def _mem():
|
|
||||||
try:
|
|
||||||
with open("/proc/meminfo") as fh:
|
|
||||||
for line in fh:
|
|
||||||
if line.startswith("MemTotal"):
|
|
||||||
kib = int(line.split()[1])
|
|
||||||
return f"{kib / 1048576:.1f} Gio"
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
return "inconnu"
|
|
||||||
|
|
||||||
|
|
||||||
def _gpus():
|
|
||||||
out = []
|
|
||||||
for line in _cmd("lspci").splitlines():
|
|
||||||
low = line.lower()
|
|
||||||
if "vga" in low or "3d" in low or "display" in low:
|
|
||||||
out.append(line.split(":", 2)[-1].strip())
|
|
||||||
return out or ["inconnu"]
|
|
||||||
|
|
||||||
|
|
||||||
def _desktop():
|
|
||||||
if os.environ.get("HYPRLAND_INSTANCE_SIGNATURE"):
|
|
||||||
return "Hyprland"
|
|
||||||
env = os.environ.get("XDG_CURRENT_DESKTOP") \
|
|
||||||
or os.environ.get("DESKTOP_SESSION") or ""
|
|
||||||
return env or "inconnu"
|
|
||||||
|
|
||||||
|
|
||||||
def _session_type():
|
|
||||||
t = os.environ.get("XDG_SESSION_TYPE", "")
|
|
||||||
return {"wayland": "Wayland", "x11": "X11"}.get(t, t or "inconnu")
|
|
||||||
|
|
||||||
|
|
||||||
def _disks():
|
|
||||||
out = []
|
|
||||||
for line in _cmd("lsblk", "-dno", "NAME,SIZE,MODEL,TYPE").splitlines():
|
|
||||||
f = line.split()
|
|
||||||
if f and f[-1] == "disk":
|
|
||||||
out.append(" ".join(f[:-1]))
|
|
||||||
return out or ["inconnu"]
|
|
||||||
|
|
||||||
|
|
||||||
def _grid(rows):
|
|
||||||
grid = Gtk.Grid(column_spacing=18, row_spacing=8, margin=18)
|
|
||||||
for i, (key, val) in enumerate(rows):
|
|
||||||
k = Gtk.Label(xalign=1)
|
|
||||||
k.set_markup(f"<b>{GLib.markup_escape_text(key)}</b>")
|
|
||||||
k.get_style_context().add_class("dim-label")
|
|
||||||
v = Gtk.Label(label=val, xalign=0, selectable=True)
|
|
||||||
v.set_line_wrap(True)
|
|
||||||
grid.attach(k, 0, i, 1, 1)
|
|
||||||
grid.attach(v, 1, i, 1, 1)
|
|
||||||
return grid
|
|
||||||
|
|
||||||
|
|
||||||
def _project_tab(osr):
|
|
||||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10, margin=20)
|
|
||||||
title = Gtk.Label()
|
|
||||||
name = osr.get("PRETTY_NAME") or osr.get("NAME") or "FWS Linux"
|
|
||||||
title.set_markup(f"<span size='xx-large' weight='bold'>{name}</span>")
|
|
||||||
box.pack_start(title, False, False, 0)
|
|
||||||
|
|
||||||
tag = Gtk.Label(label="Une distribution Linux pensée pour remplacer "
|
|
||||||
"Windows : bureau prêt à l'emploi, jeux, VR et "
|
|
||||||
"compatibilité applicative.")
|
|
||||||
tag.set_line_wrap(True)
|
|
||||||
tag.set_max_width_chars(52)
|
|
||||||
box.pack_start(tag, False, False, 0)
|
|
||||||
|
|
||||||
feats = Gtk.Label()
|
|
||||||
feats.set_markup(
|
|
||||||
"<b>Inclus :</b> installateur graphique · plusieurs bureaux "
|
|
||||||
"(GNOME, KDE, Hyprland, i3, XFCE) · Steam & Proton-GE · "
|
|
||||||
"réalité virtuelle (SteamVR) · winboat (applications Windows) · "
|
|
||||||
"pare-feu · réparation système intégrée.")
|
|
||||||
feats.set_line_wrap(True)
|
|
||||||
feats.set_max_width_chars(52)
|
|
||||||
feats.set_xalign(0)
|
|
||||||
box.pack_start(feats, False, False, 6)
|
|
||||||
|
|
||||||
home = osr.get("HOME_URL") or "https://github.com/You-re-like-Windows-a-bitch/fws"
|
|
||||||
link = Gtk.LinkButton.new_with_label(home, "Site du projet")
|
|
||||||
box.pack_start(link, False, False, 0)
|
|
||||||
return box
|
|
||||||
|
|
||||||
|
|
||||||
def _system_tab(osr):
|
|
||||||
rows = [
|
|
||||||
("Système", osr.get("PRETTY_NAME", "FWS Linux")),
|
|
||||||
("Build", osr.get("BUILD_ID", "rolling")),
|
|
||||||
("Noyau", _cmd("uname", "-r") or "inconnu"),
|
|
||||||
("Bureau", _desktop()),
|
|
||||||
("Affichage", _session_type()),
|
|
||||||
("Nom de machine", _cmd("hostname") or os.uname().nodename),
|
|
||||||
("Processeur", _cpu()),
|
|
||||||
("Mémoire", _mem()),
|
|
||||||
("Carte(s) graphique(s)", "\n".join(_gpus())),
|
|
||||||
("Disque(s)", "\n".join(_disks())),
|
|
||||||
]
|
|
||||||
scroll = Gtk.ScrolledWindow()
|
|
||||||
scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
|
|
||||||
scroll.add(_grid(rows))
|
|
||||||
return scroll
|
|
||||||
|
|
||||||
|
|
||||||
class AboutWindow(Gtk.Window):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__(title="À propos de FWS")
|
|
||||||
self.set_default_size(520, 460)
|
|
||||||
self.set_position(Gtk.WindowPosition.CENTER)
|
|
||||||
self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
|
|
||||||
|
|
||||||
osr = _osrelease()
|
|
||||||
notebook = Gtk.Notebook()
|
|
||||||
notebook.append_page(_project_tab(osr), Gtk.Label(label="Le projet FWS"))
|
|
||||||
notebook.append_page(_system_tab(osr), Gtk.Label(label="Système"))
|
|
||||||
self.add(notebook)
|
|
||||||
self.connect("destroy", Gtk.main_quit)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
if not Gtk.init_check()[0]:
|
|
||||||
return 1
|
|
||||||
win = AboutWindow()
|
|
||||||
win.show_all()
|
|
||||||
Gtk.main()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# FWS — fenêtre de bienvenue au premier démarrage (pendant post-install de la
|
|
||||||
# page récap de l'installateur). Boutons : mise à jour, codecs, VR, raccourcis,
|
|
||||||
# Proton-GE. GTK3 : présent sur TOUS les bureaux FWS (Firefox le tire) ;
|
|
||||||
# python-gobject est installé avec le bureau par le kickstart.
|
|
||||||
#
|
|
||||||
# Lancement : /etc/xdg/autostart (KDE/GNOME/XFCE) et exec des configs
|
|
||||||
# Hyprland/i3, toujours avec --autostart : on se retire sans bruit si
|
|
||||||
# l'utilisateur a coché « ne plus afficher » (~/.config/fws/hello-done).
|
|
||||||
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import gi
|
|
||||||
|
|
||||||
gi.require_version("Gtk", "3.0")
|
|
||||||
gi.require_version("Gdk", "3.0") # sinon gi charge Gdk 4.0 (conflit avec Gtk3)
|
|
||||||
from gi.repository import Gdk, GLib, Gtk # noqa: E402
|
|
||||||
|
|
||||||
# WM_CLASS déterministe : les règles i3/Hyprland « fenêtre flottante »
|
|
||||||
# ciblent cette classe.
|
|
||||||
GLib.set_prgname("fws-hello")
|
|
||||||
|
|
||||||
_FLAG = os.path.expanduser("~/.config/fws/hello-done")
|
|
||||||
|
|
||||||
_CODECS = ("gst-plugins-base gst-plugins-good gst-plugins-bad "
|
|
||||||
"gst-plugins-ugly gst-libav ffmpeg mpv")
|
|
||||||
|
|
||||||
# (libellé, description, commande, en_terminal)
|
|
||||||
_ACTIONS = [
|
|
||||||
("Mettre à jour le système",
|
|
||||||
"pacman -Syu + nettoyage du cache",
|
|
||||||
"fws-update", True),
|
|
||||||
("Installer les codecs multimédia",
|
|
||||||
"lecture vidéo/audio (GStreamer, ffmpeg, mpv)",
|
|
||||||
f"sudo pacman -S --needed {_CODECS}", True),
|
|
||||||
("Proton-GE pour Steam",
|
|
||||||
"meilleure compatibilité des jeux Windows",
|
|
||||||
"fws-proton-ge", True),
|
|
||||||
("Configurer la VR",
|
|
||||||
"drivers casque : HTC Vive, Index, Quest…",
|
|
||||||
"fws-vr-setup", True),
|
|
||||||
]
|
|
||||||
|
|
||||||
_TERMINALS = ["kitty", "konsole", "gnome-terminal", "xfce4-terminal", "xterm"]
|
|
||||||
|
|
||||||
|
|
||||||
def _terminal():
|
|
||||||
for term in _TERMINALS:
|
|
||||||
if shutil.which(term):
|
|
||||||
return term
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _run(command, in_terminal):
|
|
||||||
if in_terminal:
|
|
||||||
term = _terminal()
|
|
||||||
if term is None:
|
|
||||||
return
|
|
||||||
if term == "gnome-terminal":
|
|
||||||
argv = [term, "--", "sh", "-c", command]
|
|
||||||
else:
|
|
||||||
argv = [term, "-e", "sh", "-c", command]
|
|
||||||
else:
|
|
||||||
argv = ["sh", "-c", command]
|
|
||||||
subprocess.Popen(argv) # noqa: S603 — commandes internes FWS
|
|
||||||
|
|
||||||
|
|
||||||
class HelloWindow(Gtk.Window):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__(title="Bienvenue sur FWS")
|
|
||||||
self.set_default_size(460, -1)
|
|
||||||
self.set_position(Gtk.WindowPosition.CENTER)
|
|
||||||
self.set_border_width(18)
|
|
||||||
# DIALOGUE : les tuileurs (i3/Hyprland) laissent les dialogues en
|
|
||||||
# flottant → la fenêtre garde sa taille naturelle au lieu d'être
|
|
||||||
# étirée sur tout l'espace de travail (layout illisible sinon).
|
|
||||||
self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
|
|
||||||
self.set_resizable(False)
|
|
||||||
|
|
||||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
|
|
||||||
self.add(box)
|
|
||||||
|
|
||||||
title = Gtk.Label()
|
|
||||||
title.set_markup("<span size='x-large' weight='bold'>Bienvenue sur FWS !</span>")
|
|
||||||
box.pack_start(title, False, False, 0)
|
|
||||||
|
|
||||||
subtitle = Gtk.Label(
|
|
||||||
label="Ton système est prêt. Quelques réglages utiles pour bien démarrer :")
|
|
||||||
subtitle.set_line_wrap(True)
|
|
||||||
subtitle.set_xalign(0)
|
|
||||||
box.pack_start(subtitle, False, False, 0)
|
|
||||||
|
|
||||||
actions = list(_ACTIONS)
|
|
||||||
# Raccourcis clavier : uniquement sur les sessions qui ont fws-keys
|
|
||||||
# (Hyprland/i3) — les autres bureaux ont leurs propres réglages.
|
|
||||||
session = os.environ.get("XDG_CURRENT_DESKTOP", "").lower()
|
|
||||||
if os.environ.get("HYPRLAND_INSTANCE_SIGNATURE") or "i3" in session:
|
|
||||||
actions.append(("Voir les raccourcis clavier",
|
|
||||||
"aussi disponible à tout moment : Super+K",
|
|
||||||
"fws-keys", False))
|
|
||||||
|
|
||||||
for label, desc, command, in_term in actions:
|
|
||||||
button = Gtk.Button()
|
|
||||||
inner = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
|
|
||||||
lab = Gtk.Label()
|
|
||||||
lab.set_markup(f"<b>{label}</b>")
|
|
||||||
lab.set_xalign(0)
|
|
||||||
sub = Gtk.Label(label=desc)
|
|
||||||
sub.set_xalign(0)
|
|
||||||
sub.get_style_context().add_class("dim-label")
|
|
||||||
inner.pack_start(lab, False, False, 0)
|
|
||||||
inner.pack_start(sub, False, False, 0)
|
|
||||||
button.add(inner)
|
|
||||||
button.connect("clicked",
|
|
||||||
lambda _b, c=command, t=in_term: _run(c, t))
|
|
||||||
box.pack_start(button, False, False, 0)
|
|
||||||
|
|
||||||
self._again = Gtk.CheckButton(label="Afficher cette fenêtre au prochain démarrage")
|
|
||||||
self._again.set_active(False)
|
|
||||||
box.pack_start(self._again, False, False, 6)
|
|
||||||
|
|
||||||
close = Gtk.Button(label="Fermer")
|
|
||||||
close.connect("clicked", lambda _b: self.close())
|
|
||||||
box.pack_start(close, False, False, 0)
|
|
||||||
|
|
||||||
self.connect("destroy", self._on_destroy)
|
|
||||||
|
|
||||||
def _on_destroy(self, _win):
|
|
||||||
if not self._again.get_active():
|
|
||||||
os.makedirs(os.path.dirname(_FLAG), exist_ok=True)
|
|
||||||
with open(_FLAG, "w") as fh:
|
|
||||||
fh.write("ok\n")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
os.unlink(_FLAG)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
Gtk.main_quit()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
if "--autostart" in sys.argv and os.path.exists(_FLAG):
|
|
||||||
return 0
|
|
||||||
# Pas d'affichage joignable (lancement hors session graphique) : on se
|
|
||||||
# retire sans traceback plutôt que de crasher.
|
|
||||||
if not Gtk.init_check()[0]:
|
|
||||||
return 1
|
|
||||||
win = HelloWindow()
|
|
||||||
win.show_all()
|
|
||||||
Gtk.main()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# FWS — affiche les raccourcis clavier RÉELS de la session (bouton ⌨ de la
|
|
||||||
# barre ou Super+K). Universel :
|
|
||||||
# • Hyprland : « hyprctl binds -j » = les binds effectivement chargés
|
|
||||||
# (descriptions des bindd) — affichage wofi (Wayland).
|
|
||||||
# • i3 : parse ~/.config/i3/config (bindsym, variables résolues)
|
|
||||||
# — affichage rofi (X11).
|
|
||||||
# Aucune dépendance hors python3 + wofi/rofi (déjà installés avec le bureau).
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
_WOFI_STYLE = "/usr/local/share/fws/wofi-keys.css"
|
|
||||||
|
|
||||||
# Bits de modmask Hyprland (masques X11) dans l'ordre d'affichage voulu.
|
|
||||||
_MODS = [(64, "SUPER"), (4, "CTRL"), (8, "ALT"), (1, "SHIFT")]
|
|
||||||
|
|
||||||
_KEY_LABELS = {
|
|
||||||
"mouse:272": "clic gauche",
|
|
||||||
"mouse:273": "clic droit",
|
|
||||||
"mouse:274": "clic molette",
|
|
||||||
"mouse_down": "molette bas",
|
|
||||||
"mouse_up": "molette haut",
|
|
||||||
"button4": "molette haut",
|
|
||||||
"button5": "molette bas",
|
|
||||||
"return": "Entrée",
|
|
||||||
"space": "Espace",
|
|
||||||
"escape": "Échap",
|
|
||||||
"tab": "Tab",
|
|
||||||
"left": "←",
|
|
||||||
"right": "→",
|
|
||||||
"up": "↑",
|
|
||||||
"down": "↓",
|
|
||||||
"print": "Impr écran",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _label(key):
|
|
||||||
return _KEY_LABELS.get(key.lower(), key)
|
|
||||||
|
|
||||||
|
|
||||||
# --- Hyprland : binds réels via hyprctl ------------------------------------
|
|
||||||
def hyprland_lines():
|
|
||||||
raw = subprocess.check_output(["hyprctl", "binds", "-j"], text=True)
|
|
||||||
lines, seen = [], set()
|
|
||||||
for bind in json.loads(raw):
|
|
||||||
parts = [name for bit, name in _MODS if bind.get("modmask", 0) & bit]
|
|
||||||
key = bind.get("key") or (f"code {bind['keycode']}" if bind.get("keycode") else "")
|
|
||||||
parts.append(_label(key))
|
|
||||||
combo = " + ".join(p for p in parts if p)
|
|
||||||
if not combo or combo in seen:
|
|
||||||
continue
|
|
||||||
seen.add(combo)
|
|
||||||
desc = (bind.get("description") or "").strip() \
|
|
||||||
or f"{bind.get('dispatcher', '?')} {(bind.get('arg') or '').strip()}".strip()
|
|
||||||
lines.append(f"{combo:<24} {desc}")
|
|
||||||
return lines
|
|
||||||
|
|
||||||
|
|
||||||
# --- i3 : parse de la config (variables résolues) ----------------------------
|
|
||||||
def i3_lines():
|
|
||||||
path = os.path.expanduser("~/.config/i3/config")
|
|
||||||
variables = {}
|
|
||||||
lines, seen = [], set()
|
|
||||||
with open(path, encoding="utf-8", errors="replace") as fh:
|
|
||||||
content = fh.readlines()
|
|
||||||
for line in content:
|
|
||||||
m = re.match(r"\s*set\s+(\$\S+)\s+(.+)$", line)
|
|
||||||
if m:
|
|
||||||
variables[m.group(1)] = m.group(2).strip()
|
|
||||||
def resolve(text):
|
|
||||||
# $var les plus longs d'abord ($filemanager avant $file…).
|
|
||||||
for var in sorted(variables, key=len, reverse=True):
|
|
||||||
text = text.replace(var, variables[var])
|
|
||||||
return text
|
|
||||||
for line in content:
|
|
||||||
m = re.match(r"\s*bindsym\s+(?:--\S+\s+)*(\S+)\s+(.+)$", line)
|
|
||||||
if not m:
|
|
||||||
continue
|
|
||||||
combo_raw, action = m.groups()
|
|
||||||
combo_raw = resolve(combo_raw).replace("Mod4", "Super").replace("Mod1", "Alt")
|
|
||||||
combo = " + ".join(_label(k) for k in combo_raw.split("+"))
|
|
||||||
if combo in seen:
|
|
||||||
continue
|
|
||||||
seen.add(combo)
|
|
||||||
action = resolve(action)
|
|
||||||
action = re.sub(r"^exec\s+(--no-startup-id\s+)?", "", action).strip().strip('"')
|
|
||||||
lines.append(f"{combo:<24} {action}")
|
|
||||||
return lines
|
|
||||||
|
|
||||||
|
|
||||||
def show(lines):
|
|
||||||
if not lines:
|
|
||||||
lines = ["(aucun raccourci trouvé)"]
|
|
||||||
text = "\n".join(lines)
|
|
||||||
if os.environ.get("HYPRLAND_INSTANCE_SIGNATURE"):
|
|
||||||
cmd = ["wofi", "--dmenu", "--prompt", "Raccourcis clavier — taper pour filtrer",
|
|
||||||
"--width", "820", "--height", "600", "--insensitive",
|
|
||||||
"--cache-file", "/dev/null"]
|
|
||||||
if os.path.exists(_WOFI_STYLE):
|
|
||||||
cmd += ["--style", _WOFI_STYLE]
|
|
||||||
else:
|
|
||||||
# X11/i3 : rofi en dmenu, style assorti (mono pour l'alignement).
|
|
||||||
cmd = ["rofi", "-dmenu", "-i", "-p", "Raccourcis clavier",
|
|
||||||
"-no-custom",
|
|
||||||
"-theme-str", 'window { width: 880px; } listview { lines: 18; }',
|
|
||||||
"-theme-str", '* { font: "Noto Sans Mono 11"; }']
|
|
||||||
subprocess.run(cmd, input=text, text=True, check=False)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
try:
|
|
||||||
if os.environ.get("HYPRLAND_INSTANCE_SIGNATURE"):
|
|
||||||
lines = hyprland_lines()
|
|
||||||
else:
|
|
||||||
lines = i3_lines()
|
|
||||||
except Exception as exc: # noqa: BLE001 — best effort, jamais de crash muet
|
|
||||||
subprocess.run(["notify-send", "Raccourcis FWS", f"Erreur : {exc}"],
|
|
||||||
check=False)
|
|
||||||
return 1
|
|
||||||
show(lines)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# FWS — menu d'alimentation (verrouiller / déconnexion / suspendre /
|
|
||||||
# redémarrer / éteindre). Bouton de la barre ou Super+X. Universel :
|
|
||||||
# • Hyprland (Wayland) → wofi, verrouillage hyprlock, sortie hyprctl.
|
|
||||||
# • i3 (X11) → rofi, verrouillage i3lock, sortie i3-msg exit.
|
|
||||||
# Aucune dépendance hors python3 + wofi/rofi (déjà là avec le bureau).
|
|
||||||
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
_WOFI_STYLE = "/usr/local/share/fws/wofi-style.css"
|
|
||||||
_HYPR = bool(os.environ.get("HYPRLAND_INSTANCE_SIGNATURE"))
|
|
||||||
|
|
||||||
# (libellé affiché, commande shell). L'icône Nerd Font aide au repérage.
|
|
||||||
if _HYPR:
|
|
||||||
_lock = "hyprlock"
|
|
||||||
_logout = "hyprctl dispatch exit"
|
|
||||||
else:
|
|
||||||
_lock = "i3lock -c 1e1e2e"
|
|
||||||
_logout = "i3-msg exit"
|
|
||||||
|
|
||||||
_ITEMS = [
|
|
||||||
(" Verrouiller", _lock),
|
|
||||||
(" Déconnexion", _logout),
|
|
||||||
(" Suspendre", "systemctl suspend"),
|
|
||||||
(" Redémarrer", "systemctl reboot"),
|
|
||||||
(" Éteindre", "systemctl poweroff"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _menu(entries):
|
|
||||||
text = "\n".join(entries)
|
|
||||||
if _HYPR:
|
|
||||||
cmd = ["wofi", "--dmenu", "--prompt", "Alimentation",
|
|
||||||
"--width", "300", "--height", "260", "--cache-file", "/dev/null"]
|
|
||||||
if os.path.exists(_WOFI_STYLE):
|
|
||||||
cmd += ["--style", _WOFI_STYLE]
|
|
||||||
else:
|
|
||||||
cmd = ["rofi", "-dmenu", "-i", "-p", "Alimentation", "-no-custom",
|
|
||||||
"-theme-str", "window { width: 300px; } listview { lines: 5; }"]
|
|
||||||
out = subprocess.run(cmd, input=text, text=True,
|
|
||||||
capture_output=True, check=False)
|
|
||||||
return out.stdout.strip()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
choice = _menu([label for label, _ in _ITEMS])
|
|
||||||
if not choice:
|
|
||||||
return 0
|
|
||||||
for label, command in _ITEMS:
|
|
||||||
if label == choice:
|
|
||||||
subprocess.Popen(["sh", "-c", command]) # noqa: S603,S607
|
|
||||||
break
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# FWS — installe/actualise Proton-GE (GloriousEggroll) pour Steam :
|
|
||||||
# meilleure compatibilité des jeux Windows que le Proton officiel.
|
|
||||||
# S'exécute en UTILISATEUR (installe dans ~/.steam/root/compatibilitytools.d).
|
|
||||||
set -u
|
|
||||||
say() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; }
|
|
||||||
fail() { printf '\033[1;31m ✗ %s\033[0m\n' "$*"; read -rp "Entrée pour fermer. " _; exit 1; }
|
|
||||||
|
|
||||||
[ "$(id -u)" -eq 0 ] && fail "à lancer en utilisateur normal (pas root) : l'outil s'installe dans ton Steam."
|
|
||||||
|
|
||||||
API="https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest"
|
|
||||||
DEST="$HOME/.steam/root/compatibilitytools.d"
|
|
||||||
|
|
||||||
say "Recherche de la dernière version de Proton-GE…"
|
|
||||||
JSON="$(curl -fsSL "$API")" || fail "GitHub inaccessible (réseau ?)"
|
|
||||||
URL="$(printf '%s' "$JSON" | grep -o '"browser_download_url": *"[^"]*\.tar\.gz"' | cut -d'"' -f4 | head -1)"
|
|
||||||
SUM_URL="$(printf '%s' "$JSON" | grep -o '"browser_download_url": *"[^"]*\.sha512sum"' | cut -d'"' -f4 | head -1)"
|
|
||||||
[ -n "$URL" ] || fail "archive introuvable dans la release GitHub"
|
|
||||||
NAME="$(basename "$URL" .tar.gz)"
|
|
||||||
|
|
||||||
if [ -d "$DEST/$NAME" ]; then
|
|
||||||
say "$NAME est déjà installé ✔"
|
|
||||||
read -rp "Entrée pour fermer. " _; exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
TMP="$(mktemp -d)"
|
|
||||||
trap 'rm -rf "$TMP"' EXIT
|
|
||||||
say "Téléchargement de $NAME (~450 Mo)…"
|
|
||||||
curl -fL --progress-bar -o "$TMP/$NAME.tar.gz" "$URL" || fail "téléchargement échoué"
|
|
||||||
|
|
||||||
if [ -n "$SUM_URL" ]; then
|
|
||||||
say "Vérification de l'intégrité (sha512)…"
|
|
||||||
curl -fsSL -o "$TMP/sum" "$SUM_URL" \
|
|
||||||
&& (cd "$TMP" && sed "s|[^ ]*\.tar\.gz|$NAME.tar.gz|" sum | sha512sum -c -) \
|
|
||||||
|| fail "somme de contrôle invalide — archive corrompue ?"
|
|
||||||
fi
|
|
||||||
|
|
||||||
say "Installation dans $DEST…"
|
|
||||||
mkdir -p "$DEST"
|
|
||||||
tar -xzf "$TMP/$NAME.tar.gz" -C "$DEST" || fail "extraction échouée"
|
|
||||||
|
|
||||||
say "$NAME installé ✔"
|
|
||||||
echo " Dans Steam : Paramètres → Compatibilité → activer, puis choisir"
|
|
||||||
echo " « $NAME » (ou par jeu : Propriétés → Compatibilité)."
|
|
||||||
echo " Redémarre Steam pour qu'il apparaisse."
|
|
||||||
read -rp "Entrée pour fermer. " _
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# ============================================================================
|
|
||||||
# FWS — moteur de réparation d'une installation existante depuis le live
|
|
||||||
# (« point de restauration à la Windows »).
|
|
||||||
#
|
|
||||||
# L'interface GRAPHIQUE est fws-recovery-gui (GTK3 python — le live embarque
|
|
||||||
# python-gobject/gtk3 via Anaconda) ; elle pilote CE script en sous-commandes.
|
|
||||||
# Sans argument : assistant CONSOLE (repli sans X, ou usage à la main).
|
|
||||||
#
|
|
||||||
# Sous-commandes (pour la GUI / scripts) :
|
|
||||||
# scan → une ligne « périphérique|nom » par install FWS
|
|
||||||
# repair <action> <dev> → monte, répare, démonte ; sortie en continu
|
|
||||||
# action ∈ grub|initramfs|pacman|pacman-update|all
|
|
||||||
# shell <dev> → terminal DANS le système (xterm si DISPLAY)
|
|
||||||
#
|
|
||||||
# arch-chroot (arch-install-scripts, présent sur le live) gère les binds.
|
|
||||||
#
|
|
||||||
# Fonctionne depuis le LIVE (autre disque) ET depuis le SYSTÈME INSTALLÉ
|
|
||||||
# lui-même (menu d'applications / entrée GRUB « Réparer FWS ») : si la cible
|
|
||||||
# est la racine en cours d'exécution, on opère directement, sans mount/chroot.
|
|
||||||
# ============================================================================
|
|
||||||
set -u
|
|
||||||
MNT=/mnt/fws-recovery
|
|
||||||
ROOTDEV=""
|
|
||||||
DO_UPDATE=""
|
|
||||||
SELF="" # cible = système courant
|
|
||||||
ROOTSRC="$(findmnt -no SOURCE / 2>/dev/null)" # sur le live : overlay → jamais un lsblk
|
|
||||||
|
|
||||||
say() { printf '\n==> %s\n' "$*"; }
|
|
||||||
warn() { printf ' ⚠ %s\n' "$*"; }
|
|
||||||
|
|
||||||
[ "$(id -u)" -eq 0 ] || { echo "root requis"; exit 1; }
|
|
||||||
|
|
||||||
cleanup() { [ -n "$MNT" ] && [ "$MNT" != "/" ] && umount -R "$MNT" 2>/dev/null; }
|
|
||||||
|
|
||||||
# Exécute DANS la cible : chroot si elle est montée, directement si c'est nous.
|
|
||||||
in_target() {
|
|
||||||
if [ -n "$SELF" ]; then "$@"; else arch-chroot "$MNT" "$@"; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ── Moteur ───────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
scan_installs() {
|
|
||||||
vgscan --mknodes >/dev/null 2>&1
|
|
||||||
vgchange -ay >/dev/null 2>&1
|
|
||||||
udevadm settle --timeout=10 >/dev/null 2>&1
|
|
||||||
# Le système en cours d'exécution d'abord (app lancée depuis l'OS installé).
|
|
||||||
if [ ! -d /run/archiso ] && grep -qiE '^(ID|NAME)=.*fws' /etc/os-release 2>/dev/null; then
|
|
||||||
printf '%s|%s (ce système)\n' "$ROOTSRC" \
|
|
||||||
"$(. /etc/os-release 2>/dev/null; echo "${PRETTY_NAME:-FWS}")"
|
|
||||||
fi
|
|
||||||
mkdir -p "$MNT"
|
|
||||||
local dev name
|
|
||||||
while read -r dev; do
|
|
||||||
[ -b "$dev" ] || continue
|
|
||||||
[ "$dev" = "$ROOTSRC" ] && continue # déjà listé ci-dessus
|
|
||||||
mount -o ro "$dev" "$MNT" 2>/dev/null || continue
|
|
||||||
if [ -f "$MNT/etc/os-release" ] && [ -d "$MNT/boot" ] \
|
|
||||||
&& grep -qiE '^(ID|NAME)=.*fws' "$MNT/etc/os-release" 2>/dev/null; then
|
|
||||||
name="$(. "$MNT/etc/os-release" 2>/dev/null; echo "${PRETTY_NAME:-FWS}")"
|
|
||||||
printf '%s|%s\n' "$dev" "$name"
|
|
||||||
fi
|
|
||||||
umount "$MNT" 2>/dev/null
|
|
||||||
done < <(lsblk -rpno NAME,TYPE | awk '$2=="part"||$2=="lvm"{print $1}')
|
|
||||||
}
|
|
||||||
|
|
||||||
# Monte la cible + /boot + ESP d'après son fstab.
|
|
||||||
# Cas particulier : la cible EST la racine en cours d'exécution (app lancée
|
|
||||||
# depuis l'OS installé, ou entrée GRUB « Réparer FWS ») → tout est déjà là,
|
|
||||||
# on opère en place (MNT="" : les chemins "$MNT/…" pointent sur la vraie racine).
|
|
||||||
mount_target() {
|
|
||||||
ROOTDEV="$1"
|
|
||||||
if [ "$ROOTDEV" = "$ROOTSRC" ] && [ ! -d /run/archiso ]; then
|
|
||||||
SELF=1; MNT=""
|
|
||||||
echo " cible = système en cours d'exécution (pas de montage)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
mkdir -p "$MNT"
|
|
||||||
mount "$ROOTDEV" "$MNT" || return 1
|
|
||||||
local mp src
|
|
||||||
for mp in /boot /boot/efi; do
|
|
||||||
src="$(awk -v mp="$mp" '$2==mp && $1!~/^#/{print $1; exit}' "$MNT/etc/fstab" 2>/dev/null)"
|
|
||||||
[ -n "$src" ] || continue
|
|
||||||
mount "$(findfs "$src" 2>/dev/null || echo "$src")" "$MNT$mp" 2>/dev/null \
|
|
||||||
&& echo " $mp monté" || warn "$mp non monté (on continue)"
|
|
||||||
done
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fix_grub() {
|
|
||||||
say "Réinstallation de GRUB…"
|
|
||||||
if [ -d /sys/firmware/efi ]; then
|
|
||||||
in_target grub-install --target=x86_64-efi \
|
|
||||||
--efi-directory=/boot/efi --bootloader-id=FWS --recheck || true
|
|
||||||
in_target grub-install --target=x86_64-efi \
|
|
||||||
--efi-directory=/boot/efi --bootloader-id=FWS --removable --recheck
|
|
||||||
else
|
|
||||||
local disk
|
|
||||||
disk="$(lsblk -spno NAME,TYPE "$ROOTDEV" 2>/dev/null \
|
|
||||||
| awk '$2=="disk"{print $1; exit}' | grep -oE '/dev/[a-z0-9]+')"
|
|
||||||
[ -n "$disk" ] || { warn "disque BIOS introuvable"; return 1; }
|
|
||||||
in_target grub-install --target=i386-pc --recheck "$disk"
|
|
||||||
fi
|
|
||||||
in_target grub-mkconfig -o /boot/grub/grub.cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
fix_initramfs() {
|
|
||||||
say "Régénération de l'initramfs (mkinitcpio -P)…"
|
|
||||||
echo " NB : les avertissements « Possibly missing firmware for module … » sont"
|
|
||||||
echo " NORMAUX sur Arch : firmware de matériel exotique (cartes SCSI/fibre…)"
|
|
||||||
echo " non fourni par linux-firmware. Sans effet, sauf si tu AS ce matériel."
|
|
||||||
in_target mkinitcpio -P
|
|
||||||
}
|
|
||||||
|
|
||||||
fix_pacman() {
|
|
||||||
say "Réparation de pacman…"
|
|
||||||
rm -f "$MNT/var/lib/pacman/db.lck" && echo " verrou db.lck retiré (s'il existait)"
|
|
||||||
in_target pacman-key --init
|
|
||||||
in_target pacman-key --populate archlinux
|
|
||||||
if [ -n "$DO_UPDATE" ]; then
|
|
||||||
say "Mise à jour complète (pacman -Syu)…"
|
|
||||||
[ -z "$SELF" ] && cp -f /etc/resolv.conf "$MNT/etc/resolv.conf" 2>/dev/null
|
|
||||||
in_target pacman -Syu --noconfirm
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
fix_all() { fix_initramfs && fix_grub; }
|
|
||||||
|
|
||||||
# ── Sous-commandes (pilotées par fws-recovery-gui) ───────────────────────────
|
|
||||||
case "${1:-}" in
|
|
||||||
scan)
|
|
||||||
scan_installs
|
|
||||||
exit 0 ;;
|
|
||||||
repair)
|
|
||||||
action="${2:?action manquante}"; dev="${3:?périphérique manquant}"
|
|
||||||
trap cleanup EXIT
|
|
||||||
say "Montage de $dev…"
|
|
||||||
mount_target "$dev" || { warn "montage impossible"; exit 1; }
|
|
||||||
rc=0
|
|
||||||
case "$action" in
|
|
||||||
grub) fix_grub || rc=$? ;;
|
|
||||||
initramfs) fix_initramfs || rc=$? ;;
|
|
||||||
pacman) fix_pacman || rc=$? ;;
|
|
||||||
pacman-update) DO_UPDATE=1; fix_pacman || rc=$? ;;
|
|
||||||
all) fix_all || rc=$? ;;
|
|
||||||
*) warn "action inconnue : $action"; rc=2 ;;
|
|
||||||
esac
|
|
||||||
cleanup; trap - EXIT
|
|
||||||
[ "$rc" -eq 0 ] && say "Terminé ✔" || say "Échec (code $rc)"
|
|
||||||
exit "$rc" ;;
|
|
||||||
shell)
|
|
||||||
dev="${2:?périphérique manquant}"
|
|
||||||
trap cleanup EXIT
|
|
||||||
mount_target "$dev" || { warn "montage impossible"; exit 1; }
|
|
||||||
if [ -z "$SELF" ] && [ -n "${DISPLAY:-}" ] && command -v xterm >/dev/null 2>&1; then
|
|
||||||
xterm -title "FWS — terminal dans le système (chroot)" \
|
|
||||||
-e arch-chroot "$MNT" /bin/bash
|
|
||||||
else
|
|
||||||
in_target /bin/bash
|
|
||||||
fi
|
|
||||||
cleanup; trap - EXIT
|
|
||||||
exit 0 ;;
|
|
||||||
""|tui) : ;; # assistant console ci-dessous
|
|
||||||
*) echo "usage : fws-recovery [scan|repair <action> <dev>|shell <dev>]"; exit 2 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# ── Assistant CONSOLE (repli sans X / usage manuel) ──────────────────────────
|
|
||||||
trap cleanup EXIT
|
|
||||||
say "Recherche des installations FWS…"
|
|
||||||
mapfile -t CANDIDATES < <(scan_installs)
|
|
||||||
|
|
||||||
if [ "${#CANDIDATES[@]}" -eq 0 ]; then
|
|
||||||
warn "Aucune installation FWS trouvée. Disques visibles :"
|
|
||||||
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
|
|
||||||
read -rp "Entrée pour ouvrir un shell (exit pour redémarrer). " _
|
|
||||||
bash; exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo; i=1
|
|
||||||
for c in "${CANDIDATES[@]}"; do echo " $i) ${c%%|*} — ${c##*|}"; i=$((i+1)); done
|
|
||||||
read -rp "Installation à réparer [1] : " pick; pick="${pick:-1}"
|
|
||||||
dev="${CANDIDATES[$((pick-1))]%%|*}"
|
|
||||||
[ -n "$dev" ] || exit 1
|
|
||||||
say "Montage de $dev…"
|
|
||||||
mount_target "$dev" || { warn "montage impossible"; exit 1; }
|
|
||||||
|
|
||||||
while :; do
|
|
||||||
cat <<'MENU'
|
|
||||||
|
|
||||||
╔════════════════════════════════════════════════════════╗
|
|
||||||
║ FWS — Réparation du système ║
|
|
||||||
╠════════════════════════════════════════════════════════╣
|
|
||||||
║ 1) Réinstaller GRUB (le système ne démarre plus) ║
|
|
||||||
║ 2) Régénérer l'initramfs (mkinitcpio -P) ║
|
|
||||||
║ 3) Réparer pacman (verrou, clés, mise à jour) ║
|
|
||||||
║ 4) Tout réparer (initramfs + GRUB) ║
|
|
||||||
║ 5) Ouvrir un shell DANS le système (chroot) ║
|
|
||||||
║ q) Quitter et redémarrer ║
|
|
||||||
╚════════════════════════════════════════════════════════╝
|
|
||||||
MENU
|
|
||||||
read -rp " Choix : " choice
|
|
||||||
case "$choice" in
|
|
||||||
1) fix_grub ;;
|
|
||||||
2) fix_initramfs ;;
|
|
||||||
3) DO_UPDATE=""
|
|
||||||
read -rp " Mise à jour complète aussi (réseau) ? [o/N] " _u
|
|
||||||
case "$_u" in o|O|oui) DO_UPDATE=1 ;; esac
|
|
||||||
fix_pacman ;;
|
|
||||||
4) fix_all ;;
|
|
||||||
5) say "Shell chroot — « exit » pour revenir au menu."
|
|
||||||
in_target /bin/bash ;;
|
|
||||||
q|Q) break ;;
|
|
||||||
*) warn "choix inconnu" ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
cleanup; trap - EXIT
|
|
||||||
say "Réparation terminée."
|
|
||||||
read -rp "Redémarrer maintenant ? [O/n] " _r
|
|
||||||
case "$_r" in n|N) bash ;; *) systemctl reboot ;; esac
|
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# FWS — application GRAPHIQUE de réparation du système (mode « Réparer FWS »
|
|
||||||
# du live). GTK3 : le live embarque python-gobject/gtk3 via Anaconda.
|
|
||||||
#
|
|
||||||
# Pilote le MOTEUR fws-recovery en sous-commandes :
|
|
||||||
# fws-recovery scan → détecte les installations FWS
|
|
||||||
# fws-recovery repair <action> <dev> → répare (sortie streamée ici)
|
|
||||||
# fws-recovery shell <dev> → terminal chroot (xterm)
|
|
||||||
#
|
|
||||||
# Deux contextes de lancement :
|
|
||||||
# • mode « Réparer FWS » (live OU entrée GRUB de l'OS installé) : session
|
|
||||||
# root via fws-recovery-session → le moteur est appelé directement ;
|
|
||||||
# • menu d'applications de l'OS installé : lancée en UTILISATEUR → chaque
|
|
||||||
# appel du moteur passe par pkexec (politique org.fws.recovery,
|
|
||||||
# auth_admin_keep : un seul mot de passe pour toute la session).
|
|
||||||
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import gi
|
|
||||||
|
|
||||||
gi.require_version("Gtk", "3.0")
|
|
||||||
from gi.repository import GLib, Gtk, Pango # noqa: E402
|
|
||||||
|
|
||||||
GLib.set_prgname("fws-recovery-gui")
|
|
||||||
|
|
||||||
_ENGINE = "/usr/local/bin/fws-recovery"
|
|
||||||
|
|
||||||
|
|
||||||
def _engine_cmd(args):
|
|
||||||
"""Commande moteur, élevée via pkexec si on n'est pas root."""
|
|
||||||
cmd = [_ENGINE] + args
|
|
||||||
if os.geteuid() != 0:
|
|
||||||
cmd = ["pkexec"] + cmd
|
|
||||||
return cmd
|
|
||||||
|
|
||||||
_ACTIONS = [
|
|
||||||
("grub", "Réinstaller GRUB",
|
|
||||||
"le système ne démarre plus / « Operating System not found »"),
|
|
||||||
("initramfs", "Régénérer l'initramfs",
|
|
||||||
"kernel panic au démarrage, module manquant (mkinitcpio -P)"),
|
|
||||||
("pacman", "Réparer pacman",
|
|
||||||
"verrou bloqué, clés de signature cassées"),
|
|
||||||
("pacman-update", "Réparer pacman + mise à jour complète",
|
|
||||||
"idem, puis pacman -Syu (réseau requis)"),
|
|
||||||
("all", "Tout réparer",
|
|
||||||
"initramfs puis GRUB — le réflexe « ça ne boote plus »"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class RecoveryWindow(Gtk.Window):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__(title="FWS — Réparation du système")
|
|
||||||
self.set_icon_name("fws-recovery")
|
|
||||||
self.set_default_size(720, 560)
|
|
||||||
self.set_position(Gtk.WindowPosition.CENTER)
|
|
||||||
self.set_border_width(18)
|
|
||||||
self.connect("destroy", Gtk.main_quit)
|
|
||||||
|
|
||||||
self._proc = None
|
|
||||||
self._installs = [] # [(dev, nom), …]
|
|
||||||
self._action_buttons = []
|
|
||||||
|
|
||||||
root = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
|
|
||||||
self.add(root)
|
|
||||||
|
|
||||||
title = Gtk.Label()
|
|
||||||
title.set_markup("<span size='x-large' weight='bold'>Réparation du système FWS</span>")
|
|
||||||
root.pack_start(title, False, False, 0)
|
|
||||||
|
|
||||||
self._status = Gtk.Label(label="Recherche des installations FWS sur les disques…")
|
|
||||||
self._status.set_xalign(0)
|
|
||||||
root.pack_start(self._status, False, False, 0)
|
|
||||||
|
|
||||||
# Sélecteur d'installation (rempli après le scan).
|
|
||||||
picker_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
|
|
||||||
picker_row.pack_start(Gtk.Label(label="Système à réparer :"), False, False, 0)
|
|
||||||
self._picker = Gtk.ComboBoxText()
|
|
||||||
picker_row.pack_start(self._picker, True, True, 0)
|
|
||||||
root.pack_start(picker_row, False, False, 0)
|
|
||||||
|
|
||||||
# Boutons d'action (style fws-hello : titre + description).
|
|
||||||
for key, label, desc in _ACTIONS:
|
|
||||||
button = Gtk.Button()
|
|
||||||
inner = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
|
|
||||||
lab = Gtk.Label()
|
|
||||||
lab.set_markup(f"<b>{label}</b>")
|
|
||||||
lab.set_xalign(0)
|
|
||||||
sub = Gtk.Label(label=desc)
|
|
||||||
sub.set_xalign(0)
|
|
||||||
sub.get_style_context().add_class("dim-label")
|
|
||||||
inner.pack_start(lab, False, False, 0)
|
|
||||||
inner.pack_start(sub, False, False, 0)
|
|
||||||
button.add(inner)
|
|
||||||
button.connect("clicked", self._on_action, key)
|
|
||||||
root.pack_start(button, False, False, 0)
|
|
||||||
self._action_buttons.append(button)
|
|
||||||
|
|
||||||
shell_btn = Gtk.Button(label="Ouvrir un terminal dans le système (chroot)")
|
|
||||||
shell_btn.connect("clicked", self._on_shell)
|
|
||||||
root.pack_start(shell_btn, False, False, 0)
|
|
||||||
self._action_buttons.append(shell_btn)
|
|
||||||
|
|
||||||
# Journal (sortie du moteur, en continu).
|
|
||||||
frame = Gtk.Frame(label="Journal")
|
|
||||||
scroll = Gtk.ScrolledWindow()
|
|
||||||
scroll.set_min_content_height(150)
|
|
||||||
self._log = Gtk.TextView()
|
|
||||||
self._log.set_editable(False)
|
|
||||||
self._log.set_cursor_visible(False)
|
|
||||||
self._log.set_monospace(True)
|
|
||||||
self._log.modify_font(Pango.FontDescription("Monospace 9"))
|
|
||||||
scroll.add(self._log)
|
|
||||||
frame.add(scroll)
|
|
||||||
root.pack_start(frame, True, True, 0)
|
|
||||||
|
|
||||||
# Pied : redémarrer / éteindre.
|
|
||||||
footer = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
|
|
||||||
reboot = Gtk.Button(label="Redémarrer")
|
|
||||||
reboot.connect("clicked", lambda _b: self._power("reboot"))
|
|
||||||
poweroff = Gtk.Button(label="Éteindre")
|
|
||||||
poweroff.connect("clicked", lambda _b: self._power("poweroff"))
|
|
||||||
footer.pack_end(reboot, False, False, 0)
|
|
||||||
footer.pack_end(poweroff, False, False, 0)
|
|
||||||
root.pack_start(footer, False, False, 0)
|
|
||||||
|
|
||||||
self._set_busy(True)
|
|
||||||
GLib.idle_add(self._start_scan)
|
|
||||||
|
|
||||||
# ── Utilitaires ─────────────────────────────────────────────────────────
|
|
||||||
def _append(self, text):
|
|
||||||
# Rebranding d'affichage : les outils sous-jacents parlent
|
|
||||||
# d'« Arch Linux » — pas le journal FWS. (Les identifiants techniques
|
|
||||||
# comme archlinux-keyring restent tels quels : ce sont de vrais noms
|
|
||||||
# de fichiers/paquets, les masquer fausserait le diagnostic.)
|
|
||||||
text = text.replace("Arch Linux", "FWS").replace("Fedora", "FWS")
|
|
||||||
buf = self._log.get_buffer()
|
|
||||||
buf.insert(buf.get_end_iter(), text)
|
|
||||||
mark = buf.create_mark(None, buf.get_end_iter(), False)
|
|
||||||
self._log.scroll_mark_onscreen(mark)
|
|
||||||
buf.delete_mark(mark)
|
|
||||||
|
|
||||||
def _set_busy(self, busy):
|
|
||||||
for b in self._action_buttons:
|
|
||||||
b.set_sensitive(not busy and bool(self._installs))
|
|
||||||
# Sensible même avec UNE seule install : un combo grisé donne
|
|
||||||
# l'impression que TOUTE l'app est désactivée (retour utilisateur).
|
|
||||||
self._picker.set_sensitive(not busy and bool(self._installs))
|
|
||||||
|
|
||||||
def _selected_dev(self):
|
|
||||||
idx = self._picker.get_active()
|
|
||||||
if 0 <= idx < len(self._installs):
|
|
||||||
return self._installs[idx][0]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _power(self, what):
|
|
||||||
subprocess.run(["systemctl", what], check=False)
|
|
||||||
|
|
||||||
# ── Scan des installations ──────────────────────────────────────────────
|
|
||||||
def _start_scan(self):
|
|
||||||
self._run_engine(["scan"], self._scan_done, quiet=True)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _scan_done(self, rc, output):
|
|
||||||
self._installs = []
|
|
||||||
for line in output.splitlines():
|
|
||||||
if "|" in line:
|
|
||||||
dev, _, name = line.partition("|")
|
|
||||||
self._installs.append((dev.strip(), name.strip()))
|
|
||||||
self._picker.append_text(f"{name.strip()} — {dev.strip()}")
|
|
||||||
if self._installs:
|
|
||||||
self._picker.set_active(0)
|
|
||||||
n = len(self._installs)
|
|
||||||
self._status.set_markup(
|
|
||||||
f"<b>{n}</b> installation{'s' if n > 1 else ''} FWS détectée{'s' if n > 1 else ''}."
|
|
||||||
" Choisis une réparation :")
|
|
||||||
elif rc in (126, 127):
|
|
||||||
# pkexec : authentification annulée ou refusée.
|
|
||||||
self._status.set_markup(
|
|
||||||
"<b>Authentification annulée.</b> Ferme puis relance"
|
|
||||||
" l'application pour réessayer.")
|
|
||||||
else:
|
|
||||||
self._status.set_markup(
|
|
||||||
"<b>Aucune installation FWS détectée.</b> Vérifie les disques"
|
|
||||||
" (terminal ci-dessous) ou redémarre.")
|
|
||||||
self._append("Aucune installation FWS trouvée.\n\n"
|
|
||||||
+ subprocess.run(["lsblk", "-o", "NAME,SIZE,TYPE,FSTYPE"],
|
|
||||||
capture_output=True, text=True,
|
|
||||||
check=False).stdout)
|
|
||||||
self._set_busy(False)
|
|
||||||
# Le terminal reste utile même sans installation détectée (diagnostic).
|
|
||||||
self._action_buttons[-1].set_sensitive(True)
|
|
||||||
|
|
||||||
# ── Exécution du moteur avec sortie en continu ──────────────────────────
|
|
||||||
def _run_engine(self, args, done_cb, quiet=False):
|
|
||||||
self._set_busy(True)
|
|
||||||
self._proc = subprocess.Popen(
|
|
||||||
_engine_cmd(args), stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.STDOUT, text=True, bufsize=1)
|
|
||||||
chunks = []
|
|
||||||
|
|
||||||
def on_io(source, _cond):
|
|
||||||
line = source.readline()
|
|
||||||
if line:
|
|
||||||
chunks.append(line)
|
|
||||||
if not quiet:
|
|
||||||
self._append(line)
|
|
||||||
return True
|
|
||||||
rc = self._proc.wait()
|
|
||||||
done_cb(rc, "".join(chunks))
|
|
||||||
return False
|
|
||||||
|
|
||||||
GLib.io_add_watch(self._proc.stdout,
|
|
||||||
GLib.IO_IN | GLib.IO_HUP, on_io)
|
|
||||||
|
|
||||||
def _on_action(self, _button, key):
|
|
||||||
dev = self._selected_dev()
|
|
||||||
if not dev:
|
|
||||||
return
|
|
||||||
label = next(lab for k, lab, _d in _ACTIONS if k == key)
|
|
||||||
self._status.set_markup(f"<b>{label}</b> sur {dev} — en cours…")
|
|
||||||
self._append(f"\n───── {label} ({dev}) ─────\n")
|
|
||||||
self._run_engine(["repair", key, dev], self._action_done)
|
|
||||||
|
|
||||||
def _action_done(self, rc, _output):
|
|
||||||
if rc == 0:
|
|
||||||
self._status.set_markup("<b>Terminé ✔</b> — autre réparation, ou redémarre.")
|
|
||||||
elif rc in (126, 127):
|
|
||||||
self._status.set_markup(
|
|
||||||
"<b>Authentification annulée</b> — rien n'a été modifié.")
|
|
||||||
else:
|
|
||||||
self._status.set_markup(
|
|
||||||
f"<b>Échec (code {rc})</b> — détail dans le journal ci-dessous.")
|
|
||||||
self._set_busy(False)
|
|
||||||
|
|
||||||
def _on_shell(self, _button):
|
|
||||||
dev = self._selected_dev()
|
|
||||||
if dev and os.geteuid() == 0:
|
|
||||||
subprocess.Popen([_ENGINE, "shell", dev])
|
|
||||||
elif dev:
|
|
||||||
# pkexec purge DISPLAY → le moteur ouvre le chroot DANS cet xterm
|
|
||||||
# (lancé en utilisateur, donc visible sur X11 comme Wayland).
|
|
||||||
subprocess.Popen(["xterm", "-title",
|
|
||||||
"FWS — terminal dans le système (chroot)",
|
|
||||||
"-e", "pkexec", _ENGINE, "shell", dev])
|
|
||||||
elif shutil.which("xterm"):
|
|
||||||
subprocess.Popen(["xterm", "-title", "FWS — terminal"])
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
if not Gtk.init_check()[0]:
|
|
||||||
# Pas d'affichage : le .zprofile retombera sur l'assistant console.
|
|
||||||
return 1
|
|
||||||
if os.geteuid() != 0 and not shutil.which("pkexec"):
|
|
||||||
dlg = Gtk.MessageDialog(message_type=Gtk.MessageType.ERROR,
|
|
||||||
buttons=Gtk.ButtonsType.CLOSE,
|
|
||||||
text="pkexec introuvable : lance fws-recovery-gui en root.")
|
|
||||||
dlg.run()
|
|
||||||
return 1
|
|
||||||
win = RecoveryWindow()
|
|
||||||
win.show_all()
|
|
||||||
Gtk.main()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# FWS — session X du mode réparation. Lancé par startx (cf. /root/.zprofile,
|
|
||||||
# entrée de boot « Réparer FWS »). Openbox minimal + l'application GTK
|
|
||||||
# fws-recovery-gui. Si la GUI meurt, openbox reste pour un xterm de secours.
|
|
||||||
xsetroot -solid "#1a1a2e" 2>/dev/null
|
|
||||||
openbox &
|
|
||||||
fws-recovery-gui
|
|
||||||
exec openbox
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# ============================================================================
|
|
||||||
# FWS — détection matérielle + drivers + stack « remplacement de Windows ».
|
|
||||||
#
|
|
||||||
# Appelé par le %post du kickstart : fws-setup-hardware <bureau>
|
|
||||||
# (<bureau> = cli|gnome|kde|xfce|hyprland). Réseau requis ; CHAQUE étape est
|
|
||||||
# NON bloquante (pas de set -e) : un paquet manquant ou pas de réseau ne casse
|
|
||||||
# pas l'installation.
|
|
||||||
#
|
|
||||||
# Installe :
|
|
||||||
# - les drivers GPU détectés via lspci (Intel / AMD / NVIDIA) + Mesa/Vulkan,
|
|
||||||
# - les outils invité selon l'hyperviseur (VMware/VirtualBox/QEMU/Hyper-V),
|
|
||||||
# - le son/micro/casque (PipeWire), le Bluetooth (BlueZ), les conteneurs
|
|
||||||
# (Podman),
|
|
||||||
# - et — si un bureau est choisi — la stack « comme Windows » : Steam, Wine,
|
|
||||||
# VR (Monado/OpenXR → SteamVR pour HTC Vive / Valve Index ; Oculus n'a pas
|
|
||||||
# de support Linux officiel), winboat-bin (apps Windows).
|
|
||||||
# ============================================================================
|
|
||||||
DESK="${1:-cli}"
|
|
||||||
log() { echo "[FWS-hw] $*"; }
|
|
||||||
|
|
||||||
# ── 1) Drivers GPU (détection lspci) ─────────────────────────────────────────
|
|
||||||
DRIVERS="mesa"
|
|
||||||
GPU="$(lspci 2>/dev/null | grep -iE 'VGA compatible controller|3D controller|Display controller')"
|
|
||||||
case "$GPU" in
|
|
||||||
*NVIDIA*|*GeForce*) DRIVERS="$DRIVERS nvidia-dkms nvidia-utils linux-headers dkms" ;;
|
|
||||||
*AMD*|*ATI*|*Radeon*) DRIVERS="$DRIVERS vulkan-radeon libva-mesa-driver" ;;
|
|
||||||
*Intel*) DRIVERS="$DRIVERS vulkan-intel intel-media-driver" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# ── 2) Outils invité selon l'hyperviseur ─────────────────────────────────────
|
|
||||||
VMSVC=""
|
|
||||||
case "$(systemd-detect-virt 2>/dev/null)" in
|
|
||||||
vmware) DRIVERS="$DRIVERS open-vm-tools"; VMSVC="vmtoolsd vmware-vmblock-fuse" ;;
|
|
||||||
oracle) DRIVERS="$DRIVERS virtualbox-guest-utils"; VMSVC="vboxservice" ;;
|
|
||||||
qemu|kvm) DRIVERS="$DRIVERS qemu-guest-agent spice-vdagent"; VMSVC="qemu-guest-agent spice-vdagentd" ;;
|
|
||||||
microsoft) DRIVERS="$DRIVERS hyperv"; VMSVC="hv_fcopy_daemon hv_kvp_daemon hv_vss_daemon" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# ── 3) Stack de base : son / micro / casque, Bluetooth, luminosité, conteneurs
|
|
||||||
# brightnessctl : réglage de luminosité sans root (via logind) — consommé par
|
|
||||||
# les touches XF86MonBrightness* et la molette du module backlight de waybar.
|
|
||||||
BASE="pipewire pipewire-alsa pipewire-pulse pipewire-jack wireplumber \
|
|
||||||
pavucontrol bluez bluez-utils brightnessctl podman"
|
|
||||||
# Gestionnaire Bluetooth graphique : KDE et GNOME ont le leur ; blueman pour
|
|
||||||
# les bureaux qui n'en fournissent pas (tray + assistant d'appairage).
|
|
||||||
case "$DESK" in
|
|
||||||
hyprland|xfce|i3) BASE="$BASE blueman" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# ── 4) Stack desktop : jeux, Windows-compat, VR (si un bureau est choisi) ────
|
|
||||||
DESKTOP=""
|
|
||||||
if [ "$DESK" != "cli" ]; then
|
|
||||||
# multilib requis (Steam / Wine 32 bits).
|
|
||||||
grep -qE '^\[multilib\]' /etc/pacman.conf \
|
|
||||||
|| printf '\n[multilib]\nInclude = /etc/pacman.d/mirrorlist\n' >> /etc/pacman.conf
|
|
||||||
# mangohud : overlay FPS/perfs en jeu (mangohud %command% dans Steam).
|
|
||||||
DESKTOP="steam wine winetricks wine-mono wine-gecko \
|
|
||||||
lib32-mesa lib32-pipewire monado gamemode \
|
|
||||||
mangohud lib32-mangohud \
|
|
||||||
noto-fonts ttf-liberation"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Sync + filtre aux paquets RÉELLEMENT existants ───────────────────────────
|
|
||||||
# (un nom de paquet manquant/renommé ne doit pas faire échouer tout le reste).
|
|
||||||
pacman -Sy --noconfirm || log "synchro pacman échouée (réseau ?)"
|
|
||||||
VALID=""
|
|
||||||
for p in $DRIVERS $BASE $DESKTOP; do
|
|
||||||
pacman -Si "$p" >/dev/null 2>&1 && VALID="$VALID $p"
|
|
||||||
done
|
|
||||||
if [ -n "$VALID" ]; then
|
|
||||||
log "installation :$VALID"
|
|
||||||
pacman -S --noconfirm --needed $VALID || log "certains paquets non installés"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# NB : winboat-bin (AUR) n'est PAS installé ici (fws-local n'existe pas sur la
|
|
||||||
# cible) : il est intégré à l'image FWS via packages.x86_64 et recopié par le
|
|
||||||
# payload LiveOS d'Anaconda → déjà présent sur le système installé.
|
|
||||||
|
|
||||||
# ── Services matériels ───────────────────────────────────────────────────────
|
|
||||||
[ -n "$VMSVC" ] && systemctl enable $VMSVC 2>/dev/null
|
|
||||||
systemctl enable bluetooth.service 2>/dev/null
|
|
||||||
systemctl enable podman.socket 2>/dev/null
|
|
||||||
exit 0
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# FWS — mise à jour du système en une commande (bouton de la barre, fws-hello,
|
|
||||||
# ou terminal). pacman -Syu + nettoyage du cache + rapport des orphelins.
|
|
||||||
set -u
|
|
||||||
say() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; }
|
|
||||||
|
|
||||||
if [ "$(id -u)" -ne 0 ]; then
|
|
||||||
exec sudo "$0" "$@"
|
|
||||||
fi
|
|
||||||
|
|
||||||
say "Mise à jour du système (pacman -Syu)…"
|
|
||||||
pacman -Syu || { echo "Échec de la mise à jour (réseau ?)"; read -rp "Entrée pour fermer. " _; exit 1; }
|
|
||||||
|
|
||||||
if command -v paccache >/dev/null 2>&1; then
|
|
||||||
say "Nettoyage du cache des paquets (2 dernières versions gardées)…"
|
|
||||||
paccache -rk2
|
|
||||||
fi
|
|
||||||
|
|
||||||
ORPHANS="$(pacman -Qtdq 2>/dev/null)"
|
|
||||||
if [ -n "$ORPHANS" ]; then
|
|
||||||
say "Paquets orphelins (plus requis par rien) :"
|
|
||||||
echo "$ORPHANS"
|
|
||||||
read -rp "Les supprimer ? [o/N] " _rm
|
|
||||||
case "$_rm" in o|O|oui) echo "$ORPHANS" | pacman -Rns --noconfirm - ;; esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v flatpak >/dev/null 2>&1; then
|
|
||||||
say "Mise à jour des Flatpaks…"
|
|
||||||
flatpak update -y || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
say "Système à jour ✔"
|
|
||||||
read -rp "Entrée pour fermer. " _
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# FWS — indicateur « mises à jour disponibles » pour waybar/polybar.
|
|
||||||
# checkupdates (pacman-contrib) : lit les dépôts SANS toucher la base pacman.
|
|
||||||
# Sortie vide = rien à faire → le module de la barre se cache.
|
|
||||||
command -v checkupdates >/dev/null 2>&1 || exit 0
|
|
||||||
n="$(checkupdates 2>/dev/null | wc -l)"
|
|
||||||
[ "$n" -gt 0 ] && printf ' %s\n' "$n"
|
|
||||||
exit 0
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# ============================================================================
|
|
||||||
# FWS — installation des drivers/runtimes VR selon le casque.
|
|
||||||
#
|
|
||||||
# À lancer depuis un terminal (Super+Shift+V sous i3, ou l'entrée
|
|
||||||
# « Installation VR (FWS) » du menu d'applications). Réservé aux sessions
|
|
||||||
# X11 (i3) : SteamVR ne fonctionne PAS sous Wayland/Hyprland.
|
|
||||||
#
|
|
||||||
# Réalité du support Linux (résumé honnête) :
|
|
||||||
# • HTC Vive / Vive Pro / Valve Index (stations lighthouse) → NATIF via
|
|
||||||
# SteamVR. C'est le chemin le plus fiable.
|
|
||||||
# • Meta/Oculus Quest 1-3, Pico → PAS de support officiel ; streaming
|
|
||||||
# Wi-Fi via ALVR (+ SteamVR). ALVR n'est pas dans les dépôts Arch :
|
|
||||||
# le script installe la base et donne la marche à suivre.
|
|
||||||
# • Oculus Rift CV1/S (filaire) → pas de support utilisable.
|
|
||||||
# • Monado : runtime OpenXR libre (déjà proposé par fws-setup-hardware),
|
|
||||||
# utile pour les applis OpenXR natives sans SteamVR.
|
|
||||||
# ============================================================================
|
|
||||||
set -u
|
|
||||||
say() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; }
|
|
||||||
warn() { printf '\033[1;33m ⚠ %s\033[0m\n' "$*"; }
|
|
||||||
|
|
||||||
if [ "$(id -u)" -ne 0 ]; then
|
|
||||||
exec sudo "$0" "$@"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# VM = cul-de-sac VR : pas d'accès direct au GPU ni à l'USB/DisplayPort du
|
|
||||||
# casque, et Steam (steamwebhelper) est INSTABLE en rendu logiciel (llvmpipe).
|
|
||||||
# On prévient au lieu de laisser l'utilisateur croire à une panne de FWS.
|
|
||||||
if systemd-detect-virt --quiet 2>/dev/null; then
|
|
||||||
warn "Machine VIRTUELLE détectée ($(systemd-detect-virt 2>/dev/null))."
|
|
||||||
warn "La VR ne fonctionnera PAS ici : casque et GPU inaccessibles depuis une VM."
|
|
||||||
warn "Steam lui-même peut planter en rendu logiciel (steamwebhelper)."
|
|
||||||
echo " → Fais cette installation sur la machine RÉELLE avec le casque branché."
|
|
||||||
read -rp " Continuer quand même (tests uniquement) ? [o/N] " _ok
|
|
||||||
case "$_ok" in o|O|oui) ;; *) exit 0 ;; esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
# lib32 Vulkan selon le GPU (indispensable à SteamVR, qui est 32/64 bits).
|
|
||||||
GPU="$(lspci 2>/dev/null | grep -iE 'VGA compatible controller|3D controller|Display controller')"
|
|
||||||
LIB32="lib32-mesa"
|
|
||||||
case "$GPU" in
|
|
||||||
*NVIDIA*|*GeForce*) LIB32="$LIB32 lib32-nvidia-utils" ;;
|
|
||||||
*AMD*|*ATI*|*Radeon*) LIB32="$LIB32 lib32-vulkan-radeon" ;;
|
|
||||||
*Intel*) LIB32="$LIB32 lib32-vulkan-intel" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
install_pkgs() {
|
|
||||||
# multilib requis pour Steam/lib32.
|
|
||||||
grep -qE '^\[multilib\]' /etc/pacman.conf \
|
|
||||||
|| printf '\n[multilib]\nInclude = /etc/pacman.d/mirrorlist\n' >> /etc/pacman.conf
|
|
||||||
pacman -Sy --noconfirm || { warn "synchro pacman échouée (réseau ?)"; return 1; }
|
|
||||||
local valid="" p
|
|
||||||
for p in "$@"; do
|
|
||||||
pacman -Si "$p" >/dev/null 2>&1 && valid="$valid $p" || warn "paquet inconnu ignoré : $p"
|
|
||||||
done
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
pacman -S --noconfirm --needed $valid
|
|
||||||
}
|
|
||||||
|
|
||||||
steamvr_base() {
|
|
||||||
say "Installation de la base SteamVR (Steam + Vulkan 32 bits : $LIB32)…"
|
|
||||||
# ttf-liberation : polices attendues par l'interface Steam (évite des
|
|
||||||
# rendus cassés/plantages cosmétiques sur install minimale).
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
install_pkgs steam $LIB32 ttf-liberation || return 1
|
|
||||||
say "Base installée. Étapes suivantes :"
|
|
||||||
echo " 1. Lance Steam, connecte-toi, puis installe « SteamVR » (Bibliothèque → Outils)."
|
|
||||||
echo " 2. Branche le casque et les stations, puis lance SteamVR."
|
|
||||||
echo " 3. Premier lancement : SteamVR peut demander à ajuster les règles udev — accepte."
|
|
||||||
say "Si Steam PLANTE (fenêtre qui se ferme, souvent steamwebhelper) :"
|
|
||||||
echo " • Vérifie l'espace disque (SteamVR ≈ 5 Go) : df -h ~"
|
|
||||||
echo " • GPU faible / VM / rendu logiciel — lance :"
|
|
||||||
echo " steam -cef-disable-gpu -cef-disable-gpu-compositing"
|
|
||||||
echo " • Pour voir l'erreur exacte : lance « steam » depuis un terminal,"
|
|
||||||
echo " ou regarde ~/.steam/steam/logs/ (console_log.txt, bootstrap_log.txt)."
|
|
||||||
}
|
|
||||||
|
|
||||||
case "${1:-menu}" in
|
|
||||||
menu)
|
|
||||||
cat <<'MENU'
|
|
||||||
|
|
||||||
╔══════════════════════════════════════════════════════════╗
|
|
||||||
║ FWS — Installation VR (session X11 requise) ║
|
|
||||||
╠══════════════════════════════════════════════════════════╣
|
|
||||||
║ 1) HTC Vive / Vive Pro / Valve Index (natif, SteamVR) ║
|
|
||||||
║ 2) Meta Quest / Pico (streaming ALVR) ║
|
|
||||||
║ 3) Monado (runtime OpenXR libre, sans SteamVR) ║
|
|
||||||
║ q) Quitter ║
|
|
||||||
╚══════════════════════════════════════════════════════════╝
|
|
||||||
MENU
|
|
||||||
read -rp " Choix : " choice
|
|
||||||
exec "$0" "$choice"
|
|
||||||
;;
|
|
||||||
1|vive|index)
|
|
||||||
steamvr_base || exit 1
|
|
||||||
;;
|
|
||||||
2|quest|pico)
|
|
||||||
steamvr_base || exit 1
|
|
||||||
say "Streaming Quest/Pico : il faut AUSSI ALVR (pas dans les dépôts Arch) :"
|
|
||||||
echo " 1. Télécharge le .tar.gz « ALVR Launcher » : https://github.com/alvr-org/ALVR/releases"
|
|
||||||
echo " 2. Sur le casque, installe le client ALVR (App Lab / SideQuest)."
|
|
||||||
echo " 3. Lance ALVR sur le PC (même Wi-Fi 5 GHz que le casque) — il pilote SteamVR."
|
|
||||||
;;
|
|
||||||
3|monado)
|
|
||||||
say "Installation de Monado (OpenXR) + outils…"
|
|
||||||
install_pkgs monado || exit 1
|
|
||||||
say "Monado installé. Les applis OpenXR le trouvent via /usr/share/openxr/."
|
|
||||||
;;
|
|
||||||
q|Q) exit 0 ;;
|
|
||||||
*) warn "choix inconnu : ${1}"; exec "$0" menu ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo
|
|
||||||
read -rp "Terminé — appuie sur Entrée pour fermer. " _
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
# FWS — lanceur de l'application de réparation dans l'OS INSTALLÉ (copié tel
|
|
||||||
# quel du live). L'app tourne en utilisateur ; chaque appel du moteur passe
|
|
||||||
# par pkexec (cf. org.fws.recovery.policy).
|
|
||||||
Type=Application
|
|
||||||
Name=Récupération FWS
|
|
||||||
Name[en]=FWS Recovery
|
|
||||||
GenericName=Réparation du système
|
|
||||||
GenericName[en]=System repair
|
|
||||||
Comment=Réparer GRUB, l'initramfs ou pacman — le point de restauration FWS
|
|
||||||
Comment[en]=Repair GRUB, the initramfs or pacman — the FWS restore point
|
|
||||||
Exec=/usr/local/bin/fws-recovery-gui
|
|
||||||
Icon=fws-recovery
|
|
||||||
# Associe la fenêtre (WM_CLASS via GLib.set_prgname) à ce lanceur → bonne
|
|
||||||
# icône dans la barre de titre / barre des tâches (sinon icône générique).
|
|
||||||
StartupWMClass=fws-recovery-gui
|
|
||||||
Terminal=false
|
|
||||||
Categories=System;
|
|
||||||
Keywords=recovery;réparation;restauration;grub;boot;secours;dépannage;
|
|
||||||
Keywords[en]=recovery;repair;restore;grub;boot;rescue;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
# FWS — masque « À propos de Xfce » du menu d'applications. Le binaire
|
|
||||||
# xfce4-about vient de libxfce4ui (dépendance de Thunar) : il n'implique
|
|
||||||
# AUCUNE session XFCE. /usr/local/share étant prioritaire sur /usr/share
|
|
||||||
# dans XDG_DATA_DIRS, cette entrée NoDisplay masque celle du paquet partout.
|
|
||||||
Type=Application
|
|
||||||
Name=About Xfce
|
|
||||||
Exec=xfce4-about
|
|
||||||
NoDisplay=true
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
# FWS — configuration Hyprland par défaut.
|
|
||||||
# Le clavier (__KB_LAYOUT__ / __KB_VARIANT__) est injecté à l'installation,
|
|
||||||
# depuis le choix fait dans Anaconda. Édite ce fichier librement.
|
|
||||||
#
|
|
||||||
# Raccourcis : transposition de la config i3/sway FWS (mêmes touches).
|
|
||||||
# Couleurs : nuances de bleu (palette Catppuccin Mocha).
|
|
||||||
#
|
|
||||||
# Les raccourcis sont déclarés avec « bindd » (bind + description) : la
|
|
||||||
# description alimente l'écran « Raccourcis clavier » (bouton ⌨ de la barre
|
|
||||||
# ou SUPER+K → fws-keys, qui lit les binds RÉELS via hyprctl). Ajoute
|
|
||||||
# tes propres bindd : ils apparaîtront automatiquement dans la liste.
|
|
||||||
# ⚠ Pas de virgule dans les descriptions (séparateur de champs Hyprland).
|
|
||||||
|
|
||||||
monitor = , preferred, auto, 1
|
|
||||||
|
|
||||||
input {
|
|
||||||
kb_layout = __KB_LAYOUT__
|
|
||||||
kb_variant = __KB_VARIANT__
|
|
||||||
follow_mouse = 1
|
|
||||||
numlock_by_default = true
|
|
||||||
touchpad {
|
|
||||||
natural_scroll = true
|
|
||||||
tap-to-click = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
general {
|
|
||||||
gaps_in = 4
|
|
||||||
gaps_out = 8
|
|
||||||
border_size = 2
|
|
||||||
# Nuances de bleu : bleu → sapphire en dégradé sur la fenêtre active.
|
|
||||||
col.active_border = rgb(89b4fa) rgb(74c7ec) 45deg
|
|
||||||
col.inactive_border = rgb(313244)
|
|
||||||
layout = dwindle
|
|
||||||
}
|
|
||||||
|
|
||||||
decoration {
|
|
||||||
rounding = 8
|
|
||||||
# Flou et ombres COUPÉS : rendu logiciel (llvmpipe) en VM — cf. le
|
|
||||||
# wrapper fws-hyprland écrit par l'installateur. Sur un vrai GPU tu
|
|
||||||
# peux les réactiver (enabled = true).
|
|
||||||
blur {
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
shadow {
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
animations {
|
|
||||||
enabled = true
|
|
||||||
bezier = fws, 0.22, 1.0, 0.36, 1.0
|
|
||||||
animation = windows, 1, 3, fws
|
|
||||||
animation = workspaces, 1, 3, fws
|
|
||||||
animation = fade, 1, 2, default
|
|
||||||
}
|
|
||||||
|
|
||||||
misc {
|
|
||||||
disable_hyprland_logo = true
|
|
||||||
disable_splash_rendering = true # pas de « Thanks xxx! » en bas d'écran
|
|
||||||
force_default_wallpaper = 0
|
|
||||||
background_color = 0x1a1a2e # bleu nuit FWS (aucun fond requis)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Démarrage de session : barre d'état, notifications, réseau dans le tray,
|
|
||||||
# agent d'authentification GUI (indispensable pour les apps qui demandent
|
|
||||||
# les droits admin : gestionnaire de disques, blueman…).
|
|
||||||
exec-once = waybar
|
|
||||||
exec-once = mako
|
|
||||||
exec-once = nm-applet
|
|
||||||
# Applet Bluetooth : seulement si un adaptateur existe (en VM il n'y en a
|
|
||||||
# pas → blueman afficherait une erreur « Connection to BlueZ failed »).
|
|
||||||
exec-once = sh -c '[ -n "$(ls -A /sys/class/bluetooth 2>/dev/null)" ] && exec blueman-applet'
|
|
||||||
exec-once = /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
|
|
||||||
# Fenêtre de bienvenue (1er démarrage seulement — se retire ensuite).
|
|
||||||
exec-once = fws-hello --autostart
|
|
||||||
|
|
||||||
$mod = SUPER
|
|
||||||
|
|
||||||
# --- Applications ---------------------------------------------------------
|
|
||||||
bindd = $mod, Return, Ouvrir un terminal (kitty), exec, kitty
|
|
||||||
bindd = $mod SHIFT, Return, Terminal flottant, exec, kitty --class floating-term
|
|
||||||
bindd = $mod, R, Menu des applications, exec, wofi --show drun
|
|
||||||
bindd = $mod, E, Gestionnaire de fichiers, exec, dolphin
|
|
||||||
bindd = $mod, B, Ouvrir Firefox, exec, firefox
|
|
||||||
bindd = $mod, K, Afficher les raccourcis clavier, exec, fws-keys
|
|
||||||
bindd = $mod, X, Menu d'alimentation (arrêt/redémarrage…), exec, fws-powermenu
|
|
||||||
|
|
||||||
# --- Fenêtres ---------------------------------------------------------------
|
|
||||||
bindd = $mod, Q, Fermer la fenêtre, killactive,
|
|
||||||
bindd = $mod, V, Fenêtre flottante ou ancrée, togglefloating,
|
|
||||||
bindd = $mod, F, Plein écran, fullscreen,
|
|
||||||
bindd = $mod, J, Basculer la découpe horizontale/verticale, layoutmsg, togglesplit
|
|
||||||
bindd = $mod, L, Verrouiller l'écran, exec, hyprlock
|
|
||||||
bindd = $mod SHIFT, E, Quitter la session (retour à SDDM), exit,
|
|
||||||
bindd = $mod SHIFT, C, Recharger la configuration Hyprland, exec, hyprctl reload
|
|
||||||
|
|
||||||
# --- Focus (flèches) ---------------------------------------------------------
|
|
||||||
bindd = $mod, left, Focus à gauche, movefocus, l
|
|
||||||
bindd = $mod, right, Focus à droite, movefocus, r
|
|
||||||
bindd = $mod, up, Focus en haut, movefocus, u
|
|
||||||
bindd = $mod, down, Focus en bas, movefocus, d
|
|
||||||
|
|
||||||
# --- Déplacer la fenêtre (Maj + flèches) ---------------------------------------
|
|
||||||
bindd = $mod SHIFT, left, Déplacer la fenêtre à gauche, movewindow, l
|
|
||||||
bindd = $mod SHIFT, right, Déplacer la fenêtre à droite, movewindow, r
|
|
||||||
bindd = $mod SHIFT, up, Déplacer la fenêtre en haut, movewindow, u
|
|
||||||
bindd = $mod SHIFT, down, Déplacer la fenêtre en bas, movewindow, d
|
|
||||||
|
|
||||||
# --- Redimensionner (Ctrl + flèches, maintien possible) ------------------------
|
|
||||||
binde = $mod CTRL, left, resizeactive, -30 0
|
|
||||||
binde = $mod CTRL, right, resizeactive, 30 0
|
|
||||||
binde = $mod CTRL, up, resizeactive, 0 -30
|
|
||||||
binde = $mod CTRL, down, resizeactive, 0 30
|
|
||||||
|
|
||||||
# --- Espaces de travail 1-10 (0 = espace 10, comme i3) --------------------------
|
|
||||||
bindd = $mod, 1, Aller à l'espace 1, workspace, 1
|
|
||||||
bindd = $mod, 2, Aller à l'espace 2, workspace, 2
|
|
||||||
bindd = $mod, 3, Aller à l'espace 3, workspace, 3
|
|
||||||
bindd = $mod, 4, Aller à l'espace 4, workspace, 4
|
|
||||||
bindd = $mod, 5, Aller à l'espace 5, workspace, 5
|
|
||||||
bindd = $mod, 6, Aller à l'espace 6, workspace, 6
|
|
||||||
bindd = $mod, 7, Aller à l'espace 7, workspace, 7
|
|
||||||
bindd = $mod, 8, Aller à l'espace 8, workspace, 8
|
|
||||||
bindd = $mod, 9, Aller à l'espace 9, workspace, 9
|
|
||||||
bindd = $mod, 0, Aller à l'espace 10, workspace, 10
|
|
||||||
bindd = $mod SHIFT, 1, Envoyer la fenêtre vers l'espace 1, movetoworkspace, 1
|
|
||||||
bindd = $mod SHIFT, 2, Envoyer la fenêtre vers l'espace 2, movetoworkspace, 2
|
|
||||||
bindd = $mod SHIFT, 3, Envoyer la fenêtre vers l'espace 3, movetoworkspace, 3
|
|
||||||
bindd = $mod SHIFT, 4, Envoyer la fenêtre vers l'espace 4, movetoworkspace, 4
|
|
||||||
bindd = $mod SHIFT, 5, Envoyer la fenêtre vers l'espace 5, movetoworkspace, 5
|
|
||||||
bindd = $mod SHIFT, 6, Envoyer la fenêtre vers l'espace 6, movetoworkspace, 6
|
|
||||||
bindd = $mod SHIFT, 7, Envoyer la fenêtre vers l'espace 7, movetoworkspace, 7
|
|
||||||
bindd = $mod SHIFT, 8, Envoyer la fenêtre vers l'espace 8, movetoworkspace, 8
|
|
||||||
bindd = $mod SHIFT, 9, Envoyer la fenêtre vers l'espace 9, movetoworkspace, 9
|
|
||||||
bindd = $mod SHIFT, 0, Envoyer la fenêtre vers l'espace 10, movetoworkspace, 10
|
|
||||||
|
|
||||||
# --- Multi-écrans (comme i3 : Super+Ctrl+1/2 = écran gauche/droit) -------------
|
|
||||||
bindd = $mod CTRL, 1, Envoyer la fenêtre vers l'écran de gauche, movewindow, mon:l
|
|
||||||
bindd = $mod CTRL, 2, Envoyer la fenêtre vers l'écran de droite, movewindow, mon:r
|
|
||||||
|
|
||||||
# --- Molette = changer d'espace de travail --------------------------------------
|
|
||||||
bindd = $mod, mouse_up, Espace de travail précédent, workspace, e-1
|
|
||||||
bindd = $mod, mouse_down, Espace de travail suivant, workspace, e+1
|
|
||||||
|
|
||||||
# --- Scratchpad (espace spécial) -------------------------------------------------
|
|
||||||
bindd = $mod, S, Afficher le scratchpad, togglespecialworkspace, magic
|
|
||||||
bindd = $mod SHIFT, S, Envoyer la fenêtre au scratchpad, movetoworkspace, special:magic
|
|
||||||
|
|
||||||
# --- Captures d'écran (grim/slurp → presse-papier ou ~/Images) -------------------
|
|
||||||
bindd = , Print, Capture plein écran vers le presse-papier, exec, grim - | wl-copy
|
|
||||||
bindd = $mod, Print, Capture d'une zone vers le presse-papier, exec, grim -g "$(slurp)" - | wl-copy
|
|
||||||
bindd = $mod SHIFT, Print, Capture d'une zone vers ~/Images, exec, sh -c 'mkdir -p ~/Images && grim -g "$(slurp)" ~/Images/capture_$(date +%Y-%m-%d_%H-%M-%S).png'
|
|
||||||
|
|
||||||
# --- Multimédia (touches XF86 : volume via PipeWire, lecture via playerctl) ------
|
|
||||||
bindel = , XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
|
|
||||||
bindel = , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
|
|
||||||
bindl = , XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
|
|
||||||
bindl = , XF86AudioPlay, exec, playerctl play-pause
|
|
||||||
bindl = , XF86AudioNext, exec, playerctl next
|
|
||||||
bindl = , XF86AudioPrev, exec, playerctl previous
|
|
||||||
|
|
||||||
# --- Luminosité de l'écran (portables : touches Fn) ------------------------------
|
|
||||||
bindel = , XF86MonBrightnessUp, exec, brightnessctl set +5%
|
|
||||||
bindel = , XF86MonBrightnessDown, exec, brightnessctl set 5%-
|
|
||||||
|
|
||||||
# --- Souris : déplacer / redimensionner les fenêtres -----------------------------
|
|
||||||
bindm = $mod, mouse:272, movewindow
|
|
||||||
bindm = $mod, mouse:273, resizewindow
|
|
||||||
|
|
||||||
# --- Règles de fenêtres (comme i3) ------------------------------------------------
|
|
||||||
# Syntaxe Hyprland ≥ 0.55 : champs « <règle> <valeur> » + cibles « match:… »
|
|
||||||
# (validé contre le binaire avec --verify-config).
|
|
||||||
windowrule = float on, match:class ^(floating-term)$
|
|
||||||
windowrule = size 900 550, match:class ^(floating-term)$
|
|
||||||
windowrule = float on, match:class ^([Pp]avucontrol|org.pulseaudio.pavucontrol)$
|
|
||||||
windowrule = float on, match:class ^(.?[Bb]lueman-manager.*)$
|
|
||||||
windowrule = float on, match:class ^([Ff]ws-hello)$
|
|
||||||
windowrule = float on, match:class ^([Ff]ws-about)$
|
|
||||||
windowrule = float on, match:title ^(Picture-in-Picture)$
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
# FWS — configuration i3 par défaut (session X11, COMPATIBLE VR).
|
|
||||||
# SteamVR fonctionne en X11 : c'est le bureau à choisir pour un casque VR
|
|
||||||
# (HTC Vive, Valve Index…). Lance « fws-vr-setup » (Super+Shift+V, ou menu)
|
|
||||||
# pour installer les drivers/runtimes selon ton casque.
|
|
||||||
# Le clavier X11 est configuré système via /etc/X11/xorg.conf.d/00-keyboard.conf
|
|
||||||
# (écrit par l'installateur selon ton choix Anaconda) : rien à régler ici.
|
|
||||||
|
|
||||||
set $mod Mod4
|
|
||||||
set $term kitty
|
|
||||||
set $filemanager dolphin
|
|
||||||
set $menu rofi -show drun -show-icons
|
|
||||||
set $browser firefox
|
|
||||||
|
|
||||||
# Couleurs — nuances de bleu (Catppuccin Mocha, comme SDDM/waybar FWS)
|
|
||||||
set $base #1e1e2e
|
|
||||||
set $surface0 #313244
|
|
||||||
set $text #cdd6f4
|
|
||||||
set $blue #89b4fa
|
|
||||||
set $sapphire #74c7ec
|
|
||||||
set $red #f38ba8
|
|
||||||
|
|
||||||
# classe bordure fond texte indicateur child_border
|
|
||||||
client.focused $blue $blue $base $sapphire $blue
|
|
||||||
client.focused_inactive $surface0 $surface0 $text $surface0 $surface0
|
|
||||||
client.unfocused $surface0 $surface0 $text $surface0 $surface0
|
|
||||||
client.urgent $red $red $base $red $red
|
|
||||||
client.background $base
|
|
||||||
|
|
||||||
font pango:Noto Sans 10
|
|
||||||
default_border pixel 2
|
|
||||||
gaps inner 4
|
|
||||||
gaps outer 4
|
|
||||||
|
|
||||||
##################
|
|
||||||
## APPLICATIONS ##
|
|
||||||
##################
|
|
||||||
bindsym $mod+Return exec $term
|
|
||||||
bindsym $mod+Shift+Return exec kitty --class floating-term
|
|
||||||
bindsym $mod+r exec $menu
|
|
||||||
bindsym $mod+e exec $filemanager
|
|
||||||
bindsym $mod+b exec $browser
|
|
||||||
# VR : installation des drivers/runtimes casque (HTC Vive, Index, Quest…)
|
|
||||||
bindsym $mod+Shift+v exec kitty -e fws-vr-setup
|
|
||||||
# Panneau des raccourcis clavier (aussi via le bouton ⌌ de la barre)
|
|
||||||
bindsym $mod+k exec fws-keys
|
|
||||||
# Menu d'alimentation (verrouiller/déconnexion/redémarrer/éteindre)
|
|
||||||
bindsym $mod+x exec fws-powermenu
|
|
||||||
|
|
||||||
##############
|
|
||||||
## FENETRES ##
|
|
||||||
##############
|
|
||||||
bindsym $mod+q kill
|
|
||||||
bindsym $mod+v floating toggle
|
|
||||||
bindsym $mod+f fullscreen toggle
|
|
||||||
bindsym $mod+j split toggle
|
|
||||||
bindsym $mod+l exec i3lock -c 1e1e2e
|
|
||||||
# Quitter i3 → retour à SDDM
|
|
||||||
bindsym $mod+Shift+e exec "i3-nagbar -t warning -m 'Quitter i3 et revenir à SDDM ?' -B 'Oui, quitter' 'i3-msg exit'"
|
|
||||||
bindsym $mod+Shift+c reload
|
|
||||||
|
|
||||||
# Focus (flèches)
|
|
||||||
bindsym $mod+Left focus left
|
|
||||||
bindsym $mod+Right focus right
|
|
||||||
bindsym $mod+Up focus up
|
|
||||||
bindsym $mod+Down focus down
|
|
||||||
# Déplacer la fenêtre
|
|
||||||
bindsym $mod+Shift+Left move left
|
|
||||||
bindsym $mod+Shift+Right move right
|
|
||||||
bindsym $mod+Shift+Up move up
|
|
||||||
bindsym $mod+Shift+Down move down
|
|
||||||
# Redimensionner (Ctrl + flèches)
|
|
||||||
bindsym $mod+Ctrl+Left resize shrink width 30 px
|
|
||||||
bindsym $mod+Ctrl+Right resize grow width 30 px
|
|
||||||
bindsym $mod+Ctrl+Up resize shrink height 30 px
|
|
||||||
bindsym $mod+Ctrl+Down resize grow height 30 px
|
|
||||||
|
|
||||||
################
|
|
||||||
## WORKSPACES ##
|
|
||||||
################
|
|
||||||
bindsym $mod+1 workspace number 1
|
|
||||||
bindsym $mod+2 workspace number 2
|
|
||||||
bindsym $mod+3 workspace number 3
|
|
||||||
bindsym $mod+4 workspace number 4
|
|
||||||
bindsym $mod+5 workspace number 5
|
|
||||||
bindsym $mod+6 workspace number 6
|
|
||||||
bindsym $mod+7 workspace number 7
|
|
||||||
bindsym $mod+8 workspace number 8
|
|
||||||
bindsym $mod+9 workspace number 9
|
|
||||||
bindsym $mod+0 workspace number 10
|
|
||||||
bindsym $mod+Shift+1 move container to workspace number 1
|
|
||||||
bindsym $mod+Shift+2 move container to workspace number 2
|
|
||||||
bindsym $mod+Shift+3 move container to workspace number 3
|
|
||||||
bindsym $mod+Shift+4 move container to workspace number 4
|
|
||||||
bindsym $mod+Shift+5 move container to workspace number 5
|
|
||||||
bindsym $mod+Shift+6 move container to workspace number 6
|
|
||||||
bindsym $mod+Shift+7 move container to workspace number 7
|
|
||||||
bindsym $mod+Shift+8 move container to workspace number 8
|
|
||||||
bindsym $mod+Shift+9 move container to workspace number 9
|
|
||||||
bindsym $mod+Shift+0 move container to workspace number 10
|
|
||||||
# Multi-écrans
|
|
||||||
bindsym $mod+Ctrl+1 move container to output left
|
|
||||||
bindsym $mod+Ctrl+2 move container to output right
|
|
||||||
# Molette = changer de workspace
|
|
||||||
bindsym $mod+button4 workspace prev
|
|
||||||
bindsym $mod+button5 workspace next
|
|
||||||
# Scratchpad
|
|
||||||
bindsym $mod+s scratchpad show
|
|
||||||
bindsym $mod+Shift+s move scratchpad
|
|
||||||
|
|
||||||
##############
|
|
||||||
## CAPTURES ##
|
|
||||||
##############
|
|
||||||
bindsym Print exec --no-startup-id "maim | xclip -selection clipboard -t image/png"
|
|
||||||
bindsym $mod+Print exec --no-startup-id "maim -s | xclip -selection clipboard -t image/png"
|
|
||||||
bindsym $mod+Shift+Print exec --no-startup-id "mkdir -p ~/Images && maim -s ~/Images/capture_$(date +%Y-%m-%d_%H-%M-%S).png"
|
|
||||||
|
|
||||||
################
|
|
||||||
## MULTIMEDIA ##
|
|
||||||
################
|
|
||||||
bindsym XF86AudioRaiseVolume exec --no-startup-id wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
|
|
||||||
bindsym XF86AudioLowerVolume exec --no-startup-id wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
|
|
||||||
bindsym XF86AudioMute exec --no-startup-id wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
|
|
||||||
bindsym XF86AudioPlay exec --no-startup-id playerctl play-pause
|
|
||||||
bindsym XF86AudioNext exec --no-startup-id playerctl next
|
|
||||||
bindsym XF86AudioPrev exec --no-startup-id playerctl previous
|
|
||||||
bindsym XF86MonBrightnessUp exec --no-startup-id brightnessctl set +5%
|
|
||||||
bindsym XF86MonBrightnessDown exec --no-startup-id brightnessctl set 5%-
|
|
||||||
|
|
||||||
#####################
|
|
||||||
## REGLES FENETRES ##
|
|
||||||
#####################
|
|
||||||
for_window [class="floating-term"] floating enable, resize set 900 550
|
|
||||||
for_window [class="(?i)fws-hello"] floating enable
|
|
||||||
for_window [class="(?i)fws-about"] floating enable
|
|
||||||
for_window [class="(?i)pavucontrol"] floating enable, resize set 700 500
|
|
||||||
for_window [class="(?i)blueman-manager"] floating enable
|
|
||||||
for_window [title="Picture-in-Picture"] floating enable
|
|
||||||
|
|
||||||
###########
|
|
||||||
## BARRE ##
|
|
||||||
###########
|
|
||||||
# Polybar (config ~/.config/polybar/config.ini) : clone visuel de la waybar
|
|
||||||
# Hyprland FWS — pilules, icônes, horloge+date au centre, bouton Raccourcis.
|
|
||||||
# exec_always + pkill : la barre survit proprement aux reload ($mod+Shift+c).
|
|
||||||
exec_always --no-startup-id sh -c 'pkill -x polybar; sleep 0.3; exec polybar -q fws'
|
|
||||||
|
|
||||||
###############
|
|
||||||
## AUTOSTART ##
|
|
||||||
###############
|
|
||||||
# picom : coins arrondis (parité visuelle avec Hyprland) — config FWS légère.
|
|
||||||
# PAS en machine virtuelle : le compositing sur GPU virtuel (vmwgfx/llvmpipe)
|
|
||||||
# rate le repaint des fenêtres GTK → contenus noirs/artefacts. Les coins
|
|
||||||
# arrondis sont sacrifiés en VM ; sur matériel réel picom se lance normalement.
|
|
||||||
exec --no-startup-id sh -c 'systemd-detect-virt --quiet || exec picom --config ~/.config/picom.conf'
|
|
||||||
exec --no-startup-id dunst
|
|
||||||
exec --no-startup-id nm-applet
|
|
||||||
exec --no-startup-id /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
|
|
||||||
exec --no-startup-id sh -c '[ -n "$(ls -A /sys/class/bluetooth 2>/dev/null)" ] && exec blueman-applet'
|
|
||||||
# Fenêtre de bienvenue (1er démarrage seulement — se retire ensuite).
|
|
||||||
exec --no-startup-id fws-hello --autostart
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
$1████████ $2██$5████$2██
|
|
||||||
$1████████ $2█$5██$2██$5██$2█
|
|
||||||
$1████████ $2█$5██████$2█
|
|
||||||
$1████████ $2██$6█$2██$6█$2██
|
|
||||||
|
|
||||||
$3████████ $4████████
|
|
||||||
$3████████ $4████████
|
|
||||||
$3████████ $4████████
|
|
||||||
$3████████ $4████████
|
|
||||||
@@ -3,7 +3,7 @@ PRETTY_NAME="FWS Linux"
|
|||||||
ID=fws
|
ID=fws
|
||||||
ID_LIKE=arch
|
ID_LIKE=arch
|
||||||
BUILD_ID=rolling
|
BUILD_ID=rolling
|
||||||
VERSION_ID="0.6.0"
|
VERSION_ID="1.0.0"
|
||||||
ANSI_COLOR="38;2;23;147;209"
|
ANSI_COLOR="38;2;23;147;209"
|
||||||
HOME_URL="https://github.com/You-re-like-Windows-a-bitch/fws"
|
HOME_URL="https://github.com/You-re-like-Windows-a-bitch/fws"
|
||||||
SUPPORT_URL="https://github.com/You-re-like-Windows-a-bitch/fws/issues"
|
SUPPORT_URL="https://github.com/You-re-like-Windows-a-bitch/fws/issues"
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
# FWS — picom minimal (session i3) : coins arrondis comme sous Hyprland.
|
|
||||||
# xrender : fiable partout, y compris en VM (rendu logiciel) ; pas de flou ni
|
|
||||||
# d'ombres (parité avec la config Hyprland FWS, et léger pour la VR).
|
|
||||||
backend = "xrender";
|
|
||||||
vsync = true;
|
|
||||||
|
|
||||||
corner-radius = 8;
|
|
||||||
rounded-corners-exclude = [
|
|
||||||
"window_type = 'dock'", # polybar gère son propre arrondi
|
|
||||||
"class_g = 'i3-frame'"
|
|
||||||
];
|
|
||||||
|
|
||||||
shadow = false;
|
|
||||||
fading = true;
|
|
||||||
fade-in-step = 0.08;
|
|
||||||
fade-out-step = 0.08;
|
|
||||||
|
|
||||||
# VR / jeux plein écran : ne pas composer par-dessus (latence).
|
|
||||||
unredir-if-possible = true;
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
; FWS — polybar (session i3) : clone visuel de la waybar Hyprland.
|
|
||||||
; Barre flottante arrondie, modules en « pilules » Catppuccin Mocha,
|
|
||||||
; icônes Nerd Font (ttf-nerd-fonts-symbols). Horloge + date au centre,
|
|
||||||
; bouton « ⌌ Raccourcis » (fws-keys), volume/réseau/CPU/RAM/tray à droite.
|
|
||||||
|
|
||||||
[colors]
|
|
||||||
base = #E01e1e2e
|
|
||||||
surface0 = #313244
|
|
||||||
text = #cdd6f4
|
|
||||||
subtext = #a6adc8
|
|
||||||
blue = #89b4fa
|
|
||||||
sapphire = #74c7ec
|
|
||||||
teal = #94e2d5
|
|
||||||
mauve = #cba6f7
|
|
||||||
maroon = #eba0ac
|
|
||||||
yellow = #f9e2af
|
|
||||||
red = #f38ba8
|
|
||||||
darkbase = #1e1e2e
|
|
||||||
|
|
||||||
[bar/fws]
|
|
||||||
monitor = ${env:MONITOR:}
|
|
||||||
width = 100%:-20
|
|
||||||
offset-x = 10
|
|
||||||
offset-y = 6
|
|
||||||
height = 36
|
|
||||||
radius = 14
|
|
||||||
background = ${colors.base}
|
|
||||||
foreground = ${colors.text}
|
|
||||||
border-size = 1
|
|
||||||
border-color = #5989b4fa
|
|
||||||
padding-left = 1
|
|
||||||
padding-right = 1
|
|
||||||
module-margin = 1
|
|
||||||
line-size = 0
|
|
||||||
font-0 = Noto Sans:size=10;2
|
|
||||||
font-1 = Symbols Nerd Font:size=12;3
|
|
||||||
font-2 = Noto Sans:weight=bold:size=10;2
|
|
||||||
modules-left = i3 xwindow
|
|
||||||
modules-center = date
|
|
||||||
modules-right = updates keyhelp backlight pulseaudio network cpu memory tray power
|
|
||||||
enable-ipc = true
|
|
||||||
wm-restack = i3
|
|
||||||
|
|
||||||
[module/i3]
|
|
||||||
type = internal/i3
|
|
||||||
pin-workspaces = true
|
|
||||||
show-urgent = true
|
|
||||||
strip-wsnumbers = false
|
|
||||||
format = <label-state> <label-mode>
|
|
||||||
label-focused = %name%
|
|
||||||
label-focused-background = ${colors.blue}
|
|
||||||
label-focused-foreground = ${colors.darkbase}
|
|
||||||
label-focused-padding = 2
|
|
||||||
label-unfocused = %name%
|
|
||||||
label-unfocused-background = ${colors.surface0}
|
|
||||||
label-unfocused-foreground = ${colors.subtext}
|
|
||||||
label-unfocused-padding = 2
|
|
||||||
label-visible = %name%
|
|
||||||
label-visible-background = ${colors.surface0}
|
|
||||||
label-visible-padding = 2
|
|
||||||
label-urgent = %name%
|
|
||||||
label-urgent-background = ${colors.red}
|
|
||||||
label-urgent-foreground = ${colors.darkbase}
|
|
||||||
label-urgent-padding = 2
|
|
||||||
|
|
||||||
[module/xwindow]
|
|
||||||
type = internal/xwindow
|
|
||||||
label = %title:0:45:…%
|
|
||||||
label-foreground = ${colors.subtext}
|
|
||||||
format-padding = 1
|
|
||||||
|
|
||||||
[module/date]
|
|
||||||
type = internal/date
|
|
||||||
interval = 1
|
|
||||||
date = %A %d %B %Y
|
|
||||||
time = %H:%M
|
|
||||||
label = %time% %date%
|
|
||||||
label-font = 3
|
|
||||||
format-background = ${colors.surface0}
|
|
||||||
format-padding = 3
|
|
||||||
|
|
||||||
[module/updates]
|
|
||||||
type = custom/script
|
|
||||||
exec = fws-updates-check
|
|
||||||
interval = 1800
|
|
||||||
click-left = kitty -e fws-update
|
|
||||||
format-background = ${colors.surface0}
|
|
||||||
format-foreground = ${colors.yellow}
|
|
||||||
format-padding = 2
|
|
||||||
|
|
||||||
[module/keyhelp]
|
|
||||||
type = custom/script
|
|
||||||
exec = echo " Raccourcis"
|
|
||||||
interval = 3600
|
|
||||||
click-left = fws-keys
|
|
||||||
format-background = ${colors.blue}
|
|
||||||
format-foreground = ${colors.darkbase}
|
|
||||||
format-padding = 2
|
|
||||||
label-font = 3
|
|
||||||
|
|
||||||
[module/backlight]
|
|
||||||
type = internal/backlight
|
|
||||||
enable-scroll = true
|
|
||||||
format = <label>
|
|
||||||
label = %percentage%%
|
|
||||||
format-background = ${colors.surface0}
|
|
||||||
format-foreground = ${colors.yellow}
|
|
||||||
format-padding = 2
|
|
||||||
|
|
||||||
[module/pulseaudio]
|
|
||||||
type = internal/pulseaudio
|
|
||||||
use-ui-max = false
|
|
||||||
click-right = pavucontrol
|
|
||||||
format-volume = <label-volume>
|
|
||||||
label-volume = %percentage%%
|
|
||||||
format-volume-background = ${colors.surface0}
|
|
||||||
format-volume-foreground = ${colors.blue}
|
|
||||||
format-volume-padding = 2
|
|
||||||
label-muted = muet
|
|
||||||
format-muted-background = ${colors.surface0}
|
|
||||||
format-muted-foreground = ${colors.subtext}
|
|
||||||
format-muted-padding = 2
|
|
||||||
|
|
||||||
[module/network]
|
|
||||||
type = internal/network
|
|
||||||
interface-type = wired
|
|
||||||
interval = 5
|
|
||||||
format-connected = <label-connected>
|
|
||||||
label-connected = %local_ip%
|
|
||||||
format-connected-background = ${colors.surface0}
|
|
||||||
format-connected-foreground = ${colors.teal}
|
|
||||||
format-connected-padding = 2
|
|
||||||
format-disconnected = <label-disconnected>
|
|
||||||
label-disconnected = hors ligne
|
|
||||||
format-disconnected-background = ${colors.surface0}
|
|
||||||
format-disconnected-foreground = ${colors.red}
|
|
||||||
format-disconnected-padding = 2
|
|
||||||
|
|
||||||
[module/cpu]
|
|
||||||
type = internal/cpu
|
|
||||||
interval = 3
|
|
||||||
label = %percentage%%
|
|
||||||
format-background = ${colors.surface0}
|
|
||||||
format-foreground = ${colors.mauve}
|
|
||||||
format-padding = 2
|
|
||||||
|
|
||||||
[module/memory]
|
|
||||||
type = internal/memory
|
|
||||||
interval = 3
|
|
||||||
label = %percentage_used%%
|
|
||||||
format-background = ${colors.surface0}
|
|
||||||
format-foreground = ${colors.maroon}
|
|
||||||
format-padding = 2
|
|
||||||
|
|
||||||
[module/power]
|
|
||||||
type = custom/text
|
|
||||||
content =
|
|
||||||
content-foreground = ${colors.red}
|
|
||||||
content-background = ${colors.surface0}
|
|
||||||
content-padding = 2
|
|
||||||
content-font = 2
|
|
||||||
click-left = fws-powermenu
|
|
||||||
|
|
||||||
[module/tray]
|
|
||||||
type = internal/tray
|
|
||||||
format = <tray>
|
|
||||||
format-background = ${colors.surface0}
|
|
||||||
format-padding = 1
|
|
||||||
tray-spacing = 8px
|
|
||||||
tray-size = 16px
|
|
||||||
|
|
||||||
[settings]
|
|
||||||
screenchange-reload = true
|
|
||||||
pseudo-transparency = false
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
/* FWS — rofi (menu d'applications i3, Super+R) : même look que wofi sous
|
|
||||||
Hyprland — fond sombre, bordure et sélection bleues, icônes. */
|
|
||||||
|
|
||||||
configuration {
|
|
||||||
show-icons: true;
|
|
||||||
display-drun: "Applications";
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
bg: #1e1e2eF5;
|
|
||||||
surface: #313244;
|
|
||||||
text: #cdd6f4;
|
|
||||||
subtext: #a6adc8;
|
|
||||||
blue: #89b4fa;
|
|
||||||
darkbase: #1e1e2e;
|
|
||||||
font: "Noto Sans 12";
|
|
||||||
background-color: transparent;
|
|
||||||
text-color: @text;
|
|
||||||
}
|
|
||||||
|
|
||||||
window {
|
|
||||||
background-color: @bg;
|
|
||||||
border: 2px;
|
|
||||||
border-color: @blue;
|
|
||||||
border-radius: 14px;
|
|
||||||
width: 560px;
|
|
||||||
}
|
|
||||||
|
|
||||||
mainbox {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
inputbar {
|
|
||||||
background-color: @surface;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 4px;
|
|
||||||
padding: 8px 12px;
|
|
||||||
children: [ prompt, entry ];
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt {
|
|
||||||
text-color: @blue;
|
|
||||||
margin: 0 8px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry {
|
|
||||||
placeholder: "Rechercher…";
|
|
||||||
placeholder-color: @subtext;
|
|
||||||
}
|
|
||||||
|
|
||||||
listview {
|
|
||||||
margin: 4px;
|
|
||||||
lines: 8;
|
|
||||||
scrollbar: false;
|
|
||||||
}
|
|
||||||
|
|
||||||
element {
|
|
||||||
padding: 8px 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
spacing: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
element selected.normal {
|
|
||||||
background-color: @blue;
|
|
||||||
text-color: @darkbase;
|
|
||||||
}
|
|
||||||
|
|
||||||
element-icon {
|
|
||||||
size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
element-text {
|
|
||||||
text-color: inherit;
|
|
||||||
vertical-align: 0.5;
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
{
|
|
||||||
"layer": "top",
|
|
||||||
"position": "top",
|
|
||||||
"height": 36,
|
|
||||||
"spacing": 2,
|
|
||||||
"margin-top": 6,
|
|
||||||
"margin-left": 10,
|
|
||||||
"margin-right": 10,
|
|
||||||
"modules-left": ["hyprland/workspaces", "hyprland/window"],
|
|
||||||
"modules-center": ["clock"],
|
|
||||||
"modules-right": ["custom/updates", "custom/keyhelp", "backlight", "pulseaudio", "bluetooth", "network", "cpu", "memory", "battery", "tray", "custom/power"],
|
|
||||||
|
|
||||||
"custom/power": {
|
|
||||||
"format": "",
|
|
||||||
"tooltip-format": "Alimentation (SUPER+X)",
|
|
||||||
"on-click": "fws-powermenu"
|
|
||||||
},
|
|
||||||
|
|
||||||
"custom/updates": {
|
|
||||||
"exec": "fws-updates-check",
|
|
||||||
"interval": 1800,
|
|
||||||
"tooltip-format": "Mises à jour disponibles — clic pour installer",
|
|
||||||
"on-click": "kitty -e fws-update"
|
|
||||||
},
|
|
||||||
|
|
||||||
"hyprland/workspaces": {
|
|
||||||
"format": "{id}",
|
|
||||||
"on-click": "activate",
|
|
||||||
"on-scroll-up": "hyprctl dispatch workspace e-1",
|
|
||||||
"on-scroll-down": "hyprctl dispatch workspace e+1"
|
|
||||||
},
|
|
||||||
"hyprland/window": {
|
|
||||||
"max-length": 45,
|
|
||||||
"separate-outputs": true
|
|
||||||
},
|
|
||||||
"clock": {
|
|
||||||
"format": " {:%H:%M %A %d %B %Y}",
|
|
||||||
"format-alt": " {:%H:%M}",
|
|
||||||
"tooltip-format": "<tt>{calendar}</tt>",
|
|
||||||
"calendar": {
|
|
||||||
"format": {
|
|
||||||
"today": "<b><span color='#89b4fa'>{}</span></b>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"custom/keyhelp": {
|
|
||||||
"format": " Raccourcis",
|
|
||||||
"tooltip-format": "Raccourcis clavier (SUPER+K)",
|
|
||||||
"on-click": "fws-keys"
|
|
||||||
},
|
|
||||||
"backlight": {
|
|
||||||
"format": "{icon} {percent}%",
|
|
||||||
"format-icons": ["", "", ""],
|
|
||||||
"on-scroll-up": "brightnessctl set +5%",
|
|
||||||
"on-scroll-down": "brightnessctl set 5%-",
|
|
||||||
"tooltip-format": "Luminosité {percent}% (molette pour régler)"
|
|
||||||
},
|
|
||||||
"pulseaudio": {
|
|
||||||
"format": "{icon} {volume}%",
|
|
||||||
"format-muted": " muet",
|
|
||||||
"format-icons": { "default": ["", "", ""] },
|
|
||||||
"on-click": "pavucontrol",
|
|
||||||
"on-click-right": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
|
|
||||||
"on-scroll-up": "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 2%+",
|
|
||||||
"on-scroll-down": "wpctl set-volume @DEFAULT_AUDIO_SINK@ 2%-",
|
|
||||||
"tooltip-format": "{desc} — clic : mixeur, clic droit : muet"
|
|
||||||
},
|
|
||||||
"bluetooth": {
|
|
||||||
"format": "",
|
|
||||||
"format-disabled": "",
|
|
||||||
"format-connected": " {num_connections}",
|
|
||||||
"format-no-controller": "",
|
|
||||||
"on-click": "blueman-manager",
|
|
||||||
"tooltip-format": "{controller_alias}\t{controller_address}",
|
|
||||||
"tooltip-format-connected": "{device_enumerate}"
|
|
||||||
},
|
|
||||||
"network": {
|
|
||||||
"format-wifi": " {essid}",
|
|
||||||
"format-ethernet": " {ipaddr}",
|
|
||||||
"format-disconnected": " hors ligne",
|
|
||||||
"tooltip-format": "{ifname} — {ipaddr}\n⇡ {bandwidthUpBytes} ⇣ {bandwidthDownBytes}"
|
|
||||||
},
|
|
||||||
"cpu": {
|
|
||||||
"format": " {usage}%",
|
|
||||||
"interval": 3
|
|
||||||
},
|
|
||||||
"memory": {
|
|
||||||
"format": " {percentage}%",
|
|
||||||
"interval": 3,
|
|
||||||
"tooltip-format": "{used:0.1f} / {total:0.1f} Gio"
|
|
||||||
},
|
|
||||||
"battery": {
|
|
||||||
"states": { "warning": 25, "critical": 10 },
|
|
||||||
"format": "{icon} {capacity}%",
|
|
||||||
"format-charging": " {capacity}%",
|
|
||||||
"format-icons": ["", "", "", "", ""],
|
|
||||||
"tooltip-format": "{timeTo}"
|
|
||||||
},
|
|
||||||
"tray": {
|
|
||||||
"spacing": 8,
|
|
||||||
"icon-size": 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
/* FWS — style Waybar v2 (Catppuccin Mocha, nuances de bleu).
|
|
||||||
Barre flottante arrondie, modules en « pilules », icônes Nerd Font
|
|
||||||
(paquet ttf-nerd-fonts-symbols, installé avec le bureau Hyprland). */
|
|
||||||
|
|
||||||
* {
|
|
||||||
font-family: "Noto Sans", "Symbols Nerd Font", sans-serif;
|
|
||||||
font-size: 13px;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
window#waybar {
|
|
||||||
background: rgba(30, 30, 46, 0.88); /* base #1e1e2e */
|
|
||||||
color: #cdd6f4; /* text */
|
|
||||||
border: 1px solid rgba(137, 180, 250, 0.35);
|
|
||||||
border-radius: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
tooltip {
|
|
||||||
background: #1e1e2e;
|
|
||||||
border: 1px solid #89b4fa;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
tooltip label { color: #cdd6f4; }
|
|
||||||
|
|
||||||
/* --- Espaces de travail --------------------------------------------------- */
|
|
||||||
#workspaces {
|
|
||||||
background: #313244; /* surface0 */
|
|
||||||
margin: 5px 4px;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button {
|
|
||||||
color: #a6adc8; /* subtext0 */
|
|
||||||
padding: 0 8px;
|
|
||||||
margin: 3px 2px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
background: transparent;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button.active {
|
|
||||||
color: #1e1e2e;
|
|
||||||
background: #89b4fa; /* bleu */
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button:hover {
|
|
||||||
color: #cdd6f4;
|
|
||||||
background: #45475a;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#window {
|
|
||||||
color: #a6adc8;
|
|
||||||
margin-left: 10px;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Horloge (centre) ------------------------------------------------------ */
|
|
||||||
#clock {
|
|
||||||
color: #cdd6f4;
|
|
||||||
font-weight: bold;
|
|
||||||
background: #313244;
|
|
||||||
padding: 0 16px;
|
|
||||||
margin: 5px 0;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Modules de droite : pilules ------------------------------------------- */
|
|
||||||
#custom-keyhelp,
|
|
||||||
#backlight,
|
|
||||||
#pulseaudio,
|
|
||||||
#bluetooth,
|
|
||||||
#network,
|
|
||||||
#cpu,
|
|
||||||
#memory,
|
|
||||||
#battery,
|
|
||||||
#tray {
|
|
||||||
background: #313244;
|
|
||||||
padding: 0 12px;
|
|
||||||
margin: 5px 3px;
|
|
||||||
border-radius: 10px;
|
|
||||||
color: #cdd6f4;
|
|
||||||
}
|
|
||||||
|
|
||||||
#custom-keyhelp {
|
|
||||||
background: #89b4fa; /* bouton d'appel : bleu plein */
|
|
||||||
color: #1e1e2e;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
#custom-keyhelp:hover { background: #b4d0fb; }
|
|
||||||
|
|
||||||
#custom-power {
|
|
||||||
color: #f38ba8; /* rouge doux (Catppuccin) */
|
|
||||||
}
|
|
||||||
#custom-power:hover { color: #ff6b81; }
|
|
||||||
|
|
||||||
#backlight { color: #f9e2af; } /* jaune doux */
|
|
||||||
#pulseaudio { color: #89b4fa; }
|
|
||||||
#pulseaudio.muted { color: #6c7086; }
|
|
||||||
#bluetooth { color: #74c7ec; } /* sapphire */
|
|
||||||
#bluetooth.disabled { color: #6c7086; }
|
|
||||||
#network { color: #94e2d5; } /* teal */
|
|
||||||
#network.disconnected { color: #f38ba8; }
|
|
||||||
#cpu { color: #cba6f7; } /* mauve */
|
|
||||||
#memory { color: #eba0ac; } /* maroon */
|
|
||||||
|
|
||||||
#battery.warning { color: #f9e2af; }
|
|
||||||
#battery.critical { color: #f38ba8; }
|
|
||||||
#battery.charging { color: #a6e3a1; }
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# FWS — configuration wofi (menu d'applications).
|
|
||||||
insensitive=true
|
|
||||||
allow_images=true
|
|
||||||
image_size=24
|
|
||||||
prompt=Applications
|
|
||||||
width=560
|
|
||||||
height=440
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/* FWS — style du panneau « Raccourcis clavier » (fws-keys).
|
|
||||||
Même palette que le menu wofi, mais police MONOSPACE : les colonnes
|
|
||||||
combinaison/description sont alignées par des espaces. */
|
|
||||||
|
|
||||||
window {
|
|
||||||
background-color: rgba(30, 30, 46, 0.97);
|
|
||||||
border: 2px solid #89b4fa;
|
|
||||||
border-radius: 14px;
|
|
||||||
font-family: "Noto Sans Mono", monospace;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#input {
|
|
||||||
background-color: #313244;
|
|
||||||
color: #cdd6f4;
|
|
||||||
border: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 12px;
|
|
||||||
padding: 8px 12px;
|
|
||||||
font-family: "Noto Sans", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
#input image { color: #89b4fa; }
|
|
||||||
|
|
||||||
#inner-box {
|
|
||||||
margin: 0 8px 8px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#entry {
|
|
||||||
padding: 5px 10px;
|
|
||||||
border-radius: 8px;
|
|
||||||
color: #cdd6f4;
|
|
||||||
}
|
|
||||||
|
|
||||||
#entry:selected {
|
|
||||||
background-color: #313244;
|
|
||||||
color: #89b4fa;
|
|
||||||
}
|
|
||||||
|
|
||||||
#text { color: inherit; }
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/* FWS — style wofi (menu d'applications Super+R). Catppuccin Mocha, bleu. */
|
|
||||||
|
|
||||||
window {
|
|
||||||
background-color: rgba(30, 30, 46, 0.96); /* base #1e1e2e */
|
|
||||||
border: 2px solid #89b4fa;
|
|
||||||
border-radius: 14px;
|
|
||||||
font-family: "Noto Sans", sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#input {
|
|
||||||
background-color: #313244; /* surface0 */
|
|
||||||
color: #cdd6f4;
|
|
||||||
border: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
margin: 12px;
|
|
||||||
padding: 8px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#input image { color: #89b4fa; }
|
|
||||||
|
|
||||||
#inner-box {
|
|
||||||
margin: 0 8px 8px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#entry {
|
|
||||||
padding: 8px 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
color: #cdd6f4;
|
|
||||||
}
|
|
||||||
|
|
||||||
#entry:selected {
|
|
||||||
background-color: #89b4fa;
|
|
||||||
color: #1e1e2e;
|
|
||||||
}
|
|
||||||
|
|
||||||
#entry:selected #text { color: #1e1e2e; }
|
|
||||||
|
|
||||||
#text {
|
|
||||||
margin-left: 8px;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- FWS — icône « Récupération FWS » : croix de secours, bleus FWS. -->
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
|
|
||||||
<rect x="3" y="3" width="58" height="58" rx="13" fill="#1e1e2e"/>
|
|
||||||
<rect x="3" y="3" width="58" height="58" rx="13" fill="none" stroke="#89b4fa" stroke-width="3"/>
|
|
||||||
<path d="M25 13h14v12h12v14H39v12H25V39H13V25h12z" fill="#89b4fa"/>
|
|
||||||
<path d="M28 16h8v12h12v8H36v12h-8V36H16v-8h12z" fill="#74c7ec"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 515 B |
@@ -5,36 +5,33 @@
|
|||||||
# lancée en mode interactif (chemin fixe codé en dur dans Anaconda :
|
# lancée en mode interactif (chemin fixe codé en dur dans Anaconda :
|
||||||
# /usr/share/anaconda/interactive-defaults.ks). Aucun inst.ks= n'est requis.
|
# /usr/share/anaconda/interactive-defaults.ks). Aucun inst.ks= n'est requis.
|
||||||
#
|
#
|
||||||
# RÔLE : finaliser une install « copie du live » (surtout pas Fedora).
|
# RÔLE : empêcher Anaconda d'installer Fedora.
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Anaconda n'a AUCUN backend pacman ; son payload par défaut (DNF) poserait
|
# Sans payload défini, Anaconda utilise DNF → des RPM depuis les dépôts
|
||||||
# des RPM Fedora. Ici le payload est LiveOS, imposé par « anaconda --liveinst »
|
# Fedora = il installe Fedora. Anaconda n'a AUCUN backend pacman. La seule
|
||||||
# (cf. bloc « Source d'installation » ci-dessous) : il recopie le squashfs du
|
# façon de poser FWS/Arch est le payload « liveimg » : il recopie une image
|
||||||
# live (= FWS) sur le disque, puis on finalise nous-mêmes (grub, mkinitcpio,
|
# de système de fichiers (ici le squashfs du live = FWS) sur le disque, puis
|
||||||
# bureau) en %post. C'est le modèle « Sabayon » : le système installé EST une
|
# on finalise nous-mêmes (grub, mkinitcpio, bureau) en %post. C'est le modèle
|
||||||
# copie du live.
|
# « Sabayon » : le système installé EST une copie du live.
|
||||||
#
|
#
|
||||||
# L'utilisateur garde la main dans le GUI sur tout ce qui N'EST PAS fixé ici :
|
# L'utilisateur garde la main dans le GUI sur tout ce qui N'EST PAS fixé ici :
|
||||||
# partitionnement, fuseau, clavier, langue, création d'utilisateur, mot de
|
# partitionnement, fuseau, clavier, langue, création d'utilisateur, mot de
|
||||||
# passe root, etc.
|
# passe root, etc.
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
# --- Source d'installation : le payload LiveOS (surtout pas Fedora/DNF) ------
|
# --- Source d'installation : l'image du live (FWS), surtout pas Fedora -------
|
||||||
# AUCUNE directive de payload ici, et c'est VOULU. liveinst lance
|
# Squashfs archiso sur le média booté :
|
||||||
# « anaconda --liveinst » et anaconda.py teste opts.liveinst AVANT le type du
|
# /run/archiso/bootmnt/<install_dir>/<arch>/airootfs.sfs
|
||||||
# payload DBus → il crée TOUJOURS un payload LiveOS et IGNORE tout payload du
|
# install_dir=fws, arch=x86_64, airootfs_image_type=squashfs (cf. profiledef.sh).
|
||||||
# kickstart (un « liveimg » ici serait du code mort — vérifié dans
|
# Anaconda loop-monte ce .sfs et rsync son contenu vers la cible.
|
||||||
# pyanaconda/anaconda.py, propriété payload, et reproduit hors VM).
|
|
||||||
#
|
#
|
||||||
# Le payload LiveOS détecte l'image live via /dev/mapper/live-base ou
|
# ⚠ À VALIDER sur ISO réelle : certaines versions d'Anaconda attendent une
|
||||||
# /run/rootfsbase (chemins Fedora/dracut, absents d'archiso → c'était le crash
|
# structure « LiveOS/rootfs.img » à l'intérieur du squashfs (format Fedora
|
||||||
# « SourceSetupError: No Live OS image found! » au démarrage). Le .xinitrc du
|
# Live) et non un squashfs « plat » comme celui d'archiso. Si l'install
|
||||||
# live bind-monte donc /run/archiso/airootfs (squashfs FWS, ro) sur
|
# échoue à monter la source, basculer sur un tarball de rootfs dédié :
|
||||||
# /run/rootfsbase AVANT de lancer liveinst : Anaconda monte le loop device et
|
# liveimg --url=file:///run/archiso/bootmnt/fws/x86_64/fws-rootfs.tar.gz
|
||||||
# rsync son contenu vers la cible = le système installé EST une copie du live,
|
# (un build-rootfs.sh produirait alors ce tar via pacstrap).
|
||||||
# puis on finalise en %post (grub, mkinitcpio, bureau, matériel).
|
liveimg --url=file:///run/archiso/bootmnt/fws/x86_64/airootfs.sfs
|
||||||
# DNF/libdnf5 n'est jamais importé : le module DNF n'est chargé QUE si le
|
|
||||||
# payload actif est DNF (import lazy dans modules/payloads/payload/factory.py).
|
|
||||||
|
|
||||||
# --- Disque : repartir d'un disque PROPRE, même s'il a déjà servi ------------
|
# --- Disque : repartir d'un disque PROPRE, même s'il a déjà servi ------------
|
||||||
# Sans ça, une réinstallation sur un disque déjà partitionné (typiquement une
|
# Sans ça, une réinstallation sur un disque déjà partitionné (typiquement une
|
||||||
@@ -72,21 +69,9 @@ clearpart --all --initlabel --disklabel=msdos
|
|||||||
# partitionnement (auto ou manuel) car la plateforme l'impose.
|
# partitionnement (auto ou manuel) car la plateforme l'impose.
|
||||||
bootloader --disabled
|
bootloader --disabled
|
||||||
|
|
||||||
# Pas de section %packages : le payload LiveOS n'installe aucun paquet
|
# Pas de section %packages : le payload liveimg n'installe aucun paquet
|
||||||
# (il copie une image). Toute installation de paquets se fait en %post.
|
# (il copie une image). Toute installation de paquets se fait en %post.
|
||||||
|
|
||||||
# --- Récapitulatif avant installation : PAGE GUI dédiée (pas de %pre-install) -
|
|
||||||
# L'écran « RÉCAPITULATIF AVANT INSTALLATION » est une standalone spoke
|
|
||||||
# Anaconda (overlay airootfs : pyanaconda/ui/gui/spokes/fws_recap.py,
|
|
||||||
# postForHub=SummaryHub, priority -10) intercalée ENTRE le hub et l'écran de
|
|
||||||
# progression : l'utilisateur clique « Commencer l'installation », voit le
|
|
||||||
# récap (bureau choisi, hostname, disques, GPU, réseau — AUCUNE écriture
|
|
||||||
# disque à ce stade), et l'installation ne démarre qu'à son clic « Lancer
|
|
||||||
# l'installation ». L'ancien récap zenity en %pre-install a été abandonné :
|
|
||||||
# le module scripts tourne avec LC_ALL=C et un env X incertain (zenity 4
|
|
||||||
# refuse les libellés UTF-8 en locale C → rc=255 pris pour « Annuler » →
|
|
||||||
# poweroff en plein install au 1er test réel).
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# %post --nochroot — s'exécute dans le LIVE, la cible montée sous /mnt/sysroot
|
# %post --nochroot — s'exécute dans le LIVE, la cible montée sous /mnt/sysroot
|
||||||
@@ -104,17 +89,6 @@ SYSROOT=/mnt/sysroot
|
|||||||
# DNS pour que « pacman -S <bureau> » fonctionne dans le chroot.
|
# DNS pour que « pacman -S <bureau> » fonctionne dans le chroot.
|
||||||
cp -f /etc/resolv.conf "$SYSROOT/etc/resolv.conf" 2>/dev/null || true
|
cp -f /etc/resolv.conf "$SYSROOT/etc/resolv.conf" 2>/dev/null || true
|
||||||
|
|
||||||
# --- Hostname : valeur saisie dans le champ « Nom de machine » du spoke User --
|
|
||||||
# Anaconda ne capte PAS le hostname du spoke Réseau (→ /etc/hostname vide). On a
|
|
||||||
# donc ajouté un champ Hostname au spoke Utilisateur (cf. patch du paquet
|
|
||||||
# anaconda) : user.py écrit la valeur dans /tmp/fws-hostname (live). On la
|
|
||||||
# nettoie (caractères de hostname valides) et on l'écrit dans la cible ; défaut
|
|
||||||
# « fws » si vide/absent → plus jamais de hostname vide ni « archiso ».
|
|
||||||
HN=""
|
|
||||||
[ -f /tmp/fws-hostname ] && HN="$(tr -cd 'a-zA-Z0-9._-' < /tmp/fws-hostname)"
|
|
||||||
[ -n "$HN" ] || HN="fws"
|
|
||||||
echo "$HN" > "$SYSROOT/etc/hostname"
|
|
||||||
|
|
||||||
# Choix du bureau (écrit par l'addon « Choix du bureau », à venir ; valeur lue
|
# Choix du bureau (écrit par l'addon « Choix du bureau », à venir ; valeur lue
|
||||||
# par le %post chrooté ci-dessous). « cli » par défaut si absent.
|
# par le %post chrooté ci-dessous). « cli » par défaut si absent.
|
||||||
if [ -f /tmp/fws-desktop ]; then
|
if [ -f /tmp/fws-desktop ]; then
|
||||||
@@ -220,378 +194,78 @@ else
|
|||||||
echo "[FWS] AVERTISSEMENT : grub-install a échoué — bootloader à refaire à la main"
|
echo "[FWS] AVERTISSEMENT : grub-install a échoué — bootloader à refaire à la main"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- 3bis) Clavier (X11) + LOCALE --------------------------------------------
|
# --- 4) Drivers matériels : détection + activation/installation AUTO ----------
|
||||||
# CLAVIER : Anaconda passe par localed et n'écrit pas fiablement la disposition X
|
# Toute la DÉTECTION vit dans /usr/local/bin/fws-hw-detect : livré par
|
||||||
# sur la CIBLE → SDDM / sessions X en QWERTY US. On CALCULE la disposition CHOISIE
|
# l'airootfs → recopié dans la cible par le payload liveimg → présent dans ce
|
||||||
# (kickstart généré, sinon dérivée de la locale via langtable) en XL/XV, réutilisés
|
# chroot. C'est la MÊME source de vérité que l'écran « Récapitulatif avant
|
||||||
# plus bas (00-keyboard.conf pour SDDM/X ET la config Hyprland qui a son propre
|
# installation » du live (.xinitrc, mode --text) : ce qui a été ANNONCÉ à
|
||||||
# kb_layout). LOCALE : le locale choisi (ex. fr_CH.UTF-8) doit être GÉNÉRÉ —
|
# l'utilisateur = ce qu'on APPLIQUE ici. La détection marche en chroot car
|
||||||
# Anaconda vient de Fedora (locales pré-générées) et ne lance pas locale-gen →
|
# Anaconda bind-monte le /sys et le /proc du LIVE (le code plus haut s'appuie
|
||||||
# sans ça LANG casse et tout retombe en C.
|
# déjà dessus : /sys/firmware/efi, findmnt) → on voit le VRAI matériel.
|
||||||
XL=""; XV=""
|
# Variables produites par --env :
|
||||||
XKB="$(sed -n "s/.*--xlayouts=//p" /root/anaconda-ks.cfg 2>/dev/null | head -1)"
|
# FWS_VIRT hyperviseur (vmware/oracle/kvm/qemu/none…)
|
||||||
XKB="${XKB#[\'\"]}"; XKB="${XKB%%[\'\"]*}"; XKB="${XKB%%,*}" # 1re dispo, ex. « ch (fr) »
|
# FWS_VIRT_SVC services invité à activer (paquets DÉJÀ dans l'image → OK
|
||||||
if [ -z "$XKB" ]; then
|
# hors ligne ; c'était le chaînon manquant, rien n'activait
|
||||||
_loc="$(. /etc/locale.conf 2>/dev/null; printf '%s' "${LANG%%.*}")" # fr_CH
|
# vmtoolsd & co sur le système installé)
|
||||||
_lng="${_loc%%_*}"; _terr="${_loc#*_}"; [ "$_terr" = "$_loc" ] && _terr=""
|
# FWS_VIRT_RM guest tools des AUTRES hyperviseurs, à purger
|
||||||
[ -n "$_terr" ] && XKB="$(python3 -c "import langtable; k=langtable.list_keyboards(languageId='$_lng', territoryId='$_terr'); print(k[0] if k else '')" 2>/dev/null)"
|
# FWS_DRV_PKGS paquets GPU à installer, dédoublonnés. NVIDIA embarque
|
||||||
|
# « linux » dans la même transaction : le module des dépôts
|
||||||
|
# est bâti pour le noyau des dépôts, pas celui du live ; les
|
||||||
|
# chemins /boot/* étant stables, le grub.cfg de l'étape 3
|
||||||
|
# reste valable (le hook pacman de nvidia relance mkinitcpio).
|
||||||
|
# NB : microcode CPU (amd-ucode/intel-ucode) et linux-firmware sont DÉJÀ dans
|
||||||
|
# l'image → recopiés, et grub-mkconfig (étape 3) a généré les initrd qui vont
|
||||||
|
# bien. Rien à faire pour eux.
|
||||||
|
if [ -x /usr/local/bin/fws-hw-detect ]; then
|
||||||
|
eval "$(/usr/local/bin/fws-hw-detect --env)"
|
||||||
|
# (a) Hyperviseur : activer le bon service invité, purger les autres.
|
||||||
|
[ -n "$FWS_VIRT_SVC" ] && { systemctl enable $FWS_VIRT_SVC || true; }
|
||||||
|
[ -n "$FWS_VIRT_RM" ] && { pacman -Rns --noconfirm $FWS_VIRT_RM || true; }
|
||||||
|
# (b) GPU : install réseau, NON bloquante (même philosophie que le
|
||||||
|
# bureau). --needed : ne réinstalle pas ce que l'image a déjà (mesa…).
|
||||||
|
if [ -n "$FWS_DRV_PKGS" ]; then
|
||||||
|
pacman-key --init &>/dev/null || true
|
||||||
|
pacman-key --populate archlinux &>/dev/null || true
|
||||||
|
if ! pacman -Sy --noconfirm --needed $FWS_DRV_PKGS; then
|
||||||
|
echo "[FWS] Drivers non installés ($FWS_DRV_PKGS) — réseau ? À refaire au 1er boot."
|
||||||
fi
|
fi
|
||||||
if [ -n "$XKB" ]; then
|
fi
|
||||||
XL="${XKB%%(*}"; XL="${XL%% *}" # ch
|
else
|
||||||
case "$XKB" in *'('*')') XV="${XKB#*(}"; XV="${XV%)}" ;; *) XV="" ;; esac # fr
|
echo "[FWS] fws-hw-detect introuvable — drivers/guest tools NON configurés."
|
||||||
install -d /etc/X11/xorg.conf.d
|
|
||||||
cat > /etc/X11/xorg.conf.d/00-keyboard.conf <<KBEOF
|
|
||||||
Section "InputClass"
|
|
||||||
Identifier "system-keyboard"
|
|
||||||
MatchIsKeyboard "on"
|
|
||||||
Option "XkbLayout" "$XL"
|
|
||||||
Option "XkbVariant" "$XV"
|
|
||||||
EndSection
|
|
||||||
KBEOF
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
FWSLANG="$(. /etc/locale.conf 2>/dev/null; printf '%s' "${LANG:-}")"
|
# --- 5) Bureau choisi : pacman -S du DE (non bloquant si pas de réseau) -------
|
||||||
if [ -n "$FWSLANG" ]; then
|
|
||||||
_cs="${FWSLANG##*.}"; [ "$_cs" = "$FWSLANG" ] && _cs="UTF-8"
|
|
||||||
grep -qE "^[[:space:]]*${FWSLANG}[[:space:]]+${_cs}\b" /etc/locale.gen 2>/dev/null \
|
|
||||||
|| printf '%s %s\n' "$FWSLANG" "$_cs" >> /etc/locale.gen
|
|
||||||
locale-gen || echo "[FWS] locale-gen a échoué pour $FWSLANG"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- 4) Bureau choisi : pacman -S du DE (non bloquant si pas de réseau) -------
|
|
||||||
DESK="cli"
|
DESK="cli"
|
||||||
[ -f /tmp/fws-desktop ] && DESK="$(tr -d '[:space:]' < /tmp/fws-desktop)"
|
[ -f /tmp/fws-desktop ] && DESK="$(tr -d '[:space:]' < /tmp/fws-desktop)"
|
||||||
DM=""
|
DM=""
|
||||||
case "$DESK" in
|
case "$DESK" in
|
||||||
kde) PKGS="plasma-meta sddm konsole dolphin"; DM="sddm" ;;
|
kde) PKGS="plasma-meta sddm konsole dolphin"; DM="sddm" ;;
|
||||||
gnome) PKGS="gnome gdm"; DM="gdm" ;;
|
gnome) PKGS="gnome gdm"; DM="gdm" ;;
|
||||||
# noto-fonts : KDE l'a via plasma-meta et GNOME a cantarell, mais XFCE et
|
xfce) PKGS="xfce4 xfce4-goodies lightdm lightdm-gtk-greeter"; DM="lightdm" ;;
|
||||||
# Hyprland ne garantissent AUCUNE police TTF → greeter SDDM/apps illisibles.
|
hyprland) PKGS="hyprland kitty wofi waybar sddm"; DM="sddm" ;;
|
||||||
# Hyprland : la stack des raccourcis FWS (mêmes touches que la config
|
|
||||||
# i3/sway maison) — dolphin (Super+E), hyprlock (Super+L), grim+slurp+
|
|
||||||
# wl-clipboard (captures), playerctl (touches multimédia) — plus la
|
|
||||||
# plomberie de bureau : mako+libnotify (notifications), polkit-gnome
|
|
||||||
# (agent d'authentification GUI), network-manager-applet (réseau dans le
|
|
||||||
# tray), xdg-desktop-portal-hyprland (partage d'écran/portails), gvfs
|
|
||||||
# (montage USB via udisks2), ttf-nerd-fonts-symbols (icônes waybar).
|
|
||||||
# Son et Bluetooth GARANTIS avec le bureau : la stack PipeWire + le
|
|
||||||
# mixeur (pavucontrol) + bluez/blueman partent dans la MÊME transaction
|
|
||||||
# pacman que le DE — si le bureau s'installe, ses gestionnaires aussi
|
|
||||||
# (fws-setup-hardware les re-propose ensuite en --needed : sans effet
|
|
||||||
# s'ils sont déjà là, filet de secours sinon).
|
|
||||||
xfce) PKGS="xfce4 xfce4-goodies sddm noto-fonts pipewire pipewire-pulse wireplumber pavucontrol"; DM="sddm" ;;
|
|
||||||
hyprland) PKGS="hyprland kitty wofi waybar sddm noto-fonts dolphin kio-extras udisks2 hyprlock grim slurp wl-clipboard playerctl mako libnotify polkit-gnome network-manager-applet xdg-desktop-portal-hyprland ttf-nerd-fonts-symbols pipewire pipewire-alsa pipewire-pulse wireplumber pavucontrol bluez bluez-utils blueman"; DM="sddm" ;;
|
|
||||||
# i3 : session X11 → COMPATIBLE VR (SteamVR ne marche pas sous Wayland).
|
|
||||||
# xorg-server est requis (SDDM ne le tire pas) ; maim+xclip = captures X11,
|
|
||||||
# dunst = notifications X11 (équivalent de mako), i3lock = verrouillage.
|
|
||||||
# fws-vr-setup (Super+Shift+V ou menu) installe les drivers du casque.
|
|
||||||
i3) PKGS="i3-wm i3lock xorg-server polybar picom rofi kitty dolphin kio-extras udisks2 sddm noto-fonts ttf-nerd-fonts-symbols maim xclip dunst libnotify polkit-gnome network-manager-applet playerctl brightnessctl pipewire pipewire-alsa pipewire-pulse wireplumber pavucontrol bluez bluez-utils blueman"; DM="sddm" ;;
|
|
||||||
cli|*) PKGS="" ;;
|
cli|*) PKGS="" ;;
|
||||||
esac
|
esac
|
||||||
# Navigateur de base ajouté DÈS QU'un bureau est choisi (jamais en CLI).
|
|
||||||
# + python-gobject : fenêtre de bienvenue fws-hello (GTK3 vient avec Firefox)
|
|
||||||
# + pacman-contrib : checkupdates/paccache (indicateur de MàJ + fws-update)
|
|
||||||
[ -n "$PKGS" ] && PKGS="$PKGS firefox python-gobject pacman-contrib"
|
|
||||||
if [ -n "$PKGS" ]; then
|
if [ -n "$PKGS" ]; then
|
||||||
pacman-key --init &>/dev/null || true
|
pacman-key --init &>/dev/null || true
|
||||||
pacman-key --populate archlinux &>/dev/null || true
|
pacman-key --populate archlinux &>/dev/null || true
|
||||||
|
if pacman -Sy --noconfirm $PKGS; then
|
||||||
# Script de RATTRAPAGE généré avec les valeurs de CE choix : si l'install
|
|
||||||
# du bureau échoue (réseau/miroirs), l'utilisateur la rejoue en une
|
|
||||||
# commande depuis le système installé. --needed → inoffensif si déjà fait.
|
|
||||||
cat > /usr/local/bin/fws-desktop-retry <<RETRYEOF
|
|
||||||
#!/bin/bash
|
|
||||||
# Généré par l'installateur FWS — (ré)installe le bureau « $DESK ».
|
|
||||||
set -u
|
|
||||||
[ "\$(id -u)" -eq 0 ] || exec sudo "\$0" "\$@"
|
|
||||||
echo "==> Installation du bureau $DESK…"
|
|
||||||
pacman -Syy --noconfirm --needed $PKGS || { echo "Échec (réseau ?) — réessayez."; exit 1; }
|
|
||||||
[ -n "$DM" ] && systemctl enable "$DM"
|
|
||||||
systemctl set-default graphical.target
|
|
||||||
rm -f /var/lib/fws/desktop-failed
|
|
||||||
[ -f /etc/motd ] && grep -q fws-desktop-retry /etc/motd && rm -f /etc/motd
|
|
||||||
echo "==> Bureau installé ✔ — redémarrez (reboot) pour arriver sur l'écran de connexion."
|
|
||||||
RETRYEOF
|
|
||||||
chmod 755 /usr/local/bin/fws-desktop-retry
|
|
||||||
|
|
||||||
# Le réseau peut mettre quelques secondes à monter dans l'environnement
|
|
||||||
# d'installation : on attend la résolution DNS avant le premier essai,
|
|
||||||
# puis on tente pacman jusqu'à 3 fois (miroir capricieux ≠ échec définitif).
|
|
||||||
for _i in 1 2 3 4 5 6 7 8 9 10; do
|
|
||||||
getent hosts geo.mirror.pkgbuild.com >/dev/null 2>&1 && break
|
|
||||||
sleep 3
|
|
||||||
done
|
|
||||||
_DE_OK=""
|
|
||||||
for _try in 1 2 3; do
|
|
||||||
pacman -Sy --noconfirm --needed $PKGS && { _DE_OK=1; break; }
|
|
||||||
echo "[FWS] installation du bureau : tentative $_try échouée — nouvel essai dans 15 s…"
|
|
||||||
sleep 15
|
|
||||||
done
|
|
||||||
if [ -n "$_DE_OK" ]; then
|
|
||||||
[ -n "$DM" ] && systemctl enable "$DM" || true
|
[ -n "$DM" ] && systemctl enable "$DM" || true
|
||||||
systemctl set-default graphical.target || true
|
systemctl set-default graphical.target || true
|
||||||
# SDDM (KDE / XFCE / Hyprland) : pavé numérique + écran de connexion FWS.
|
|
||||||
# Le thème « fws » (livré par l'airootfs → recopié par le payload dans
|
|
||||||
# /usr/share/sddm/themes/fws) est en pur QtQuick/Controls Basic :
|
|
||||||
# qt6-declarative, dépendance de sddm, suffit → même écran soigné pour
|
|
||||||
# les 3 DE SANS paquet de thème supplémentaire (Breeze n'existe qu'avec
|
|
||||||
# plasma). Accent adapté au bureau via theme.conf.user (mécanisme de
|
|
||||||
# surcharge standard de SDDM, lu par-dessus theme.conf du thème).
|
|
||||||
if [ "$DM" = "sddm" ]; then
|
|
||||||
install -d /etc/sddm.conf.d
|
|
||||||
cat > /etc/sddm.conf.d/10-fws.conf <<'SDDMEOF'
|
|
||||||
[General]
|
|
||||||
Numlock=on
|
|
||||||
|
|
||||||
[Theme]
|
|
||||||
Current=fws
|
|
||||||
SDDMEOF
|
|
||||||
case "$DESK" in
|
|
||||||
xfce) _ACCENT="#16a085" ;; # sarcelle (assorti à XFCE)
|
|
||||||
hyprland) _ACCENT="#89b4fa" ;; # bleu Catppuccin (assorti à Hyprland)
|
|
||||||
*) _ACCENT="#3daee9" ;; # bleu Breeze (KDE, défaut)
|
|
||||||
esac
|
|
||||||
if [ -d /usr/share/sddm/themes/fws ]; then
|
|
||||||
printf '[General]\naccent=%s\n' "$_ACCENT" \
|
|
||||||
> /usr/share/sddm/themes/fws/theme.conf.user
|
|
||||||
else
|
else
|
||||||
echo "[FWS] thème SDDM fws introuvable — thème par défaut conservé"
|
echo "[FWS] Bureau '$DESK' non installé (réseau ?) — à refaire après le 1er boot."
|
||||||
sed -i '/^Current=fws$/d' /etc/sddm.conf.d/10-fws.conf
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# GDM (GNOME) : « la même chose » autant que possible — le greeter de
|
|
||||||
# GDM EST GNOME Shell, il ne se thème pas comme SDDM. On passe par
|
|
||||||
# dconf (base système « gdm ») : bannière d'accueil FWS, horloge avec
|
|
||||||
# date complète, pavé numérique allumé — même confort qu'avec SDDM.
|
|
||||||
if [ "$DM" = "gdm" ]; then
|
|
||||||
install -d /etc/dconf/profile /etc/dconf/db/gdm.d
|
|
||||||
cat > /etc/dconf/profile/gdm <<'GDMEOF'
|
|
||||||
user-db:user
|
|
||||||
system-db:gdm
|
|
||||||
file-db:/usr/share/gdm/greeter-dconf-defaults
|
|
||||||
GDMEOF
|
|
||||||
cat > /etc/dconf/db/gdm.d/10-fws <<'GDMEOF'
|
|
||||||
[org/gnome/login-screen]
|
|
||||||
banner-message-enable=true
|
|
||||||
banner-message-text='Bienvenue sur FWS'
|
|
||||||
|
|
||||||
[org/gnome/desktop/interface]
|
|
||||||
clock-show-date=true
|
|
||||||
clock-show-weekday=true
|
|
||||||
|
|
||||||
[org/gnome/desktop/peripherals/keyboard]
|
|
||||||
numlock-state=true
|
|
||||||
remember-numlock-state=false
|
|
||||||
GDMEOF
|
|
||||||
dconf update || echo "[FWS] dconf update a échoué (bannière GDM à refaire)"
|
|
||||||
fi
|
|
||||||
# Hyprland en VM : sur GPU virtuel (VMware/VirtualBox) le compositeur
|
|
||||||
# crashe au lancement → SDDM « remet au login ». On le lance via un
|
|
||||||
# wrapper qui force le rendu LOGICIEL UNIQUEMENT en VM (systemd-detect-virt)
|
|
||||||
# ; sur un vrai GPU le rendu matériel est conservé.
|
|
||||||
if [ "$DESK" = "hyprland" ]; then
|
|
||||||
cat > /usr/local/bin/fws-hyprland <<'HYEOF'
|
|
||||||
#!/bin/sh
|
|
||||||
if systemd-detect-virt --quiet 2>/dev/null; then
|
|
||||||
# GPU virtuel (VMware vmwgfx…) : on FORCE le chemin d'affichage 100% logiciel.
|
|
||||||
# La PIÈCE MAÎTRESSE est MESA_LOADER_DRIVER_OVERRIDE=kms_swrast : elle force
|
|
||||||
# Mesa à allouer le GBM/EGL via kms_swrast (rendu logiciel dans les « dumb
|
|
||||||
# buffers » KMS) AU LIEU du pilote vmwgfx (qui ne fournit pas le GBM dont
|
|
||||||
# aquamarine a besoin → « CBackend::create() failed »). + llvmpipe pour le GL.
|
|
||||||
export MESA_LOADER_DRIVER_OVERRIDE=kms_swrast
|
|
||||||
export GALLIUM_DRIVER=llvmpipe LIBGL_ALWAYS_SOFTWARE=1
|
|
||||||
export WLR_RENDERER_ALLOW_SOFTWARE=1 WLR_NO_HARDWARE_CURSORS=1
|
|
||||||
export AQ_NO_ATOMIC=1
|
|
||||||
fi
|
|
||||||
# Hyprland ≥ 0.53 : start-hyprland est le lanceur officiel (prépare la
|
|
||||||
# session/env systemd) — sans lui, bandeau « started without start-hyprland ».
|
|
||||||
if command -v start-hyprland >/dev/null 2>&1; then
|
|
||||||
exec start-hyprland "$@"
|
|
||||||
fi
|
|
||||||
exec Hyprland "$@"
|
|
||||||
HYEOF
|
|
||||||
chmod 755 /usr/local/bin/fws-hyprland
|
|
||||||
if [ -f /usr/share/wayland-sessions/hyprland.desktop ]; then
|
|
||||||
sed -i 's|^Exec=.*|Exec=/usr/local/bin/fws-hyprland|' \
|
|
||||||
/usr/share/wayland-sessions/hyprland.desktop
|
|
||||||
fi
|
|
||||||
# Bluetooth prêt au 1er démarrage (bluez vient d'être installé
|
|
||||||
# avec le bureau ; fws-setup-hardware le refait — idempotent).
|
|
||||||
systemctl enable bluetooth.service 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
if [ "$DESK" = "i3" ]; then
|
|
||||||
systemctl enable bluetooth.service 2>/dev/null || true
|
|
||||||
# Icône du lanceur « Installation VR » (casque) visible dans rofi.
|
|
||||||
gtk-update-icon-cache -f /usr/share/icons/hicolor 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "[FWS] Bureau '$DESK' NON installé après 3 tentatives (réseau/miroirs ?)."
|
|
||||||
echo "[FWS] Rattrapage sur le système installé : sudo fws-desktop-retry"
|
|
||||||
mkdir -p /var/lib/fws
|
|
||||||
echo "$DESK" > /var/lib/fws/desktop-failed
|
|
||||||
# Le premier login (console) explique quoi faire — le motd est retiré
|
|
||||||
# par fws-desktop-retry une fois le bureau posé.
|
|
||||||
cat > /etc/motd <<'MOTD'
|
|
||||||
|
|
||||||
⚠ FWS : l'installation du bureau a échoué (réseau indisponible pendant
|
|
||||||
l'installation). Le système fonctionne, mais en console.
|
|
||||||
|
|
||||||
Une fois le réseau branché, lancez : sudo fws-desktop-retry
|
|
||||||
puis redémarrez : le bureau choisi sera installé et activé.
|
|
||||||
|
|
||||||
MOTD
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- 4quater) ADN FWS : détection matériel + drivers + stack « Windows » ------
|
# --- 6) Sécurité : verrouiller root s'il est resté SANS mot de passe ----------
|
||||||
# Script dédié (NON bloquant ; réseau requis) : drivers GPU détectés via lspci +
|
|
||||||
# outils invité VM, son/micro/casque (PipeWire), Bluetooth, Podman, et — si un
|
|
||||||
# bureau est choisi — Steam, Wine, VR (Monado/SteamVR), winboat-bin (apps
|
|
||||||
# Windows). C'est le cœur « remplacement de Windows » de FWS.
|
|
||||||
if [ -x /usr/local/bin/fws-setup-hardware ]; then
|
|
||||||
/usr/local/bin/fws-setup-hardware "$DESK" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Config Hyprland (si choisi) : clavier + apps + waybar -------------------
|
|
||||||
# Hyprland (Wayland) NE lit PAS /etc/X11/xorg.conf.d/00-keyboard.conf : sa
|
|
||||||
# disposition est dans SA config (input { kb_layout }). Sans config il démarre nu
|
|
||||||
# (QWERTY US, aucune touche utile). On déploie une config FWS (template +
|
|
||||||
# clavier XL/XV injecté) dans /etc/skel ET le home de chaque utilisateur déjà
|
|
||||||
# créé par Anaconda (le %post tourne APRÈS la création de l'utilisateur).
|
|
||||||
if [ "$DESK" = "hyprland" ] && [ -f /usr/local/share/fws/hyprland.conf.tmpl ]; then
|
|
||||||
sed -e "s/__KB_LAYOUT__/${XL:-us}/" -e "s/__KB_VARIANT__/${XV}/" \
|
|
||||||
/usr/local/share/fws/hyprland.conf.tmpl > /tmp/fws-hyprland.conf
|
|
||||||
install -Dm644 /tmp/fws-hyprland.conf /etc/skel/.config/hypr/hyprland.conf
|
|
||||||
# Waybar FWS : barre assortie + bouton « ⌨ Raccourcis » (fws-keys,
|
|
||||||
# aussi sur SUPER+K) qui liste les binds RÉELS chargés par Hyprland
|
|
||||||
# (hyprctl binds -j) — les descriptions viennent des bindd de la config.
|
|
||||||
install -Dm644 /usr/local/share/fws/waybar-config.json /etc/skel/.config/waybar/config
|
|
||||||
install -Dm644 /usr/local/share/fws/waybar-style.css /etc/skel/.config/waybar/style.css
|
|
||||||
# wofi : style du menu d'applications (Super+R) — le panneau « Raccourcis »
|
|
||||||
# a son propre style (wofi-keys.css), passé en --style par fws-keys.
|
|
||||||
install -Dm644 /usr/local/share/fws/wofi-config /etc/skel/.config/wofi/config
|
|
||||||
install -Dm644 /usr/local/share/fws/wofi-style.css /etc/skel/.config/wofi/style.css
|
|
||||||
while IFS=: read -r _u _x _uid _g _gec _home _sh; do
|
|
||||||
[ "$_uid" -ge 1000 ] && [ "$_uid" -le 60000 ] && [ -d "$_home" ] || continue
|
|
||||||
install -Dm644 /tmp/fws-hyprland.conf "$_home/.config/hypr/hyprland.conf"
|
|
||||||
install -Dm644 /usr/local/share/fws/waybar-config.json "$_home/.config/waybar/config"
|
|
||||||
install -Dm644 /usr/local/share/fws/waybar-style.css "$_home/.config/waybar/style.css"
|
|
||||||
install -Dm644 /usr/local/share/fws/wofi-config "$_home/.config/wofi/config"
|
|
||||||
install -Dm644 /usr/local/share/fws/wofi-style.css "$_home/.config/wofi/style.css"
|
|
||||||
chown "$_u": "$_home/.config" 2>/dev/null || true
|
|
||||||
chown -R "$_u": "$_home/.config/hypr" "$_home/.config/waybar" \
|
|
||||||
"$_home/.config/wofi" 2>/dev/null || true
|
|
||||||
done < /etc/passwd
|
|
||||||
rm -f /tmp/fws-hyprland.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Config i3 (si choisi) : raccourcis FWS + barre + autostart -------------
|
|
||||||
# Pas d'injection clavier ici : les sessions X11 lisent la disposition dans
|
|
||||||
# /etc/X11/xorg.conf.d/00-keyboard.conf, écrite à l'étape 3bis.
|
|
||||||
if [ "$DESK" = "i3" ] && [ -f /usr/local/share/fws/i3-config ]; then
|
|
||||||
install -Dm644 /usr/local/share/fws/i3-config /etc/skel/.config/i3/config
|
|
||||||
# Polybar (clone de la waybar) + picom (coins arrondis) : même rendu
|
|
||||||
# visuel que la session Hyprland FWS.
|
|
||||||
install -Dm644 /usr/local/share/fws/polybar-config.ini /etc/skel/.config/polybar/config.ini
|
|
||||||
install -Dm644 /usr/local/share/fws/picom.conf /etc/skel/.config/picom.conf
|
|
||||||
# rofi : menu d'applications au look FWS (équivalent du wofi d'Hyprland).
|
|
||||||
install -Dm644 /usr/local/share/fws/rofi-config.rasi /etc/skel/.config/rofi/config.rasi
|
|
||||||
while IFS=: read -r _u _x _uid _g _gec _home _sh; do
|
|
||||||
[ "$_uid" -ge 1000 ] && [ "$_uid" -le 60000 ] && [ -d "$_home" ] || continue
|
|
||||||
install -Dm644 /usr/local/share/fws/i3-config "$_home/.config/i3/config"
|
|
||||||
install -Dm644 /usr/local/share/fws/polybar-config.ini "$_home/.config/polybar/config.ini"
|
|
||||||
install -Dm644 /usr/local/share/fws/picom.conf "$_home/.config/picom.conf"
|
|
||||||
install -Dm644 /usr/local/share/fws/rofi-config.rasi "$_home/.config/rofi/config.rasi"
|
|
||||||
chown "$_u": "$_home/.config" 2>/dev/null || true
|
|
||||||
chown -R "$_u": "$_home/.config/i3" "$_home/.config/polybar" \
|
|
||||||
"$_home/.config/rofi" "$_home/.config/picom.conf" 2>/dev/null || true
|
|
||||||
done < /etc/passwd
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- 4bis) Sudo : l'utilisateur créé doit pouvoir administrer -----------------
|
|
||||||
# Arch laisse « %wheel » COMMENTÉ dans sudoers → même un membre de wheel ne peut
|
|
||||||
# pas sudo. On active le groupe wheel (drop-in) ET on y ajoute tous les
|
|
||||||
# utilisateurs HUMAINS (UID 1000-60000) créés par Anaconda → ils sont sudoers.
|
|
||||||
install -d -m 750 /etc/sudoers.d
|
|
||||||
printf '%%wheel ALL=(ALL:ALL) ALL\n' > /etc/sudoers.d/10-fws-wheel
|
|
||||||
chmod 440 /etc/sudoers.d/10-fws-wheel
|
|
||||||
while IFS=: read -r _u _x _uid _rest; do
|
|
||||||
if [ "$_uid" -ge 1000 ] && [ "$_uid" -le 60000 ]; then
|
|
||||||
usermod -aG wheel "$_u" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
done < /etc/passwd
|
|
||||||
|
|
||||||
# --- 4ter) Branding FWS sur le système installé -------------------------------
|
|
||||||
# Le hook os-release du build n'est PAS sur la cible et « pacman -S » a pu
|
|
||||||
# remettre l'os-release d'Arch → d'où « Arch Linux » au menu GRUB, le logo Arch
|
|
||||||
# sur l'écran de connexion (GDM lit os-release LOGO) et le nom « Arch ». On FORCE
|
|
||||||
# donc l'os-release FWS (+ lsb-release + GRUB_DISTRIBUTOR), on RÉGÉNÈRE le menu
|
|
||||||
# GRUB (10_linux lit le NAME d'os-release), et on retire le motd « live ».
|
|
||||||
if [ -f /usr/local/share/fws/os-release ]; then
|
|
||||||
cp -f /usr/local/share/fws/os-release /usr/lib/os-release
|
|
||||||
ln -sf ../usr/lib/os-release /etc/os-release
|
|
||||||
fi
|
|
||||||
if [ -f /etc/lsb-release ]; then
|
|
||||||
sed -i 's/^DISTRIB_ID=.*/DISTRIB_ID=FWS/; s/^DISTRIB_DESCRIPTION=.*/DISTRIB_DESCRIPTION="FWS Linux"/' \
|
|
||||||
/etc/lsb-release
|
|
||||||
fi
|
|
||||||
if grep -q '^GRUB_DISTRIBUTOR=' /etc/default/grub 2>/dev/null; then
|
|
||||||
sed -i 's/^GRUB_DISTRIBUTOR=.*/GRUB_DISTRIBUTOR="FWS"/' /etc/default/grub
|
|
||||||
else
|
|
||||||
echo 'GRUB_DISTRIBUTOR="FWS"' >> /etc/default/grub
|
|
||||||
fi
|
|
||||||
grub-mkconfig -o /boot/grub/grub.cfg 2>/dev/null || true
|
|
||||||
# motd « live » (texte inadapté + codes ANSI bruts affichés crus par GDM) —
|
|
||||||
# SAUF si c'est notre message « fws-desktop-retry » (échec d'install du
|
|
||||||
# bureau), qui doit survivre jusqu'au rattrapage.
|
|
||||||
grep -q fws-desktop-retry /etc/motd 2>/dev/null || rm -f /etc/motd
|
|
||||||
|
|
||||||
# --- 4quinquies) Pare-feu par défaut -------------------------------------------
|
|
||||||
# firewalld est dans l'IMAGE (packages.x86_64) → recopié par le payload,
|
|
||||||
# activation possible HORS LIGNE. Zone par défaut « public » : entrées
|
|
||||||
# bloquées, sortant libre — le comportement attendu d'un poste de travail.
|
|
||||||
# GUI incluse dans le paquet (firewall-config) ; KDE a en plus plasma-firewall.
|
|
||||||
systemctl enable firewalld.service 2>/dev/null || true
|
|
||||||
|
|
||||||
# --- 4quinquies-bis) Horloge juste : synchronisation NTP -----------------------
|
|
||||||
# Sans NTP l'horloge dérive (TLS/pacman finissent par râler, et un utilisateur
|
|
||||||
# venant de Windows s'attend à une heure exacte). systemd-timesyncd est dans
|
|
||||||
# l'image ; on l'ACTIVE explicitement — les symlinks d'activation hérités du
|
|
||||||
# live étaient des FICHIERS ordinaires (artefact de checkout) que systemd
|
|
||||||
# ignore (« not a symlink »), corrigés côté live mais on assure ici quoi
|
|
||||||
# qu'il arrive sur le système installé.
|
|
||||||
systemctl enable systemd-timesyncd.service 2>/dev/null || true
|
|
||||||
|
|
||||||
# NB Récupération : l'entrée GRUB « Réparer FWS » (générée par
|
|
||||||
# /etc/grub.d/42_fws-recovery lors des grub-mkconfig ci-dessus) boote
|
|
||||||
# systemd.unit=fws-recovery.target → fws-recovery-boot.service prend tty1 et
|
|
||||||
# lance l'application de réparation. RIEN à activer : le service n'a pas de
|
|
||||||
# [Install] (son Conflicts=getty@tty1 ne doit jamais entrer dans un boot
|
|
||||||
# normal), la target le tire par Requires. Dans le bureau, le lanceur
|
|
||||||
# « Récupération FWS » (fws-recovery.desktop, polkit) fait le même travail.
|
|
||||||
|
|
||||||
# --- 5) Sécurité : verrouiller root s'il est resté SANS mot de passe ----------
|
|
||||||
# L'airootfs live a souvent un root sans mot de passe ; si l'utilisateur n'en a
|
# L'airootfs live a souvent un root sans mot de passe ; si l'utilisateur n'en a
|
||||||
# pas défini dans le GUI, on verrouille plutôt que de laisser le compte ouvert.
|
# pas défini dans le GUI, on verrouille plutôt que de laisser le compte ouvert.
|
||||||
if passwd -S root 2>/dev/null | grep -qE '\bNP\b'; then
|
if passwd -S root 2>/dev/null | grep -qE '\bNP\b'; then
|
||||||
passwd -l root || true
|
passwd -l root || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- 6) DNS du système installé : NetworkManager + systemd-resolved ----------
|
# --- 7) Le resolv.conf du live ne doit pas rester figé -----------------------
|
||||||
# Le live utilise systemd-resolved (/etc/resolv.conf → stub 127.0.0.53). L'ANCIEN
|
# NetworkManager (activé sur le système installé) régénère /etc/resolv.conf.
|
||||||
# « rm /etc/resolv.conf » comptait sur NM pour recréer le fichier — MAIS avec
|
rm -f /tmp/fws-desktop /etc/resolv.conf 2>/dev/null || true
|
||||||
# systemd-resolved actif, NM ne réécrit PAS resolv.conf (il pousse les DNS vers
|
|
||||||
# resolved). Sans le lien, la résolution est MORTE → « Limited Connectivity » /
|
|
||||||
# Firefox « Server Not Found » (le réseau marche, le DNS non). Fix robuste :
|
|
||||||
# - resolv.conf → stub systemd-resolved,
|
|
||||||
# - service systemd-resolved activé,
|
|
||||||
# - on FORCE NM à utiliser resolved (dns=systemd-resolved) → il lui fournit les
|
|
||||||
# DNS DHCP, la résolution marche dès le 1er boot.
|
|
||||||
rm -f /tmp/fws-desktop 2>/dev/null || true
|
|
||||||
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
|
|
||||||
systemctl enable systemd-resolved.service 2>/dev/null || true
|
|
||||||
install -d /etc/NetworkManager/conf.d
|
|
||||||
printf '[main]\ndns=systemd-resolved\n' > /etc/NetworkManager/conf.d/dns.conf
|
|
||||||
%end
|
%end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Name=À propos de FWS
|
|
||||||
Name[en]=About FWS
|
|
||||||
Comment=Informations sur le projet FWS et sur le système
|
|
||||||
Exec=fws-about
|
|
||||||
Icon=help-about
|
|
||||||
Terminal=false
|
|
||||||
Categories=System;
|
|
||||||
Keywords=about;système;projet;info;FWS;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Name=Installation VR (FWS)
|
|
||||||
Name[en]=VR Setup (FWS)
|
|
||||||
Comment=Installer les drivers/runtimes pour casque VR (HTC Vive, Index, Quest…)
|
|
||||||
Exec=kitty -e fws-vr-setup
|
|
||||||
Icon=fws-vr
|
|
||||||
Terminal=false
|
|
||||||
Categories=System;Settings;
|
|
||||||
Keywords=VR;SteamVR;Vive;Index;Quest;casque;
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- FWS — icône « casque VR » (lanceur fws-vr-setup). Palette FWS/Catppuccin. -->
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
|
|
||||||
<!-- sangles -->
|
|
||||||
<path d="M8 28 C1 29 1 41 8 42" fill="none" stroke="#89b4fa" stroke-width="4" stroke-linecap="round"/>
|
|
||||||
<path d="M56 28 C63 29 63 41 56 42" fill="none" stroke="#89b4fa" stroke-width="4" stroke-linecap="round"/>
|
|
||||||
<!-- corps du casque avec encoche du nez -->
|
|
||||||
<path d="M16 18 h32 a10 10 0 0 1 10 10 v10 a10 10 0 0 1 -10 10 h-7
|
|
||||||
a9 9 0 0 0 -18 0 h-7 a10 10 0 0 1 -10 -10 v-10 a10 10 0 0 1 10 -10 z"
|
|
||||||
fill="#1e1e2e" stroke="#89b4fa" stroke-width="3" stroke-linejoin="round"/>
|
|
||||||
<!-- lentilles -->
|
|
||||||
<circle cx="23" cy="32" r="5.5" fill="#74c7ec"/>
|
|
||||||
<circle cx="41" cy="32" r="5.5" fill="#74c7ec"/>
|
|
||||||
<circle cx="21.5" cy="30.5" r="1.8" fill="#cdd6f4"/>
|
|
||||||
<circle cx="39.5" cy="30.5" r="1.8" fill="#cdd6f4"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 964 B |
@@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE policyconfig PUBLIC
|
|
||||||
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
|
||||||
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
|
|
||||||
<!--
|
|
||||||
FWS — autorise fws-recovery-gui (lancée en utilisateur depuis le menu de
|
|
||||||
l'OS installé) à exécuter le MOTEUR fws-recovery via pkexec.
|
|
||||||
auth_admin_keep : le mot de passe administrateur n'est demandé qu'une fois
|
|
||||||
(mémorisé quelques minutes), pas à chaque bouton.
|
|
||||||
-->
|
|
||||||
<policyconfig>
|
|
||||||
<vendor>FWS</vendor>
|
|
||||||
<action id="org.fws.recovery">
|
|
||||||
<description>Réparer le système FWS</description>
|
|
||||||
<description xml:lang="en">Repair the FWS system</description>
|
|
||||||
<message>Authentification requise pour réparer le système</message>
|
|
||||||
<message xml:lang="en">Authentication is required to repair the system</message>
|
|
||||||
<icon_name>fws-recovery</icon_name>
|
|
||||||
<defaults>
|
|
||||||
<allow_any>auth_admin_keep</allow_any>
|
|
||||||
<allow_inactive>auth_admin_keep</allow_inactive>
|
|
||||||
<allow_active>auth_admin_keep</allow_active>
|
|
||||||
</defaults>
|
|
||||||
<annotate key="org.freedesktop.policykit.exec.path">/usr/local/bin/fws-recovery</annotate>
|
|
||||||
</action>
|
|
||||||
</policyconfig>
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
// Thème SDDM « fws » — écran de connexion FWS (KDE / XFCE / Hyprland).
|
|
||||||
//
|
|
||||||
// VOLONTAIREMENT en pur QtQuick + QtQuick.Controls (style Basic) : ces
|
|
||||||
// modules arrivent avec qt6-declarative, dépendance de sddm lui-même →
|
|
||||||
// AUCUN paquet supplémentaire requis (pas de Breeze/plasma-workspace, pas
|
|
||||||
// de QtGraphicalEffects — supprimé en Qt6). Le greeter fournit en contexte
|
|
||||||
// les objets sddm, userModel, sessionModel, keyboard et config (theme.conf,
|
|
||||||
// surchargé par theme.conf.user écrit par le %post : accent selon le DE).
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls.Basic
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
width: 1280
|
|
||||||
height: 800
|
|
||||||
|
|
||||||
readonly property color accent: config.accent || "#3daee9"
|
|
||||||
readonly property color fieldBg: "#2a2a44"
|
|
||||||
|
|
||||||
gradient: Gradient {
|
|
||||||
GradientStop { position: 0.0; color: config.colorTop || "#1a1a2e" }
|
|
||||||
GradientStop { position: 1.0; color: config.colorBottom || "#16213e" }
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: sddm
|
|
||||||
function onLoginFailed() {
|
|
||||||
errorLabel.text = "Échec de la connexion — vérifiez le mot de passe"
|
|
||||||
passwordField.text = ""
|
|
||||||
passwordField.forceActiveFocus()
|
|
||||||
}
|
|
||||||
function onLoginSucceeded() {
|
|
||||||
errorLabel.text = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function doLogin() {
|
|
||||||
errorLabel.text = ""
|
|
||||||
sddm.login(userField.text, passwordField.text, sessionBox.currentIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Horloge -----------------------------------------------------------
|
|
||||||
Timer {
|
|
||||||
interval: 1000
|
|
||||||
running: true
|
|
||||||
repeat: true
|
|
||||||
onTriggered: {
|
|
||||||
var now = new Date()
|
|
||||||
clockLabel.text = Qt.formatTime(now, "hh:mm")
|
|
||||||
dateLabel.text = Qt.formatDate(now, "dddd d MMMM yyyy")
|
|
||||||
}
|
|
||||||
Component.onCompleted: triggered()
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: parent.height * 0.10
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: clockLabel
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
color: "#f0f0f5"
|
|
||||||
font.pixelSize: 84
|
|
||||||
font.weight: Font.Light
|
|
||||||
}
|
|
||||||
Text {
|
|
||||||
id: dateLabel
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
color: "#a0a0b8"
|
|
||||||
font.pixelSize: 20
|
|
||||||
font.capitalization: Font.Capitalize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Carte de connexion --------------------------------------------------
|
|
||||||
Rectangle {
|
|
||||||
id: card
|
|
||||||
anchors.centerIn: parent
|
|
||||||
anchors.verticalCenterOffset: parent.height * 0.08
|
|
||||||
width: 400
|
|
||||||
height: cardContent.implicitHeight + 56
|
|
||||||
radius: 14
|
|
||||||
color: "#232338"
|
|
||||||
border.color: Qt.rgba(1, 1, 1, 0.10)
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: cardContent
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: parent.width - 56
|
|
||||||
spacing: 14
|
|
||||||
|
|
||||||
Text {
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
text: config.greeting || "Bienvenue sur FWS"
|
|
||||||
color: "#f0f0f5"
|
|
||||||
font.pixelSize: 22
|
|
||||||
font.weight: Font.Medium
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle { width: 44; height: 3; radius: 1.5; color: accent
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter }
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
id: userField
|
|
||||||
width: parent.width
|
|
||||||
text: userModel.lastUser
|
|
||||||
placeholderText: "Utilisateur"
|
|
||||||
placeholderTextColor: "#7d7d95"
|
|
||||||
color: "#f0f0f5"
|
|
||||||
font.pixelSize: 15
|
|
||||||
leftPadding: 14
|
|
||||||
background: Rectangle {
|
|
||||||
radius: 8
|
|
||||||
color: fieldBg
|
|
||||||
border.color: userField.activeFocus ? accent : "transparent"
|
|
||||||
border.width: 2
|
|
||||||
}
|
|
||||||
onAccepted: passwordField.forceActiveFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
id: passwordField
|
|
||||||
width: parent.width
|
|
||||||
placeholderText: "Mot de passe"
|
|
||||||
placeholderTextColor: "#7d7d95"
|
|
||||||
color: "#f0f0f5"
|
|
||||||
font.pixelSize: 15
|
|
||||||
leftPadding: 14
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
focus: true
|
|
||||||
background: Rectangle {
|
|
||||||
radius: 8
|
|
||||||
color: fieldBg
|
|
||||||
border.color: passwordField.activeFocus ? accent : "transparent"
|
|
||||||
border.width: 2
|
|
||||||
}
|
|
||||||
onAccepted: doLogin()
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
visible: keyboard.capsLock
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
text: "⚠ Verrouillage majuscules activé"
|
|
||||||
color: "#e6c07b"
|
|
||||||
font.pixelSize: 13
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: errorLabel
|
|
||||||
width: parent.width
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
color: "#e06c75"
|
|
||||||
font.pixelSize: 13
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: loginButton
|
|
||||||
width: parent.width
|
|
||||||
height: 44
|
|
||||||
onClicked: doLogin()
|
|
||||||
contentItem: Text {
|
|
||||||
text: "Se connecter"
|
|
||||||
color: "#ffffff"
|
|
||||||
font.pixelSize: 16
|
|
||||||
font.weight: Font.Medium
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
background: Rectangle {
|
|
||||||
radius: 8
|
|
||||||
color: loginButton.down ? Qt.darker(accent, 1.25)
|
|
||||||
: loginButton.hovered ? Qt.lighter(accent, 1.10)
|
|
||||||
: accent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ComboBox {
|
|
||||||
id: sessionBox
|
|
||||||
width: parent.width
|
|
||||||
model: sessionModel
|
|
||||||
currentIndex: sessionModel.lastIndex
|
|
||||||
textRole: "name"
|
|
||||||
font.pixelSize: 13
|
|
||||||
contentItem: Text {
|
|
||||||
leftPadding: 14
|
|
||||||
text: sessionBox.displayText
|
|
||||||
color: "#a0a0b8"
|
|
||||||
font.pixelSize: 13
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
background: Rectangle {
|
|
||||||
radius: 8
|
|
||||||
color: "transparent"
|
|
||||||
border.color: Qt.rgba(1, 1, 1, 0.15)
|
|
||||||
border.width: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Boutons d'alimentation ----------------------------------------------
|
|
||||||
// PAS de Repeater avec des fonctions dans un modèle JS : en Qt6 elles ne
|
|
||||||
// survivent pas toujours à la conversion QVariant (clic silencieusement
|
|
||||||
// inopérant). Deux boutons explicites via un composant inline.
|
|
||||||
component PowerButton: Button {
|
|
||||||
id: powerButton
|
|
||||||
property string label: ""
|
|
||||||
height: 36
|
|
||||||
contentItem: Text {
|
|
||||||
text: powerButton.label
|
|
||||||
color: powerButton.hovered ? "#f0f0f5" : "#a0a0b8"
|
|
||||||
font.pixelSize: 14
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
leftPadding: 14
|
|
||||||
rightPadding: 14
|
|
||||||
}
|
|
||||||
background: Rectangle {
|
|
||||||
radius: 8
|
|
||||||
color: powerButton.hovered ? Qt.rgba(1, 1, 1, 0.08) : "transparent"
|
|
||||||
border.color: Qt.rgba(1, 1, 1, 0.15)
|
|
||||||
border.width: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.margins: 24
|
|
||||||
spacing: 10
|
|
||||||
|
|
||||||
PowerButton { label: "Redémarrer"; onClicked: sddm.reboot() }
|
|
||||||
PowerButton { label: "Éteindre"; onClicked: sddm.powerOff() }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nom de la machine, discret, en bas à gauche.
|
|
||||||
Text {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.margins: 24
|
|
||||||
text: sddm.hostName
|
|
||||||
color: "#5d5d78"
|
|
||||||
font.pixelSize: 13
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
[SddmGreeterTheme]
|
|
||||||
Name=FWS
|
|
||||||
Description=Écran de connexion FWS
|
|
||||||
Author=FWS
|
|
||||||
Copyright=FWS
|
|
||||||
License=GPL-2.0-or-later
|
|
||||||
Type=sddm-theme
|
|
||||||
Version=1.0
|
|
||||||
Website=
|
|
||||||
Screenshot=
|
|
||||||
MainScript=Main.qml
|
|
||||||
ConfigFile=theme.conf
|
|
||||||
TranslationsDirectory=
|
|
||||||
Email=
|
|
||||||
Theme-Id=fws
|
|
||||||
Theme-API=2.0
|
|
||||||
QtVersion=6
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# Thème SDDM « fws » — valeurs par défaut, surchargées par theme.conf.user
|
|
||||||
# (écrit par le %post du kickstart : accent différent selon le bureau choisi).
|
|
||||||
[General]
|
|
||||||
accent=#3daee9
|
|
||||||
colorTop=#1a1a2e
|
|
||||||
colorBottom=#16213e
|
|
||||||
greeting=Bienvenue sur FWS
|
|
||||||
@@ -2,4 +2,4 @@ title FWS install medium (%ARCH%, UEFI)
|
|||||||
sort-key 01
|
sort-key 01
|
||||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||||
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% udev.event_timeout=45
|
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
# Mode graphique de secours : SANS KMS (nomodeset). Filet de sécurité
|
|
||||||
# standard pour les GPU/hyperviseurs dont le pilote KMS pose problème :
|
|
||||||
# X démarre alors sur le framebuffer VESA/EFI (plus lent mais fiable).
|
|
||||||
title FWS install medium (%ARCH%, UEFI, safe graphics)
|
|
||||||
sort-key 01b
|
|
||||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
|
||||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
|
||||||
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% nomodeset udev.event_timeout=45
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# « Réparer FWS » : boote le live SANS installateur et lance l'assistant de
|
|
||||||
# réparation en console (fws-recovery, via le hook fws.recovery=1 du
|
|
||||||
# .zprofile) : réinstaller GRUB, régénérer l'initramfs, réparer pacman,
|
|
||||||
# shell chroot — le point de restauration FWS.
|
|
||||||
title FWS - Reparer une installation (recovery)
|
|
||||||
sort-key 01d
|
|
||||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
|
||||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
|
||||||
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% udev.event_timeout=45 fws.recovery=1
|
|
||||||
@@ -2,4 +2,4 @@ title FWS install medium (%ARCH%, UEFI) with speech
|
|||||||
sort-key 02
|
sort-key 02
|
||||||
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||||
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||||
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on udev.event_timeout=45
|
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ mdadm
|
|||||||
parted
|
parted
|
||||||
gptfdisk
|
gptfdisk
|
||||||
fatresize
|
fatresize
|
||||||
firewalld
|
|
||||||
testdisk
|
testdisk
|
||||||
smartmontools
|
smartmontools
|
||||||
nvme-cli
|
nvme-cli
|
||||||
@@ -88,8 +87,6 @@ sof-firmware
|
|||||||
|
|
||||||
# --- Xorg + Openbox (session graphique de l'installateur) ---
|
# --- Xorg + Openbox (session graphique de l'installateur) ---
|
||||||
xorg-server
|
xorg-server
|
||||||
zenity
|
|
||||||
zram-generator
|
|
||||||
xorg-xinit
|
xorg-xinit
|
||||||
xorg-xrandr
|
xorg-xrandr
|
||||||
xorg-xsetroot
|
xorg-xsetroot
|
||||||
@@ -117,7 +114,3 @@ python-crypt_r
|
|||||||
# Namespace GI 'OSTree-1.0' : pyanaconda/rescue.py importe le payload rpm_ostree
|
# Namespace GI 'OSTree-1.0' : pyanaconda/rescue.py importe le payload rpm_ostree
|
||||||
# (inconditionnellement, même si FWS utilise liveimg) → fourni par 'ostree'.
|
# (inconditionnellement, même si FWS utilise liveimg) → fourni par 'ostree'.
|
||||||
ostree
|
ostree
|
||||||
# Terminal X : debug de l'installateur + entrée « Terminals » du menu Openbox.
|
|
||||||
xterm
|
|
||||||
# Gestionnaire D&D 5e (dés, personnages, grimoire, sessions, combat).
|
|
||||||
fws-dnd
|
|
||||||
|
|||||||
@@ -24,17 +24,4 @@ file_permissions=(
|
|||||||
["/usr/local/bin/livecd-sound"]="0:0:755"
|
["/usr/local/bin/livecd-sound"]="0:0:755"
|
||||||
["/usr/bin/load_policy"]="0:0:755"
|
["/usr/bin/load_policy"]="0:0:755"
|
||||||
["/usr/bin/setstatus"]="0:0:755"
|
["/usr/bin/setstatus"]="0:0:755"
|
||||||
["/usr/local/bin/fws-setup-hardware"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-keys"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-vr-setup"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-hello"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-update"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-updates-check"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-proton-ge"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-recovery"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-recovery-session"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-recovery-gui"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-powermenu"]="0:0:755"
|
|
||||||
["/usr/local/bin/fws-about"]="0:0:755"
|
|
||||||
["/etc/grub.d/42_fws-recovery"]="0:0:755"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,36 +6,7 @@ ENDTEXT
|
|||||||
MENU LABEL FWS install medium (%ARCH%, BIOS)
|
MENU LABEL FWS install medium (%ARCH%, BIOS)
|
||||||
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||||
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||||
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% udev.event_timeout=45
|
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID%
|
||||||
|
|
||||||
# Mode graphique de secours : SANS KMS (nomodeset). Filet de sécurité
|
|
||||||
# standard pour les GPU/hyperviseurs dont le pilote KMS pose problème :
|
|
||||||
# X démarre alors sur le framebuffer VESA/EFI (plus lent mais fiable).
|
|
||||||
# NB : la lenteur au démarrage vue en VM VMware (2-4 min d'écran bleu)
|
|
||||||
# N'EST PAS un gel graphique — c'est un worker udev tué après 180 s ;
|
|
||||||
# le boot normal finit par afficher l'installateur (splash ajouté).
|
|
||||||
LABEL fwsnomodeset
|
|
||||||
TEXT HELP
|
|
||||||
Boot FWS WITHOUT kernel mode setting (safe graphics).
|
|
||||||
Use this if the normal boot freezes on a black/blank screen
|
|
||||||
(e.g. VMware Workstation vmwgfx hang).
|
|
||||||
ENDTEXT
|
|
||||||
MENU LABEL FWS install medium (%ARCH%, BIOS, ^safe graphics)
|
|
||||||
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
|
||||||
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
|
||||||
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% nomodeset udev.event_timeout=45
|
|
||||||
|
|
||||||
# « Réparer FWS » : live sans installateur → assistant de réparation console
|
|
||||||
# (fws-recovery via le hook fws.recovery=1 du .zprofile).
|
|
||||||
LABEL fwsrecovery
|
|
||||||
TEXT HELP
|
|
||||||
Repair an existing FWS installation (GRUB, initramfs, pacman)
|
|
||||||
without reinstalling. Boots to a guided console assistant.
|
|
||||||
ENDTEXT
|
|
||||||
MENU LABEL FWS - ^Repair installation (recovery)
|
|
||||||
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
|
||||||
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
|
||||||
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% udev.event_timeout=45 fws.recovery=1
|
|
||||||
|
|
||||||
# Accessibility boot option
|
# Accessibility boot option
|
||||||
LABEL fwsspeech
|
LABEL fwsspeech
|
||||||
@@ -46,4 +17,4 @@ ENDTEXT
|
|||||||
MENU LABEL FWS install medium (%ARCH%, BIOS) with ^speech
|
MENU LABEL FWS install medium (%ARCH%, BIOS) with ^speech
|
||||||
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux
|
||||||
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img
|
||||||
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on udev.event_timeout=45
|
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
INCLUDE archiso_head.cfg
|
INCLUDE archiso_head.cfg
|
||||||
|
|
||||||
DEFAULT fws
|
DEFAULT arch
|
||||||
TIMEOUT 150
|
TIMEOUT 150
|
||||||
|
|
||||||
INCLUDE archiso_sys-linux.cfg
|
INCLUDE archiso_sys-linux.cfg
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 44 KiB |
@@ -6,30 +6,6 @@ Ce document décrit **l'environnement exact** sur lequel le build FWS fonctionne
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 0. Build natif Arch Linux — la référence actuelle
|
|
||||||
|
|
||||||
Depuis juillet 2026, l'environnement de référence est un **Arch Linux natif**
|
|
||||||
(même machine, sans Windows/WSL) : `build.sh` et `setup-aur.sh` détectent le
|
|
||||||
cas et s'exécutent directement. Les ISO récentes du dépôt sont produites ainsi.
|
|
||||||
|
|
||||||
| Composant | Valeur de référence |
|
|
||||||
|---|---|
|
|
||||||
| OS | Arch Linux (rolling), kernel `7.x-archN` |
|
|
||||||
| Paquets requis | `base-devel`, `git`, `archiso`, `dos2unix` (`pacman -S --needed …`) |
|
|
||||||
| Droits | `sudo` (mkarchiso et pacstrap exigent root) |
|
|
||||||
| Réseau | requis (pacstrap télécharge les paquets ; `setup-aur.sh` clone/compile l'AUR) |
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo ./setup-aur.sh # paquets AUR/anaconda → local-repo/
|
|
||||||
sudo ./build.sh # mkarchiso → out/FWS-<date>-x86_64.iso
|
|
||||||
```
|
|
||||||
|
|
||||||
Le reste de ce document décrit le chemin **Windows 11 + WSL Arch**, toujours
|
|
||||||
fonctionnel (bascule automatique depuis Git Bash) mais qui n'est plus la
|
|
||||||
référence.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Matériel (host Windows)
|
## 1. Matériel (host Windows)
|
||||||
|
|
||||||
| Composant | Valeur de référence | Minimum acceptable | Pourquoi |
|
| Composant | Valeur de référence | Minimum acceptable | Pourquoi |
|
||||||
|
|||||||
+1
-102
@@ -25,7 +25,7 @@
|
|||||||
# ============================================================================
|
# ============================================================================
|
||||||
pkgname=anaconda
|
pkgname=anaconda
|
||||||
pkgver=45.8
|
pkgver=45.8
|
||||||
pkgrel=4
|
pkgrel=2
|
||||||
pkgdesc="The Anaconda installer (porté sur Arch pour FWS, payload liveimg)"
|
pkgdesc="The Anaconda installer (porté sur Arch pour FWS, payload liveimg)"
|
||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
url="https://github.com/rhinstaller/anaconda"
|
url="https://github.com/rhinstaller/anaconda"
|
||||||
@@ -57,107 +57,6 @@ source=("anaconda-$pkgver.tar.gz::https://github.com/rhinstaller/anaconda/archiv
|
|||||||
sha256sums=('5ef1464bff7f28a4a09a821cbb0121f09ef504ec169288d742373f86807ec4ed')
|
sha256sums=('5ef1464bff7f28a4a09a821cbb0121f09ef504ec169288d742373f86807ec4ed')
|
||||||
_srcdir="anaconda-anaconda-$pkgver"
|
_srcdir="anaconda-anaconda-$pkgver"
|
||||||
|
|
||||||
prepare() {
|
|
||||||
cd "$srcdir/$_srcdir"
|
|
||||||
# ── FWS : clavier par défaut selon le TERRITOIRE de la locale ──────────────
|
|
||||||
# set_layouts() amont fait « locale = get_language_id(self.language) » →
|
|
||||||
# get_language_id JETTE le territoire (fr_CH.UTF-8 → 'fr') → list_keyboards('fr')
|
|
||||||
# = 'fr(oss)' (France) au lieu de 'ch(fr)' (Suisse). On passe la LOCALE COMPLÈTE
|
|
||||||
# (self.language) : langtable.list_keyboards('fr_CH.UTF-8') = 'ch(fr)'. La
|
|
||||||
# validation l'accepte (is_valid_langcode → parse_locale().language non vide).
|
|
||||||
# → le clavier du GUI ET du système installé suivent enfin la locale choisie.
|
|
||||||
sed -i \
|
|
||||||
's/^\(\s*\)locale = get_language_id(self\.language)/\1locale = self.language # FWS: garder le territoire (fr_CH -> ch(fr))/' \
|
|
||||||
pyanaconda/modules/localization/localization.py
|
|
||||||
# Garde-fou : casse le build si le motif amont a changé (le patch silencieux).
|
|
||||||
grep -q 'locale = self.language # FWS' \
|
|
||||||
pyanaconda/modules/localization/localization.py
|
|
||||||
|
|
||||||
# ── FWS : champ « Nom de machine » dans le spoke Utilisateur ───────────────
|
|
||||||
# Anaconda ne propage PAS le hostname (le champ du spoke Réseau est perdu chez
|
|
||||||
# nous → /etc/hostname vide). On ajoute donc un champ Hostname à l'onglet de
|
|
||||||
# création de compte (ligne 2 de la grille, libre) ; user.py écrit la valeur
|
|
||||||
# dans /tmp/fws-hostname, que le %post du kickstart applique à /etc/hostname.
|
|
||||||
python3 - <<'PYEOF'
|
|
||||||
import glob
|
|
||||||
|
|
||||||
# 1) user.glade : insérer label + entry "Host name" APRÈS le bloc username_entry.
|
|
||||||
glades = [p for p in glob.glob("pyanaconda/**/user.glade", recursive=True)
|
|
||||||
if 'username_entry' in open(p, encoding='utf-8').read()]
|
|
||||||
assert glades, "FWS: user.glade introuvable"
|
|
||||||
g = glades[0]
|
|
||||||
s = open(g, encoding='utf-8').read()
|
|
||||||
assert 'id="hostname_entry"' not in s, "FWS: glade déjà patché"
|
|
||||||
i = s.index('id="username_entry"')
|
|
||||||
k = s.index('</packing>', i) # fin du packing de username_entry
|
|
||||||
j = s.index('</child>', k) + len('</child>') # fin du <child> EXTERNE (pas l'accessible)
|
|
||||||
block = '''
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="hostname_label">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">False</property>
|
|
||||||
<property name="xpad">10</property>
|
|
||||||
<property name="label" translatable="yes" context="GUI|User">_Host name</property>
|
|
||||||
<property name="use-underline">True</property>
|
|
||||||
<property name="mnemonic-widget">hostname_entry</property>
|
|
||||||
<property name="xalign">1</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="weight" value="bold"/>
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">0</property>
|
|
||||||
<property name="top-attach">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEntry" id="hostname_entry">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can-focus">True</property>
|
|
||||||
<property name="width-chars">50</property>
|
|
||||||
<child internal-child="accessible">
|
|
||||||
<object class="AtkObject" id="hostname_entry-atkobject">
|
|
||||||
<property name="AtkObject::accessible-name" translatable="yes">Host Name</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left-attach">1</property>
|
|
||||||
<property name="top-attach">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>'''
|
|
||||||
open(g, 'w', encoding='utf-8').write(s[:j] + block + s[j:])
|
|
||||||
|
|
||||||
# 2) user.py (GUI) : récupérer le widget + écrire /tmp/fws-hostname dans apply().
|
|
||||||
pys = glob.glob("pyanaconda/ui/gui/spokes/user.py")
|
|
||||||
assert pys, "FWS: user.py introuvable"
|
|
||||||
p = pys[0]
|
|
||||||
s = open(p, encoding='utf-8').read()
|
|
||||||
anchor = 'self._username_entry = self.builder.get_object("username_entry")'
|
|
||||||
assert anchor in s, "FWS: ancre username_entry absente"
|
|
||||||
s = s.replace(anchor,
|
|
||||||
anchor + '\n self._hostname_entry = self.builder.get_object("hostname_entry")',
|
|
||||||
1)
|
|
||||||
ap = 'set_user_list(self._users_module, self._user_list, remove_unset=True)'
|
|
||||||
assert ap in s, "FWS: ancre apply() absente"
|
|
||||||
s = s.replace(ap, ap + '''
|
|
||||||
|
|
||||||
# FWS : mémorise le nom de machine saisi (cible pas encore montée) pour
|
|
||||||
# le %post du kickstart, qui l'écrira dans /etc/hostname.
|
|
||||||
try:
|
|
||||||
_hn = self._hostname_entry.get_text().strip()
|
|
||||||
except Exception:
|
|
||||||
_hn = ""
|
|
||||||
try:
|
|
||||||
with open("/tmp/fws-hostname", "w") as _f:
|
|
||||||
_f.write(_hn)
|
|
||||||
except OSError:
|
|
||||||
pass''', 1)
|
|
||||||
open(p, 'w', encoding='utf-8').write(s)
|
|
||||||
print("FWS: champ Hostname ajouté au spoke Utilisateur (glade + user.py)")
|
|
||||||
PYEOF
|
|
||||||
}
|
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "$srcdir/$_srcdir"
|
cd "$srcdir/$_srcdir"
|
||||||
# Glade désactivé (catalogue designer, inutile au runtime) → pas de dép glade.
|
# Glade désactivé (catalogue designer, inutile au runtime) → pas de dép glade.
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
# Maintainer: FWS
|
|
||||||
pkgname=fws-dnd
|
|
||||||
pkgver=2.0.0
|
|
||||||
pkgrel=1
|
|
||||||
pkgdesc="Gestionnaire D&D 5e pour FWS (GTK4) : dés, créateur de personnage (règles officielles), grimoire, sessions, tracker de combat"
|
|
||||||
arch=('any')
|
|
||||||
url="https://fws.local/fws-dnd"
|
|
||||||
license=('MIT')
|
|
||||||
depends=('python' 'python-gobject' 'gtk4')
|
|
||||||
source=("${pkgname}-${pkgver}.tar.gz")
|
|
||||||
sha256sums=('SKIP')
|
|
||||||
|
|
||||||
package() {
|
|
||||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
|
||||||
|
|
||||||
# Module Python (application GTK4)
|
|
||||||
install -d "${pkgdir}/usr/share/${pkgname}"
|
|
||||||
cp -r gui/fwsdnd "${pkgdir}/usr/share/${pkgname}/"
|
|
||||||
# pas de bytecode pré-généré dans le paquet
|
|
||||||
find "${pkgdir}/usr/share/${pkgname}" -name '__pycache__' -type d -exec rm -rf {} +
|
|
||||||
|
|
||||||
# Exécutable
|
|
||||||
install -Dm755 bin/fws-dnd "${pkgdir}/usr/bin/fws-dnd"
|
|
||||||
|
|
||||||
# Intégration bureau
|
|
||||||
install -Dm644 fws-dnd.desktop "${pkgdir}/usr/share/applications/fws-dnd.desktop"
|
|
||||||
install -Dm644 fws-dnd.svg "${pkgdir}/usr/share/icons/hicolor/scalable/apps/fws-dnd.svg"
|
|
||||||
|
|
||||||
# Documentation
|
|
||||||
install -Dm644 README.md "${pkgdir}/usr/share/doc/${pkgname}/README.md"
|
|
||||||
}
|
|
||||||
Binary file not shown.
@@ -5,7 +5,7 @@
|
|||||||
# l'arborescence à la main plutôt que de patcher la chaîne distutils.
|
# l'arborescence à la main plutôt que de patcher la chaîne distutils.
|
||||||
pkgname=python-meh
|
pkgname=python-meh
|
||||||
pkgver=0.43
|
pkgver=0.43
|
||||||
pkgrel=2
|
pkgrel=1
|
||||||
pkgdesc="Python exception handling library (dépendance Anaconda)"
|
pkgdesc="Python exception handling library (dépendance Anaconda)"
|
||||||
arch=('any')
|
arch=('any')
|
||||||
url="https://github.com/rhinstaller/python-meh"
|
url="https://github.com/rhinstaller/python-meh"
|
||||||
@@ -16,24 +16,6 @@ source=("$pkgname-$pkgver.tar.gz::https://github.com/rhinstaller/python-meh/arch
|
|||||||
sha256sums=('cd50301794ab68a064715f1ace960f6de07aea809aa85f73087d44573c98be52')
|
sha256sums=('cd50301794ab68a064715f1ace960f6de07aea809aa85f73087d44573c98be52')
|
||||||
_srcdir="python-meh-$_tag"
|
_srcdir="python-meh-$_tag"
|
||||||
|
|
||||||
prepare() {
|
|
||||||
cd "$srcdir/$_srcdir"
|
|
||||||
# gettext.ldgettext a été SUPPRIMÉ en Python 3.11 : sans ce remplacement, le
|
|
||||||
# gestionnaire d'exceptions d'Anaconda plante LUI-MÊME (AttributeError) dès
|
|
||||||
# qu'il doit AFFICHER une erreur → traceback brut au lieu du dialogue meh,
|
|
||||||
# en GUI comme en TUI. dgettext est l'équivalent direct (ldgettext n'était
|
|
||||||
# que la variante bytes). Corrigé en amont après 0.43.
|
|
||||||
sed -i 's/gettext\.ldgettext(/gettext.dgettext(/' \
|
|
||||||
meh/handler.py meh/ui/text.py meh/ui/gui.py
|
|
||||||
# .pyc d'une extraction précédente (src/ n'est pas nettoyé par makepkg -f) :
|
|
||||||
# à jeter, sinon ils partiraient dans le paquet via cp -a.
|
|
||||||
find meh -type d -name __pycache__ -exec rm -rf {} +
|
|
||||||
if grep -rn --include='*.py' 'ldgettext' meh/; then
|
|
||||||
echo "ERREUR : ldgettext encore présent après patch" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
cd "$srcdir/$_srcdir"
|
cd "$srcdir/$_srcdir"
|
||||||
local sitedir
|
local sitedir
|
||||||
|
|||||||
+7
-29
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# FWS — Build des paquets AUR (winboat-bin) + PKGBUILD locaux (deps Anaconda,
|
# FWS — Build des paquets AUR (winboat-bin) + PKGBUILD locaux (deps Anaconda)
|
||||||
# fws-dnd) et création du repo pacman local fws-local/.
|
# et création du repo pacman local fws-local/.
|
||||||
#
|
#
|
||||||
# Rejoue-le si tu changes la liste AUR_PACKAGES ou pour
|
# Rejoue-le si tu changes la liste AUR_PACKAGES ou pour
|
||||||
# rafraîchir le repo local.
|
# rafraîchir le repo local.
|
||||||
@@ -114,10 +114,7 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
|
|||||||
"${SUDO[@]}" podman pull "$IMAGE"
|
"${SUDO[@]}" podman pull "$IMAGE"
|
||||||
|
|
||||||
info "Relance dans le conteneur (setup-aur.sh)…"
|
info "Relance dans le conteneur (setup-aur.sh)…"
|
||||||
# --network=host : le bridge podman ne route plus vers l'extérieur sur
|
"${SUDO[@]}" podman run --rm "${TTY[@]}" --privileged \
|
||||||
# certains réseaux (firewalld/NAT, → « Resolving timed out »). On partage la
|
|
||||||
# pile réseau de l'hôte (internet + DNS OK) pour les pacman/makepkg/AUR.
|
|
||||||
"${SUDO[@]}" podman run --rm "${TTY[@]}" --network=host --privileged \
|
|
||||||
-e FWS_IN_CONTAINER=1 \
|
-e FWS_IN_CONTAINER=1 \
|
||||||
-v "$REPO_DIR":"$REPO_DIR" \
|
-v "$REPO_DIR":"$REPO_DIR" \
|
||||||
-v fws-pacman-cache:/var/cache/pacman/pkg \
|
-v fws-pacman-cache:/var/cache/pacman/pkg \
|
||||||
@@ -232,14 +229,11 @@ repo_has() {
|
|||||||
# Publie les .pkg d'un build vers local-repo en purgeant d'abord les anciennes
|
# Publie les .pkg d'un build vers local-repo en purgeant d'abord les anciennes
|
||||||
# versions de CE paquet (+ son sous-paquet -debug). Le « -[0-9] » empêche
|
# versions de CE paquet (+ son sous-paquet -debug). Le « -[0-9] » empêche
|
||||||
# « anaconda » d'attraper « anaconda-widgets » / « anaconda-debug ».
|
# « anaconda » d'attraper « anaconda-widgets » / « anaconda-debug ».
|
||||||
# Prend la LISTE EXPLICITE des fichiers à publier (pas un glob sur le dossier :
|
|
||||||
# un .pkg d'un build précédent peut traîner dans $BUILD_DIR/<name> et serait
|
|
||||||
# republié par-dessus la purge).
|
|
||||||
publish_to_repo() {
|
publish_to_repo() {
|
||||||
local name="$1"; shift
|
local name="$1" d="$2"
|
||||||
rm -f "$LOCAL_REPO/$name"-[0-9]*.pkg.tar.* \
|
rm -f "$LOCAL_REPO/$name"-[0-9]*.pkg.tar.* \
|
||||||
"$LOCAL_REPO/$name"-debug-[0-9]*.pkg.tar.* 2>/dev/null || true
|
"$LOCAL_REPO/$name"-debug-[0-9]*.pkg.tar.* 2>/dev/null || true
|
||||||
cp -f "$@" "$LOCAL_REPO/" || error "Copie de $name vers local-repo échouée"
|
cp -f "$d"/*.pkg.tar.* "$LOCAL_REPO/" || error "Copie de $name vers local-repo échouée"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Construit OU saute <name> (source déjà déposée dans $BUILD_DIR/<name>).
|
# Construit OU saute <name> (source déjà déposée dans $BUILD_DIR/<name>).
|
||||||
@@ -262,23 +256,8 @@ build_or_skip() {
|
|||||||
chown -R "$BUILD_USER:$BUILD_USER" "$d"
|
chown -R "$BUILD_USER:$BUILD_USER" "$d"
|
||||||
su - "$BUILD_USER" -c "cd '$d' && makepkg -sf --noconfirm" \
|
su - "$BUILD_USER" -c "cd '$d' && makepkg -sf --noconfirm" \
|
||||||
|| error "Build $name échoué"
|
|| error "Build $name échoué"
|
||||||
# N'installer/publier QUE les paquets produits par CE PKGBUILD dans SA
|
pacman -U --noconfirm "$d/"*.pkg.tar.* || error "Install de $name échouée"
|
||||||
# version courante (makepkg --packagelist), jamais un glob "$d/"* : après
|
publish_to_repo "$name" "$d"
|
||||||
# un bump de pkgrel, l'ancien .pkg traîne encore dans $d (BUILD_DIR n'est
|
|
||||||
# pas nettoyé entre les runs) → pacman -U recevait 2× le même nom de
|
|
||||||
# paquet (« erreur : cible répétée ») et la vieille version repartait
|
|
||||||
# dans local-repo. Le grep filtre tout bruit éventuel de su - (PAM…).
|
|
||||||
# NB : --packagelist liste aussi un -debug qui n'est PAS toujours produit
|
|
||||||
# (paquet pur python : rien à stripper) → ne garder que les fichiers
|
|
||||||
# réellement présents sur disque.
|
|
||||||
local -a pkgfiles=() existing=()
|
|
||||||
local f
|
|
||||||
mapfile -t pkgfiles < <(su - "$BUILD_USER" -c "cd '$d' && makepkg --packagelist" \
|
|
||||||
| grep -E '\.pkg\.tar\.')
|
|
||||||
for f in "${pkgfiles[@]}"; do [ -e "$f" ] && existing+=("$f"); done
|
|
||||||
[ "${#existing[@]}" -gt 0 ] || error "Aucun paquet produit trouvé pour $name"
|
|
||||||
pacman -U --noconfirm "${existing[@]}" || error "Install de $name échouée"
|
|
||||||
publish_to_repo "$name" "${existing[@]}"
|
|
||||||
success "$name build + install OK"
|
success "$name build + install OK"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,7 +276,6 @@ done
|
|||||||
# --- PKGBUILD locaux FWS (ordre explicite, feuilles d'abord, anaconda dernier) ---
|
# --- PKGBUILD locaux FWS (ordre explicite, feuilles d'abord, anaconda dernier) ---
|
||||||
LOCAL_PKGBUILDS_DIR="$SCRIPT_DIR/pkgbuilds"
|
LOCAL_PKGBUILDS_DIR="$SCRIPT_DIR/pkgbuilds"
|
||||||
LOCAL_BUILD_ORDER=(
|
LOCAL_BUILD_ORDER=(
|
||||||
fws-dnd
|
|
||||||
pykickstart python-productmd python-requests-file python-simpleline
|
pykickstart python-productmd python-requests-file python-simpleline
|
||||||
python-crypt_r python-iso639 python-langtable python-meh python-blivet
|
python-crypt_r python-iso639 python-langtable python-meh python-blivet
|
||||||
anaconda-widgets anaconda
|
anaconda-widgets anaconda
|
||||||
|
|||||||
Reference in New Issue
Block a user