1use core::fmt;
2use std::sync::Arc;
3
4use zng_app::APP;
5use zng_app::event::{AnyEventArgs, event, event_args};
6use zng_app::update::EventUpdate;
7use zng_app::view_process::raw_events::{RAW_MONITORS_CHANGED_EVENT, RAW_SCALE_FACTOR_CHANGED_EVENT, RawMonitorsChangedArgs};
8use zng_app::window::{MonitorId, WINDOW, WindowId};
9use zng_app_context::app_local;
10use zng_layout::unit::{Dip, DipRect, DipSize, DipToPx, Factor, FactorUnits, Px, PxDensity, PxPoint, PxRect, PxSize, PxToDip};
11use zng_txt::{ToTxt, Txt};
12use zng_unique_id::IdMap;
13use zng_var::{Var, VarValue, impl_from_and_into_var, var};
14use zng_view_api::window::VideoMode;
15
16use crate::{WINDOWS, WindowManager};
17
18app_local! {
19 pub(super) static MONITORS_SV: MonitorsService = {
20 APP.extensions().require::<WindowManager>();
21 MonitorsService {
22 monitors: var(IdMap::new()),
23 }
24 };
25}
26
27pub struct MONITORS;
64impl MONITORS {
65 pub fn monitor(&self, monitor_id: MonitorId) -> Option<MonitorInfo> {
69 MONITORS_SV.read().monitors.with(|m| m.get(&monitor_id).cloned())
70 }
71
72 pub fn available_monitors(&self) -> Var<Vec<MonitorInfo>> {
76 MONITORS_SV.read().monitors.map(|w| {
77 let mut list: Vec<_> = w.values().cloned().collect();
78 list.sort_by(|a, b| a.name.with(|a| b.name.with(|b| a.cmp(b))));
79 list
80 })
81 }
82
83 pub fn primary_monitor(&self) -> Var<Option<MonitorInfo>> {
85 MONITORS_SV
86 .read()
87 .monitors
88 .map(|w| w.values().find(|m| m.is_primary().get()).cloned())
89 }
90}
91
92pub(super) struct MonitorsService {
93 monitors: Var<IdMap<MonitorId, MonitorInfo>>,
94}
95impl MonitorsService {
96 fn on_monitors_changed(&mut self, args: &RawMonitorsChangedArgs) {
97 let mut available_monitors: IdMap<_, _> = args.available_monitors.iter().cloned().collect();
98 let event_ts = args.timestamp;
99 let event_propagation = args.propagation().clone();
100
101 self.monitors.modify(move |m| {
102 let mut removed = vec![];
103 let mut changed = vec![];
104
105 m.retain(|key, value| {
106 if let Some(new) = available_monitors.remove(key) {
107 if value.update(new) {
108 changed.push(*key);
109 }
110 true
111 } else {
112 removed.push(*key);
113 false
114 }
115 });
116
117 let mut added = Vec::with_capacity(available_monitors.len());
118
119 for (id, info) in available_monitors {
120 added.push(id);
121
122 m.insert(id, MonitorInfo::from_gen(id, info));
123 }
124
125 if !removed.is_empty() || !added.is_empty() || !changed.is_empty() {
126 let args = MonitorsChangedArgs::new(event_ts, event_propagation, removed, added, changed);
127 MONITORS_CHANGED_EVENT.notify(args);
128 }
129 });
130 }
131
132 pub(super) fn on_pre_event(update: &EventUpdate) {
133 if let Some(args) = RAW_SCALE_FACTOR_CHANGED_EVENT.on(update) {
134 MONITORS_SV.read().monitors.with(|m| {
135 if let Some(m) = m.get(&args.monitor_id) {
136 m.scale_factor.set(args.scale_factor);
137 }
138 });
139 } else if let Some(args) = RAW_MONITORS_CHANGED_EVENT.on(update) {
140 MONITORS_SV.write().on_monitors_changed(args);
141 }
142 }
143}
144
145#[derive(Clone, Copy, PartialEq)]
149#[non_exhaustive]
150pub struct HeadlessMonitor {
151 pub scale_factor: Option<Factor>,
159
160 pub size: DipSize,
167
168 pub density: PxDensity,
170}
171impl fmt::Debug for HeadlessMonitor {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 if f.alternate() || self.density != PxDensity::default() {
174 f.debug_struct("HeadlessMonitor")
175 .field("scale_factor", &self.scale_factor)
176 .field("screen_size", &self.size)
177 .field("density", &self.density)
178 .finish()
179 } else {
180 write!(f, "({:?}, ({}, {}))", self.scale_factor, self.size.width, self.size.height)
181 }
182 }
183}
184impl HeadlessMonitor {
185 pub fn new(size: DipSize) -> Self {
187 HeadlessMonitor {
188 scale_factor: None,
189 size,
190 density: PxDensity::default(),
191 }
192 }
193
194 pub fn new_scaled(size: DipSize, scale_factor: Factor) -> Self {
196 HeadlessMonitor {
197 scale_factor: Some(scale_factor),
198 size,
199 density: PxDensity::default(),
200 }
201 }
202
203 pub fn new_scale(scale_factor: Factor) -> Self {
205 HeadlessMonitor {
206 scale_factor: Some(scale_factor),
207 ..Self::default()
208 }
209 }
210}
211impl Default for HeadlessMonitor {
212 fn default() -> Self {
214 (11608, 8708).into()
215 }
216}
217impl_from_and_into_var! {
218 fn from<W: Into<Dip>, H: Into<Dip>>((width, height): (W, H)) -> HeadlessMonitor {
219 HeadlessMonitor::new(DipSize::new(width.into(), height.into()))
220 }
221 fn from<W: Into<Dip>, H: Into<Dip>, F: Into<Factor>>((width, height, scale): (W, H, F)) -> HeadlessMonitor {
222 HeadlessMonitor::new_scaled(DipSize::new(width.into(), height.into()), scale.into())
223 }
224}
225
226#[derive(Clone)]
228pub struct MonitorInfo {
229 id: MonitorId,
230 is_primary: Var<bool>,
231 name: Var<Txt>,
232 position: Var<PxPoint>,
233 size: Var<PxSize>,
234 video_modes: Var<Vec<VideoMode>>,
235 scale_factor: Var<Factor>,
236 density: Var<PxDensity>,
237}
238impl fmt::Debug for MonitorInfo {
239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240 f.debug_struct("MonitorInfo")
241 .field("id", &self.id)
242 .field("name", &self.name.get())
243 .field("position", &self.position.get())
244 .field("size", &self.size.get())
245 .finish_non_exhaustive()
246 }
247}
248impl PartialEq for MonitorInfo {
249 fn eq(&self, other: &Self) -> bool {
250 self.id == other.id && self.name.var_eq(&other.name)
251 }
252}
253impl MonitorInfo {
254 fn from_gen(id: MonitorId, info: zng_view_api::window::MonitorInfo) -> Self {
256 MonitorInfo {
257 id,
258 is_primary: var(info.is_primary),
259 name: var(info.name.to_txt()),
260 position: var(info.position),
261 size: var(info.size),
262 scale_factor: var(info.scale_factor),
263 video_modes: var(info.video_modes),
264 density: var(PxDensity::default()),
265 }
266 }
267
268 fn update(&self, info: zng_view_api::window::MonitorInfo) -> bool {
271 fn check_set<T: VarValue + PartialEq>(var: &Var<T>, value: T) -> bool {
272 let ne = var.with(|v| v != &value);
273 var.set(value);
274 ne
275 }
276
277 check_set(&self.is_primary, info.is_primary)
278 | check_set(&self.name, info.name.to_txt())
279 | check_set(&self.position, info.position)
280 | check_set(&self.size, info.size)
281 | check_set(&self.scale_factor, info.scale_factor)
282 | check_set(&self.video_modes, info.video_modes)
283 }
284
285 pub fn id(&self) -> MonitorId {
287 self.id
288 }
289
290 pub fn is_primary(&self) -> Var<bool> {
292 self.is_primary.read_only()
293 }
294
295 pub fn name(&self) -> Var<Txt> {
297 self.name.read_only()
298 }
299 pub fn position(&self) -> Var<PxPoint> {
301 self.position.read_only()
302 }
303 pub fn size(&self) -> Var<PxSize> {
305 self.size.read_only()
306 }
307
308 pub fn video_modes(&self) -> Var<Vec<VideoMode>> {
310 self.video_modes.read_only()
311 }
312
313 pub fn scale_factor(&self) -> Var<Factor> {
317 self.scale_factor.read_only()
318 }
319 pub fn density(&self) -> Var<PxDensity> {
321 self.density.clone()
322 }
323
324 pub fn px_rect(&self) -> PxRect {
326 let pos = self.position.get();
327 let size = self.size.get();
328
329 PxRect::new(pos, size)
330 }
331
332 pub fn dip_rect(&self) -> DipRect {
334 let pos = self.position.get();
335 let size = self.size.get();
336 let factor = self.scale_factor.get();
337
338 PxRect::new(pos, size).to_dip(factor)
339 }
340
341 pub fn fallback() -> Self {
345 let defaults = HeadlessMonitor::default();
346 let fct = 1.fct();
347
348 Self {
349 id: MonitorId::fallback(),
350 is_primary: var(false),
351 name: var("<fallback>".into()),
352 position: var(PxPoint::zero()),
353 size: var(defaults.size.to_px(fct)),
354 video_modes: var(vec![]),
355 scale_factor: var(fct),
356 density: var(PxDensity::default()),
357 }
358 }
359}
360
361#[derive(Clone, Default)]
363pub enum MonitorQuery {
364 #[default]
371 ParentOrPrimary,
372
373 Primary,
375 Query(Arc<dyn Fn() -> Option<MonitorInfo> + Send + Sync>),
381}
382impl std::fmt::Debug for MonitorQuery {
383 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
384 if f.alternate() {
385 write!(f, "MonitorQuery::")?;
386 }
387 match self {
388 Self::ParentOrPrimary => write!(f, "ParentOrPrimary"),
389 Self::Primary => write!(f, "Primary"),
390 Self::Query(_) => write!(f, "Query(_)"),
391 }
392 }
393}
394impl MonitorQuery {
395 pub fn new(query: impl Fn() -> Option<MonitorInfo> + Send + Sync + 'static) -> Self {
397 Self::Query(Arc::new(query))
398 }
399
400 pub fn select(&self) -> Option<MonitorInfo> {
402 self.select_for(WINDOW.id())
403 }
404 fn select_for(&self, win_id: WindowId) -> Option<MonitorInfo> {
405 match self {
406 MonitorQuery::ParentOrPrimary => Self::parent_or_primary_query(win_id),
407 MonitorQuery::Primary => Self::primary_query(),
408 MonitorQuery::Query(q) => q(),
409 }
410 }
411
412 pub fn select_fallback(&self) -> MonitorInfo {
414 match self {
415 MonitorQuery::ParentOrPrimary => Self::parent_or_primary_query(WINDOW.id()),
416 MonitorQuery::Primary => Self::primary_query(),
417 MonitorQuery::Query(q) => q().or_else(Self::primary_query),
418 }
419 .unwrap_or_else(Self::fallback)
420 }
421
422 fn fallback() -> MonitorInfo {
423 MONITORS_SV.read().monitors.with(|m| {
424 let mut best = None;
425 let mut best_area = Px(0);
426 for m in m.values() {
427 let m_area = m.px_rect().area();
428 if m_area > best_area {
429 best = Some(m);
430 best_area = m_area;
431 }
432 }
433 best.cloned().unwrap_or_else(MonitorInfo::fallback)
434 })
435 }
436
437 fn parent_or_primary_query(win_id: WindowId) -> Option<MonitorInfo> {
438 if let Some(parent) = WINDOWS.vars(win_id).unwrap().parent().get()
439 && let Ok(w) = WINDOWS.vars(parent)
440 {
441 return if let Some(monitor) = w.actual_monitor().get() {
442 MONITORS.monitor(monitor)
443 } else {
444 w.monitor().get().select_for(parent)
445 };
446 }
447 MONITORS.primary_monitor().get()
448 }
449
450 fn primary_query() -> Option<MonitorInfo> {
451 MONITORS.primary_monitor().get()
452 }
453}
454impl PartialEq for MonitorQuery {
455 fn eq(&self, other: &Self) -> bool {
457 matches!((self, other), (Self::Primary, Self::Primary))
458 }
459}
460
461event_args! {
462 pub struct MonitorsChangedArgs {
464 pub removed: Vec<MonitorId>,
466
467 pub added: Vec<MonitorId>,
471
472 pub modified: Vec<MonitorId>,
476
477 ..
478
479 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
481 list.search_all()
482 }
483 }
484}
485
486event! {
487 pub static MONITORS_CHANGED_EVENT: MonitorsChangedArgs;
489}