Initial macOS detect implementation
This commit is contained in:
parent
18515319a8
commit
afb64df17c
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -145,6 +145,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cc",
|
"cc",
|
||||||
"enum-as-inner",
|
"enum-as-inner",
|
||||||
|
"lazy_static",
|
||||||
"lazycell",
|
"lazycell",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
@ -18,6 +18,9 @@ widestring = "0.4.3"
|
||||||
libc = "0.2.85"
|
libc = "0.2.85"
|
||||||
scopeguard = "1.1.0"
|
scopeguard = "1.1.0"
|
||||||
|
|
||||||
|
[target.'cfg(target_os="macos")'.dependencies]
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.0.66"
|
cc = "1.0.66"
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,16 @@ fn cc_config() {
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
fn cc_config() {
|
fn cc_config() {
|
||||||
// TODO
|
println!("cargo:rerun-if-changed=src/mac/native.mm");
|
||||||
|
println!("cargo:rerun-if-changed=src/mac/native.h");
|
||||||
|
cc::Build::new()
|
||||||
|
.cpp(true)
|
||||||
|
.include("src/mac/native.h")
|
||||||
|
.file("src/mac/native.mm")
|
||||||
|
.compile("espansodetect");
|
||||||
|
println!("cargo:rustc-link-lib=dylib=c++");
|
||||||
|
println!("cargo:rustc-link-lib=static=espansodetect");
|
||||||
|
println!("cargo:rustc-link-lib=framework=Cocoa");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -227,6 +227,9 @@ fn key_sym_to_key(key_sym: i32) -> (Key, Option<Variant>) {
|
||||||
0xFF56 => (PageDown, None),
|
0xFF56 => (PageDown, None),
|
||||||
0xFF55 => (PageUp, None),
|
0xFF55 => (PageUp, None),
|
||||||
|
|
||||||
|
// UI
|
||||||
|
0xFF1B => (Escape, None),
|
||||||
|
|
||||||
// Editing keys
|
// Editing keys
|
||||||
0xFF08 => (Backspace, None),
|
0xFF08 => (Backspace, None),
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,9 @@ pub enum Key {
|
||||||
PageDown,
|
PageDown,
|
||||||
PageUp,
|
PageUp,
|
||||||
|
|
||||||
|
// UI
|
||||||
|
Escape,
|
||||||
|
|
||||||
// Editing keys
|
// Editing keys
|
||||||
Backspace,
|
Backspace,
|
||||||
|
|
||||||
|
|
|
@ -27,3 +27,10 @@ pub mod x11;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub mod evdev;
|
pub mod evdev;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub mod mac;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
343
espanso-detect/src/mac/mod.rs
Normal file
343
espanso-detect/src/mac/mod.rs
Normal file
|
@ -0,0 +1,343 @@
|
||||||
|
/*
|
||||||
|
* This file is part of espanso.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019-2021 Federico Terzi
|
||||||
|
*
|
||||||
|
* espanso is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* espanso is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::{ffi::{CStr}, sync::{
|
||||||
|
mpsc::{channel, Receiver, Sender},
|
||||||
|
Arc, Mutex,
|
||||||
|
}};
|
||||||
|
|
||||||
|
use lazycell::LazyCell;
|
||||||
|
use log::{error, trace, warn};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::event::Status::*;
|
||||||
|
use crate::event::Variant::*;
|
||||||
|
use crate::event::{InputEvent, Key, KeyboardEvent, Variant};
|
||||||
|
use crate::event::{Key::*, MouseButton, MouseEvent};
|
||||||
|
|
||||||
|
const INPUT_EVENT_TYPE_KEYBOARD: i32 = 1;
|
||||||
|
const INPUT_EVENT_TYPE_MOUSE: i32 = 2;
|
||||||
|
|
||||||
|
const INPUT_STATUS_PRESSED: i32 = 1;
|
||||||
|
const INPUT_STATUS_RELEASED: i32 = 2;
|
||||||
|
|
||||||
|
const INPUT_MOUSE_LEFT_BUTTON: i32 = 1;
|
||||||
|
const INPUT_MOUSE_RIGHT_BUTTON: i32 = 2;
|
||||||
|
const INPUT_MOUSE_MIDDLE_BUTTON: i32 = 3;
|
||||||
|
|
||||||
|
// Take a look at the native.h header file for an explanation of the fields
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct RawInputEvent {
|
||||||
|
pub event_type: i32,
|
||||||
|
|
||||||
|
pub buffer: [u8; 24],
|
||||||
|
pub buffer_len: i32,
|
||||||
|
|
||||||
|
pub key_code: i32,
|
||||||
|
pub status: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(improper_ctypes)]
|
||||||
|
#[link(name = "espansodetect", kind = "static")]
|
||||||
|
extern "C" {
|
||||||
|
pub fn detect_initialize(callback: extern "C" fn(event: RawInputEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref CURRENT_SENDER: Arc<Mutex<Option<Sender<InputEvent>>>> = Arc::new(Mutex::new(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn native_callback(raw_event: RawInputEvent) {
|
||||||
|
let lock = CURRENT_SENDER
|
||||||
|
.lock()
|
||||||
|
.expect("unable to acquire CocoaSource sender lock");
|
||||||
|
if let Some(sender) = lock.as_ref() {
|
||||||
|
let event: Option<InputEvent> = raw_event.into();
|
||||||
|
if let Some(event) = event {
|
||||||
|
if let Err(error) = sender.send(event) {
|
||||||
|
error!("Unable to send event to Cocoa Sender: {}", error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trace!("Unable to convert raw event to input event");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("Lost raw event, as Cocoa Sender is not available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CocoaSourceCallback = Box<dyn Fn(InputEvent)>;
|
||||||
|
pub struct CocoaSource {
|
||||||
|
receiver: LazyCell<Receiver<InputEvent>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
impl CocoaSource {
|
||||||
|
pub fn new() -> CocoaSource {
|
||||||
|
Self {
|
||||||
|
receiver: LazyCell::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initialize(&mut self) -> Result<()> {
|
||||||
|
let (sender, receiver) = channel();
|
||||||
|
|
||||||
|
// Set the global sender
|
||||||
|
{
|
||||||
|
let mut lock = CURRENT_SENDER
|
||||||
|
.lock()
|
||||||
|
.expect("unable to acquire CocoaSource sender lock during initialization");
|
||||||
|
*lock = Some(sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { detect_initialize(native_callback) };
|
||||||
|
|
||||||
|
if self.receiver.fill(receiver).is_err() {
|
||||||
|
error!("Unable to set CocoaSource receiver");
|
||||||
|
return Err(CocoaSourceError::Unknown().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eventloop(&self, event_callback: CocoaSourceCallback) {
|
||||||
|
if let Some(receiver) = self.receiver.borrow() {
|
||||||
|
loop {
|
||||||
|
let event = receiver.recv();
|
||||||
|
match event {
|
||||||
|
Ok(event) => {
|
||||||
|
event_callback(event);
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
error!("CocoaSource receiver reported error: {}", error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("Unable to start event loop if CocoaSource receiver is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for CocoaSource {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Reset the global sender
|
||||||
|
{
|
||||||
|
let mut lock = CURRENT_SENDER
|
||||||
|
.lock()
|
||||||
|
.expect("unable to acquire CocoaSource sender lock during initialization");
|
||||||
|
*lock = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum CocoaSourceError {
|
||||||
|
#[error("unknown error")]
|
||||||
|
Unknown(),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RawInputEvent> for Option<InputEvent> {
|
||||||
|
fn from(raw: RawInputEvent) -> Option<InputEvent> {
|
||||||
|
let status = match raw.status {
|
||||||
|
INPUT_STATUS_RELEASED => Released,
|
||||||
|
INPUT_STATUS_PRESSED => Pressed,
|
||||||
|
_ => Pressed,
|
||||||
|
};
|
||||||
|
|
||||||
|
match raw.event_type {
|
||||||
|
// Keyboard events
|
||||||
|
INPUT_EVENT_TYPE_KEYBOARD => {
|
||||||
|
let (key, variant) = key_code_to_key(raw.key_code);
|
||||||
|
|
||||||
|
let value = if raw.buffer_len > 0 {
|
||||||
|
let raw_string_result = CStr::from_bytes_with_nul(&raw.buffer[..((raw.buffer_len + 1) as usize)]);
|
||||||
|
match raw_string_result {
|
||||||
|
Ok(c_string) => {
|
||||||
|
let string_result = c_string.to_str();
|
||||||
|
match string_result {
|
||||||
|
Ok(value) => Some(value.to_string()),
|
||||||
|
Err(err) => {
|
||||||
|
warn!("CocoaSource event utf8 conversion error: {}", err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
trace!("Received malformed event buffer: {}", err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
return Some(InputEvent::Keyboard(KeyboardEvent {
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
status,
|
||||||
|
variant,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// Mouse events
|
||||||
|
INPUT_EVENT_TYPE_MOUSE => {
|
||||||
|
let button = raw_to_mouse_button(raw.key_code);
|
||||||
|
|
||||||
|
if let Some(button) = button {
|
||||||
|
return Some(InputEvent::Mouse(MouseEvent { button, status }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mappings from: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
|
||||||
|
fn key_code_to_key(key_code: i32) -> (Key, Option<Variant>) {
|
||||||
|
match key_code {
|
||||||
|
// Modifiers
|
||||||
|
0x3A => (Alt, Some(Left)),
|
||||||
|
0x3D => (Alt, Some(Right)),
|
||||||
|
0x39 => (CapsLock, None), // TODO
|
||||||
|
0x3B => (Control, Some(Left)),
|
||||||
|
0x3E => (Control, Some(Right)),
|
||||||
|
0x37 => (Meta, Some(Left)),
|
||||||
|
0x36 => (Meta, Some(Right)),
|
||||||
|
0x38 => (Shift, Some(Left)),
|
||||||
|
0x3C => (Shift, Some(Right)),
|
||||||
|
|
||||||
|
// Whitespace
|
||||||
|
0x24 => (Enter, None),
|
||||||
|
0x30 => (Tab, None),
|
||||||
|
0x31 => (Space, None),
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
0x7D => (ArrowDown, None),
|
||||||
|
0x7B => (ArrowLeft, None),
|
||||||
|
0x7C => (ArrowRight, None),
|
||||||
|
0x7E => (ArrowUp, None),
|
||||||
|
0x77 => (End, None),
|
||||||
|
0x73 => (Home, None),
|
||||||
|
0x79 => (PageDown, None),
|
||||||
|
0x74 => (PageUp, None),
|
||||||
|
|
||||||
|
// UI
|
||||||
|
0x35 => (Escape, None),
|
||||||
|
|
||||||
|
// Editing keys
|
||||||
|
0x33 => (Backspace, None),
|
||||||
|
|
||||||
|
// Function keys
|
||||||
|
0x7A => (F1, None),
|
||||||
|
0x78 => (F2, None),
|
||||||
|
0x63 => (F3, None),
|
||||||
|
0x76 => (F4, None),
|
||||||
|
0x60 => (F5, None),
|
||||||
|
0x61 => (F6, None),
|
||||||
|
0x62 => (F7, None),
|
||||||
|
0x64 => (F8, None),
|
||||||
|
0x65 => (F9, None),
|
||||||
|
0x6D => (F10, None),
|
||||||
|
0x67 => (F11, None),
|
||||||
|
0x6F => (F12, None),
|
||||||
|
0x69 => (F13, None),
|
||||||
|
0x6B => (F14, None),
|
||||||
|
0x71 => (F15, None),
|
||||||
|
0x6A => (F16, None),
|
||||||
|
0x40 => (F17, None),
|
||||||
|
0x4F => (F18, None),
|
||||||
|
0x50 => (F19, None),
|
||||||
|
0x5A => (F20, None),
|
||||||
|
|
||||||
|
// Other keys, includes the raw code provided by the operating system
|
||||||
|
_ => (Other(key_code), None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn raw_to_mouse_button(raw: i32) -> Option<MouseButton> {
|
||||||
|
match raw {
|
||||||
|
INPUT_MOUSE_LEFT_BUTTON => Some(MouseButton::Left),
|
||||||
|
INPUT_MOUSE_RIGHT_BUTTON => Some(MouseButton::Right),
|
||||||
|
INPUT_MOUSE_MIDDLE_BUTTON => Some(MouseButton::Middle),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn default_raw_input_event() -> RawInputEvent {
|
||||||
|
RawInputEvent {
|
||||||
|
event_type: INPUT_EVENT_TYPE_KEYBOARD,
|
||||||
|
buffer: [0; 24],
|
||||||
|
buffer_len: 0,
|
||||||
|
key_code: 0,
|
||||||
|
status: INPUT_STATUS_PRESSED,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn raw_to_input_event_keyboard_works_correctly() {
|
||||||
|
let c_string = CString::new("k".to_string()).unwrap();
|
||||||
|
let mut buffer: [u8; 24] = [0; 24];
|
||||||
|
buffer[..1].copy_from_slice(c_string.as_bytes());
|
||||||
|
|
||||||
|
let mut raw = default_raw_input_event();
|
||||||
|
raw.buffer = buffer;
|
||||||
|
raw.buffer_len = 1;
|
||||||
|
raw.status = INPUT_STATUS_RELEASED;
|
||||||
|
raw.key_code = 40;
|
||||||
|
|
||||||
|
let result: Option<InputEvent> = raw.into();
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap(),
|
||||||
|
InputEvent::Keyboard(KeyboardEvent {
|
||||||
|
key: Other(40),
|
||||||
|
status: Released,
|
||||||
|
value: Some("k".to_string()),
|
||||||
|
variant: None,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn raw_to_input_event_mouse_works_correctly() {
|
||||||
|
let mut raw = default_raw_input_event();
|
||||||
|
raw.event_type = INPUT_EVENT_TYPE_MOUSE;
|
||||||
|
raw.status = INPUT_STATUS_RELEASED;
|
||||||
|
raw.key_code = INPUT_MOUSE_RIGHT_BUTTON;
|
||||||
|
|
||||||
|
let result: Option<InputEvent> = raw.into();
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap(),
|
||||||
|
InputEvent::Mouse(MouseEvent {
|
||||||
|
status: Released,
|
||||||
|
button: MouseButton::Right,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
61
espanso-detect/src/mac/native.h
Normal file
61
espanso-detect/src/mac/native.h
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* This file is part of espanso.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019-2021 Federico Terzi
|
||||||
|
*
|
||||||
|
* espanso is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* espanso is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ESPANSO_DETECT_H
|
||||||
|
#define ESPANSO_DETECT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define INPUT_EVENT_TYPE_KEYBOARD 1
|
||||||
|
#define INPUT_EVENT_TYPE_MOUSE 2
|
||||||
|
|
||||||
|
#define INPUT_STATUS_PRESSED 1
|
||||||
|
#define INPUT_STATUS_RELEASED 2
|
||||||
|
|
||||||
|
#define INPUT_LEFT_VARIANT 1
|
||||||
|
#define INPUT_RIGHT_VARIANT 2
|
||||||
|
|
||||||
|
#define INPUT_MOUSE_LEFT_BUTTON 1
|
||||||
|
#define INPUT_MOUSE_RIGHT_BUTTON 2
|
||||||
|
#define INPUT_MOUSE_MIDDLE_BUTTON 3
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
// Keyboard or Mouse event
|
||||||
|
int32_t event_type;
|
||||||
|
|
||||||
|
// Contains the string corresponding to the key, if any
|
||||||
|
char buffer[24];
|
||||||
|
// Length of the extracted string. Equals 0 if no string is extracted
|
||||||
|
int32_t buffer_len;
|
||||||
|
|
||||||
|
// Virtual key code of the pressed key in case of keyboard events
|
||||||
|
// Mouse button code otherwise.
|
||||||
|
int32_t key_code;
|
||||||
|
|
||||||
|
// Pressed or Released status
|
||||||
|
int32_t status;
|
||||||
|
} InputEvent;
|
||||||
|
|
||||||
|
typedef void (*EventCallback)(InputEvent data);
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize the event global monitor
|
||||||
|
extern "C" void * detect_initialize(EventCallback callback);
|
||||||
|
|
||||||
|
#endif //ESPANSO_DETECT_H
|
78
espanso-detect/src/mac/native.mm
Normal file
78
espanso-detect/src/mac/native.mm
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* This file is part of espanso.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019-2021 Federico Terzi
|
||||||
|
*
|
||||||
|
* espanso is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* espanso is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "native.h"
|
||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#include <Carbon/Carbon.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
const unsigned long long FLAGS = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged | NSEventMaskLeftMouseDown |
|
||||||
|
NSEventMaskLeftMouseUp | NSEventMaskRightMouseDown | NSEventMaskRightMouseUp |
|
||||||
|
NSEventMaskOtherMouseDown | NSEventMaskOtherMouseUp;
|
||||||
|
|
||||||
|
void * detect_initialize(EventCallback callback) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||||
|
[NSEvent addGlobalMonitorForEventsMatchingMask:FLAGS handler:^(NSEvent *event){
|
||||||
|
InputEvent inputEvent = {};
|
||||||
|
if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp ) {
|
||||||
|
inputEvent.event_type = INPUT_EVENT_TYPE_KEYBOARD;
|
||||||
|
inputEvent.status = (event.type == NSEventTypeKeyDown) ? INPUT_STATUS_PRESSED : INPUT_STATUS_RELEASED;
|
||||||
|
inputEvent.key_code = event.keyCode;
|
||||||
|
|
||||||
|
const char *chars = [event.characters UTF8String];
|
||||||
|
strncpy(inputEvent.buffer, chars, 23);
|
||||||
|
inputEvent.buffer_len = event.characters.length;
|
||||||
|
|
||||||
|
callback(inputEvent);
|
||||||
|
}else if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown ||
|
||||||
|
event.type == NSEventTypeLeftMouseUp || event.type == NSEventTypeRightMouseUp || event.type == NSEventTypeOtherMouseUp) {
|
||||||
|
inputEvent.event_type = INPUT_EVENT_TYPE_MOUSE;
|
||||||
|
inputEvent.status = (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown ||
|
||||||
|
event.type == NSEventTypeOtherMouseDown) ? INPUT_STATUS_PRESSED : INPUT_STATUS_RELEASED;
|
||||||
|
if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeLeftMouseUp) {
|
||||||
|
inputEvent.key_code = INPUT_MOUSE_LEFT_BUTTON;
|
||||||
|
} else if (event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeRightMouseUp) {
|
||||||
|
inputEvent.key_code = INPUT_MOUSE_RIGHT_BUTTON;
|
||||||
|
} else if (event.type == NSEventTypeOtherMouseDown || event.type == NSEventTypeOtherMouseUp) {
|
||||||
|
inputEvent.key_code = INPUT_MOUSE_MIDDLE_BUTTON;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(inputEvent);
|
||||||
|
}else{
|
||||||
|
// Modifier keys (SHIFT, CTRL, ecc) are handled as a separate case on macOS
|
||||||
|
inputEvent.event_type = INPUT_EVENT_TYPE_KEYBOARD;
|
||||||
|
inputEvent.key_code = event.keyCode;
|
||||||
|
|
||||||
|
// To determine whether these keys are pressed or released, we have to analyze each case
|
||||||
|
if (event.keyCode == kVK_Shift || event.keyCode == kVK_RightShift) {
|
||||||
|
inputEvent.status = (([event modifierFlags] & NSEventModifierFlagShift) == 0) ? INPUT_STATUS_RELEASED : INPUT_STATUS_PRESSED;
|
||||||
|
} else if (event.keyCode == kVK_Command || event.keyCode == kVK_RightCommand) {
|
||||||
|
inputEvent.status = (([event modifierFlags] & NSEventModifierFlagCommand) == 0) ? INPUT_STATUS_RELEASED : INPUT_STATUS_PRESSED;
|
||||||
|
} else if (event.keyCode == kVK_Control || event.keyCode == kVK_RightControl) {
|
||||||
|
inputEvent.status = (([event modifierFlags] & NSEventModifierFlagControl) == 0) ? INPUT_STATUS_RELEASED : INPUT_STATUS_PRESSED;
|
||||||
|
} else if (event.keyCode == kVK_Option || event.keyCode == kVK_RightOption) {
|
||||||
|
inputEvent.status = (([event modifierFlags] & NSEventModifierFlagOption) == 0) ? INPUT_STATUS_RELEASED : INPUT_STATUS_PRESSED;
|
||||||
|
}
|
||||||
|
callback(inputEvent);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
});
|
||||||
|
}
|
|
@ -260,6 +260,9 @@ fn key_code_to_key(key_code: i32) -> (Key, Option<Variant>) {
|
||||||
0x22 => (PageDown, None),
|
0x22 => (PageDown, None),
|
||||||
0x21 => (PageUp, None),
|
0x21 => (PageUp, None),
|
||||||
|
|
||||||
|
// UI
|
||||||
|
0x1B => (Escape, None),
|
||||||
|
|
||||||
// Editing keys
|
// Editing keys
|
||||||
0x08 => (Backspace, None),
|
0x08 => (Backspace, None),
|
||||||
|
|
||||||
|
|
|
@ -260,6 +260,9 @@ fn key_sym_to_key(key_sym: i32) -> (Key, Option<Variant>) {
|
||||||
0xFF56 => (PageDown, None),
|
0xFF56 => (PageDown, None),
|
||||||
0xFF55 => (PageUp, None),
|
0xFF55 => (PageUp, None),
|
||||||
|
|
||||||
|
// UI keys
|
||||||
|
0xFF1B => (Escape, None),
|
||||||
|
|
||||||
// Editing keys
|
// Editing keys
|
||||||
0xFF08 => (Backspace, None),
|
0xFF08 => (Backspace, None),
|
||||||
|
|
||||||
|
|
|
@ -46,26 +46,27 @@ fn main() {
|
||||||
icon_paths: &icon_paths,
|
icon_paths: &icon_paths,
|
||||||
});
|
});
|
||||||
|
|
||||||
let handle = std::thread::spawn(move || {
|
eventloop.initialize();
|
||||||
|
|
||||||
|
let handle = std::thread::spawn(move || {
|
||||||
//let mut source = espanso_detect::win32::Win32Source::new();
|
//let mut source = espanso_detect::win32::Win32Source::new();
|
||||||
//let mut source = espanso_detect::x11::X11Source::new();
|
//let mut source = espanso_detect::x11::X11Source::new();
|
||||||
// source.initialize();
|
let mut source = espanso_detect::mac::CocoaSource::new();
|
||||||
// source.eventloop(Box::new(move |event: InputEvent| {
|
source.initialize();
|
||||||
// println!("ev {:?}", event);
|
source.eventloop(Box::new(move |event: InputEvent| {
|
||||||
// match event {
|
println!("ev {:?}", event);
|
||||||
// InputEvent::Mouse(_) => {}
|
match event {
|
||||||
// InputEvent::Keyboard(evt) => {
|
InputEvent::Mouse(_) => {}
|
||||||
// if evt.key == espanso_detect::event::Key::Shift && evt.status == Status::Pressed {
|
InputEvent::Keyboard(evt) => {
|
||||||
// //remote.update_tray_icon(espanso_ui::icons::TrayIcon::Disabled);
|
if evt.key == espanso_detect::event::Key::Shift && evt.status == Status::Pressed {
|
||||||
// remote.show_notification("Espanso is running!");
|
//remote.update_tray_icon(espanso_ui::icons::TrayIcon::Disabled);
|
||||||
// }
|
//remote.show_notification("Espanso is running!");
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }));
|
}
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
eventloop.initialize();
|
|
||||||
eventloop.run(Box::new(move |event| {
|
eventloop.run(Box::new(move |event| {
|
||||||
println!("ui {:?}", event);
|
println!("ui {:?}", event);
|
||||||
let menu = Menu::from(vec![
|
let menu = Menu::from(vec![
|
||||||
|
|
Loading…
Reference in New Issue
Block a user