From 2a4264a1f033c62bc2f2d962b544f8141f407c0f Mon Sep 17 00:00:00 2001 From: Matteo Benedetto Date: Sun, 30 Aug 2026 15:28:47 +0200 Subject: [PATCH] feat: add KDE Show Desktop automation --- index.ts | 79 +++++++++++++++++++++++++++++++++++++- skills/kde-verify/SKILL.md | 13 ++++++- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/index.ts b/index.ts index e3a1da6..3ddeaf0 100644 --- a/index.ts +++ b/index.ts @@ -5,6 +5,7 @@ * - Screenshot (spectacle CLI) * - Input simulato (ydotool: click, type, mousemove) * - Gestione finestre (KWin scripting via D-Bus) + * - Mostra Desktop (KWin D-Bus / KGlobalAccel, compatibile Wayland) * - Ispezione UI (AT-SPI via python-dbus) * * Include la prassi di verifica UI come promptGuidelines e una skill /skill:kde-verify. @@ -73,6 +74,62 @@ print(found ? "ok" : "notfound");`; return "script KWin eseguito per " + resourceClass; } +/** Legge lo stato Mostra Desktop esposto da KWin. */ +async function readShowDesktopState(): Promise { + return (await run("qdbus6", [ + "org.kde.KWin", + "/KWin", + "org.freedesktop.DBus.Properties.Get", + "org.kde.KWin", + "showingDesktop", + ])).trim(); +} + +/** Attende lo stato desiderato, perché showDesktop è Q_NOREPLY e asincrono. */ +async function waitShowDesktopState(expected?: string): Promise { + let state = await readShowDesktopState(); + for (let i = 0; expected && state !== expected && i < 10; i++) { + await new Promise((resolve) => setTimeout(resolve, 100)); + state = await readShowDesktopState(); + } + return state; +} + +/** Imposta o alterna la modalità Mostra Desktop via API D-Bus native Wayland. */ +async function setShowDesktop(action: string): Promise { + if (action !== "show" && action !== "hide" && action !== "toggle") { + throw new Error(`azione non valida: ${action}; usare show, hide oppure toggle`); + } + + let expected: string | undefined; + if (action === "toggle") { + const before = await readShowDesktopState(); + expected = before === "true" ? "false" : before === "false" ? "true" : undefined; + // KGlobalAccel esegue l'azione registrata da KWin senza simulare input. + await run("qdbus6", [ + "org.kde.kglobalaccel", + "/component/kwin", + "org.kde.kglobalaccel.Component.invokeShortcut", + "Show Desktop", + ]); + } else { + expected = action === "show" ? "true" : "false"; + // dbus-send tipizza esplicitamente il booleano; qdbus6 su questa build + // lo interpreta in modo non affidabile se passato come stringa "true". + await run("dbus-send", [ + "--session", + "--dest=org.kde.KWin", + "--type=method_call", + "/KWin", + "org.kde.KWin.showDesktop", + `boolean:${action === "show"}`, + ]); + } + + const state = await waitShowDesktopState(expected); + return `Mostra Desktop: azione=${action}; showingDesktop=${state}`; +} + /** Ispeziona l'albero AT-SPI (applicazioni + primi children). */ async function inspectUI(depth: number): Promise { const py = ` @@ -109,11 +166,12 @@ export default function kdeAutomation(pi: ExtensionAPI) { pi.on("before_agent_start", async (event) => { const guidelines = event.systemPromptOptions.promptGuidelines ?? []; const kdeGuidelines = [ - "KDE AUTOMATION (desktop Plasma 6 Wayland): per verifiche UI usa i tool kde_* (kde_screenshot, kde_click, kde_type, kde_mousemove, kde_window_activate, kde_ui_inspect).", + "KDE AUTOMATION (desktop Plasma 6 Wayland): per verifiche UI usa i tool kde_* (kde_screenshot, kde_click, kde_type, kde_mousemove, kde_window_activate, kde_ui_inspect, kde_show_desktop).", " - Prassi di verifica: 1) kde_screenshot per catturare lo stato; 2) analizza l'immagine (agy_analyze se serve visione); 3) azione (kde_click/kde_type/kde_window_activate); 4) kde_screenshot di verifica e confronta.", " - ydotool usa la mappa tastiera US: con layout IT i caratteri speciali (trattini, @, ecc.) vanno specificati con keycode o attenzione.", " - Su Wayland non esiste iniezione input nativa: ydotool (via /dev/uinput) è il metodo; il daemon si avvia da solo (sudoers NOPASSWD configurato).", " - Per finestre: kde_window_activate con resourceClass (es. org.kde.konsole, org.kde.dolphin).", + " - Per Mostra Desktop usa kde_show_desktop: show/hide sono deterministici, toggle invoca KGlobalAccel; evitare xdotool/ydotool per questa azione.", " - Dettagli completi: skill /skill:kde-verify e memoria qmem (project host-enne2-gk35).", ]; return { @@ -142,7 +200,8 @@ export default function kdeAutomation(pi: ExtensionAPI) { "2. Analizza l'immagine (read o agy_analyze per visione)", "3. Azione: kde_window_activate (resourceClass) / kde_click (x,y,button) / kde_type (text)", "4. kde_screenshot di verifica e confronta con lo stato atteso", - "Nota: ydotool usa mappa US (layout IT: caratteri speciali diversi).", + "5. Per nascondere/ripristinare tutte le finestre: kde_show_desktop (show|hide|toggle)", + "Nota: ydotool usa mappa US (layout IT: caratteri speciali diversi); per Show Desktop usare D-Bus.", "Daemon: si avvia da solo via sudoers NOPASSWD (ydotoold).", ].join("\n"); ctx.ui.notify(text, "info"); @@ -180,6 +239,22 @@ export default function kdeAutomation(pi: ExtensionAPI) { }, }); + // --- tool: kde_show_desktop --- + pi.registerTool({ + name: "kde_show_desktop", + label: "KDE Show Desktop", + description: + "Mostra o nasconde tutte le finestre con l'API D-Bus nativa di KWin su Plasma Wayland. action=show nasconde le finestre, hide le ripristina, toggle invoca l'azione globale KWin. Restituisce e verifica la proprietà showingDesktop.", + parameters: Type.Object({ + action: Type.Optional(Type.String({ description: "show | hide | toggle (default show)" })), + }), + async execute(_id, params) { + const action = params.action ?? "show"; + const result = await setShowDesktop(action); + return { content: [{ type: "text", text: result }], details: { action } }; + }, + }); + // --- tool: kde_click --- pi.registerTool({ name: "kde_click", diff --git a/skills/kde-verify/SKILL.md b/skills/kde-verify/SKILL.md index 313b8c0..2098f33 100644 --- a/skills/kde-verify/SKILL.md +++ b/skills/kde-verify/SKILL.md @@ -12,9 +12,10 @@ Prassi operativa per verificare e automatizzare applicazioni a schermo su KDE Pl ## Ciclo di verifica 1. **Cattura**: `kde_screenshot` (mode=fullscreen|active|window|region, output=/tmp/x.png) -2. **Analizza**: leggi l'immagine con `read` (se il modello supporta immagini) o `agy_analyze` (visione Gemini) per descrivere lo stato +2. **Analizza**: leggi direttamente l'immagine con il tool `read` (invio nativo come allegato multimodale) per descrivere lo stato a schermo 3. **Azione**: `kde_window_activate` (resourceClass) → `kde_click` (x,y,button) → `kde_type` (text) 4. **Verifica**: nuovo `kde_screenshot` e confronta con lo stato atteso +5. **Mostra Desktop**: usa `kde_show_desktop` con `action=show|hide|toggle` per nascondere o ripristinare le finestre senza simulare input ## Strumenti @@ -26,6 +27,7 @@ Prassi operativa per verificare e automatizzare applicazioni a schermo su KDE Pl | `kde_mousemove` | ydotool mousemove (x, y) | | `kde_window_activate` | KWin scripting: attiva finestra per resourceClass | | `kde_ui_inspect` | AT-SPI: elenca applicazioni e controlli | +| `kde_show_desktop` | KWin D-Bus: mostra/nasconde tutte le finestre su Wayland | ## Comandi diretti (se i tool non bastano) @@ -46,6 +48,14 @@ qdbus6 org.kde.KWin /KWin org.kde.KWin.queryWindowInfo # info finestra attiva qdbus6 org.kde.KWin /Scripting org.kde.kwin.Scripting.loadScript /tmp/script.js qdbus6 org.kde.KWin /Scripting org.kde.kwin.Scripting.start +# Mostra Desktop (stato esplicito, robusto su Plasma Wayland) +dbus-send --session --dest=org.kde.KWin --type=method_call /KWin org.kde.KWin.showDesktop boolean:true +dbus-send --session --dest=org.kde.KWin --type=method_call /KWin org.kde.KWin.showDesktop boolean:false +# Toggle via azione globale KWin +qdbus6 org.kde.kglobalaccel /component/kwin org.kde.kglobalaccel.Component.invokeShortcut "Show Desktop" +# Verifica stato +qdbus6 org.kde.KWin /KWin org.freedesktop.DBus.Properties.Get org.kde.KWin showingDesktop + # Clipboard (workaround per inserire testo) qdbus6 org.kde.klipper /klipper org.kde.klipper.klipper.setClipboardContents "testo" ``` @@ -56,4 +66,5 @@ qdbus6 org.kde.klipper /klipper org.kde.klipper.klipper.setClipboardContents "te - **Wayland non ha iniezione input nativa**: ydotool (via /dev/uinput) è il metodo; il daemon si avvia da solo (sudoers NOPASSWD per ydotoold configurato). - **resourceClass comuni**: org.kde.konsole, org.kde.dolphin, org.kde.plasma-systemmonitor, firefox, org.kde.discover. - **AT-SPI**: il bus è su unix:path=/run/user/1000/at-spi/bus_1; per uso avanzato serve pyatspi (non installato). +- **Mostra Desktop**: `org.kde.KWin.showDesktop(bool)` è esposto su `/KWin`; su questa macchina usare `dbus-send` con `boolean:true|false` perché `qdbus6` non tipizza sempre correttamente il booleano. Per toggle usare KGlobalAccel `invokeShortcut "Show Desktop"`. - **Dettagli e lezioni**: memoria qmem project `host-enne2-gk35` (record automazione KDE).