diff --git a/Cargo.lock b/Cargo.lock index 4344009..27c6919 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,6 +134,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cocoa" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "667fdc068627a2816b9ff831201dd9864249d6ee8d190b9532357f1fc0f61ea7" +dependencies = [ + "bitflags", + "block", + "core-foundation 0.9.3", + "core-graphics 0.21.0", + "foreign-types", + "libc", + "objc", +] + [[package]] name = "cocoa" version = "0.24.1" @@ -143,8 +158,8 @@ dependencies = [ "bitflags", "block", "cocoa-foundation", - "core-foundation", - "core-graphics", + "core-foundation 0.9.3", + "core-graphics 0.22.3", "foreign-types", "libc", "objc", @@ -158,7 +173,7 @@ checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" dependencies = [ "bitflags", "block", - "core-foundation", + "core-foundation 0.9.3", "core-graphics-types", "foreign-types", "libc", @@ -181,22 +196,62 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys 0.7.0", + "libc", +] + [[package]] name = "core-foundation" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.8.3", "libc", ] +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + [[package]] name = "core-foundation-sys" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "core-graphics" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" +dependencies = [ + "bitflags", + "core-foundation 0.7.0", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a67c4378cf203eace8fb6567847eb641fd6ff933c1145a115c6ee820ebb978" +dependencies = [ + "bitflags", + "core-foundation 0.9.3", + "foreign-types", + "libc", +] + [[package]] name = "core-graphics" version = "0.22.3" @@ -204,7 +259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.9.3", "core-graphics-types", "foreign-types", "libc", @@ -217,7 +272,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.9.3", "foreign-types", "libc", ] @@ -228,8 +283,8 @@ version = "19.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" dependencies = [ - "core-foundation", - "core-graphics", + "core-foundation 0.9.3", + "core-graphics 0.22.3", "foreign-types", "libc", ] @@ -294,8 +349,8 @@ dependencies = [ "block", "cairo-rs", "cfg-if", - "cocoa", - "core-graphics", + "cocoa 0.24.1", + "core-graphics 0.22.3", "foreign-types", "gdk", "gdk-pixbuf", @@ -673,14 +728,15 @@ name = "goxkey" version = "0.1.0" dependencies = [ "bitflags", - "core-foundation", - "core-graphics", + "core-foundation 0.9.3", + "core-graphics 0.22.3", "druid", "env_logger", "foreign-types", "libc", "log", "once_cell", + "rdev", "vi", ] @@ -1034,7 +1090,7 @@ dependencies = [ "cairo-rs", "cairo-sys-rs", "cfg-if", - "core-graphics", + "core-graphics 0.22.3", "piet", "piet-cairo", "piet-coregraphics", @@ -1050,9 +1106,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5af1ce12cf0a1dc7f894f6f90ba62467d685a39b7f1d235516c8688f98e92d18" dependencies = [ - "core-foundation", - "core-foundation-sys", - "core-graphics", + "core-foundation 0.9.3", + "core-foundation-sys 0.8.3", + "core-graphics 0.22.3", "core-text", "foreign-types", "piet", @@ -1176,6 +1232,22 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "rdev" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f183f2103ea19c0e2ab5c41c930f0fb3270b6ba7695fd0f5d12b73b86e64b10" +dependencies = [ + "cocoa 0.22.0", + "core-foundation 0.7.0", + "core-foundation-sys 0.7.0", + "core-graphics 0.19.2", + "lazy_static", + "libc", + "winapi", + "x11", +] + [[package]] name = "regex" version = "1.7.1" @@ -1826,6 +1898,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "xi-unicode" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 2d542a6..7fd72de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ vi = "0.3.5" bitflags = "1.3.2" druid = "0.7.0" once_cell = "1.17.0" +rdev = "0.5.2" [package.metadata.bundle] copyright = "Copyright (c) Huy Tran 2023. All rights reserved." diff --git a/src/input.rs b/src/input.rs index 5122876..d3091bf 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,8 +1,8 @@ -use std::{fmt::Display, str::FromStr}; +use std::{collections::HashMap, fmt::Display, str::FromStr}; use druid::{Data, Target}; use log::debug; -use once_cell::sync::Lazy; +use once_cell::sync::{Lazy, OnceCell}; use crate::{ config::{CONFIG_MANAGER, HOTKEY_CONFIG_KEY, TYPING_METHOD_CONFIG_KEY}, @@ -32,6 +32,68 @@ const TONABLE_VOWELS: [char; 144] = [ pub static mut INPUT_STATE: Lazy = Lazy::new(|| InputState::new()); +pub const PREDEFINED_CHARS: [char; 47] = [ + 'a', '`', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 'q', 'w', 'e', 'r', 't', + 'y', 'u', 'i', 'o', 'p', '[', ']', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '\\', + 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', +]; + +pub fn get_key_from_char(c: char) -> rdev::Key { + use rdev::Key::*; + match &c { + 'a' => KeyA, + '`' => BackQuote, + '1' => Num1, + '2' => Num2, + '3' => Num3, + '4' => Num4, + '5' => Num5, + '6' => Num6, + '7' => Num7, + '8' => Num8, + '9' => Num9, + '0' => Num0, + '-' => Minus, + '=' => Equal, + 'q' => KeyQ, + 'w' => KeyW, + 'e' => KeyE, + 'r' => KeyR, + 't' => KeyT, + 'y' => KeyY, + 'u' => KeyU, + 'i' => KeyI, + 'o' => KeyO, + 'p' => KeyP, + '[' => LeftBracket, + ']' => RightBracket, + 's' => KeyS, + 'd' => KeyD, + 'f' => KeyF, + 'g' => KeyG, + 'h' => KeyH, + 'j' => KeyJ, + 'k' => KeyK, + 'l' => KeyL, + ';' => SemiColon, + '\'' => Quote, + '\\' => BackSlash, + 'z' => KeyZ, + 'x' => KeyX, + 'c' => KeyC, + 'v' => KeyV, + 'b' => KeyB, + 'n' => KeyN, + 'm' => KeyM, + ',' => Comma, + '.' => Dot, + '/' => Slash, + _ => Unknown(0), + } +} + +pub static KEYBOARD_LAYOUT_CHARACTER_MAP: OnceCell> = OnceCell::new(); + #[derive(PartialEq, Eq, Data, Clone, Copy)] pub enum TypingMethod { VNI, diff --git a/src/main.rs b/src/main.rs index bdf738b..6ee0934 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,14 +5,15 @@ mod platform; mod ui; use druid::{AppLauncher, ExtEventSink, Target, WindowDesc}; -use input::INPUT_STATE; +use input::{get_key_from_char, INPUT_STATE, KEYBOARD_LAYOUT_CHARACTER_MAP, PREDEFINED_CHARS}; use log::debug; use once_cell::sync::OnceCell; use platform::{ run_event_listener, send_backspace, send_string, Handle, KeyModifier, KEY_DELETE, KEY_ENTER, KEY_ESCAPE, KEY_SPACE, KEY_TAB, }; -use std::thread; +use rdev::{EventType, Keyboard, KeyboardState}; +use std::{collections::HashMap, thread}; use ui::{UIDataAdapter, UPDATE_UI}; static UI_EVENT_SINK: OnceCell = OnceCell::new(); @@ -38,6 +39,7 @@ fn event_handler(handle: Handle, keycode: Option, modifiers: KeyModifier) unsafe { match keycode { Some(keycode) => { + println!("KEYCODE: {}", keycode); // Toggle Vietnamese input mod with Ctrl + Cmd + Space key if INPUT_STATE.get_hotkey().is_match(modifiers, &keycode) { INPUT_STATE.toggle_vietnamese(); @@ -99,6 +101,17 @@ fn event_handler(handle: Handle, keycode: Option, modifiers: KeyModifier) fn main() { env_logger::init(); + let mut map = HashMap::new(); + let mut kb = Keyboard::new().unwrap(); + for c in PREDEFINED_CHARS { + let key = rdev::EventType::KeyPress(get_key_from_char(c)); + if let Some(s) = kb.add(&key) { + let ch = s.chars().last().unwrap(); + map.insert(c, ch); + } + } + _ = KEYBOARD_LAYOUT_CHARACTER_MAP.set(map); + let win = WindowDesc::new(ui::main_ui_builder) .title("gõkey") .window_size((320.0, 234.0)) diff --git a/src/platform/macos.rs b/src/platform/macos.rs index c6b952b..7d392ed 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -1,4 +1,6 @@ -use std::{env, os, path::PathBuf, ptr}; +use std::{env, path::PathBuf, ptr}; + +use crate::input::KEYBOARD_LAYOUT_CHARACTER_MAP; use super::{CallbackFn, KeyModifier, KEY_DELETE, KEY_ENTER, KEY_ESCAPE, KEY_SPACE, KEY_TAB}; use core_foundation::runloop::{kCFRunLoopCommonModes, CFRunLoop}; @@ -21,52 +23,61 @@ pub fn get_home_dir() -> Option { env::var("HOME").ok().map(PathBuf::from) } -// Modified from http://ritter.ist.psu.edu/projects/RUI/macosx/rui.c +// List of keycode: https://eastmanreference.com/complete-list-of-applescript-key-codes fn get_char(keycode: CGKeyCode) -> Option { - match keycode { - 0 => Some('a'), - 1 => Some('s'), - 2 => Some('d'), - 3 => Some('f'), - 4 => Some('h'), - 5 => Some('g'), - 6 => Some('z'), - 7 => Some('x'), - 8 => Some('c'), - 9 => Some('v'), - 11 => Some('b'), - 12 => Some('q'), - 13 => Some('w'), - 14 => Some('e'), - 15 => Some('r'), - 16 => Some('y'), - 17 => Some('t'), - 31 => Some('o'), - 32 => Some('u'), - 34 => Some('i'), - 35 => Some('p'), - 37 => Some('l'), - 38 => Some('j'), - 40 => Some('k'), - 45 => Some('n'), - 46 => Some('m'), - 18 => Some('1'), - 19 => Some('2'), - 20 => Some('3'), - 21 => Some('4'), - 22 => Some('6'), - 23 => Some('5'), - 25 => Some('9'), - 26 => Some('7'), - 28 => Some('8'), - 29 => Some('0'), - 36 | 52 => Some(KEY_ENTER), // ENTER - 49 => Some(KEY_SPACE), // SPACE - 48 => Some(KEY_TAB), // TAB - 51 => Some(KEY_DELETE), // DELETE - 53 => Some(KEY_ESCAPE), // ESC - _ => None, + if let Some(key_map) = KEYBOARD_LAYOUT_CHARACTER_MAP.get() { + println!("GOT KEYCODE {:?}", keycode); + return match keycode { + 0 => Some(key_map[&'a']), + 1 => Some(key_map[&'s']), + 2 => Some(key_map[&'d']), + 3 => Some(key_map[&'f']), + 4 => Some(key_map[&'h']), + 5 => Some(key_map[&'g']), + 6 => Some(key_map[&'z']), + 7 => Some(key_map[&'x']), + 8 => Some(key_map[&'c']), + 9 => Some(key_map[&'v']), + 11 => Some(key_map[&'b']), + 12 => Some(key_map[&'q']), + 13 => Some(key_map[&'w']), + 14 => Some(key_map[&'e']), + 15 => Some(key_map[&'r']), + 16 => Some(key_map[&'y']), + 17 => Some(key_map[&'t']), + 31 => Some(key_map[&'o']), + 32 => Some(key_map[&'u']), + 34 => Some(key_map[&'i']), + 35 => Some(key_map[&'p']), + 37 => Some(key_map[&'l']), + 38 => Some(key_map[&'j']), + 40 => Some(key_map[&'k']), + 45 => Some(key_map[&'n']), + 46 => Some(key_map[&'m']), + 18 => Some(key_map[&'1']), + 19 => Some(key_map[&'2']), + 20 => Some(key_map[&'3']), + 21 => Some(key_map[&'4']), + 22 => Some(key_map[&'6']), + 23 => Some(key_map[&'5']), + 25 => Some(key_map[&'9']), + 26 => Some(key_map[&'7']), + 28 => Some(key_map[&'8']), + 29 => Some(key_map[&'0']), + 27 => Some(key_map[&'-']), + 33 => Some(key_map[&'[']), + 30 => Some(key_map[&']']), + 41 => Some(key_map[&';']), + 43 => Some(key_map[&',']), + 36 | 52 => Some(KEY_ENTER), // ENTER + 49 => Some(KEY_SPACE), // SPACE + 48 => Some(KEY_TAB), // TAB + 51 => Some(KEY_DELETE), // DELETE + 53 => Some(KEY_ESCAPE), // ESC + _ => None, + }; } + None } #[link(name = "CoreGraphics", kind = "framework")] @@ -265,6 +276,7 @@ pub fn run_event_listener(callback: &CallbackFn) { if flags.contains(CGEventFlags::CGEventFlagAlternate) { modifiers.add_alt(); } + if callback(proxy, get_char(key_code), modifiers) { // block the key if already processed return None;