@@ -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]]
@@ -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<GlobalPixels>,
+ uuid: Uuid,
+}
impl WindowsDisplay {
- pub(crate) fn new() -> Self {
- Self
+ pub(crate) fn new(display_id: DisplayId) -> Option<Self> {
+ 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<Self> {
+ // 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<Rc<dyn PlatformDisplay>> {
+ available_monitors()
+ .into_iter()
+ .enumerate()
+ .map(|(id, handle)| {
+ Rc::new(WindowsDisplay::new_with_handle_and_id(
+ handle,
+ DisplayId(id as _),
+ )) as Rc<dyn PlatformDisplay>
+ })
+ .collect()
}
}
impl PlatformDisplay for WindowsDisplay {
- // todo(windows)
fn id(&self) -> DisplayId {
- DisplayId(1)
+ self.display_id
}
- // todo(windows)
- fn uuid(&self) -> Result<Uuid> {
- Err(anyhow!("not implemented yet."))
+ fn uuid(&self) -> anyhow::Result<Uuid> {
+ Ok(self.uuid)
}
fn bounds(&self) -> Bounds<GlobalPixels> {
- let mut dev = DEVMODEA {
- dmSize: std::mem::size_of::<DEVMODEA>() 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<MONITORINFOEXW> {
+ let mut monitor_info: MONITORINFOEXW = unsafe { std::mem::zeroed() };
+ monitor_info.monitorInfo.cbSize = std::mem::size_of::<MONITORINFOEXW>() 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)
+}
@@ -637,6 +637,7 @@ struct Callbacks {
pub(crate) struct WindowsWindow {
inner: Rc<WindowsWindowInner>,
drag_drop_handler: IDropTarget,
+ display: Rc<WindowsDisplay>,
}
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<dyn PlatformDisplay> {
- Rc::new(WindowsDisplay::new())
+ self.display.clone()
}
fn mouse_position(&self) -> Point<Pixels> {