refactor(output config): move to config crate
this allows the greeter to easily sync to the user config
This commit is contained in:
parent
4a385d5535
commit
416b66b776
17 changed files with 283 additions and 194 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
|
@ -828,6 +828,7 @@ dependencies = [
|
||||||
"cosmic-text",
|
"cosmic-text",
|
||||||
"egui",
|
"egui",
|
||||||
"egui_plot",
|
"egui_plot",
|
||||||
|
"futures",
|
||||||
"i18n-embed",
|
"i18n-embed",
|
||||||
"i18n-embed-fl",
|
"i18n-embed-fl",
|
||||||
"iced_tiny_skia",
|
"iced_tiny_skia",
|
||||||
|
|
@ -880,7 +881,9 @@ dependencies = [
|
||||||
"cosmic-config",
|
"cosmic-config",
|
||||||
"input",
|
"input",
|
||||||
"libdisplay-info",
|
"libdisplay-info",
|
||||||
|
"ron 0.9.0",
|
||||||
"serde",
|
"serde",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ reis = { version = "0.5", features = ["calloop"] }
|
||||||
clap_lex = "0.7"
|
clap_lex = "0.7"
|
||||||
parking_lot = "0.12.4"
|
parking_lot = "0.12.4"
|
||||||
logind-zbus = { version = "5.3.2", optional = true }
|
logind-zbus = { version = "5.3.2", optional = true }
|
||||||
|
futures = "0.3.31"
|
||||||
|
|
||||||
[dependencies.id_tree]
|
[dependencies.id_tree]
|
||||||
branch = "feature/copy_clone"
|
branch = "feature/copy_clone"
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,12 @@ cosmic-config = { git = "https://github.com/pop-os/libcosmic/" }
|
||||||
input = "0.9.0"
|
input = "0.9.0"
|
||||||
libdisplay-info = { version = "0.2.0", optional = true }
|
libdisplay-info = { version = "0.2.0", optional = true }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
ron = { version = "0.9.0-alpha.0", optional = true }
|
||||||
|
tracing = { version = "0.1.37", features = [
|
||||||
|
"max_level_debug",
|
||||||
|
"release_max_level_info",
|
||||||
|
], optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["output"]
|
||||||
|
output = ["ron", "tracing"]
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use std::collections::HashMap;
|
||||||
use crate::input::TouchpadOverride;
|
use crate::input::TouchpadOverride;
|
||||||
|
|
||||||
pub mod input;
|
pub mod input;
|
||||||
|
#[cfg(feature = "output")]
|
||||||
pub mod output;
|
pub mod output;
|
||||||
pub mod workspace;
|
pub mod workspace;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{collections::HashMap, fs::OpenOptions, path::Path};
|
||||||
|
use tracing::{error, warn};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Deserialize, Serialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct EdidProduct {
|
pub struct EdidProduct {
|
||||||
|
|
@ -25,3 +27,133 @@ impl From<libdisplay_info::edid::VendorProduct> for EdidProduct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum OutputState {
|
||||||
|
#[serde(rename = "true")]
|
||||||
|
Enabled,
|
||||||
|
#[serde(rename = "false")]
|
||||||
|
Disabled,
|
||||||
|
Mirroring(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_state() -> OutputState {
|
||||||
|
OutputState::Enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum AdaptiveSync {
|
||||||
|
#[serde(rename = "true")]
|
||||||
|
Enabled,
|
||||||
|
#[serde(rename = "false")]
|
||||||
|
Disabled,
|
||||||
|
Force,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_sync() -> AdaptiveSync {
|
||||||
|
AdaptiveSync::Enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct OutputsConfig {
|
||||||
|
pub config: HashMap<Vec<OutputInfo>, Vec<OutputConfig>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
|
pub struct OutputConfig {
|
||||||
|
pub mode: ((i32, i32), Option<u32>),
|
||||||
|
#[serde(default = "default_sync")]
|
||||||
|
pub vrr: AdaptiveSync,
|
||||||
|
pub scale: f64,
|
||||||
|
pub transform: TransformDef,
|
||||||
|
pub position: (u32, u32),
|
||||||
|
#[serde(default = "default_state")]
|
||||||
|
pub enabled: OutputState,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub max_bpc: Option<u32>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub xwayland_primary: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OutputConfig {
|
||||||
|
fn default() -> OutputConfig {
|
||||||
|
OutputConfig {
|
||||||
|
mode: ((0, 0), None),
|
||||||
|
vrr: AdaptiveSync::Enabled,
|
||||||
|
scale: 1.0,
|
||||||
|
transform: TransformDef::Normal,
|
||||||
|
position: (0, 0),
|
||||||
|
enabled: OutputState::Enabled,
|
||||||
|
max_bpc: None,
|
||||||
|
xwayland_primary: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct OutputInfo {
|
||||||
|
pub connector: String,
|
||||||
|
pub make: String,
|
||||||
|
pub model: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_outputs(path: Option<impl AsRef<Path>>) -> OutputsConfig {
|
||||||
|
if let Some(path) = path.as_ref() {
|
||||||
|
let path: &Path = path.as_ref();
|
||||||
|
if path.exists() {
|
||||||
|
match ron::de::from_reader::<_, OutputsConfig>(
|
||||||
|
OpenOptions::new().read(true).open(path).unwrap(),
|
||||||
|
) {
|
||||||
|
Ok(mut config) => {
|
||||||
|
for (info, config) in config.config.iter_mut() {
|
||||||
|
let config_clone = config.clone();
|
||||||
|
for conf in config.iter_mut() {
|
||||||
|
if let OutputState::Mirroring(conn) = &conf.enabled {
|
||||||
|
if let Some((j, _)) = info
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, info)| &info.connector == conn)
|
||||||
|
{
|
||||||
|
if config_clone[j].enabled != OutputState::Enabled {
|
||||||
|
warn!("Invalid Mirroring tag, overriding with `Enabled` instead");
|
||||||
|
conf.enabled = OutputState::Enabled;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Invalid Mirroring tag, overriding with `Enabled` instead"
|
||||||
|
);
|
||||||
|
conf.enabled = OutputState::Enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!(?err, "Failed to read output_config, resetting..");
|
||||||
|
if let Err(err) = std::fs::remove_file(path) {
|
||||||
|
error!(?err, "Failed to remove output_config.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputsConfig {
|
||||||
|
config: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum TransformDef {
|
||||||
|
Normal,
|
||||||
|
_90,
|
||||||
|
_180,
|
||||||
|
_270,
|
||||||
|
Flipped,
|
||||||
|
Flipped90,
|
||||||
|
Flipped180,
|
||||||
|
Flipped270,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@ use crate::{
|
||||||
kms::render::gles::GbmGlowBackend,
|
kms::render::gles::GbmGlowBackend,
|
||||||
render::{init_shaders, output_elements, CursorMode, GlMultiRenderer, CLEAR_COLOR},
|
render::{init_shaders, output_elements, CursorMode, GlMultiRenderer, CLEAR_COLOR},
|
||||||
},
|
},
|
||||||
config::{AdaptiveSync, EdidProduct, OutputConfig, OutputState, ScreenFilter},
|
config::{CompTransformDef, EdidProduct, ScreenFilter},
|
||||||
shell::Shell,
|
shell::Shell,
|
||||||
utils::{env::dev_list_var, prelude::*},
|
utils::{env::dev_list_var, prelude::*},
|
||||||
wayland::handlers::screencopy::PendingImageCopyData,
|
wayland::handlers::screencopy::PendingImageCopyData,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use cosmic_comp_config::output::{AdaptiveSync, OutputConfig, OutputState};
|
||||||
use libc::dev_t;
|
use libc::dev_t;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
|
|
@ -1008,7 +1009,7 @@ fn populate_modes(
|
||||||
position,
|
position,
|
||||||
max_bpc,
|
max_bpc,
|
||||||
scale,
|
scale,
|
||||||
transform,
|
transform: CompTransformDef::from(transform).0,
|
||||||
// Try opportunistic VRR by default,
|
// Try opportunistic VRR by default,
|
||||||
// if not supported this will be turned off on `resume`,
|
// if not supported this will be turned off on `resume`,
|
||||||
// when we have the `Surface` to actually check for support.
|
// when we have the `Surface` to actually check for support.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{AdaptiveSync, OutputState, ScreenFilter},
|
config::{CompOutputConfig, ScreenFilter},
|
||||||
shell::Shell,
|
shell::Shell,
|
||||||
state::BackendData,
|
state::BackendData,
|
||||||
utils::{env::dev_var, prelude::*},
|
utils::{env::dev_var, prelude::*},
|
||||||
|
|
@ -9,6 +9,7 @@ use crate::{
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use calloop::LoopSignal;
|
use calloop::LoopSignal;
|
||||||
|
use cosmic_comp_config::output::{AdaptiveSync, OutputState};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use render::gles::GbmGlowBackend;
|
use render::gles::GbmGlowBackend;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
|
|
@ -798,7 +799,7 @@ impl<'a> KmsGuard<'a> {
|
||||||
|
|
||||||
// reconfigure existing
|
// reconfigure existing
|
||||||
for (crtc, surface) in device.inner.surfaces.iter_mut() {
|
for (crtc, surface) in device.inner.surfaces.iter_mut() {
|
||||||
let output_config = surface.output.config();
|
let output_config = CompOutputConfig(surface.output.config());
|
||||||
|
|
||||||
let drm = &mut device.drm;
|
let drm = &mut device.drm;
|
||||||
let conn = surface.connector;
|
let conn = surface.connector;
|
||||||
|
|
@ -814,7 +815,7 @@ impl<'a> KmsGuard<'a> {
|
||||||
// and then select the closest refresh rate (e.g. to match 59.98 as 60)
|
// and then select the closest refresh rate (e.g. to match 59.98 as 60)
|
||||||
.min_by_key(|mode| {
|
.min_by_key(|mode| {
|
||||||
let refresh_rate = drm_helpers::calculate_refresh_rate(**mode);
|
let refresh_rate = drm_helpers::calculate_refresh_rate(**mode);
|
||||||
(output_config.mode.1.unwrap() as i32 - refresh_rate as i32).abs()
|
(output_config.0.mode.1.unwrap() as i32 - refresh_rate as i32).abs()
|
||||||
})
|
})
|
||||||
.ok_or(anyhow::anyhow!("Unable to find matching mode"))?;
|
.ok_or(anyhow::anyhow!("Unable to find matching mode"))?;
|
||||||
|
|
||||||
|
|
@ -876,7 +877,7 @@ impl<'a> KmsGuard<'a> {
|
||||||
compositor
|
compositor
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(bpc) = output_config.max_bpc {
|
if let Some(bpc) = output_config.0.max_bpc {
|
||||||
if let Err(err) = drm_helpers::set_max_bpc(drm.device(), conn, bpc) {
|
if let Err(err) = drm_helpers::set_max_bpc(drm.device(), conn, bpc) {
|
||||||
warn!(
|
warn!(
|
||||||
?bpc,
|
?bpc,
|
||||||
|
|
@ -887,7 +888,7 @@ impl<'a> KmsGuard<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let vrr = output_config.vrr;
|
let vrr = output_config.0.vrr;
|
||||||
std::mem::drop(output_config);
|
std::mem::drop(output_config);
|
||||||
|
|
||||||
let compositor_ref = drm.compositors().get(crtc).unwrap().lock().unwrap();
|
let compositor_ref = drm.compositors().get(crtc).unwrap().lock().unwrap();
|
||||||
|
|
@ -928,7 +929,7 @@ impl<'a> KmsGuard<'a> {
|
||||||
surface.output.set_adaptive_sync(AdaptiveSync::Disabled);
|
surface.output.set_adaptive_sync(AdaptiveSync::Disabled);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let vrr = output_config.vrr;
|
let vrr = output_config.0.vrr;
|
||||||
std::mem::drop(output_config);
|
std::mem::drop(output_config);
|
||||||
if vrr != surface.output.adaptive_sync() {
|
if vrr != surface.output.adaptive_sync() {
|
||||||
if match surface.output.adaptive_sync_support() {
|
if match surface.output.adaptive_sync_support() {
|
||||||
|
|
@ -1030,7 +1031,7 @@ impl<'a> KmsGuard<'a> {
|
||||||
Some(
|
Some(
|
||||||
all_outputs
|
all_outputs
|
||||||
.iter()
|
.iter()
|
||||||
.find(|output| &output.name() == conn)
|
.find(|output| output.name() == *conn)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or(anyhow::anyhow!("Unable to find mirroring output"))?,
|
.ok_or(anyhow::anyhow!("Unable to find mirroring output"))?,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
init_shaders, output_elements, CursorMode, GlMultiError, GlMultiRenderer,
|
init_shaders, output_elements, CursorMode, GlMultiError, GlMultiRenderer,
|
||||||
PostprocessOutputConfig, PostprocessShader, PostprocessState, CLEAR_COLOR,
|
PostprocessOutputConfig, PostprocessShader, PostprocessState, CLEAR_COLOR,
|
||||||
},
|
},
|
||||||
config::{AdaptiveSync, ScreenFilter},
|
config::ScreenFilter,
|
||||||
shell::Shell,
|
shell::Shell,
|
||||||
state::SurfaceDmabufFeedback,
|
state::SurfaceDmabufFeedback,
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
|
|
@ -23,6 +23,7 @@ use crate::{
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use calloop::channel::Channel;
|
use calloop::channel::Channel;
|
||||||
|
use cosmic_comp_config::output::AdaptiveSync;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
allocator::{
|
allocator::{
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::render,
|
backend::render,
|
||||||
config::{OutputConfig, ScreenFilter},
|
config::ScreenFilter,
|
||||||
shell::{Devices, SeatExt},
|
shell::{Devices, SeatExt},
|
||||||
state::{BackendData, Common},
|
state::{BackendData, Common},
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use cosmic_comp_config::output::{OutputConfig, TransformDef};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
drm::NodeType,
|
drm::NodeType,
|
||||||
|
|
@ -167,7 +168,7 @@ pub fn init_backend(
|
||||||
output.user_data().insert_if_missing(|| {
|
output.user_data().insert_if_missing(|| {
|
||||||
RefCell::new(OutputConfig {
|
RefCell::new(OutputConfig {
|
||||||
mode: ((size.w, size.h), None),
|
mode: ((size.w, size.h), None),
|
||||||
transform: Transform::Flipped180.into(),
|
transform: TransformDef::Flipped180,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::render,
|
backend::render,
|
||||||
config::{OutputConfig, ScreenFilter},
|
config::ScreenFilter,
|
||||||
shell::{Devices, SeatExt},
|
shell::{Devices, SeatExt},
|
||||||
state::{BackendData, Common},
|
state::{BackendData, Common},
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use cosmic_comp_config::output::OutputConfig;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
allocator::{
|
allocator::{
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ pub use smithay::{
|
||||||
utils::{Logical, Physical, Point, Size, Transform, SERIAL_COUNTER},
|
utils::{Logical, Physical, Point, Size, Transform, SERIAL_COUNTER},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::{Ref, RefCell},
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap},
|
||||||
fs::OpenOptions,
|
fs::OpenOptions,
|
||||||
io::Write,
|
io::Write,
|
||||||
|
|
@ -39,17 +39,19 @@ use tracing::{error, warn};
|
||||||
|
|
||||||
mod input_config;
|
mod input_config;
|
||||||
pub mod key_bindings;
|
pub mod key_bindings;
|
||||||
pub use key_bindings::{Action, PrivateAction};
|
|
||||||
mod types;
|
mod types;
|
||||||
pub use self::types::*;
|
|
||||||
use cosmic::config::CosmicTk;
|
use cosmic::config::CosmicTk;
|
||||||
pub use cosmic_comp_config::output::EdidProduct;
|
pub use cosmic_comp_config::output::EdidProduct;
|
||||||
use cosmic_comp_config::{
|
use cosmic_comp_config::{
|
||||||
input::{DeviceState as InputDeviceState, InputConfig, TouchpadOverride},
|
input::{DeviceState as InputDeviceState, InputConfig, TouchpadOverride},
|
||||||
|
output::{load_outputs, OutputConfig, OutputInfo, OutputState, OutputsConfig, TransformDef},
|
||||||
workspace::WorkspaceConfig,
|
workspace::WorkspaceConfig,
|
||||||
CosmicCompConfig, KeyboardConfig, TileBehavior, XkbConfig, XwaylandDescaling,
|
CosmicCompConfig, KeyboardConfig, TileBehavior, XkbConfig, XwaylandDescaling,
|
||||||
XwaylandEavesdropping, ZoomConfig,
|
XwaylandEavesdropping, ZoomConfig,
|
||||||
};
|
};
|
||||||
|
pub use key_bindings::{Action, PrivateAction};
|
||||||
|
use types::WlXkbConfig;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
|
@ -74,105 +76,24 @@ pub struct DynamicConfig {
|
||||||
accessibility_filter: (Option<PathBuf>, ScreenFilter),
|
accessibility_filter: (Option<PathBuf>, ScreenFilter),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct OutputsConfig {
|
|
||||||
pub config: HashMap<Vec<OutputInfo>, Vec<OutputConfig>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct OutputInfo {
|
|
||||||
pub connector: String,
|
|
||||||
pub make: String,
|
|
||||||
pub model: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Output> for OutputInfo {
|
|
||||||
fn from(o: Output) -> OutputInfo {
|
|
||||||
let physical = o.physical_properties();
|
|
||||||
OutputInfo {
|
|
||||||
connector: o.name(),
|
|
||||||
make: physical.make,
|
|
||||||
model: physical.model,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Deserialize, Serialize)]
|
#[derive(Default, Debug, Deserialize, Serialize)]
|
||||||
pub struct NumlockStateConfig {
|
pub struct NumlockStateConfig {
|
||||||
pub last_state: bool,
|
pub last_state: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
pub struct CompOutputConfig<'a>(pub Ref<'a, OutputConfig>);
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum OutputState {
|
|
||||||
#[serde(rename = "true")]
|
|
||||||
Enabled,
|
|
||||||
#[serde(rename = "false")]
|
|
||||||
Disabled,
|
|
||||||
Mirroring(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_state() -> OutputState {
|
impl<'a> CompOutputConfig<'a> {
|
||||||
OutputState::Enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum AdaptiveSync {
|
|
||||||
#[serde(rename = "true")]
|
|
||||||
Enabled,
|
|
||||||
#[serde(rename = "false")]
|
|
||||||
Disabled,
|
|
||||||
Force,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_sync() -> AdaptiveSync {
|
|
||||||
AdaptiveSync::Enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
|
||||||
pub struct OutputConfig {
|
|
||||||
pub mode: ((i32, i32), Option<u32>),
|
|
||||||
#[serde(default = "default_sync")]
|
|
||||||
pub vrr: AdaptiveSync,
|
|
||||||
pub scale: f64,
|
|
||||||
#[serde(with = "TransformDef")]
|
|
||||||
pub transform: Transform,
|
|
||||||
pub position: (u32, u32),
|
|
||||||
#[serde(default = "default_state")]
|
|
||||||
pub enabled: OutputState,
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub max_bpc: Option<u32>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub xwayland_primary: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for OutputConfig {
|
|
||||||
fn default() -> OutputConfig {
|
|
||||||
OutputConfig {
|
|
||||||
mode: ((0, 0), None),
|
|
||||||
vrr: AdaptiveSync::Enabled,
|
|
||||||
scale: 1.0,
|
|
||||||
transform: Transform::Normal,
|
|
||||||
position: (0, 0),
|
|
||||||
enabled: OutputState::Enabled,
|
|
||||||
max_bpc: None,
|
|
||||||
xwayland_primary: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutputConfig {
|
|
||||||
pub fn mode_size(&self) -> Size<i32, Physical> {
|
pub fn mode_size(&self) -> Size<i32, Physical> {
|
||||||
self.mode.0.into()
|
self.0.mode.0.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mode_refresh(&self) -> u32 {
|
pub fn mode_refresh(&self) -> u32 {
|
||||||
self.mode.1.unwrap_or(60_000)
|
self.0.mode.1.unwrap_or(60_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transformed_size(&self) -> Size<i32, Physical> {
|
pub fn transformed_size(&self) -> Size<i32, Physical> {
|
||||||
self.transform.transform_size(self.mode_size())
|
self.transform().transform_size(self.mode_size())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output_mode(&self) -> Mode {
|
pub fn output_mode(&self) -> Mode {
|
||||||
|
|
@ -181,6 +102,57 @@ impl OutputConfig {
|
||||||
refresh: self.mode_refresh() as i32,
|
refresh: self.mode_refresh() as i32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn transform(&self) -> Transform {
|
||||||
|
Transform::from(CompTransformDef(self.0.transform))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CompTransformDef(pub TransformDef);
|
||||||
|
|
||||||
|
impl From<Transform> for CompTransformDef {
|
||||||
|
fn from(transform: Transform) -> Self {
|
||||||
|
let def = match transform {
|
||||||
|
Transform::Normal => TransformDef::Normal,
|
||||||
|
Transform::_90 => TransformDef::_90,
|
||||||
|
Transform::_180 => TransformDef::_180,
|
||||||
|
Transform::_270 => TransformDef::_270,
|
||||||
|
Transform::Flipped => TransformDef::Flipped,
|
||||||
|
Transform::Flipped90 => TransformDef::Flipped90,
|
||||||
|
Transform::Flipped180 => TransformDef::Flipped180,
|
||||||
|
Transform::Flipped270 => TransformDef::Flipped270,
|
||||||
|
};
|
||||||
|
CompTransformDef(def)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CompTransformDef> for Transform {
|
||||||
|
fn from(comp_transform: CompTransformDef) -> Self {
|
||||||
|
match comp_transform.0 {
|
||||||
|
TransformDef::Normal => Transform::Normal,
|
||||||
|
TransformDef::_90 => Transform::_90,
|
||||||
|
TransformDef::_180 => Transform::_180,
|
||||||
|
TransformDef::_270 => Transform::_270,
|
||||||
|
TransformDef::Flipped => Transform::Flipped,
|
||||||
|
TransformDef::Flipped90 => Transform::Flipped90,
|
||||||
|
TransformDef::Flipped180 => Transform::Flipped180,
|
||||||
|
TransformDef::Flipped270 => Transform::Flipped270,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "libdisplay-info")]
|
||||||
|
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(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
|
|
@ -355,7 +327,7 @@ impl Config {
|
||||||
|
|
||||||
fn load_dynamic(xdg: &xdg::BaseDirectories) -> DynamicConfig {
|
fn load_dynamic(xdg: &xdg::BaseDirectories) -> DynamicConfig {
|
||||||
let output_path = xdg.place_state_file("cosmic-comp/outputs.ron").ok();
|
let output_path = xdg.place_state_file("cosmic-comp/outputs.ron").ok();
|
||||||
let outputs = Self::load_outputs(&output_path);
|
let outputs = load_outputs(output_path.as_ref());
|
||||||
let numlock_path = xdg.place_state_file("cosmic-comp/numlock.ron").ok();
|
let numlock_path = xdg.place_state_file("cosmic-comp/numlock.ron").ok();
|
||||||
let numlock = Self::load_numlock(&numlock_path);
|
let numlock = Self::load_numlock(&numlock_path);
|
||||||
|
|
||||||
|
|
@ -371,54 +343,6 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_outputs(path: &Option<PathBuf>) -> OutputsConfig {
|
|
||||||
if let Some(path) = path.as_ref() {
|
|
||||||
if path.exists() {
|
|
||||||
match ron::de::from_reader::<_, OutputsConfig>(
|
|
||||||
OpenOptions::new().read(true).open(path).unwrap(),
|
|
||||||
) {
|
|
||||||
Ok(mut config) => {
|
|
||||||
for (info, config) in config.config.iter_mut() {
|
|
||||||
let config_clone = config.clone();
|
|
||||||
for conf in config.iter_mut() {
|
|
||||||
if let OutputState::Mirroring(conn) = &conf.enabled {
|
|
||||||
if let Some((j, _)) = info
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.find(|(_, info)| &info.connector == conn)
|
|
||||||
{
|
|
||||||
if config_clone[j].enabled != OutputState::Enabled {
|
|
||||||
warn!(
|
|
||||||
"Invalid Mirroring tag, overriding with `Enabled` instead"
|
|
||||||
);
|
|
||||||
conf.enabled = OutputState::Enabled;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
warn!(
|
|
||||||
"Invalid Mirroring tag, overriding with `Enabled` instead"
|
|
||||||
);
|
|
||||||
conf.enabled = OutputState::Enabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(?err, "Failed to read output_config, resetting..");
|
|
||||||
if let Err(err) = std::fs::remove_file(path) {
|
|
||||||
error!(?err, "Failed to remove output_config.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputsConfig {
|
|
||||||
config: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_numlock(path: &Option<PathBuf>) -> NumlockStateConfig {
|
fn load_numlock(path: &Option<PathBuf>) -> NumlockStateConfig {
|
||||||
path.as_deref()
|
path.as_deref()
|
||||||
.filter(|path| path.exists())
|
.filter(|path| path.exists())
|
||||||
|
|
@ -479,7 +403,8 @@ impl Config {
|
||||||
let mut infos = outputs
|
let mut infos = outputs
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(Into::<crate::config::OutputInfo>::into)
|
.map(Into::<crate::config::CompOutputInfo>::into)
|
||||||
|
.map(|i| i.0)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
infos.sort();
|
infos.sort();
|
||||||
|
|
||||||
|
|
@ -657,7 +582,7 @@ impl Config {
|
||||||
.map(|o| {
|
.map(|o| {
|
||||||
let o = o.borrow();
|
let o = o.borrow();
|
||||||
(
|
(
|
||||||
Into::<crate::config::OutputInfo>::into(o.clone()),
|
Into::<CompOutputInfo>::into(o.clone()).0,
|
||||||
o.user_data()
|
o.user_data()
|
||||||
.get::<RefCell<OutputConfig>>()
|
.get::<RefCell<OutputConfig>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
@ -801,6 +726,16 @@ impl DynamicConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn xkb_config_to_wl(config: &XkbConfig) -> WlXkbConfig<'_> {
|
||||||
|
WlXkbConfig {
|
||||||
|
rules: &config.rules,
|
||||||
|
model: &config.model,
|
||||||
|
layout: &config.layout,
|
||||||
|
variant: &config.variant,
|
||||||
|
options: config.options.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_config<T: Default + serde::de::DeserializeOwned>(
|
fn get_config<T: Default + serde::de::DeserializeOwned>(
|
||||||
config: &cosmic_config::Config,
|
config: &cosmic_config::Config,
|
||||||
key: &str,
|
key: &str,
|
||||||
|
|
@ -1002,12 +937,16 @@ fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xkb_config_to_wl(config: &XkbConfig) -> WlXkbConfig<'_> {
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
WlXkbConfig {
|
pub struct CompOutputInfo(OutputInfo);
|
||||||
rules: &config.rules,
|
|
||||||
model: &config.model,
|
impl From<Output> for CompOutputInfo {
|
||||||
layout: &config.layout,
|
fn from(o: Output) -> CompOutputInfo {
|
||||||
variant: &config.variant,
|
let physical = o.physical_properties();
|
||||||
options: config.options.clone(),
|
CompOutputInfo(OutputInfo {
|
||||||
|
connector: o.name(),
|
||||||
|
make: physical.make,
|
||||||
|
model: physical.model,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,4 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
pub use smithay::input::keyboard::XkbConfig as WlXkbConfig;
|
||||||
pub use smithay::{input::keyboard::XkbConfig as WlXkbConfig, utils::Transform};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
#[serde(remote = "Transform")]
|
|
||||||
pub enum TransformDef {
|
|
||||||
Normal,
|
|
||||||
_90,
|
|
||||||
_180,
|
|
||||||
_270,
|
|
||||||
Flipped,
|
|
||||||
Flipped90,
|
|
||||||
Flipped180,
|
|
||||||
Flipped270,
|
|
||||||
}
|
|
||||||
|
|
|
||||||
25
src/state.rs
25
src/state.rs
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
winit::WinitState,
|
winit::WinitState,
|
||||||
x11::X11State,
|
x11::X11State,
|
||||||
},
|
},
|
||||||
config::{Config, OutputConfig, OutputState, ScreenFilter},
|
config::{CompOutputConfig, Config, ScreenFilter},
|
||||||
input::{gestures::GestureState, PointerFocusState},
|
input::{gestures::GestureState, PointerFocusState},
|
||||||
shell::{grabs::SeatMoveGrabState, CosmicSurface, SeatExt, Shell},
|
shell::{grabs::SeatMoveGrabState, CosmicSurface, SeatExt, Shell},
|
||||||
utils::prelude::OutputExt,
|
utils::prelude::OutputExt,
|
||||||
|
|
@ -31,6 +31,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use calloop::RegistrationToken;
|
use calloop::RegistrationToken;
|
||||||
|
use cosmic_comp_config::output::{OutputConfig, OutputState};
|
||||||
use i18n_embed::{
|
use i18n_embed::{
|
||||||
fluent::{fluent_language_loader, FluentLanguageLoader},
|
fluent::{fluent_language_loader, FluentLanguageLoader},
|
||||||
DesktopLanguageRequester,
|
DesktopLanguageRequester,
|
||||||
|
|
@ -455,28 +456,30 @@ impl<'a> LockedBackend<'a> {
|
||||||
// update outputs, so that `OutputModeSource`s are correct
|
// update outputs, so that `OutputModeSource`s are correct
|
||||||
for output in &all_outputs {
|
for output in &all_outputs {
|
||||||
// apply to Output
|
// apply to Output
|
||||||
let final_config = output
|
let final_config = CompOutputConfig(
|
||||||
.user_data()
|
output
|
||||||
.get::<RefCell<OutputConfig>>()
|
.user_data()
|
||||||
.unwrap()
|
.get::<RefCell<OutputConfig>>()
|
||||||
.borrow();
|
.unwrap()
|
||||||
|
.borrow(),
|
||||||
|
);
|
||||||
|
|
||||||
let mode = Some(final_config.output_mode()).filter(|m| match output.current_mode() {
|
let mode = Some(final_config.output_mode()).filter(|m| match output.current_mode() {
|
||||||
None => true,
|
None => true,
|
||||||
Some(c_m) => m.size != c_m.size || m.refresh != c_m.refresh,
|
Some(c_m) => m.size != c_m.size || m.refresh != c_m.refresh,
|
||||||
});
|
});
|
||||||
let transform =
|
let transform =
|
||||||
Some(final_config.transform.into()).filter(|x| *x != output.current_transform());
|
Some(final_config.transform()).filter(|x| *x != output.current_transform());
|
||||||
let scale = Some(final_config.scale)
|
let scale = Some(final_config.0.scale)
|
||||||
.filter(|x| *x != output.current_scale().fractional_scale());
|
.filter(|x| *x != output.current_scale().fractional_scale());
|
||||||
let location = Some(Point::from((
|
let location = Some(Point::from((
|
||||||
final_config.position.0 as i32,
|
final_config.0.position.0 as i32,
|
||||||
final_config.position.1 as i32,
|
final_config.0.position.1 as i32,
|
||||||
)))
|
)))
|
||||||
.filter(|x| *x != output.current_location());
|
.filter(|x| *x != output.current_location());
|
||||||
output.change_current_state(mode, transform, scale.map(Scale::Fractional), location);
|
output.change_current_state(mode, transform, scale.map(Scale::Fractional), location);
|
||||||
|
|
||||||
output.set_adaptive_sync(final_config.vrr);
|
output.set_adaptive_sync(final_config.0.vrr);
|
||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use cosmic_comp_config::output::{AdaptiveSync, OutputConfig, OutputState};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::drm::VrrSupport as Support,
|
backend::drm::VrrSupport as Support,
|
||||||
output::{Output, WeakOutput},
|
output::{Output, WeakOutput},
|
||||||
|
|
@ -8,10 +9,7 @@ pub use super::geometry::*;
|
||||||
pub use crate::shell::{SeatExt, Shell, Workspace};
|
pub use crate::shell::{SeatExt, Shell, Workspace};
|
||||||
pub use crate::state::{Common, State};
|
pub use crate::state::{Common, State};
|
||||||
pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups;
|
pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups;
|
||||||
use crate::{
|
use crate::{config::EdidProduct, shell::zoom::OutputZoomState};
|
||||||
config::{AdaptiveSync, EdidProduct, OutputConfig, OutputState},
|
|
||||||
shell::zoom::OutputZoomState,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Ref, RefCell, RefMut},
|
cell::{Ref, RefCell, RefMut},
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use cosmic_comp_config::output::{OutputConfig, OutputState, TransformDef};
|
||||||
use smithay::{output::Output, utils::Point};
|
use smithay::{output::Output, utils::Point};
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{OutputConfig, OutputState},
|
|
||||||
state::State,
|
state::State,
|
||||||
utils::prelude::OutputExt,
|
utils::prelude::OutputExt,
|
||||||
wayland::protocols::output_configuration::{
|
wayland::protocols::output_configuration::{
|
||||||
|
|
@ -123,7 +123,16 @@ impl State {
|
||||||
current_config.scale = *scale;
|
current_config.scale = *scale;
|
||||||
}
|
}
|
||||||
if let Some(transform) = transform {
|
if let Some(transform) = transform {
|
||||||
current_config.transform = *transform;
|
current_config.transform = match transform {
|
||||||
|
smithay::utils::Transform::Normal => TransformDef::Normal,
|
||||||
|
smithay::utils::Transform::_90 => TransformDef::_90,
|
||||||
|
smithay::utils::Transform::_180 => TransformDef::_180,
|
||||||
|
smithay::utils::Transform::_270 => TransformDef::_270,
|
||||||
|
smithay::utils::Transform::Flipped => TransformDef::Flipped,
|
||||||
|
smithay::utils::Transform::Flipped90 => TransformDef::Flipped90,
|
||||||
|
smithay::utils::Transform::Flipped180 => TransformDef::Flipped180,
|
||||||
|
smithay::utils::Transform::Flipped270 => TransformDef::Flipped270,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Some(position) = position {
|
if let Some(position) = position {
|
||||||
current_config.position = (position.x as u32, position.y as u32);
|
current_config.position = (position.x as u32, position.y as u32);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ use smithay::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use cosmic_comp_config::output::OutputState as EnabledState;
|
||||||
|
|
||||||
use cosmic_protocols::output_management::v1::server::{
|
use cosmic_protocols::output_management::v1::server::{
|
||||||
zcosmic_output_configuration_head_v1::{self, ZcosmicOutputConfigurationHeadV1},
|
zcosmic_output_configuration_head_v1::{self, ZcosmicOutputConfigurationHeadV1},
|
||||||
zcosmic_output_configuration_v1::{self, ZcosmicOutputConfigurationV1},
|
zcosmic_output_configuration_v1::{self, ZcosmicOutputConfigurationV1},
|
||||||
|
|
@ -21,7 +23,7 @@ use cosmic_protocols::output_management::v1::server::{
|
||||||
zcosmic_output_manager_v1::{self, ZcosmicOutputManagerV1},
|
zcosmic_output_manager_v1::{self, ZcosmicOutputManagerV1},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{config::OutputState as EnabledState, wayland::protocols::output_configuration::*};
|
use crate::wayland::protocols::output_configuration::*;
|
||||||
|
|
||||||
impl<D> GlobalDispatch<ZcosmicOutputManagerV1, OutputMngrGlobalData, D>
|
impl<D> GlobalDispatch<ZcosmicOutputManagerV1, OutputMngrGlobalData, D>
|
||||||
for OutputConfigurationState<D>
|
for OutputConfigurationState<D>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use calloop::{
|
||||||
timer::{TimeoutAction, Timer},
|
timer::{TimeoutAction, Timer},
|
||||||
LoopHandle,
|
LoopHandle,
|
||||||
};
|
};
|
||||||
|
use cosmic_comp_config::output::AdaptiveSync;
|
||||||
use cosmic_protocols::output_management::v1::server::{
|
use cosmic_protocols::output_management::v1::server::{
|
||||||
zcosmic_output_configuration_head_v1::ZcosmicOutputConfigurationHeadV1,
|
zcosmic_output_configuration_head_v1::ZcosmicOutputConfigurationHeadV1,
|
||||||
zcosmic_output_configuration_v1::ZcosmicOutputConfigurationV1,
|
zcosmic_output_configuration_v1::ZcosmicOutputConfigurationV1,
|
||||||
|
|
@ -580,4 +581,4 @@ macro_rules! delegate_output_configuration {
|
||||||
}
|
}
|
||||||
pub(crate) use delegate_output_configuration;
|
pub(crate) use delegate_output_configuration;
|
||||||
|
|
||||||
use crate::{config::AdaptiveSync, utils::prelude::OutputExt};
|
use crate::utils::prelude::OutputExt;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue