From 819745a39220b49e848422b4bf0ee71dc317fc4d Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Tue, 10 Nov 2026 10:03:00 +0100 Subject: feat: dashboard-only mode and keystone settings integration The second display is now optional: [display] enabled = false skips the headless output, wayvnc, AVNC and the VNC tunnel, so the tablet runs the dashboard alone. `deck-it env` reports it as EXTEND. Keystone's new deck-it page (quickshell, desktop/modules/deckit) edits the config and the autostart line, which needed: - The user config is merged over the shipped config.toml key by key within each table instead of replacing it whole, so the page writes a sparse file and an unset key falls back to the default. - `deck-it defaults` prints the shipped config as JSON for the page's placeholders, so keystone never copies the defaults. - `deck-it status` prints one JSON line (server, display, waiting, tablet, extend) for the page's status line and restart check. - [launch] is now `apps`, a list of desktop ids run with gtk-launch, with name and icon read from the .desktop file, so the page can pick launchers from the installed apps. The old command tables are gone; the client still sends only an id, which must be listed and installed. A launcher whose icon the theme lacks shows its name. - [wifi] address gives the tablet's address in config. The last address that worked is kept in ~/.config/deck-it/last-wifi: `stop` deletes the runtime serial file, so a restart from the page used to wait forever. - `stop` also ends a login's `wifi --wait`, matched by its own command line only, so repeated restarts do not pile up waiters. Also fixed: `deck-it env` fell through into the usage text, and the usage text printed the license header since the header was added. Co-Authored-By: Claude Opus 5.5 --- tests/test_windows.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'tests/test_windows.py') diff --git a/tests/test_windows.py b/tests/test_windows.py index e52e23d..e632406 100644 --- a/tests/test_windows.py +++ b/tests/test_windows.py @@ -31,10 +31,12 @@ class DesktopIcons(unittest.TestCase): from pathlib import Path base = Path(tempfile.mkdtemp()) (base / "com.obsproject.Studio.desktop").write_text( - "[Desktop Entry]\nIcon=com.obsproject.Studio\nStartupWMClass=obs\n") - mapping = windows._desktop_icons([base]) + "[Desktop Entry]\nName=OBS Studio\nIcon=com.obsproject.Studio\nStartupWMClass=obs\n" + "[Desktop Action x]\nName=Other\n") + mapping = windows._desktop_icons(windows._desktop_entries([base])) self.assertEqual(mapping["com.obsproject.studio"], ["com.obsproject.Studio", "obs"]) self.assertEqual(mapping["obs"], ["com.obsproject.Studio", "obs"]) + self.assertEqual(windows._desktop_entries([base])["com.obsproject.Studio"]["name"], "OBS Studio") class Move(unittest.TestCase): @@ -86,14 +88,30 @@ class Commands(unittest.TestCase): ("hyprctl", "activewindow", "-j"): "{}", ("hyprctl", "workspaces", "-j"): '[{"id": 3}]', } - m = windows.make({ - "windows": {"workspaces": [1, 2, 3, 4]}, - "launch": {"obs": {"icon": "obsstudio", "command": "obs"}}, - "run": lambda cmd: responses[tuple(cmd)], - }) + entries = {"com.obsproject.Studio": {"name": "OBS Studio", "icon": "obs", "wmclass": None}} + with mock.patch.object(windows, "_desktop_entries", lambda: entries): + m = windows.make({ + "windows": {"workspaces": [1, 2, 3, 4]}, + "launch": {"apps": ["com.obsproject.Studio", "not-installed"]}, + "run": lambda cmd: responses[tuple(cmd)], + }) st = m.state() self.assertEqual(st["workspaces"], [1, 2, 3, 4]) - self.assertEqual(st["launches"], [{"key": "obs", "icon": "obsstudio", "label": "obs"}]) + self.assertEqual(st["launches"], + [{"key": "com.obsproject.Studio", "icon": "obs", "label": "OBS Studio"}]) + + def test_launch_runs_only_listed_installed_apps(self): + entries = {"kitty": {"name": "kitty", "icon": "kitty", "wmclass": None}, + "other": {"name": "Other", "icon": None, "wmclass": None}} + calls = [] + with mock.patch.object(windows, "_desktop_entries", lambda: entries): + m = windows.make({"launch": {"apps": ["kitty", "gone"]}, "run": lambda cmd: "[]"}) + with mock.patch.object(windows, "_popen", calls.append): + m.actions["launch"]({"key": "kitty"}) + for bad in ("other", "gone", "kitty; rm"): + with self.assertRaises(windows.ModuleError): + m.actions["launch"]({"key": bad}) + self.assertEqual(calls, [["gtk-launch", "kitty"]]) if __name__ == "__main__": -- cgit v1.2.3