Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/backend/kms/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use crate::{
backend::render::{output_elements, CursorMode, GlMultiRenderer, CLEAR_COLOR},
config::{AdaptiveSync, OutputConfig, OutputState, ScreenFilter},
config::{AdaptiveSync, EdidProduct, OutputConfig, OutputState, ScreenFilter},
shell::Shell,
utils::prelude::*,
wayland::protocols::screencopy::Frame as ScreencopyFrame,
Expand Down Expand Up @@ -743,7 +743,7 @@ fn create_output_for_conn(drm: &mut DrmDevice, conn: connector::Handle) -> Resul
.ok();
let (phys_w, phys_h) = conn_info.size().unwrap_or((0, 0));

Ok(Output::new(
let output = Output::new(
interface,
PhysicalProperties {
size: (phys_w as i32, phys_h as i32).into(),
Expand All @@ -764,7 +764,13 @@ fn create_output_for_conn(drm: &mut DrmDevice, conn: connector::Handle) -> Resul
.and_then(|info| info.model())
.unwrap_or_else(|| String::from("Unknown")),
},
))
);
if let Some(edid) = edid_info.as_ref().and_then(|x| x.edid()) {
output
.user_data()
.insert_if_missing(|| EdidProduct::from(edid.vendor_product()));
}
Ok(output)
}

fn populate_modes(
Expand Down
23 changes: 23 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,29 @@ impl From<Output> for OutputInfo {
}
}

#[derive(Debug, Deserialize, Serialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EdidProduct {
pub manufacturer: [char; 3],
pub product: u16,
pub serial: Option<u32>,
pub manufacture_week: i32,
pub manufacture_year: i32,
pub model_year: Option<i32>,
}

impl From<libdisplay_info::edid::VendorProduct> for EdidProduct {
fn from(vp: libdisplay_info::edid::VendorProduct) -> Self {
Self {
manufacturer: vp.manufacturer,
product: vp.product,
serial: vp.serial,
manufacture_week: vp.manufacture_week,
manufacture_year: vp.manufacture_year,
model_year: vp.model_year,
}
}
}

#[derive(Default, Debug, Deserialize, Serialize)]
pub struct NumlockStateConfig {
pub last_state: bool,
Expand Down
13 changes: 7 additions & 6 deletions src/shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,14 +518,14 @@ impl WorkspaceSet {
}
}

