// Copyright 2022 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use std::collections::BTreeMap as Map; use std::fmt; use std::fmt::Display; use std::path::Path; use serde::Deserialize; use serde::Serialize; use serde_keyvalue::FromKeyValues; pub use crate::sys::handle_request; pub use crate::sys::DisplayMode; pub use crate::sys::MouseMode; pub use crate::*; pub const DEFAULT_DISPLAY_WIDTH: u32 = 1280; pub const DEFAULT_DISPLAY_HEIGHT: u32 = 1024; pub const DEFAULT_DPI: u32 = 320; pub const DEFAULT_REFRESH_RATE: u32 = 60; fn default_refresh_rate() -> u32 { DEFAULT_REFRESH_RATE } /// Trait that the platform-specific type `DisplayMode` needs to implement. pub(crate) trait DisplayModeTrait { /// Returns the initial host window size. fn get_window_size(&self) -> (u32, u32); /// Returns the virtual display size used for creating the display device. /// /// We need to query the phenotype flags to see if resolutions higher than 1080p should be /// enabled. This functions assumes process invariants have been set up and phenotype flags are /// available. If not, use `get_virtual_display_size_4k_uhd()` instead. /// /// This may be different from the initial host window size since different display backends may /// have different alignment requirements on it. fn get_virtual_display_size(&self) -> (u32, u32); /// Returns the virtual display size used for creating the display device. /// /// While `get_virtual_display_size()` reads phenotype flags internally, this function does not, /// so it can be used when process invariants and phenotype flags are not yet ready. fn get_virtual_display_size_4k_uhd(&self, is_4k_uhd_enabled: bool) -> (u32, u32); } impl Default for DisplayMode { fn default() -> Self { Self::Windowed(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT) } } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromKeyValues)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] pub struct DisplayParameters { #[serde(default)] pub mode: DisplayMode, #[serde(default)] pub hidden: bool, #[serde(default = "default_refresh_rate")] pub refresh_rate: u32, // TODO(b/260101753): `dpi` has to be an `Option` for supporting CLI backward compatibility. // That should be changed once compat fields below are deprecated. pub dpi: Option<(u32, u32)>, // `horizontal-dpi` and `vertical-dpi` are supported for CLI backward compatibility. #[serde(rename = "horizontal-dpi")] pub __horizontal_dpi_compat: Option, #[serde(rename = "vertical-dpi")] pub __vertical_dpi_compat: Option, } impl DisplayParameters { pub fn new( mode: DisplayMode, hidden: bool, refresh_rate: u32, horizontal_dpi: u32, vertical_dpi: u32, ) -> Self { Self { mode, hidden, refresh_rate, dpi: Some((horizontal_dpi, vertical_dpi)), __horizontal_dpi_compat: None, __vertical_dpi_compat: None, } } pub fn default_with_mode(mode: DisplayMode) -> Self { Self::new(mode, false, DEFAULT_REFRESH_RATE, DEFAULT_DPI, DEFAULT_DPI) } pub fn get_window_size(&self) -> (u32, u32) { self.mode.get_window_size() } pub fn get_virtual_display_size(&self) -> (u32, u32) { self.mode.get_virtual_display_size() } pub fn get_virtual_display_size_4k_uhd(&self, is_4k_uhd_enabled: bool) -> (u32, u32) { self.mode.get_virtual_display_size_4k_uhd(is_4k_uhd_enabled) } pub fn horizontal_dpi(&self) -> u32 { self.dpi.expect("'dpi' is None").0 } pub fn vertical_dpi(&self) -> u32 { self.dpi.expect("'dpi' is None").1 } } impl Default for DisplayParameters { fn default() -> Self { Self::default_with_mode(Default::default()) } } #[derive(Serialize, Deserialize, Debug)] pub enum GpuControlCommand { AddDisplays { displays: Vec, }, ListDisplays, RemoveDisplays { display_ids: Vec, }, SetDisplayMouseMode { display_id: u32, mouse_mode: MouseMode, }, } #[derive(Serialize, Deserialize, Debug, Clone)] pub enum GpuControlResult { DisplaysUpdated, DisplayList { displays: Map, }, TooManyDisplays { allowed: usize, requested: usize, }, NoSuchDisplay { display_id: u32, }, DisplayMouseModeSet, ErrString(String), } impl Display for GpuControlResult { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::GpuControlResult::*; match self { DisplaysUpdated => write!(f, "displays updated"), DisplayList { displays } => { let json: serde_json::Value = serde_json::json!({ "displays": displays, }); let json_pretty = serde_json::to_string_pretty(&json).map_err(|_| std::fmt::Error)?; write!(f, "{}", json_pretty) } TooManyDisplays { allowed, requested } => write!( f, "too_many_displays: allowed {}, requested {}", allowed, requested ), NoSuchDisplay { display_id } => write!(f, "no_such_display {}", display_id), DisplayMouseModeSet => write!(f, "display_mouse_mode_set"), ErrString(reason) => write!(f, "err_string {}", reason), } } } pub enum ModifyGpuError { SocketFailed, UnexpectedResponse(VmResponse), UnknownCommand(String), GpuControl(GpuControlResult), } impl fmt::Display for ModifyGpuError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::ModifyGpuError::*; match self { SocketFailed => write!(f, "socket failed"), UnexpectedResponse(r) => write!(f, "unexpected response: {}", r), UnknownCommand(c) => write!(f, "unknown display command: `{}`", c), GpuControl(e) => write!(f, "{}", e), } } } pub type ModifyGpuResult = std::result::Result; impl From for ModifyGpuResult { fn from(response: VmResponse) -> Self { match response { VmResponse::GpuResponse(gpu_response) => Ok(gpu_response), r => Err(ModifyGpuError::UnexpectedResponse(r)), } } } pub fn do_gpu_display_add + std::fmt::Debug>( control_socket_path: T, displays: Vec, ) -> ModifyGpuResult { let request = VmRequest::GpuCommand(GpuControlCommand::AddDisplays { displays }); handle_request(&request, control_socket_path) .map_err(|_| ModifyGpuError::SocketFailed)? .into() } pub fn do_gpu_display_list + std::fmt::Debug>( control_socket_path: T, ) -> ModifyGpuResult { let request = VmRequest::GpuCommand(GpuControlCommand::ListDisplays); handle_request(&request, control_socket_path) .map_err(|_| ModifyGpuError::SocketFailed)? .into() } pub fn do_gpu_display_remove + std::fmt::Debug>( control_socket_path: T, display_ids: Vec, ) -> ModifyGpuResult { let request = VmRequest::GpuCommand(GpuControlCommand::RemoveDisplays { display_ids }); handle_request(&request, control_socket_path) .map_err(|_| ModifyGpuError::SocketFailed)? .into() } pub fn do_gpu_set_display_mouse_mode + std::fmt::Debug>( control_socket_path: T, display_id: u32, mouse_mode: MouseMode, ) -> ModifyGpuResult { let request = VmRequest::GpuCommand(GpuControlCommand::SetDisplayMouseMode { display_id, mouse_mode, }); handle_request(&request, control_socket_path) .map_err(|_| ModifyGpuError::SocketFailed)? .into() }