Add config for loading keybindings
This commit is contained in:
parent
4796832521
commit
aab52b502c
7 changed files with 367 additions and 63 deletions
63
Cargo.lock
generated
63
Cargo.lock
generated
|
|
@ -112,6 +112,12 @@ dependencies = [
|
|||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
|
@ -291,6 +297,7 @@ dependencies = [
|
|||
"egui",
|
||||
"id_tree",
|
||||
"indexmap",
|
||||
"ron",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"slog",
|
||||
|
|
@ -303,6 +310,8 @@ dependencies = [
|
|||
"thiserror",
|
||||
"wayland-scanner",
|
||||
"xcursor",
|
||||
"xdg",
|
||||
"xkbcommon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -366,6 +375,15 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
|
|
@ -376,6 +394,17 @@ dependencies = [
|
|||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
|
|
@ -1134,6 +1163,17 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b861ecaade43ac97886a512b360d01d66be9f41f3c61088b42cedf92e03d678"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitflags",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
|
|
@ -1175,6 +1215,20 @@ name = "serde"
|
|||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
|
|
@ -1734,6 +1788,15 @@ dependencies = [
|
|||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xdg"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xkbcommon"
|
||||
version = "0.4.0"
|
||||
|
|
|
|||
|
|
@ -13,14 +13,17 @@ slog-term = "2.8"
|
|||
slog-async = "2.7"
|
||||
slog-scope = "4.4"
|
||||
slog-stdlog = "4.1"
|
||||
serde = { version = "1", optional = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = { version = "1", optional = true }
|
||||
egui = { version = "0.16", optional = true }
|
||||
edid-rs = { version = "0.1" }
|
||||
thiserror = "1.0.26"
|
||||
xcursor = "0.3.3"
|
||||
id_tree = "1.8.0"
|
||||
xkbcommon = "0.4"
|
||||
indexmap = "1.8.0"
|
||||
xdg = "^2.1"
|
||||
ron = "0.7"
|
||||
|
||||
[dependencies.smithay]
|
||||
version = "0.3"
|
||||
|
|
@ -39,7 +42,7 @@ wayland-scanner = "0.29"
|
|||
|
||||
[features]
|
||||
default = []
|
||||
debug = ["egui", "smithay-egui", "serde", "serde_json"]
|
||||
debug = ["egui", "smithay-egui", "serde_json"]
|
||||
|
||||
[profile.dev]
|
||||
lto = "thin"
|
||||
|
|
|
|||
32
config.ron
Normal file
32
config.ron
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
(
|
||||
key_bindings: {
|
||||
(modifiers: [Logo, Shift], key: "Escape"): Terminate,
|
||||
(modifiers: [Logo], key: "Escape"): Debug,
|
||||
(modifiers: [Logo, Shift], key: "Q"): Close,
|
||||
(modifiers: [Logo], key: "1"): Workspace(1),
|
||||
(modifiers: [Logo], key: "2"): Workspace(2),
|
||||
(modifiers: [Logo], key: "3"): Workspace(3),
|
||||
(modifiers: [Logo], key: "4"): Workspace(4),
|
||||
(modifiers: [Logo], key: "5"): Workspace(5),
|
||||
(modifiers: [Logo], key: "6"): Workspace(6),
|
||||
(modifiers: [Logo], key: "7"): Workspace(7),
|
||||
(modifiers: [Logo], key: "8"): Workspace(8),
|
||||
(modifiers: [Logo], key: "9"): Workspace(9),
|
||||
(modifiers: [Logo], key: "0"): Workspace(0),
|
||||
(modifiers: [Logo, Shift], key: "1"): MoveToWorkspace(1),
|
||||
(modifiers: [Logo, Shift], key: "2"): MoveToWorkspace(2),
|
||||
(modifiers: [Logo, Shift], key: "3"): MoveToWorkspace(3),
|
||||
(modifiers: [Logo, Shift], key: "4"): MoveToWorkspace(4),
|
||||
(modifiers: [Logo, Shift], key: "5"): MoveToWorkspace(5),
|
||||
(modifiers: [Logo, Shift], key: "6"): MoveToWorkspace(6),
|
||||
(modifiers: [Logo, Shift], key: "7"): MoveToWorkspace(7),
|
||||
(modifiers: [Logo, Shift], key: "8"): MoveToWorkspace(8),
|
||||
(modifiers: [Logo, Shift], key: "9"): MoveToWorkspace(9),
|
||||
(modifiers: [Logo, Shift], key: "0"): MoveToWorkspace(0),
|
||||
(modifiers: [Logo], key: "Left"): Focus(Left),
|
||||
(modifiers: [Logo], key: "Right"): Focus(Right),
|
||||
(modifiers: [Logo], key: "Up"): Focus(Up),
|
||||
(modifiers: [Logo], key: "Down"): Focus(Down),
|
||||
(modifiers: [Ctrl], key: "Return"): Spawn("gnome-terminal"),
|
||||
}
|
||||
)
|
||||
192
src/config.rs
Normal file
192
src/config.rs
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use serde::Deserialize;
|
||||
use smithay::wayland::seat::Keysym;
|
||||
pub use smithay::{
|
||||
backend::input::KeyState,
|
||||
wayland::seat::{keysyms as KeySyms, ModifiersState as KeyModifiers},
|
||||
};
|
||||
use std::{collections::HashMap, fs::OpenOptions, path::PathBuf};
|
||||
use xkbcommon::xkb;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Config {
|
||||
pub key_bindings: HashMap<KeyPattern, Action>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load() -> Config {
|
||||
let mut locations = if let Ok(base) = xdg::BaseDirectories::new() {
|
||||
base.list_config_files_once("cosmic-comp.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 {
|
||||
if path.exists() {
|
||||
return ron::de::from_reader(OpenOptions::new().read(true).open(path).unwrap())
|
||||
.expect("Malformed config file");
|
||||
}
|
||||
}
|
||||
|
||||
Config {
|
||||
key_bindings: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
pub enum KeyModifier {
|
||||
Ctrl,
|
||||
Alt,
|
||||
Shift,
|
||||
Logo,
|
||||
CapsLock,
|
||||
NumLock,
|
||||
}
|
||||
|
||||
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,
|
||||
KeyModifier::Logo => self.logo = true,
|
||||
KeyModifier::CapsLock => self.caps_lock = true,
|
||||
KeyModifier::NumLock => self.num_lock = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
caps_lock: false,
|
||||
logo: false,
|
||||
num_lock: false,
|
||||
};
|
||||
modifiers += self;
|
||||
modifiers
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(transparent)]
|
||||
struct KeyModifiersDef(Vec<KeyModifier>);
|
||||
|
||||
impl From<KeyModifiersDef> for KeyModifiers {
|
||||
fn from(src: KeyModifiersDef) -> Self {
|
||||
src.0.into_iter().fold(
|
||||
KeyModifiers {
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
caps_lock: false,
|
||||
logo: false,
|
||||
num_lock: false,
|
||||
},
|
||||
|mut modis, modi| {
|
||||
modis += modi;
|
||||
modis
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn deserialize_KeyModifiers<'de, D>(deserializer: D) -> Result<KeyModifiers, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
KeyModifiersDef::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn deserialize_Keysym<'de, D>(deserializer: D) -> Result<Keysym, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::{Error, Unexpected};
|
||||
|
||||
let name = String::deserialize(deserializer)?;
|
||||
//let name = format!("KEY_{}", code);
|
||||
match xkb::keysym_from_name(&name, xkb::KEYSYM_NO_FLAGS) {
|
||||
KeySyms::KEY_NoSymbol => match xkb::keysym_from_name(&name, xkb::KEYSYM_CASE_INSENSITIVE) {
|
||||
KeySyms::KEY_NoSymbol => Err(<D::Error as Error>::invalid_value(
|
||||
Unexpected::Str(&name),
|
||||
&"One of the keysym names of xkbcommon.h without the 'KEY_' prefix",
|
||||
)),
|
||||
x => {
|
||||
slog_scope::warn!(
|
||||
"Key-Binding '{}' only matched case insensitive for {:?}",
|
||||
name,
|
||||
xkb::keysym_get_name(x)
|
||||
);
|
||||
Ok(x)
|
||||
}
|
||||
},
|
||||
x => Ok(x),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub enum Action {
|
||||
Terminate,
|
||||
Debug,
|
||||
Close,
|
||||
Workspace(u8),
|
||||
MoveToWorkspace(u8),
|
||||
Focus(FocusAction),
|
||||
Spawn(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FocusAction {
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
}
|
||||
130
src/input/mod.rs
130
src/input/mod.rs
|
|
@ -1,9 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::state::Common;
|
||||
use smithay::{backend::input::KeyState, wayland::seat::keysyms};
|
||||
use crate::{config::Action, state::Common};
|
||||
use smithay::{
|
||||
backend::input::{Device, DeviceCapability, InputBackend, InputEvent},
|
||||
backend::input::{Device, DeviceCapability, InputBackend, InputEvent, KeyState},
|
||||
desktop::{layer_map_for_output, Space, WindowSurfaceType},
|
||||
reexports::wayland_server::{protocol::wl_surface::WlSurface, Display},
|
||||
utils::{Logical, Point},
|
||||
|
|
@ -213,66 +212,8 @@ impl Common {
|
|||
return FilterResult::Intercept(());
|
||||
}
|
||||
|
||||
// here we can handle global shortcuts and the like
|
||||
if modifiers.logo
|
||||
&& handle.raw_syms().contains(&keysyms::KEY_Return)
|
||||
&& state == KeyState::Pressed
|
||||
{
|
||||
if let Err(err) = std::process::Command::new("gnome-terminal")
|
||||
.env("WAYLAND_DISPLAY", &self.socket)
|
||||
.spawn()
|
||||
{
|
||||
slog_scope::warn!("Failed to spawn terminal: {}", err);
|
||||
}
|
||||
userdata.get::<SupressedKeys>().unwrap().add(&handle);
|
||||
return FilterResult::Intercept(());
|
||||
}
|
||||
|
||||
if modifiers.logo
|
||||
&& handle
|
||||
.raw_syms()
|
||||
.iter()
|
||||
.any(|sym| *sym >= keysyms::KEY_0 && *sym <= keysyms::KEY_9)
|
||||
&& state == KeyState::Pressed
|
||||
{
|
||||
let current_output = active_output(seat, &self);
|
||||
let key_num = handle
|
||||
.raw_syms()
|
||||
.iter()
|
||||
.find(|sym| {
|
||||
**sym >= keysyms::KEY_0 && **sym <= keysyms::KEY_9
|
||||
})
|
||||
.unwrap()
|
||||
- keysyms::KEY_0;
|
||||
let workspace = match key_num {
|
||||
0 => 9,
|
||||
x => x - 1,
|
||||
};
|
||||
self.shell.activate(¤t_output, workspace as usize);
|
||||
userdata.get::<SupressedKeys>().unwrap().add(&handle);
|
||||
return FilterResult::Intercept(());
|
||||
}
|
||||
|
||||
if modifiers.logo
|
||||
&& modifiers.shift
|
||||
&& handle.raw_syms().contains(&keysyms::KEY_Escape)
|
||||
&& state == KeyState::Pressed
|
||||
{
|
||||
self.should_stop = true;
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
self.egui.modifiers = modifiers.clone();
|
||||
if self.seats.iter().position(|x| x == seat).unwrap() == 0
|
||||
&& modifiers.logo
|
||||
&& handle.raw_syms().contains(&keysyms::KEY_Escape)
|
||||
&& state == KeyState::Pressed
|
||||
{
|
||||
self.egui.active = !self.egui.active;
|
||||
userdata.get::<SupressedKeys>().unwrap().add(&handle);
|
||||
return FilterResult::Intercept(());
|
||||
}
|
||||
if self.seats.iter().position(|x| x == seat).unwrap() == 0
|
||||
&& self.egui.active
|
||||
{
|
||||
|
|
@ -295,6 +236,73 @@ impl Common {
|
|||
}
|
||||
}
|
||||
|
||||
// here we can handle global shortcuts and the like
|
||||
for (binding, action) in self.config.key_bindings.iter() {
|
||||
if state == KeyState::Pressed
|
||||
&& binding.modifiers == *modifiers
|
||||
&& handle.raw_syms().contains(&binding.key)
|
||||
{
|
||||
match action {
|
||||
Action::Terminate => {
|
||||
userdata
|
||||
.get::<SupressedKeys>()
|
||||
.unwrap()
|
||||
.add(&handle);
|
||||
self.should_stop = true;
|
||||
return FilterResult::Intercept(());
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
Action::Debug => {
|
||||
self.egui.active = !self.egui.active;
|
||||
userdata
|
||||
.get::<SupressedKeys>()
|
||||
.unwrap()
|
||||
.add(&handle);
|
||||
return FilterResult::Intercept(());
|
||||
}
|
||||
#[cfg(not(feature = "debug"))]
|
||||
Action::Debug => slog_scope::info!(
|
||||
"Debug overlay not included in this version"
|
||||
),
|
||||
Action::Close => { /* TODO */ }
|
||||
Action::Workspace(key_num) => {
|
||||
let current_output = active_output(seat, &self);
|
||||
let workspace = match key_num {
|
||||
0 => 9,
|
||||
x => x - 1,
|
||||
};
|
||||
self.shell
|
||||
.activate(¤t_output, workspace as usize);
|
||||
userdata
|
||||
.get::<SupressedKeys>()
|
||||
.unwrap()
|
||||
.add(&handle);
|
||||
return FilterResult::Intercept(());
|
||||
}
|
||||
Action::MoveToWorkspace(num) => { /* TODO */ }
|
||||
Action::Focus(focus) => match focus {
|
||||
_ => { /* TODO */ }
|
||||
},
|
||||
Action::Spawn(command) => {
|
||||
if let Err(err) =
|
||||
std::process::Command::new("/bin/sh")
|
||||
.arg("-c")
|
||||
.arg(command)
|
||||
.env("WAYLAND_DISPLAY", &self.socket)
|
||||
.spawn()
|
||||
{
|
||||
slog_scope::warn!("Failed to spawn: {}", err);
|
||||
}
|
||||
userdata
|
||||
.get::<SupressedKeys>()
|
||||
.unwrap()
|
||||
.add(&handle);
|
||||
return FilterResult::Intercept(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FilterResult::Forward
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use anyhow::{Context, Result};
|
|||
use std::{ffi::OsString, sync::atomic::Ordering};
|
||||
|
||||
pub mod backend;
|
||||
pub mod config;
|
||||
pub mod input;
|
||||
mod logger;
|
||||
pub mod shell;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use crate::{
|
||||
backend::{kms::KmsState, winit::WinitState, x11::X11State},
|
||||
config::Config,
|
||||
logger::LogState,
|
||||
shell::{init_shell, Shell},
|
||||
};
|
||||
|
|
@ -35,6 +36,8 @@ pub struct State {
|
|||
}
|
||||
|
||||
pub struct Common {
|
||||
pub config: Config,
|
||||
|
||||
pub display: Rc<RefCell<Display>>,
|
||||
pub socket: OsString,
|
||||
pub event_loop_handle: LoopHandle<'static, State>,
|
||||
|
|
@ -167,6 +170,8 @@ impl State {
|
|||
|
||||
State {
|
||||
common: Common {
|
||||
config: Config::load(),
|
||||
|
||||
display: Rc::new(RefCell::new(display)),
|
||||
socket,
|
||||
event_loop_handle: handle,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue