Skip to content

Commit 82a67d8

Browse files
authored
Merge pull request #29 from compio-rs/dev/monitor
feat: monitors
2 parents 3b24198 + dd11af4 commit 82a67d8

15 files changed

Lines changed: 298 additions & 11 deletions

File tree

build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ fn main() {
1919
let sources = [
2020
"src/runtime/qt",
2121
"src/ui/qt/widget",
22+
"src/ui/qt/monitor",
2223
"src/ui/qt/msgbox",
2324
"src/ui/qt/filebox",
2425
"src/ui/qt/window",

examples/basic.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use compio_log::info;
55
use winio::{
66
App, BrushPen, Canvas, CanvasEvent, Child, Color, ColorTheme, Component, ComponentSender,
77
CustomButton, DrawingFontBuilder, Grid, HAlign, Layoutable, MessageBox, MessageBoxButton,
8-
MessageBoxResponse, MessageBoxStyle, MouseButton, Point, Rect, Size, SolidColorBrush, VAlign,
9-
Visible, Window, WindowEvent,
8+
MessageBoxResponse, MessageBoxStyle, Monitor, MouseButton, Point, Rect, Size, SolidColorBrush,
9+
VAlign, Visible, Window, WindowEvent,
1010
};
1111

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

49+
{
50+
let monitors = Monitor::all();
51+
let region = monitors[0].client_scaled();
52+
window.set_loc(region.origin + region.size / 2.0 - window.size() / 2.0);
53+
}
54+
4955
let sender = sender.clone();
5056
spawn(async move {
5157
let mut interval = interval(Duration::from_secs(1));

src/ui/gtk/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ pub use canvas::*;
77
mod widget;
88
pub use widget::*;
99

10+
mod monitor;
11+
pub use monitor::*;
12+
1013
mod msgbox;
1114
pub use msgbox::*;
1215

src/ui/gtk/monitor.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use gtk4::{
2+
gdk::{
3+
self,
4+
prelude::{DisplayExt, MonitorExt},
5+
},
6+
glib::object::Cast,
7+
};
8+
9+
use crate::{Monitor, Point, Rect, Size};
10+
11+
pub fn monitor_get_all() -> Vec<Monitor> {
12+
gdk::DisplayManager::get()
13+
.list_displays()
14+
.into_iter()
15+
.flat_map(|d| {
16+
d.monitors()
17+
.into_iter()
18+
.filter_map(|m| m.ok().and_then(|m| m.downcast::<gdk::Monitor>().ok()))
19+
.collect::<Vec<_>>()
20+
})
21+
.map(|m| {
22+
let geo = rect_from(m.geometry());
23+
let scale = m.scale();
24+
Monitor::new(geo * scale, geo * scale, Size::new(scale, scale))
25+
})
26+
.collect()
27+
}
28+
29+
#[inline]
30+
fn rect_from(r: gdk::Rectangle) -> Rect {
31+
Rect::new(
32+
Point::new(r.x() as _, r.y() as _),
33+
Size::new(r.width() as _, r.height() as _),
34+
)
35+
}

src/ui/mac/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ pub use canvas::*;
44
mod window;
55
pub use window::*;
66

7+
mod monitor;
8+
pub use monitor::*;
9+
710
mod msgbox;
811
pub use msgbox::*;
912

src/ui/mac/monitor.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use objc2::{MainThreadMarker, rc::Retained};
2+
use objc2_app_kit::{NSDeviceResolution, NSScreen};
3+
use objc2_foundation::NSValue;
4+
5+
use crate::{
6+
Monitor, Point, Rect, Size,
7+
ui::{from_cgsize, transform_cgrect},
8+
};
9+
10+
pub fn monitor_get_all() -> Vec<Monitor> {
11+
let mtm = MainThreadMarker::new().unwrap();
12+
let mut res = vec![];
13+
for screen in NSScreen::screens(mtm) {
14+
let frame = screen.frame();
15+
let vframe = screen.visibleFrame();
16+
17+
let frame_size = from_cgsize(frame.size);
18+
let frame = transform_cgrect(frame_size, frame);
19+
let vframe = transform_cgrect(frame_size, vframe);
20+
21+
let dpi = screen
22+
.deviceDescription()
23+
.objectForKey(unsafe { NSDeviceResolution })
24+
.map(|obj| from_cgsize(unsafe { Retained::cast_unchecked::<NSValue>(obj).sizeValue() }))
25+
.unwrap_or(Size::new(1.0, 1.0));
26+
27+
res.push(Monitor::new(
28+
rect_scale(frame, dpi),
29+
rect_scale(vframe, dpi),
30+
dpi,
31+
))
32+
}
33+
res
34+
}
35+
36+
#[inline]
37+
fn rect_scale(r: Rect, dpi: Size) -> Rect {
38+
Rect::new(
39+
Point::new(r.origin.x * dpi.width, r.origin.y * dpi.height),
40+
Size::new(r.size.width * dpi.width, r.size.height * dpi.height),
41+
)
42+
}

src/ui/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub use sys::*;
2626
mod canvas;
2727
mod drawing;
2828
mod filebox;
29+
mod monitor;
2930
mod msgbox;
3031
mod window_handle;
3132

@@ -34,6 +35,7 @@ pub mod export {
3435
canvas::*,
3536
drawing::*,
3637
filebox::*,
38+
monitor::*,
3739
msgbox::*,
3840
sys::{Brush, Pen, RawWindow},
3941
window_handle::*,

src/ui/monitor.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use crate::{Point, Rect, Size, ui::sys};
2+
3+
/// Represents the geometry of a monitor.
4+
#[derive(Debug, Clone, PartialEq)]
5+
pub struct Monitor {
6+
region: Rect,
7+
client: Rect,
8+
dpi: Size,
9+
}
10+
11+
impl Monitor {
12+
pub(crate) fn new(region: Rect, client: Rect, dpi: Size) -> Self {
13+
Self {
14+
region,
15+
client,
16+
dpi,
17+
}
18+
}
19+
20+
/// Retrieve all monitors.
21+
pub fn all() -> Vec<Self> {
22+
sys::monitor_get_all()
23+
}
24+
25+
/// The physical region.
26+
pub fn region(&self) -> Rect {
27+
self.region
28+
}
29+
30+
/// The client region.
31+
pub fn client(&self) -> Rect {
32+
self.client
33+
}
34+
35+
/// Dpi of the monitor, 1.0 if no scale. You should take it into
36+
/// consideration when setting the location of windows.
37+
/// See [`Monitor::region_scaled`] & [`Monitor::client_scaled`].
38+
pub fn dpi(&self) -> Size {
39+
self.dpi
40+
}
41+
42+
/// Scaled physical region.
43+
pub fn region_scaled(&self) -> Rect {
44+
div_rect(self.region, self.dpi)
45+
}
46+
47+
/// Scaled client region.
48+
pub fn client_scaled(&self) -> Rect {
49+
div_rect(self.client, self.dpi)
50+
}
51+
}
52+
53+
#[inline]
54+
fn div_rect(r: Rect, s: Size) -> Rect {
55+
Rect::new(div_point(r.origin, s), div_size(r.size, s))
56+
}
57+
58+
#[inline]
59+
fn div_point(p: Point, s: Size) -> Point {
60+
Point::new(p.x / s.width, p.y / s.height)
61+
}
62+
63+
#[inline]
64+
fn div_size(s1: Size, s2: Size) -> Size {
65+
Size::new(s1.width / s2.width, s1.height / s2.height)
66+
}

src/ui/qt/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ pub use canvas::*;
77
mod widget;
88
pub use widget::*;
99

10+
mod monitor;
11+
pub use monitor::*;
12+
1013
mod msgbox;
1114
pub use msgbox::*;
1215

src/ui/qt/monitor.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include "monitor.hpp"
2+
#include <QApplication>
3+
4+
rust::Vec<Monitor> screen_all() {
5+
rust::Vec<Monitor> res{};
6+
for (QScreen *s : QApplication::screens()) {
7+
res.push_back(Monitor{s->geometry(), s->availableGeometry(),
8+
s->logicalDotsPerInchX(),
9+
s->logicalDotsPerInchY()});
10+
}
11+
return res;
12+
}

0 commit comments

Comments
 (0)