use crate::core::Rect;
use crate::platform::types::{Display, DisplayId};
use gtk::prelude::*;
fn with_display<R>(body: impl FnOnce(>k::gdk::Display) -> R) -> Option<R> {
if !gtk::is_initialized_main_thread() {
return None;
}
let display = gtk::gdk::Display::default()?;
Some(body(&display))
}
fn rect_of(gdk: >k::gdk::Rectangle) -> Rect {
Rect::new(gdk.x(), gdk.y(), gdk.width().max(0) as u32, gdk.height().max(0) as u32)
}
pub(crate) fn displays() -> Vec<Display> {
with_display(|display| {
let count = display.n_monitors();
let mut out = Vec::new();
for index in 0..count {
let Some(monitor) = display.monitor(index) else {
continue;
};
let scale = monitor.scale_factor();
out.push(Display {
id: DisplayId::new(index as u64),
bounds: rect_of(&monitor.geometry()),
work_area: rect_of(&monitor.workarea()),
scale: if scale > 0 { scale as f32 } else { 1.0 },
is_primary: monitor.is_primary(),
});
}
out
})
.unwrap_or_default()
}
pub(crate) fn display_index_at(x: i32, y: i32) -> Option<u64> {
with_display(|display| {
let hit = display.monitor_at_point(x, y)?;
(0..display.n_monitors())
.find(|&index| display.monitor(index).is_some_and(|m| m == hit))
.map(|index| index as u64)
})
.flatten()
}
pub(crate) fn generation() -> u64 {
const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
with_display(|display| {
let mut hash = FNV_OFFSET;
let mut mix = |value: u64| {
hash ^= value;
hash = hash.wrapping_mul(FNV_PRIME);
};
mix(display.n_monitors() as u64);
for display in displays() {
for part in [
display.id.get(),
display.bounds.x as i64 as u64,
display.bounds.y as i64 as u64,
display.bounds.width as u64,
display.bounds.height as u64,
display.work_area.x as i64 as u64,
display.work_area.y as i64 as u64,
display.work_area.width as u64,
display.work_area.height as u64,
display.scale.to_bits() as u64,
display.is_primary as u64,
] {
mix(part);
}
}
hash
})
.unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_non_main_thread_is_answered_instead_of_touching_gdk() {
let answer = std::thread::spawn(|| (displays(), display_index_at(0, 0), generation()))
.join()
.expect("the probe thread must not panic");
assert_eq!(answer, (Vec::new(), None, 0));
}
}