mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-06 11:35:46 +00:00
Merge pull request #28 from powellnorma/aw-instance
x11: include wm_instance
This commit is contained in:
commit
8cfc14c501
@ -4,6 +4,7 @@ use anyhow::Context;
|
|||||||
use aw_client_rust::{AwClient, Event as AwEvent};
|
use aw_client_rust::{AwClient, Event as AwEvent};
|
||||||
use chrono::{DateTime, TimeDelta, Utc};
|
use chrono::{DateTime, TimeDelta, Utc};
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
||||||
@ -93,6 +94,16 @@ impl ReportClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_active_window(&self, app_id: &str, title: &str) -> anyhow::Result<()> {
|
pub async fn send_active_window(&self, app_id: &str, title: &str) -> anyhow::Result<()> {
|
||||||
|
self.send_active_window_with_extra(app_id, title, None)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_active_window_with_extra(
|
||||||
|
&self,
|
||||||
|
app_id: &str,
|
||||||
|
title: &str,
|
||||||
|
extra_data: Option<HashMap<String, String>>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
let mut data = Map::new();
|
let mut data = Map::new();
|
||||||
|
|
||||||
if let Some((inserted_app_id, inserted_title)) = self.get_filtered_data(app_id, title) {
|
if let Some((inserted_app_id, inserted_title)) = self.get_filtered_data(app_id, title) {
|
||||||
@ -104,6 +115,12 @@ impl ReportClient {
|
|||||||
|
|
||||||
data.insert("app".to_string(), Value::String(inserted_app_id));
|
data.insert("app".to_string(), Value::String(inserted_app_id));
|
||||||
data.insert("title".to_string(), Value::String(inserted_title));
|
data.insert("title".to_string(), Value::String(inserted_title));
|
||||||
|
|
||||||
|
if let Some(extra) = extra_data {
|
||||||
|
for (key, value) in extra {
|
||||||
|
data.insert(key, Value::String(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ use x11rb::rust_connection::RustConnection;
|
|||||||
pub struct WindowData {
|
pub struct WindowData {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub app_id: String,
|
pub app_id: String,
|
||||||
|
pub wm_instance: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct X11Client {
|
pub struct X11Client {
|
||||||
@ -86,10 +87,12 @@ impl X11Client {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let title = str::from_utf8(&name.value).with_context(|| "Invalid title UTF")?;
|
let title = str::from_utf8(&name.value).with_context(|| "Invalid title UTF")?;
|
||||||
|
let (instance, class) = parse_wm_class(&class)?;
|
||||||
|
|
||||||
Ok(WindowData {
|
Ok(WindowData {
|
||||||
title: title.to_string(),
|
title: title.to_string(),
|
||||||
app_id: parse_wm_class(&class)?.to_string(),
|
app_id: class,
|
||||||
|
wm_instance: instance,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -149,21 +152,26 @@ impl X11Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_wm_class(property: &GetPropertyReply) -> anyhow::Result<&str> {
|
fn parse_wm_class(property: &GetPropertyReply) -> anyhow::Result<(String, String)> {
|
||||||
if property.format != 8 {
|
if property.format != 8 {
|
||||||
bail!("Malformed property: wrong format");
|
bail!("Malformed property: wrong format");
|
||||||
}
|
}
|
||||||
let value = &property.value;
|
let value = &property.value;
|
||||||
// The property should contain two null-terminated strings. Find them.
|
// The property should contain two null-terminated strings. Find them.
|
||||||
if let Some(middle) = value.iter().position(|&b| b == 0) {
|
if let Some(middle) = value.iter().position(|&b| b == 0) {
|
||||||
let (_, class) = value.split_at(middle);
|
let (instance, class) = value.split_at(middle);
|
||||||
// Skip the null byte at the beginning
|
// Remove the null byte at the end of the instance
|
||||||
|
let instance = &instance[..instance.len()];
|
||||||
|
// Skip the null byte at the beginning of the class
|
||||||
let mut class = &class[1..];
|
let mut class = &class[1..];
|
||||||
// Remove the last null byte from the class, if it is there.
|
// Remove the last null byte from the class, if it is there.
|
||||||
if class.last() == Some(&0) {
|
if class.last() == Some(&0) {
|
||||||
class = &class[..class.len() - 1];
|
class = &class[..class.len() - 1];
|
||||||
}
|
}
|
||||||
Ok(std::str::from_utf8(class)?)
|
Ok((
|
||||||
|
std::str::from_utf8(instance)?.to_string(),
|
||||||
|
std::str::from_utf8(class)?.to_string(),
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
bail!("Missing null byte")
|
bail!("Missing null byte")
|
||||||
}
|
}
|
||||||
|
@ -2,29 +2,53 @@ use super::{x11_connection::X11Client, Watcher};
|
|||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct WindowWatcher {
|
pub struct WindowWatcher {
|
||||||
client: X11Client,
|
client: X11Client,
|
||||||
last_title: String,
|
|
||||||
last_app_id: String,
|
last_app_id: String,
|
||||||
|
last_title: String,
|
||||||
|
last_wm_instance: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowWatcher {
|
impl WindowWatcher {
|
||||||
|
pub async fn send_active_window_with_instance(
|
||||||
|
&self,
|
||||||
|
client: &ReportClient,
|
||||||
|
app_id: &str,
|
||||||
|
title: &str,
|
||||||
|
wm_instance: &str,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let mut extra_data = HashMap::new();
|
||||||
|
extra_data.insert("wm_instance".to_string(), wm_instance.to_string());
|
||||||
|
client
|
||||||
|
.send_active_window_with_extra(app_id, title, Some(extra_data))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn send_active_window(&mut self, client: &ReportClient) -> anyhow::Result<()> {
|
async fn send_active_window(&mut self, client: &ReportClient) -> anyhow::Result<()> {
|
||||||
let data = self.client.active_window_data()?;
|
let data = self.client.active_window_data()?;
|
||||||
|
|
||||||
if data.app_id != self.last_app_id || data.title != self.last_title {
|
if data.app_id != self.last_app_id
|
||||||
|
|| data.title != self.last_title
|
||||||
|
|| data.wm_instance != self.last_wm_instance
|
||||||
|
{
|
||||||
debug!(
|
debug!(
|
||||||
r#"Changed window app_id="{}", title="{}""#,
|
r#"Changed window app_id="{}", title="{}", wm_instance="{}""#,
|
||||||
data.app_id, data.title
|
data.app_id, data.title, data.wm_instance
|
||||||
);
|
);
|
||||||
self.last_app_id = data.app_id;
|
self.last_app_id = data.app_id.clone();
|
||||||
self.last_title = data.title;
|
self.last_title = data.title.clone();
|
||||||
|
self.last_wm_instance = data.wm_instance.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
client
|
self.send_active_window_with_instance(
|
||||||
.send_active_window(&self.last_app_id, &self.last_title)
|
client,
|
||||||
|
&self.last_app_id,
|
||||||
|
&self.last_title,
|
||||||
|
&self.last_wm_instance,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.with_context(|| "Failed to send heartbeat for active window")
|
.with_context(|| "Failed to send heartbeat for active window")
|
||||||
}
|
}
|
||||||
@ -40,6 +64,7 @@ impl Watcher for WindowWatcher {
|
|||||||
client,
|
client,
|
||||||
last_title: String::new(),
|
last_title: String::new(),
|
||||||
last_app_id: String::new(),
|
last_app_id: String::new(),
|
||||||
|
last_wm_instance: String::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user