cosmic-comp/src/config/mod.rs

1058 lines
35 KiB
Rust
Raw Normal View History

2022-04-25 23:00:30 +02:00
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
2023-06-28 19:20:06 +02:00
shell::{
2023-07-05 23:57:38 +02:00
focus::FocusDirection, grabs::ResizeEdge, layout::tiling::Direction, ResizeDirection,
Shell, WorkspaceAmount,
2023-06-28 19:20:06 +02:00
},
2022-11-11 21:36:42 +01:00
state::{BackendData, Data, State},
wayland::protocols::output_configuration::OutputConfigurationState,
2022-04-25 23:00:30 +02:00
};
use serde::{Deserialize, Serialize};
use smithay::input::{keyboard::xkb::keysym_get_name, 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},
output::{Mode, Output},
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};
use tracing::{debug, error, info, warn};
2022-04-25 23:00:30 +02:00
mod types;
pub use self::types::*;
pub struct Config {
pub static_conf: StaticConfig,
pub dynamic_conf: DynamicConfig,
}
#[derive(Debug, Deserialize)]
pub struct StaticConfig {
pub key_bindings: HashMap<KeyPattern, Action>,
pub workspace_mode: WorkspaceMode,
2022-09-28 12:01:29 +02:00
pub workspace_amount: WorkspaceAmount,
2023-05-25 00:10:24 +02:00
#[serde(default = "default_workspace_layout")]
pub workspace_layout: WorkspaceLayout,
2023-01-27 13:26:28 +01:00
pub tiling_enabled: bool,
#[serde(default = "default_active_hint")]
pub active_hint: u8,
2023-03-09 18:27:29 +01:00
#[serde(default = "default_gaps")]
pub gaps: (u8, u8),
}
2022-07-04 16:00:03 +02:00
#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum WorkspaceMode {
OutputBound,
Global,
2022-04-25 23:00:30 +02:00
}
#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum WorkspaceLayout {
Vertical,
Horizontal,
}
2022-04-25 23:00:30 +02:00
pub struct DynamicConfig {
outputs: (Option<PathBuf>, OutputsConfig),
inputs: (Option<PathBuf>, InputsConfig),
}
#[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
}
fn default_active_hint() -> u8 {
4
}
2023-03-09 18:27:29 +01:00
fn default_gaps() -> (u8, u8) {
(0, 4)
}
2023-05-25 00:10:24 +02:00
fn default_workspace_layout() -> WorkspaceLayout {
WorkspaceLayout::Vertical
}
2022-04-25 23:00:30 +02:00
#[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,
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct InputsConfig {
xkb: XkbConfig,
devices: HashMap<String, InputConfig>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct InputConfig {
state: DeviceState,
2022-05-03 13:37:51 +02:00
#[serde(skip_serializing_if = "Option::is_none", default)]
2022-04-25 23:00:30 +02:00
acceleration: Option<AccelConfig>,
2022-05-03 13:37:51 +02:00
#[serde(skip_serializing_if = "Option::is_none", default)]
2022-04-25 23:00:30 +02:00
calibration: Option<[f32; 6]>,
#[serde(with = "ClickMethodDef")]
2022-05-03 13:37:51 +02:00
#[serde(skip_serializing_if = "Option::is_none", default)]
2022-04-25 23:00:30 +02:00
click_method: Option<ClickMethod>,
2022-05-03 13:37:51 +02:00
#[serde(skip_serializing_if = "Option::is_none", default)]
2022-04-25 23:00:30 +02:00
disable_while_typing: Option<bool>,
2022-05-03 13:37:51 +02:00
#[serde(skip_serializing_if = "Option::is_none", default)]
2022-04-25 23:00:30 +02:00
left_handed: Option<bool>,
2022-05-03 13:37:51 +02:00
#[serde(skip_serializing_if = "Option::is_none", default)]
2022-04-25 23:00:30 +02:00
middle_button_emulation: Option<bool>,
2022-05-03 13:37:51 +02:00
#[serde(skip_serializing_if = "Option::is_none", default)]
2022-04-25 23:00:30 +02:00
rotation_angle: Option<u32>,
2022-05-03 13:37:51 +02:00
#[serde(skip_serializing_if = "Option::is_none", default)]
2022-04-25 23:00:30 +02:00
scroll_config: Option<ScrollConfig>,
2022-05-03 13:37:51 +02:00
#[serde(skip_serializing_if = "Option::is_none", default)]
2022-04-25 23:00:30 +02:00
tap_config: Option<TapConfig>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct AccelConfig {
#[serde(with = "AccelProfileDef")]
profile: Option<AccelProfile>,
speed: f64,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ScrollConfig {
#[serde(with = "ScrollMethodDef")]
method: Option<ScrollMethod>,
natural_scroll: Option<bool>,
scroll_button: Option<u32>,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum DeviceState {
Enabled,
Disabled,
DisabledOnExternalMouse,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct TapConfig {
enabled: bool,
#[serde(with = "TapButtonMapDef")]
button_map: Option<TapButtonMap>,
drag: bool,
drag_lock: bool,
}
impl Config {
pub fn load() -> Config {
let xdg = xdg::BaseDirectories::new().ok();
Config {
static_conf: Self::load_static(xdg.as_ref()),
dynamic_conf: Self::load_dynamic(xdg.as_ref()),
}
}
fn load_static(xdg: Option<&xdg::BaseDirectories>) -> StaticConfig {
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 {
debug!("Trying config location: {}", path.display());
2022-04-25 23:00:30 +02:00
if path.exists() {
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");
let (workspace_previous, workspace_next, output_previous, output_next) =
match config.workspace_layout {
WorkspaceLayout::Horizontal => (
[KeySyms::KEY_Left, KeySyms::KEY_h],
[KeySyms::KEY_Right, KeySyms::KEY_j],
[KeySyms::KEY_Up, KeySyms::KEY_k],
[KeySyms::KEY_Down, KeySyms::KEY_j],
),
WorkspaceLayout::Vertical => (
[KeySyms::KEY_Up, KeySyms::KEY_k],
[KeySyms::KEY_Down, KeySyms::KEY_j],
[KeySyms::KEY_Left, KeySyms::KEY_h],
[KeySyms::KEY_Right, KeySyms::KEY_j],
),
};
fn insert_binding(
key_bindings: &mut HashMap<KeyPattern, Action>,
modifiers: KeyModifiers,
keys: impl Iterator<Item = u32>,
action: Action,
) {
if !key_bindings.values().any(|a| a == &action) {
for key in keys {
let pattern = KeyPattern {
modifiers: modifiers.clone(),
key,
};
if !key_bindings.contains_key(&pattern) {
key_bindings.insert(pattern, action.clone());
}
}
}
}
insert_binding(
&mut config.key_bindings,
KeyModifiers {
logo: true,
ctrl: true,
..Default::default()
},
workspace_previous.iter().copied(),
Action::PreviousWorkspace,
);
insert_binding(
&mut config.key_bindings,
KeyModifiers {
logo: true,
ctrl: true,
..Default::default()
},
workspace_next.iter().copied(),
Action::NextWorkspace,
);
insert_binding(
&mut config.key_bindings,
KeyModifiers {
logo: true,
ctrl: true,
shift: true,
..Default::default()
},
workspace_previous.iter().copied(),
Action::MoveToPreviousWorkspace,
);
insert_binding(
&mut config.key_bindings,
KeyModifiers {
logo: true,
ctrl: true,
shift: true,
..Default::default()
},
workspace_next.iter().copied(),
Action::MoveToNextWorkspace,
);
insert_binding(
&mut config.key_bindings,
KeyModifiers {
logo: true,
ctrl: true,
..Default::default()
},
output_previous.iter().copied(),
Action::PreviousOutput,
);
insert_binding(
&mut config.key_bindings,
KeyModifiers {
logo: true,
ctrl: true,
..Default::default()
},
output_next.iter().copied(),
Action::NextOutput,
);
insert_binding(
&mut config.key_bindings,
KeyModifiers {
logo: true,
ctrl: true,
shift: true,
..Default::default()
},
output_previous.iter().copied(),
Action::MoveToPreviousOutput,
);
insert_binding(
&mut config.key_bindings,
KeyModifiers {
logo: true,
ctrl: true,
shift: true,
..Default::default()
},
output_next.iter().copied(),
Action::MoveToNextOutput,
);
return config;
2022-04-25 23:00:30 +02:00
}
}
StaticConfig {
key_bindings: HashMap::new(),
workspace_mode: WorkspaceMode::Global,
2022-09-28 12:01:29 +02:00
workspace_amount: WorkspaceAmount::Dynamic,
2023-05-25 00:10:24 +02:00
workspace_layout: WorkspaceLayout::Vertical,
2023-01-27 13:26:28 +01:00
tiling_enabled: false,
active_hint: default_active_hint(),
2023-03-09 18:27:29 +01:00
gaps: default_gaps(),
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);
2022-05-03 13:37:51 +02:00
let input_path = xdg.and_then(|base| base.place_state_file("cosmic-comp/inputs.ron").ok());
2022-04-25 23:00:30 +02:00
let inputs = Self::load_inputs(&input_path);
DynamicConfig {
outputs: (output_path, outputs),
inputs: (input_path, inputs),
}
}
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) => {
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) {
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
fn load_inputs(path: &Option<PathBuf>) -> InputsConfig {
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) => {
warn!(?err, "Failed to read input_config, resetting..");
2022-04-25 23:00:30 +02:00
if let Err(err) = std::fs::remove_file(path) {
error!(?err, "Failed to remove input_config.");
2022-04-25 23:00:30 +02:00
}
}
};
}
}
InputsConfig {
xkb: XkbConfig::default(),
devices: 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,
seats: impl Iterator<Item = Seat<State>>,
loop_handle: &LoopHandle<'_, Data>,
2022-04-25 23:00:30 +02: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;
if let Err(err) = backend.apply_config_for_output(
&output,
false,
shell,
seats.iter().cloned(),
loop_handle,
) {
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;
if let Err(err) = backend.apply_config_for_output(
&output,
false,
shell,
seats.iter().cloned(),
loop_handle,
) {
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
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,
) {
warn!(
?err,
"Failed to set new config for output {}.",
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 {
self.dynamic_conf.inputs().xkb.clone()
}
2022-04-25 23:00:30 +02:00
pub fn read_device(&mut self, device: &mut InputDevice) {
use std::collections::hash_map::Entry;
let mut inputs = self.dynamic_conf.inputs_mut();
match inputs.devices.entry(device.name().into()) {
2022-04-25 23:00:30 +02:00
Entry::Occupied(entry) => {
let config = entry.get();
if let Err(err) = match config.state {
2022-05-03 13:37:51 +02:00
DeviceState::Enabled => {
device.config_send_events_set_mode(SendEventsMode::ENABLED)
}
DeviceState::Disabled => {
device.config_send_events_set_mode(SendEventsMode::DISABLED)
}
DeviceState::DisabledOnExternalMouse => device
.config_send_events_set_mode(SendEventsMode::DISABLED_ON_EXTERNAL_MOUSE),
2022-04-25 23:00:30 +02:00
} {
warn!(
?err,
"Failed to apply mode {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
config.state,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
if let Some(accel) = config.acceleration.as_ref() {
if let Some(profile) = accel.profile {
if let Err(err) = device.config_accel_set_profile(profile) {
warn!(
?err,
"Failed to apply acceleration profile {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
profile,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
}
if let Err(err) = device.config_accel_set_speed(accel.speed) {
warn!(
?err,
"Failed to apply acceleration speed {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
accel.speed,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
}
if let Some(matrix) = config.calibration {
if let Err(err) = device.config_calibration_set_matrix(matrix) {
warn!(
?err,
"Failed to apply calibration matrix {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
matrix,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
}
if let Some(method) = config.click_method {
if let Err(err) = device.config_click_set_method(method) {
warn!(
?err,
"Failed to apply click method {:?} for device {:?}.",
method,
device.name(),
);
}
}
2022-04-25 23:00:30 +02:00
if let Some(dwt) = config.disable_while_typing {
if let Err(err) = device.config_dwt_set_enabled(dwt) {
warn!(
?err,
"Failed to apply disable-while-typing {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
dwt,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
}
if let Some(left) = config.left_handed {
if let Err(err) = device.config_left_handed_set(left) {
warn!(
?err,
"Failed to apply left-handed {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
left,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
}
if let Some(middle) = config.middle_button_emulation {
if let Err(err) = device.config_middle_emulation_set_enabled(middle) {
warn!(
?err,
"Failed to apply middle-button-emulation {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
middle,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
}
if let Some(angle) = config.rotation_angle {
if let Err(err) = device.config_rotation_set_angle(angle) {
warn!(
?err,
"Failed to apply rotation-angle {:?} for device {:?}",
2022-05-03 13:37:51 +02:00
angle,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
}
if let Some(scroll) = config.scroll_config.as_ref() {
if let Some(method) = scroll.method {
if let Err(err) = device.config_scroll_set_method(method) {
warn!(
?err,
"Failed to apply scroll method {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
method,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
}
if let Some(natural) = scroll.natural_scroll {
if let Err(err) = device.config_scroll_set_natural_scroll_enabled(natural) {
warn!(
?err,
"Failed to apply natural scrolling {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
natural,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
}
if let Some(button) = scroll.scroll_button {
if let Err(err) = device.config_scroll_set_button(button) {
warn!(
?err,
"Failed to apply scroll button {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
button,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
}
}
if let Some(tap) = config.tap_config.as_ref() {
if let Err(err) = device.config_tap_set_enabled(tap.enabled) {
warn!(
?err,
"Failed to apply tap-to-click {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
tap.enabled,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
if let Some(button_map) = tap.button_map {
if let Err(err) = device.config_tap_set_button_map(button_map) {
warn!(
?err,
"Failed to apply button map {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
button_map,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
}
if let Err(err) = device.config_tap_set_drag_enabled(tap.drag) {
warn!(
?err,
"Failed to apply tap-drag {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
tap.drag,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
if let Err(err) = device.config_tap_set_drag_lock_enabled(tap.drag_lock) {
warn!(
?err,
"Failed to apply tap-drag-lock {:?} for device {:?}.",
2022-05-03 13:37:51 +02:00
tap.drag_lock,
device.name(),
);
2022-04-25 23:00:30 +02:00
}
}
2022-05-03 13:37:51 +02:00
}
2022-04-25 23:00:30 +02:00
Entry::Vacant(entry) => {
entry.insert(InputConfig {
state: match device.config_send_events_mode() {
x if x.contains(SendEventsMode::ENABLED) => DeviceState::Enabled,
2022-05-03 13:37:51 +02:00
x if x.contains(SendEventsMode::DISABLED_ON_EXTERNAL_MOUSE) => {
DeviceState::DisabledOnExternalMouse
}
2022-04-25 23:00:30 +02:00
x if x.contains(SendEventsMode::DISABLED) => DeviceState::Disabled,
_ => DeviceState::Disabled,
},
acceleration: if device.config_accel_is_available() {
Some(AccelConfig {
profile: device.config_accel_profile(),
speed: device.config_accel_speed(),
})
2022-05-03 13:37:51 +02:00
} else {
None
},
2022-04-25 23:00:30 +02:00
calibration: device.config_calibration_matrix(),
click_method: device.config_click_method(),
disable_while_typing: if device.config_dwt_is_available() {
Some(device.config_dwt_enabled())
} else {
None
},
left_handed: if device.config_left_handed_is_available() {
Some(device.config_left_handed())
} else {
None
},
middle_button_emulation: if device.config_middle_emulation_is_available() {
Some(device.config_middle_emulation_enabled())
} else {
None
},
rotation_angle: if device.config_rotation_is_available() {
Some(device.config_rotation_angle())
} else {
None
},
2022-05-03 13:37:51 +02:00
scroll_config: if device
.config_scroll_methods()
.iter()
.any(|x| *x != ScrollMethod::NoScroll)
{
2022-04-25 23:00:30 +02:00
Some(ScrollConfig {
method: device.config_scroll_method(),
natural_scroll: if device.config_scroll_has_natural_scroll() {
Some(device.config_scroll_natural_scroll_enabled())
} else {
None
},
2022-05-03 13:37:51 +02:00
scroll_button: if device.config_scroll_method()
== Some(ScrollMethod::OnButtonDown)
{
2022-04-25 23:00:30 +02:00
Some(device.config_scroll_button())
} else {
None
},
})
2022-05-03 13:37:51 +02:00
} else {
None
},
2022-04-25 23:00:30 +02:00
tap_config: if device.config_tap_finger_count() > 0 {
Some(TapConfig {
enabled: device.config_tap_enabled(),
button_map: device.config_tap_button_map(),
drag: device.config_tap_drag_enabled(),
drag_lock: device.config_tap_drag_lock_enabled(),
})
2022-05-03 13:37:51 +02:00
} else {
None
},
2022-04-25 23:00:30 +02:00
});
2022-05-03 13:37:51 +02:00
}
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) => {
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()) {
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)
}
2022-05-03 13:37:51 +02:00
2022-04-25 23:00:30 +02:00
pub fn inputs(&self) -> &InputsConfig {
&self.inputs.1
}
pub fn inputs_mut<'a>(&'a mut self) -> PersistenceGuard<'a, InputsConfig> {
PersistenceGuard(self.inputs.0.clone(), &mut self.inputs.1)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub enum KeyModifier {
Ctrl,
Alt,
Shift,
2022-11-10 18:42:11 +01:00
Super,
2022-04-25 23:00:30 +02:00
}
2023-05-25 00:10:24 +02:00
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
2022-08-31 13:01:23 +02:00
pub struct KeyModifiers {
pub ctrl: bool,
pub alt: bool,
pub shift: bool,
pub logo: bool,
2022-08-31 13:01:23 +02:00
}
impl PartialEq<ModifiersState> for KeyModifiers {
fn eq(&self, other: &ModifiersState) -> bool {
self.ctrl == other.ctrl
&& self.alt == other.alt
&& self.shift == other.shift
&& self.logo == other.logo
}
}
impl Into<KeyModifiers> for ModifiersState {
fn into(self) -> KeyModifiers {
KeyModifiers {
ctrl: self.ctrl,
alt: self.alt,
shift: self.shift,
logo: self.logo,
}
}
}
2022-04-25 23:00:30 +02:00
impl std::ops::AddAssign<KeyModifier> for KeyModifiers {
fn add_assign(&mut self, rhs: KeyModifier) {
match rhs {
KeyModifier::Ctrl => self.ctrl = true,
KeyModifier::Alt => self.alt = true,
KeyModifier::Shift => self.shift = true,
2022-11-10 18:42:11 +01:00
KeyModifier::Super => self.logo = true,
2022-04-25 23:00:30 +02:00
};
}
}
impl std::ops::BitOr for KeyModifier {
type Output = KeyModifiers;
fn bitor(self, rhs: KeyModifier) -> Self::Output {
let mut modifiers = self.into();
modifiers += rhs;
modifiers
}
}
impl Into<KeyModifiers> for KeyModifier {
fn into(self) -> KeyModifiers {
let mut modifiers = KeyModifiers {
ctrl: false,
alt: false,
shift: false,
logo: false,
};
modifiers += self;
modifiers
}
}
/// Describtion of a key combination that might be
/// handled by the compositor.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Hash)]
#[serde(deny_unknown_fields)]
pub struct KeyPattern {
/// What modifiers are expected to be pressed alongside the key
#[serde(deserialize_with = "deserialize_KeyModifiers")]
pub modifiers: KeyModifiers,
/// The actual key, that was pressed
#[serde(deserialize_with = "deserialize_Keysym")]
pub key: u32,
}
impl KeyPattern {
pub fn new(modifiers: impl Into<KeyModifiers>, key: u32) -> KeyPattern {
KeyPattern {
modifiers: modifiers.into(),
key,
}
}
}
impl ToString for KeyPattern {
fn to_string(&self) -> String {
let mut result = String::new();
if self.modifiers.logo {
result += "Super+";
}
if self.modifiers.ctrl {
result += "Ctrl+";
}
if self.modifiers.alt {
result += "Alt+";
}
if self.modifiers.shift {
result += "Shift+";
}
result += &keysym_get_name(self.key);
result
}
}
2022-04-25 23:00:30 +02:00
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
pub enum Action {
Terminate,
Debug,
Close,
2022-11-10 18:42:11 +01:00
2022-04-25 23:00:30 +02:00
Workspace(u8),
2022-11-10 18:42:11 +01:00
NextWorkspace,
PreviousWorkspace,
LastWorkspace,
2022-04-25 23:00:30 +02:00
MoveToWorkspace(u8),
2022-11-10 18:42:11 +01:00
MoveToNextWorkspace,
MoveToPreviousWorkspace,
MoveToLastWorkspace,
2023-01-24 19:22:00 +01:00
SendToWorkspace(u8),
SendToNextWorkspace,
SendToPreviousWorkspace,
SendToLastWorkspace,
2022-11-10 18:42:11 +01:00
NextOutput,
PreviousOutput,
MoveToNextOutput,
MoveToPreviousOutput,
2023-05-25 00:10:24 +02:00
SendToNextOutput,
SendToPreviousOutput,
2022-11-10 18:42:11 +01:00
2022-04-25 23:00:30 +02:00
Focus(FocusDirection),
2022-10-27 16:11:54 +02:00
Move(Direction),
2022-11-10 18:42:11 +01:00
ToggleOrientation,
2022-04-25 23:00:30 +02:00
Orientation(crate::shell::layout::Orientation),
2022-11-10 18:42:11 +01:00
2023-06-05 17:52:47 +02:00
ToggleStacking,
ToggleTiling,
ToggleWindowFloating,
2022-11-10 18:42:11 +01:00
2023-06-28 19:20:06 +02:00
Resizing(ResizeDirection),
2023-07-05 23:57:38 +02:00
#[serde(skip)]
_ResizingInternal(ResizeDirection, ResizeEdge, KeyState),
2022-11-10 18:42:11 +01:00
Maximize,
2022-04-25 23:00:30 +02:00
Spawn(String),
}