2025-08-20 17:59:06 -04:00
|
|
|
use cosmic_comp_config::output::comp::{AdaptiveSync, OutputConfig, OutputState};
|
2022-07-04 15:27:08 +02:00
|
|
|
use smithay::{
|
2024-11-18 21:30:02 +01:00
|
|
|
backend::drm::VrrSupport as Support,
|
2024-04-29 19:00:39 +02:00
|
|
|
output::{Output, WeakOutput},
|
2025-10-16 13:50:32 +02:00
|
|
|
utils::Rectangle,
|
2022-07-04 15:27:08 +02:00
|
|
|
};
|
|
|
|
|
|
2023-10-25 19:24:51 +02:00
|
|
|
pub use super::geometry::*;
|
2024-04-05 13:53:35 +02:00
|
|
|
pub use crate::shell::{SeatExt, Shell, Workspace};
|
2022-08-31 13:01:23 +02:00
|
|
|
pub use crate::state::{Common, State};
|
2022-07-05 18:46:38 +02:00
|
|
|
pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups;
|
2025-08-19 16:51:37 -04:00
|
|
|
use crate::{config::EdidProduct, shell::zoom::OutputZoomState};
|
2022-07-04 15:27:08 +02:00
|
|
|
|
2024-06-07 19:10:13 +02:00
|
|
|
use std::{
|
|
|
|
|
cell::{Ref, RefCell, RefMut},
|
|
|
|
|
sync::{
|
|
|
|
|
Mutex,
|
2025-10-16 18:53:57 +02:00
|
|
|
atomic::{AtomicU8, Ordering},
|
2024-06-07 19:10:13 +02:00
|
|
|
},
|
2024-04-29 19:00:39 +02:00
|
|
|
};
|
2024-04-29 15:40:29 +02:00
|
|
|
|
2022-07-04 15:27:08 +02:00
|
|
|
pub trait OutputExt {
|
2025-07-29 15:56:26 +02:00
|
|
|
fn is_internal(&self) -> bool;
|
2023-10-25 19:24:51 +02:00
|
|
|
fn geometry(&self) -> Rectangle<i32, Global>;
|
2025-03-25 17:31:48 +01:00
|
|
|
fn zoomed_geometry(&self) -> Option<Rectangle<i32, Global>>;
|
2025-02-13 21:09:13 +01:00
|
|
|
|
2024-11-18 19:47:59 +01:00
|
|
|
fn adaptive_sync(&self) -> AdaptiveSync;
|
|
|
|
|
fn set_adaptive_sync(&self, vrr: AdaptiveSync);
|
2024-11-18 21:30:02 +01:00
|
|
|
fn adaptive_sync_support(&self) -> Option<Support>;
|
|
|
|
|
fn set_adaptive_sync_support(&self, vrr: Option<Support>);
|
2024-04-29 19:00:39 +02:00
|
|
|
fn mirroring(&self) -> Option<Output>;
|
|
|
|
|
fn set_mirroring(&self, output: Option<Output>);
|
2024-06-07 19:10:13 +02:00
|
|
|
|
|
|
|
|
fn is_enabled(&self) -> bool;
|
|
|
|
|
fn config(&self) -> Ref<'_, OutputConfig>;
|
|
|
|
|
fn config_mut(&self) -> RefMut<'_, OutputConfig>;
|
2025-03-24 15:20:22 -07:00
|
|
|
|
|
|
|
|
fn edid(&self) -> Option<&EdidProduct>;
|
2022-07-04 15:27:08 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-18 19:47:59 +01:00
|
|
|
struct Vrr(AtomicU8);
|
2024-11-18 21:30:02 +01:00
|
|
|
struct VrrSupport(AtomicU8);
|
2024-04-29 19:00:39 +02:00
|
|
|
struct Mirroring(Mutex<Option<WeakOutput>>);
|
|
|
|
|
|
2022-07-04 15:27:08 +02:00
|
|
|
impl OutputExt for Output {
|
2025-07-29 15:56:26 +02:00
|
|
|
fn is_internal(&self) -> bool {
|
|
|
|
|
let name = self.name();
|
|
|
|
|
name.starts_with("eDP-") || name.starts_with("LVDS-") || name.starts_with("DSI-")
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-25 19:24:51 +02:00
|
|
|
fn geometry(&self) -> Rectangle<i32, Global> {
|
2024-12-26 18:18:35 -08:00
|
|
|
Rectangle::new(self.current_location(), {
|
2025-10-16 13:50:32 +02:00
|
|
|
self.current_transform()
|
2022-07-04 16:00:29 +02:00
|
|
|
.transform_size(
|
|
|
|
|
self.current_mode()
|
|
|
|
|
.map(|m| m.size)
|
|
|
|
|
.unwrap_or_else(|| (0, 0).into()),
|
|
|
|
|
)
|
|
|
|
|
.to_f64()
|
|
|
|
|
.to_logical(self.current_scale().fractional_scale())
|
|
|
|
|
.to_i32_round()
|
|
|
|
|
})
|
2023-10-25 19:24:51 +02:00
|
|
|
.as_global()
|
2022-07-04 15:27:08 +02:00
|
|
|
}
|
2024-04-29 15:40:29 +02:00
|
|
|
|
2025-03-25 17:31:48 +01:00
|
|
|
fn zoomed_geometry(&self) -> Option<Rectangle<i32, Global>> {
|
2025-02-13 21:09:13 +01:00
|
|
|
let output_geometry = self.geometry();
|
|
|
|
|
|
|
|
|
|
let output_state = self.user_data().get::<Mutex<OutputZoomState>>()?;
|
|
|
|
|
let mut output_state_ref = output_state.lock().unwrap();
|
|
|
|
|
|
2025-02-17 17:59:07 +01:00
|
|
|
let focal_point = output_state_ref.current_focal_point().to_global(self);
|
2025-02-13 21:09:13 +01:00
|
|
|
let mut zoomed_output_geo = output_geometry.to_f64();
|
|
|
|
|
zoomed_output_geo.loc -= focal_point;
|
2025-03-25 17:31:48 +01:00
|
|
|
zoomed_output_geo = zoomed_output_geo.downscale(output_state_ref.current_level());
|
2025-02-13 21:09:13 +01:00
|
|
|
zoomed_output_geo.loc += focal_point;
|
|
|
|
|
|
|
|
|
|
Some(zoomed_output_geo.to_i32_round())
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-18 19:47:59 +01:00
|
|
|
fn adaptive_sync(&self) -> AdaptiveSync {
|
2024-04-29 15:40:29 +02:00
|
|
|
self.user_data()
|
|
|
|
|
.get::<Vrr>()
|
2024-11-18 19:47:59 +01:00
|
|
|
.map(|vrr| match vrr.0.load(Ordering::SeqCst) {
|
|
|
|
|
2 => AdaptiveSync::Force,
|
|
|
|
|
1 => AdaptiveSync::Enabled,
|
|
|
|
|
_ => AdaptiveSync::Disabled,
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(AdaptiveSync::Disabled)
|
2024-04-29 15:40:29 +02:00
|
|
|
}
|
2024-11-18 19:47:59 +01:00
|
|
|
fn set_adaptive_sync(&self, vrr: AdaptiveSync) {
|
2024-04-29 15:40:29 +02:00
|
|
|
let user_data = self.user_data();
|
2024-11-18 19:47:59 +01:00
|
|
|
user_data.insert_if_missing_threadsafe(|| Vrr(AtomicU8::new(0)));
|
|
|
|
|
user_data.get::<Vrr>().unwrap().0.store(
|
|
|
|
|
match vrr {
|
|
|
|
|
AdaptiveSync::Disabled => 0,
|
|
|
|
|
AdaptiveSync::Enabled => 1,
|
|
|
|
|
AdaptiveSync::Force => 2,
|
|
|
|
|
},
|
|
|
|
|
Ordering::SeqCst,
|
|
|
|
|
);
|
2024-04-29 15:40:29 +02:00
|
|
|
}
|
2024-04-29 19:00:39 +02:00
|
|
|
|
2024-11-18 21:30:02 +01:00
|
|
|
fn adaptive_sync_support(&self) -> Option<Support> {
|
|
|
|
|
self.user_data()
|
|
|
|
|
.get::<VrrSupport>()
|
2025-10-16 13:50:32 +02:00
|
|
|
.and_then(|vrr| match vrr.0.load(Ordering::SeqCst) {
|
2024-11-18 21:30:02 +01:00
|
|
|
0 => None,
|
|
|
|
|
2 => Some(Support::RequiresModeset),
|
|
|
|
|
3 => Some(Support::Supported),
|
|
|
|
|
_ => Some(Support::NotSupported),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_adaptive_sync_support(&self, vrr: Option<Support>) {
|
|
|
|
|
let user_data = self.user_data();
|
|
|
|
|
user_data.insert_if_missing_threadsafe(|| VrrSupport(AtomicU8::new(0)));
|
|
|
|
|
user_data.get::<VrrSupport>().unwrap().0.store(
|
|
|
|
|
match vrr {
|
|
|
|
|
None => 0,
|
|
|
|
|
Some(Support::NotSupported) => 1,
|
|
|
|
|
Some(Support::RequiresModeset) => 2,
|
|
|
|
|
Some(Support::Supported) => 3,
|
|
|
|
|
},
|
|
|
|
|
Ordering::SeqCst,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-29 19:00:39 +02:00
|
|
|
fn mirroring(&self) -> Option<Output> {
|
|
|
|
|
self.user_data().get::<Mirroring>().and_then(|mirroring| {
|
|
|
|
|
mirroring
|
|
|
|
|
.0
|
|
|
|
|
.lock()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.clone()
|
|
|
|
|
.and_then(|o| o.upgrade())
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
fn set_mirroring(&self, output: Option<Output>) {
|
|
|
|
|
let user_data = self.user_data();
|
|
|
|
|
user_data.insert_if_missing_threadsafe(|| Mirroring(Mutex::new(None)));
|
|
|
|
|
*user_data.get::<Mirroring>().unwrap().0.lock().unwrap() =
|
|
|
|
|
output.map(|output| output.downgrade());
|
|
|
|
|
}
|
2024-06-07 19:10:13 +02:00
|
|
|
|
|
|
|
|
fn is_enabled(&self) -> bool {
|
|
|
|
|
self.user_data()
|
|
|
|
|
.get::<RefCell<OutputConfig>>()
|
|
|
|
|
.map(|conf| conf.borrow().enabled != OutputState::Disabled)
|
|
|
|
|
.unwrap_or(false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn config(&self) -> Ref<'_, OutputConfig> {
|
|
|
|
|
self.user_data()
|
|
|
|
|
.get::<RefCell<OutputConfig>>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.borrow()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn config_mut(&self) -> RefMut<'_, OutputConfig> {
|
|
|
|
|
self.user_data()
|
|
|
|
|
.get::<RefCell<OutputConfig>>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.borrow_mut()
|
|
|
|
|
}
|
2025-03-24 15:20:22 -07:00
|
|
|
|
|
|
|
|
fn edid(&self) -> Option<&EdidProduct> {
|
|
|
|
|
self.user_data().get()
|
|
|
|
|
}
|
2022-07-04 15:27:08 +02:00
|
|
|
}
|