Rename wm_class to application

This commit is contained in:
Takashi Kokubun 2021-12-21 20:01:02 -08:00
parent 4b251b1fce
commit 6547b4883d
No known key found for this signature in database
GPG Key ID: 6FFC433B12EE23DD
11 changed files with 92 additions and 91 deletions

View File

@ -11,14 +11,14 @@ modmap:
Henkan: Shift_L
- name: Kana -> Windows
wm_class:
application:
not: jetbrains-idea
remap:
# Use Windows since Alt is annoying in Electron apps (Slack, Nocturn)
KatakanaHiragana: Win_R
- name: Kana -> Alt
wm_class:
application:
only: jetbrains-idea
remap:
# Use Alt since Windows is annoying in IDEA
@ -34,7 +34,7 @@ keymap:
C-j: C-m
- name: Default (Nocturn, etc.)
wm_class:
application:
not: [Google-chrome, Slack, Gnome-terminal, jetbrains-idea]
remap:
# Emacs basic
@ -69,7 +69,7 @@ keymap:
Super-d: C-delete
- name: IDEA (modified from Default)
wm_class:
application:
only: jetbrains-idea
remap:
# Emacs basic
@ -113,7 +113,7 @@ keymap:
C-c: Esc
- name: Chrome, Slack (modified from Default)
wm_class:
application:
only: [Google-chrome, Slack]
remap:
# Emacs basic
@ -152,7 +152,7 @@ keymap:
Super-d: C-delete
- name: Tab changes + Alt-Enter simulation
wm_class:
application:
not: [Gnome-terminal, Nocturn]
remap:
Super-o: C-Shift-tab
@ -160,13 +160,13 @@ keymap:
Super-Enter: M-Enter
- name: Google Search
wm_class:
application:
only: Google-chrome
remap:
Super-s: C-f
- name: Terminal
wm_class:
application:
only: Gnome-terminal
remap:
Super-o: M-o
@ -185,7 +185,7 @@ keymap:
Super-v: M-v
- name: Nocturn
wm_class:
application:
only: Nocturn
remap:
Super-j: M-j
@ -195,7 +195,7 @@ keymap:
Super-Enter: Shift-Enter
- name: Slack
wm_class:
application:
only: Slack
remap:
Super-n: C-k

View File

@ -4,7 +4,7 @@
# a: b
keymap:
- name: Global
wm_class:
application:
only: Slack
remap:
C-i: C-u

View File

