Files

205 lines
5.9 KiB
Python
Executable File

#!/usr/bin/env python3
import os
import signal
import sys
import time
import gi
gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Adw, Gdk, GLib, Gtk
try:
gi.require_version("Gtk4LayerShell", "1.0")
from gi.repository import Gtk4LayerShell
except (ImportError, ValueError):
Gtk4LayerShell = None
BACKLIGHT_PATH = os.environ.get(
"BRIGHTNESS_OSD_BACKLIGHT", "/sys/class/backlight/intel_backlight"
)
PID_FILE = os.environ.get("BRIGHTNESS_OSD_PID", "/tmp/brightness-osd.pid")
VALUE_FILE = os.environ.get("BRIGHTNESS_OSD_VALUE", "/tmp/brightness-osd.value")
VISIBLE_MS = int(os.environ.get("BRIGHTNESS_OSD_VISIBLE_MS", "1400"))
def read_percent():
if len(sys.argv) > 1:
return max(0, min(100, int(round(float(sys.argv[1])))))
with open(os.path.join(BACKLIGHT_PATH, "brightness"), encoding="utf-8") as handle:
current = int(handle.read().strip())
with open(os.path.join(BACKLIGHT_PATH, "max_brightness"), encoding="utf-8") as handle:
maximum = int(handle.read().strip())
return max(0, min(100, round(current * 100 / maximum)))
def read_existing_pid():
try:
with open(PID_FILE, encoding="utf-8") as handle:
return int(handle.read().strip())
except (FileNotFoundError, ValueError):
return None
def process_exists(pid):
try:
os.kill(pid, 0)
return True
except ProcessLookupError:
return False
except PermissionError:
return True
def signal_existing(percent):
pid = None
for _attempt in range(5):
pid = read_existing_pid()
if pid and pid != os.getpid() and process_exists(pid):
break
time.sleep(0.03)
else:
return False
with open(VALUE_FILE, "w", encoding="utf-8") as handle:
handle.write(str(percent))
try:
os.kill(pid, signal.SIGUSR1)
return True
except ProcessLookupError:
return False
class BrightnessOsd(Adw.Application):
def __init__(self, percent):
super().__init__(application_id="local.brightness.osd")
self.percent = percent
self.label = None
self.bar = None
self.timeout_id = None
def update_percent(self, percent):
self.percent = max(0, min(100, percent))
if self.label:
self.label.set_label(f"{self.percent}%")
if self.bar:
self.bar.set_value(self.percent)
if self.timeout_id:
GLib.source_remove(self.timeout_id)
self.timeout_id = GLib.timeout_add(VISIBLE_MS, self.quit)
def handle_refresh(self, *_args):
try:
with open(VALUE_FILE, encoding="utf-8") as handle:
percent = int(handle.read().strip())
except (FileNotFoundError, ValueError):
percent = read_percent()
GLib.idle_add(self.update_percent, percent)
def do_activate(self):
with open(PID_FILE, "w", encoding="utf-8") as handle:
handle.write(str(os.getpid()))
window = Gtk.ApplicationWindow(application=self)
window.set_title("Brightness")
window.set_decorated(False)
window.set_resizable(False)
window.set_default_size(190, 58)
window.set_opacity(0.92)
window.set_hide_on_close(True)
if Gtk4LayerShell is not None:
Gtk4LayerShell.init_for_window(window)
Gtk4LayerShell.set_layer(window, Gtk4LayerShell.Layer.OVERLAY)
Gtk4LayerShell.set_anchor(window, Gtk4LayerShell.Edge.TOP, True)
Gtk4LayerShell.set_margin(window, Gtk4LayerShell.Edge.TOP, 28)
Gtk4LayerShell.set_keyboard_mode(window, Gtk4LayerShell.KeyboardMode.NONE)
css = Gtk.CssProvider()
css.load_from_data(
b"""
window {
background: transparent;
}
.osd {
background: alpha(@window_fg_color, 0.88);
color: @window_bg_color;
border-radius: 15px;
padding: 10px 14px;
box-shadow: 0 10px 34px alpha(black, 0.34);
}
.osd image {
color: @window_bg_color;
}
.percent {
font-size: 15px;
font-weight: 700;
}
levelbar trough {
min-height: 6px;
border-radius: 999px;
background: alpha(@window_bg_color, 0.24);
}
levelbar block.filled {
min-height: 6px;
border-radius: 999px;
background: @window_bg_color;
}
"""
)
Gtk.StyleContext.add_provider_for_display(
Gdk.Display.get_default(), css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
box.add_css_class("osd")
box.set_halign(Gtk.Align.CENTER)
box.set_valign(Gtk.Align.START)
box.set_margin_top(28)
icon = Gtk.Image.new_from_icon_name("display-brightness-symbolic")
icon.set_pixel_size(22)
self.label = Gtk.Label(label=f"{self.percent}%")
self.label.add_css_class("percent")
self.label.set_width_chars(4)
self.bar = Gtk.LevelBar.new_for_interval(0, 100)
self.bar.set_value(self.percent)
self.bar.set_size_request(96, -1)
self.bar.set_valign(Gtk.Align.CENTER)
box.append(icon)
box.append(self.bar)
box.append(self.label)
window.set_child(box)
signal.signal(signal.SIGUSR1, self.handle_refresh)
window.present()
self.timeout_id = GLib.timeout_add(VISIBLE_MS, self.quit)
def main():
percent = read_percent()
if signal_existing(percent):
return 0
with open(PID_FILE, "w", encoding="utf-8") as handle:
handle.write(str(os.getpid()))
app = BrightnessOsd(percent)
return app.run([])
if __name__ == "__main__":
raise SystemExit(main())