-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(refactor): refactor to allow generic usage of komokana
- Loading branch information
Showing
6 changed files
with
463 additions
and
397 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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())) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()?; | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!() | ||
} | ||
} |
Oops, something went wrong.