use crate::client::kde::kwin_scripts::KwinScripts;
use crate::client::kde::plugin_script_handler::ensure_script_loaded;
use crate::client::{Client, WindowInfo};
use anyhow::{bail, Result};
use log::{debug, error, warn};
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use zbus::connection::Builder;
use zbus::{block_on, interface, Connection};
pub const KWIN_SCRIPT: &str = include_str!("kwin-script.js");
pub const KWIN_SCRIPT_PLUGIN_NAME: &str = "xremap";
pub struct KdeClient {
active_window: Arc<Mutex<ActiveWindow>>,
oneoff_scripts: KwinScripts,
log_window_changes: bool,
}
impl KdeClient {
pub fn new(log_window_changes: bool) -> KdeClient {
let active_window = Arc::new(Mutex::new(ActiveWindow::default()));
let oneoff_scripts = KwinScripts::new();
KdeClient {
active_window,
oneoff_scripts,
log_window_changes,
}
}
fn connect(&mut self) -> Result<()> {
let active_window = Arc::clone(&self.active_window);
let log_window_changes = self.log_window_changes;
let (tx, rx) = channel();
std::thread::spawn(move || {
let connect = move || -> Result<Connection> {
let awi = DbusServerInterface {
active_window,
log_window_changes,
};
let connection = Builder::session()?
.name("com.k0kubun.Xremap")?
.serve_at("/com/k0kubun/Xremap", awi)?
.build();
Ok(block_on(connection)?)
};
match connect() {
Ok(_) => {
tx.send(Ok(())).unwrap();
loop {
thread::sleep(Duration::from_secs(86400));
}
}
Err(err) => tx.send(Err(err)).unwrap(),
}
});
rx.recv().unwrap()?;
ensure_script_loaded()?;
if let Err(err) = self.oneoff_scripts.send_active_window_script_once() {
error!("{err:?}")
}
for i in 0..10 {
if let Ok(aw) = self.active_window.try_lock() {
if !aw.title.is_empty() {
debug!("Connected to KDE within: {}ms", i * 10);
return Ok(());
}
}
thread::sleep(Duration::from_millis(10));
}
debug!("Connection to KDE was not established within 100ms");
Ok(())
}
}
impl Client for KdeClient {
fn supported(&mut self) -> bool {
let conn_res = self.connect();
if let Err(err) = &conn_res {
warn!("Could not connect to kwin-script. Error: {err:?}");
}
conn_res.is_ok()
}
fn current_window(&mut self) -> Option<String> {
let aw = self.active_window.lock().ok()?;
Some(aw.title.clone())
}
fn current_application(&mut self) -> Option<String> {
let aw = self.active_window.lock().ok()?;
Some(aw.res_class.clone())
}
fn window_list(&mut self) -> Result<Vec<WindowInfo>> {
bail!("window_list not implemented for KDE")
}
fn close_windows_by_app_class(&mut self, app_class: &str) -> Result<()> {
self.oneoff_scripts.close_windows_by_app_class(app_class)
}
}
#[derive(Default)]
pub struct ActiveWindow {
res_class: String,
title: String,
}
struct DbusServerInterface {
active_window: Arc<Mutex<ActiveWindow>>,
log_window_changes: bool,
}
#[interface(name = "com.k0kubun.Xremap")]
impl DbusServerInterface {
fn notify_active_window(&self, title: String, res_class: String, _res_name: String) {
println!("Restart or relogin is needed to finish update and make KDE integration work fully.");
self.notify_active_window2(title, res_class)
}
fn notify_active_window2(&self, title: String, res_class: String) {
if self.log_window_changes {
println!("active window: caption: '{title}', class: '{res_class}'");
}
let mut aw = self.active_window.lock().unwrap();
aw.title = title;
aw.res_class = res_class;
}
}