chore(repo): stop tracking build artifacts; add README, license metadata, ruff config
Untrack iso/out/ (3.4 GB ISO), packaging/*/pkg/ trees, *.pkg.tar.zst and tanin-icons/src/ (529 files) and ignore them going forward. Files stay on disk. NOTE: history still carries ~13 GiB of old ISO blobs — needs a git filter-repo pass (see README/report). Also: first README.md, license + license-files in pyproject, minimal ruff config, demo scripts moved to scripts/demo/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,24 +0,0 @@
|
||||
# Activate the tanin-greet greetd config. greetd owns /etc/greetd/config.toml,
|
||||
# so we can't package that path — install it from the shipped template here,
|
||||
# backing up any existing (e.g. distro default / regreet) config once.
|
||||
_activate() {
|
||||
local src=/usr/share/tanin/greetd/config.toml
|
||||
local dst=/etc/greetd/config.toml
|
||||
[ -f "$src" ] || return 0
|
||||
if [ -e "$dst" ] && ! grep -q 'tanin-greet' "$dst" 2>/dev/null && [ ! -e "$dst.tanin-bak" ]; then
|
||||
cp -a "$dst" "$dst.tanin-bak"
|
||||
fi
|
||||
install -Dm644 "$src" "$dst"
|
||||
}
|
||||
|
||||
post_install() {
|
||||
_activate
|
||||
cat <<'EOF'
|
||||
>> tanin-greet: greetd now launches the TANINUX greeter (backup: /etc/greetd/config.toml.tanin-bak)
|
||||
Enable the login screen with: systemctl enable greetd (disable any other DM first)
|
||||
EOF
|
||||
}
|
||||
|
||||
post_upgrade() {
|
||||
_activate
|
||||
}
|
||||
Binary file not shown.
@@ -1,22 +0,0 @@
|
||||
# Generated by makepkg 7.1.0
|
||||
# using fakeroot version 1.37.2
|
||||
pkgname = tanin-greet
|
||||
pkgbase = tanin-greet
|
||||
xdata = pkgtype=pkg
|
||||
pkgver = 0.1.0-2
|
||||
pkgdesc = TANINUX login — custom GTK4 greeter for greetd (mirrors the topbar)
|
||||
url = https://taninux.kgva.ch
|
||||
builddate = 1781890154
|
||||
packager = Unknown Packager
|
||||
size = 18155
|
||||
arch = any
|
||||
license = GPL-3.0-or-later
|
||||
backup = etc/greetd/tanin-greet.css
|
||||
depend = greetd
|
||||
depend = cage
|
||||
depend = gtk4
|
||||
depend = python
|
||||
depend = python-gobject
|
||||
depend = adwaita-icon-theme
|
||||
depend = inter-font
|
||||
optdepend = tanin-backgrounds: default login wallpaper at /usr/share/backgrounds/tanin/login.jpg
|
||||
@@ -1,176 +0,0 @@
|
||||
/* =========================================================================
|
||||
* tanin-greet — stylesheet. Mirrors the eww topbar (eww.scss): Fuji palette,
|
||||
* monochrome, pill shapes, Inter Medium. Accent is the brand Fuji violet.
|
||||
* ========================================================================= */
|
||||
@define-color bg #0f0f0f;
|
||||
@define-color bg_alt #191919;
|
||||
@define-color bg_hi #242424;
|
||||
@define-color fg #f0f0f0;
|
||||
@define-color yuki #f0f0f0; /* snow white — pill fill */
|
||||
@define-color muted #8a8a8a;
|
||||
@define-color accent #a39ec4;
|
||||
|
||||
/* No font-family override — inherit the system UI font. */
|
||||
.greeter {
|
||||
font-weight: 500;
|
||||
color: @fg;
|
||||
}
|
||||
* { text-shadow: none; -gtk-icon-shadow: none; outline: none; }
|
||||
|
||||
/* dark scrim over the wallpaper — keeps text readable, keeps it moody */
|
||||
.scrim { background-color: alpha(black, 0.55); }
|
||||
|
||||
/* ---- top bar: the eww bar look (opaque #0f0f0f strip) ------------------- *
|
||||
* left = session dropdown · center = clock (time/day/date) · right = power. */
|
||||
.topbar {
|
||||
background-color: @bg;
|
||||
min-height: 34px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
.topbar .clock {
|
||||
color: @fg;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* session dropdown, top-left corner — quiet on the black bar, no box */
|
||||
.session-dd,
|
||||
.session-dd > button {
|
||||
background: transparent;
|
||||
background-image: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
min-height: 0;
|
||||
padding: 2px 6px;
|
||||
color: @muted;
|
||||
font-size: 12px;
|
||||
}
|
||||
.session-dd > button:hover { color: @fg; }
|
||||
|
||||
/* power button — NO background at all, just the glyph */
|
||||
.topbar .power,
|
||||
.topbar .power > button {
|
||||
background: transparent;
|
||||
background-image: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
padding: 2px 6px;
|
||||
color: @muted;
|
||||
}
|
||||
.topbar .power:hover,
|
||||
.topbar .power > button:hover { color: @accent; background: transparent; }
|
||||
|
||||
/* TANINUX wordmark — bottom center, understated */
|
||||
.brand {
|
||||
color: @fg;
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
letter-spacing: 3px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* ---- centered login card (flat, no heavy frame — minimal) --------------- *
|
||||
* NB: class is .logincard, never .card — libadwaita's built-in .card recipe
|
||||
* paints an elevated background + shadow (the "solid box, too big" bug). */
|
||||
.logincard { padding: 8px; background: transparent; box-shadow: none; }
|
||||
|
||||
.avatar {
|
||||
color: @fg;
|
||||
background-color: alpha(@accent, 0.14);
|
||||
border-radius: 999px;
|
||||
padding: 16px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
/* username row — plain name + chevron on the right, same width as the pill so
|
||||
* the chevron sits where the password pill ends. No background (just the name). */
|
||||
.namerow { min-width: 300px; }
|
||||
.username {
|
||||
color: @yuki;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.userswitch {
|
||||
background: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
padding: 2px 4px;
|
||||
color: @muted;
|
||||
}
|
||||
.userswitch:hover { color: @fg; }
|
||||
|
||||
/* password pill — snow white (yuki), dark text, accent focus ring.
|
||||
* border-radius 999px like the bar's workspaces/clock. */
|
||||
.pill {
|
||||
min-width: 300px;
|
||||
min-height: 0;
|
||||
background-color: alpha(@bg_alt, 0.45); /* greyish, slightly transparent */
|
||||
color: @fg;
|
||||
border: 1.5px solid alpha(@fg, 0.22); /* grey border (unfocused) */
|
||||
border-radius: 999px;
|
||||
padding: 11px 18px;
|
||||
box-shadow: none;
|
||||
}
|
||||
entry.pill { caret-color: @yuki; }
|
||||
entry.pill text { color: @fg; }
|
||||
entry.pill text > placeholder { color: alpha(@fg, 0.45); }
|
||||
entry.pill image { color: alpha(@fg, 0.6); } /* peek-eye icon */
|
||||
/* clicked / focused → white border */
|
||||
.pill:focus-within {
|
||||
border-color: @yuki;
|
||||
background-color: alpha(@bg_alt, 0.55);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* popup lists (session dropdown, user switcher).
|
||||
* IMPORTANT: only style `popover > contents` — the outer `popover` node also
|
||||
* covers the reserved shadow/margin area, so a solid bg there paints a huge
|
||||
* solid box. Keep the outer node fully transparent. */
|
||||
popover {
|
||||
background: transparent;
|
||||
background-image: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
popover > contents {
|
||||
background-color: @bg_alt;
|
||||
color: @fg;
|
||||
border: 1px solid alpha(@fg, 0.06);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 24px alpha(black, 0.45);
|
||||
padding: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
/* kill the inner list's OWN themed background (the lighter nested box) so the
|
||||
* whole popover reads as one uniform surface. */
|
||||
popover listview,
|
||||
popover scrolledwindow,
|
||||
popover scrolledwindow > viewport,
|
||||
popover .view {
|
||||
background: transparent;
|
||||
background-color: transparent;
|
||||
color: @fg;
|
||||
}
|
||||
popover listview > row,
|
||||
popover .menuitem { border-radius: 8px; padding: 5px 10px; }
|
||||
popover listview > row:selected,
|
||||
popover listview > row:hover,
|
||||
popover row:selected,
|
||||
popover .menuitem:hover { background-color: alpha(@accent, 0.14); color: @accent; }
|
||||
|
||||
.error { color: #e0a3a3; font-size: 12px; margin-top: 4px; }
|
||||
|
||||
/* power popover buttons */
|
||||
.menuitem {
|
||||
background: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
border-radius: 8px;
|
||||
padding: 6px 14px;
|
||||
color: @fg;
|
||||
}
|
||||
.menuitem:hover { background-color: alpha(@accent, 0.14); color: @accent; }
|
||||
@@ -1,328 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""tanin-greet — TANINUX login greeter (GTK4 / PyGObject).
|
||||
|
||||
Custom layout: a black top bar like the eww topbar (brand · clock · power), a
|
||||
wallpaper background, user + password centered as pills, and a small session
|
||||
selector tucked in the bottom-right corner. Talks to greetd over GREETD_SOCK.
|
||||
|
||||
Run by greetd via cage: cage -s -- tanin-greet
|
||||
Preview without greetd: just run it — the UI renders; only login needs the
|
||||
socket (set GREETD_SOCK via a mock for a full dry-run).
|
||||
|
||||
Env overrides (handy for previewing from the repo):
|
||||
TANIN_GREET_CSS path to the stylesheet (default /etc/greetd/tanin-greet.css)
|
||||
TANIN_GREET_BG wallpaper image path (default /usr/share/backgrounds/tanin/login.jpg)
|
||||
TANIN_GREET_SESSIONS wayland-sessions dir (default /usr/share/wayland-sessions)
|
||||
"""
|
||||
import gi, os, sys, json, socket, struct, shlex, glob, pwd
|
||||
|
||||
gi.require_version("Gtk", "4.0")
|
||||
from gi.repository import Gtk, GLib, Gdk, Gio # noqa: E402
|
||||
|
||||
CSS_PATH = os.environ.get("TANIN_GREET_CSS", "/etc/greetd/tanin-greet.css")
|
||||
WALLPAPER = os.environ.get("TANIN_GREET_BG", "/usr/share/backgrounds/tanin/login.jpg")
|
||||
SESSION_DIR = os.environ.get("TANIN_GREET_SESSIONS", "/usr/share/wayland-sessions")
|
||||
BRAND = os.environ.get("TANIN_GREET_BRAND", "TANINUX")
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# greetd IPC client — native-endian u32 length prefix + JSON payload.
|
||||
# --------------------------------------------------------------------------- #
|
||||
class Greetd:
|
||||
def __init__(self):
|
||||
path = os.environ.get("GREETD_SOCK")
|
||||
self.sock = None
|
||||
if path:
|
||||
try:
|
||||
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self.sock.connect(path)
|
||||
except OSError:
|
||||
# No reachable greetd (e.g. preview without a daemon). Render the
|
||||
# UI anyway; login() will report the missing socket on submit.
|
||||
self.sock = None
|
||||
|
||||
def _recvn(self, n):
|
||||
buf = b""
|
||||
while len(buf) < n:
|
||||
chunk = self.sock.recv(n - len(buf))
|
||||
if not chunk:
|
||||
raise RuntimeError("greetd closed the connection")
|
||||
buf += chunk
|
||||
return buf
|
||||
|
||||
def _send(self, obj):
|
||||
data = json.dumps(obj).encode()
|
||||
self.sock.sendall(struct.pack("=I", len(data)) + data)
|
||||
|
||||
def _recv(self):
|
||||
n = struct.unpack("=I", self._recvn(4))[0]
|
||||
return json.loads(self._recvn(n))
|
||||
|
||||
def login(self, username, password, cmd_argv):
|
||||
"""Authenticate and start the session. Raises on failure."""
|
||||
if self.sock is None:
|
||||
raise RuntimeError("GREETD_SOCK not set (preview mode — cannot log in)")
|
||||
self._send({"type": "create_session", "username": username})
|
||||
while True:
|
||||
r = self._recv()
|
||||
t = r.get("type")
|
||||
if t == "auth_message":
|
||||
amt = r.get("auth_message_type")
|
||||
if amt in ("secret", "visible"):
|
||||
self._send({"type": "post_auth_message_response", "response": password})
|
||||
else: # info / error — acknowledge with no response
|
||||
self._send({"type": "post_auth_message_response"})
|
||||
elif t == "success":
|
||||
break
|
||||
elif t == "error":
|
||||
self._send({"type": "cancel_session"})
|
||||
raise RuntimeError(r.get("description", "authentication failed"))
|
||||
else:
|
||||
raise RuntimeError(f"unexpected greetd reply: {r}")
|
||||
# authenticated → launch the chosen session
|
||||
self._send({"type": "start_session", "cmd": cmd_argv, "env": []})
|
||||
r = self._recv()
|
||||
if r.get("type") != "success":
|
||||
self._send({"type": "cancel_session"})
|
||||
raise RuntimeError(r.get("description", "failed to start session"))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# System enumeration
|
||||
# --------------------------------------------------------------------------- #
|
||||
def list_users():
|
||||
users = []
|
||||
for p in pwd.getpwall():
|
||||
if 1000 <= p.pw_uid < 65534 and p.pw_shell not in ("/usr/bin/nologin", "/sbin/nologin", "/bin/false"):
|
||||
users.append(p.pw_name)
|
||||
return users or [os.environ.get("USER", "user")]
|
||||
|
||||
|
||||
def list_sessions():
|
||||
sessions = [] # (name, exec_argv)
|
||||
for f in sorted(glob.glob(os.path.join(SESSION_DIR, "*.desktop"))):
|
||||
name = exec_ = None
|
||||
try:
|
||||
for line in open(f, encoding="utf-8"):
|
||||
if line.startswith("Name=") and name is None:
|
||||
name = line[5:].strip()
|
||||
elif line.startswith("Exec=") and exec_ is None:
|
||||
exec_ = line[5:].strip()
|
||||
except OSError:
|
||||
continue
|
||||
if name and exec_:
|
||||
sessions.append((name, shlex.split(exec_)))
|
||||
return sessions or [("Niri", ["niri-session"])]
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# UI
|
||||
# --------------------------------------------------------------------------- #
|
||||
class Greeter(Gtk.Application):
|
||||
def __init__(self):
|
||||
super().__init__(application_id="ch.gabrielevarano.TaninGreet")
|
||||
self.greetd = Greetd()
|
||||
self.users = list_users()
|
||||
self.sessions = list_sessions()
|
||||
|
||||
def do_activate(self):
|
||||
self._load_css()
|
||||
Gtk.Settings.get_default().set_property("gtk-application-prefer-dark-theme", True)
|
||||
|
||||
win = Gtk.ApplicationWindow(application=self)
|
||||
win.set_decorated(False)
|
||||
win.fullscreen()
|
||||
win.add_css_class("greeter")
|
||||
|
||||
overlay = Gtk.Overlay()
|
||||
win.set_child(overlay)
|
||||
|
||||
# background image (falls back to the scrim colour if missing)
|
||||
if os.path.exists(WALLPAPER):
|
||||
bg = Gtk.Picture.new_for_filename(WALLPAPER)
|
||||
bg.set_content_fit(Gtk.ContentFit.COVER)
|
||||
overlay.set_child(bg)
|
||||
else:
|
||||
overlay.set_child(Gtk.Box())
|
||||
|
||||
# dark scrim so text stays readable over any wallpaper
|
||||
scrim = Gtk.Box()
|
||||
scrim.add_css_class("scrim")
|
||||
scrim.set_hexpand(True)
|
||||
scrim.set_vexpand(True)
|
||||
overlay.add_overlay(scrim)
|
||||
|
||||
# main column: top bar + centered login
|
||||
col = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
col.append(self._topbar())
|
||||
center = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
center.set_vexpand(True)
|
||||
center.set_valign(Gtk.Align.CENTER)
|
||||
center.set_halign(Gtk.Align.CENTER)
|
||||
center.append(self._login_card())
|
||||
col.append(center)
|
||||
col.append(self._footer()) # TANINUX wordmark, bottom center
|
||||
overlay.add_overlay(col)
|
||||
|
||||
win.present()
|
||||
self.password.grab_focus()
|
||||
|
||||
# debug: auto-open the session dropdown so previews can screenshot the
|
||||
# popover without a click (TANIN_GREET_POPUP=1). No effect in production.
|
||||
if os.environ.get("TANIN_GREET_POPUP"):
|
||||
def _open():
|
||||
btn = self.session_dd.get_first_child()
|
||||
try:
|
||||
btn.set_active(True)
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
return False
|
||||
GLib.timeout_add(900, _open)
|
||||
|
||||
# ---- pieces ----------------------------------------------------------- #
|
||||
def _topbar(self):
|
||||
bar = Gtk.CenterBox()
|
||||
bar.add_css_class("topbar")
|
||||
|
||||
# top-left corner: session dropdown
|
||||
bar.set_start_widget(self._session_dd())
|
||||
|
||||
# center: clock — time · weekday, date
|
||||
self.clock = Gtk.Label()
|
||||
self.clock.add_css_class("clock")
|
||||
bar.set_center_widget(self.clock)
|
||||
self._tick()
|
||||
GLib.timeout_add_seconds(1, self._tick)
|
||||
|
||||
# top-right corner: power
|
||||
bar.set_end_widget(self._power_button())
|
||||
return bar
|
||||
|
||||
def _session_dd(self):
|
||||
self.session_dd = Gtk.DropDown.new_from_strings([s[0] for s in self.sessions])
|
||||
self.session_dd.add_css_class("session-dd")
|
||||
for i, (name, _) in enumerate(self.sessions):
|
||||
if name.lower().startswith("niri"):
|
||||
self.session_dd.set_selected(i)
|
||||
break
|
||||
return self.session_dd
|
||||
|
||||
def _footer(self):
|
||||
box = Gtk.Box()
|
||||
box.set_halign(Gtk.Align.CENTER)
|
||||
box.set_valign(Gtk.Align.END)
|
||||
box.set_margin_bottom(18)
|
||||
lbl = Gtk.Label(label=BRAND)
|
||||
lbl.add_css_class("brand")
|
||||
box.append(lbl)
|
||||
return box
|
||||
|
||||
def _power_button(self):
|
||||
btn = Gtk.MenuButton()
|
||||
btn.add_css_class("power")
|
||||
btn.set_icon_name("system-shutdown-symbolic")
|
||||
pop = Gtk.Popover()
|
||||
pop.add_css_class("menu")
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
|
||||
for label, argv in (("Reboot", ["systemctl", "reboot"]),
|
||||
("Power Off", ["systemctl", "poweroff"])):
|
||||
b = Gtk.Button(label=label)
|
||||
b.add_css_class("menuitem")
|
||||
b.connect("clicked", lambda _w, a=argv: self._run(a))
|
||||
box.append(b)
|
||||
pop.set_child(box)
|
||||
btn.set_popover(pop)
|
||||
return btn
|
||||
|
||||
def _login_card(self):
|
||||
card = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=14)
|
||||
card.add_css_class("logincard") # NOT "card" — libadwaita's .card adds a bg+shadow
|
||||
card.set_halign(Gtk.Align.CENTER)
|
||||
|
||||
avatar = Gtk.Image.new_from_icon_name("avatar-default-symbolic")
|
||||
avatar.set_pixel_size(64)
|
||||
avatar.add_css_class("avatar")
|
||||
avatar.set_halign(Gtk.Align.CENTER)
|
||||
card.append(avatar)
|
||||
|
||||
# user row — just the NAME as text, with a small dropdown chevron on the
|
||||
# right edge (aligned with where the password pill ends). Not a pill.
|
||||
name_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
name_row.add_css_class("namerow")
|
||||
name_row.set_halign(Gtk.Align.CENTER)
|
||||
|
||||
self.name_label = Gtk.Label(label=self.users[0])
|
||||
self.name_label.add_css_class("username")
|
||||
self.name_label.set_xalign(0.5) # centered
|
||||
self.name_label.set_hexpand(True)
|
||||
name_row.append(self.name_label)
|
||||
|
||||
self.user_btn = Gtk.MenuButton()
|
||||
self.user_btn.add_css_class("userswitch")
|
||||
self.user_btn.set_icon_name("pan-down-symbolic")
|
||||
self.user_btn.set_visible(len(self.users) > 1) # no point if there's one user
|
||||
upop = Gtk.Popover()
|
||||
upop.add_css_class("menu")
|
||||
ubox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
|
||||
for u in self.users:
|
||||
b = Gtk.Button(label=u)
|
||||
b.add_css_class("menuitem")
|
||||
b.connect("clicked", lambda _w, name=u: (self.name_label.set_text(name),
|
||||
self.user_btn.popdown()))
|
||||
ubox.append(b)
|
||||
upop.set_child(ubox)
|
||||
self.user_btn.set_popover(upop)
|
||||
name_row.append(self.user_btn)
|
||||
card.append(name_row)
|
||||
|
||||
# password pill
|
||||
self.password = Gtk.PasswordEntry()
|
||||
self.password.set_show_peek_icon(True)
|
||||
self.password.set_property("placeholder-text", "Password")
|
||||
self.password.add_css_class("pill")
|
||||
self.password.connect("activate", self._on_submit)
|
||||
card.append(self.password)
|
||||
|
||||
self.error = Gtk.Label()
|
||||
self.error.add_css_class("error")
|
||||
self.error.set_visible(False)
|
||||
card.append(self.error)
|
||||
return card
|
||||
|
||||
# ---- behaviour -------------------------------------------------------- #
|
||||
def _tick(self):
|
||||
now = GLib.DateTime.new_now_local()
|
||||
self.clock.set_text(now.format("%H:%M · %A, %d %B %Y"))
|
||||
return True
|
||||
|
||||
def _on_submit(self, _entry):
|
||||
user = self.name_label.get_text()
|
||||
pw = self.password.get_text()
|
||||
cmd = self.sessions[self.session_dd.get_selected()][1]
|
||||
try:
|
||||
self.greetd.login(user, pw, cmd)
|
||||
self.quit() # greetd takes over from here
|
||||
except Exception as e: # noqa: BLE001
|
||||
self.password.set_text("")
|
||||
self.error.set_text(str(e))
|
||||
self.error.set_visible(True)
|
||||
|
||||
def _run(self, argv):
|
||||
try:
|
||||
Gio.Subprocess.new(argv, Gio.SubprocessFlags.NONE)
|
||||
except GLib.Error as e:
|
||||
self.error.set_text(str(e))
|
||||
self.error.set_visible(True)
|
||||
|
||||
def _load_css(self):
|
||||
if not os.path.exists(CSS_PATH):
|
||||
return
|
||||
provider = Gtk.CssProvider()
|
||||
provider.load_from_path(CSS_PATH)
|
||||
Gtk.StyleContext.add_provider_for_display(
|
||||
Gdk.Display.get_default(), provider,
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(Greeter().run(None))
|
||||
@@ -1,8 +0,0 @@
|
||||
# greetd config for tanin-greet (TANINUX custom GTK4 greeter).
|
||||
# The greeter is a GTK app, so it's hosted by `cage` (one-window kiosk compositor).
|
||||
[terminal]
|
||||
vt = 1
|
||||
|
||||
[default_session]
|
||||
command = "cage -s -- tanin-greet"
|
||||
user = "greeter"
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user