@ -8,36 +8,54 @@ mod x11_client;
mod null_client;
trait Client {
fn current_wm_class(&mut self) -> Option<String>;
fn supported(&mut self) -> bool;
fn current_application(&mut self) -> Option<String>;
}
pub struct WMClient {
name: String,
client: Box<dyn Client>,
called: bool,
last_application: String,
}
impl WMClient {
pub fn current_wm_class(&mut self) -> Option<String> {
self.client.current_wm_class()
fn new(name: &str, client: Box<dyn Client>) -> WMClient {
WMClient {
name: name.to_string(),
client,
called: false,
last_application: String::new(),
}
}
pub fn current_application(&mut self) -> Option<String> {
if !self.called {
self.called = true;
println!("application-client: {} (supported: {})", self.name, self.client.supported());
}
let result = self.client.current_application();
if let Some(application) = &result {
if &self.last_application != application {
self.last_application = application.clone();
println!("application: {}", application);
}
}
result
}
}
#[cfg(feature = "sway")]
pub fn build_client() -> WMClient {
WMClient {
client: Box::new(sway_client::SwayClient::new()),
}
WMClient::new("Sway", Box::new(sway_client::SwayClient::new()))
}
#[cfg(feature = "x11")]
pub fn build_client() -> WMClient {
WMClient {
client: Box::new(x11_client::X11Client::new()),
}
WMClient::new("X11", Box::new(x11_client::X11Client::new()))
}
#[cfg(not(any(feature = "sway", feature = "x11")))]
pub fn build_client() -> WMClient {
WMClient {
client: Box::new(null_client::NullClient::new()),
}
WMClient::new("none", Box::new(null_client::NullClient))
}

View File

@ -1,21 +1,13 @@
use crate::client::Client;
pub struct NullClient {
called: bool,
}
impl NullClient {
pub fn new() -> NullClient {
NullClient { called: false }
}
}
pub struct NullClient;
impl Client for NullClient {
fn current_wm_class(&mut self) -> Option<String> {
if !self.called {
self.called = true;
println!("NullClient.supported = false");
}
fn supported(&mut self) -> bool {
false
}
fn current_application(&mut self) -> Option<String> {
None
}
}

View File

@ -16,7 +16,9 @@ impl SwayClient {
supported: None,
}
}
}
impl Client for SwayClient {
fn supported(&mut self) -> bool {
match self.supported {
Some(supported) => supported,
@ -28,16 +30,13 @@ impl SwayClient {
supported = true;
}
}
println!("SwayClient.supported = {}", supported);
self.supported = Some(supported);
supported
}
}
}
}
impl Client for SwayClient {
fn current_wm_class(&mut self) -> Option<String> {
fn current_application(&mut self) -> Option<String> {
if !self.supported() {
return None;
}

View File

@ -5,7 +5,6 @@ pub struct X11Client {
// Both of them are lazily initialized
display: Option<*mut xlib::Display>,
supported: Option<bool>,
last_wm_class: String,
}
impl X11Client {
@ -13,27 +12,6 @@ impl X11Client {
X11Client {
display: None,
supported: None,
last_wm_class: String::new(),
}
}
fn supported(&mut self) -> bool {
match self.supported {
Some(supported) => supported,
None => {
let display = self.display();
let supported = if display.is_null() {
false
} else {
let mut focused_window = 0;
let mut focus_state = 0;
unsafe { xlib::XGetInputFocus(display, &mut focused_window, &mut focus_state) };
focused_window > 0
};
println!("X11Client.supported = {}", supported);
self.supported = Some(supported);
supported
}
}
}
@ -50,7 +28,26 @@ impl X11Client {
}
impl Client for X11Client {
fn current_wm_class(&mut self) -> Option<String> {
fn supported(&mut self) -> bool {
match self.supported {
Some(supported) => supported,
None => {
let display = self.display();
let supported = if display.is_null() {
false
} else {
let mut focused_window = 0;
let mut focus_state = 0;
unsafe { xlib::XGetInputFocus(display, &mut focused_window, &mut focus_state) };
focused_window > 0
};
self.supported = Some(supported);
supported
}
}
}
fn current_application(&mut self) -> Option<String> {
if !self.supported() {
return None;
}
@ -114,11 +111,6 @@ impl Client for X11Client {
}
focused_window = parent;
}
if &self.last_wm_class != &wm_class {
self.last_wm_class = wm_class.clone();
println!("wm_class: {}", &wm_class);
}
Some(wm_class)
}
}

View File

@ -5,7 +5,7 @@ use std::fmt;
// TODO: Use trait to allow only either `only` or `not`
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WMClass {
pub struct Application {
#[serde(default, deserialize_with = "string_or_vec")]
pub only: Option<Vec<String>>,
#[serde(default, deserialize_with = "string_or_vec")]

View File

@ -1,7 +1,7 @@
use crate::config::action::Action;
use crate::config::actions::Actions;
use crate::config::key_press::KeyPress;
use crate::config::wm_class::WMClass;
use crate::config::application::Application;
use serde::de::{MapAccess, Visitor};
use serde::{Deserialize, Deserializer};
use std::collections::HashMap;
@ -14,7 +14,7 @@ pub struct Keymap {
pub name: String,
#[serde(deserialize_with = "keymap_remap")]
pub remap: HashMap<KeyPress, Vec<Action>>,
pub wm_class: Option<WMClass>,
pub application: Option<Application>,
}
fn keymap_remap<'de, D>(deserializer: D) -> Result<HashMap<KeyPress, Vec<Action>>, D::Error>

View File

@ -4,7 +4,7 @@ mod key;
pub mod key_press;
mod keymap;
mod modmap;
pub mod wm_class;
pub mod application;
extern crate serde_yaml;

View File

@ -1,5 +1,5 @@
use crate::config::key::parse_key;
use crate::config::wm_class::WMClass;
use crate::config::application::Application;
use evdev::Key;
use serde::de::{value, Error, MapAccess, Visitor};
use serde::{Deserialize, Deserializer};
@ -12,7 +12,7 @@ pub struct Modmap {
pub name: String,
#[serde(deserialize_with = "modmap_remap")]
pub remap: HashMap<Key, Key>,
pub wm_class: Option<WMClass>,
pub application: Option<Application>,
}
fn modmap_remap<'de, D>(deserializer: D) -> Result<HashMap<Key, Key>, D::Error>

View File

@ -1,7 +1,7 @@
use crate::client::{build_client, WMClient};
use crate::config::action::Action;
use crate::config::key_press::{KeyPress, Modifier};
use crate::config::wm_class::WMClass;
use crate::config::application::Application;
use crate::Config;
use evdev::uinput::VirtualDevice;
use evdev::{EventType, InputEvent, Key};
@ -13,7 +13,7 @@ pub struct EventHandler {
device: VirtualDevice,
wm_client: WMClient,
override_remap: Option<HashMap<KeyPress, Vec<Action>>>,
wm_class_cache: Option<String>,
application_cache: Option<String>,
shift: PressState,
control: PressState,
alt: PressState,
@ -26,7 +26,7 @@ impl EventHandler {
device,
wm_client: build_client(),
override_remap: None,
wm_class_cache: None,
application_cache: None,
shift: PressState::new(false),
control: PressState::new(false),
alt: PressState::new(false),
@ -36,14 +36,14 @@ impl EventHandler {
// Handle EventType::KEY
pub fn on_event(&mut self, event: InputEvent, config: &Config) -> Result<(), Box<dyn Error>> {
self.wm_class_cache = None; // expire cache
self.application_cache = None; // expire cache
let mut key = Key::new(event.code());
// println!("=> {}: {:?}", event.value(), &key);
// Apply modmap
for modmap in &config.modmap {
if let Some(modmap_key) = modmap.remap.get(&key) {
if let Some(wm_class_matcher) = &modmap.wm_class {
if let Some(wm_class_matcher) = &modmap.application {
if !self.match_wm_class(wm_class_matcher) {
continue;
}
@ -98,7 +98,7 @@ impl EventHandler {
}
for keymap in &config.keymap {
if let Some(actions) = keymap.remap.get(&key_press) {
if let Some(wm_class_matcher) = &keymap.wm_class {
if let Some(wm_class_matcher) = &keymap.application {
if !self.match_wm_class(wm_class_matcher) {
continue;
}
@ -210,21 +210,21 @@ impl EventHandler {
}
}
fn match_wm_class(&mut self, wm_class_matcher: &WMClass) -> bool {
fn match_wm_class(&mut self, application_matcher: &Application) -> bool {
// Lazily fill the wm_class cache
if let None = self.wm_class_cache {
match self.wm_client.current_wm_class() {
Some(wm_class) => self.wm_class_cache = Some(wm_class),
None => self.wm_class_cache = Some(String::new()),
if let None = self.application_cache {
match self.wm_client.current_application() {
Some(application) => self.application_cache = Some(application),
None => self.application_cache = Some(String::new()),
}
}
if let Some(wm_class) = &self.wm_class_cache {
if let Some(wm_class_only) = &wm_class_matcher.only {
return wm_class_only.contains(wm_class);
if let Some(application) = &self.application_cache {
if let Some(application_only) = &application_matcher.only {
return application_only.contains(application);
}
if let Some(wm_class_not) = &wm_class_matcher.not {
return !wm_class_not.contains(wm_class);
if let Some(application_not) = &application_matcher.not {
return !application_not.contains(application);
}
}
false