1use core::fmt;
2use std::sync::Arc;
3
4use zng_app::event::{AnyEventArgs, event, event_args};
5use zng_app::update::EventUpdate;
6use zng_app::view_process::VIEW_PROCESS_INITED_EVENT;
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, Ppi, Px, PxPoint, PxRect, PxSize, PxToDip};
11use zng_txt::{ToTxt, Txt};
12use zng_unique_id::IdMap;
13use zng_var::{ArcVar, ReadOnlyArcVar, Var, VarValue, impl_from_and_into_var, var};
14use zng_view_api::window::VideoMode;
15
16use crate::WINDOWS;
17
18app_local! {
19 pub(super) static MONITORS_SV: MonitorsService = const { MonitorsService { monitors: IdMap::new() } };
20}
21
22pub struct MONITORS;
59impl MONITORS {
60 pub fn monitor(&self, monitor_id: MonitorId) -> Option<MonitorInfo> {
64 MONITORS_SV.read().monitors.get(&monitor_id).cloned()
65 }
66
67 pub fn available_monitors(&self) -> Vec<MonitorInfo> {
71 MONITORS_SV.read().monitors.values().cloned().collect()
72 }
73
74 pub fn primary_monitor(&self) -> Option<MonitorInfo> {
76 MONITORS_SV.read().monitors.values().find(|m| m.is_primary().get()).cloned()
77 }
78}
79
80pub(super) struct MonitorsService {
81 monitors: IdMap<MonitorId, MonitorInfo>,
82}
83impl MonitorsService {
84 fn on_monitors_changed(&mut self, args: &RawMonitorsChangedArgs) {
85 let mut available_monitors: IdMap<_, _> = args.available_monitors.iter().cloned().collect();
86
87 let mut removed = vec![];
88 let mut changed = vec![];
89
90 self.monitors.retain(|key, value| {
91 if let Some(new) = available_monitors.remove(key) {
92 if value.update(new) {
93 changed.push(*key);
94 }
95 true
96 } else {
97 removed.push(*key);
98 false
99 }
100 });
101
102 let mut added = Vec::with_capacity(available_monitors.len());
103
104 for (id, info) in available_monitors {
105 added.push(id);
106
107 self.monitors.insert(id, MonitorInfo::from_gen(id, info));
108 }
109
110 if !removed.is_empty() || !added.is_empty() || !changed.is_empty() {
111 let args = MonitorsChangedArgs::new(args.timestamp, args.propagation().clone(), removed, added, changed);
112 MONITORS_CHANGED_EVENT.notify(args);
113 }
114 }
115
116 pub(super) fn on_pre_event(update: &EventUpdate) {
117 if let Some(args) = RAW_SCALE_FACTOR_CHANGED_EVENT.on(update) {
118 if let Some(m) = MONITORS_SV.read().monitors.get(&args.monitor_id) {
119 m.scale_factor.set(args.scale_factor);
120 }
121 } else if let Some(args) = RAW_MONITORS_CHANGED_EVENT.on(update) {
122 MONITORS_SV.write().on_monitors_changed(args);
123 } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update) {
124 let args = RawMonitorsChangedArgs::new(args.timestamp, args.propagation().clone(), args.available_monitors.clone());
125 MONITORS_SV.write().on_monitors_changed(&args);
126 }
127 }
128}
129
130#[derive(Clone, Copy, PartialEq)]
134pub struct HeadlessMonitor {
135 pub scale_factor: Option<Factor>,
143
144 pub size: DipSize,
151
152 pub ppi: Ppi,
154}
155impl fmt::Debug for HeadlessMonitor {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 if f.alternate() || self.ppi != Ppi::default() {
158 f.debug_struct("HeadlessMonitor")
159 .field("scale_factor", &self.scale_factor)
160 .field("screen_size", &self.size)
161 .field("ppi", &self.ppi)
162 .finish()
163 } else {
164 write!(f, "({:?}, ({}, {}))", self.scale_factor, self.size.width, self.size.height)
165 }
166 }
167}
168impl HeadlessMonitor {
169 pub fn new(size: DipSize) -> Self {
171 HeadlessMonitor {
172 scale_factor: None,
173 size,
174 ppi: Ppi::default(),
175 }
176 }
177
178 pub fn new_scaled(size: DipSize, scale_factor: Factor) -> Self {
180 HeadlessMonitor {
181 scale_factor: Some(scale_factor),
182 size,
183 ppi: Ppi::default(),
184 }
185 }
186
187 pub fn new_scale(scale_factor: Factor) -> Self {
189 HeadlessMonitor {
190 scale_factor: Some(scale_factor),
191 ..Self::default()
192 }
193 }
194}
195impl Default for HeadlessMonitor {
196 fn default() -> Self {
198 (11608, 8708).into()
199 }
200}
201impl_from_and_into_var! {
202 fn from<W: Into<Dip>, H: Into<Dip>>((width, height): (W, H)) -> HeadlessMonitor {
203 HeadlessMonitor::new(DipSize::new(width.into(), height.into()))
204 }
205 fn from<W: Into<Dip>, H: Into<Dip>, F: Into<Factor>>((width, height, scale): (W, H, F)) -> HeadlessMonitor {
206 HeadlessMonitor::new_scaled(DipSize::new(width.into(), height.into()), scale.into())
207 }
208}
209
210#[derive(Clone)]
212pub struct MonitorInfo {
213 id: MonitorId,
214 is_primary: ArcVar<bool>,
215 name: ArcVar<Txt>,
216 position: ArcVar<PxPoint>,
217 size: ArcVar<PxSize>,
218 video_modes: ArcVar<Vec<VideoMode>>,
219 scale_factor: ArcVar<Factor>,
220 ppi: ArcVar<Ppi>,
221}
222impl fmt::Debug for MonitorInfo {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 f.debug_struct("MonitorFullInfo").field("id", &self.id).finish_non_exhaustive()
225 }
226}
227impl MonitorInfo {
228 fn from_gen(id: MonitorId, info: zng_view_api::window::MonitorInfo) -> Self {
230 MonitorInfo {
231 id,
232 is_primary: var(info.is_primary),
233 name: var(info.name.to_txt()),
234 position: var(info.position),
235 size: var(info.size),
236 scale_factor: var(info.scale_factor),
237 video_modes: var(info.video_modes),
238 ppi: var(Ppi::default()),
239 }
240 }
241
242 fn update(&self, info: zng_view_api::window::MonitorInfo) -> bool {
245 fn check_set<T: VarValue + PartialEq>(var: &impl Var<T>, value: T) -> bool {
246 let ne = var.with(|v| v != &value);
247 var.set(value).unwrap();
248 ne
249 }
250
251 check_set(&self.is_primary, info.is_primary)
252 | check_set(&self.name, info.name.to_txt())
253 | check_set(&self.position, info.position)
254 | check_set(&self.size, info.size)
255 | check_set(&self.scale_factor, info.scale_factor)
256 | check_set(&self.video_modes, info.video_modes)
257 }
258
259 pub fn id(&self) -> MonitorId {
261 self.id
262 }
263
264 pub fn is_primary(&self) -> ReadOnlyArcVar<bool> {
266 self.is_primary.read_only()
267 }
268
269 pub fn name(&self) -> ReadOnlyArcVar<Txt> {
271 self.name.read_only()
272 }
273 pub fn position(&self) -> ReadOnlyArcVar<PxPoint> {
275 self.position.read_only()
276 }
277 pub fn size(&self) -> ReadOnlyArcVar<PxSize> {
279 self.size.read_only()
280 }
281
282 pub fn video_modes(&self) -> ReadOnlyArcVar<Vec<VideoMode>> {
284 self.video_modes.read_only()
285 }
286
287 pub fn scale_factor(&self) -> ReadOnlyArcVar<Factor> {
291 self.scale_factor.read_only()
292 }
293 pub fn ppi(&self) -> ArcVar<Ppi> {
295 self.ppi.clone()
296 }
297
298 pub fn px_rect(&self) -> PxRect {
300 let pos = self.position.get();
301 let size = self.size.get();
302
303 PxRect::new(pos, size)
304 }
305
306 pub fn dip_rect(&self) -> DipRect {
308 let pos = self.position.get();
309 let size = self.size.get();
310 let factor = self.scale_factor.get();
311
312 PxRect::new(pos, size).to_dip(factor)
313 }
314
315 pub fn fallback() -> Self {
319 let defaults = HeadlessMonitor::default();
320 let fct = 1.fct();
321
322 Self {
323 id: MonitorId::fallback(),
324 is_primary: var(false),
325 name: var("<fallback>".into()),
326 position: var(PxPoint::zero()),
327 size: var(defaults.size.to_px(fct)),
328 video_modes: var(vec![]),
329 scale_factor: var(fct),
330 ppi: var(Ppi::default()),
331 }
332 }
333}
334
335#[derive(Clone, Default)]
337pub enum MonitorQuery {
338 #[default]
345 ParentOrPrimary,
346
347 Primary,
349 Query(Arc<dyn Fn() -> Option<MonitorInfo> + Send + Sync>),
355}
356impl std::fmt::Debug for MonitorQuery {
357 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
358 if f.alternate() {
359 write!(f, "MonitorQuery::")?;
360 }
361 match self {
362 Self::ParentOrPrimary => write!(f, "ParentOrPrimary"),
363 Self::Primary => write!(f, "Primary"),
364 Self::Query(_) => write!(f, "Query(_)"),
365 }
366 }
367}
368impl MonitorQuery {
369 pub fn new(query: impl Fn() -> Option<MonitorInfo> + Send + Sync + 'static) -> Self {
371 Self::Query(Arc::new(query))
372 }
373
374 pub fn select(&self) -> Option<MonitorInfo> {
376 self.select_for(WINDOW.id())
377 }
378 fn select_for(&self, win_id: WindowId) -> Option<MonitorInfo> {
379 match self {
380 MonitorQuery::ParentOrPrimary => Self::parent_or_primary_query(win_id),
381 MonitorQuery::Primary => Self::primary_query(),
382 MonitorQuery::Query(q) => q(),
383 }
384 }
385
386 pub fn select_fallback(&self) -> MonitorInfo {
388 match self {
389 MonitorQuery::ParentOrPrimary => Self::parent_or_primary_query(WINDOW.id()),
390 MonitorQuery::Primary => Self::primary_query(),
391 MonitorQuery::Query(q) => q().or_else(Self::primary_query),
392 }
393 .unwrap_or_else(Self::fallback)
394 }
395
396 fn fallback() -> MonitorInfo {
397 let mut best = MonitorInfo::fallback();
398 let mut best_area = Px(0);
399 for m in MONITORS.available_monitors() {
400 let m_area = m.px_rect().area();
401 if m_area > best_area {
402 best = m;
403 best_area = m_area;
404 }
405 }
406 best
407 }
408
409 fn parent_or_primary_query(win_id: WindowId) -> Option<MonitorInfo> {
410 if let Some(parent) = WINDOWS.vars(win_id).unwrap().parent().get() {
411 if let Ok(w) = WINDOWS.vars(parent) {
412 return if let Some(monitor) = w.actual_monitor().get() {
413 MONITORS.monitor(monitor)
414 } else {
415 w.monitor().get().select_for(parent)
416 };
417 }
418 }
419 MONITORS.primary_monitor()
420 }
421
422 fn primary_query() -> Option<MonitorInfo> {
423 MONITORS.primary_monitor()
424 }
425}
426impl PartialEq for MonitorQuery {
427 fn eq(&self, other: &Self) -> bool {
429 matches!((self, other), (Self::Primary, Self::Primary))
430 }
431}
432
433event_args! {
434 pub struct MonitorsChangedArgs {
436 pub removed: Vec<MonitorId>,
438
439 pub added: Vec<MonitorId>,
443
444 pub modified: Vec<MonitorId>,
448
449 ..
450
451 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
453 list.search_all()
454 }
455 }
456}
457
458event! {
459 pub static MONITORS_CHANGED_EVENT: MonitorsChangedArgs;
461}