aboutsummaryrefslogtreecommitdiff
path: root/src/protocols
diff options
context:
space:
mode:
authorMerlijn <32853531+ToxicMushroom@users.noreply.github.com>2025-10-29 07:10:38 +0100
committerGitHub <noreply@github.com>2025-10-29 09:10:38 +0300
commit6a2c6261df130cccb5262eddf71d40b2fffcf8f9 (patch)
tree48639aef4ebddbc315234b925954c5cc768d0f1c /src/protocols
parente6f3c538da0c646bda43fcde7ef7dc3b771e0c8b (diff)
downloadniri-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.rs88
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 });