diff options
| author | Merlijn <32853531+ToxicMushroom@users.noreply.github.com> | 2025-10-29 07:10:38 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-29 09:10:38 +0300 |
| commit | 6a2c6261df130cccb5262eddf71d40b2fffcf8f9 (patch) | |
| tree | 48639aef4ebddbc315234b925954c5cc768d0f1c /src/protocols | |
| parent | e6f3c538da0c646bda43fcde7ef7dc3b771e0c8b (diff) | |
| download | niri-6a2c6261df130cccb5262eddf71d40b2fffcf8f9.tar.gz niri-6a2c6261df130cccb5262eddf71d40b2fffcf8f9.tar.bz2 niri-6a2c6261df130cccb5262eddf71d40b2fffcf8f9.zip | |
Add support for custom modes and modelines. (#2479)
* Implement custom modes and modelines
Co-authored-by: ToxicMushroom <32853531+ToxicMushroom@users.noreply.github.com>
* fixes
* refactor mode and modeline kdl parsers.
* add IPC parse checks
* refactor: address feedback
* fix: add missing > 0 refresh rate check
* move things around
* fixes
* wiki fixes
---------
Co-authored-by: Christian Meissl <meissl.christian@gmail.com>
Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
Diffstat (limited to 'src/protocols')
| -rw-r--r-- | src/protocols/output_management.rs | 88 |
1 files changed, 57 insertions, 31 deletions
diff --git a/src/protocols/output_management.rs b/src/protocols/output_management.rs index 4f1ac8c0..2b45588f 100644 --- a/src/protocols/output_management.rs +++ b/src/protocols/output_management.rs @@ -110,19 +110,47 @@ impl OutputManagementManagerState { } } - // TTY outputs can't change modes I think, however, winit and virtual outputs can. + // Winit and virtual outputs can change modes; on a TTY custom modes can add/remove + // a mode. let modes_changed = old.modes != conf.modes; if modes_changed { changed = true; - if old.modes.len() != conf.modes.len() { - error!("output's old mode count doesn't match new modes"); - } else { - for client in self.clients.values() { - if let Some((_, modes)) = client.heads.get(output) { - for (wl_mode, mode) in zip(modes, &conf.modes) { - wl_mode.size(i32::from(mode.width), i32::from(mode.height)); - if let Ok(refresh_rate) = mode.refresh_rate.try_into() { - wl_mode.refresh(refresh_rate); + for client in self.clients.values_mut() { + if let Some((head, modes)) = client.heads.get_mut(output) { + // Ends on the shortest iterator. + let zwlr_modes_with_modes = zip(modes.iter(), &conf.modes); + let least_modes_len = zwlr_modes_with_modes.len(); + + for (wl_mode, mode) in zwlr_modes_with_modes { + wl_mode.size(i32::from(mode.width), i32::from(mode.height)); + if let Ok(refresh_rate) = mode.refresh_rate.try_into() { + wl_mode.refresh(refresh_rate); + } + } + + if let Some(client) = client.manager.client() { + if conf.modes.len() > least_modes_len { + for mode in &conf.modes[least_modes_len..] { + // One or more modes were added. + let new_mode = client + .create_resource::<ZwlrOutputModeV1, _, State>( + &self.display, + head.version(), + (), + ) + .unwrap(); + head.mode(&new_mode); + new_mode + .size(i32::from(mode.width), i32::from(mode.height)); + if let Ok(refresh_rate) = mode.refresh_rate.try_into() { + new_mode.refresh(refresh_rate) + } + modes.push(new_mode); + } + } else if modes.len() > least_modes_len { + // One or more modes were removed. + for mode in modes.drain(least_modes_len..) { + mode.finished(); } } } @@ -619,18 +647,21 @@ where return; }; - new_config.mode = Some(niri_ipc::ConfiguredMode { - width: mode.width, - height: mode.height, - refresh: Some(mode.refresh_rate as f64 / 1000.), + new_config.mode = Some(niri_config::output::Mode { + custom: false, + mode: niri_ipc::ConfiguredMode { + width: mode.width, + height: mode.height, + refresh: Some(mode.refresh_rate as f64 / 1000.), + }, }); + new_config.modeline = None; } zwlr_output_configuration_head_v1::Request::SetCustomMode { width, height, refresh, } => { - // FIXME: Support custom mode let (width, height, refresh): (u16, u16, u32) = match (width.try_into(), height.try_into(), refresh.try_into()) { (Ok(width), Ok(height), Ok(refresh)) => (width, height, refresh), @@ -640,25 +671,20 @@ where } }; - let Some(current_config) = g_state.current_state.get(output_id) else { - warn!("SetMode: output missing from the current config"); - return; - }; - - let Some(mode) = current_config.modes.iter().find(|m| { - m.width == width - && m.height == height - && (refresh == 0 || m.refresh_rate == refresh) - }) else { - warn!("SetCustomMode: no matching mode"); + if refresh == 0 { + warn!("SetCustomMode: refresh 0 requested, ignoring"); return; - }; + } - new_config.mode = Some(niri_ipc::ConfiguredMode { - width: mode.width, - height: mode.height, - refresh: Some(mode.refresh_rate as f64 / 1000.), + new_config.mode = Some(niri_config::output::Mode { + custom: true, + mode: niri_ipc::ConfiguredMode { + width, + height, + refresh: Some(refresh as f64 / 1000.), + }, }); + new_config.modeline = None; } zwlr_output_configuration_head_v1::Request::SetPosition { x, y } => { new_config.position = Some(niri_config::Position { x, y }); |
