From adaf7de10bf2c7057f5a4ad313b1b35f6196f268 Mon Sep 17 00:00:00 2001 From: Jonathan Ami <126116089+jonathan-ami@users.noreply.github.com> Date: Mon, 1 Apr 2024 11:21:47 -0700 Subject: [PATCH] working shortcut keybinds --- Cargo.lock | 2 + core/Cargo.toml | 2 + core/src/keybinds.rs | 101 +++++++++++++++++++++++++++++++++++-- core/src/main.rs | 19 ++++--- core/src/user_config.rs | 79 +++++++++++++++++++++++++++++ core/src/window_manager.rs | 9 ++-- src-tauri/src/config.rs | 7 +-- 7 files changed, 198 insertions(+), 21 deletions(-) create mode 100644 core/src/user_config.rs diff --git a/Cargo.lock b/Cargo.lock index 38f4d3e..12f4551 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3362,6 +3362,8 @@ name = "whirlwin" version = "0.1.0" dependencies = [ "crossbeam-channel", + "serde", + "serde_json", "windows 0.54.0", ] diff --git a/core/Cargo.toml b/core/Cargo.toml index f0b5ed6..14b966a 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -5,6 +5,8 @@ edition = "2021" [dependencies] crossbeam-channel = "0.5" +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } [dependencies.windows] version = "0.54" diff --git a/core/src/keybinds.rs b/core/src/keybinds.rs index e5771b9..8ffc6d2 100644 --- a/core/src/keybinds.rs +++ b/core/src/keybinds.rs @@ -1,6 +1,9 @@ +use crate::user_config::Config; use crate::window_manager::{Direction, WindowManagerMessage}; use std::io::Error; +use std::path::PathBuf; +use std::process::Command; use std::sync::mpsc::Sender; use std::sync::Arc; @@ -9,17 +12,28 @@ use windows::Win32::UI::Input::KeyboardAndMouse::{ }; //Hotkey indentifies + +const LEADER: i32 = 0; const EXIT: i32 = 1; const SWITCH_LEFT: i32 = 2; const SWITCH_RIGHT: i32 = 3; const SWITCH_ABOVE: i32 = 4; const SWITCH_BELOW: i32 = 5; const SWITCH_NEXT: i32 = 6; -const LEADER: i32 = 7; const CLOSE_WINDOW: i32 = 8; const SWITCH_PREVIOUS: i32 = 9; +const SHORTCUT_1: i32 = 10; +const SHORTCUT_2: i32 = 11; +const SHORTCUT_3: i32 = 12; +const SHORTCUT_4: i32 = 13; +const SHORTCUT_5: i32 = 14; +const SHORTCUT_6: i32 = 15; +const SHORTCUT_7: i32 = 16; +const SHORTCUT_8: i32 = 17; +const SHORTCUT_9: i32 = 18; +const SHORTCUT_10: i32 = 19; -//Keycode +//Keycodes const KEY_H: u32 = 0x48; const KEY_L: u32 = 0x4C; const KEY_J: u32 = 0x4A; @@ -32,20 +46,32 @@ const SHIFT: u32 = 0x10; const SPACE: u32 = 0x20; const ENTER: u32 = 0x0D; const CAPS: u32 = 0x14; +const KEY_0: u32 = 0x30; +const KEY_1: u32 = 0x31; +const KEY_2: u32 = 0x32; +const KEY_3: u32 = 0x33; +const KEY_4: u32 = 0x34; +const KEY_5: u32 = 0x35; +const KEY_6: u32 = 0x36; +const KEY_7: u32 = 0x37; +const KEY_8: u32 = 0x38; +const KEY_9: u32 = 0x39; pub fn handle_hotkey( wparam: i32, sender: &Arc>, leader_pressed: bool, + config: &Config, ) -> Result { if !leader_pressed && wparam == LEADER { println!("leader pressed"); - match register_hotkeys() { + match register_hotkeys(config) { Ok(_) => return Ok(true), Err(e) => return Err(format!("Error: {}", e)), }; } if leader_pressed { + println!("leader pressed"); match wparam { EXIT => { if let Err(err) = sender.send(WindowManagerMessage::EndListener) { @@ -96,6 +122,56 @@ pub fn handle_hotkey( println!("Failed to send message: {}", err); } } + SHORTCUT_1 => { + let _ = Command::new(PathBuf::from(config.programs[0].clone())) + .status() + .expect("Failed to oppen program 1"); + } + SHORTCUT_2 => { + let _ = Command::new(PathBuf::from(config.programs[1].clone())) + .status() + .expect("Failed to oppen program 1"); + } + SHORTCUT_3 => { + let _ = Command::new(PathBuf::from(config.programs[2].clone())) + .status() + .expect("Failed to oppen program 1"); + } + SHORTCUT_4 => { + let _ = Command::new(PathBuf::from(config.programs[3].clone())) + .status() + .expect("Failed to oppen program 1"); + } + SHORTCUT_5 => { + let _ = Command::new(PathBuf::from(config.programs[4].clone())) + .status() + .expect("Failed to oppen program 1"); + } + SHORTCUT_6 => { + let _ = Command::new(PathBuf::from(config.programs[5].clone())) + .status() + .expect("Failed to oppen program 1"); + } + SHORTCUT_7 => { + let _ = Command::new(PathBuf::from(config.programs[6].clone())) + .status() + .expect("Failed to oppen program 1"); + } + SHORTCUT_8 => { + let _ = Command::new(PathBuf::from(config.programs[7].clone())) + .status() + .expect("Failed to oppen program 1"); + } + SHORTCUT_9 => { + let _ = Command::new(PathBuf::from(config.programs[8].clone())) + .status() + .expect("Failed to oppen program 1"); + } + SHORTCUT_10 => { + let _ = Command::new(PathBuf::from(config.programs[9].clone())) + .status() + .expect("Failed to oppen program 1"); + } _ => { println!("idk bru"); } @@ -119,8 +195,19 @@ pub fn unregister_leader() { let _ = UnregisterHotKey(None, LEADER); } } -fn register_hotkeys() -> Result<(), Error> { +fn register_hotkeys(config: &Config) -> Result<(), Error> { unsafe { + for i in 0..config.programs.len() { + if config.programs[i] != "" { + if let Err(e) = + RegisterHotKey(None, i as i32 + 10, HOT_KEY_MODIFIERS(0), i as u32 + 0x31) + { + println!("failed to register key {}. Error: {}", i as u32 + 0x30, e); + } else { + println!("registered key {}", i + 0x30); + } + } + } if let Err(_) = RegisterHotKey(None, EXIT, HOT_KEY_MODIFIERS(0), ESC) { println!("failed to register ESC"); } @@ -166,6 +253,10 @@ pub fn unregister_hotkeys() { let _ = UnregisterHotKey(None, SWITCH_NEXT); let _ = UnregisterHotKey(None, CLOSE_WINDOW); let _ = UnregisterHotKey(None, SWITCH_PREVIOUS); + let _ = UnregisterHotKey(None, SWITCH_PREVIOUS); + // Unregister shortcuts + for i in 10..20 { + let _ = UnregisterHotKey(None, i as i32); + } } } - diff --git a/core/src/main.rs b/core/src/main.rs index 26e6c05..90f421c 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -1,5 +1,6 @@ mod callbacks; mod keybinds; +mod user_config; mod window; mod window_manager; @@ -7,7 +8,7 @@ use callbacks::win_event_proc; use keybinds::{handle_hotkey, register_leader, unregister_leader}; use window_manager::{WindowManager, WindowManagerMessage}; -use std::io::Error; +use std::error::Error; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Sender}; use std::sync::Arc; @@ -69,8 +70,12 @@ fn spawn_hook( } } -fn key_listener(sender: Arc>, callback_thread_id: u32) { - println!("key listener running..."); +fn key_listener( + sender: Arc>, + callback_thread_id: u32, +) -> Result<(), Box> { + let config = user_config::read_config()?; + let mut leader_pressed = false; unsafe { let mut msg: MSG = MSG::default(); @@ -78,7 +83,7 @@ fn key_listener(sender: Arc>, callback_thread_id: u if msg.message == WM_HOTKEY { HOTKEY_PRESSED.store(true, Ordering::Relaxed); let wparam = msg.wParam.0 as i32; - match handle_hotkey(wparam, &sender, leader_pressed) { + match handle_hotkey(wparam, &sender, leader_pressed, &config) { Ok(leader) => { leader_pressed = leader; } @@ -95,9 +100,10 @@ fn key_listener(sender: Arc>, callback_thread_id: u } } } + Ok(()) } -fn main() -> Result<(), Error> { +fn main() -> Result<(), Box> { let (sender, receiver) = channel(); let sender_arc = Arc::new(sender); @@ -118,8 +124,8 @@ fn main() -> Result<(), Error> { let sender = Arc::clone(&sender_arc); thread::spawn(move || spawn_hook(sender, thread_id_sender)) }; - let callback_thread_id = callback_receiver.recv().unwrap(); + let callback_thread_id = callback_receiver.recv().unwrap(); key_listener(Arc::clone(&sender_arc), callback_thread_id); window_manger_listener.join().unwrap(); @@ -127,4 +133,3 @@ fn main() -> Result<(), Error> { unregister_leader(); Ok(()) } - diff --git a/core/src/user_config.rs b/core/src/user_config.rs new file mode 100644 index 0000000..38d4858 --- /dev/null +++ b/core/src/user_config.rs @@ -0,0 +1,79 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::env; +use std::error::Error; +use std::fs::{File, OpenOptions}; +use std::io::Read; +use std::path::PathBuf; + +#[derive(Serialize, Deserialize)] +pub struct Config { + pub programs: Vec, + pub keybinds: HashMap, +} + +impl Config { + fn new() -> Self { + let mut keybinds = HashMap::new(); + keybinds.insert("left".to_string(), 0); + keybinds.insert("right".to_string(), 0); + keybinds.insert("down".to_string(), 0); + keybinds.insert("next".to_string(), 0); + keybinds.insert("prev".to_string(), 0); + keybinds.insert("above".to_string(), 0); + let programs: Vec = vec!["".to_string(); 10]; + Config { programs, keybinds } + } +} + +pub fn read_config() -> Result> { + let mut path = match get_path() { + Ok(path) => path, + Err(e) => return Err(e), + }; + path.push("config.json"); + println!("{:?}", path); + let mut file = match OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open(&path) + { + Ok(file) => file, + Err(e) => { + return Err(format!("Error opening file {}", e).into()); + } + }; + + let mut json_data = String::new(); + if let Err(e) = file.read_to_string(&mut json_data) { + return Err(format!("Failed to read user data from file {}", e).into()); + } + + let config = match serde_json::from_str(&json_data) { + Ok(data) => data, + Err(_) => Config::new(), + }; + Ok(config) +} + +fn get_path() -> Result> { + let mut appdata_path = match env::var_os("LOCALAPPDATA") { + Some(path) => PathBuf::from(path), + None => { + eprintln!("Failed to retreive APPDATA env var"); + return Err("Failed to retreive APPDATA env var".into()); + } + }; + + if !appdata_path.exists() { + return Err(r"Appdata\Local does not exist".into()); + } + appdata_path.push("whirlwin"); + if !appdata_path.exists() { + if let Err(err) = std::fs::create_dir_all(&appdata_path) { + return Err("Failed to create whirlwin dir".into()); + } + } + Ok(appdata_path) +} diff --git a/core/src/window_manager.rs b/core/src/window_manager.rs index 787663a..e4d1d3b 100644 --- a/core/src/window_manager.rs +++ b/core/src/window_manager.rs @@ -5,8 +5,8 @@ use std::collections::HashSet; use std::sync::mpsc::Receiver; use windows::Win32::Foundation::{HWND, LPARAM}; use windows::Win32::UI::WindowsAndMessaging::{ - CloseWindow, EnumWindows, GetForegroundWindow, SetForegroundWindow, SetWindowPos, HWND_BOTTOM, - SWP_NOMOVE, SWP_NOSIZE, + CloseWindow, EnumWindows, GetForegroundWindow, SendMessageW, SetForegroundWindow, SetWindowPos, + HWND_BOTTOM, SWP_NOMOVE, SWP_NOSIZE, WM_CLOSE, }; pub enum Direction { Left, @@ -118,9 +118,7 @@ impl WindowManager { fn close_window(&mut self) { unsafe { - if let Err(e) = CloseWindow(self.current.hwnd) { - println!("Failed to close window {}", e); - } + let _ = SendMessageW(self.current.hwnd, WM_CLOSE, None, None); } if let Some(window) = &self.next { self.current = window.to_owned(); @@ -237,4 +235,3 @@ impl WindowManager { self.set_windows(); } } - diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index e45c078..a1f8326 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::env; use std::error::Error; use std::fs::{File, OpenOptions}; -use std::io::{Read, Write}; +use std::io::{Read, Seek, SeekFrom, Write}; use std::path::PathBuf; #[derive(Serialize, Deserialize)] @@ -27,6 +27,7 @@ impl Config { fn write_to_file(&self, mut file: File) -> Result<(), Box> { let json_data = serde_json::to_string(self)?; + file.seek(SeekFrom::Start(0))?; file.write_all(json_data.as_bytes())?; Ok(()) } @@ -69,7 +70,7 @@ pub fn set_data(shortcut: PathBuf, shortcut_num: usize) { }; println!("programs size: {}", config.programs.len()); - config.programs[shortcut_num] = shortcut.to_string_lossy().to_string(); + config.programs[shortcut_num - 1] = shortcut.to_string_lossy().to_string(); if let Err(e) = config.write_to_file(file) { eprintln!("Error writing to file: {}", e); } @@ -89,7 +90,7 @@ fn get_path() -> Result> { } appdata_path.push("whirlwin"); if !appdata_path.exists() { - if let Err(err) = std::fs::create_dir_all(&appdata_path) { + if let Err(_) = std::fs::create_dir_all(&appdata_path) { return Err("Failed to create whirlwin dir".into()); } }