Skip to main content

qemu_command_builder/args/
display.rs

1use crate::parsers::ARG_DISPLAY;
2use std::path::PathBuf;
3use std::str::FromStr;
4
5use proptest_derive::Arbitrary;
6
7use crate::common::{OnOff, YesNo};
8use crate::to_command::{ToArg, ToCommand};
9
10/// QEMU `-display` backend selection.
11///
12/// Each variant models one documented `-display` form from the bundled QEMU
13/// option reference. `to_args()` emits raw `argv` values, while `FromStr`
14/// accepts the single argument that follows `-display`.
15#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
16pub enum OnCoreEsOff {
17    On,
18    Core,
19    Es,
20    Off,
21}
22
23impl ToArg for OnCoreEsOff {
24    fn to_arg(&self) -> &str {
25        match self {
26            OnCoreEsOff::On => "on",
27            OnCoreEsOff::Core => "core",
28            OnCoreEsOff::Es => "es",
29            OnCoreEsOff::Off => "off",
30        }
31    }
32}
33
34#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
35pub enum QemuDisplay {
36    Spice {
37        gl: Option<OnOff>,
38    },
39    Sdl {
40        gl: Option<OnCoreEsOff>,
41        grab_mod: Option<String>,
42        show_cursor: Option<OnOff>,
43        window_close: Option<OnOff>,
44    },
45    Gtk {
46        fullscreen: Option<OnOff>,
47        gl: Option<OnOff>,
48        grab_on_hover: Option<OnOff>,
49        show_tabs: Option<OnOff>,
50        show_cursor: Option<OnOff>,
51        window_close: Option<OnOff>,
52        show_menubar: Option<OnOff>,
53        zoom_to_fit: Option<OnOff>,
54    },
55    Vnc {
56        vnc: String,
57        optargs: Option<String>,
58    },
59    Curses {
60        charset: Option<String>,
61    },
62    Cocoa {
63        full_grab: Option<OnOff>,
64        swap_opt_cmd: Option<OnOff>,
65        show_cursor: Option<OnOff>,
66        left_command_key: Option<OnOff>,
67        full_screen: Option<OnOff>,
68        zoom_to_fit: Option<OnOff>,
69    },
70    EglHeadless {
71        rendernode: Option<PathBuf>,
72    },
73    Dbus {
74        addr: Option<String>,
75        p2p: Option<YesNo>,
76        gl: Option<OnCoreEsOff>,
77        rendernode: Option<PathBuf>,
78    },
79    None,
80}
81
82impl ToCommand for QemuDisplay {
83    fn command(&self) -> String {
84        ARG_DISPLAY.to_string()
85    }
86    fn to_args(&self) -> Vec<String> {
87        let mut args = vec![];
88        match self {
89            QemuDisplay::Spice { gl } => {
90                args.push("spice-app".to_string());
91                if let Some(gl) = gl {
92                    args.push(format!("gl={}", gl.to_arg()));
93                }
94            }
95            QemuDisplay::Sdl {
96                gl,
97                grab_mod,
98                show_cursor,
99                window_close,
100            } => {
101                args.push("sdl".to_string());
102                if let Some(gl) = gl {
103                    args.push(format!("gl={}", gl.to_arg()));
104                }
105                if let Some(grab_mod) = grab_mod {
106                    args.push(format!("grab-mod={}", grab_mod));
107                }
108                if let Some(show_cursor) = show_cursor {
109                    args.push(format!("show-cursor={}", show_cursor.to_arg()));
110                }
111                if let Some(window_close) = window_close {
112                    args.push(format!("window-close={}", window_close.to_arg()));
113                }
114            }
115            QemuDisplay::Gtk {
116                fullscreen,
117                gl,
118                grab_on_hover,
119                show_tabs,
120                show_cursor,
121                window_close,
122                show_menubar,
123                zoom_to_fit,
124            } => {
125                args.push("gtk".to_string());
126                if let Some(fullscreen) = fullscreen {
127                    args.push(format!("full-screen={}", fullscreen.to_arg()));
128                }
129                if let Some(gl) = gl {
130                    args.push(format!("gl={}", gl.to_arg()));
131                }
132                if let Some(grab_on_hover) = grab_on_hover {
133                    args.push(format!("grab-on-hover={}", grab_on_hover.to_arg()));
134                }
135                if let Some(show_tabs) = show_tabs {
136                    args.push(format!("show-tabs={}", show_tabs.to_arg()));
137                }
138                if let Some(show_cursor) = show_cursor {
139                    args.push(format!("show-cursor={}", show_cursor.to_arg()));
140                }
141                if let Some(window_close) = window_close {
142                    args.push(format!("window-close={}", window_close.to_arg()));
143                }
144                if let Some(show_menubar) = show_menubar {
145                    args.push(format!("show-menubar={}", show_menubar.to_arg()));
146                }
147                if let Some(zoom_to_fit) = zoom_to_fit {
148                    args.push(format!("zoom-to-fit={}", zoom_to_fit.to_arg()));
149                }
150            }
151            QemuDisplay::Vnc { vnc, optargs } => {
152                args.push(format!("vnc={}", vnc.clone()));
153                if let Some(optargs) = optargs {
154                    args.push(optargs.clone());
155                }
156            }
157            QemuDisplay::Curses { charset } => {
158                args.push("curses".to_string());
159                if let Some(charset) = charset {
160                    args.push(format!("charset={}", charset));
161                }
162            }
163            QemuDisplay::Cocoa {
164                full_grab,
165                swap_opt_cmd,
166                show_cursor,
167                left_command_key,
168                full_screen,
169                zoom_to_fit,
170            } => {
171                args.push("cocoa".to_string());
172                if let Some(full_grab) = full_grab {
173                    args.push(format!("full-grab={}", full_grab.to_arg()));
174                }
175                if let Some(swap_opt_cmd) = swap_opt_cmd {
176                    args.push(format!("swap-opt-cmd={}", swap_opt_cmd.to_arg()));
177                }
178                if let Some(show_cursor) = show_cursor {
179                    args.push(format!("show-cursor={}", show_cursor.to_arg()));
180                }
181                if let Some(left_command_key) = left_command_key {
182                    args.push(format!("left-command-key={}", left_command_key.to_arg()));
183                }
184                if let Some(full_screen) = full_screen {
185                    args.push(format!("full-screen={}", full_screen.to_arg()));
186                }
187                if let Some(zoom_to_fit) = zoom_to_fit {
188                    args.push(format!("zoom-to-fit={}", zoom_to_fit.to_arg()));
189                }
190            }
191            QemuDisplay::EglHeadless { rendernode } => {
192                args.push("egl-headless".to_string());
193                if let Some(rendernode) = rendernode {
194                    args.push(format!("rendernode={}", rendernode.display()));
195                }
196            }
197            QemuDisplay::Dbus { addr, p2p, gl, rendernode } => {
198                args.push("dbus".to_string());
199                if let Some(addr) = addr {
200                    args.push(format!("addr={}", addr));
201                }
202                if let Some(p2p) = p2p {
203                    args.push(format!("p2p={}", p2p.to_arg()));
204                }
205                if let Some(gl) = gl {
206                    args.push(format!("gl={}", gl.to_arg()));
207                }
208                if let Some(rendernode) = rendernode {
209                    args.push(format!("rendernode={}", rendernode.display()));
210                }
211            }
212            QemuDisplay::None => {
213                args.push("none".to_string());
214            }
215        }
216
217        args
218    }
219}
220
221impl FromStr for QemuDisplay {
222    type Err = String;
223
224    fn from_str(s: &str) -> Result<Self, Self::Err> {
225        if s == "none" {
226            return Ok(Self::None);
227        }
228        if s == "spice-app" {
229            return Ok(Self::Spice { gl: None });
230        }
231        if s == "sdl" {
232            return Ok(Self::Sdl {
233                gl: None,
234                grab_mod: None,
235                show_cursor: None,
236                window_close: None,
237            });
238        }
239        if s == "gtk" {
240            return Ok(Self::Gtk {
241                fullscreen: None,
242                gl: None,
243                grab_on_hover: None,
244                show_tabs: None,
245                show_cursor: None,
246                window_close: None,
247                show_menubar: None,
248                zoom_to_fit: None,
249            });
250        }
251        if s == "curses" {
252            return Ok(Self::Curses { charset: None });
253        }
254        if s == "cocoa" {
255            return Ok(Self::Cocoa {
256                full_grab: None,
257                swap_opt_cmd: None,
258                show_cursor: None,
259                left_command_key: None,
260                full_screen: None,
261                zoom_to_fit: None,
262            });
263        }
264        if s == "egl-headless" {
265            return Ok(Self::EglHeadless { rendernode: None });
266        }
267        if s == "dbus" {
268            return Ok(Self::Dbus {
269                addr: None,
270                p2p: None,
271                gl: None,
272                rendernode: None,
273            });
274        }
275        if let Some(rest) = s.strip_prefix("vnc=") {
276            let (vnc, optargs) = match rest.split_once(',') {
277                Some((vnc, optargs)) => (vnc.to_string(), Some(optargs.to_string())),
278                None => (rest.to_string(), None),
279            };
280            return Ok(Self::Vnc { vnc, optargs });
281        }
282
283        Err(format!("unsupported -display value: {s}"))
284    }
285}