Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ fn main() {
let sources = [
"src/runtime/qt",
"src/ui/qt/widget",
"src/ui/qt/monitor",
"src/ui/qt/msgbox",
"src/ui/qt/filebox",
"src/ui/qt/window",
Expand Down
10 changes: 8 additions & 2 deletions examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use compio_log::info;
use winio::{
App, BrushPen, Canvas, CanvasEvent, Child, Color, ColorTheme, Component, ComponentSender,
CustomButton, DrawingFontBuilder, Grid, HAlign, Layoutable, MessageBox, MessageBoxButton,
MessageBoxResponse, MessageBoxStyle, MouseButton, Point, Rect, Size, SolidColorBrush, VAlign,
Visible, Window, WindowEvent,
MessageBoxResponse, MessageBoxStyle, Monitor, MouseButton, Point, Rect, Size, SolidColorBrush,
VAlign, Visible, Window, WindowEvent,
};

fn main() {
Expand Down Expand Up @@ -46,6 +46,12 @@ impl Component for MainModel {
window.set_text("Basic example");
window.set_size(Size::new(800.0, 600.0));

{
let monitors = Monitor::all();
let region = monitors[0].client_scaled();
window.set_loc(region.origin + region.size / 2.0 - window.size() / 2.0);
}

let sender = sender.clone();
spawn(async move {
let mut interval = interval(Duration::from_secs(1));
Expand Down
3 changes: 3 additions & 0 deletions src/ui/gtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ pub use canvas::*;
mod widget;
pub use widget::*;

mod monitor;
pub use monitor::*;

mod msgbox;
pub use msgbox::*;

Expand Down
35 changes: 35 additions & 0 deletions src/ui/gtk/monitor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use gtk4::{
gdk::{
self,
prelude::{DisplayExt, MonitorExt},
},
glib::object::Cast,
};

use crate::{Monitor, Point, Rect, Size};

pub fn monitor_get_all() -> Vec<Monitor> {
gdk::DisplayManager::get()
.list_displays()
.into_iter()
.flat_map(|d| {
d.monitors()
.into_iter()
.filter_map(|m| m.ok().and_then(|m| m.downcast::<gdk::Monitor>().ok()))
.collect::<Vec<_>>()
})
.map(|m| {
let geo = rect_from(m.geometry());
let scale = m.scale();
Monitor::new(geo * scale, geo * scale, Size::new(scale, scale))
})
.collect()
}

#[inline]
fn rect_from(r: gdk::Rectangle) -> Rect {
Rect::new(
Point::new(r.x() as _, r.y() as _),
Size::new(r.width() as _, r.height() as _),
)
}
3 changes: 3 additions & 0 deletions src/ui/mac/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ pub use canvas::*;
mod window;
pub use window::*;

mod monitor;
pub use monitor::*;

mod msgbox;
pub use msgbox::*;

Expand Down
42 changes: 42 additions & 0 deletions src/ui/mac/monitor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use objc2::{MainThreadMarker, rc::Retained};
use objc2_app_kit::{NSDeviceResolution, NSScreen};
use objc2_foundation::NSValue;

use crate::{
Monitor, Point, Rect, Size,
ui::{from_cgsize, transform_cgrect},
};

pub fn monitor_get_all() -> Vec<Monitor> {
let mtm = MainThreadMarker::new().unwrap();
let mut res = vec![];
for screen in NSScreen::screens(mtm) {
let frame = screen.frame();
let vframe = screen.visibleFrame();

let frame_size = from_cgsize(frame.size);
let frame = transform_cgrect(frame_size, frame);
let vframe = transform_cgrect(frame_size, vframe);

let dpi = screen
.deviceDescription()
.objectForKey(unsafe { NSDeviceResolution })
.map(|obj| from_cgsize(unsafe { Retained::cast_unchecked::<NSValue>(obj).sizeValue() }))
.unwrap_or(Size::new(1.0, 1.0));

res.push(Monitor::new(
rect_scale(frame, dpi),
rect_scale(vframe, dpi),
dpi,
))
}
res
}

#[inline]
fn rect_scale(r: Rect, dpi: Size) -> Rect {
Rect::new(
Point::new(r.origin.x * dpi.width, r.origin.y * dpi.height),
Size::new(r.size.width * dpi.width, r.size.height * dpi.height),
)
}
2 changes: 2 additions & 0 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub use sys::*;
mod canvas;
mod drawing;
mod filebox;
mod monitor;
mod msgbox;
mod window_handle;

Expand All @@ -34,6 +35,7 @@ pub mod export {
canvas::*,
drawing::*,
filebox::*,
monitor::*,
msgbox::*,
sys::{Brush, Pen, RawWindow},
window_handle::*,
Expand Down
66 changes: 66 additions & 0 deletions src/ui/monitor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use crate::{Point, Rect, Size, ui::sys};

/// Represents the geometry of a monitor.
#[derive(Debug, Clone, PartialEq)]
pub struct Monitor {
region: Rect,
client: Rect,
dpi: Size,
}

impl Monitor {
pub(crate) fn new(region: Rect, client: Rect, dpi: Size) -> Self {
Self {
region,
client,
dpi,
}
}

/// Retrieve all monitors.
pub fn all() -> Vec<Self> {
sys::monitor_get_all()
}

/// The physical region.
pub fn region(&self) -> Rect {
self.region
}

/// The client region.
pub fn client(&self) -> Rect {
self.client
}

/// Dpi of the monitor, 1.0 if no scale. You should take it into
/// consideration when setting the location of windows.
/// See [`Monitor::region_scaled`] & [`Monitor::client_scaled`].
pub fn dpi(&self) -> Size {
self.dpi
}

/// Scaled physical region.
pub fn region_scaled(&self) -> Rect {
div_rect(self.region, self.dpi)
}

/// Scaled client region.
pub fn client_scaled(&self) -> Rect {
div_rect(self.client, self.dpi)
}
}

#[inline]
fn div_rect(r: Rect, s: Size) -> Rect {
Rect::new(div_point(r.origin, s), div_size(r.size, s))
}

#[inline]
fn div_point(p: Point, s: Size) -> Point {
Point::new(p.x / s.width, p.y / s.height)
}

#[inline]
fn div_size(s1: Size, s2: Size) -> Size {
Size::new(s1.width / s2.width, s1.height / s2.height)
}
3 changes: 3 additions & 0 deletions src/ui/qt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ pub use canvas::*;
mod widget;
pub use widget::*;

mod monitor;
pub use monitor::*;

mod msgbox;
pub use msgbox::*;

Expand Down
12 changes: 12 additions & 0 deletions src/ui/qt/monitor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "monitor.hpp"
#include <QApplication>

rust::Vec<Monitor> screen_all() {
rust::Vec<Monitor> res{};
for (QScreen *s : QApplication::screens()) {
res.push_back(Monitor{s->geometry(), s->availableGeometry(),
s->logicalDotsPerInchX(),
s->logicalDotsPerInchY()});
}
return res;
}
8 changes: 8 additions & 0 deletions src/ui/qt/monitor.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

#include <QScreen>

#include <rust/cxx.h>
#include <winio/src/ui/qt/monitor.rs.h>

rust::Vec<Monitor> screen_all();
40 changes: 40 additions & 0 deletions src/ui/qt/monitor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::{Monitor, Point, Rect, Size, ui::QRect};

pub fn monitor_get_all() -> Vec<Monitor> {
ffi::screen_all()
.into_iter()
.map(|m| {
let dpi = Size::new(m.dpix / 96.0, m.dpiy / 96.0);
Monitor::new(rect_from(m.geo, dpi), rect_from(m.avail_geo, dpi), dpi)
})
.collect()
}

#[inline]
fn rect_from(r: QRect, dpi: Size) -> Rect {
Rect::new(
Point::new(r.x1 as f64 * dpi.width, r.y1 as f64 * dpi.height),
Size::new(
(r.x2 - r.x1) as f64 * dpi.width,
(r.y2 - r.y1) as f64 * dpi.height,
),
)
}

#[cxx::bridge]
mod ffi {
struct Monitor {
geo: QRect,
avail_geo: QRect,
dpix: f64,
dpiy: f64,
}

unsafe extern "C++" {
include!("winio/src/ui/qt/monitor.hpp");

type QRect = super::QRect;

fn screen_all() -> Vec<Monitor>;
}
}
15 changes: 9 additions & 6 deletions src/ui/windows/mod.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
mod canvas;
pub use canvas::*;

pub(crate) mod darkmode;
pub(crate) mod dpi;
pub(crate) mod font;

mod msgbox;
pub use msgbox::*;

mod filebox;
pub use filebox::*;

mod monitor;
pub use monitor::*;

mod msgbox;
pub use msgbox::*;

mod window;
pub use window::*;

mod button;
pub use button::*;

mod canvas;
pub use canvas::*;

mod edit;
pub use edit::*;

Expand Down
59 changes: 59 additions & 0 deletions src/ui/windows/monitor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use std::ptr::{addr_of_mut, null_mut};

use compio::driver::syscall;
use windows_sys::{
Win32::{
Foundation::{LPARAM, RECT, S_OK},
Graphics::Gdi::{EnumDisplayMonitors, GetMonitorInfoW, HDC, HMONITOR, MONITORINFO},
UI::{
HiDpi::{GetDpiForMonitor, MDT_EFFECTIVE_DPI},
WindowsAndMessaging::USER_DEFAULT_SCREEN_DPI,
},
},
core::BOOL,
};

use crate::{Monitor, Point, Rect, Size};

pub fn monitor_get_all() -> Vec<Monitor> {
let mut res = vec![];
syscall!(
BOOL,
EnumDisplayMonitors(
null_mut(),
null_mut(),
Some(enum_monitor),
addr_of_mut!(res) as _
)
)
.unwrap();
res
}

unsafe extern "system" fn enum_monitor(m: HMONITOR, _: HDC, _: *mut RECT, res: LPARAM) -> BOOL {
let res = &mut *(res as *mut Vec<Monitor>);
let mut info: MONITORINFO = unsafe { std::mem::zeroed() };
info.cbSize = size_of::<MONITORINFO>() as _;
if GetMonitorInfoW(m, &mut info) == 0 {
return 0;
}
let mut dpix = 0;
let mut dpiy = 0;
if GetDpiForMonitor(m, MDT_EFFECTIVE_DPI, &mut dpix, &mut dpiy) != S_OK {
return 0;
}
res.push(Monitor::new(
rect_from(info.rcMonitor),
rect_from(info.rcWork),
Size::new(dpix as f64, dpiy as f64) / USER_DEFAULT_SCREEN_DPI as f64,
));
1
}

#[inline]
fn rect_from(r: RECT) -> Rect {
Rect::new(
Point::new(r.left as f64, r.top as f64),
Size::new((r.right - r.left) as f64, (r.bottom - r.top) as f64),
)
}
10 changes: 7 additions & 3 deletions src/ui/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,15 @@ impl Widget {
x: rect.left,
y: rect.top,
};
syscall!(
SetLastError(0);
match syscall!(
BOOL,
MapWindowPoints(HWND_DESKTOP, GetParent(handle), &mut point, 2,)
)
.unwrap();
) {
Ok(_) => {}
Err(e) if e.raw_os_error() == Some(0) => {}
Err(e) => panic!("{e:?}"),
}
(point.x, point.y)
}
}
Expand Down