qemu_command_builder/args/
display.rs1use 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#[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}