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 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 std::mem::drop(async_fd);
145 let event = read_guard.read();
147 match event {
148 Ok(0) => {},
150 Ok(_) => {
152 event_queue.dispatch_pending(&mut output_mgmt_state).unwrap();
153 },
154 Err(_) => {}
156 }
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 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}