use accesskit::{ActionHandler, ActivationHandler, TreeUpdate};
use std::{
cell::{Cell, RefCell},
ffi::c_void,
mem::transmute,
};
use windows::{
core::*,
Win32::{Foundation::*, UI::WindowsAndMessaging::*},
};
use crate::{Adapter, QueuedEvents};
fn win32_error() -> ! {
panic!("{}", Error::from_win32())
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
type LongPtr = isize;
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
type LongPtr = i32;
const PROP_NAME: PCWSTR = w!("AccessKitAdapter");
struct SubclassState {
adapter: Adapter,
activation_handler: Box<dyn ActivationHandler>,
}
struct SubclassImpl {
hwnd: HWND,
state: RefCell<SubclassState>,
prev_wnd_proc: WNDPROC,
window_destroyed: Cell<bool>,
}
extern "system" fn wnd_proc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
let handle = unsafe { GetPropW(window, PROP_NAME) };
let impl_ptr = handle.0 as *const SubclassImpl;
assert!(!impl_ptr.is_null());
let r#impl = unsafe { &*impl_ptr };
match message {
WM_GETOBJECT => {
let mut state = r#impl.state.borrow_mut();
let state_mut = &mut *state;
if let Some(result) = state_mut.adapter.handle_wm_getobject(
wparam,
lparam,
&mut *state_mut.activation_handler,
) {
drop(state);
return result.into();
}
}
WM_SETFOCUS | WM_EXITMENULOOP | WM_EXITSIZEMOVE => {
r#impl.update_window_focus_state(true);
}
WM_KILLFOCUS | WM_ENTERMENULOOP | WM_ENTERSIZEMOVE => {
r#impl.update_window_focus_state(false);
}
WM_NCDESTROY => {
r#impl.window_destroyed.set(true);
}
_ => (),
}
unsafe { CallWindowProcW(r#impl.prev_wnd_proc, window, message, wparam, lparam) }
}
impl SubclassImpl {
fn new(
hwnd: HWND,
activation_handler: impl 'static + ActivationHandler,
action_handler: impl 'static + ActionHandler + Send,
) -> Box<Self> {
let adapter = Adapter::new(hwnd, false, action_handler);
let state = RefCell::new(SubclassState {
adapter,
activation_handler: Box::new(activation_handler),
});
Box::new(Self {
hwnd,
state,
prev_wnd_proc: None,
window_destroyed: Cell::new(false),
})
}
fn install(&mut self) {
unsafe {
SetPropW(
self.hwnd,
PROP_NAME,
HANDLE(self as *const SubclassImpl as _),
)
}
.unwrap();
let result =
unsafe { SetWindowLongPtrW(self.hwnd, GWLP_WNDPROC, wnd_proc as *const c_void as _) };
if result == 0 {
win32_error();
}
self.prev_wnd_proc = unsafe { transmute::<LongPtr, WNDPROC>(result) };
}
fn update_window_focus_state(&self, is_focused: bool) {
let mut state = self.state.borrow_mut();
if let Some(events) = state.adapter.update_window_focus_state(is_focused) {
drop(state);
events.raise();
}
}
fn uninstall(&self) {
if self.window_destroyed.get() {
return;
}
let result = unsafe {
SetWindowLongPtrW(
self.hwnd,
GWLP_WNDPROC,
transmute::<WNDPROC, LongPtr>(self.prev_wnd_proc),
)
};
if result == 0 {
win32_error();
}
unsafe { RemovePropW(self.hwnd, PROP_NAME) }.unwrap();
}
}
pub struct SubclassingAdapter(Box<SubclassImpl>);
impl SubclassingAdapter {
pub fn new(
hwnd: HWND,
activation_handler: impl 'static + ActivationHandler,
action_handler: impl 'static + ActionHandler + Send,
) -> Self {
let mut r#impl = SubclassImpl::new(hwnd, activation_handler, action_handler);
r#impl.install();
Self(r#impl)
}
pub fn update_if_active(
&mut self,
update_factory: impl FnOnce() -> TreeUpdate,
) -> Option<QueuedEvents> {
let mut state = self.0.state.borrow_mut();
state.adapter.update_if_active(update_factory)
}
}
impl Drop for SubclassingAdapter {
fn drop(&mut self) {
self.0.uninstall();
}
}