xref: /aosp_15_r20/external/crosvm/vm_control/src/sys/windows/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::marker::PhantomData;
6 
7 use base::info;
8 use serde::Deserialize;
9 use serde::Serialize;
10 use serde_keyvalue::FromKeyValues;
11 use winapi::um::winuser::GetSystemMetrics;
12 use winapi::um::winuser::SM_CXSCREEN;
13 use winapi::um::winuser::SM_CYSCREEN;
14 
15 use crate::gpu::DisplayModeTrait;
16 
17 const DISPLAY_WIDTH_SOFT_MAX: u32 = 1920;
18 const DISPLAY_HEIGHT_SOFT_MAX: u32 = 1080;
19 
20 const DISPLAY_WIDTH_SOFT_MAX_4K_UHD_ENABLED: u32 = 3840;
21 const DISPLAY_HEIGHT_SOFT_MAX_4K_UHD_ENABLED: u32 = 2160;
22 
23 #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, FromKeyValues)]
24 #[serde(rename_all = "snake_case")]
25 pub enum WinMouseMode {
26     /// Sends relative motion & mouse button events to the guest (captured only).
27     Relative,
28     /// Sends multi-touch events to the guest.
29     Touchscreen,
30 }
31 
32 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
33 #[serde(rename_all = "snake_case")]
34 pub enum WinDisplayMode<T> {
35     Windowed(u32, u32),
36     BorderlessFullScreen(#[serde(skip)] PhantomData<T>),
37 }
38 
39 impl<T: ProvideDisplayData> DisplayModeTrait for WinDisplayMode<T> {
get_window_size(&self) -> (u32, u32)40     fn get_window_size(&self) -> (u32, u32) {
41         match self {
42             Self::Windowed(width, height) => (*width, *height),
43             Self::BorderlessFullScreen(_) => T::get_host_display_size(),
44         }
45     }
46 
get_virtual_display_size(&self) -> (u32, u32)47     fn get_virtual_display_size(&self) -> (u32, u32) {
48         self.get_virtual_display_size_4k_uhd(vm_control_product::is_4k_uhd_enabled())
49     }
50 
get_virtual_display_size_4k_uhd(&self, is_4k_uhd_enabled: bool) -> (u32, u32)51     fn get_virtual_display_size_4k_uhd(&self, is_4k_uhd_enabled: bool) -> (u32, u32) {
52         let (width, height) = self.get_window_size();
53         let (width, height) = adjust_virtual_display_size(width, height, is_4k_uhd_enabled);
54         info!("Guest display size: {}x{}", width, height);
55         (width, height)
56     }
57 }
58 
59 /// Trait for returning host display data such as resolution. Tests may overwrite this to specify
60 /// display data rather than rely on properties of the actual display device.
61 trait ProvideDisplayData {
get_host_display_size() -> (u32, u32)62     fn get_host_display_size() -> (u32, u32);
63 }
64 
65 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
66 pub struct DisplayDataProvider;
67 
68 impl ProvideDisplayData for DisplayDataProvider {
get_host_display_size() -> (u32, u32)69     fn get_host_display_size() -> (u32, u32) {
70         // SAFETY:
71         // Safe because we're passing valid values and screen size won't exceed u32 range.
72         let (width, height) = unsafe {
73             (
74                 GetSystemMetrics(SM_CXSCREEN) as u32,
75                 GetSystemMetrics(SM_CYSCREEN) as u32,
76             )
77         };
78         // Note: This is the size of the host's display. The guest display size given by
79         // (width, height) may be smaller if we are letterboxing.
80         info!("Host display size: {}x{}", width, height);
81         (width, height)
82     }
83 }
84 
adjust_virtual_display_size(width: u32, height: u32, is_4k_uhd_enabled: bool) -> (u32, u32)85 fn adjust_virtual_display_size(width: u32, height: u32, is_4k_uhd_enabled: bool) -> (u32, u32) {
86     let (max_width, max_height) = if is_4k_uhd_enabled {
87         (
88             DISPLAY_WIDTH_SOFT_MAX_4K_UHD_ENABLED,
89             DISPLAY_HEIGHT_SOFT_MAX_4K_UHD_ENABLED,
90         )
91     } else {
92         (DISPLAY_WIDTH_SOFT_MAX, DISPLAY_HEIGHT_SOFT_MAX)
93     };
94     let width = std::cmp::min(width, max_width);
95     let height = std::cmp::min(height, max_height);
96     // Widths that aren't a multiple of 8 break gfxstream: b/156110663.
97     let width = width - (width % 8);
98     (width, height)
99 }
100 
101 #[cfg(test)]
102 mod tests {
103     use super::*;
104 
105     #[test]
borderless_full_screen_virtual_window_width_should_be_multiple_of_8()106     fn borderless_full_screen_virtual_window_width_should_be_multiple_of_8() {
107         struct MockDisplayDataProvider;
108 
109         impl ProvideDisplayData for MockDisplayDataProvider {
110             fn get_host_display_size() -> (u32, u32) {
111                 (1366, 768)
112             }
113         }
114 
115         let mode = WinDisplayMode::<MockDisplayDataProvider>::BorderlessFullScreen(PhantomData);
116         let (width, _) = mode.get_virtual_display_size_4k_uhd(/* is_4k_uhd_enabled */ false);
117         assert_eq!(width % 8, 0);
118     }
119 
120     #[test]
borderless_full_screen_virtual_window_size_should_be_smaller_than_soft_max()121     fn borderless_full_screen_virtual_window_size_should_be_smaller_than_soft_max() {
122         struct MockDisplayDataProvider;
123 
124         impl ProvideDisplayData for MockDisplayDataProvider {
125             fn get_host_display_size() -> (u32, u32) {
126                 (DISPLAY_WIDTH_SOFT_MAX + 1, DISPLAY_HEIGHT_SOFT_MAX + 1)
127             }
128         }
129 
130         let mode = WinDisplayMode::<MockDisplayDataProvider>::BorderlessFullScreen(PhantomData);
131         let (width, height) =
132             mode.get_virtual_display_size_4k_uhd(/* is_4k_uhd_enabled */ false);
133         assert!(width <= DISPLAY_WIDTH_SOFT_MAX);
134         assert!(height <= DISPLAY_HEIGHT_SOFT_MAX);
135     }
136 }
137