Skip to content

Commit

Permalink
feat(refactor): refactor to allow generic usage of komokana
Browse files Browse the repository at this point in the history
  • Loading branch information
Abdalrahman Mursi authored and LGUG2Z committed Jan 1, 2024
1 parent 6a53686 commit ff83a29
Show file tree
Hide file tree
Showing 6 changed files with 463 additions and 397 deletions.
105 changes: 105 additions & 0 deletions src/events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use crate::configuration::Strategy;
use crate::{CONFIG, DEFAULT_LAYER, KANATA};
use color_eyre::Result;
use serde_json::json;
use std::io::Write;
use windows::Win32::UI::Input::KeyboardAndMouse::GetKeyState;

#[derive(Debug, Copy, Clone)]
pub enum Event {
Show,
FocusChange,
}

pub fn handle_event(event: Event, exe: &str, title: &str) -> Result<()> {
let target = calculate_target(
event,
exe,
title,
if matches!(event, Event::FocusChange) {
Option::from(DEFAULT_LAYER.get().unwrap().as_ref())
} else {
None
},
);

if let Some(target) = target {
let stream = &mut KANATA.get().unwrap().get_stream();
let mut stream = stream.lock();
let request = json!({
"ChangeLayer": {
"new": target,
}
});

stream.write_all(request.to_string().as_bytes())?;
log::debug!("request sent: {request}");
};

Ok(())
}

fn calculate_target(event: Event, exe: &str, title: &str, default: Option<&str>) -> Option<String> {
let configuration = CONFIG.get().unwrap();
let mut new_layer = default;
for entry in configuration {
if entry.exe == exe {
if matches!(event, Event::FocusChange) {
new_layer = Option::from(entry.target_layer.as_str());
}

if let Some(title_overrides) = &entry.title_overrides {
for title_override in title_overrides {
match title_override.strategy {
Strategy::StartsWith => {
if title.starts_with(&title_override.title) {
new_layer = Option::from(title_override.target_layer.as_str());
}
}
Strategy::EndsWith => {
if title.ends_with(&title_override.title) {
new_layer = Option::from(title_override.target_layer.as_str());
}
}
Strategy::Contains => {
if title.contains(&title_override.title) {
new_layer = Option::from(title_override.target_layer.as_str());
}
}
Strategy::Equals => {
if title.eq(&title_override.title) {
new_layer = Option::from(title_override.target_layer.as_str());
}
}
}
}

// This acts like a default target layer within the application
// which defaults back to the entry's main target layer
if new_layer.is_none() {
new_layer = Option::from(entry.target_layer.as_str());
}
}

if matches!(event, Event::FocusChange) {
if let Some(virtual_key_overrides) = &entry.virtual_key_overrides {
for virtual_key_override in virtual_key_overrides {
if unsafe { GetKeyState(virtual_key_override.virtual_key_code) } < 0 {
new_layer = Option::from(virtual_key_override.targer_layer.as_str());
}
}
}

if let Some(virtual_key_ignores) = &entry.virtual_key_ignores {
for virtual_key in virtual_key_ignores {
if unsafe { GetKeyState(*virtual_key) } < 0 {
new_layer = None;
}
}
}
}
}
}

new_layer.and_then(|new_layer| Option::from(new_layer.to_string()))
}
95 changes: 95 additions & 0 deletions src/kanata.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use color_eyre::Result;
use json_dotpath::DotPaths;
use parking_lot::Mutex;
use std::io::Read;
use std::net::TcpStream;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use std::time::Duration;
use crate::TMPFILE;

pub static KANATA_DISCONNECTED: AtomicBool = AtomicBool::new(false);

#[derive(Debug)]
pub struct Kanata {
stream: Arc<Mutex<TcpStream>>,
port: i32,
}

impl Kanata {
pub fn new(port: i32) -> Result<Self> {
Ok(Self {
stream: Arc::new(Mutex::new(Self::connect_to_kanata(port)?)),
port,
})
}

pub fn get_stream(&self) -> Arc<Mutex<TcpStream>> {
self.stream.clone()
}

fn connect_to_kanata(port: i32) -> Result<TcpStream> {
Ok(TcpStream::connect(format!("localhost:{port}"))?)
}

fn re_establish_connection(&self) -> Result<TcpStream> {
KANATA_DISCONNECTED.store(true, Ordering::SeqCst);
log::warn!("kanata tcp server is no longer running");

let mut result = Self::connect_to_kanata(self.port);
while result.is_err() {
log::warn!("kanata tcp server is not running, retrying connection in 5 seconds");
std::thread::sleep(Duration::from_secs(5));
result = Self::connect_to_kanata(self.port);
}

log::info!("reconnected to kanata on read thread");
KANATA_DISCONNECTED.store(false, Ordering::SeqCst);
result
}

pub fn spawn_kanata_listener(&'static self) {
let stream_read = self.get_stream();
let tmpfile = TMPFILE.get().unwrap().to_owned();
log::info!("listening");

std::thread::spawn(move || -> Result<()> {
let mut reader = stream_read.lock();
let mut read_stream = reader.try_clone()?;

loop {
let mut buf = vec![0; 1024];
match read_stream.read(&mut buf) {
Ok(bytes_read) => {
let data = String::from_utf8(buf[0..bytes_read].to_vec())?;
if data == "\n" {
continue;
}

let notification: serde_json::Value = serde_json::from_str(&data)?;

if notification.dot_has("LayerChange.new") {
if let Some(new) = notification.dot_get::<String>("LayerChange.new")? {
log::info!("current layer: {new}");
if tmpfile {
let mut tmp = std::env::temp_dir();
tmp.push("kanata_layer");
std::fs::write(tmp, new)?;
}
}
}
}
Err(error) => {
// Connection reset
if error.raw_os_error().expect("could not get raw os error") == 10054 {
*reader = self.re_establish_connection()?;
read_stream = reader.try_clone()?;
}
}
}
}
});
}
}
22 changes: 22 additions & 0 deletions src/linux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use color_eyre::eyre::Result;
use std::path::PathBuf;
use crate::Provider;

pub struct Komokana {
}

impl Provider for Komokana {
fn init() -> Result<Self>
where
Self: Sized {
todo!()
}

fn listen(self) {
todo!()
}

fn resolve_config_path(config: &str) -> Result<PathBuf> {
todo!()
}
}

0 comments on commit ff83a29

Please sign in to comment.