Files
dotfiles-violet-chaton/INSTALL/configs/waybar/scripts/wob-overlay.py
Tetardtek 140351675d feat(wob): remplacer wob par overlay GTK arrondi violet-chaton
- Nouveau wob-overlay.py : fenêtre GTK3+GtkLayerShell transparente
  avec barre arrondie (border-radius 12px), couleur pink pour volume
  et cyan pour luminosité
- Protocole FIFO typé : v:N pour volume, b:N pour luminosité
- wob-start.sh lance wob-overlay.py au lieu du binaire wob
- wob-volume.sh, wob-brightness.sh, vc-media-popup.py et
  power-profile.sh mis à jour pour envoyer les messages typés
- Correction bug vc-media-popup.py : les deux sliders audio
  contrôlaient @DEFAULT_AUDIO_SINK@ (mauvaise capture de target)
2026-02-23 13:32:02 +01:00

216 lines
6.4 KiB
Python

#!/usr/bin/env python3
# wob-overlay.py — Overlay volume/luminosité violet-chaton (remplace wob)
# Protocole FIFO : "v:N" = volume (rose), "b:N" = luminosité (cyan), "N" = volume
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GtkLayerShell', '0.1')
from gi.repository import Gtk, Gdk, GtkLayerShell, GLib
import os
import threading
FIFO = '/tmp/wob.fifo'
POPUP_WIDTH = 280
POPUP_HEIGHT = -1
MARGIN_BOTTOM = 48
HIDE_DELAY = 1200 # ms
CSS = """
window {
background: transparent;
}
#wob-box {
background-color: rgba(38, 21, 55, 0.92);
border: 2px solid rgba(92, 73, 108, 0.80);
border-radius: 12px;
padding: 10px 16px;
}
#wob-icon {
font-family: "JetBrainsMono Nerd Font";
font-size: 15px;
min-width: 22px;
margin-right: 4px;
}
#wob-pct {
color: #f8f8f2;
font-family: "JetBrainsMono Nerd Font";
font-size: 11px;
font-weight: bold;
min-width: 36px;
margin-left: 6px;
}
/* ── Volume (rose) ──────────────────────────────────────────── */
progressbar.vol trough {
background-color: rgba(92, 73, 108, 0.45);
border-radius: 5px;
min-height: 7px;
border: none;
}
progressbar.vol progress {
background-color: #ff79c6;
border-radius: 5px;
border: none;
min-height: 7px;
}
/* ── Luminosité (cyan) ──────────────────────────────────────── */
progressbar.bright trough {
background-color: rgba(92, 73, 108, 0.45);
border-radius: 5px;
min-height: 7px;
border: none;
}
progressbar.bright progress {
background-color: #8be9fd;
border-radius: 5px;
border: none;
min-height: 7px;
}
"""
VOL_ICONS = ['󰕿', '󰖀', '󰕾']
BRIGHT_ICONS = ['󰃞', '󰃟', '󰃠']
def _icon(icons, pct):
if pct <= 0:
return icons[0]
if pct < 50:
return icons[1]
return icons[2]
class WobOverlay(Gtk.Window):
def __init__(self):
super().__init__()
self._hide_timer = None
# ── Layer shell ──────────────────────────────────────────────────────
GtkLayerShell.init_for_window(self)
GtkLayerShell.set_layer(self, GtkLayerShell.Layer.OVERLAY)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.BOTTOM, True)
GtkLayerShell.set_margin(self, GtkLayerShell.Edge.BOTTOM, MARGIN_BOTTOM)
GtkLayerShell.set_exclusive_zone(self, -1)
GtkLayerShell.set_keyboard_mode(self, GtkLayerShell.KeyboardMode.NONE)
self.set_decorated(False)
self.set_resizable(False)
self.set_app_paintable(True)
self.set_default_size(POPUP_WIDTH, POPUP_HEIGHT)
# Fenêtre transparente
screen = self.get_screen()
visual = screen.get_rgba_visual()
if visual:
self.set_visual(visual)
# ── CSS ──────────────────────────────────────────────────────────────
provider = Gtk.CssProvider()
provider.load_from_data(CSS.encode())
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(), provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
# ── Layout ───────────────────────────────────────────────────────────
outer = Gtk.Box()
outer.set_name('wob-box')
self.add(outer)
self._icon_lbl = Gtk.Label()
self._icon_lbl.set_name('wob-icon')
outer.pack_start(self._icon_lbl, False, False, 0)
self._bar = Gtk.ProgressBar()
self._bar.set_hexpand(True)
self._bar.set_valign(Gtk.Align.CENTER)
outer.pack_start(self._bar, True, True, 0)
self._pct_lbl = Gtk.Label()
self._pct_lbl.set_name('wob-pct')
self._pct_lbl.set_halign(Gtk.Align.END)
outer.pack_end(self._pct_lbl, False, False, 0)
self.show_all()
self.hide()
def show_value(self, val, kind='v'):
"""Appelé depuis le thread FIFO via GLib.idle_add."""
val = max(0, min(100, val))
fraction = val / 100.0
# Icône + couleur selon le type
if kind == 'b':
icon = _icon(BRIGHT_ICONS, val)
css_class = 'bright'
else:
icon = _icon(VOL_ICONS, val)
css_class = 'vol'
sc = self._bar.get_style_context()
for c in ['vol', 'bright']:
sc.remove_class(c)
sc.add_class(css_class)
self._icon_lbl.set_label(icon)
self._icon_lbl.set_markup(
f'<span foreground="{"#8be9fd" if kind == "b" else "#ff79c6"}">{icon}</span>'
)
self._bar.set_fraction(fraction)
self._pct_lbl.set_label(f'{val}%')
self.show()
# Reset timer
if self._hide_timer:
GLib.source_remove(self._hide_timer)
self._hide_timer = GLib.timeout_add(HIDE_DELAY, self._do_hide)
def _do_hide(self):
self.hide()
self._hide_timer = None
return False
def _fifo_reader(overlay):
"""Thread : lit le FIFO en boucle et schedule les mises à jour UI."""
while True:
try:
if not os.path.exists(FIFO):
os.mkfifo(FIFO)
with open(FIFO, 'r') as f:
for line in f:
line = line.strip()
if not line:
continue
# Protocole : "v:N", "b:N" ou plain "N"
if ':' in line:
kind, _, raw = line.partition(':')
else:
kind, raw = 'v', line
try:
val = int(raw)
GLib.idle_add(overlay.show_value, val, kind)
except ValueError:
pass
except OSError:
import time
time.sleep(0.5)
if __name__ == '__main__':
win = WobOverlay()
win.connect('destroy', Gtk.main_quit)
t = threading.Thread(target=_fifo_reader, args=(win,), daemon=True)
t.start()
Gtk.main()