diff --git a/Cargo.lock b/Cargo.lock index 73ba546b71036d90364365ff3a0bbcfd5435af95..458f1a883517fea21a444b9822f16a3b464a0330 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8729,6 +8729,12 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.9.9" @@ -11034,6 +11040,7 @@ checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "getrandom 0.2.10", "serde", + "sha1_smol", ] [[package]] diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 02cb514d9e92782746942a41d4285698198cdd8d..2ac7c3d4a5327bb1c7b4364cac40557b79a4885b 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -70,7 +70,7 @@ time.workspace = true tiny-skia = "0.5" usvg = { version = "0.14", features = [] } util.workspace = true -uuid = { version = "1.1.2", features = ["v4"] } +uuid = { version = "1.1.2", features = ["v4", "v5"] } waker-fn = "1.1.0" [dev-dependencies] diff --git a/crates/gpui/src/platform/windows/display.rs b/crates/gpui/src/platform/windows/display.rs index 28d65c70a2e4dc798490b7183a258387f45e7a75..fe1553ca4f94a64b0c308fc4e3868438635d7f4d 100644 --- a/crates/gpui/src/platform/windows/display.rs +++ b/crates/gpui/src/platform/windows/display.rs @@ -1,48 +1,169 @@ -use anyhow::{anyhow, Result}; +use std::rc::Rc; + +use itertools::Itertools; +use smallvec::SmallVec; use uuid::Uuid; -use windows::{ - core::PCSTR, - Win32::Graphics::Gdi::{EnumDisplaySettingsA, DEVMODEA, ENUM_CURRENT_SETTINGS}, +use windows::Win32::{ + Foundation::{BOOL, LPARAM, POINT, RECT}, + Graphics::Gdi::{ + EnumDisplayMonitors, GetMonitorInfoW, MonitorFromPoint, HDC, HMONITOR, MONITORINFO, + MONITORINFOEXW, MONITOR_DEFAULTTOPRIMARY, + }, }; use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Point, Size}; #[derive(Debug)] -pub(crate) struct WindowsDisplay; +pub(crate) struct WindowsDisplay { + pub display_id: DisplayId, + bounds: Bounds, + uuid: Uuid, +} impl WindowsDisplay { - pub(crate) fn new() -> Self { - Self + pub(crate) fn new(display_id: DisplayId) -> Option { + let Some(screen) = available_monitors().into_iter().nth(display_id.0 as _) else { + return None; + }; + let Ok(info) = get_monitor_info(screen).inspect_err(|e| log::error!("{}", e)) else { + return None; + }; + let size = info.monitorInfo.rcMonitor; + let uuid = generate_uuid(&info.szDevice); + + Some(WindowsDisplay { + display_id, + bounds: Bounds { + origin: Point { + x: GlobalPixels(size.left as f32), + y: GlobalPixels(size.top as f32), + }, + size: Size { + width: GlobalPixels((size.right - size.left) as f32), + height: GlobalPixels((size.bottom - size.top) as f32), + }, + }, + uuid, + }) + } + + fn new_with_handle_and_id(handle: HMONITOR, display_id: DisplayId) -> Self { + let info = get_monitor_info(handle).expect("unable to get monitor info"); + let size = info.monitorInfo.rcMonitor; + let uuid = generate_uuid(&info.szDevice); + + WindowsDisplay { + display_id, + bounds: Bounds { + origin: Point { + x: GlobalPixels(size.left as f32), + y: GlobalPixels(size.top as f32), + }, + size: Size { + width: GlobalPixels((size.right - size.left) as f32), + height: GlobalPixels((size.bottom - size.top) as f32), + }, + }, + uuid, + } + } + + pub fn primary_monitor() -> Option { + // https://devblogs.microsoft.com/oldnewthing/20070809-00/?p=25643 + const POINT_ZERO: POINT = POINT { x: 0, y: 0 }; + let monitor = unsafe { MonitorFromPoint(POINT_ZERO, MONITOR_DEFAULTTOPRIMARY) }; + if monitor.is_invalid() { + log::error!( + "can not find the primary monitor: {}", + std::io::Error::last_os_error() + ); + return None; + } + let Some(display_id) = available_monitors() + .iter() + .position(|handle| handle.0 == monitor.0) + else { + return None; + }; + + Some(WindowsDisplay::new_with_handle_and_id( + monitor, + DisplayId(display_id as _), + )) + } + + pub fn displays() -> Vec> { + available_monitors() + .into_iter() + .enumerate() + .map(|(id, handle)| { + Rc::new(WindowsDisplay::new_with_handle_and_id( + handle, + DisplayId(id as _), + )) as Rc + }) + .collect() } } impl PlatformDisplay for WindowsDisplay { - // todo(windows) fn id(&self) -> DisplayId { - DisplayId(1) + self.display_id } - // todo(windows) - fn uuid(&self) -> Result { - Err(anyhow!("not implemented yet.")) + fn uuid(&self) -> anyhow::Result { + Ok(self.uuid) } fn bounds(&self) -> Bounds { - let mut dev = DEVMODEA { - dmSize: std::mem::size_of::() as _, - ..unsafe { std::mem::zeroed() } - }; - unsafe { EnumDisplaySettingsA(PCSTR::null(), ENUM_CURRENT_SETTINGS, &mut dev) }; - let w = dev.dmPelsWidth; - let h = dev.dmPelsHeight; - - log::debug!("Screen size: {w} {h}"); - Bounds::new( - Point::new(0.0.into(), 0.0.into()), - Size { - width: GlobalPixels(w as f32), - height: GlobalPixels(h as f32), - }, + self.bounds + } +} + +fn available_monitors() -> SmallVec<[HMONITOR; 4]> { + let mut monitors: SmallVec<[HMONITOR; 4]> = SmallVec::new(); + unsafe { + EnumDisplayMonitors( + HDC::default(), + None, + Some(monitor_enum_proc), + LPARAM(&mut monitors as *mut _ as _), + ); + } + monitors +} + +unsafe extern "system" fn monitor_enum_proc( + hmonitor: HMONITOR, + _hdc: HDC, + _place: *mut RECT, + data: LPARAM, +) -> BOOL { + let monitors = data.0 as *mut SmallVec<[HMONITOR; 4]>; + unsafe { (*monitors).push(hmonitor) }; + BOOL(1) +} + +fn get_monitor_info(hmonitor: HMONITOR) -> anyhow::Result { + let mut monitor_info: MONITORINFOEXW = unsafe { std::mem::zeroed() }; + monitor_info.monitorInfo.cbSize = std::mem::size_of::() as u32; + let status = unsafe { + GetMonitorInfoW( + hmonitor, + &mut monitor_info as *mut MONITORINFOEXW as *mut MONITORINFO, ) + }; + if status.as_bool() { + Ok(monitor_info) + } else { + Err(anyhow::anyhow!(std::io::Error::last_os_error())) } } + +fn generate_uuid(device_name: &[u16]) -> Uuid { + let name = device_name + .iter() + .flat_map(|&a| a.to_be_bytes().to_vec()) + .collect_vec(); + Uuid::new_v5(&Uuid::NAMESPACE_DNS, &name) +} diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index f302447453384034cdbb7777b5a1bdda85b95261..5fb83957e5e7f4c89adbfc06490d5f1e3a4a3ed5 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -284,19 +284,24 @@ impl Platform for WindowsPlatform { unimplemented!() } - // todo(windows) fn displays(&self) -> Vec> { - vec![Rc::new(WindowsDisplay::new())] + WindowsDisplay::displays() } - // todo(windows) fn display(&self, id: crate::DisplayId) -> Option> { - Some(Rc::new(WindowsDisplay::new())) + if let Some(display) = WindowsDisplay::new(id) { + Some(Rc::new(display) as Rc) + } else { + None + } } - // todo(windows) fn primary_display(&self) -> Option> { - Some(Rc::new(WindowsDisplay::new())) + if let Some(display) = WindowsDisplay::primary_monitor() { + Some(Rc::new(display) as Rc) + } else { + None + } } // todo(windows) diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 42b07a678a8520a81f5c499abbc168012b7ceff6..58dad7c07433be55752554109fd4c1252099d77e 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -637,6 +637,7 @@ struct Callbacks { pub(crate) struct WindowsWindow { inner: Rc, drag_drop_handler: IDropTarget, + display: Rc, } struct WindowCreateContext { @@ -701,9 +702,12 @@ impl WindowsWindow { }; drag_drop_handler }; + // todo(windows) move window to target monitor + // options.display_id let wnd = Self { inner: context.inner.unwrap(), drag_drop_handler, + display: Rc::new(WindowsDisplay::primary_monitor().unwrap()), }; platform_inner .raw_window_handles @@ -780,9 +784,8 @@ impl PlatformWindow for WindowsWindow { WindowAppearance::Dark } - // todo(windows) fn display(&self) -> Rc { - Rc::new(WindowsDisplay::new()) + self.display.clone() } fn mouse_position(&self) -> Point {