xref: /aosp_15_r20/external/crosvm/vm_control/src/gpu.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::collections::BTreeMap as Map;
6 use std::fmt;
7 use std::fmt::Display;
8 use std::path::Path;
9 
10 use serde::Deserialize;
11 use serde::Serialize;
12 use serde_keyvalue::FromKeyValues;
13 
14 pub use crate::sys::handle_request;
15 pub use crate::sys::DisplayMode;
16 pub use crate::sys::MouseMode;
17 pub use crate::*;
18 
19 pub const DEFAULT_DISPLAY_WIDTH: u32 = 1280;
20 pub const DEFAULT_DISPLAY_HEIGHT: u32 = 1024;
21 pub const DEFAULT_DPI: u32 = 320;
22 pub const DEFAULT_REFRESH_RATE: u32 = 60;
23 
default_refresh_rate() -> u3224 fn default_refresh_rate() -> u32 {
25     DEFAULT_REFRESH_RATE
26 }
27 
28 /// Trait that the platform-specific type `DisplayMode` needs to implement.
29 pub(crate) trait DisplayModeTrait {
30     /// Returns the initial host window size.
get_window_size(&self) -> (u32, u32)31     fn get_window_size(&self) -> (u32, u32);
32 
33     /// Returns the virtual display size used for creating the display device.
34     ///
35     /// We need to query the phenotype flags to see if resolutions higher than 1080p should be
36     /// enabled. This functions assumes process invariants have been set up and phenotype flags are
37     /// available. If not, use `get_virtual_display_size_4k_uhd()` instead.
38     ///
39     /// This may be different from the initial host window size since different display backends may
40     /// have different alignment requirements on it.
get_virtual_display_size(&self) -> (u32, u32)41     fn get_virtual_display_size(&self) -> (u32, u32);
42 
43     /// Returns the virtual display size used for creating the display device.
44     ///
45     /// While `get_virtual_display_size()` reads phenotype flags internally, this function does not,
46     /// so it can be used when process invariants and phenotype flags are not yet ready.
get_virtual_display_size_4k_uhd(&self, is_4k_uhd_enabled: bool) -> (u32, u32)47     fn get_virtual_display_size_4k_uhd(&self, is_4k_uhd_enabled: bool) -> (u32, u32);
48 }
49 
50 impl Default for DisplayMode {
default() -> Self51     fn default() -> Self {
52         Self::Windowed(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT)
53     }
54 }
55 
56 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromKeyValues)]
57 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
58 pub struct DisplayParameters {
59     #[serde(default)]
60     pub mode: DisplayMode,
61     #[serde(default)]
62     pub hidden: bool,
63     #[serde(default = "default_refresh_rate")]
64     pub refresh_rate: u32,
65     // TODO(b/260101753): `dpi` has to be an `Option` for supporting CLI backward compatibility.
66     // That should be changed once compat fields below are deprecated.
67     pub dpi: Option<(u32, u32)>,
68     // `horizontal-dpi` and `vertical-dpi` are supported for CLI backward compatibility.
69     #[serde(rename = "horizontal-dpi")]
70     pub __horizontal_dpi_compat: Option<u32>,
71     #[serde(rename = "vertical-dpi")]
72     pub __vertical_dpi_compat: Option<u32>,
73 }
74 
75 impl DisplayParameters {
new( mode: DisplayMode, hidden: bool, refresh_rate: u32, horizontal_dpi: u32, vertical_dpi: u32, ) -> Self76     pub fn new(
77         mode: DisplayMode,
78         hidden: bool,
79         refresh_rate: u32,
80         horizontal_dpi: u32,
81         vertical_dpi: u32,
82     ) -> Self {
83         Self {
84             mode,
85             hidden,
86             refresh_rate,
87             dpi: Some((horizontal_dpi, vertical_dpi)),
88             __horizontal_dpi_compat: None,
89             __vertical_dpi_compat: None,
90         }
91     }
92 
default_with_mode(mode: DisplayMode) -> Self93     pub fn default_with_mode(mode: DisplayMode) -> Self {
94         Self::new(mode, false, DEFAULT_REFRESH_RATE, DEFAULT_DPI, DEFAULT_DPI)
95     }
96 
get_window_size(&self) -> (u32, u32)97     pub fn get_window_size(&self) -> (u32, u32) {
98         self.mode.get_window_size()
99     }
100 
get_virtual_display_size(&self) -> (u32, u32)101     pub fn get_virtual_display_size(&self) -> (u32, u32) {
102         self.mode.get_virtual_display_size()
103     }
104 
get_virtual_display_size_4k_uhd(&self, is_4k_uhd_enabled: bool) -> (u32, u32)105     pub fn get_virtual_display_size_4k_uhd(&self, is_4k_uhd_enabled: bool) -> (u32, u32) {
106         self.mode.get_virtual_display_size_4k_uhd(is_4k_uhd_enabled)
107     }
108 
horizontal_dpi(&self) -> u32109     pub fn horizontal_dpi(&self) -> u32 {
110         self.dpi.expect("'dpi' is None").0
111     }
112 
vertical_dpi(&self) -> u32113     pub fn vertical_dpi(&self) -> u32 {
114         self.dpi.expect("'dpi' is None").1
115     }
116 }
117 
118 impl Default for DisplayParameters {
default() -> Self119     fn default() -> Self {
120         Self::default_with_mode(Default::default())
121     }
122 }
123 
124 #[derive(Serialize, Deserialize, Debug)]
125 pub enum GpuControlCommand {
126     AddDisplays {
127         displays: Vec<DisplayParameters>,
128     },
129     ListDisplays,
130     RemoveDisplays {
131         display_ids: Vec<u32>,
132     },
133     SetDisplayMouseMode {
134         display_id: u32,
135         mouse_mode: MouseMode,
136     },
137 }
138 
139 #[derive(Serialize, Deserialize, Debug, Clone)]
140 pub enum GpuControlResult {
141     DisplaysUpdated,
142     DisplayList {
143         displays: Map<u32, DisplayParameters>,
144     },
145     TooManyDisplays {
146         allowed: usize,
147         requested: usize,
148     },
149     NoSuchDisplay {
150         display_id: u32,
151     },
152     DisplayMouseModeSet,
153     ErrString(String),
154 }
155 
156 impl Display for GpuControlResult {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result157     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158         use self::GpuControlResult::*;
159 
160         match self {
161             DisplaysUpdated => write!(f, "displays updated"),
162             DisplayList { displays } => {
163                 let json: serde_json::Value = serde_json::json!({
164                     "displays": displays,
165                 });
166                 let json_pretty =
167                     serde_json::to_string_pretty(&json).map_err(|_| std::fmt::Error)?;
168                 write!(f, "{}", json_pretty)
169             }
170             TooManyDisplays { allowed, requested } => write!(
171                 f,
172                 "too_many_displays: allowed {}, requested {}",
173                 allowed, requested
174             ),
175             NoSuchDisplay { display_id } => write!(f, "no_such_display {}", display_id),
176             DisplayMouseModeSet => write!(f, "display_mouse_mode_set"),
177             ErrString(reason) => write!(f, "err_string {}", reason),
178         }
179     }
180 }
181 
182 pub enum ModifyGpuError {
183     SocketFailed,
184     UnexpectedResponse(VmResponse),
185     UnknownCommand(String),
186     GpuControl(GpuControlResult),
187 }
188 
189 impl fmt::Display for ModifyGpuError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result190     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191         use self::ModifyGpuError::*;
192 
193         match self {
194             SocketFailed => write!(f, "socket failed"),
195             UnexpectedResponse(r) => write!(f, "unexpected response: {}", r),
196             UnknownCommand(c) => write!(f, "unknown display command: `{}`", c),
197             GpuControl(e) => write!(f, "{}", e),
198         }
199     }
200 }
201 
202 pub type ModifyGpuResult = std::result::Result<GpuControlResult, ModifyGpuError>;
203 
204 impl From<VmResponse> for ModifyGpuResult {
from(response: VmResponse) -> Self205     fn from(response: VmResponse) -> Self {
206         match response {
207             VmResponse::GpuResponse(gpu_response) => Ok(gpu_response),
208             r => Err(ModifyGpuError::UnexpectedResponse(r)),
209         }
210     }
211 }
212 
do_gpu_display_add<T: AsRef<Path> + std::fmt::Debug>( control_socket_path: T, displays: Vec<DisplayParameters>, ) -> ModifyGpuResult213 pub fn do_gpu_display_add<T: AsRef<Path> + std::fmt::Debug>(
214     control_socket_path: T,
215     displays: Vec<DisplayParameters>,
216 ) -> ModifyGpuResult {
217     let request = VmRequest::GpuCommand(GpuControlCommand::AddDisplays { displays });
218     handle_request(&request, control_socket_path)
219         .map_err(|_| ModifyGpuError::SocketFailed)?
220         .into()
221 }
222 
do_gpu_display_list<T: AsRef<Path> + std::fmt::Debug>( control_socket_path: T, ) -> ModifyGpuResult223 pub fn do_gpu_display_list<T: AsRef<Path> + std::fmt::Debug>(
224     control_socket_path: T,
225 ) -> ModifyGpuResult {
226     let request = VmRequest::GpuCommand(GpuControlCommand::ListDisplays);
227     handle_request(&request, control_socket_path)
228         .map_err(|_| ModifyGpuError::SocketFailed)?
229         .into()
230 }
231 
do_gpu_display_remove<T: AsRef<Path> + std::fmt::Debug>( control_socket_path: T, display_ids: Vec<u32>, ) -> ModifyGpuResult232 pub fn do_gpu_display_remove<T: AsRef<Path> + std::fmt::Debug>(
233     control_socket_path: T,
234     display_ids: Vec<u32>,
235 ) -> ModifyGpuResult {
236     let request = VmRequest::GpuCommand(GpuControlCommand::RemoveDisplays { display_ids });
237     handle_request(&request, control_socket_path)
238         .map_err(|_| ModifyGpuError::SocketFailed)?
239         .into()
240 }
241 
do_gpu_set_display_mouse_mode<T: AsRef<Path> + std::fmt::Debug>( control_socket_path: T, display_id: u32, mouse_mode: MouseMode, ) -> ModifyGpuResult242 pub fn do_gpu_set_display_mouse_mode<T: AsRef<Path> + std::fmt::Debug>(
243     control_socket_path: T,
244     display_id: u32,
245     mouse_mode: MouseMode,
246 ) -> ModifyGpuResult {
247     let request = VmRequest::GpuCommand(GpuControlCommand::SetDisplayMouseMode {
248         display_id,
249         mouse_mode,
250     });
251     handle_request(&request, control_socket_path)
252         .map_err(|_| ModifyGpuError::SocketFailed)?
253         .into()
254 }
255