2022-04-25 23:00:30 +02:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
|
|
|
|
|
|
use crate::{
|
2023-09-07 13:28:08 -07:00
|
|
|
shell::Shell,
|
2023-09-29 21:33:16 +02:00
|
|
|
state::{BackendData, State},
|
2022-11-11 21:36:42 +01:00
|
|
|
wayland::protocols::output_configuration::OutputConfigurationState,
|
2022-04-25 23:00:30 +02:00
|
|
|
};
|
2023-08-29 13:49:41 -07:00
|
|
|
use cosmic_config::ConfigGet;
|
2022-04-25 23:00:30 +02:00
|
|
|
use serde::{Deserialize, Serialize};
|
2023-07-27 14:06:43 -07:00
|
|
|
use smithay::input::Seat;
|
2022-04-25 23:00:30 +02:00
|
|
|
pub use smithay::{
|
|
|
|
|
backend::input::KeyState,
|
2022-08-31 13:01:23 +02:00
|
|
|
input::keyboard::{keysyms as KeySyms, Keysym, ModifiersState},
|
2022-09-09 20:00:00 -07:00
|
|
|
output::{Mode, Output},
|
2022-05-12 14:04:21 +02:00
|
|
|
reexports::{
|
|
|
|
|
calloop::LoopHandle,
|
|
|
|
|
input::{
|
|
|
|
|
AccelProfile, ClickMethod, Device as InputDevice, ScrollMethod, SendEventsMode,
|
|
|
|
|
TapButtonMap,
|
|
|
|
|
},
|
2022-05-03 13:37:51 +02:00
|
|
|
},
|
2022-04-25 23:00:30 +02:00
|
|
|
utils::{Logical, Physical, Point, Size, Transform},
|
|
|
|
|
};
|
|
|
|
|
use std::{cell::RefCell, collections::HashMap, fs::OpenOptions, path::PathBuf};
|
2023-02-24 17:41:52 +01:00
|
|
|
use tracing::{debug, error, info, warn};
|
2022-04-25 23:00:30 +02:00
|
|
|
|
2023-07-27 14:22:46 -07:00
|
|
|
mod input_config;
|
2023-07-27 14:06:43 -07:00
|
|
|
mod key_bindings;
|
|
|
|
|
pub use key_bindings::{Action, KeyModifier, KeyModifiers, KeyPattern};
|
2022-04-25 23:00:30 +02:00
|
|
|
mod types;
|
|
|
|
|
pub use self::types::*;
|
2023-09-07 13:28:08 -07:00
|
|
|
use cosmic_comp_config::{
|
|
|
|
|
input::InputConfig,
|
|
|
|
|
workspace::{WorkspaceConfig, WorkspaceLayout},
|
|
|
|
|
XkbConfig,
|
|
|
|
|
};
|
2022-04-25 23:00:30 +02:00
|
|
|
|
2023-07-12 18:54:53 +02:00
|
|
|
#[derive(Debug)]
|
2022-04-25 23:00:30 +02:00
|
|
|
pub struct Config {
|
|
|
|
|
pub static_conf: StaticConfig,
|
|
|
|
|
pub dynamic_conf: DynamicConfig,
|
2023-07-28 12:54:02 -07:00
|
|
|
pub config: cosmic_config::Config,
|
|
|
|
|
pub xkb: XkbConfig,
|
2023-08-29 13:49:41 -07:00
|
|
|
pub input_default: InputConfig,
|
|
|
|
|
pub input_touchpad: InputConfig,
|
2023-07-28 12:54:02 -07:00
|
|
|
pub input_devices: HashMap<String, InputConfig>,
|
2023-09-07 13:28:08 -07:00
|
|
|
pub workspace: WorkspaceConfig,
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
|
pub struct StaticConfig {
|
2023-07-27 14:06:43 -07:00
|
|
|
pub key_bindings: HashMap<key_bindings::KeyPattern, key_bindings::Action>,
|
2023-01-27 13:26:28 +01:00
|
|
|
pub tiling_enabled: bool,
|
2023-11-14 00:47:43 +04:00
|
|
|
pub data_control_enabled: bool,
|
2022-07-04 15:28:03 +02:00
|
|
|
}
|
|
|
|
|
|
2023-12-07 19:27:00 +00:00
|
|
|
impl StaticConfig {
|
|
|
|
|
pub fn get_shortcut_for_action(&self, action: &Action) -> Option<String> {
|
|
|
|
|
let possible_variants = self
|
|
|
|
|
.key_bindings
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(_, a)| *a == action)
|
|
|
|
|
.map(|(b, _)| b)
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
|
|
possible_variants
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|b| b.key.is_none()) // prefer short bindings
|
|
|
|
|
.or_else(|| {
|
|
|
|
|
possible_variants
|
|
|
|
|
.iter() // prefer bindings containing arrow keys
|
|
|
|
|
.find(|b| {
|
|
|
|
|
matches!(
|
|
|
|
|
b.key,
|
|
|
|
|
Some(Keysym::Down)
|
|
|
|
|
| Some(Keysym::Up)
|
|
|
|
|
| Some(Keysym::Left)
|
|
|
|
|
| Some(Keysym::Right)
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.or_else(|| possible_variants.first()) // take the first one
|
|
|
|
|
.map(|binding| binding.to_string())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-12 18:54:53 +02:00
|
|
|
#[derive(Debug)]
|
2022-04-25 23:00:30 +02:00
|
|
|
pub struct DynamicConfig {
|
|
|
|
|
outputs: (Option<PathBuf>, OutputsConfig),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn default_enabled() -> bool {
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
|
|
|
|
pub struct OutputConfig {
|
|
|
|
|
pub mode: ((i32, i32), Option<u32>),
|
|
|
|
|
pub vrr: bool,
|
|
|
|
|
pub scale: f64,
|
|
|
|
|
#[serde(with = "TransformDef")]
|
|
|
|
|
pub transform: Transform,
|
|
|
|
|
pub position: (i32, i32),
|
|
|
|
|
#[serde(default = "default_enabled")]
|
|
|
|
|
pub enabled: bool,
|
2023-04-18 19:14:31 +02:00
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub max_bpc: Option<u32>,
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for OutputConfig {
|
|
|
|
|
fn default() -> OutputConfig {
|
|
|
|
|
OutputConfig {
|
|
|
|
|
mode: ((0, 0), None),
|
|
|
|
|
vrr: false,
|
|
|
|
|
scale: 1.0,
|
|
|
|
|
transform: Transform::Normal,
|
|
|
|
|
position: (0, 0),
|
|
|
|
|
enabled: true,
|
2023-04-18 19:14:31 +02:00
|
|
|
max_bpc: None,
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl OutputConfig {
|
|
|
|
|
pub fn mode_size(&self) -> Size<i32, Physical> {
|
|
|
|
|
self.mode.0.into()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn mode_refresh(&self) -> u32 {
|
|
|
|
|
self.mode.1.unwrap_or(60_000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn output_mode(&self) -> Mode {
|
|
|
|
|
Mode {
|
|
|
|
|
size: self.mode_size(),
|
|
|
|
|
refresh: self.mode_refresh() as i32,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Config {
|
2023-09-29 21:33:16 +02:00
|
|
|
pub fn load(loop_handle: &LoopHandle<'_, State>) -> Config {
|
2023-07-28 12:54:02 -07:00
|
|
|
let config = cosmic_config::Config::new("com.system76.CosmicComp", 1).unwrap();
|
|
|
|
|
let source = cosmic_config::calloop::ConfigWatchSource::new(&config).unwrap();
|
|
|
|
|
loop_handle
|
2023-09-29 21:33:16 +02:00
|
|
|
.insert_source(source, |(config, keys), (), state| {
|
|
|
|
|
config_changed(config, keys, state);
|
2023-07-28 12:54:02 -07:00
|
|
|
})
|
|
|
|
|
.expect("Failed to add cosmic-config to the event loop");
|
2022-04-25 23:00:30 +02:00
|
|
|
let xdg = xdg::BaseDirectories::new().ok();
|
2023-09-07 13:28:08 -07:00
|
|
|
let workspace = get_config::<WorkspaceConfig>(&config, "workspaces");
|
2022-04-25 23:00:30 +02:00
|
|
|
Config {
|
2023-09-07 13:28:08 -07:00
|
|
|
static_conf: Self::load_static(xdg.as_ref(), workspace.workspace_layout),
|
2022-04-25 23:00:30 +02:00
|
|
|
dynamic_conf: Self::load_dynamic(xdg.as_ref()),
|
2023-12-20 16:52:44 -08:00
|
|
|
xkb: get_config(&config, "xkb_config"),
|
|
|
|
|
input_default: get_config(&config, "input_default"),
|
|
|
|
|
input_touchpad: get_config(&config, "input_touchpad"),
|
|
|
|
|
input_devices: get_config(&config, "input_devices"),
|
2023-09-07 13:28:08 -07:00
|
|
|
workspace,
|
2023-07-28 12:54:02 -07:00
|
|
|
config,
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-07 13:28:08 -07:00
|
|
|
fn load_static(
|
|
|
|
|
xdg: Option<&xdg::BaseDirectories>,
|
|
|
|
|
workspace_layout: WorkspaceLayout,
|
|
|
|
|
) -> StaticConfig {
|
2022-04-25 23:00:30 +02:00
|
|
|
let mut locations = if let Some(base) = xdg {
|
|
|
|
|
vec![
|
|
|
|
|
base.get_config_file("cosmic-comp.ron"),
|
|
|
|
|
base.get_config_file("cosmic-comp/config.ron"),
|
|
|
|
|
]
|
|
|
|
|
} else {
|
|
|
|
|
Vec::with_capacity(3)
|
|
|
|
|
};
|
|
|
|
|
if cfg!(debug_assertions) {
|
|
|
|
|
if let Ok(mut cwd) = std::env::current_dir() {
|
|
|
|
|
cwd.push("config.ron");
|
|
|
|
|
locations.push(cwd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
locations.push(PathBuf::from("/etc/cosmic-comp/config.ron"));
|
|
|
|
|
locations.push(PathBuf::from("/etc/cosmic-comp.ron"));
|
|
|
|
|
|
|
|
|
|
for path in locations {
|
2023-02-24 17:41:52 +01:00
|
|
|
debug!("Trying config location: {}", path.display());
|
2022-04-25 23:00:30 +02:00
|
|
|
if path.exists() {
|
2023-02-24 17:41:52 +01:00
|
|
|
info!("Using config at {}", path.display());
|
2023-05-25 00:10:24 +02:00
|
|
|
let mut config: StaticConfig =
|
|
|
|
|
ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap())
|
|
|
|
|
.expect("Malformed config file");
|
|
|
|
|
|
2023-09-07 13:28:08 -07:00
|
|
|
key_bindings::add_default_bindings(&mut config.key_bindings, workspace_layout);
|
2023-05-25 00:10:24 +02:00
|
|
|
|
|
|
|
|
return config;
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StaticConfig {
|
|
|
|
|
key_bindings: HashMap::new(),
|
2023-01-27 13:26:28 +01:00
|
|
|
tiling_enabled: false,
|
2023-11-14 00:47:43 +04:00
|
|
|
data_control_enabled: false,
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn load_dynamic(xdg: Option<&xdg::BaseDirectories>) -> DynamicConfig {
|
|
|
|
|
let output_path =
|
|
|
|
|
xdg.and_then(|base| base.place_state_file("cosmic-comp/outputs.ron").ok());
|
|
|
|
|
let outputs = Self::load_outputs(&output_path);
|
|
|
|
|
|
|
|
|
|
DynamicConfig {
|
|
|
|
|
outputs: (output_path, outputs),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn load_outputs(path: &Option<PathBuf>) -> OutputsConfig {
|
|
|
|
|
if let Some(path) = path.as_ref() {
|
|
|
|
|
if path.exists() {
|
|
|
|
|
match ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap()) {
|
|
|
|
|
Ok(config) => return config,
|
|
|
|
|
Err(err) => {
|
2023-02-24 17:41:52 +01:00
|
|
|
warn!(?err, "Failed to read output_config, resetting..");
|
2022-04-25 23:00:30 +02:00
|
|
|
if let Err(err) = std::fs::remove_file(path) {
|
2023-02-24 17:41:52 +01:00
|
|
|
error!(?err, "Failed to remove output_config.");
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutputsConfig {
|
|
|
|
|
config: HashMap::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-05-03 13:37:51 +02:00
|
|
|
|
2022-04-25 23:00:30 +02:00
|
|
|
pub fn read_outputs(
|
|
|
|
|
&mut self,
|
2022-11-11 21:36:42 +01:00
|
|
|
output_state: &mut OutputConfigurationState<State>,
|
2022-04-25 23:00:30 +02:00
|
|
|
backend: &mut BackendData,
|
|
|
|
|
shell: &mut Shell,
|
2022-11-22 10:07:17 +01:00
|
|
|
seats: impl Iterator<Item = Seat<State>>,
|
2023-09-29 21:33:16 +02:00
|
|
|
loop_handle: &LoopHandle<'_, State>,
|
2022-04-25 23:00:30 +02:00
|
|
|
) {
|
2022-11-22 10:07:17 +01:00
|
|
|
let seats = seats.collect::<Vec<_>>();
|
2022-11-11 21:36:42 +01:00
|
|
|
let outputs = output_state.outputs().collect::<Vec<_>>();
|
2022-04-25 23:00:30 +02:00
|
|
|
let mut infos = outputs
|
|
|
|
|
.iter()
|
|
|
|
|
.cloned()
|
|
|
|
|
.map(Into::<crate::config::OutputInfo>::into)
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
infos.sort();
|
|
|
|
|
if let Some(configs) = self.dynamic_conf.outputs().config.get(&infos).cloned() {
|
|
|
|
|
let mut reset = false;
|
|
|
|
|
let known_good_configs = outputs
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|output| {
|
|
|
|
|
output
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<RefCell<OutputConfig>>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.borrow()
|
|
|
|
|
.clone()
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
|
|
for (name, output_config) in infos.iter().map(|o| &o.connector).zip(configs.into_iter())
|
|
|
|
|
{
|
2022-05-03 13:37:51 +02:00
|
|
|
let output = outputs.iter().find(|o| &o.name() == name).unwrap().clone();
|
2022-11-11 21:36:42 +01:00
|
|
|
let enabled = output_config.enabled;
|
2022-04-25 23:00:30 +02:00
|
|
|
*output
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<RefCell<OutputConfig>>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.borrow_mut() = output_config;
|
2022-11-22 10:07:17 +01:00
|
|
|
if let Err(err) = backend.apply_config_for_output(
|
|
|
|
|
&output,
|
|
|
|
|
false,
|
|
|
|
|
shell,
|
|
|
|
|
seats.iter().cloned(),
|
|
|
|
|
loop_handle,
|
|
|
|
|
) {
|
2023-02-24 17:41:52 +01:00
|
|
|
warn!(
|
|
|
|
|
?err,
|
|
|
|
|
"Failed to set new config for output {}.",
|
2022-04-25 23:00:30 +02:00
|
|
|
output.name(),
|
|
|
|
|
);
|
|
|
|
|
reset = true;
|
|
|
|
|
break;
|
2022-11-11 21:36:42 +01:00
|
|
|
} else {
|
|
|
|
|
if enabled {
|
|
|
|
|
output_state.enable_head(&output);
|
|
|
|
|
} else {
|
|
|
|
|
output_state.disable_head(&output);
|
|
|
|
|
}
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if reset {
|
|
|
|
|
for (output, output_config) in outputs
|
|
|
|
|
.clone()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.zip(known_good_configs.into_iter())
|
|
|
|
|
{
|
2022-11-11 21:36:42 +01:00
|
|
|
let enabled = output_config.enabled;
|
2022-04-25 23:00:30 +02:00
|
|
|
*output
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<RefCell<OutputConfig>>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.borrow_mut() = output_config;
|
2022-11-22 10:07:17 +01:00
|
|
|
if let Err(err) = backend.apply_config_for_output(
|
|
|
|
|
&output,
|
|
|
|
|
false,
|
|
|
|
|
shell,
|
|
|
|
|
seats.iter().cloned(),
|
|
|
|
|
loop_handle,
|
|
|
|
|
) {
|
2023-02-24 17:41:52 +01:00
|
|
|
error!(?err, "Failed to reset config for output {}.", output.name());
|
2022-11-11 21:36:42 +01:00
|
|
|
} else {
|
|
|
|
|
if enabled {
|
|
|
|
|
output_state.enable_head(&output);
|
|
|
|
|
} else {
|
|
|
|
|
output_state.disable_head(&output);
|
|
|
|
|
}
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-11 21:36:42 +01:00
|
|
|
|
2022-12-05 23:36:09 +01:00
|
|
|
output_state.update();
|
|
|
|
|
self.write_outputs(output_state.outputs());
|
|
|
|
|
} else {
|
|
|
|
|
for output in outputs {
|
|
|
|
|
if let Err(err) = backend.apply_config_for_output(
|
|
|
|
|
&output,
|
|
|
|
|
false,
|
|
|
|
|
shell,
|
|
|
|
|
seats.iter().cloned(),
|
|
|
|
|
loop_handle,
|
|
|
|
|
) {
|
2023-02-24 17:41:52 +01:00
|
|
|
warn!(
|
|
|
|
|
?err,
|
|
|
|
|
"Failed to set new config for output {}.",
|
2022-12-05 23:36:09 +01:00
|
|
|
output.name(),
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
if output
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<RefCell<OutputConfig>>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.borrow()
|
|
|
|
|
.enabled
|
|
|
|
|
{
|
|
|
|
|
output_state.enable_head(&output);
|
|
|
|
|
} else {
|
|
|
|
|
output_state.disable_head(&output);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-11 21:36:42 +01:00
|
|
|
output_state.update();
|
|
|
|
|
self.write_outputs(output_state.outputs());
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-05-03 13:37:51 +02:00
|
|
|
|
|
|
|
|
pub fn write_outputs(
|
|
|
|
|
&mut self,
|
|
|
|
|
outputs: impl Iterator<Item = impl std::borrow::Borrow<Output>>,
|
|
|
|
|
) {
|
2022-04-25 23:00:30 +02:00
|
|
|
let mut infos = outputs
|
|
|
|
|
.map(|o| {
|
|
|
|
|
let o = o.borrow();
|
|
|
|
|
(
|
|
|
|
|
Into::<crate::config::OutputInfo>::into(o.clone()),
|
|
|
|
|
o.user_data()
|
|
|
|
|
.get::<RefCell<OutputConfig>>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.borrow()
|
|
|
|
|
.clone(),
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<(OutputInfo, OutputConfig)>>();
|
|
|
|
|
infos.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
|
|
|
|
|
let (infos, configs) = infos.into_iter().unzip();
|
2022-05-03 13:37:51 +02:00
|
|
|
self.dynamic_conf
|
2022-04-25 23:00:30 +02:00
|
|
|
.outputs_mut()
|
|
|
|
|
.config
|
|
|
|
|
.insert(infos, configs);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-29 14:23:23 +02:00
|
|
|
pub fn xkb_config(&self) -> XkbConfig {
|
2023-07-28 12:54:02 -07:00
|
|
|
self.xkb.clone()
|
2022-07-29 14:23:23 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-29 13:49:41 -07:00
|
|
|
pub fn read_device(&self, device: &mut InputDevice) {
|
2023-09-01 12:33:55 -07:00
|
|
|
let (device_config, default_config) = self.get_device_config(device);
|
|
|
|
|
input_config::update_device(device, device_config, default_config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn scroll_factor(&self, device: &InputDevice) -> f64 {
|
|
|
|
|
let (device_config, default_config) = self.get_device_config(device);
|
|
|
|
|
input_config::get_config(device_config, default_config, |x| {
|
|
|
|
|
x.scroll_config.as_ref()?.scroll_factor
|
|
|
|
|
})
|
|
|
|
|
.map_or(1.0, |x| x.0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_device_config(&self, device: &InputDevice) -> (Option<&InputConfig>, &InputConfig) {
|
2023-08-29 13:49:41 -07:00
|
|
|
let default_config = if device.config_tap_finger_count() > 0 {
|
|
|
|
|
&self.input_touchpad
|
|
|
|
|
} else {
|
|
|
|
|
&self.input_default
|
|
|
|
|
};
|
|
|
|
|
let device_config = self.input_devices.get(device.name());
|
2023-09-01 12:33:55 -07:00
|
|
|
(device_config, default_config)
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct PersistenceGuard<'a, T: Serialize>(Option<PathBuf>, &'a mut T);
|
|
|
|
|
|
|
|
|
|
impl<'a, T: Serialize> std::ops::Deref for PersistenceGuard<'a, T> {
|
|
|
|
|
type Target = T;
|
|
|
|
|
fn deref(&self) -> &T {
|
|
|
|
|
&self.1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a, T: Serialize> std::ops::DerefMut for PersistenceGuard<'a, T> {
|
|
|
|
|
fn deref_mut(&mut self) -> &mut T {
|
|
|
|
|
&mut self.1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a, T: Serialize> Drop for PersistenceGuard<'a, T> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
if let Some(path) = self.0.as_ref() {
|
|
|
|
|
let writer = match OpenOptions::new()
|
|
|
|
|
.create(true)
|
|
|
|
|
.truncate(true)
|
|
|
|
|
.write(true)
|
|
|
|
|
.open(path)
|
|
|
|
|
{
|
|
|
|
|
Ok(writer) => writer,
|
|
|
|
|
Err(err) => {
|
2023-02-24 17:41:52 +01:00
|
|
|
warn!(?err, "Failed to persist {}.", path.display());
|
2022-04-25 23:00:30 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
if let Err(err) = ron::ser::to_writer_pretty(writer, &self.1, Default::default()) {
|
2023-02-24 17:41:52 +01:00
|
|
|
warn!(?err, "Failed to persist {}", path.display());
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl DynamicConfig {
|
|
|
|
|
pub fn outputs(&self) -> &OutputsConfig {
|
|
|
|
|
&self.outputs.1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn outputs_mut<'a>(&'a mut self) -> PersistenceGuard<'a, OutputsConfig> {
|
|
|
|
|
PersistenceGuard(self.outputs.0.clone(), &mut self.outputs.1)
|
|
|
|
|
}
|
2023-07-28 12:54:02 -07:00
|
|
|
}
|
2022-05-03 13:37:51 +02:00
|
|
|
|
2023-07-28 12:54:02 -07:00
|
|
|
fn get_config<T: Default + serde::de::DeserializeOwned>(
|
|
|
|
|
config: &cosmic_config::Config,
|
|
|
|
|
key: &str,
|
|
|
|
|
) -> T {
|
|
|
|
|
config.get(key).unwrap_or_else(|err| {
|
|
|
|
|
error!(?err, "Failed to read config '{}'", key);
|
|
|
|
|
T::default()
|
|
|
|
|
})
|
|
|
|
|
}
|
2022-04-25 23:00:30 +02:00
|
|
|
|
2023-08-29 13:49:41 -07:00
|
|
|
fn update_input(state: &mut State) {
|
|
|
|
|
if let BackendData::Kms(ref mut kms_state) = &mut state.backend {
|
|
|
|
|
for device in kms_state.input_devices.values_mut() {
|
|
|
|
|
state.common.config.read_device(device);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-28 12:54:02 -07:00
|
|
|
fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut State) {
|
|
|
|
|
for key in &keys {
|
|
|
|
|
match key.as_str() {
|
|
|
|
|
"xkb-config" => {
|
|
|
|
|
let value = get_config::<XkbConfig>(&config, "xkb-config");
|
|
|
|
|
for seat in state.common.seats().cloned().collect::<Vec<_>>().iter() {
|
|
|
|
|
if let Some(keyboard) = seat.get_keyboard() {
|
2023-08-29 13:49:41 -07:00
|
|
|
if let Err(err) = keyboard.set_xkb_config(state, xkb_config_to_wl(&value)) {
|
2023-07-28 12:54:02 -07:00
|
|
|
error!(?err, "Failed to load provided xkb config");
|
|
|
|
|
// TODO Revert to default?
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
state.common.config.xkb = value;
|
|
|
|
|
}
|
2023-12-20 16:52:44 -08:00
|
|
|
"input_default" => {
|
|
|
|
|
let value = get_config::<InputConfig>(&config, "input_default");
|
2023-08-29 13:49:41 -07:00
|
|
|
state.common.config.input_default = value;
|
|
|
|
|
update_input(state);
|
|
|
|
|
}
|
2023-12-20 16:52:44 -08:00
|
|
|
"input_touchpad" => {
|
|
|
|
|
let value = get_config::<InputConfig>(&config, "input_touchpad");
|
2023-08-29 13:49:41 -07:00
|
|
|
state.common.config.input_touchpad = value;
|
|
|
|
|
update_input(state);
|
|
|
|
|
}
|
2023-12-20 16:52:44 -08:00
|
|
|
"input_devices" => {
|
|
|
|
|
let value = get_config::<HashMap<String, InputConfig>>(&config, "input_devices");
|
2023-07-28 12:54:02 -07:00
|
|
|
state.common.config.input_devices = value;
|
2023-08-29 13:49:41 -07:00
|
|
|
update_input(state);
|
2023-07-28 12:54:02 -07:00
|
|
|
}
|
2023-09-07 13:28:08 -07:00
|
|
|
"workspaces" => {
|
|
|
|
|
state.common.config.workspace =
|
|
|
|
|
get_config::<WorkspaceConfig>(&config, "workspaces");
|
|
|
|
|
state.common.shell.update_config(&state.common.config);
|
|
|
|
|
}
|
2023-07-28 12:54:02 -07:00
|
|
|
_ => {}
|
|
|
|
|
}
|
2022-04-25 23:00:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-08-29 13:49:41 -07:00
|
|
|
|
|
|
|
|
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(),
|
|
|
|
|
}
|
|
|
|
|
}
|