Skip to content

Commit 91a183c

Browse files
authored
feat: get title by 'xprop' directly (#42)
1 parent baee9d7 commit 91a183c

2 files changed

Lines changed: 52 additions & 66 deletions

File tree

src/get_active_window.rs

Lines changed: 49 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,37 @@
55
* @LastEditTime: 2023-10-30 22:22:22
66
* @FilePath: /processforlinux/src/get_active_window.rs
77
*/
8+
9+
/*
10+
* It seems that 'xprop' can get the title directly.
11+
*/
812
use std::error::Error;
913
use std::io::{BufRead, BufReader};
1014
use std::process::{Command, Stdio};
1115

1216
enum WindowTitle {
1317
Code,
14-
_WebStorm, //TODO: how to get it?
15-
_Telgram, //TODO: how to get it?
16-
_WeChat, // Linux not have it
18+
WebStorm,
19+
Telegram,
20+
WeChat,
1721
Discord,
1822
Mail,
1923
QQ,
2024
Chrome,
2125
QQ音乐,
2226
NetEaseMusic,
23-
iTerm2, //TODO: why it not CamalCase?
27+
iTerm2,
2428
Typora,
2529
None,
2630
}
31+
2732
impl std::fmt::Display for WindowTitle {
2833
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2934
match self {
3035
WindowTitle::Code => write!(f, "Code"),
31-
//WindowTitle::WebStorm => write!(f, "WebStorm"), //TODO: how to get it?
32-
//WindowTitle::Telgram => write!(f, "Telgram"),
33-
//WindowTitle::WeChat => write!(f, "WeChat"),
36+
WindowTitle::WebStorm => write!(f, "WebStorm"),
37+
WindowTitle::Telegram => write!(f, "Telegram"),
38+
WindowTitle::WeChat => write!(f, "WeChat"),
3439
WindowTitle::Discord => write!(f, "Discord"),
3540
WindowTitle::Mail => write!(f, "Mail"),
3641
WindowTitle::QQ => write!(f, "QQ"),
@@ -40,96 +45,77 @@ impl std::fmt::Display for WindowTitle {
4045
WindowTitle::iTerm2 => write!(f, "iTerm2"),
4146
WindowTitle::Typora => write!(f, "Typora"),
4247
WindowTitle::None => write!(f, "None"),
43-
_ => write!(f, "None"),
4448
}
4549
}
4650
}
4751

4852
impl WindowTitle {
4953
fn from_string(s: &str) -> WindowTitle {
5054
match s {
51-
"Code" => WindowTitle::Code,
52-
"Telgram" => WindowTitle::None, //TODO: can't get it
53-
"WebStorm" => WindowTitle::None, //TODO: can't get it
54-
"WeChat" => WindowTitle::None, //TODO: can't get it
55-
"Discord" => WindowTitle::Discord, //TODO: not test
56-
"Thunderbird" => WindowTitle::Mail,
57-
"Kmail" => WindowTitle::Mail,
58-
"QQ" => WindowTitle::QQ,
59-
"Chrome" => WindowTitle::Chrome,
60-
"Chromium" => WindowTitle::Chrome,
61-
"Thorium" => WindowTitle::Chrome,
55+
"code" => WindowTitle::Code,
56+
"jetbrains-webstorm" => WindowTitle::WebStorm,
57+
"telegram" => WindowTitle::Telegram, // TODO: Can't get the title of Telegram
58+
"wechat" => WindowTitle::WeChat,
59+
"discord" => WindowTitle::Discord, // TODO: Can't test
60+
"thunderbird" => WindowTitle::Mail,
61+
"kmail" => WindowTitle::Mail, // TODO: Can't get the title of KMail
62+
"qq" => WindowTitle::QQ,
63+
"google-chrome" => WindowTitle::Chrome,
64+
"chromium" => WindowTitle::Chrome,
65+
"thorium" => WindowTitle::Chrome,
6266
"qqmusic" => WindowTitle::QQ音乐,
63-
"Music" => WindowTitle::NetEaseMusic,
64-
"YesPlayMusic" => WindowTitle::NetEaseMusic,
65-
"Yakuake" => WindowTitle::iTerm2,
66-
"Konsole" => WindowTitle::iTerm2,
67-
"Typora" => WindowTitle::Typora,
68-
_ => WindowTitle::None, // Default
67+
"music" => WindowTitle::NetEaseMusic,
68+
"yesplaymusic" => WindowTitle::NetEaseMusic,
69+
"yakuake" => WindowTitle::iTerm2, // TODO: Can't get the title of Yakuake
70+
"konsole" => WindowTitle::iTerm2, // TODO: Can't get the title of Konsole
71+
"typora" => WindowTitle::Typora,
72+
_ => WindowTitle::None,
6973
}
7074
}
7175
}
7276

7377
pub fn get_active_window_process_and_title() -> Result<String, Box<dyn Error>> {
74-
let active_window_id = get_active_window_id()?;
75-
let window_title = get_window_title_by_id(&active_window_id)?;
76-
let process_name = get_last_part(&window_title).unwrap_or("None".to_string());
77-
let window_title_enum = WindowTitle::from_string(&process_name);
78-
79-
Ok(window_title_enum.to_string())
80-
}
81-
82-
fn get_active_window_id() -> Result<String, Box<dyn Error>> {
8378
let xprop_output = Command::new("xprop")
8479
.arg("-root")
8580
.arg("_NET_ACTIVE_WINDOW")
8681
.stdout(Stdio::piped())
87-
.spawn()?;
88-
89-
let xprop_stdout = xprop_output
82+
.spawn()?
9083
.stdout
9184
.ok_or("Failed to capture xprop stdout")?;
9285

93-
let xprop_reader = BufReader::new(xprop_stdout);
86+
let xprop_reader = BufReader::new(xprop_output);
87+
let mut window_id = String::new();
9488
for line in xprop_reader.lines() {
9589
let line = line?;
9690
if line.contains("_NET_ACTIVE_WINDOW(WINDOW)") {
97-
return Ok(line.split_whitespace().nth(4).unwrap_or("").to_string());
91+
window_id = line.split_whitespace().nth(4).unwrap_or("").to_string();
92+
break;
9893
}
9994
}
10095

101-
Err("Failed to get active window ID".into())
102-
}
96+
if window_id.is_empty() {
97+
return Err("Failed to get active window ID".into());
98+
}
10399

104-
fn get_window_title_by_id(window_id: &str) -> Result<String, Box<dyn Error>> {
105-
let xwininfo_output = Command::new("xwininfo")
100+
let xprop_output = Command::new("xprop")
106101
.arg("-id")
107-
.arg(window_id)
102+
.arg(&window_id)
103+
.arg("WM_CLASS")
108104
.stdout(Stdio::piped())
109105
.spawn()?
110106
.stdout
111-
.ok_or("Failed to capture xwininfo stdout")?;
107+
.ok_or("Failed to capture xprop stdout")?;
112108

113-
let xwininfo_reader = BufReader::new(xwininfo_output);
114-
for line in xwininfo_reader.lines() {
109+
let xprop_reader = BufReader::new(xprop_output);
110+
for line in xprop_reader.lines() {
115111
let line = line?;
116-
if line.contains("xwininfo: Window id:") {
117-
let window_name_parts: Vec<&str> = line.split('"').collect();
118-
return Ok(window_name_parts[1].to_string());
112+
if line.contains("WM_CLASS(STRING)") {
113+
let class_name = line.split('"').nth(1).unwrap_or("");
114+
println!("class_name: {}", class_name);
115+
let window_title_enum = WindowTitle::from_string(class_name);
116+
return Ok(window_title_enum.to_string());
119117
}
120118
}
121119

122-
Err("Failed to get window title".into())
123-
}
124-
125-
fn get_last_part(original_string: &str) -> Option<String> {
126-
let last_space_index = match original_string.rfind(' ') {
127-
Some(index) => index,
128-
None => {
129-
return Some(original_string.to_string());
130-
}
131-
};
132-
133-
let result_string = &original_string[(last_space_index + 1)..];
134-
Some(result_string.to_string())
120+
Err("Failed to get window class".into())
135121
}

src/get_media.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@ use dbus::blocking::stdintf::org_freedesktop_dbus::Properties;
1010
use dbus::blocking::Connection;
1111
use dbus::blocking::Proxy;
1212

13-
#[derive(Clone, PartialEq)]
14-
#[derive(Default)]
13+
#[derive(Clone, PartialEq, Default, Debug)]
1514
pub struct MediaMetadata {
1615
pub title: Option<String>,
1716
pub artist: Option<String>,
1817
}
1918

2019

2120
mod constants {
22-
pub const MEDIA_PLAYER_IDENTIFIERS: [&str; 2] = [
21+
pub const MEDIA_PLAYER_IDENTIFIERS: [&str; 3] = [
2322
"org.mpris.MediaPlayer2.yesplaymusic",
2423
"org.mpris.MediaPlayer2.netease-cloud-music",
24+
"org.mpris.MediaPlayer2.spotify"
2525
];
2626
pub const MPRIS_PLAYER_INTERFACE: &str = "org.mpris.MediaPlayer2.Player";
2727
pub const METADATA_PROPERTY: &str = "Metadata";

0 commit comments

Comments
 (0)