Skip to main content

virtuoso_cli/client/
window_ops.rs

1use crate::client::bridge::escape_skill_string;
2
3pub struct WindowOps;
4
5impl WindowOps {
6    /// List all open Virtuoso windows.
7    /// Returns a JSON array string: [{"name":"ADE Explorer Editing: ..."}]
8    pub fn list_windows(&self) -> String {
9        r#"let((out sep) out = "[" sep = "" foreach(w hiGetWindowList() out = strcat(out sep sprintf(nil "{\"name\":\"%s\"}" hiGetWindowName(w))) sep = ",") strcat(out "]"))"#.into()
10    }
11
12    /// Dismiss the current blocking dialog.
13    /// action: "cancel" closes via Cancel; "ok" attempts OK/Yes button.
14    pub fn dismiss_dialog(&self, action: &str) -> String {
15        if action == "ok" {
16            r#"let((d) d = hiGetCurrentDialog() if(d hiSendOK(d) "no-dialog"))"#.into()
17        } else {
18            r#"let((d) d = hiGetCurrentDialog() if(d hiCancelDialog(d) "no-dialog"))"#.into()
19        }
20    }
21
22    /// Get the name of the current dialog without dismissing it.
23    /// Returns "no-dialog" if no dialog is active.
24    pub fn get_dialog_info(&self) -> String {
25        r#"let((d) d = hiGetCurrentDialog() if(d hiGetWindowName(d) "no-dialog"))"#.into()
26    }
27
28    /// Capture a screenshot of the current Virtuoso window to a PNG file.
29    ///
30    /// IC23.1 does not have `hiGetWindowScreenDump`, so we use X11 `import`
31    /// (ImageMagick) via system().  The file path is verified with `isFile`
32    /// after the capture to distinguish success from failure.
33    pub fn screenshot(&self, path: &str) -> String {
34        let path = escape_skill_string(path);
35        Self::skill_capture(&path)
36    }
37
38    /// Capture a screenshot of the first window whose name matches a regex pattern.
39    /// Falls back to full-screen root capture (X11 import does not support per-window
40    /// targeting without xdotool).
41    pub fn screenshot_by_pattern(&self, path: &str, pattern: &str) -> String {
42        let path = escape_skill_string(path);
43        let pattern = escape_skill_string(pattern);
44        let capture = Self::skill_capture(&path);
45        format!(
46            r#"let((matched) matched = nil foreach(w hiGetWindowList() when(rexMatchp("{pattern}" hiGetWindowName(w)) matched = t)) if(matched {capture} "no-match"))"#
47        )
48    }
49
50    /// SKILL fragment: run X11 import and return path on success, nil on failure.
51    fn skill_capture(escaped_path: &str) -> String {
52        format!(
53            r#"let((cmd) cmd = sprintf(nil "import -window root -display %s {escaped_path}" getShellEnvVar("DISPLAY")) system(cmd) if(isFile("{escaped_path}") "{escaped_path}" nil))"#
54        )
55    }
56}