fix: IPC byte order, globales GTK-Theme, Session-Vorauswahl
- ipc.py: !I (Big-Endian) → =I (Native Byte Order) für greetd-Protokoll - Per-User GTK-Theme entfernt, stattdessen globales Theme aus moongreet.toml - Last-Session pro User in /var/cache/moongreet/last-session/ speichern/laden - PKGBUILD und install-Hook für last-session-Cache erweitert
This commit is contained in:
@@ -1,12 +1,15 @@
|
||||
# ABOUTME: Configuration loading from moongreet.toml.
|
||||
# ABOUTME: Parses appearance and behavior settings with wallpaper path resolution.
|
||||
|
||||
import re
|
||||
import tomllib
|
||||
from contextlib import AbstractContextManager
|
||||
from dataclasses import dataclass
|
||||
from importlib.resources import as_file, files
|
||||
from pathlib import Path
|
||||
|
||||
VALID_THEME_NAME = re.compile(r"^[A-Za-z0-9_-]+$")
|
||||
|
||||
DEFAULT_CONFIG_PATHS = [
|
||||
Path("/etc/moongreet/moongreet.toml"),
|
||||
]
|
||||
@@ -17,6 +20,7 @@ class Config:
|
||||
"""Greeter configuration loaded from moongreet.toml."""
|
||||
|
||||
background: Path | None = None
|
||||
gtk_theme: str | None = None
|
||||
|
||||
|
||||
def load_config(config_path: Path | None = None) -> Config:
|
||||
@@ -51,6 +55,10 @@ def load_config(config_path: Path | None = None) -> Config:
|
||||
bg_path = config_path.parent / bg_path
|
||||
config.background = bg_path
|
||||
|
||||
gtk_theme = appearance.get("gtk-theme")
|
||||
if gtk_theme and VALID_THEME_NAME.match(gtk_theme):
|
||||
config.gtk_theme = gtk_theme
|
||||
|
||||
return config
|
||||
|
||||
|
||||
|
||||
+51
-12
@@ -21,13 +21,14 @@ from gi.repository import Gtk, Gdk, GLib, GdkPixbuf
|
||||
from moongreet.config import load_config, resolve_wallpaper_path
|
||||
from moongreet.i18n import load_strings, Strings
|
||||
from moongreet.ipc import create_session, post_auth_response, start_session, cancel_session
|
||||
from moongreet.users import User, get_users, get_avatar_path, get_user_gtk_theme
|
||||
from moongreet.users import User, get_users, get_avatar_path
|
||||
from moongreet.sessions import Session, get_sessions
|
||||
from moongreet.power import reboot, shutdown
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
LAST_USER_PATH = Path("/var/cache/moongreet/last-user")
|
||||
LAST_SESSION_DIR = Path("/var/cache/moongreet/last-session")
|
||||
FAILLOCK_MAX_ATTEMPTS = 3
|
||||
VALID_USERNAME = re.compile(r"^[a-zA-Z0-9_.-]+$")
|
||||
MAX_USERNAME_LENGTH = 256
|
||||
@@ -93,6 +94,7 @@ class GreeterWindow(Gtk.ApplicationWindow):
|
||||
self._failed_attempts: dict[str, int] = {}
|
||||
self._bg_path = bg_path
|
||||
|
||||
self._apply_global_theme()
|
||||
self._build_ui()
|
||||
self._setup_keyboard_navigation()
|
||||
# Defer initial user selection until the window is realized,
|
||||
@@ -286,26 +288,23 @@ class GreeterWindow(Gtk.ApplicationWindow):
|
||||
# which works in ZIP wheels too, no exists() check needed
|
||||
self._set_default_avatar()
|
||||
|
||||
# Apply user's GTK theme if available
|
||||
self._apply_user_theme(user)
|
||||
# Pre-select last used session for this user
|
||||
self._select_last_session(user)
|
||||
|
||||
# Focus password entry
|
||||
self._password_entry.grab_focus()
|
||||
|
||||
def _apply_user_theme(self, user: User) -> None:
|
||||
"""Load the user's preferred GTK theme from their settings.ini."""
|
||||
gtk_config_dir = user.home / ".config" / "gtk-4.0"
|
||||
theme_name = get_user_gtk_theme(config_dir=gtk_config_dir)
|
||||
def _apply_global_theme(self) -> None:
|
||||
"""Apply the GTK theme from moongreet.toml configuration."""
|
||||
theme_name = self._config.gtk_theme
|
||||
if not theme_name:
|
||||
return
|
||||
|
||||
settings = Gtk.Settings.get_default()
|
||||
if settings is None:
|
||||
return
|
||||
|
||||
current = settings.get_property("gtk-theme-name")
|
||||
if theme_name and current != theme_name:
|
||||
settings.set_property("gtk-theme-name", theme_name)
|
||||
elif not theme_name and current:
|
||||
settings.reset_property("gtk-theme-name")
|
||||
settings.set_property("gtk-theme-name", theme_name)
|
||||
|
||||
def _get_foreground_color(self) -> str:
|
||||
"""Get the current GTK theme foreground color as a hex string."""
|
||||
@@ -479,6 +478,7 @@ class GreeterWindow(Gtk.ApplicationWindow):
|
||||
|
||||
if response.get("type") == "success":
|
||||
self._save_last_user(user.username)
|
||||
self._save_last_session(user.username, session.name)
|
||||
self._close_greetd_sock()
|
||||
GLib.idle_add(self.get_application().quit)
|
||||
return
|
||||
@@ -533,6 +533,18 @@ class GreeterWindow(Gtk.ApplicationWindow):
|
||||
return self._sessions[idx]
|
||||
return None
|
||||
|
||||
def _select_last_session(self, user: User) -> None:
|
||||
"""Pre-select the last used session for a user in the dropdown."""
|
||||
if not self._sessions:
|
||||
return
|
||||
last_session_name = self._load_last_session(user.username)
|
||||
if not last_session_name:
|
||||
return
|
||||
for i, session in enumerate(self._sessions):
|
||||
if session.name == last_session_name:
|
||||
self._session_dropdown.set_selected(i)
|
||||
return
|
||||
|
||||
MAX_GREETD_ERROR_LENGTH = 200
|
||||
|
||||
def _show_greetd_error(self, response: dict, fallback: str) -> None:
|
||||
@@ -585,3 +597,30 @@ class GreeterWindow(Gtk.ApplicationWindow):
|
||||
LAST_USER_PATH.write_text(username)
|
||||
except OSError:
|
||||
pass # Non-critical — cache dir may not be writable
|
||||
|
||||
MAX_SESSION_NAME_LENGTH = 256
|
||||
|
||||
@staticmethod
|
||||
def _save_last_session(username: str, session_name: str) -> None:
|
||||
"""Save the last used session name for a user to cache."""
|
||||
if not VALID_USERNAME.match(username) or len(username) > MAX_USERNAME_LENGTH:
|
||||
return
|
||||
try:
|
||||
LAST_SESSION_DIR.mkdir(parents=True, exist_ok=True)
|
||||
(LAST_SESSION_DIR / username).write_text(session_name)
|
||||
except OSError:
|
||||
pass # Non-critical — cache dir may not be writable
|
||||
|
||||
@staticmethod
|
||||
def _load_last_session(username: str) -> str | None:
|
||||
"""Load the last used session name for a user from cache."""
|
||||
session_file = LAST_SESSION_DIR / username
|
||||
if not session_file.exists():
|
||||
return None
|
||||
try:
|
||||
name = session_file.read_text().strip()
|
||||
except OSError:
|
||||
return None
|
||||
if not name or len(name) > GreeterWindow.MAX_SESSION_NAME_LENGTH:
|
||||
return None
|
||||
return name
|
||||
|
||||
@@ -22,14 +22,14 @@ def _recvall(sock: Any, n: int) -> bytes:
|
||||
def send_message(sock: Any, msg: dict) -> None:
|
||||
"""Send a length-prefixed JSON message to the greetd socket."""
|
||||
payload = json.dumps(msg).encode("utf-8")
|
||||
header = struct.pack("!I", len(payload))
|
||||
header = struct.pack("=I", len(payload))
|
||||
sock.sendall(header + payload)
|
||||
|
||||
|
||||
def recv_message(sock: Any) -> dict:
|
||||
"""Receive a length-prefixed JSON message from the greetd socket."""
|
||||
header = _recvall(sock, 4)
|
||||
length = struct.unpack("!I", header)[0]
|
||||
length = struct.unpack("=I", header)[0]
|
||||
|
||||
if length > MAX_PAYLOAD_SIZE:
|
||||
raise ConnectionError(f"Payload too large: {length} bytes (max {MAX_PAYLOAD_SIZE})")
|
||||
|
||||
Reference in New Issue
Block a user