Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ nix = "0.22.0"
[target.'cfg(target_os="windows")'.dependencies]
winapi = { version = "0.3.8", features = ["libloaderapi", "winuser", "windef", "minwindef", "guiddef", "combaseapi", "wingdi", "errhandlingapi", "ole2", "oleidl", "shellapi", "winerror"] }
uuid = { version = "0.8", features = ["v4"], optional = true }
dtor = "=0.0.6"

[target.'cfg(target_os="macos")'.dependencies]
cocoa = "0.24.0"
Expand Down
129 changes: 129 additions & 0 deletions src/win/hook.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use std::{
ffi::c_int,
ptr,
sync::{Mutex, Once},
};

use dtor::dtor;
use winapi::{
shared::{
minwindef::{LPARAM, WPARAM},
windef::{HHOOK, POINT},
},
um::{
libloaderapi::GetModuleHandleA,
processthreadsapi::GetCurrentThreadId,
winuser::{
CallNextHookEx, GetClassNameA, GetWindowLongPtrW, SetWindowsHookExA,
UnhookWindowsHookEx, GWLP_USERDATA, HC_ACTION, MSG, PM_REMOVE, WH_GETMESSAGE, WM_CHAR,
WM_KEYDOWN, WM_KEYUP, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_USER,
},
},
};

use crate::win::{wnd_proc_inner, WindowState};

static HOOK: Mutex<WinKeyboardHook> = Mutex::new(WinKeyboardHook::new());
static ONCE: Once = Once::new();

// initialize keyboard hook
// some DAWs (particularly Ableton) intercept incoming keyboard messages,
// but we're naughty so we intercept them right back
//
// this is invoked by Window::open() since Rust doesn't have runtime static ctors
pub(crate) fn init_keyboard_hook() {
ONCE.call_once(|| {
HOOK.lock().unwrap().hook = unsafe {
SetWindowsHookExA(
WH_GETMESSAGE,
Some(keyboard_hook_callback),
GetModuleHandleA(ptr::null()),
GetCurrentThreadId(),
)
};
});
}

#[dtor]
fn deinit_keyboard_hook() {
let hook = HOOK.lock().unwrap();

if !hook.hook.is_null() {
unsafe {
UnhookWindowsHookEx(hook.hook);
}
}
}

struct WinKeyboardHook {
hook: HHOOK,
}

impl WinKeyboardHook {
const fn new() -> Self {
Self { hook: ptr::null_mut() }
}
}

// SAFETY: it's a pointer behind a mutex. we'll live
unsafe impl Send for WinKeyboardHook {}
unsafe impl Sync for WinKeyboardHook {}

unsafe extern "system" fn keyboard_hook_callback(
n_code: c_int, wparam: WPARAM, lparam: LPARAM,
) -> isize {
let msg = lparam as *mut MSG;

if n_code == HC_ACTION && wparam == PM_REMOVE as usize && offer_message_to_baseview(msg) {
*msg = MSG {
hwnd: ptr::null_mut(),
message: WM_USER,
wParam: 0,
lParam: 0,
time: 0,
pt: POINT { x: 0, y: 0 },
};

0
} else {
CallNextHookEx(ptr::null_mut(), n_code, wparam, lparam)
}
}

// check if `msg` is a keyboard message addressed
// to a baseview window, and intercept it if so
unsafe fn offer_message_to_baseview(msg: *mut MSG) -> bool {
let msg = &*msg;

// if this isn't a keyboard message, ignore it
match msg.message {
WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP | WM_CHAR | WM_SYSCHAR => {}

_ => return false,
}

// check if this is a baseview window (gross)
let mut classname = [0u8; 9];

// SAFETY: It's Probably ASCII Lmao
if GetClassNameA(msg.hwnd, &mut classname as *mut u8 as *mut i8, 9) != 0 {
if &classname[0..8] == "Baseview".as_bytes() {
let window_state_ptr =
GetWindowLongPtrW(msg.hwnd, GWLP_USERDATA) as *mut WindowState;

// NASTY to invoke wnd_proc_inner directly like this But It Works
// should we do anything with the return value here?
let _ = wnd_proc_inner(
msg.hwnd,
msg.message,
msg.wParam,
msg.lParam,
&*window_state_ptr,
);

return true;
}
}

false
}
1 change: 1 addition & 0 deletions src/win/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod cursor;
mod drop_target;
mod hook;
mod keyboard;
mod window;

Expand Down
15 changes: 10 additions & 5 deletions src/win/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use raw_window_handle::{

const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1;

use crate::win::hook;
use crate::{
Event, MouseButton, MouseCursor, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent,
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
Expand Down Expand Up @@ -165,7 +166,7 @@ unsafe extern "system" fn wnd_proc(

/// Our custom `wnd_proc` handler. If the result contains a value, then this is returned after
/// handling any deferred tasks. otherwise the default window procedure is invoked.
unsafe fn wnd_proc_inner(
pub(crate) unsafe fn wnd_proc_inner(
hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM, window_state: &WindowState,
) -> Option<LRESULT> {
match msg {
Expand Down Expand Up @@ -686,11 +687,15 @@ impl Window<'_> {
);
// todo: manage error ^

hook::init_keyboard_hook();

#[cfg(feature = "opengl")]
let gl_context: Option<GlContext> = options.gl_config.map(|gl_config| {
let mut handle = Win32WindowHandle::empty();
handle.hwnd = hwnd as *mut c_void;
let handle = RawWindowHandle::Win32(handle);
let gl_context: Option<GlContext> = options
.gl_config
.map(|gl_config| {
let mut handle = Win32WindowHandle::empty();
handle.hwnd = hwnd as *mut c_void;
let handle = RawWindowHandle::Win32(handle);

GlContext::create(&handle, gl_config).expect("Could not create OpenGL context")
});
Expand Down