Skip to content

Commit 1b042ed

Browse files
authored
Merge branch 'master' into resize-window
2 parents 82f5e78 + 237d323 commit 1b042ed

File tree

8 files changed

+181
-28
lines changed

8 files changed

+181
-28
lines changed

src/gl/x11.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,5 +241,9 @@ impl GlContext {
241241
}
242242

243243
impl Drop for GlContext {
244-
fn drop(&mut self) {}
244+
fn drop(&mut self) {
245+
unsafe {
246+
glx::glXDestroyContext(self.display, self.context);
247+
}
248+
}
245249
}

src/win/drop_target.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,22 @@ use super::WindowState;
2727
// These function pointers have to be stored in a (const) variable before they can be transmuted
2828
// Transmuting is needed because winapi has a bug where the pt parameter has an incorrect
2929
// type `*const POINTL`
30+
#[allow(non_snake_case)]
3031
const DRAG_ENTER_PTR: unsafe extern "system" fn(
3132
this: *mut IDropTarget,
3233
pDataObj: *const IDataObject,
3334
grfKeyState: DWORD,
3435
pt: POINTL,
3536
pdwEffect: *mut DWORD,
3637
) -> HRESULT = DropTarget::drag_enter;
38+
#[allow(non_snake_case)]
3739
const DRAG_OVER_PTR: unsafe extern "system" fn(
3840
this: *mut IDropTarget,
3941
grfKeyState: DWORD,
4042
pt: POINTL,
4143
pdwEffect: *mut DWORD,
4244
) -> HRESULT = DropTarget::drag_over;
45+
#[allow(non_snake_case)]
4346
const DROP_PTR: unsafe extern "system" fn(
4447
this: *mut IDropTarget,
4548
pDataObj: *const IDataObject,

src/win/hook.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
use std::{
2+
collections::HashSet,
3+
ffi::c_int,
4+
ptr,
5+
sync::{LazyLock, RwLock},
6+
};
7+
8+
use winapi::{
9+
shared::{
10+
minwindef::{LPARAM, WPARAM},
11+
windef::{HHOOK, HWND, POINT},
12+
},
13+
um::{
14+
libloaderapi::GetModuleHandleW,
15+
processthreadsapi::GetCurrentThreadId,
16+
winuser::{
17+
CallNextHookEx, SetWindowsHookExW, UnhookWindowsHookEx, HC_ACTION, MSG, PM_REMOVE,
18+
WH_GETMESSAGE, WM_CHAR, WM_KEYDOWN, WM_KEYUP, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP,
19+
WM_USER,
20+
},
21+
},
22+
};
23+
24+
use crate::win::wnd_proc;
25+
26+
// track all windows opened by this instance of baseview
27+
// we use an RwLock here since the vast majority of uses (event interceptions)
28+
// will only need to read from the HashSet
29+
static HOOK_STATE: LazyLock<RwLock<KeyboardHookState>> = LazyLock::new(|| RwLock::default());
30+
31+
pub(crate) struct KeyboardHookHandle(HWNDWrapper);
32+
33+
#[derive(Default)]
34+
struct KeyboardHookState {
35+
hook: Option<HHOOK>,
36+
open_windows: HashSet<HWNDWrapper>,
37+
}
38+
39+
#[derive(Hash, PartialEq, Eq, Clone, Copy)]
40+
struct HWNDWrapper(HWND);
41+
42+
// SAFETY: it's a pointer behind an RwLock. we'll live
43+
unsafe impl Send for KeyboardHookState {}
44+
unsafe impl Sync for KeyboardHookState {}
45+
46+
// SAFETY: we never access the underlying HWND ourselves, just use it as a HashSet entry
47+
unsafe impl Send for HWNDWrapper {}
48+
unsafe impl Sync for HWNDWrapper {}
49+
50+
impl Drop for KeyboardHookHandle {
51+
fn drop(&mut self) {
52+
deinit_keyboard_hook(self.0);
53+
}
54+
}
55+
56+
// initialize keyboard hook
57+
// some DAWs (particularly Ableton) intercept incoming keyboard messages,
58+
// but we're naughty so we intercept them right back
59+
pub(crate) fn init_keyboard_hook(hwnd: HWND) -> KeyboardHookHandle {
60+
let state = &mut *HOOK_STATE.write().unwrap();
61+
62+
// register hwnd to global window set
63+
state.open_windows.insert(HWNDWrapper(hwnd));
64+
65+
if state.hook.is_some() {
66+
// keyboard hook already exists, just return handle
67+
KeyboardHookHandle(HWNDWrapper(hwnd))
68+
} else {
69+
// keyboard hook doesn't exist (no windows open before this), create it
70+
let new_hook = unsafe {
71+
SetWindowsHookExW(
72+
WH_GETMESSAGE,
73+
Some(keyboard_hook_callback),
74+
GetModuleHandleW(ptr::null()),
75+
GetCurrentThreadId(),
76+
)
77+
};
78+
79+
state.hook = Some(new_hook);
80+
81+
KeyboardHookHandle(HWNDWrapper(hwnd))
82+
}
83+
}
84+
85+
fn deinit_keyboard_hook(hwnd: HWNDWrapper) {
86+
let state = &mut *HOOK_STATE.write().unwrap();
87+
88+
state.open_windows.remove(&hwnd);
89+
90+
if state.open_windows.is_empty() {
91+
if let Some(hhook) = state.hook {
92+
unsafe {
93+
UnhookWindowsHookEx(hhook);
94+
}
95+
96+
state.hook = None;
97+
}
98+
}
99+
}
100+
101+
unsafe extern "system" fn keyboard_hook_callback(
102+
n_code: c_int, wparam: WPARAM, lparam: LPARAM,
103+
) -> isize {
104+
let msg = lparam as *mut MSG;
105+
106+
if n_code == HC_ACTION && wparam == PM_REMOVE as usize && offer_message_to_baseview(msg) {
107+
*msg = MSG {
108+
hwnd: ptr::null_mut(),
109+
message: WM_USER,
110+
wParam: 0,
111+
lParam: 0,
112+
time: 0,
113+
pt: POINT { x: 0, y: 0 },
114+
};
115+
116+
0
117+
} else {
118+
CallNextHookEx(ptr::null_mut(), n_code, wparam, lparam)
119+
}
120+
}
121+
122+
// check if `msg` is a keyboard message addressed to a window
123+
// in KeyboardHookState::open_windows, and intercept it if so
124+
unsafe fn offer_message_to_baseview(msg: *mut MSG) -> bool {
125+
let msg = &*msg;
126+
127+
// if this isn't a keyboard message, ignore it
128+
match msg.message {
129+
WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP | WM_CHAR | WM_SYSCHAR => {}
130+
131+
_ => return false,
132+
}
133+
134+
// check if this is one of our windows. if so, intercept it
135+
if HOOK_STATE.read().unwrap().open_windows.contains(&HWNDWrapper(msg.hwnd)) {
136+
let _ = wnd_proc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
137+
138+
return true;
139+
}
140+
141+
false
142+
}

src/win/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod cursor;
22
mod drop_target;
3+
mod hook;
34
mod keyboard;
45
mod window;
56

src/win/window.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use raw_window_handle::{
3333

3434
const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1;
3535

36+
use crate::win::hook::{self, KeyboardHookHandle};
3637
use crate::{
3738
Event, MouseButton, MouseCursor, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent,
3839
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
@@ -118,7 +119,7 @@ impl Drop for ParentHandle {
118119
}
119120
}
120121

121-
unsafe extern "system" fn wnd_proc(
122+
pub(crate) unsafe extern "system" fn wnd_proc(
122123
hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM,
123124
) -> LRESULT {
124125
if msg == WM_CREATE {
@@ -521,6 +522,11 @@ pub(super) struct WindowState {
521522
scale_policy: WindowScalePolicy,
522523
dw_style: u32,
523524

525+
// handle to the win32 keyboard hook
526+
// we don't need to read from this, just carry it around so the Drop impl can run
527+
#[allow(dead_code)]
528+
kb_hook: KeyboardHookHandle,
529+
524530
/// Tasks that should be executed at the end of `wnd_proc`. This is needed to avoid mutably
525531
/// borrowing the fields from `WindowState` more than once. For instance, when the window
526532
/// handler requests a resize in response to a keyboard event, the window state will already be
@@ -533,19 +539,19 @@ pub(super) struct WindowState {
533539
}
534540

535541
impl WindowState {
536-
pub(super) fn create_window(&self) -> Window {
542+
pub(super) fn create_window(&self) -> Window<'_> {
537543
Window { state: self }
538544
}
539545

540-
pub(super) fn window_info(&self) -> Ref<WindowInfo> {
546+
pub(super) fn window_info(&self) -> Ref<'_, WindowInfo> {
541547
self.window_info.borrow()
542548
}
543549

544-
pub(super) fn keyboard_state(&self) -> Ref<KeyboardState> {
550+
pub(super) fn keyboard_state(&self) -> Ref<'_, KeyboardState> {
545551
self.keyboard_state.borrow()
546552
}
547553

548-
pub(super) fn handler_mut(&self) -> RefMut<Option<Box<dyn WindowHandler>>> {
554+
pub(super) fn handler_mut(&self) -> RefMut<'_, Option<Box<dyn WindowHandler>>> {
549555
self.handler.borrow_mut()
550556
}
551557

@@ -700,6 +706,8 @@ impl Window<'_> {
700706
);
701707
// todo: manage error ^
702708

709+
let kb_hook = hook::init_keyboard_hook(hwnd);
710+
703711
#[cfg(feature = "opengl")]
704712
let gl_context: Option<GlContext> = options.gl_config.map(|gl_config| {
705713
let mut handle = Win32WindowHandle::empty();
@@ -730,6 +738,8 @@ impl Window<'_> {
730738

731739
deferred_tasks: RefCell::new(VecDeque::with_capacity(4)),
732740

741+
kb_hook,
742+
733743
#[cfg(feature = "opengl")]
734744
gl_context,
735745
});

src/x11/event_loop.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,6 @@ impl EventLoop {
108108

109109
// Check if the parents's handle was dropped (such as when the host
110110
// requested the window to close)
111-
//
112-
// FIXME: This will need to be changed from just setting an atomic to somehow
113-
// synchronizing with the window being closed (using a synchronous channel, or
114-
// by joining on the event loop thread).
115111
if let Some(parent_handle) = &self.parent_handle {
116112
if parent_handle.parent_did_drop() {
117113
self.handle_must_close();

src/x11/keyboard.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -370,9 +370,9 @@ pub(super) fn key_mods(mods: KeyButMask) -> Modifiers {
370370
// X11's mod keys are configurable, but this seems
371371
// like a reasonable default for US keyboards, at least,
372372
// where the "windows" key seems to be MOD_MASK_4.
373-
(KeyButMask::BUTTON1, Modifiers::ALT),
374-
(KeyButMask::BUTTON2, Modifiers::NUM_LOCK),
375-
(KeyButMask::BUTTON4, Modifiers::META),
373+
(KeyButMask::MOD1, Modifiers::ALT),
374+
(KeyButMask::MOD2, Modifiers::NUM_LOCK),
375+
(KeyButMask::MOD4, Modifiers::META),
376376
(KeyButMask::LOCK, Modifiers::CAPS_LOCK),
377377
];
378378
for (mask, modifiers) in &key_masks {

src/x11/window.rs

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::ffi::c_void;
44
use std::sync::atomic::{AtomicBool, Ordering};
55
use std::sync::mpsc;
66
use std::sync::Arc;
7-
use std::thread;
7+
use std::thread::{self, JoinHandle};
88

99
use raw_window_handle::{
1010
HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, XlibDisplayHandle,
@@ -31,18 +31,16 @@ use crate::x11::visual_info::WindowVisualConfig;
3131

3232
pub struct WindowHandle {
3333
raw_window_handle: Option<RawWindowHandle>,
34+
event_loop_handle: Option<JoinHandle<()>>,
3435
close_requested: Arc<AtomicBool>,
3536
is_open: Arc<AtomicBool>,
3637
}
3738

3839
impl WindowHandle {
3940
pub fn close(&mut self) {
40-
if self.raw_window_handle.take().is_some() {
41-
// FIXME: This will need to be changed from just setting an atomic to somehow
42-
// synchronizing with the window being closed (using a synchronous channel, or
43-
// by joining on the event loop thread).
44-
45-
self.close_requested.store(true, Ordering::Relaxed);
41+
self.close_requested.store(true, Ordering::Relaxed);
42+
if let Some(event_loop) = self.event_loop_handle.take() {
43+
let _ = event_loop.join();
4644
}
4745
}
4846

@@ -72,9 +70,9 @@ impl ParentHandle {
7270
pub fn new() -> (Self, WindowHandle) {
7371
let close_requested = Arc::new(AtomicBool::new(false));
7472
let is_open = Arc::new(AtomicBool::new(true));
75-
7673
let handle = WindowHandle {
7774
raw_window_handle: None,
75+
event_loop_handle: None,
7876
close_requested: Arc::clone(&close_requested),
7977
is_open: Arc::clone(&is_open),
8078
};
@@ -94,16 +92,17 @@ impl Drop for ParentHandle {
9492
}
9593

9694
pub(crate) struct WindowInner {
95+
// GlContext should be dropped **before** XcbConnection is dropped
96+
#[cfg(feature = "opengl")]
97+
gl_context: Option<GlContext>,
98+
9799
pub(crate) xcb_connection: XcbConnection,
98100
window_id: XWindow,
99101
pub(crate) window_info: WindowInfo,
100102
visual_id: Visualid,
101103
mouse_cursor: Cell<MouseCursor>,
102104

103105
pub(crate) close_requested: Cell<bool>,
104-
105-
#[cfg(feature = "opengl")]
106-
gl_context: Option<GlContext>,
107106
}
108107

109108
pub struct Window<'a> {
@@ -133,17 +132,15 @@ impl<'a> Window<'a> {
133132
};
134133

135134
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
136-
137135
let (parent_handle, mut window_handle) = ParentHandle::new();
138-
139-
thread::spawn(move || {
136+
let join_handle = thread::spawn(move || {
140137
Self::window_thread(Some(parent_id), options, build, tx.clone(), Some(parent_handle))
141138
.unwrap();
142139
});
143140

144141
let raw_window_handle = rx.recv().unwrap().unwrap();
145142
window_handle.raw_window_handle = Some(raw_window_handle.0);
146-
143+
window_handle.event_loop_handle = Some(join_handle);
147144
window_handle
148145
}
149146

0 commit comments

Comments
 (0)