diff --git a/configs/baseline/profiledef.sh b/configs/baseline/profiledef.sh index 8a3ec2e..2245265 100644 --- a/configs/baseline/profiledef.sh +++ b/configs/baseline/profiledef.sh @@ -29,6 +29,6 @@ file_permissions=( ["/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-hypr-keys"]="0:0:755" + ["/usr/local/bin/fws-keys"]="0:0:755" ["/usr/local/bin/fws-vr-setup"]="0:0:755" ) diff --git a/configs/releng/airootfs/usr/local/bin/fws-hypr-keys b/configs/releng/airootfs/usr/local/bin/fws-hypr-keys deleted file mode 100755 index 9580d94..0000000 --- a/configs/releng/airootfs/usr/local/bin/fws-hypr-keys +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python3 -# FWS — affiche les raccourcis clavier RÉELS de Hyprland. -# -# Source de vérité : « hyprctl binds -j » = les binds effectivement CHARGÉS -# par le compositeur (ceux de ~/.config/hypr/hyprland.conf, y compris ceux -# ajoutés par l'utilisateur après l'installation). Les descriptions viennent -# des « bindd » de la config ; un bind sans description affiche son action -# brute (dispatcher + argument). Affichage : wofi en mode dmenu (recherche -# incrémentale intégrée) — déclenché par le bouton ⌨ de la waybar ou SUPER+K. -# -# python3 et wofi sont déjà sur le système (image FWS + paquets Hyprland) : -# aucune dépendance supplémentaire. - -import json -import os -import subprocess -import sys - -_STYLE = "/usr/local/share/fws/wofi-keys.css" - -# Bits de modmask (masques X11) dans l'ordre d'affichage voulu. -_MODS = [ - (64, "SUPER"), - (4, "CTRL"), - (8, "ALT"), - (1, "SHIFT"), -] - -# Libellés humains pour les « touches » spéciales. -_KEY_LABELS = { - "mouse:272": "clic gauche", - "mouse:273": "clic droit", - "mouse:274": "clic molette", - "mouse_down": "molette bas", - "mouse_up": "molette haut", - "return": "Entrée", - "space": "Espace", - "escape": "Échap", - "tab": "Tab", - "left": "←", - "right": "→", - "up": "↑", - "down": "↓", -} - - -def _mods(mask): - return [name for bit, name in _MODS if mask & bit] - - -def _combo(bind): - parts = _mods(bind.get("modmask", 0)) - key = bind.get("key") or "" - if not key and bind.get("keycode"): - key = f"code {bind['keycode']}" - parts.append(_KEY_LABELS.get(key.lower(), key)) - return " + ".join(p for p in parts if p) - - -def _action(bind): - desc = (bind.get("description") or "").strip() - if desc: - return desc - arg = (bind.get("arg") or "").strip() - return f"{bind.get('dispatcher', '?')} {arg}".strip() - - -def main(): - try: - raw = subprocess.check_output(["hyprctl", "binds", "-j"], text=True) - binds = json.loads(raw) - except (OSError, subprocess.CalledProcessError, ValueError) as exc: - subprocess.run(["notify-send", "Raccourcis FWS", - f"hyprctl binds a échoué : {exc}"], check=False) - return 1 - - lines = [] - seen = set() - for bind in binds: - combo = _combo(bind) - if not combo or combo in seen: # binde répétés (resize) : une ligne - continue - seen.add(combo) - lines.append(f"{combo:<24} {_action(bind)}") - - if not lines: - lines = ["(aucun raccourci déclaré)"] - - # dmenu = liste + filtre incrémental ; la sélection n'a pas d'effet. - cmd = ["wofi", "--dmenu", "--prompt", "Raccourcis clavier — taper pour filtrer", - "--width", "820", "--height", "600", "--insensitive", - "--cache-file", "/dev/null"] - if os.path.exists(_STYLE): - # Style dédié : monospace (colonnes alignées) + palette FWS. - cmd += ["--style", _STYLE] - subprocess.run(cmd, input="\n".join(lines), text=True, check=False) - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/configs/releng/airootfs/usr/local/bin/fws-keys b/configs/releng/airootfs/usr/local/bin/fws-keys new file mode 100755 index 0000000..0e3dc5e --- /dev/null +++ b/configs/releng/airootfs/usr/local/bin/fws-keys @@ -0,0 +1,129 @@ +#!/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()) diff --git a/configs/releng/airootfs/usr/local/bin/fws-vr-setup b/configs/releng/airootfs/usr/local/bin/fws-vr-setup index 65e4fb1..34beca4 100755 --- a/configs/releng/airootfs/usr/local/bin/fws-vr-setup +++ b/configs/releng/airootfs/usr/local/bin/fws-vr-setup @@ -24,6 +24,18 @@ 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" @@ -48,12 +60,20 @@ install_pkgs() { 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 || return 1 + 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 diff --git a/configs/releng/airootfs/usr/local/share/fws/hyprland.conf.tmpl b/configs/releng/airootfs/usr/local/share/fws/hyprland.conf.tmpl index 676bcb5..2d00db5 100644 --- a/configs/releng/airootfs/usr/local/share/fws/hyprland.conf.tmpl +++ b/configs/releng/airootfs/usr/local/share/fws/hyprland.conf.tmpl @@ -7,7 +7,7 @@ # # 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-hypr-keys, qui lit les binds RÉELS via hyprctl). Ajoute +# 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). @@ -81,7 +81,7 @@ 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, thunar bindd = $mod, B, Ouvrir Firefox, exec, firefox -bindd = $mod, K, Afficher les raccourcis clavier, exec, fws-hypr-keys +bindd = $mod, K, Afficher les raccourcis clavier, exec, fws-keys # --- Fenêtres --------------------------------------------------------------- bindd = $mod, Q, Fermer la fenêtre, killactive, diff --git a/configs/releng/airootfs/usr/local/share/fws/i3-config b/configs/releng/airootfs/usr/local/share/fws/i3-config index 2742931..52bfb39 100644 --- a/configs/releng/airootfs/usr/local/share/fws/i3-config +++ b/configs/releng/airootfs/usr/local/share/fws/i3-config @@ -41,6 +41,8 @@ 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 ############## ## FENETRES ## @@ -133,23 +135,16 @@ for_window [title="Picture-in-Picture"] floating enable ########### ## BARRE ## ########### -bar { - status_command i3status - position top - colors { - background $base - statusline $text - separator $surface0 - # classe bordure fond texte - focused_workspace $blue $blue $base - inactive_workspace $surface0 $base $text - urgent_workspace $red $red $base - } -} +# 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. +exec --no-startup-id 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 diff --git a/configs/releng/airootfs/usr/local/share/fws/picom.conf b/configs/releng/airootfs/usr/local/share/fws/picom.conf new file mode 100644 index 0000000..6edbb89 --- /dev/null +++ b/configs/releng/airootfs/usr/local/share/fws/picom.conf @@ -0,0 +1,19 @@ +# 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; diff --git a/configs/releng/airootfs/usr/local/share/fws/polybar-config.ini b/configs/releng/airootfs/usr/local/share/fws/polybar-config.ini new file mode 100644 index 0000000..9922af7 --- /dev/null +++ b/configs/releng/airootfs/usr/local/share/fws/polybar-config.ini @@ -0,0 +1,156 @@ +; 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 = keyhelp backlight pulseaudio network cpu memory tray +enable-ipc = true +wm-restack = i3 + +[module/i3] +type = internal/i3 +pin-workspaces = true +show-urgent = true +strip-wsnumbers = false +format = +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/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 =