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