fn set_output(&mut self, new_output: &Output) {
fn set_output(&mut self, new_output: &Output, explicit: bool) {
self.sticky_layer.set_output(new_output);
for window in self.sticky_layer.windows() {
toplevel_leave_output(&window, &self.output);
toplevel_enter_output(&window, &new_output);
}
for workspace in &mut self.workspaces {
workspace.set_output(new_output);
workspace.set_output(new_output, explicit);
}
self.output = new_output.clone();
}
Expand Down Expand Up @@ -711,7 +711,7 @@ impl Workspaces {
.backup_set
.take()
.map(|mut set| {
set.set_output(output);
set.set_output(output, false);
set
})
.unwrap_or_else(|| {
Expand All @@ -738,7 +738,7 @@ impl Workspaces {
}
set.update_workspace_idxs(workspace_state);
for (i, workspace) in set.workspaces.iter_mut().enumerate() {
workspace.set_output(output);
workspace.set_output(output, false);
workspace.refresh();
if i == set.active {
workspace_state.add_workspace_state(&workspace.handle, WState::Active);
Expand Down Expand Up @@ -790,7 +790,7 @@ impl Workspaces {
move_workspace_to_group(&mut workspace, &workspace_group, workspace_state);

// update mapping
workspace.set_output(&new_output);
workspace.set_output(&new_output, false);
workspace.refresh();
new_set.workspaces.push(workspace);

Expand Down Expand Up @@ -840,6 +840,7 @@ impl Workspaces {
}
}

// Move workspace from one output to another, explicitly by the user
fn migrate_workspace(
&mut self,
from: &Output,
Expand All @@ -858,7 +859,7 @@ impl Workspaces {
{
let new_set = self.sets.get_mut(to).unwrap();
move_workspace_to_group(&mut workspace, &new_set.group, workspace_state);
workspace.set_output(to);
workspace.set_output(to, true);
workspace.refresh();
new_set.workspaces.insert(new_set.active + 1, workspace);
new_set.update_workspace_idxs(workspace_state);
Expand Down
66 changes: 58 additions & 8 deletions src/shell/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
element::{AsGlowRenderer, FromGlesError},
BackdropShader,
},
config::EdidProduct,
shell::{
layout::{floating::FloatingLayout, tiling::TilingLayout},
OverviewMode, ANIMATION_DURATION,
Expand Down Expand Up @@ -73,6 +74,33 @@ use super::{

const FULLSCREEN_ANIMATION_DURATION: Duration = Duration::from_millis(200);

#[derive(Debug, Clone, PartialEq, Eq)]
struct OutputMatch {
name: String,
edid: Option<EdidProduct>,
}

impl OutputMatch {
fn for_output(output: &Output) -> Self {
Self {
name: output.name(),
edid: output.edid().cloned(),
}
}

// If `disambguate` is true, check that edid *and* connector name match.
// Otherwise, match only edid (if it exists)
fn matches(&self, output: &Output, disambiguate: bool) -> bool {
if self.edid.as_ref() != output.edid() {
false
} else if disambiguate || self.edid.is_none() {
self.name == output.name()
} else {
true
}
}
}

#[derive(Debug)]
pub struct Workspace {
pub output: Output,
Expand All @@ -85,7 +113,7 @@ pub struct Workspace {
pub handle: WorkspaceHandle,
pub focus_stack: FocusStacks,
pub screencopy: ScreencopySessions,
pub output_stack: VecDeque<String>,
output_stack: VecDeque<OutputMatch>,
pub(super) backdrop_id: Id,
pub dirty: AtomicBool,
}
Expand Down Expand Up @@ -241,7 +269,7 @@ impl Workspace {
) -> Workspace {
let tiling_layer = TilingLayout::new(theme.clone(), &output);
let floating_layer = FloatingLayout::new(theme, &output);
let output_name = output.name();
let output_match = OutputMatch::for_output(&output);

Workspace {
output,
Expand All @@ -255,7 +283,7 @@ impl Workspace {
screencopy: ScreencopySessions::default(),
output_stack: {
let mut queue = VecDeque::new();
queue.push_back(output_name);
queue.push_back(output_match);
queue
},
backdrop_id: Id::new(),
Expand Down Expand Up @@ -361,7 +389,11 @@ impl Workspace {
&self.output
}

pub fn set_output(&mut self, output: &Output) {
// Set output the workspace is on
//
// If `explicit` is `true`, the user has explicitly moved the workspace
// to this output, so previous outputs it was on can be forgotten.
pub fn set_output(&mut self, output: &Output, explicit: bool) {
self.tiling_layer.set_output(output);
self.floating_layer.set_output(output);
for mapped in self.mapped() {
Expand All @@ -376,21 +408,39 @@ impl Workspace {
toplevel_enter_output(&surface, output);
}
}
let output_name = output.name();
if explicit {
self.output_stack.clear();
}
if let Some(pos) = self
.output_stack
.iter()
.position(|name| name == &output_name)
.position(|i| i.matches(output, true))
{
// Matched edid and connector name
self.output_stack.truncate(pos + 1);
} else if let Some(pos) = self
.output_stack
.iter()
.position(|i| i.matches(output, false))
{
// Matched edid but not connector name; truncate entries that don't match edid,
// but keep old entry in case we see two outputs with the same edid.
self.output_stack.truncate(pos + 1);
self.output_stack.push_back(OutputMatch::for_output(output));
} else {
self.output_stack.push_back(output.name());
self.output_stack.push_back(OutputMatch::for_output(output));
}
self.output = output.clone();
}

pub fn prefers_output(&self, output: &Output) -> bool {
self.output_stack.contains(&output.name())
// Disambiguate match by connector name if existing output has same edid
let disambiguate = output
.edid()
.is_some_and(|edid| self.output().edid() == Some(edid));
self.output_stack
.iter()
.any(|i| i.matches(output, disambiguate))
}

pub fn unmap(&mut self, mapped: &CosmicMapped) -> Option<ManagedState> {
Expand Down
8 changes: 7 additions & 1 deletion src/utils/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub use crate::shell::{SeatExt, Shell, Workspace};
pub use crate::state::{Common, State};
pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups;
use crate::{
config::{AdaptiveSync, OutputConfig, OutputState},
config::{AdaptiveSync, EdidProduct, OutputConfig, OutputState},
shell::zoom::OutputZoomState,
};

Expand All @@ -35,6 +35,8 @@ pub trait OutputExt {
fn is_enabled(&self) -> bool;
fn config(&self) -> Ref<'_, OutputConfig>;
fn config_mut(&self) -> RefMut<'_, OutputConfig>;

fn edid(&self) -> Option<&EdidProduct>;
}

struct Vrr(AtomicU8);
Expand Down Expand Up @@ -158,4 +160,8 @@ impl OutputExt for Output {
.unwrap()
.borrow_mut()
}

fn edid(&self) -> Option<&EdidProduct> {
self.user_data().get()
}
}
Loading