1 | use std::{ |
2 | any::Any, |
3 | fmt::{self, Display, Formatter}, |
4 | sync::{Arc, Mutex, Weak}, |
5 | }; |
6 | |
7 | use log::warn; |
8 | use wayland_client::{ |
9 | globals::GlobalList, |
10 | protocol::wl_output::{self, Subpixel, Transform}, |
11 | Connection, Dispatch, Proxy, QueueHandle, WEnum, |
12 | }; |
13 | use wayland_protocols::xdg::xdg_output::zv1::client::{ |
14 | zxdg_output_manager_v1::{self, ZxdgOutputManagerV1}, |
15 | zxdg_output_v1, |
16 | }; |
17 | |
18 | use crate::{ |
19 | globals::GlobalData, |
20 | registry::{GlobalProxy, ProvidesRegistryState, RegistryHandler}, |
21 | }; |
22 | |
23 | /// Simplified event handler for [`wl_output::WlOutput`]. |
24 | /// See [`OutputState`]. |
25 | pub trait OutputHandler: Sized { |
26 | fn output_state(&mut self) -> &mut OutputState; |
27 | |
28 | /// A new output has been advertised. |
29 | fn new_output( |
30 | &mut self, |
31 | conn: &Connection, |
32 | qh: &QueueHandle<Self>, |
33 | output: wl_output::WlOutput, |
34 | ); |
35 | |
36 | /// An existing output has changed. |
37 | fn update_output( |
38 | &mut self, |
39 | conn: &Connection, |
40 | qh: &QueueHandle<Self>, |
41 | output: wl_output::WlOutput, |
42 | ); |
43 | |
44 | /// An output is no longer advertised. |
45 | /// |
46 | /// The info passed to this function was the state of the output before destruction. |
47 | fn output_destroyed( |
48 | &mut self, |
49 | conn: &Connection, |
50 | qh: &QueueHandle<Self>, |
51 | output: wl_output::WlOutput, |
52 | ); |
53 | } |
54 | |
55 | type ScaleWatcherFn = |
56 | dyn Fn(&mut dyn Any, &Connection, &dyn Any, &wl_output::WlOutput) + Send + Sync; |
57 | |
58 | /// A handler for delegating [`wl_output::WlOutput`](wayland_client::protocol::wl_output::WlOutput). |
59 | /// |
60 | /// When implementing [`ProvidesRegistryState`], |
61 | /// [`registry_handlers!`](crate::registry_handlers) may be used to delegate all |
62 | /// output events to an instance of this type. It will internally store the internal state of all |
63 | /// outputs and allow querying them via the `OutputState::outputs` and `OutputState::info` methods. |
64 | /// |
65 | /// ## Example |
66 | /// |
67 | /// ``` |
68 | /// use smithay_client_toolkit::output::{OutputHandler,OutputState}; |
69 | /// use smithay_client_toolkit::registry::{ProvidesRegistryState,RegistryHandler}; |
70 | /// # use smithay_client_toolkit::registry::RegistryState; |
71 | /// use smithay_client_toolkit::{registry_handlers,delegate_output, delegate_registry}; |
72 | /// use wayland_client::{Connection,QueueHandle,protocol::wl_output}; |
73 | /// |
74 | /// struct ExampleState { |
75 | /// # registry_state: RegistryState, |
76 | /// // The state is usually kept as an attribute of the application state. |
77 | /// output_state: OutputState, |
78 | /// } |
79 | /// |
80 | /// // When using `OutputState`, an implementation of `OutputHandler` should be supplied: |
81 | /// impl OutputHandler for ExampleState { |
82 | /// // Custom implementation handling output events here ... |
83 | /// # fn output_state(&mut self) -> &mut OutputState { |
84 | /// # &mut self.output_state |
85 | /// # } |
86 | /// # |
87 | /// # fn new_output(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_output::WlOutput) { |
88 | /// # } |
89 | /// # |
90 | /// # fn update_output(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_output::WlOutput) { |
91 | /// # } |
92 | /// # |
93 | /// # fn output_destroyed(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_output::WlOutput) { |
94 | /// # } |
95 | /// } |
96 | /// |
97 | /// // Delegating to the registry is required to use `OutputState`. |
98 | /// delegate_registry!(ExampleState); |
99 | /// delegate_output!(ExampleState); |
100 | /// |
101 | /// impl ProvidesRegistryState for ExampleState { |
102 | /// # fn registry(&mut self) -> &mut RegistryState { |
103 | /// # &mut self.registry_state |
104 | /// # } |
105 | /// // ... |
106 | /// |
107 | /// registry_handlers!(OutputState); |
108 | /// } |
109 | /// ``` |
110 | pub struct OutputState { |
111 | xdg: GlobalProxy<ZxdgOutputManagerV1>, |
112 | outputs: Vec<OutputInner>, |
113 | callbacks: Vec<Weak<ScaleWatcherFn>>, |
114 | } |
115 | |
116 | impl fmt::Debug for OutputState { |
117 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
118 | fmt&mut DebugStruct<'_, '_>.debug_struct("OutputState" ) |
119 | .field("xdg" , &self.xdg) |
120 | .field("outputs" , &self.outputs) |
121 | .field(name:"callbacks" , &self.callbacks.len()) |
122 | .finish() |
123 | } |
124 | } |
125 | |
126 | pub struct ScaleWatcherHandle(Arc<ScaleWatcherFn>); |
127 | |
128 | impl fmt::Debug for ScaleWatcherHandle { |
129 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
130 | fmt.debug_struct(name:"ScaleWatcherHandle" ).finish_non_exhaustive() |
131 | } |
132 | } |
133 | |
134 | impl OutputState { |
135 | pub fn new< |
136 | D: Dispatch<wl_output::WlOutput, OutputData> |
137 | + Dispatch<zxdg_output_v1::ZxdgOutputV1, OutputData> |
138 | + Dispatch<zxdg_output_manager_v1::ZxdgOutputManagerV1, GlobalData> |
139 | + 'static, |
140 | >( |
141 | global_list: &GlobalList, |
142 | qh: &QueueHandle<D>, |
143 | ) -> OutputState { |
144 | let (outputs, xdg) = global_list.contents().with_list(|globals| { |
145 | let outputs: Vec<wl_output::WlOutput> = crate::registry::bind_all( |
146 | global_list.registry(), |
147 | globals, |
148 | qh, |
149 | 1..=4, |
150 | OutputData::new, |
151 | ) |
152 | .expect("Failed to bind global" ); |
153 | let xdg = |
154 | crate::registry::bind_one(global_list.registry(), globals, qh, 1..=3, GlobalData) |
155 | .into(); |
156 | (outputs, xdg) |
157 | }); |
158 | |
159 | let mut output_state = OutputState { xdg, outputs: vec![], callbacks: vec![] }; |
160 | for wl_output in outputs { |
161 | output_state.setup(wl_output, qh); |
162 | } |
163 | output_state |
164 | } |
165 | |
166 | /// Returns an iterator over all outputs. |
167 | pub fn outputs(&self) -> impl Iterator<Item = wl_output::WlOutput> { |
168 | self.outputs.iter().map(|output| &output.wl_output).cloned().collect::<Vec<_>>().into_iter() |
169 | } |
170 | |
171 | /// Returns information about an output. |
172 | /// |
173 | /// This may be none if the output has been destroyed or the compositor has not sent information about the |
174 | /// output yet. |
175 | pub fn info(&self, output: &wl_output::WlOutput) -> Option<OutputInfo> { |
176 | self.outputs |
177 | .iter() |
178 | .find(|inner| &inner.wl_output == output) |
179 | .and_then(|inner| inner.current_info.clone()) |
180 | } |
181 | |
182 | pub fn add_scale_watcher<F, D>(data: &mut D, f: F) -> ScaleWatcherHandle |
183 | where |
184 | D: OutputHandler + 'static, |
185 | F: Fn(&mut D, &Connection, &QueueHandle<D>, &wl_output::WlOutput) + Send + Sync + 'static, |
186 | { |
187 | let state = data.output_state(); |
188 | let rv = ScaleWatcherHandle(Arc::new(move |data, conn, qh, output| { |
189 | if let (Some(data), Some(qh)) = (data.downcast_mut(), qh.downcast_ref()) { |
190 | f(data, conn, qh, output); |
191 | } |
192 | })); |
193 | state.callbacks.retain(|f| f.upgrade().is_some()); |
194 | state.callbacks.push(Arc::downgrade(&rv.0)); |
195 | rv |
196 | } |
197 | |
198 | fn setup<D>(&mut self, wl_output: wl_output::WlOutput, qh: &QueueHandle<D>) |
199 | where |
200 | D: Dispatch<zxdg_output_v1::ZxdgOutputV1, OutputData> + 'static, |
201 | { |
202 | let data = wl_output.data::<OutputData>().unwrap().clone(); |
203 | |
204 | let pending_info = data.0.lock().unwrap().clone(); |
205 | let name = pending_info.id; |
206 | |
207 | let version = wl_output.version(); |
208 | let pending_xdg = self.xdg.get().is_ok(); |
209 | |
210 | let xdg_output = if pending_xdg { |
211 | let xdg = self.xdg.get().unwrap(); |
212 | |
213 | Some(xdg.get_xdg_output(&wl_output, qh, data)) |
214 | } else { |
215 | None |
216 | }; |
217 | |
218 | let inner = OutputInner { |
219 | name, |
220 | wl_output, |
221 | xdg_output, |
222 | just_created: true, |
223 | // wl_output::done was added in version 2. |
224 | // If we have an output at version 1, assume the data was already sent. |
225 | current_info: if version > 1 { None } else { Some(OutputInfo::new(name)) }, |
226 | |
227 | pending_info, |
228 | pending_wl: true, |
229 | pending_xdg, |
230 | }; |
231 | |
232 | self.outputs.push(inner); |
233 | } |
234 | } |
235 | |
236 | #[derive (Debug, Clone)] |
237 | pub struct OutputData(Arc<Mutex<OutputInfo>>); |
238 | |
239 | impl OutputData { |
240 | pub fn new(name: u32) -> OutputData { |
241 | OutputData(Arc::new(Mutex::new(OutputInfo::new(name)))) |
242 | } |
243 | |
244 | /// Get the output scale factor. |
245 | pub fn scale_factor(&self) -> i32 { |
246 | let guard = self.0.lock().unwrap(); |
247 | |
248 | guard.scale_factor |
249 | } |
250 | |
251 | /// Get the output transform. |
252 | pub fn transform(&self) -> wl_output::Transform { |
253 | let guard = self.0.lock().unwrap(); |
254 | |
255 | guard.transform |
256 | } |
257 | |
258 | /// Access the underlying [`OutputInfo`]. |
259 | /// |
260 | /// Reentrant calls within the `callback` will deadlock. |
261 | pub fn with_output_info<T, F: FnOnce(&OutputInfo) -> T>(&self, callback: F) -> T { |
262 | let guard = self.0.lock().unwrap(); |
263 | callback(&guard) |
264 | } |
265 | } |
266 | |
267 | #[derive (Debug, Clone)] |
268 | pub struct Mode { |
269 | /// Number of pixels of this mode in format `(width, height)` |
270 | /// |
271 | /// for example `(1920, 1080)` |
272 | pub dimensions: (i32, i32), |
273 | |
274 | /// Refresh rate for this mode. |
275 | /// |
276 | /// The refresh rate is specified in terms of millihertz (mHz). To convert approximately to Hertz, |
277 | /// divide the value by 1000. |
278 | /// |
279 | /// This value could be zero if an output has no correct refresh rate, such as a virtual output. |
280 | pub refresh_rate: i32, |
281 | |
282 | /// Whether this is the current mode for this output. |
283 | /// |
284 | /// Per the Wayland protocol, non-current modes are deprecated and clients should not rely on deprecated |
285 | /// modes. |
286 | pub current: bool, |
287 | |
288 | /// Whether this is the preferred mode for this output. |
289 | pub preferred: bool, |
290 | } |
291 | |
292 | impl Display for Mode { |
293 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
294 | if self.current { |
295 | write!(f, "(current) " )?; |
296 | } |
297 | |
298 | if self.preferred { |
299 | write!(f, "(preferred) " )?; |
300 | } |
301 | |
302 | write!( |
303 | f, |
304 | " {}× {}px @ {}. {:03} Hz" , |
305 | self.dimensions.0, |
306 | self.dimensions.1, |
307 | // Print the refresh rate in hertz since it is more familiar unit. |
308 | self.refresh_rate / 1000, |
309 | self.refresh_rate % 1000 |
310 | ) |
311 | } |
312 | } |
313 | |
314 | /// Information about an output. |
315 | #[derive (Debug, Clone)] |
316 | #[non_exhaustive ] |
317 | pub struct OutputInfo { |
318 | /// The id of the output. |
319 | /// |
320 | /// This corresponds to the global `name` of the wl_output. |
321 | pub id: u32, |
322 | |
323 | /// The model name of this output as advertised by the server. |
324 | pub model: String, |
325 | |
326 | /// The make name of this output as advertised by the server. |
327 | pub make: String, |
328 | |
329 | /// Location of the top-left corner of this output in compositor space. |
330 | /// |
331 | /// Note that the compositor may decide to always report (0,0) if it decides clients are not allowed to |
332 | /// know this information. |
333 | pub location: (i32, i32), |
334 | |
335 | /// Physical dimensions of this output, in millimeters. |
336 | /// |
337 | /// This value may be set to (0, 0) if a physical size does not make sense for the output (e.g. projectors |
338 | /// and virtual outputs). |
339 | pub physical_size: (i32, i32), |
340 | |
341 | /// The subpixel layout for this output. |
342 | pub subpixel: Subpixel, |
343 | |
344 | /// The current transformation applied to this output |
345 | /// |
346 | /// You can pre-render your buffers taking this information into account and advertising it via |
347 | /// `wl_buffer.set_transform` for better performance. |
348 | pub transform: Transform, |
349 | |
350 | /// The scaling factor of this output |
351 | /// |
352 | /// Any buffer whose scaling factor does not match the one of the output it is displayed on will be |
353 | /// rescaled accordingly. |
354 | /// |
355 | /// For example, a buffer of scaling factor 1 will be doubled in size if the output scaling factor is 2. |
356 | /// |
357 | /// You can pre-render your buffers taking this information into account and advertising it via |
358 | /// `wl_surface.set_buffer_scale` so you may advertise a higher detail image. |
359 | pub scale_factor: i32, |
360 | |
361 | /// Possible modes for an output. |
362 | pub modes: Vec<Mode>, |
363 | |
364 | /// Logical position in global compositor space |
365 | pub logical_position: Option<(i32, i32)>, |
366 | |
367 | /// Logical size in global compositor space |
368 | pub logical_size: Option<(i32, i32)>, |
369 | |
370 | /// The name of the this output as advertised by the surface. |
371 | /// |
372 | /// Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do not assume that the name is a |
373 | /// reflection of an underlying DRM connector, X11 connection, etc. |
374 | /// |
375 | /// Compositors are not required to provide a name for the output and the value may be [`None`]. |
376 | /// |
377 | /// The name will be [`None`] if the compositor does not support version 4 of the wl-output protocol or |
378 | /// version 2 of the zxdg-output-v1 protocol. |
379 | pub name: Option<String>, |
380 | |
381 | /// The description of this output as advertised by the server |
382 | /// |
383 | /// The description is a UTF-8 string with no convention defined for its contents. The description is not |
384 | /// guaranteed to be unique among all wl_output globals. Examples might include 'Foocorp 11" Display' or |
385 | /// 'Virtual X11 output via :1'. |
386 | /// |
387 | /// Compositors are not required to provide a description of the output and the value may be [`None`]. |
388 | /// |
389 | /// The value will be [`None`] if the compositor does not support version 4 of the wl-output |
390 | /// protocol, version 2 of the zxdg-output-v1 protocol. |
391 | pub description: Option<String>, |
392 | } |
393 | |
394 | #[macro_export ] |
395 | macro_rules! delegate_output { |
396 | ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { |
397 | $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ |
398 | $crate::reexports::client::protocol::wl_output::WlOutput: $crate::output::OutputData |
399 | ] => $crate::output::OutputState); |
400 | $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ |
401 | $crate::reexports::protocols::xdg::xdg_output::zv1::client::zxdg_output_manager_v1::ZxdgOutputManagerV1: $crate::globals::GlobalData |
402 | ] => $crate::output::OutputState); |
403 | $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ |
404 | $crate::reexports::protocols::xdg::xdg_output::zv1::client::zxdg_output_v1::ZxdgOutputV1: $crate::output::OutputData |
405 | ] => $crate::output::OutputState); |
406 | }; |
407 | } |
408 | |
409 | impl<D> Dispatch<wl_output::WlOutput, OutputData, D> for OutputState |
410 | where |
411 | D: Dispatch<wl_output::WlOutput, OutputData> + OutputHandler + 'static, |
412 | { |
413 | fn event( |
414 | state: &mut D, |
415 | output: &wl_output::WlOutput, |
416 | event: wl_output::Event, |
417 | data: &OutputData, |
418 | conn: &Connection, |
419 | qh: &QueueHandle<D>, |
420 | ) { |
421 | let inner = match state |
422 | .output_state() |
423 | .outputs |
424 | .iter_mut() |
425 | .find(|inner| &inner.wl_output == output) |
426 | { |
427 | Some(inner) => inner, |
428 | None => { |
429 | warn!("Received {event:?} for dead wl_output" ); |
430 | return; |
431 | } |
432 | }; |
433 | |
434 | match event { |
435 | wl_output::Event::Geometry { |
436 | x, |
437 | y, |
438 | physical_width, |
439 | physical_height, |
440 | subpixel, |
441 | make, |
442 | model, |
443 | transform, |
444 | } => { |
445 | inner.pending_info.location = (x, y); |
446 | inner.pending_info.physical_size = (physical_width, physical_height); |
447 | inner.pending_info.subpixel = match subpixel { |
448 | WEnum::Value(subpixel) => subpixel, |
449 | WEnum::Unknown(_) => todo!("Warn about invalid subpixel value" ), |
450 | }; |
451 | inner.pending_info.make = make; |
452 | inner.pending_info.model = model; |
453 | inner.pending_info.transform = match transform { |
454 | WEnum::Value(subpixel) => subpixel, |
455 | WEnum::Unknown(_) => todo!("Warn about invalid transform value" ), |
456 | }; |
457 | inner.pending_wl = true; |
458 | } |
459 | |
460 | wl_output::Event::Mode { flags, width, height, refresh } => { |
461 | // Remove the old mode |
462 | inner.pending_info.modes.retain(|mode| { |
463 | mode.dimensions != (width, height) || mode.refresh_rate != refresh |
464 | }); |
465 | |
466 | let flags = match flags { |
467 | WEnum::Value(flags) => flags, |
468 | WEnum::Unknown(_) => panic!("Invalid flags" ), |
469 | }; |
470 | |
471 | let current = flags.contains(wl_output::Mode::Current); |
472 | let preferred = flags.contains(wl_output::Mode::Preferred); |
473 | |
474 | // Any mode that isn't current is deprecated, let's deprecate any existing modes that may be |
475 | // marked as current. |
476 | // |
477 | // If a new mode is advertised as preferred, then mark the existing preferred mode as not. |
478 | for mode in &mut inner.pending_info.modes { |
479 | // This mode is no longer preferred. |
480 | if preferred { |
481 | mode.preferred = false; |
482 | } |
483 | |
484 | // This mode is no longer current. |
485 | if current { |
486 | mode.current = false; |
487 | } |
488 | } |
489 | |
490 | // Now create the new mode. |
491 | inner.pending_info.modes.push(Mode { |
492 | dimensions: (width, height), |
493 | refresh_rate: refresh, |
494 | current, |
495 | preferred, |
496 | }); |
497 | |
498 | inner.pending_wl = true; |
499 | } |
500 | |
501 | wl_output::Event::Scale { factor } => { |
502 | inner.pending_info.scale_factor = factor; |
503 | inner.pending_wl = true; |
504 | } |
505 | |
506 | wl_output::Event::Name { name } => { |
507 | inner.pending_info.name = Some(name); |
508 | inner.pending_wl = true; |
509 | } |
510 | |
511 | wl_output::Event::Description { description } => { |
512 | inner.pending_info.description = Some(description); |
513 | inner.pending_wl = true; |
514 | } |
515 | |
516 | wl_output::Event::Done => { |
517 | let info = inner.pending_info.clone(); |
518 | inner.current_info = Some(info.clone()); |
519 | inner.pending_wl = false; |
520 | |
521 | // Set the user data, see if we need to run scale callbacks |
522 | let run_callbacks = data.set(info); |
523 | |
524 | // Don't call `new_output` until we have xdg output info |
525 | if !inner.pending_xdg { |
526 | if inner.just_created { |
527 | inner.just_created = false; |
528 | state.new_output(conn, qh, output.clone()); |
529 | } else { |
530 | state.update_output(conn, qh, output.clone()); |
531 | } |
532 | } |
533 | |
534 | if run_callbacks { |
535 | let callbacks = state.output_state().callbacks.clone(); |
536 | for cb in callbacks { |
537 | if let Some(cb) = cb.upgrade() { |
538 | cb(state, conn, qh, output); |
539 | } |
540 | } |
541 | } |
542 | } |
543 | _ => unreachable!(), |
544 | } |
545 | } |
546 | } |
547 | |
548 | impl<D> Dispatch<zxdg_output_manager_v1::ZxdgOutputManagerV1, GlobalData, D> for OutputState |
549 | where |
550 | D: Dispatch<zxdg_output_manager_v1::ZxdgOutputManagerV1, GlobalData> + OutputHandler, |
551 | { |
552 | fn event( |
553 | _: &mut D, |
554 | _: &zxdg_output_manager_v1::ZxdgOutputManagerV1, |
555 | _: zxdg_output_manager_v1::Event, |
556 | _: &GlobalData, |
557 | _: &Connection, |
558 | _: &QueueHandle<D>, |
559 | ) { |
560 | unreachable!("zxdg_output_manager_v1 has no events" ) |
561 | } |
562 | } |
563 | |
564 | impl<D> Dispatch<zxdg_output_v1::ZxdgOutputV1, OutputData, D> for OutputState |
565 | where |
566 | D: Dispatch<zxdg_output_v1::ZxdgOutputV1, OutputData> + OutputHandler, |
567 | { |
568 | fn event( |
569 | state: &mut D, |
570 | output: &zxdg_output_v1::ZxdgOutputV1, |
571 | event: zxdg_output_v1::Event, |
572 | data: &OutputData, |
573 | conn: &Connection, |
574 | qh: &QueueHandle<D>, |
575 | ) { |
576 | let inner = match state |
577 | .output_state() |
578 | .outputs |
579 | .iter_mut() |
580 | .find(|inner| inner.xdg_output.as_ref() == Some(output)) |
581 | { |
582 | Some(inner) => inner, |
583 | None => { |
584 | warn!("Received {event:?} for dead xdg_output" ); |
585 | return; |
586 | } |
587 | }; |
588 | |
589 | // zxdg_output_v1::done is deprecated in version 3. So we only need |
590 | // to wait for wl_output::done, once we get any xdg output info. |
591 | if output.version() >= 3 { |
592 | inner.pending_xdg = false; |
593 | } |
594 | |
595 | match event { |
596 | zxdg_output_v1::Event::LogicalPosition { x, y } => { |
597 | inner.pending_info.logical_position = Some((x, y)); |
598 | if output.version() < 3 { |
599 | inner.pending_xdg = true; |
600 | } |
601 | } |
602 | zxdg_output_v1::Event::LogicalSize { width, height } => { |
603 | inner.pending_info.logical_size = Some((width, height)); |
604 | if output.version() < 3 { |
605 | inner.pending_xdg = true; |
606 | } |
607 | } |
608 | zxdg_output_v1::Event::Name { name } => { |
609 | if inner.wl_output.version() < 4 { |
610 | inner.pending_info.name = Some(name); |
611 | } |
612 | if output.version() < 3 { |
613 | inner.pending_xdg = true; |
614 | } |
615 | } |
616 | |
617 | zxdg_output_v1::Event::Description { description } => { |
618 | if inner.wl_output.version() < 4 { |
619 | inner.pending_info.description = Some(description); |
620 | } |
621 | if output.version() < 3 { |
622 | inner.pending_xdg = true; |
623 | } |
624 | } |
625 | |
626 | zxdg_output_v1::Event::Done => { |
627 | // This event is deprecated starting in version 3, wl_output::done should be sent instead. |
628 | if output.version() < 3 { |
629 | let info = inner.pending_info.clone(); |
630 | inner.current_info = Some(info.clone()); |
631 | inner.pending_xdg = false; |
632 | |
633 | // Set the user data |
634 | data.set(info); |
635 | |
636 | let pending_wl = inner.pending_wl; |
637 | let just_created = inner.just_created; |
638 | let output = inner.wl_output.clone(); |
639 | |
640 | if just_created { |
641 | inner.just_created = false; |
642 | } |
643 | |
644 | if !pending_wl { |
645 | if just_created { |
646 | state.new_output(conn, qh, output); |
647 | } else { |
648 | state.update_output(conn, qh, output); |
649 | } |
650 | } |
651 | } |
652 | } |
653 | |
654 | _ => unreachable!(), |
655 | } |
656 | } |
657 | } |
658 | |
659 | impl<D> RegistryHandler<D> for OutputState |
660 | where |
661 | D: Dispatch<wl_output::WlOutput, OutputData> |
662 | + Dispatch<zxdg_output_v1::ZxdgOutputV1, OutputData> |
663 | + Dispatch<zxdg_output_manager_v1::ZxdgOutputManagerV1, GlobalData> |
664 | + OutputHandler |
665 | + ProvidesRegistryState |
666 | + 'static, |
667 | { |
668 | fn new_global( |
669 | data: &mut D, |
670 | _: &Connection, |
671 | qh: &QueueHandle<D>, |
672 | name: u32, |
673 | interface: &str, |
674 | _version: u32, |
675 | ) { |
676 | if interface == "wl_output" { |
677 | let output = data |
678 | .registry() |
679 | .bind_specific(qh, name, 1..=4, OutputData::new(name)) |
680 | .expect("Failed to bind global" ); |
681 | data.output_state().setup(output, qh); |
682 | } |
683 | } |
684 | |
685 | fn remove_global( |
686 | data: &mut D, |
687 | conn: &Connection, |
688 | qh: &QueueHandle<D>, |
689 | name: u32, |
690 | interface: &str, |
691 | ) { |
692 | if interface == "wl_output" { |
693 | let output = data |
694 | .output_state() |
695 | .outputs |
696 | .iter() |
697 | .position(|o| o.name == name) |
698 | .expect("Removed non-existing output" ); |
699 | |
700 | let wl_output = data.output_state().outputs[output].wl_output.clone(); |
701 | data.output_destroyed(conn, qh, wl_output); |
702 | |
703 | let output = data.output_state().outputs.remove(output); |
704 | if let Some(xdg_output) = &output.xdg_output { |
705 | xdg_output.destroy(); |
706 | } |
707 | if output.wl_output.version() >= 3 { |
708 | output.wl_output.release(); |
709 | } |
710 | } |
711 | } |
712 | } |
713 | |
714 | impl OutputInfo { |
715 | fn new(id: u32) -> OutputInfo { |
716 | OutputInfo { |
717 | id, |
718 | model: String::new(), |
719 | make: String::new(), |
720 | location: (0, 0), |
721 | physical_size: (0, 0), |
722 | subpixel: Subpixel::Unknown, |
723 | transform: Transform::Normal, |
724 | scale_factor: 1, |
725 | modes: vec![], |
726 | logical_position: None, |
727 | logical_size: None, |
728 | name: None, |
729 | description: None, |
730 | } |
731 | } |
732 | } |
733 | |
734 | impl OutputData { |
735 | pub(crate) fn set(&self, info: OutputInfo) -> bool { |
736 | let mut guard: MutexGuard<'_, OutputInfo> = self.0.lock().unwrap(); |
737 | |
738 | let rv: bool = guard.scale_factor != info.scale_factor; |
739 | |
740 | *guard = info; |
741 | |
742 | rv |
743 | } |
744 | } |
745 | |
746 | #[derive (Debug)] |
747 | struct OutputInner { |
748 | /// The name of the wl_output global. |
749 | name: u32, |
750 | wl_output: wl_output::WlOutput, |
751 | xdg_output: Option<zxdg_output_v1::ZxdgOutputV1>, |
752 | /// Whether this output was just created and has not an event yet. |
753 | just_created: bool, |
754 | |
755 | current_info: Option<OutputInfo>, |
756 | pending_info: OutputInfo, |
757 | pending_wl: bool, |
758 | pending_xdg: bool, |
759 | } |
760 | |