From 7187efd58f2ece8c404efb1a521f481294e399b0 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Sat, 25 Jul 2026 16:15:30 +0200 Subject: [PATCH 1/2] feat: prefer highest refresh rate at native resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit populate_modes() now selects the mode with the highest refresh rate at the preferred (native) resolution instead of blindly using the EDID PREFERRED flag. This ensures high-refresh panels (120 Hz, 144 Hz, …) are used out-of-the-box, including in the greeter session. Also fix libcosmic → libcosmic-yoda package reference. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- src/backend/kms/device.rs | 17 ++++++++++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d489aa2..8f2882ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1009,7 +1009,7 @@ dependencies = [ "indexmap 2.13.0", "jiff", "keyframe", - "libcosmic", + "libcosmic-yoda", "libdisplay-info", "libsystemd", "log-panics", @@ -1115,7 +1115,7 @@ dependencies = [ [[package]] name = "cosmic-randr-shell" -version = "0.1.0" +version = "1.3.0" dependencies = [ "kdl", "slotmap", @@ -3139,7 +3139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] -name = "libcosmic" +name = "libcosmic-yoda" version = "0.1.0-yoda.2" dependencies = [ "apply", diff --git a/Cargo.toml b/Cargo.toml index fa3747f6..fddb41b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" } iced_graphics = { path = "../libcosmic/iced/graphics", features = ["geometry"] } indexmap = "2.13" keyframe = "1.1.1" -cosmic = { package = "libcosmic", path = "../libcosmic", default-features = false, features = [ +cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [ "tokio", "wayland", "multi-window", diff --git a/src/backend/kms/device.rs b/src/backend/kms/device.rs index 44682dc5..bb32f5a3 100644 --- a/src/backend/kms/device.rs +++ b/src/backend/kms/device.rs @@ -1269,15 +1269,26 @@ fn populate_modes( ) -> Result<()> { let conn_info = drm.get_connector(conn, false)?; let max_bpc = drm_helpers::get_max_bpc(drm, conn)?.map(|(_val, range)| range.end.min(16)); - let Some(mode) = conn_info + let preferred = conn_info .modes() .iter() .find(|mode| mode.mode_type().contains(ModeTypeFlags::PREFERRED)) .copied() - .or(conn_info.modes().first().copied()) - else { + .or(conn_info.modes().first().copied()); + let Some(preferred) = preferred else { anyhow::bail!("No mode found"); }; + // Prefer the highest refresh rate at the native resolution so that + // high-refresh panels (120 Hz, 144 Hz, …) are used out-of-the-box + // without a pre-existing outputs.ron config. + let preferred_size = preferred.size(); + let mode = conn_info + .modes() + .iter() + .filter(|m| m.size() == preferred_size) + .max_by_key(|m| drm_helpers::calculate_refresh_rate(**m)) + .copied() + .unwrap_or(preferred); let refresh_rate = drm_helpers::calculate_refresh_rate(mode); let output_mode = OutputMode { From b45d12edef7d4b37c12f1c0cdee2845482d448d1 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Wed, 19 Aug 2026 21:18:39 +0200 Subject: [PATCH 2/2] stack tabs: follow WindowControlsPosition for the close button The window stack tabs build their own Tab widget rather than using libcosmic segmented_button, so they kept the close button on the right even with macOS-style controls enabled. Order the tab items from the configured position: close first when Start, last when End, with the container alignment following. Co-Authored-By: Claude Opus 5 (1M context) --- src/shell/element/stack/tab.rs | 69 +++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/src/shell/element/stack/tab.rs b/src/shell/element/stack/tab.rs index a92c13c5..1a311dfc 100644 --- a/src/shell/element/stack/tab.rs +++ b/src/shell/element/stack/tab.rs @@ -13,7 +13,7 @@ use cosmic::{ widget::{self, container::draw_background, rule::FillMode, scrollable::AbsoluteOffset}, }, theme, - widget::{Icon, icon::from_name, text}, + widget::{Icon, WindowControlsPosition, icon::from_name, text}, }; #[derive(Clone, Copy)] @@ -185,32 +185,47 @@ impl Tab { close_button = close_button.on_press(close_message); } - let items = vec![ - widget::rule::vertical(4).class(self.rule_theme).into(), - self.app_icon - .clone() - .apply(widget::container) - .width(Length::Shrink) - .padding([2, 4]) - .center_y(Length::Fill) - .into(), - text::body(self.title) - .font(self.font) - .wrapping(Wrapping::None) - .shaping(Shaping::Advanced) - .ellipsize(Ellipsize::End(EllipsizeHeightLimit::Lines(1))) - .align_y(alignment::Vertical::Center) - .height(Length::Fill) - .width(Length::Fill) - .into(), - close_button - .apply(widget::container) - .width(Length::Shrink) - .padding([2, 4]) - .center_y(Length::Fill) - .align_x(alignment::Horizontal::Right) - .into(), - ]; + let controls_position = cosmic::config::window_controls_position(); + + let rule: cosmic::Element<'a, Message> = + widget::rule::vertical(4).class(self.rule_theme).into(); + + let app_icon: cosmic::Element<'a, Message> = self + .app_icon + .clone() + .apply(widget::container) + .width(Length::Shrink) + .padding([2, 4]) + .center_y(Length::Fill) + .into(); + + let title: cosmic::Element<'a, Message> = text::body(self.title) + .font(self.font) + .wrapping(Wrapping::None) + .shaping(Shaping::Advanced) + .ellipsize(Ellipsize::End(EllipsizeHeightLimit::Lines(1))) + .align_y(alignment::Vertical::Center) + .height(Length::Fill) + .width(Length::Fill) + .into(); + + let close: cosmic::Element<'a, Message> = close_button + .apply(widget::container) + .width(Length::Shrink) + .padding([2, 4]) + .center_y(Length::Fill) + .align_x(match controls_position { + WindowControlsPosition::Start => alignment::Horizontal::Left, + WindowControlsPosition::End => alignment::Horizontal::Right, + }) + .into(); + + // With macOS-style controls the close button leads the tab contents, + // mirroring the window buttons in the header bar. + let items = match controls_position { + WindowControlsPosition::Start => vec![rule, close, app_icon, title], + WindowControlsPosition::End => vec![rule, app_icon, title, close], + }; TabInternal { id: self.id,