mirror of
https://github.com/outbackdingo/hyprcosmic-comp.git
synced 2026-08-25 14:53:22 +00:00
Fork of pop-os/cosmic-comp @ upstream
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "cosmic-comp-config"
|
||||
version = "1.0.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
cosmic-config = { git = "https://github.com/pop-os/libcosmic/" }
|
||||
cosmic-randr-shell = { git = "https://github.com/pop-os/cosmic-randr/", optional = true }
|
||||
input = "0.10.0"
|
||||
libdisplay-info = { version = "0.3.0", optional = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
ron = { version = "0.12", optional = true }
|
||||
tracing = { version = "0.1.44", features = [
|
||||
"max_level_debug",
|
||||
"release_max_level_info",
|
||||
], optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
output = ["ron", "tracing"]
|
||||
randr = ["cosmic-randr-shell", "output"]
|
||||
@@ -0,0 +1,216 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
pub use input::{AccelProfile, ClickMethod, ScrollMethod, TapButtonMap};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// Note: For the following values, None is used to represent the system default
|
||||
// Configuration for input devices
|
||||
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
|
||||
pub struct InputConfig {
|
||||
pub state: DeviceState,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub acceleration: Option<AccelConfig>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub calibration: Option<[f32; 6]>,
|
||||
#[serde(with = "ClickMethodDef")]
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub click_method: Option<ClickMethod>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub disable_while_typing: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub left_handed: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub middle_button_emulation: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub rotation_angle: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub scroll_config: Option<ScrollConfig>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub tap_config: Option<TapConfig>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub map_to_output: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
|
||||
pub struct AccelConfig {
|
||||
#[serde(with = "AccelProfileDef")]
|
||||
pub profile: Option<AccelProfile>,
|
||||
pub speed: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
|
||||
pub struct ScrollConfig {
|
||||
#[serde(with = "ScrollMethodDef")]
|
||||
pub method: Option<ScrollMethod>,
|
||||
pub natural_scroll: Option<bool>,
|
||||
pub scroll_button: Option<u32>,
|
||||
pub scroll_factor: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
pub enum DeviceState {
|
||||
#[default]
|
||||
Enabled,
|
||||
Disabled,
|
||||
DisabledOnExternalMouse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum TouchpadOverride {
|
||||
#[default]
|
||||
None,
|
||||
ForceDisable,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct TapConfig {
|
||||
pub enabled: bool,
|
||||
#[serde(with = "TapButtonMapDef")]
|
||||
pub button_map: Option<TapButtonMap>,
|
||||
pub drag: bool,
|
||||
pub drag_lock: bool,
|
||||
}
|
||||
|
||||
mod ClickMethodDef {
|
||||
use input::ClickMethod as ClickMethodOrig;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum ClickMethod {
|
||||
ButtonAreas,
|
||||
Clickfinger,
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<ClickMethodOrig>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let o = Option::deserialize(deserializer)?;
|
||||
Ok(o.map(|x| match x {
|
||||
ClickMethod::ButtonAreas => ClickMethodOrig::ButtonAreas,
|
||||
ClickMethod::Clickfinger => ClickMethodOrig::Clickfinger,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn serialize<S>(arg: &Option<ClickMethodOrig>, ser: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let arg = match arg {
|
||||
Some(ClickMethodOrig::ButtonAreas) => Some(ClickMethod::ButtonAreas),
|
||||
Some(ClickMethodOrig::Clickfinger) => Some(ClickMethod::Clickfinger),
|
||||
Some(_) | None => None,
|
||||
};
|
||||
Option::serialize(&arg, ser)
|
||||
}
|
||||
}
|
||||
|
||||
mod AccelProfileDef {
|
||||
use input::AccelProfile as AccelProfileOrig;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
enum AccelProfile {
|
||||
Flat,
|
||||
Adaptive,
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<AccelProfileOrig>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let o = Option::deserialize(deserializer)?;
|
||||
Ok(o.map(|x| match x {
|
||||
AccelProfile::Flat => AccelProfileOrig::Flat,
|
||||
AccelProfile::Adaptive => AccelProfileOrig::Adaptive,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn serialize<S>(arg: &Option<AccelProfileOrig>, ser: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let arg = match arg {
|
||||
Some(AccelProfileOrig::Flat) => Some(AccelProfile::Flat),
|
||||
Some(AccelProfileOrig::Adaptive) => Some(AccelProfile::Adaptive),
|
||||
Some(_) | None => None,
|
||||
};
|
||||
Option::serialize(&arg, ser)
|
||||
}
|
||||
}
|
||||
|
||||
mod ScrollMethodDef {
|
||||
use input::ScrollMethod as ScrollMethodOrig;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum ScrollMethod {
|
||||
NoScroll,
|
||||
TwoFinger,
|
||||
Edge,
|
||||
OnButtonDown,
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<ScrollMethodOrig>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let o = Option::deserialize(deserializer)?;
|
||||
Ok(o.map(|x| match x {
|
||||
ScrollMethod::NoScroll => ScrollMethodOrig::NoScroll,
|
||||
ScrollMethod::TwoFinger => ScrollMethodOrig::TwoFinger,
|
||||
ScrollMethod::Edge => ScrollMethodOrig::Edge,
|
||||
ScrollMethod::OnButtonDown => ScrollMethodOrig::OnButtonDown,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn serialize<S>(arg: &Option<ScrollMethodOrig>, ser: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let arg = match arg {
|
||||
Some(ScrollMethodOrig::NoScroll) => Some(ScrollMethod::NoScroll),
|
||||
Some(ScrollMethodOrig::TwoFinger) => Some(ScrollMethod::TwoFinger),
|
||||
Some(ScrollMethodOrig::Edge) => Some(ScrollMethod::Edge),
|
||||
Some(ScrollMethodOrig::OnButtonDown) => Some(ScrollMethod::OnButtonDown),
|
||||
Some(_) | None => None,
|
||||
};
|
||||
Option::serialize(&arg, ser)
|
||||
}
|
||||
}
|
||||
|
||||
mod TapButtonMapDef {
|
||||
use input::TapButtonMap as TapButtonMapOrig;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum TapButtonMap {
|
||||
LeftRightMiddle,
|
||||
LeftMiddleRight,
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<TapButtonMapOrig>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let o = Option::deserialize(deserializer)?;
|
||||
Ok(o.map(|x| match x {
|
||||
TapButtonMap::LeftRightMiddle => TapButtonMapOrig::LeftRightMiddle,
|
||||
TapButtonMap::LeftMiddleRight => TapButtonMapOrig::LeftMiddleRight,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn serialize<S>(arg: &Option<TapButtonMapOrig>, ser: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let arg = match arg {
|
||||
Some(TapButtonMapOrig::LeftRightMiddle) => Some(TapButtonMap::LeftRightMiddle),
|
||||
Some(TapButtonMapOrig::LeftMiddleRight) => Some(TapButtonMap::LeftMiddleRight),
|
||||
Some(_) | None => None,
|
||||
};
|
||||
Option::serialize(&arg, ser)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use cosmic_config::{CosmicConfigEntry, cosmic_config_derive::CosmicConfigEntry};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::input::TouchpadOverride;
|
||||
|
||||
pub mod input;
|
||||
#[cfg(feature = "output")]
|
||||
pub mod output;
|
||||
pub mod workspace;
|
||||
|
||||
#[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>,
|
||||
}
|
||||
|
||||
#[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(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
pub struct KeyboardConfig {
|
||||
/// Boot state for numlock
|
||||
pub numlock_state: NumlockState,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum NumlockState {
|
||||
BootOn,
|
||||
#[default]
|
||||
BootOff,
|
||||
LastBoot,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct AppearanceConfig {
|
||||
pub clip_floating_windows: bool,
|
||||
pub clip_tiled_windows: bool,
|
||||
pub shadow_tiled_windows: bool,
|
||||
}
|
||||
|
||||
impl Default for AppearanceConfig {
|
||||
fn default() -> Self {
|
||||
AppearanceConfig {
|
||||
clip_floating_windows: true,
|
||||
clip_tiled_windows: true,
|
||||
shadow_tiled_windows: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, CosmicConfigEntry)]
|
||||
#[version = 1]
|
||||
pub struct CosmicCompConfig {
|
||||
pub workspaces: workspace::WorkspaceConfig,
|
||||
pub pinned_workspaces: Vec<workspace::PinnedWorkspace>,
|
||||
pub input_default: input::InputConfig,
|
||||
pub input_touchpad: input::InputConfig,
|
||||
pub input_touchpad_override: TouchpadOverride,
|
||||
pub input_devices: HashMap<String, input::InputConfig>,
|
||||
pub xkb_config: XkbConfig,
|
||||
pub keyboard_config: KeyboardConfig,
|
||||
/// Autotiling enabled
|
||||
pub autotile: bool,
|
||||
/// Determines the behavior of the autotile variable
|
||||
/// If set to Global, autotile applies to all windows in all workspaces
|
||||
/// If set to PerWorkspace, autotile only applies to new windows, and new workspaces
|
||||
pub autotile_behavior: TileBehavior,
|
||||
/// Active hint enabled
|
||||
pub active_hint: bool,
|
||||
/// Enables changing keyboard focus to windows when the cursor passes into them
|
||||
pub focus_follows_cursor: bool,
|
||||
/// Enables warping the cursor to the focused window when focus changes due to keyboard input
|
||||
pub cursor_follows_focus: bool,
|
||||
/// The delay in milliseconds before focus follows mouse (if enabled)
|
||||
pub focus_follows_cursor_delay: u64,
|
||||
/// Let X11 applications scale themselves
|
||||
pub descale_xwayland: XwaylandDescaling,
|
||||
/// Let X11 applications snoop on certain key-presses to allow for global shortcuts
|
||||
pub xwayland_eavesdropping: XwaylandEavesdropping,
|
||||
/// The threshold before windows snap themselves to output edges
|
||||
pub edge_snap_threshold: u32,
|
||||
pub accessibility_zoom: ZoomConfig,
|
||||
pub appearance_settings: AppearanceConfig,
|
||||
/// Hide the cursor after this many seconds of pointer inactivity (None disables)
|
||||
pub cursor_hide_timeout: Option<u32>,
|
||||
pub activation_policy: ActivationPolicy,
|
||||
}
|
||||
|
||||
impl Default for CosmicCompConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
workspaces: Default::default(),
|
||||
pinned_workspaces: Vec::new(),
|
||||
input_default: Default::default(),
|
||||
// By default, enable tap-to-click and disable-while-typing.
|
||||
input_touchpad: input::InputConfig {
|
||||
state: input::DeviceState::Enabled,
|
||||
click_method: Some(input::ClickMethod::Clickfinger),
|
||||
disable_while_typing: Some(true),
|
||||
tap_config: Some(input::TapConfig {
|
||||
enabled: true,
|
||||
button_map: Some(input::TapButtonMap::LeftRightMiddle),
|
||||
drag: true,
|
||||
drag_lock: false,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
input_touchpad_override: Default::default(),
|
||||
input_devices: Default::default(),
|
||||
xkb_config: Default::default(),
|
||||
keyboard_config: Default::default(),
|
||||
autotile: Default::default(),
|
||||
autotile_behavior: Default::default(),
|
||||
active_hint: true,
|
||||
focus_follows_cursor: false,
|
||||
cursor_follows_focus: false,
|
||||
focus_follows_cursor_delay: 250,
|
||||
descale_xwayland: XwaylandDescaling::Fractional,
|
||||
xwayland_eavesdropping: XwaylandEavesdropping::default(),
|
||||
edge_snap_threshold: 0,
|
||||
accessibility_zoom: ZoomConfig::default(),
|
||||
appearance_settings: AppearanceConfig::default(),
|
||||
cursor_hide_timeout: None,
|
||||
activation_policy: ActivationPolicy::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub enum TileBehavior {
|
||||
#[default]
|
||||
Global,
|
||||
PerWorkspace,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct XkbConfig {
|
||||
pub rules: String,
|
||||
pub model: String,
|
||||
pub layout: String,
|
||||
pub variant: String,
|
||||
pub options: Option<String>,
|
||||
#[serde(default = "default_repeat_delay")]
|
||||
pub repeat_delay: u32,
|
||||
#[serde(default = "default_repeat_rate")]
|
||||
pub repeat_rate: u32,
|
||||
}
|
||||
|
||||
impl Default for XkbConfig {
|
||||
fn default() -> XkbConfig {
|
||||
XkbConfig {
|
||||
rules: String::new(),
|
||||
model: String::new(),
|
||||
layout: String::new(),
|
||||
variant: String::new(),
|
||||
options: None,
|
||||
repeat_delay: default_repeat_delay(),
|
||||
repeat_rate: default_repeat_rate(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_repeat_rate() -> u32 {
|
||||
25
|
||||
}
|
||||
|
||||
fn default_repeat_delay() -> u32 {
|
||||
600
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct ZoomConfig {
|
||||
pub start_on_login: bool,
|
||||
pub show_overlay: bool,
|
||||
pub increment: u32,
|
||||
pub view_moves: ZoomMovement,
|
||||
pub enable_mouse_zoom_shortcuts: bool,
|
||||
}
|
||||
|
||||
impl Default for ZoomConfig {
|
||||
fn default() -> Self {
|
||||
ZoomConfig {
|
||||
start_on_login: false,
|
||||
show_overlay: true,
|
||||
increment: 50,
|
||||
view_moves: ZoomMovement::Continuously,
|
||||
enable_mouse_zoom_shortcuts: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub enum ZoomMovement {
|
||||
OnEdge,
|
||||
Centered,
|
||||
Continuously,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct XwaylandEavesdropping {
|
||||
pub keyboard: EavesdroppingKeyboardMode,
|
||||
pub pointer: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub enum EavesdroppingKeyboardMode {
|
||||
#[default]
|
||||
None,
|
||||
Modifiers,
|
||||
Combinations,
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub enum ActivationPolicy {
|
||||
#[default]
|
||||
Focus,
|
||||
FocusIfActiveWorkspace,
|
||||
Urgent,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum XwaylandDescaling {
|
||||
#[serde(rename = "true")]
|
||||
Enabled,
|
||||
#[serde(rename = "false")]
|
||||
Disabled,
|
||||
#[default]
|
||||
Fractional,
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, fs::OpenOptions, path::Path};
|
||||
use tracing::{error, warn};
|
||||
|
||||
#[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,
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/// Internal output configurations used by cosmic-comp
|
||||
pub mod comp;
|
||||
|
||||
#[cfg(feature = "randr")]
|
||||
/// cosmic-randr style output configurations
|
||||
pub mod randr;
|
||||
@@ -0,0 +1,95 @@
|
||||
use std::path::Path;
|
||||
|
||||
use cosmic_randr_shell::{AdaptiveSyncState, List};
|
||||
|
||||
use crate::output::comp::OutputState;
|
||||
|
||||
pub struct CompList {
|
||||
infos: Vec<super::comp::OutputInfo>,
|
||||
outputs: Vec<super::comp::OutputConfig>,
|
||||
}
|
||||
|
||||
impl From<CompList> for cosmic_randr_shell::List {
|
||||
fn from(CompList { infos, outputs }: CompList) -> cosmic_randr_shell::List {
|
||||
let mut list = cosmic_randr_shell::List::default();
|
||||
for (info, output) in infos.into_iter().zip(outputs.into_iter()) {
|
||||
let current = list.modes.insert(cosmic_randr_shell::Mode {
|
||||
size: (output.mode.0.0 as u32, output.mode.0.1 as u32),
|
||||
refresh_rate: output.mode.1.unwrap_or_default(),
|
||||
// XXX not in config as far as i can tell
|
||||
preferred: false,
|
||||
});
|
||||
let modes = vec![current];
|
||||
|
||||
// for mode in output. {}
|
||||
list.outputs.insert(cosmic_randr_shell::Output {
|
||||
name: info.connector,
|
||||
enabled: !matches!(output.enabled, OutputState::Disabled),
|
||||
mirroring: match output.enabled {
|
||||
OutputState::Mirroring(m) => Some(m),
|
||||
_ => None,
|
||||
},
|
||||
make: Some(info.make).filter(|make| make != "Unknown"),
|
||||
model: if info.model.as_str() == "Unknown" {
|
||||
String::new()
|
||||
} else {
|
||||
info.model
|
||||
},
|
||||
position: (output.position.0 as i32, output.position.1 as i32),
|
||||
scale: output.scale,
|
||||
transform: Some(match output.transform {
|
||||
crate::output::comp::TransformDef::Normal => {
|
||||
cosmic_randr_shell::Transform::Normal
|
||||
}
|
||||
crate::output::comp::TransformDef::_90 => {
|
||||
cosmic_randr_shell::Transform::Rotate90
|
||||
}
|
||||
crate::output::comp::TransformDef::_180 => {
|
||||
cosmic_randr_shell::Transform::Rotate180
|
||||
}
|
||||
crate::output::comp::TransformDef::_270 => {
|
||||
cosmic_randr_shell::Transform::Rotate270
|
||||
}
|
||||
crate::output::comp::TransformDef::Flipped => {
|
||||
cosmic_randr_shell::Transform::Flipped
|
||||
}
|
||||
crate::output::comp::TransformDef::Flipped90 => {
|
||||
cosmic_randr_shell::Transform::Flipped90
|
||||
}
|
||||
crate::output::comp::TransformDef::Flipped180 => {
|
||||
cosmic_randr_shell::Transform::Flipped180
|
||||
}
|
||||
crate::output::comp::TransformDef::Flipped270 => {
|
||||
cosmic_randr_shell::Transform::Flipped270
|
||||
}
|
||||
}),
|
||||
modes,
|
||||
current: Some(current),
|
||||
adaptive_sync: Some(match output.vrr {
|
||||
crate::output::comp::AdaptiveSync::Enabled => AdaptiveSyncState::Auto,
|
||||
crate::output::comp::AdaptiveSync::Disabled => AdaptiveSyncState::Disabled,
|
||||
crate::output::comp::AdaptiveSync::Force => AdaptiveSyncState::Always,
|
||||
}),
|
||||
xwayland_primary: Some(output.xwayland_primary),
|
||||
// XXX no physical output size in the config
|
||||
physical: (0, 0),
|
||||
adaptive_sync_availability: None,
|
||||
serial_number: String::new(),
|
||||
});
|
||||
}
|
||||
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_outputs(path: Option<impl AsRef<Path>>) -> Vec<List> {
|
||||
let output_config = crate::output::comp::load_outputs(path);
|
||||
output_config
|
||||
.config
|
||||
.into_iter()
|
||||
.map(|(infos, outputs)| {
|
||||
let comp_config = CompList { infos, outputs };
|
||||
List::from(comp_config)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::EdidProduct;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct WorkspaceConfig {
|
||||
pub workspace_mode: WorkspaceMode,
|
||||
#[serde(default)]
|
||||
pub workspace_layout: WorkspaceLayout,
|
||||
#[serde(default)]
|
||||
pub action_on_typing: Action,
|
||||
#[serde(default = "default_wraparound")]
|
||||
pub workspace_wraparound: bool,
|
||||
}
|
||||
|
||||
fn default_wraparound() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
impl Default for WorkspaceConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
workspace_mode: WorkspaceMode::default(),
|
||||
workspace_layout: WorkspaceLayout::default(),
|
||||
action_on_typing: Action::default(),
|
||||
workspace_wraparound: default_wraparound(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum WorkspaceMode {
|
||||
#[default]
|
||||
OutputBound,
|
||||
Global,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum WorkspaceLayout {
|
||||
#[default]
|
||||
Vertical,
|
||||
Horizontal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Action {
|
||||
#[default]
|
||||
None,
|
||||
OpenLauncher,
|
||||
OpenApplications,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct OutputMatch {
|
||||
pub name: String,
|
||||
pub edid: Option<EdidProduct>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PinnedWorkspace {
|
||||
pub output: OutputMatch,
|
||||
pub tiling_enabled: bool,
|
||||
pub id: Option<String>,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
Reference in New Issue
Block a user