From e71f51687be3cfa73eba997a32cd61ad754433ec Mon Sep 17 00:00:00 2001 From: moparisthebest Date: Mon, 14 Oct 2019 22:17:17 -0400 Subject: [PATCH] intermediate macos commit to remove --- Cargo.lock | 45 ++++ Cargo.toml | 7 + build.sh | 10 + src/lib.rs | 10 + src/macos/codes.rs | 561 +++++++++++++++++++++++++++++++++++++++++++++ src/macos/mod.rs | 503 ++++++++++++++++++++++++++++++++++++++++ src/main.rs | 4 +- 7 files changed, 1138 insertions(+), 2 deletions(-) create mode 100644 build.sh create mode 100644 src/macos/codes.rs create mode 100644 src/macos/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 68201d0..3ad3131 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,44 @@ name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "core-foundation" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "core-graphics" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "getopts" version = "0.2.21" @@ -88,6 +126,8 @@ dependencies = [ name = "rusty-keys" version = "0.0.3" dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "inotify 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -182,6 +222,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2" "checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +"checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" "checksum inotify 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24e40d6fd5d64e2082e0c796495c8ef5ad667a96d03e5aaa0becfd9d47bcbfb8" "checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" diff --git a/Cargo.toml b/Cargo.toml index fb36c3f..fb49146 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,13 @@ getopts = "0.2.21" toml = "0.5.3" serde = { version = "1.0.101", features = ["derive"] } +#[target.'cfg(target_os="macos")'.dependencies] +core-graphics = "0.17" +core-foundation-sys = "0.6.2" +#rustkit = "0.0.1" +lazy_static = "1.4.0" +libc = "0.2" + [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winuser", "wincon"] } lazy_static = "1.4.0" diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..2dd089a --- /dev/null +++ b/build.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# https://github.com/millerjs/modelm +# mount.cifs -o user=mopar,pass=,uid=1000,gid=1000,forceuid //mojave/mopar /mnt/mojave/ + +ssh mojave bash -s "$@" < HashMap<&'static str, CGKeyCode> { + [ + // grep 'Key => 0x' ../rusty-keys-win/src/windows/inputs.rs | tr '[a-z]' '[A-Z]' | sed -r -e 's/KEY => 0X/: CGKeyCode = 0x/' -e 's/^[ ]+/pub const KEY_/' | tr ',' ';' + ("ESC", KEY_ESC), + ("1", KEY_1), + ("2", KEY_2), + ("3", KEY_3), + ("4", KEY_4), + ("5", KEY_5), + ("6", KEY_6), + ("7", KEY_7), + ("8", KEY_8), + ("9", KEY_9), + ("10", KEY_10), + ("MINUS", KEY_MINUS), + ("EQUAL", KEY_EQUAL), + ("BACKSPACE", KEY_BACKSPACE), + ("TAB", KEY_TAB), + ("Q", KEY_Q), + ("W", KEY_W), + ("E", KEY_E), + ("R", KEY_R), + ("T", KEY_T), + ("Y", KEY_Y), + ("U", KEY_U), + ("I", KEY_I), + ("O", KEY_O), + ("P", KEY_P), + ("LEFTBRACE", KEY_LEFTBRACE), + ("RIGHTBRACE", KEY_RIGHTBRACE), + ("ENTER", KEY_ENTER), + ("LEFTCTRL", KEY_LEFTCTRL), + ("A", KEY_A), + ("S", KEY_S), + ("D", KEY_D), + ("F", KEY_F), + ("G", KEY_G), + ("H", KEY_H), + ("J", KEY_J), + ("K", KEY_K), + ("L", KEY_L), + ("SEMICOLON", KEY_SEMICOLON), + ("APOSTROPHE", KEY_APOSTROPHE), + ("GRAVE", KEY_GRAVE), + ("LEFTSHIFT", KEY_LEFTSHIFT), + ("BACKSLASH", KEY_BACKSLASH), + ("Z", KEY_Z), + ("X", KEY_X), + ("C", KEY_C), + ("V", KEY_V), + ("B", KEY_B), + ("N", KEY_N), + ("M", KEY_M), + ("COMMA", KEY_COMMA), + ("DOT", KEY_DOT), + ("SLASH", KEY_SLASH), + ("RIGHTSHIFT", KEY_RIGHTSHIFT), + ("KPASTERISK", KEY_KPASTERISK), + ("LEFTALT", KEY_LEFTALT), + ("SPACE", KEY_SPACE), + ("CAPSLOCK", KEY_CAPSLOCK), + ("F1", KEY_F1), + ("F2", KEY_F2), + ("F3", KEY_F3), + ("F4", KEY_F4), + ("F5", KEY_F5), + ("F6", KEY_F6), + ("F7", KEY_F7), + ("F8", KEY_F8), + ("F9", KEY_F9), + ("F10", KEY_F10), + ("NUMLOCK", KEY_NUMLOCK), + ("SCROLLLOCK", KEY_SCROLLLOCK), + ("KP7", KEY_KP7), + ("KP8", KEY_KP8), + ("KP9", KEY_KP9), + ("KPMINUS", KEY_KPMINUS), + ("KP4", KEY_KP4), + ("KP5", KEY_KP5), + ("KP6", KEY_KP6), + ("KPPLUS", KEY_KPPLUS), + ("KP1", KEY_KP1), + ("KP2", KEY_KP2), + ("KP3", KEY_KP3), + ("KP0", KEY_KP0), + ("KPDOT", KEY_KPDOT), + /* + ("ZENKAKUHANKAKU", KEY_ZENKAKUHANKAKU), + ("102ND", KEY_102ND), + */ + ("F11", KEY_F11), + ("F12", KEY_F12), + /* + ("RO", KEY_RO), + ("KATAKANA", KEY_KATAKANA), + ("HIRAGANA", KEY_HIRAGANA), + ("HENKAN", KEY_HENKAN), + ("KATAKANAHIRAGANA", KEY_KATAKANAHIRAGANA), + ("MUHENKAN", KEY_MUHENKAN), + ("KPJPCOMMA", KEY_KPJPCOMMA), + */ + ("KPENTER", KEY_KPENTER), + ("RIGHTCTRL", KEY_RIGHTCTRL), + ("KPSLASH", KEY_KPSLASH), + ("SYSRQ", KEY_SYSRQ), + ("RIGHTALT", KEY_RIGHTALT), + /* + ("LINEFEED", KEY_LINEFEED), + */ + ("HOME", KEY_HOME), + ("UP", KEY_UP), + ("PAGEUP", KEY_PAGEUP), + ("LEFT", KEY_LEFT), + ("RIGHT", KEY_RIGHT), + ("END", KEY_END), + ("DOWN", KEY_DOWN), + ("PAGEDOWN", KEY_PAGEDOWN), + ("INSERT", KEY_INSERT), + ("DELETE", KEY_DELETE), + /* + ("MACRO", KEY_MACRO), + ("MUTE", KEY_MUTE), + ("VOLUMEDOWN", KEY_VOLUMEDOWN), + ("VOLUMEUP", KEY_VOLUMEUP), + ("POWER", KEY_POWER), + ("KPEQUAL", KEY_KPEQUAL), + ("KPPLUSMINUS", KEY_KPPLUSMINUS), + ("PAUSE", KEY_PAUSE), + ("SCALE", KEY_SCALE), + ("KPCOMMA", KEY_KPCOMMA), + ("HANGEUL", KEY_HANGEUL), + ("HANGUEL", KEY_HANGUEL), + ("HANGEUL", KEY_HANGEUL), + ("HANJA", KEY_HANJA), + ("YEN", KEY_YEN), + ("LEFTMETA", KEY_LEFTMETA), + ("RIGHTMETA", KEY_RIGHTMETA), + ("COMPOSE", KEY_COMPOSE), + ("STOP", KEY_STOP), + ("AGAIN", KEY_AGAIN), + ("PROPS", KEY_PROPS), + ("UNDO", KEY_UNDO), + ("FRONT", KEY_FRONT), + ("COPY", KEY_COPY), + ("OPEN", KEY_OPEN), + ("PASTE", KEY_PASTE), + ("FIND", KEY_FIND), + ("CUT", KEY_CUT), + ("HELP", KEY_HELP), + ("MENU", KEY_MENU), + ("CALC", KEY_CALC), + ("SETUP", KEY_SETUP), + ("SLEEP", KEY_SLEEP), + ("WAKEUP", KEY_WAKEUP), + ("FILE", KEY_FILE), + ("SENDFILE", KEY_SENDFILE), + ("DELETEFILE", KEY_DELETEFILE), + ("XFER", KEY_XFER), + ("PROG1", KEY_PROG1), + ("PROG2", KEY_PROG2), + ("WWW", KEY_WWW), + ("MSDOS", KEY_MSDOS), + ("COFFEE", KEY_COFFEE), + ("SCREENLOCK", KEY_SCREENLOCK), + ("COFFEE", KEY_COFFEE), + ("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY), + ("DIRECTION", KEY_DIRECTION), + ("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY), + ("CYCLEWINDOWS", KEY_CYCLEWINDOWS), + ("MAIL", KEY_MAIL), + ("BOOKMARKS", KEY_BOOKMARKS), + ("COMPUTER", KEY_COMPUTER), + ("BACK", KEY_BACK), + ("FORWARD", KEY_FORWARD), + ("CLOSECD", KEY_CLOSECD), + ("EJECTCD", KEY_EJECTCD), + ("EJECTCLOSECD", KEY_EJECTCLOSECD), + ("NEXTSONG", KEY_NEXTSONG), + ("PLAYPAUSE", KEY_PLAYPAUSE), + ("PREVIOUSSONG", KEY_PREVIOUSSONG), + ("STOPCD", KEY_STOPCD), + ("RECORD", KEY_RECORD), + ("REWIND", KEY_REWIND), + ("PHONE", KEY_PHONE), + ("ISO", KEY_ISO), + ("CONFIG", KEY_CONFIG), + ("HOMEPAGE", KEY_HOMEPAGE), + ("REFRESH", KEY_REFRESH), + ("EXIT", KEY_EXIT), + ("MOVE", KEY_MOVE), + ("EDIT", KEY_EDIT), + ("SCROLLUP", KEY_SCROLLUP), + ("SCROLLDOWN", KEY_SCROLLDOWN), + ("KPLEFTPAREN", KEY_KPLEFTPAREN), + ("KPRIGHTPAREN", KEY_KPRIGHTPAREN), + ("NEW", KEY_NEW), + ("REDO", KEY_REDO), + */ + ("F13", KEY_F13), + ("F14", KEY_F14), + ("F15", KEY_F15), + ("F16", KEY_F16), + ("F17", KEY_F17), + ("F18", KEY_F18), + ("F19", KEY_F19), + ("F20", KEY_F20), + ("F21", KEY_F21), + ("F22", KEY_F22), + ("F23", KEY_F23), + ("F24", KEY_F24), + /* + ("PLAYCD", KEY_PLAYCD), + ("PAUSECD", KEY_PAUSECD), + ("PROG3", KEY_PROG3), + ("PROG4", KEY_PROG4), + ("DASHBOARD", KEY_DASHBOARD), + ("SUSPEND", KEY_SUSPEND), + ("CLOSE", KEY_CLOSE), + ("PLAY", KEY_PLAY), + ("FASTFORWARD", KEY_FASTFORWARD), + ("BASSBOOST", KEY_BASSBOOST), + ("PRINT", KEY_PRINT), + ("HP", KEY_HP), + ("CAMERA", KEY_CAMERA), + ("SOUND", KEY_SOUND), + ("QUESTION", KEY_QUESTION), + ("EMAIL", KEY_EMAIL), + ("CHAT", KEY_CHAT), + ("SEARCH", KEY_SEARCH), + ("CONNECT", KEY_CONNECT), + ("FINANCE", KEY_FINANCE), + ("SPORT", KEY_SPORT), + ("SHOP", KEY_SHOP), + ("ALTERASE", KEY_ALTERASE), + ("CANCEL", KEY_CANCEL), + ("BRIGHTNESSDOWN", KEY_BRIGHTNESSDOWN), + ("BRIGHTNESSUP", KEY_BRIGHTNESSUP), + ("MEDIA", KEY_MEDIA), + ("SWITCHVIDEOMODE", KEY_SWITCHVIDEOMODE), + ("KBDILLUMTOGGLE", KEY_KBDILLUMTOGGLE), + ("KBDILLUMDOWN", KEY_KBDILLUMDOWN), + ("KBDILLUMUP", KEY_KBDILLUMUP), + ("SEND", KEY_SEND), + ("REPLY", KEY_REPLY), + ("FORWARDMAIL", KEY_FORWARDMAIL), + ("SAVE", KEY_SAVE), + ("DOCUMENTS", KEY_DOCUMENTS), + ("BATTERY", KEY_BATTERY), + ("BLUETOOTH", KEY_BLUETOOTH), + ("WLAN", KEY_WLAN), + ("UWB", KEY_UWB), + ("UNKNOWN", KEY_UNKNOWN), + ("VIDEO_NEXT", KEY_VIDEO_NEXT), + ("VIDEO_PREV", KEY_VIDEO_PREV), + ("BRIGHTNESS_CYCLE", KEY_BRIGHTNESS_CYCLE), + ("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO), + ("BRIGHTNESS_ZERO", KEY_BRIGHTNESS_ZERO), + ("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO), + ("DISPLAY_OFF", KEY_DISPLAY_OFF), + ("WWAN", KEY_WWAN), + ("WIMAX", KEY_WIMAX), + ("WWAN", KEY_WWAN), + ("RFKILL", KEY_RFKILL), + ("MICMUTE", KEY_MICMUTE), + */ + // below manual shortcuts + ("PSCR", KEY_SYSRQ), + ("SLCK", KEY_SCROLLLOCK), + ("BRK", KEY_PAUSE), + ("GRV", KEY_GRAVE), + ("0", KEY_10), // dumb or named wrong? + ("MINS", KEY_MINUS), + ("EQL", KEY_EQUAL), + ("BSPC", KEY_BACKSPACE), + ("LBRC", KEY_LEFTBRACE), + ("RBRC", KEY_RIGHTBRACE), + ("BSLS", KEY_BACKSLASH), + ("SCLN", KEY_SEMICOLON), + ("QUOT", KEY_APOSTROPHE), + ("ENT", KEY_ENTER), + ("COMM", KEY_COMMA), + ("DOT", KEY_DOT), + ("SLSH", KEY_SLASH), + ("CAPS", KEY_CAPSLOCK), + ("LSFT", KEY_LEFTSHIFT), + ("RSFT", KEY_RIGHTSHIFT), + ("SPC", KEY_SPACE), + ("APP", KEY_COMPOSE), + ("LCTL", KEY_LEFTCTRL), + ("RCTL", KEY_RIGHTCTRL), + ("LALT", KEY_LEFTALT), + ("RALT", KEY_RIGHTALT), + ("LGUI", KEY_LEFTMETA), + ("RGUI", KEY_RIGHTMETA), + ("INS", KEY_INSERT), + ("PGUP", KEY_PAGEUP), + ("PGDN", KEY_PAGEDOWN), + ("DEL", KEY_DELETE), + ("RGHT", KEY_RIGHT), + ("NLCK", KEY_NUMLOCK), + ("PSLS", KEY_KPSLASH), + ("PAST", KEY_KPASTERISK), + ("PMNS", KEY_KPMINUS), + ("P7", KEY_KP7), + ("P8", KEY_KP8), + ("P9", KEY_KP9), + ("P4", KEY_KP4), + ("P5", KEY_KP5), + ("P6", KEY_KP6), + ("PPLS", KEY_KPPLUS), + ("P1", KEY_KP1), + ("P2", KEY_KP2), + ("P3", KEY_KP3), + ("P0", KEY_KP0), + ("PDOT", KEY_KPDOT), + ("PENT", KEY_KPENTER), + ].iter().cloned().map(|(m, v)| (m, v)).collect() +} diff --git a/src/macos/mod.rs b/src/macos/mod.rs new file mode 100644 index 0000000..557bf53 --- /dev/null +++ b/src/macos/mod.rs @@ -0,0 +1,503 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(improper_ctypes)] + +use crate::*; +use std::env; +use std::process::exit; + +use getopts::Options; +use std::fs::File; + +pub mod codes; +use codes::*; + +use lazy_static::lazy_static; +use std::sync::Mutex; +use core_graphics::event::CGKeyCode; +use core_graphics::event::*; +use core_foundation_sys::*; +use core_foundation_sys::base::*; +use core_foundation_sys::runloop::*; + +/* +extern { + /// Return the type identifier for the opaque type `CGEventRef'. + //fn CGEventGetTypeID() -> CFTypeID; + pub fn CFRunLoopRun(); +} +*/ + +type MacOSKeyMaps = KeyMaps>; + +// this is used for identifying the fake keypresses we insert, so we don't process them in an infinite loop +//const FAKE_EXTRA_INFO: ULONG_PTR = 332; + +//const BLOCK_KEY: *const CGEventRef = std::ptr::null(); +//const BLOCK_KEY: *mut CGEventRef = std::ptr::null_mut(); + +pub struct InputEvent { + event_type: CGEventType, + event: CGEventRef, +} + +impl KeyEvent for InputEvent { + fn code(&self) -> CGKeyCode { + //1 + //self.event.to_owned().get_integer_value_field(EventField::KEYBOARD_EVENT_KEYCODE) as CGKeyCode + unsafe { CGEventGetIntegerValueField(self.event, kCGKeyboardEventKeycode) } + } + + fn value(&self) -> KeyState { + match self.event_type { + kCGEventKeyDown => KeyState::DOWN, + kCGEventKeyUp => KeyState::UP, + kCGEventTapDisabledByTimeout => { + println!("Quartz event tap disabled because of timeout; attempting to reregister."); + //register_listener(channel); + KeyState::OTHER + }, + _ => { + println!("Received unknown EventType: {}", self.event_type); + KeyState::OTHER + }, + } + } +} + +pub enum DeviceRet { + EVENT(CGEventRef), + NULL, +} + +pub struct Device; + +impl Keyboard> for Device { + fn send(&self, event: &mut InputEvent) -> Result> { + Ok(Some(event.event)) + } + + fn send_mod_code(&self, code: CGKeyCode, event: &mut InputEvent) -> Result> { + // event.value should only ever be UP/DOWN when this method is called + Ok(None) + } + + fn send_mod_code_value(&self, code: CGKeyCode, up_not_down: bool, _event: &mut InputEvent) -> Result> { + Ok(None) + } + + fn synchronize(&self) -> Result> { + // no-op here + Ok(None) + } + + fn left_shift_code(&self) -> CGKeyCode { + KEY_LEFTSHIFT + } + + fn right_shift_code(&self) -> CGKeyCode { + KEY_RIGHTSHIFT + } + + fn caps_lock_code(&self) -> CGKeyCode { + KEY_CAPSLOCK + } + + fn block_key(&self) -> Result> { + Ok(None) + } +} + +unsafe impl Send for MacOSKeyMaps { + // windows promises us keybd_proc will only be called by a single thread at a time + // but rust makes us wrap in mutex anyway, so we are extra safe... +} + +const DEVICE: Device = Device; + +/* +lazy_static! { +static ref KEY_MAPPER: Mutex = { + + let config = parse_args(); + //println!("Config: {:?}", config); + + let key_map = key_map(); + //println!("key_map: {:?}", key_map); + + println!("chosen config file: {}", config.config_file); + + Mutex::new(MacOSKeyMaps::from_cfg(&key_map, &config.config_file)) +}; +} +*/ + + +pub fn main_res() -> Result<()> { + // this is just to cause the lazy_static init to run first, so if -h or -v is wanted, we do that + // and exit immediately... todo: how to avoid mutex/lazy_static entirely??? + //let _ = KEY_MAPPER.lock().unwrap(); + + let config = parse_args(); + //println!("Config: {:?}", config); + + let key_map = key_map(); + //println!("key_map: {:?}", key_map); + + println!("chosen config file: {}", config.config_file); + + let key_maps = MacOSKeyMaps::from_cfg(&key_map, &config.config_file); + + let mask = CGEventMaskBit(kCGEventKeyDown) + | CGEventMaskBit(kCGEventKeyUp); + + unsafe { + let options = 0; + + // Create the event tap + let event_tap = CGEventTapCreate( + kCGSessionEventTap, + kCGHeadInsertEventTap, + options, + mask, + callback, + &key_maps, + ); + assert!(!event_tap.is_null(), + "Unable to create event tap. Please make sure you have the correct permissions"); + println!("Created event tap..."); + + let allocator = kCFAllocatorDefault; + let current_event_loop = CFRunLoopGetCurrent(); + let mode = kCFRunLoopCommonModes; + + // Create Run Loop Source + let run_loop_source = CFMachPortCreateRunLoopSource(allocator, event_tap, 0); + + // Add Run Loop Source to the current event loop + CFRunLoopAddSource(current_event_loop, run_loop_source, mode); + + // Enable the tap + CGEventTapEnable(event_tap, true); + + CFRunLoopRun(); + } + + unsafe { + /* + CFMachPortRef eventTap; + CGEventMask eventMask; + CFRunLoopSourceRef runLoopSource; + + + // Create an event tap. We are interested in key presses. + let eventMask = ((1 << kCGEventKeyDown) | (1 << kCGEventKeyUp)); + let eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0, + eventMask, myCGEventCallback, NULL); + + */ + /* + if (!eventTap) { + fprintf(stderr, "failed to create event tap\n"); + exit(1); + } + + // Create a run loop source. + let runLoopSource = CFMachPortCreateRunLoopSource( + kCFAllocatorDefault, eventTap, 0); + + // Add to the current run loop. + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, + kCFRunLoopCommonModes); + + // Enable the event tap. + CGEventTapEnable(eventTap, true); + + // Set it all running. + println!("woo1"); + CFRunLoopRun(); + println!("woo2"); + */ + } + + //std::thread::sleep(std::time::Duration::from_millis(400000)); + + Ok(()) +} + +#[derive(Debug)] +struct Config { + config_file: String +} + +impl Config { + fn new(config_file: String) -> Self { + Config { config_file: config_file } + } +} + +fn get_env_push(key: &str, to_push: &str, vec: &mut Vec) { + if let Some(var) = env::var_os(key) { + if let Ok(str) = var.into_string() { + let mut str = str.to_owned(); + str.push_str(to_push); + vec.push(str); + } + } +} + +fn parse_args() -> Config { + fn print_usage(program: &str, opts: Options) { + let brief = format!("Usage: {} [options] [keymap.toml]", program); + println!("{}", opts.usage(&brief)); + } + + let args: Vec<_> = env::args().collect(); + + let mut default_configs = Vec::new(); + get_env_push("USERPROFILE", "\\keymap.toml", &mut default_configs); + get_env_push("APPDATA", "\\keymap.toml", &mut default_configs); + + default_configs.push("keymap.toml".to_string()); + + let c_msg = format!("specify the keymap config file to use (default in order: {:?})", default_configs); + + let mut opts = Options::new(); + opts.optflag("h", "help", "prints this help message"); + opts.optflag("v", "version", "prints the version"); + opts.optopt("c", "config", &c_msg, "FILE"); + + let matches = opts.parse(&args[1..]); + if matches.is_err() { + print_usage(&args[0], opts); + exit(1); + } + let matches = matches.unwrap(); + if matches.opt_present("h") { + print_usage(&args[0], opts); + exit(0); + } + + if matches.opt_present("v") { + println!("rusty-keys {}", VERSION); + exit(0); + } + + let config_file = matches.opt_str("c").unwrap_or_else(|| { + let remaining_args = matches.free; + if remaining_args.len() > 0 { + remaining_args[0].clone() + } else { + for keymap in default_configs.drain(..) { + if File::open(&keymap).is_ok() { + return keymap; + } + } + println!("Error: no keymap.toml found..."); + print_usage(&args[0], opts); + exit(1); + } + }); + + Config::new(config_file) +} + +use libc; + +// Opaque Pointer Types +pub type Pointer = *mut libc::c_void; +pub type CGEventRef = Pointer; +pub type CFMachPortRef = Pointer; + +// Integer Types +pub type CGEventField = u32; +pub type CGEventMask = u64; +pub type CGEventTapLocation = u32; +pub type CGEventTapOptions = u32; +pub type CGEventTapPlacement = u32; +pub type CGEventType = u32; +//pub type CGKeyCode = u16; + +// Callback Type +pub type CGEventTapCallBack = extern "C" +fn(Pointer, CGEventType, CGEventRef, &mut MacOSKeyMaps) -> CGEventRef; + +// Constants +/* +pub const kCGEventKeyDown: CGEventType = CGEventType::KeyDown; +pub const kCGEventKeyUp: CGEventType = CGEventType::KeyUp; +pub const kCGEventFlagsChanged: CGEventType = CGEventType::FlagsChanged; +pub const kCGSessionEventTap: CGEventTapLocation = 1; +pub const kCGHeadInsertEventTap: CGEventTapPlacement = 0; +pub const kCGKeyboardEventKeycode: CGEventField = 9; +pub const kCGEventTapDisabledByTimeout: CGEventType = CGEventType::TapDisabledByTimeout; +*/ +pub const kCGEventKeyDown: CGEventType = 10; +pub const kCGEventKeyUp: CGEventType = 11; +pub const kCGEventFlagsChanged: CGEventType = 12; +pub const kCGSessionEventTap: CGEventTapLocation = 1; +pub const kCGHeadInsertEventTap: CGEventTapPlacement = 0; +pub const kCGKeyboardEventKeycode: CGEventField = 9; +pub const kCGEventTapDisabledByTimeout: CGEventType = 0xFFFFFFFE; + + // Link to ApplicationServices/ApplicationServices.h and Carbon/Carbon.h + #[link(name = "ApplicationServices", kind = "framework")] + #[link(name = "Carbon", kind = "framework")] + extern { + + /// Pass through to the default loop modes + pub static kCFRunLoopCommonModes: Pointer; + + /// Pass through to the default allocator + pub static kCFAllocatorDefault: Pointer; + + /// Run the current threads loop in default mode + pub fn CFRunLoopRun(); + + /// Obtain the current threads loop + pub fn CFRunLoopGetCurrent() -> Pointer; + + /// Get the code of the event back, e.g. the key code + pub fn CGEventGetIntegerValueField( + event: CGEventRef, + field: CGEventField, + ) -> CGKeyCode; + + /// Create an event tap + /// + /// # Arguments + /// + /// * `place` - The location of the new event tap. Pass one of + /// the constants listed in Event Tap Locations. Only + /// processes running as the root user may locate an + /// event tap at the point where HID events enter the + /// window server; for other users, this function + /// returns NULL. + /// + /// * `options` - The placement of the new event tap in the + /// list of active event taps. Pass one of the + /// constants listed in Event Tap Placement. + /// + /// * `eventsOfInterest` - A constant that specifies whether + /// the new event tap is a passive listener or an + /// active filter. + /// + /// * `callback` - A bit mask that specifies the set of events + /// to be observed. For a list of possible events, + /// see Event Types. For information on how to + /// specify the mask, see CGEventMask. If the event + /// tap is not permitted to monitor one or more of + /// the events specified in the eventsOfInterest + /// parameter, then the appropriate bits in the mask + /// are cleared. If that action results in an empty + /// mask, this function returns NULL. callback + /// + /// * `refcon` - An event tap callback function that you + /// provide. Your callback function is invoked from + /// the run loop to which the event tap is added as a + /// source. The thread safety of the callback is + /// defined by the run loop’s environment. To learn + /// more about event tap callbacks, see + /// CGEventTapCallBack. refcon + /// + /// * `channel` - A pointer to user-defined data. This pointer + /// is passed into the callback function specified in + /// the callback parameter. Here we use it as a mpsc + /// channel. + pub fn CGEventTapCreate( + tap: CGEventTapLocation, + place: CGEventTapPlacement, + options: CGEventTapOptions, + eventsOfInterest: CGEventMask, + callback: CGEventTapCallBack, + channel: &MacOSKeyMaps, + ) -> CFMachPortRef; + + /// Creates a CFRunLoopSource object for a CFMachPort + /// object. + /// + /// The run loop source is not automatically added to + /// a run loop. To add the source to a run loop, use + /// CFRunLoopAddSource + pub fn CFMachPortCreateRunLoopSource( + allocator: Pointer, + port: CFMachPortRef, + order: libc::c_int, + ) -> Pointer; + + /// Adds a CFRunLoopSource object to a run loop mode. + pub fn CFRunLoopAddSource( + run_loop: Pointer, + run_loop_source: Pointer, + mode: Pointer, + ); + + pub fn CGEventTapEnable(port: CFMachPortRef, enable: bool); + } + +/// This callback will be registered to be invoked from the run loop +/// to which the event tap is added as a source. +#[no_mangle] +#[allow(unused_variables)] +pub extern fn callback(proxy: Pointer, event_type: CGEventType, event: CGEventRef, key_maps: &mut MacOSKeyMaps) + -> CGEventRef { + + + match event_type { + kCGEventKeyDown => println!("key down"), + kCGEventKeyUp => println!("key up"), + kCGEventFlagsChanged => println!("flags changed"), + kCGEventTapDisabledByTimeout => { + println!("Quartz event tap disabled because of timeout; attempting to reregister."); + //register_listener(channel); + //return event; + }, + _ => { + println!("Received unknown EventType: {}", event_type as u32); + //return event; + }, + }; + + unsafe { + let mut input_event = InputEvent { + event_type, + event, + }; + println!("got keyCode: {}", input_event.code()); + //Some(input_event.event) + //input_event.event + key_maps.send_event(&mut input_event, &DEVICE).expect("macos shouldn't error...") + .unwrap_or_else(|| std::ptr::null_mut()) // None means return NULL + //let keyCode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode); + //println!("got keyCode: {}", keyCode); + /* + let event = KeyEvent { + etype: match etype as u32 { + kCGEventKeyDown => EventType::KeyDown, + kCGEventKeyUp => EventType::KeyUp, + kCGEventFlagsChanged => EventType::FlagsChanged, + kCGEventTapDisabledByTimeout => { + warn!("Quartz event tap disabled because of timeout; attempting to reregister."); + register_listener(channel); + return event; + }, + _ => { + error!("Received unknown EventType: {:}", etype); + return event; + }, + }, + code: keyCode, + }; + println!("Received event: {:?}", event); + let _ = channel.send(event); + */ + } + //event +} + +/// Redefine macro for bitshifting from header as function here +pub fn CGEventMaskBit(eventType: u32) -> CGEventMask { + 1 << (eventType) +} + diff --git a/src/main.rs b/src/main.rs index 4c97f0b..e073df4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] fn main() { let ret = rusty_keys::main_res(); if let Err(e) = ret { @@ -7,7 +7,7 @@ fn main() { } } -#[cfg(not(any(target_os = "windows", target_os = "linux")))] +#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] fn main() { panic!("sorry no main impl for this platform"); }