From b14815010118d023081f4f8e99a40fa41a6d06aa Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 20 Nov 2024 18:16:06 +0100 Subject: [PATCH] feat: Add vrr settings to display page --- cosmic-settings/src/pages/display/mod.rs | 129 ++++++++++++++++++++--- i18n/de/cosmic_settings.ftl | 6 ++ i18n/en/cosmic_settings.ftl | 6 ++ 3 files changed, 125 insertions(+), 16 deletions(-) diff --git a/cosmic-settings/src/pages/display/mod.rs b/cosmic-settings/src/pages/display/mod.rs index edee50b..f36fe9a 100644 --- a/cosmic-settings/src/pages/display/mod.rs +++ b/cosmic-settings/src/pages/display/mod.rs @@ -13,7 +13,9 @@ use cosmic::widget::{ }; use cosmic::{Apply, Element, Task}; use cosmic_config::{ConfigGet, ConfigSet}; -use cosmic_randr_shell::{List, Output, OutputKey, Transform}; +use cosmic_randr_shell::{ + AdaptiveSyncAvailability, AdaptiveSyncState, List, Output, OutputKey, Transform, +}; use cosmic_settings_page::{self as page, section, Section}; use once_cell::sync::Lazy; use slab::Slab; @@ -90,6 +92,8 @@ pub enum Message { Refresh, /// Set the refresh rate of a display. RefreshRate(usize), + /// Set the VRR mode of a display. + VariableRefreshRate(usize), /// Set the resolution of a display. Resolution(usize), /// Set the preferred scale for a display. @@ -114,6 +118,7 @@ enum Randr { Mirror(OutputKey), Position(i32, i32), RefreshRate(u32), + VariableRefreshRate(AdaptiveSyncState), Resolution(u32, u32), Scale(u32), Transform(Transform), @@ -181,6 +186,7 @@ struct Config { /// Whether night light is enabled. // night_light_enabled: bool, refresh_rate: Option, + vrr: Option, resolution: Option<(u32, u32)>, scale: u32, } @@ -190,10 +196,12 @@ struct Config { struct ViewCache { modes: BTreeMap<(u32, u32), Vec>, orientations: [String; 4], + vrr_modes: Vec, refresh_rates: Vec, resolutions: Vec, orientation_selected: Option, refresh_rate_selected: Option, + vrr_selected: Option, resolution_selected: Option, scale_selected: Option, } @@ -291,7 +299,7 @@ impl page::Page for Page { #[cfg(feature = "test")] fn on_enter( &mut self, - sender: tokio::sync::mpsc::Sender, + _sender: tokio::sync::mpsc::Sender, ) -> Task { cosmic::task::future(async move { let mut randr = List::default(); @@ -314,6 +322,8 @@ impl page::Page for Page { transform: Some(Transform::Normal), modes: vec![test_mode], current: Some(test_mode), + adaptive_sync: None, + adaptive_sync_availability: None, }); randr.outputs.insert(cosmic_randr_shell::Output { @@ -328,6 +338,8 @@ impl page::Page for Page { transform: Some(Transform::Normal), modes: vec![test_mode], current: Some(test_mode), + adaptive_sync: Some(AdaptiveSyncState::Disabled), + adaptive_sync_availability: Some(AdaptiveSyncAvailability::Supported), }); crate::pages::Message::Displays(Message::Update { @@ -480,6 +492,8 @@ impl Page { Message::RefreshRate(rate) => return self.set_refresh_rate(rate), + Message::VariableRefreshRate(mode) => return self.set_vrr(mode), + Message::Resolution(option) => return self.set_resolution(option), Message::Scale(scale) => return self.set_scale(scale), @@ -613,10 +627,12 @@ impl Page { self.active_display = output_id; self.config.refresh_rate = None; self.config.resolution = None; + self.config.vrr = output.adaptive_sync; self.config.scale = (output.scale * 100.0) as u32; self.cache.modes.clear(); self.cache.refresh_rates.clear(); + self.cache.vrr_modes.clear(); self.cache.resolutions.clear(); self.cache.orientation_selected = match output.transform { Some(Transform::Normal) => Some(0), @@ -627,6 +643,7 @@ impl Page { }; self.cache.resolution_selected = None; self.cache.refresh_rate_selected = None; + self.cache.vrr_selected = None; self.cache.scale_selected = Some( DPI_SCALES @@ -663,6 +680,31 @@ impl Page { } } + if let Some(state) = output.adaptive_sync { + match output.adaptive_sync_availability { + Some(AdaptiveSyncAvailability::Supported) => { + self.cache.vrr_modes = vec![ + fl!("vrr", "force"), + fl!("vrr", "auto"), + fl!("vrr", "disabled"), + ]; + self.cache.vrr_selected = match state { + AdaptiveSyncState::Always => Some(0), + AdaptiveSyncState::Auto => Some(1), + AdaptiveSyncState::Disabled => Some(2), + }; + } + Some(AdaptiveSyncAvailability::RequiresModeset) => { + self.cache.vrr_modes = vec![fl!("vrr", "enabled"), fl!("vrr", "disabled")]; + self.cache.vrr_selected = match state { + AdaptiveSyncState::Always | AdaptiveSyncState::Auto => Some(0), + AdaptiveSyncState::Disabled => Some(1), + }; + } + _ => {} + } + } + self.mirror_menu.clear(); self.mirror_menu.insert(widget::dropdown::multi::list( @@ -791,6 +833,32 @@ impl Page { Task::none() } + /// Changes the variable refresh rate mode of the active display. + pub fn set_vrr(&mut self, option: usize) -> Task { + let Some(output) = self.list.outputs.get(self.active_display) else { + return Task::none(); + }; + + let mode = match output.adaptive_sync_availability { + Some(AdaptiveSyncAvailability::Supported) => match option { + 0 => AdaptiveSyncState::Always, + 1 => AdaptiveSyncState::Auto, + 2 => AdaptiveSyncState::Disabled, + _ => return Task::none(), + }, + Some(AdaptiveSyncAvailability::RequiresModeset) => match option { + 0 => AdaptiveSyncState::Always, + 1 => AdaptiveSyncState::Disabled, + _ => return Task::none(), + }, + _ => return Task::none(), + }; + + self.cache.vrr_selected = Some(option); + self.config.vrr = Some(mode); + self.exec_randr(output, Randr::VariableRefreshRate(mode)) + } + /// Change the resolution of the active display. pub fn set_resolution(&mut self, option: usize) -> Task { let mut tasks = Vec::with_capacity(2); @@ -924,6 +992,19 @@ impl Page { .arg(itoa::Buffer::new().format(current.size.1)); } + Randr::VariableRefreshRate(mode) => { + let Some(current) = output.current.and_then(|id| self.list.modes.get(id)) else { + return Task::none(); + }; + + task.arg("mode") + .arg("--adaptive-sync") + .arg(format!("{}", mode)) + .arg(name) + .arg(itoa::Buffer::new().format(current.size.0)) + .arg(itoa::Buffer::new().format(current.size.1)); + } + Randr::Resolution(width, height) => { task.arg("mode") .arg(name) @@ -1026,6 +1107,7 @@ pub fn display_configuration() -> Section { let _display = descriptions.insert(fl!("display")); let refresh_rate = descriptions.insert(fl!("display", "refresh-rate")); + let vrr = descriptions.insert(fl!("vrr")); let resolution = descriptions.insert(fl!("display", "resolution")); let scale = descriptions.insert(fl!("display", "scale")); let orientation = descriptions.insert(fl!("orientation")); @@ -1046,7 +1128,7 @@ pub fn display_configuration() -> Section { let active_output = &page.list.outputs[active_id]; let display_options = (page.show_display_options && active_output.enabled).then(|| { - vec![ + let mut items = vec![ widget::settings::item( &descriptions[resolution], dropdown( @@ -1063,6 +1145,20 @@ pub fn display_configuration() -> Section { Message::RefreshRate, ), ), + ]; + + if let Some(vrr_selected) = page.cache.vrr_selected { + items.push(widget::settings::item( + &descriptions[vrr], + dropdown( + &page.cache.vrr_modes, + Some(vrr_selected), + Message::VariableRefreshRate, + ), + )); + } + + items.extend(vec![ widget::settings::item( &descriptions[scale], dropdown(&DPI_SCALE_LABELS, page.cache.scale_selected, Message::Scale), @@ -1082,7 +1178,9 @@ pub fn display_configuration() -> Section { }, ), ), - ] + ]); + + items }); let mut content = column().spacing(theme.cosmic().space_xs()); @@ -1092,7 +1190,7 @@ pub fn display_configuration() -> Section { .button_alignment(Alignment::Center) .on_activate(Message::Display); - let display_enable = (page + let mut display_enable = (page // Don't allow disabling display if it's the only active .list .outputs @@ -1102,7 +1200,7 @@ pub fn display_configuration() -> Section { > 1 || !active_output.enabled) .then(|| { - let mut column = list_column() + list_column() .add(widget::settings::item( &descriptions[enable_label], toggler(active_output.enabled).on_toggle(Message::DisplayToggle), @@ -1113,18 +1211,17 @@ pub fn display_configuration() -> Section { &page.mirror_menu, Message::Mirroring, ), - )); + )) + }) + .unwrap_or_else(list_column); - if let Some(items) = display_options { - for item in items { - column = column.add(item); - } - } + if let Some(items) = display_options { + for item in items { + display_enable = display_enable.add(item); + } + } - column - }); - - content = content.push(display_switcher).push_maybe(display_enable); + content = content.push(display_switcher).push(display_enable); } else { content = content .push(widget::text::heading(&descriptions[options_label])) diff --git a/i18n/de/cosmic_settings.ftl b/i18n/de/cosmic_settings.ftl index 88240bc..4f5f921 100644 --- a/i18n/de/cosmic_settings.ftl +++ b/i18n/de/cosmic_settings.ftl @@ -351,6 +351,12 @@ orientation = Ausrichtung .rotate-180 = Um 180 Grad drehen .rotate-270 = Um 270 Grad drehen +vrr = Variable refresh rate + .enabled = Aktiv + .force = Immer aktiv + .auto = Automatisch + .disabled = Deaktiviert + scheduling = Zeitplanung .manual = Manueller Zeitplan diff --git a/i18n/en/cosmic_settings.ftl b/i18n/en/cosmic_settings.ftl index 1499327..ed301ad 100644 --- a/i18n/en/cosmic_settings.ftl +++ b/i18n/en/cosmic_settings.ftl @@ -384,6 +384,12 @@ orientation = Orientation .rotate-180 = Rotate 180 .rotate-270 = Rotate 270 +vrr = Variable refresh rate + .enabled = Enabled + .force = Always + .auto = Automatic + .disabled = Disabled + scheduling = Scheduling .manual = Manual schedule