Skip to content

Commit

Permalink
Support non-US keyboard layout on macOS
Browse files Browse the repository at this point in the history
This commit implements a look up table to map between CGKeyCode
on the physical keyboard and the actual output character from the
selected input source. This table is built once when the application
starts.

Currently, we have to restart the application if we change the
input source. This can be improved in the future.

Fixes #20
  • Loading branch information
huytd committed Feb 21, 2023
1 parent 4a03672 commit 8aa549f
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 65 deletions.
114 changes: 98 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -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."
Expand Down
66 changes: 64 additions & 2 deletions 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},
Expand Down Expand Up @@ -32,6 +32,68 @@ const TONABLE_VOWELS: [char; 144] = [

pub static mut INPUT_STATE: Lazy<InputState> = 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<HashMap<char, char>> = OnceCell::new();

#[derive(PartialEq, Eq, Data, Clone, Copy)]
pub enum TypingMethod {
VNI,
Expand Down
17 changes: 15 additions & 2 deletions src/main.rs
Expand Up @@ -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<ExtEventSink> = OnceCell::new();
Expand All @@ -38,6 +39,7 @@ fn event_handler(handle: Handle, keycode: Option<char>, 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();
Expand Down Expand Up @@ -99,6 +101,17 @@ fn event_handler(handle: Handle, keycode: Option<char>, 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))
Expand Down

0 comments on commit 8aa549f

Please sign in to comment.