104 lines
3.1 KiB
Python
104 lines
3.1 KiB
Python
import logging
|
|
import os
|
|
import signal
|
|
import sys
|
|
import threading
|
|
import gi
|
|
|
|
gi.require_version("Gtk", "4.0")
|
|
gi.require_version("Adw", "1")
|
|
|
|
from gi.repository import Gtk, Gdk, Adw, Gio, GLib
|
|
|
|
from cht.config import APP_ID, APP_NAME
|
|
from cht.window import ChtWindow
|
|
|
|
|
|
class ChtApp(Adw.Application):
|
|
def __init__(self):
|
|
super().__init__(
|
|
application_id=APP_ID,
|
|
flags=Gio.ApplicationFlags.DEFAULT_FLAGS,
|
|
)
|
|
# Let GLib handle SIGINT/SIGTERM so Ctrl+C triggers graceful shutdown
|
|
GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, self._on_signal)
|
|
GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, self._on_signal)
|
|
|
|
def _on_signal(self):
|
|
log = logging.getLogger("cht")
|
|
log.info("Signal received — shutting down gracefully")
|
|
self.quit()
|
|
return GLib.SOURCE_REMOVE
|
|
|
|
def do_shutdown(self):
|
|
# Ensure all windows tear down before the process exits
|
|
for win in self.get_windows():
|
|
if hasattr(win, "teardown"):
|
|
win.teardown()
|
|
Adw.Application.do_shutdown(self)
|
|
|
|
def do_activate(self):
|
|
win = self.props.active_window
|
|
if not win:
|
|
css = Gtk.CssProvider()
|
|
css.load_from_string(
|
|
".frame-selected { outline: 3px solid @accent_color; outline-offset: -3px; border-radius: 6px; }\n"
|
|
"row.frame-selected, row.frame-selected:hover { background: alpha(@accent_color, 0.25); outline: none; border-radius: 0; }"
|
|
)
|
|
Gtk.StyleContext.add_provider_for_display(
|
|
Gdk.Display.get_default(),
|
|
css,
|
|
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
|
|
)
|
|
win = ChtWindow(application=self)
|
|
win.present()
|
|
|
|
|
|
_STDERR_SKIP = [b"eglExportDMABUFImage"]
|
|
|
|
|
|
def _filter_stderr():
|
|
"""Redirect fd 2 through a pipe; drop lines matching _STDERR_SKIP."""
|
|
real_stderr_fd = os.dup(2)
|
|
real_stderr = os.fdopen(real_stderr_fd, "wb", buffering=0)
|
|
r_fd, w_fd = os.pipe()
|
|
os.dup2(w_fd, 2)
|
|
os.close(w_fd)
|
|
|
|
def _pump():
|
|
with os.fdopen(r_fd, "rb", buffering=0) as pipe:
|
|
buf = b""
|
|
while True:
|
|
chunk = pipe.read(4096)
|
|
if not chunk:
|
|
break
|
|
buf += chunk
|
|
while b"\n" in buf:
|
|
line, buf = buf.split(b"\n", 1)
|
|
if line.strip() and not any(skip in line for skip in _STDERR_SKIP):
|
|
real_stderr.write(line + b"\n")
|
|
real_stderr.flush()
|
|
if buf:
|
|
real_stderr.write(buf)
|
|
real_stderr.flush()
|
|
|
|
t = threading.Thread(target=_pump, daemon=True, name="stderr_filter")
|
|
t.start()
|
|
|
|
|
|
def main():
|
|
_filter_stderr()
|
|
logging.basicConfig(
|
|
level=logging.DEBUG,
|
|
format="%(asctime)s %(levelname)-7s %(name)s: %(message)s",
|
|
datefmt="%H:%M:%S",
|
|
)
|
|
log = logging.getLogger("cht")
|
|
log.info("CHT starting")
|
|
app = ChtApp()
|
|
return app.run(sys.argv)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|