wayland_protocols_async/zwlr_output_management_v1/
handler.rs

1use std::collections::HashMap;
2
3use tokio::{sync::{mpsc::{Sender, Receiver}, oneshot}, io::unix::AsyncFd};
4use wayland_client::{EventQueue, protocol::{wl_seat::{WlSeat, self}, wl_registry::{WlRegistry, self}, wl_output::{WlOutput, self, Transform}}, globals::{self, GlobalListContents}, Connection, QueueHandle, Dispatch, backend::{ObjectId, protocol::WEnumError}};
5use wayland_protocols_wlr::output_management::v1::client::{zwlr_output_manager_v1::ZwlrOutputManagerV1, zwlr_output_configuration_v1::ZwlrOutputConfigurationV1, zwlr_output_head_v1::AdaptiveSyncState, zwlr_output_configuration_head_v1::ZwlrOutputConfigurationHeadV1};
6use super::errors::{OutputManagementHandlerError, OutputManagementHandlerErrorCodes};
7use crate::zwlr_output_management_v1::output::{output_head::WlOutputHead, output_mode::WlOutputMode};
8
9#[derive(Debug)]
10pub enum OutputManagementMessage {
11    GetHeads { reply_to: oneshot::Sender<Vec<OutputHeadMeta>> },
12    GetHead { id: ObjectId, reply_to: oneshot::Sender<Result<OutputHeadMeta, OutputManagementHandlerError>> },
13    GetMode { id: ObjectId, reply_to: oneshot::Sender<Result<OutputModeMeta, OutputManagementHandlerError>> },
14    EnableHead { id: ObjectId, reply_to: oneshot::Sender<Result<bool, OutputManagementHandlerError>> },
15    DisableHead { id: ObjectId, reply_to: oneshot::Sender<Result<bool, OutputManagementHandlerError>> },
16    SetMode { head_id: ObjectId, mode_id: ObjectId, reply_to: oneshot::Sender<Result<bool, OutputManagementHandlerError>> },
17    SetCustomMode { head_id: ObjectId, width: i32, height: i32, refresh: i32, reply_to: oneshot::Sender<Result<bool, OutputManagementHandlerError>> },
18    SetPosition { head_id: ObjectId, x: i32, y: i32, reply_to: oneshot::Sender<Result<bool, OutputManagementHandlerError>> },
19    SetTransform { head_id: ObjectId, transform: Transform, reply_to: oneshot::Sender<Result<bool, OutputManagementHandlerError>> },
20    SetScale { head_id: ObjectId, scale: f64, reply_to: oneshot::Sender<Result<bool, OutputManagementHandlerError>> },
21    SetAdaptiveSync { head_id: ObjectId, enable: bool, reply_to: oneshot::Sender<Result<bool, OutputManagementHandlerError>> },
22}
23
24#[derive(Debug)]
25pub enum OutputManagementEvent {
26    Head { id: ObjectId },
27    Done { serial: u32 },
28    Finished,
29    HeadName { id: ObjectId, name: String },
30    HeadDescription { id: ObjectId, description: String },
31    HeadPhysicalSize { id: ObjectId, height: i32, width: i32 },
32    HeadMode { id: ObjectId, mode_id: ObjectId },
33    HeadEnabled { id: ObjectId, enabled: bool },
34    HeadCurrentMode { id: ObjectId, mode_id: ObjectId },
35    HeadPosition { id: ObjectId, x: i32, y: i32 },
36    HeadTransform { id: ObjectId, transform: Result<Transform, WEnumError> },
37    HeadScale { id: ObjectId, scale: f64 },
38    HeadFinished { id: ObjectId },
39    HeadMake { id: ObjectId, make: String },
40    HeadModel { id: ObjectId, model: String },
41    HeadSerialNumber { id: ObjectId, serial_number: String },
42    HeadAdaptiveSync { id: ObjectId, state: Result<AdaptiveSyncState, WEnumError> },
43    ModeSize { height: i32, width: i32 },
44    ModeRefresh { refresh: i32 },
45    ModePreferred,
46    ModeFinished,
47    ConfigurationSucceeded,
48    ConfigurationFailed,
49    ConfigurationCancelled,
50}
51
52#[derive(Debug, Clone)]
53pub struct OutputHeadMeta {
54    pub id: ObjectId,
55    pub adaptive_sync: Option<AdaptiveSyncState>,
56    pub current_mode: Option<ObjectId>,
57    pub description: String,
58    pub enabled: bool,
59    pub height: i32,
60    pub make: String,
61    pub model: String,
62    pub modes: Vec<ObjectId>,
63    pub name: String,
64    pub pos_x: i32,
65    pub pos_y: i32,
66    pub scale: f64,
67    pub transform: Option<Transform>,
68    pub width: i32,
69}
70
71#[derive(Debug, Clone)]
72
73pub struct OutputModeMeta {
74    pub id: ObjectId,
75    pub width: i32,
76    pub height: i32,
77    pub refresh: i32,
78    pub preferred: bool,
79}
80
81pub struct OutputManagementState {
82    qh: QueueHandle<OutputManagementState>,
83    event_tx: Sender<OutputManagementEvent>,
84    pub output_manager: ZwlrOutputManagerV1,
85    pub output_manager_serial: Option<u32>,
86    pub output_heads: HashMap<ObjectId, WlOutputHead>,
87    pub output_modes: HashMap<ObjectId, WlOutputMode>,
88    pub output_configuration: Option<ZwlrOutputConfigurationV1>,
89    pub mode_to_head_ids: HashMap<ObjectId, ObjectId>,
90}
91
92pub struct OutputManagementHandler {
93    event_queue: EventQueue<OutputManagementState>,
94    state: OutputManagementState,
95}
96
97impl OutputManagementHandler {
98    pub fn new(event_tx: Sender<OutputManagementEvent>) -> Self {
99        let conn = wayland_client::Connection::connect_to_env()
100            .map_err(|_| "could not connect to wayland socket, try setting WAYLAND_DISPLAY.")
101            .unwrap();
102        let (globals, mut event_queue) = globals::registry_queue_init::<OutputManagementState>(&conn).unwrap();
103        let qh = event_queue.handle();
104
105        let output_manager = globals
106            .bind::<ZwlrOutputManagerV1, _, _>(&qh, core::ops::RangeInclusive::new(4, 4), ())
107            .map_err(|_| "compositor does not implement output manager (v4).").unwrap();
108
109        let mut state = OutputManagementState {
110            event_tx,
111            qh,
112            output_manager,
113            output_manager_serial: None,
114            output_heads: HashMap::new(),
115            output_modes: HashMap::new(),
116            mode_to_head_ids: HashMap::new(),
117            output_configuration: None,
118        };
119
120        event_queue.roundtrip(&mut state).unwrap();
121    
122        OutputManagementHandler {
123            event_queue,
124            state,
125        }
126    }
127
128    pub async fn run(&mut self, mut msg_rx: Receiver<OutputManagementMessage>) {
129        let event_queue = &mut self.event_queue;
130        let mut output_mgmt_state = &mut self.state;
131    
132        loop {
133            // This would be required if other threads were reading from the socket.
134            event_queue.dispatch_pending(&mut output_mgmt_state).unwrap();
135            let read_guard = event_queue.prepare_read().unwrap();
136            let fd = read_guard.connection_fd();
137            let async_fd = AsyncFd::new(fd).unwrap();
138    
139            tokio::select! {
140                async_guard = async_fd.readable() => {
141                    async_guard.unwrap().clear_ready();
142                    // Drop the async_fd since it's holding a reference to the read_guard,
143                    // which is dropped on read. We don't need to read from it anyways.
144                    std::mem::drop(async_fd);
145                    // This should not block because we already ensured readiness
146                    let event = read_guard.read();
147                    match event {
148                        // There are events but another thread processed them, we don't need to dispatch
149                        Ok(0) => {},
150                        // We have some events
151                        Ok(_) => {
152                            event_queue.dispatch_pending(&mut output_mgmt_state).unwrap();
153                        },
154                        // No events to receive
155                        Err(_) => {}
156                        // Err(e) => eprintln!("error reading event {}", e),
157                    }
158                },
159                msg = msg_rx.recv() => {
160                    if msg.is_none() {
161                        continue;
162                    }
163                    match msg.unwrap() {
164                        OutputManagementMessage::GetHeads { reply_to } => {
165                            let heads = output_mgmt_state.get_heads();
166                            let _ = reply_to.send(heads);
167                        },
168                        OutputManagementMessage::GetHead { id, reply_to } => {
169                            let head = output_mgmt_state.get_head(id);
170                            let _ = reply_to.send(head);
171                        },
172                        OutputManagementMessage::GetMode { id, reply_to } => {
173                            let mode = output_mgmt_state.get_mode(id);
174                            let _ = reply_to.send(mode);
175                        },
176                        OutputManagementMessage::EnableHead { id, reply_to } => {
177                            let res = output_mgmt_state.enable_head(id);
178                            let _ = reply_to.send(res);
179                        },
180                        OutputManagementMessage::DisableHead { id, reply_to } => {
181                            let res = output_mgmt_state.disable_head(id);
182                            let _ = reply_to.send(res);
183                        },
184                        OutputManagementMessage::SetMode { head_id, mode_id, reply_to } => {
185                            let res = output_mgmt_state.set_mode(head_id, mode_id);
186                            let _ = reply_to.send(res);
187                        },
188                        OutputManagementMessage::SetCustomMode { head_id, width, height, refresh, reply_to } => {
189                            let res = output_mgmt_state.set_custom_mode(head_id, width, height, refresh);
190                            let _ = reply_to.send(res);
191                        }
192                        OutputManagementMessage::SetPosition { head_id, x, y, reply_to } => {
193                            let res = output_mgmt_state.set_position(head_id, x, y);
194                            let _ = reply_to.send(res);
195                        },
196                        OutputManagementMessage::SetTransform { head_id, transform, reply_to } => {
197                            let res = output_mgmt_state.set_transform(head_id, transform);
198                            let _ = reply_to.send(res);
199                        },
200                        OutputManagementMessage::SetScale { head_id, scale, reply_to } => {
201                            let res = output_mgmt_state.set_scale(head_id, scale);
202                            let _ = reply_to.send(res);
203                        },
204                        OutputManagementMessage::SetAdaptiveSync { head_id, enable, reply_to } => {
205                            let res = output_mgmt_state.set_adaptive_sync(head_id, enable);
206                            let _ = reply_to.send(res);
207                        }
208                    }
209                }
210            }
211    
212            // Send any new messages to the socket.
213            let _ = event_queue.flush().expect("wayland connection closed");
214        }
215    }
216}
217
218impl OutputManagementState {
219    pub fn dispatch_event(&self, event: OutputManagementEvent) {
220        let tx: Sender<OutputManagementEvent> = self.event_tx.clone();
221        tokio::task::spawn(async move {
222            let _ = tx.send(event).await;
223        });
224    }
225
226    fn generate_config(&self, head_id: &ObjectId) -> Result<(ZwlrOutputConfigurationV1, ZwlrOutputConfigurationHeadV1), OutputManagementHandlerError> {
227        let output_manager = &self.output_manager;
228        let serial = match self.output_manager_serial {
229            Some(s) => s,
230            None => {
231                return Err(OutputManagementHandlerError::new(
232                    OutputManagementHandlerErrorCodes::NoSerialManagerNotReady,
233                    format!("output serial not found, manager not ready for configuration")
234                ))
235            },
236        };
237        let head = match self.output_heads.get(head_id) {
238            Some(h) => h,
239            None => {
240                return Err(OutputManagementHandlerError::new(
241                    OutputManagementHandlerErrorCodes::HeadNotFoundError,
242                    format!("output head not found")
243                ))
244            }
245        };
246
247        let configuration = output_manager.create_configuration(serial, &self.qh, ());
248        let head_config = configuration.enable_head(&head.output_head, &self.qh, ());
249        
250        Ok((configuration, head_config))
251    }
252
253    fn get_heads(&self) -> Vec<OutputHeadMeta> {
254        let mut output_head_meta: Vec<OutputHeadMeta> = vec![];
255        for (key, wl_output) in self.output_heads.iter() {
256            output_head_meta.push(OutputHeadMeta {
257                id: key.clone(),
258                adaptive_sync: wl_output.adaptive_sync,
259                current_mode: wl_output.current_mode.clone(),
260                description: wl_output.description.clone(),
261                enabled: wl_output.enabled,
262                height: wl_output.height,
263                make: wl_output.make.clone(),
264                model: wl_output.model.clone(),
265                modes: wl_output.modes.clone(),
266                name: wl_output.name.clone(),
267                pos_x: wl_output.pos_x,
268                pos_y: wl_output.pos_y,
269                scale: wl_output.scale,
270                transform: wl_output.transform,
271                width: wl_output.width,
272            })
273        }
274        output_head_meta
275    }
276
277    fn get_head(&self, head_id: ObjectId) -> Result<OutputHeadMeta, OutputManagementHandlerError> {
278        match &self.output_heads.get(&head_id) {
279            Some(wl_output) => Ok(OutputHeadMeta {
280                id: head_id.clone(),
281                adaptive_sync: wl_output.adaptive_sync,
282                current_mode: wl_output.current_mode.clone(),
283                description: wl_output.description.clone(),
284                enabled: wl_output.enabled,
285                height: wl_output.height,
286                make: wl_output.make.clone(),
287                model: wl_output.model.clone(),
288                modes: wl_output.modes.clone(),
289                name: wl_output.name.clone(),
290                pos_x: wl_output.pos_x,
291                pos_y: wl_output.pos_y,
292                scale: wl_output.scale,
293                transform: wl_output.transform,
294                width: wl_output.width,
295            }),
296            None => Err(OutputManagementHandlerError::new(
297                OutputManagementHandlerErrorCodes::HeadNotFoundError,
298                format!("output head not found")
299            )),
300        }
301    }
302
303    fn get_mode(&self, mode_id: ObjectId) -> Result<OutputModeMeta, OutputManagementHandlerError> {
304        match &self.output_modes.get(&mode_id) {
305            Some(wl_output_mode) => Ok(OutputModeMeta {
306                id: mode_id.clone(),
307                width: wl_output_mode.width,
308                height: wl_output_mode.height,
309                refresh: wl_output_mode.refresh,
310                preferred: wl_output_mode.preferred,
311            }),
312            None => Err(OutputManagementHandlerError::new(
313                OutputManagementHandlerErrorCodes::ModeNotFoundError,
314                format!("output mode not found")
315            )),
316        }
317    }
318
319    pub fn enable_head(&self, head_id: ObjectId) -> Result<bool, OutputManagementHandlerError> {
320        let output_manager = &self.output_manager;
321        let serial = match self.output_manager_serial {
322            Some(s) => s,
323            None => {
324                return Err(OutputManagementHandlerError::new(
325                    OutputManagementHandlerErrorCodes::NoSerialManagerNotReady,
326                    format!("output serial not found, manager not ready for configuration")
327                ))
328            },
329        };
330
331        if self.output_heads.get(&head_id).is_none() {
332            return Err(OutputManagementHandlerError::new(
333                OutputManagementHandlerErrorCodes::HeadNotFoundError,
334                format!("output head not found")
335            ));
336        }
337
338        let head = match self.output_heads.get(&head_id) {
339            Some(h) => h,
340            None => {
341                return Err(OutputManagementHandlerError::new(
342                    OutputManagementHandlerErrorCodes::HeadNotFoundError,
343                    format!("output head not found")
344                ))
345            }
346        };
347
348        let configuration = output_manager.create_configuration(serial, &self.qh, ());
349        let head_config = configuration.enable_head(&head.output_head, &self.qh, ());
350        
351        configuration.apply();
352
353        Ok(true)
354    }
355
356    pub fn disable_head(&self, head_id: ObjectId) -> Result<bool, OutputManagementHandlerError> {
357        let output_manager = &self.output_manager;
358        let serial = match self.output_manager_serial {
359            Some(s) => s,
360            None => {
361                return Err(OutputManagementHandlerError::new(
362                    OutputManagementHandlerErrorCodes::NoSerialManagerNotReady,
363                    format!("output serial not found, manager not ready for configuration")
364                ))
365            },
366        };
367
368        let head = match self.output_heads.get(&head_id) {
369            Some(h) => h,
370            None => {
371                return Err(OutputManagementHandlerError::new(
372                    OutputManagementHandlerErrorCodes::HeadNotFoundError,
373                    format!("output head not found")
374                ))
375            }
376        };
377
378        let head = self.output_heads.get(&head_id).unwrap();
379        let configuration = output_manager.create_configuration(serial, &self.qh, ());
380
381        configuration.disable_head(&head.output_head);
382        configuration.apply();
383
384        Ok(true)
385    }
386
387    pub fn set_mode(&self, head_id: ObjectId, mode_id: ObjectId) -> Result<bool, OutputManagementHandlerError> {
388        let (configuration, head_config) = match self.generate_config(&head_id) {
389            Ok((c, h)) => (c, h),
390            Err(e) => {
391                return Err(e);
392            },
393        };
394
395        let mode = match self.output_modes.get(&mode_id) {
396            Some(h) => h,
397            None => {
398                return Err(OutputManagementHandlerError::new(
399                    OutputManagementHandlerErrorCodes::ModeNotFoundError,
400                    format!("output mode not found")
401                ))
402            }
403        };
404
405        head_config.set_mode(&mode.wlr_mode);
406        configuration.apply();
407
408        Ok(true)
409    }
410
411    pub fn set_custom_mode(&self, head_id: ObjectId, width: i32, height: i32, refresh: i32) -> Result<bool, OutputManagementHandlerError> {
412        let (configuration, head_config) = match self.generate_config(&head_id) {
413            Ok((c, h)) => (c, h),
414            Err(e) => {
415                return Err(e);
416            },
417        };
418        head_config.set_custom_mode(width, height, refresh);
419        configuration.apply();
420        Ok(true)
421    }
422
423    pub fn set_transform(&self, head_id: ObjectId, transform: Transform) -> Result<bool, OutputManagementHandlerError> {
424        let (configuration, head_config) = match self.generate_config(&head_id) {
425            Ok((c, h)) => (c, h),
426            Err(e) => {
427                return Err(e);
428            },
429        };
430        head_config.set_transform(transform);
431        configuration.apply();
432        Ok(true)
433    }
434
435    pub fn set_position(&self, head_id: ObjectId, x: i32, y: i32) -> Result<bool, OutputManagementHandlerError> {
436        let (configuration, head_config) = match self.generate_config(&head_id) {
437            Ok((c, h)) => (c, h),
438            Err(e) => {
439                return Err(e);
440            },
441        };
442        head_config.set_position(x, y);
443        configuration.apply();
444        Ok(true)
445    }
446
447    pub fn set_scale(&self, head_id: ObjectId, scale: f64) -> Result<bool, OutputManagementHandlerError> {
448        let (configuration, head_config) = match self.generate_config(&head_id) {
449            Ok((c, h)) => (c, h),
450            Err(e) => {
451                return Err(e);
452            },
453        };
454        head_config.set_scale(scale);
455        configuration.apply();
456        Ok(true)
457    }
458
459    pub fn set_adaptive_sync(&self, head_id: ObjectId, enable: bool) -> Result<bool, OutputManagementHandlerError> {
460        let (configuration, head_config) = match self.generate_config(&head_id) {
461            Ok((c, h)) => (c, h),
462            Err(e) => {
463                return Err(e);
464            },
465        };
466        let _ = match enable {
467            true => head_config.set_adaptive_sync(AdaptiveSyncState::Enabled),
468            false => head_config.set_adaptive_sync(AdaptiveSyncState::Disabled),
469        };
470        configuration.apply();
471        Ok(true)
472    }
473
474    pub(crate) fn remove_mode(&mut self, id: &ObjectId) -> Result<(), OutputManagementHandlerError> {
475        let head_id = match self
476            .mode_to_head_ids
477            .remove(&id) {
478                Some(h) => h,
479                None => {
480                    return Err(OutputManagementHandlerError::new(
481                        OutputManagementHandlerErrorCodes::RemoveModeError,
482                        format!("remove mode failed, maybe mode not found")
483                    ))
484                },
485            };
486
487        let head = match self.output_heads.get_mut(&head_id) {
488            Some(h) => h,
489            None => {
490                return Err(OutputManagementHandlerError::new(
491                    OutputManagementHandlerErrorCodes::HeadNotFoundError,
492                    format!("head not found")
493                ));
494            },
495        };
496
497        if let Some(mode_id) = &head.current_mode {
498            if mode_id == id {
499                head.current_mode = None;
500            }
501        }
502
503        head.modes.retain(|e| e != id);
504
505        Ok(())
506    }
507}
508
509impl Dispatch<WlRegistry, GlobalListContents> for OutputManagementState {
510    fn event(
511        _: &mut Self,
512        registry: &wl_registry::WlRegistry,
513        event: wl_registry::Event,
514        _: &GlobalListContents,
515        _: &Connection,
516        qh: &QueueHandle<OutputManagementState>,
517    ) {}
518}