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