Make it possible to match a full WM_CLASS in X11

Close #107
pull/145/head
Takashi Kokubun 2 years ago
parent 98d5589ce7
commit a448a24ec8
No known key found for this signature in database
GPG Key ID: 6FFC433B12EE23DD

@ -241,17 +241,21 @@ application:
only: [Application, ...]
```
The application name can be specified as a normal string to exactly match the name,
or a regex surrounded by `/`s like `/application/`.
To check the application names, you can use the following commands:
#### X11
```
$ wmctrl -x -l
0x0280000a 0 gnome-terminal-server.Gnome-terminal ubuntu-focal Terminal
0x02600001 0 nocturn.Nocturn ubuntu-focal Nocturn
0x02800003 0 slack.Slack ubuntu-jammy Slack | general | ruby-jp
0x05400003 0 code.Code ubuntu-jammy application.rs - xremap - Visual Studio Code
```
Use the name after `.` in the third column (`WM_CLASS`), i.e. `Gnome-terminal` or `Nocturn` in the above output.
You may use the entire string of the third column (`slack.Slack`, `code.Code`),
or just the last segment after `.` (`Slack`, `Code`).
#### GNOME Wayland

@ -86,11 +86,13 @@ fn get_wm_class(conn: &RustConnection, window: Window) -> Option<String> {
}
if let Some(delimiter) = reply.value.iter().position(|byte| *byte == '\0' as u8) {
let value = reply.value[(delimiter + 1)..].to_vec();
if let Some(end) = value.iter().position(|byte| *byte == '\0' as u8) {
if end == value.len() - 1 {
if let Ok(string) = String::from_utf8(value[..end].to_vec()) {
return Some(string);
if let Ok(prefix) = String::from_utf8(reply.value[..delimiter].to_vec()) {
let name = reply.value[(delimiter + 1)..].to_vec();
if let Some(end) = name.iter().position(|byte| *byte == '\0' as u8) {
if end == name.len() - 1 {
if let Ok(name) = String::from_utf8(name[..end].to_vec()) {
return Some(format!("{prefix}.{name}"));
}
}
}
}

@ -16,15 +16,26 @@ pub struct Application {
#[derive(Debug)]
pub enum ApplicationMatcher {
// class.name
Literal(String),
// name
Name(String),
// /regex/
Regex(Regex),
}
impl ApplicationMatcher {
pub fn matches(&self, name: &str) -> bool {
pub fn matches(&self, app: &str) -> bool {
match &self {
ApplicationMatcher::Literal(s) => s == name,
ApplicationMatcher::Regex(r) => r.is_match(name),
ApplicationMatcher::Literal(s) => s == app,
ApplicationMatcher::Name(s) => {
if let Some(pos) = app.rfind('.') {
s == &app[(pos + 1)..]
} else {
s == app
}
}
ApplicationMatcher::Regex(r) => r.is_match(app),
}
}
}
@ -33,12 +44,15 @@ impl FromStr for ApplicationMatcher {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let first_char = s.chars().next();
match first_char {
None => Err(anyhow!("Empty application name")),
Some('/') if s.len() < 3 => Err(anyhow!("Application name regex format must be /<regex>/")),
Some('/') => Ok(ApplicationMatcher::Regex(Regex::new(&slash_unescape(s)?)?)),
Some(_) => Ok(ApplicationMatcher::Literal(s.to_owned())),
match s.as_bytes() {
[b'/', ..] => Ok(ApplicationMatcher::Regex(Regex::new(&slash_unescape(s)?)?)),
_ => {
if s.find('.').is_some() {
Ok(ApplicationMatcher::Literal(s.to_owned()))
} else {
Ok(ApplicationMatcher::Name(s.to_owned()))
}
}
}
}
}

Loading…
Cancel
Save