1use std::collections::BTreeMap as Map;
6use std::fmt;
7use std::fmt::Display;
8use std::path::Path;
9
10use serde::Deserialize;
11use serde::Serialize;
12use serde_keyvalue::FromKeyValues;
13
14pub use crate::sys::handle_request;
15pub use crate::sys::DisplayMode;
16pub use crate::sys::MouseMode;
17pub use crate::*;
18
19pub const DEFAULT_DISPLAY_WIDTH: u32 = 1280;
20pub const DEFAULT_DISPLAY_HEIGHT: u32 = 1024;
21pub const DEFAULT_DPI: u32 = 320;
22pub const DEFAULT_REFRESH_RATE: u32 = 60;
23
24fn default_refresh_rate() -> u32 {
25 DEFAULT_REFRESH_RATE
26}
27
28fn default_dpi() -> (u32, u32) {
29 (DEFAULT_DPI, DEFAULT_DPI)
30}
31
32pub(crate) trait DisplayModeTrait {
34 fn get_window_size(&self) -> (u32, u32);
36
37 fn get_virtual_display_size(&self) -> (u32, u32);
46
47 fn get_virtual_display_size_4k_uhd(&self, is_4k_uhd_enabled: bool) -> (u32, u32);
52}
53
54impl Default for DisplayMode {
55 fn default() -> Self {
56 Self::Windowed(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT)
57 }
58}
59
60#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromKeyValues)]
61#[serde(deny_unknown_fields, rename_all = "kebab-case")]
62pub struct DisplayParameters {
63 #[serde(default)]
64 pub mode: DisplayMode,
65 #[serde(default)]
66 pub hidden: bool,
67 #[serde(default = "default_refresh_rate")]
68 pub refresh_rate: u32,
69 #[serde(default = "default_dpi")]
70 pub dpi: (u32, u32),
71}
72
73impl DisplayParameters {
74 pub fn new(
75 mode: DisplayMode,
76 hidden: bool,
77 refresh_rate: u32,
78 horizontal_dpi: u32,
79 vertical_dpi: u32,
80 ) -> Self {
81 Self {
82 mode,
83 hidden,
84 refresh_rate,
85 dpi: (horizontal_dpi, vertical_dpi),
86 }
87 }
88
89 pub fn default_with_mode(mode: DisplayMode) -> Self {
90 Self::new(mode, false, DEFAULT_REFRESH_RATE, DEFAULT_DPI, DEFAULT_DPI)
91 }
92
93 pub fn get_window_size(&self) -> (u32, u32) {
94 self.mode.get_window_size()
95 }
96
97 pub fn get_virtual_display_size(&self) -> (u32, u32) {
98 self.mode.get_virtual_display_size()
99 }
100
101 pub fn get_virtual_display_size_4k_uhd(&self, is_4k_uhd_enabled: bool) -> (u32, u32) {
102 self.mode.get_virtual_display_size_4k_uhd(is_4k_uhd_enabled)
103 }
104
105 pub fn horizontal_dpi(&self) -> u32 {
106 self.dpi.0
107 }
108
109 pub fn vertical_dpi(&self) -> u32 {
110 self.dpi.1
111 }
112}
113
114impl Default for DisplayParameters {
115 fn default() -> Self {
116 Self::default_with_mode(Default::default())
117 }
118}
119
120#[derive(Serialize, Deserialize, Debug)]
121pub enum GpuControlCommand {
122 AddDisplays {
123 displays: Vec<DisplayParameters>,
124 },
125 ListDisplays,
126 RemoveDisplays {
127 display_ids: Vec<u32>,
128 },
129 SetDisplayMouseMode {
130 display_id: u32,
131 mouse_mode: MouseMode,
132 },
133}
134
135#[derive(Serialize, Deserialize, Debug, Clone)]
136pub enum GpuControlResult {
137 DisplaysUpdated,
138 DisplayList {
139 displays: Map<u32, DisplayParameters>,
140 },
141 TooManyDisplays {
142 allowed: usize,
143 requested: usize,
144 },
145 NoSuchDisplay {
146 display_id: u32,
147 },
148 DisplayMouseModeSet,
149 ErrString(String),
150}
151
152impl Display for GpuControlResult {
153 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154 use self::GpuControlResult::*;
155
156 match self {
157 DisplaysUpdated => write!(f, "displays updated"),
158 DisplayList { displays } => {
159 let json: serde_json::Value = serde_json::json!({
160 "displays": displays,
161 });
162 let json_pretty =
163 serde_json::to_string_pretty(&json).map_err(|_| std::fmt::Error)?;
164 write!(f, "{json_pretty}")
165 }
166 TooManyDisplays { allowed, requested } => write!(
167 f,
168 "too_many_displays: allowed {allowed}, requested {requested}"
169 ),
170 NoSuchDisplay { display_id } => write!(f, "no_such_display {display_id}"),
171 DisplayMouseModeSet => write!(f, "display_mouse_mode_set"),
172 ErrString(reason) => write!(f, "err_string {reason}"),
173 }
174 }
175}
176
177pub enum ModifyGpuError {
178 SocketFailed,
179 UnexpectedResponse(VmResponse),
180 UnknownCommand(String),
181 GpuControl(GpuControlResult),
182}
183
184impl fmt::Display for ModifyGpuError {
185 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186 use self::ModifyGpuError::*;
187
188 match self {
189 SocketFailed => write!(f, "socket failed"),
190 UnexpectedResponse(r) => write!(f, "unexpected response: {r}"),
191 UnknownCommand(c) => write!(f, "unknown display command: `{c}`"),
192 GpuControl(e) => write!(f, "{e}"),
193 }
194 }
195}
196
197pub type ModifyGpuResult = std::result::Result<GpuControlResult, ModifyGpuError>;
198
199impl From<VmResponse> for ModifyGpuResult {
200 fn from(response: VmResponse) -> Self {
201 match response {
202 VmResponse::GpuResponse(gpu_response) => Ok(gpu_response),
203 r => Err(ModifyGpuError::UnexpectedResponse(r)),
204 }
205 }
206}
207
208pub fn do_gpu_display_add<T: AsRef<Path> + std::fmt::Debug>(
209 control_socket_path: T,
210 displays: Vec<DisplayParameters>,
211) -> ModifyGpuResult {
212 let request = VmRequest::GpuCommand(GpuControlCommand::AddDisplays { displays });
213 handle_request(&request, control_socket_path)
214 .map_err(|_| ModifyGpuError::SocketFailed)?
215 .into()
216}
217
218pub fn do_gpu_display_list<T: AsRef<Path> + std::fmt::Debug>(
219 control_socket_path: T,
220) -> ModifyGpuResult {
221 let request = VmRequest::GpuCommand(GpuControlCommand::ListDisplays);
222 handle_request(&request, control_socket_path)
223 .map_err(|_| ModifyGpuError::SocketFailed)?
224 .into()
225}
226
227pub fn do_gpu_display_remove<T: AsRef<Path> + std::fmt::Debug>(
228 control_socket_path: T,
229 display_ids: Vec<u32>,
230) -> ModifyGpuResult {
231 let request = VmRequest::GpuCommand(GpuControlCommand::RemoveDisplays { display_ids });
232 handle_request(&request, control_socket_path)
233 .map_err(|_| ModifyGpuError::SocketFailed)?
234 .into()
235}
236
237pub fn do_gpu_set_display_mouse_mode<T: AsRef<Path> + std::fmt::Debug>(
238 control_socket_path: T,
239 display_id: u32,
240 mouse_mode: MouseMode,
241) -> ModifyGpuResult {
242 let request = VmRequest::GpuCommand(GpuControlCommand::SetDisplayMouseMode {
243 display_id,
244 mouse_mode,
245 });
246 handle_request(&request, control_socket_path)
247 .map_err(|_| ModifyGpuError::SocketFailed)?
248 .into()
249}