Skip to content

Commit 81a8a0b

Browse files
committed
wip
1 parent 3dcd681 commit 81a8a0b

3 files changed

Lines changed: 269 additions & 0 deletions

File tree

‎src/wrappers/win32.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
pub mod h_instance;
2+
pub mod h_window;
3+
pub mod h_wnd;
24
pub mod uuid;
35
pub mod window_class;

‎src/wrappers/win32/h_window.rs‎

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
use crate::wrappers::win32::h_wnd::HWnd;
2+
use std::cell::{Cell, OnceCell};
3+
use std::mem::ManuallyDrop;
4+
use std::panic::{catch_unwind, AssertUnwindSafe};
5+
use std::ptr::NonNull;
6+
use std::rc::Rc;
7+
use windows_sys::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM};
8+
use windows_sys::Win32::UI::WindowsAndMessaging::*;
9+
10+
pub trait Initializer<W>: 'static {
11+
// TODO: handle errors
12+
fn initialize(self: Box<Self>, window: &HWnd) -> W;
13+
}
14+
15+
impl<F: 'static, W> Initializer<W> for F
16+
where
17+
F: FnOnce(&HWnd) -> W,
18+
{
19+
#[inline]
20+
fn initialize(self: Box<Self>, ctx: &HWnd) -> W {
21+
self(ctx)
22+
}
23+
}
24+
25+
pub trait WindowImpl: 'static {
26+
fn handle_message(
27+
&self, window: &HWnd, message_code: u32, w_param: WPARAM, l_param: LPARAM,
28+
) -> Option<LRESULT>;
29+
fn destroy_started(&self, window: &HWnd);
30+
}
31+
32+
pub enum LifeCycleWindowMessage<'a> {
33+
Create { create: &'a CREATESTRUCTW },
34+
Destroy,
35+
Message(u32, WPARAM, LPARAM),
36+
}
37+
38+
impl<'a> LifeCycleWindowMessage<'a> {
39+
pub unsafe fn parse(message: u32, w_param: WPARAM, l_param: LPARAM) -> Option<Self> {
40+
Some(match message {
41+
WM_CREATE => Self::Create { create: unsafe { &*(l_param as *const CREATESTRUCTW) } },
42+
WM_DESTROY => Self::Destroy,
43+
_ => Self::Message(message, w_param, l_param),
44+
})
45+
}
46+
}
47+
48+
// TODO: bikeshed
49+
struct WindowUserData<W> {
50+
initializer: Cell<Option<Box<dyn Initializer<W>>>>,
51+
inner_impl: OnceCell<W>,
52+
}
53+
54+
impl<W: WindowImpl> WindowUserData<W> {
55+
pub fn new(initializer: impl Initializer<W>) -> Rc<Self> {
56+
Rc::new(Self {
57+
initializer: Cell::new(Some(Box::new(initializer))),
58+
inner_impl: OnceCell::new(),
59+
})
60+
}
61+
62+
pub unsafe fn consume_from_raw(raw: NonNull<WindowUserData<W>>) -> Rc<Self> {
63+
Rc::from_raw(raw.as_ptr())
64+
}
65+
66+
pub unsafe fn from_raw(raw: NonNull<WindowUserData<W>>) -> Rc<Self> {
67+
let this = ManuallyDrop::new(Rc::from_raw(raw.as_ptr()));
68+
Rc::clone(&this)
69+
}
70+
71+
pub fn initialize(&self, window: &HWnd) -> Result<(), ()> {
72+
let Some(initializer) = self.initializer.take() else {
73+
panic!("AdapterContainer is already initialized");
74+
};
75+
76+
// TODO: allow initializer to return error
77+
if self.inner_impl.set(initializer.initialize(window)).is_err() {
78+
// Should not be possible
79+
panic!("AdapterContainer is already initialized");
80+
}
81+
82+
Ok(())
83+
}
84+
85+
pub fn destroy_started(&self, window: &HWnd) {
86+
if let Some(inner) = self.inner_impl.get() {
87+
inner.destroy_started(window);
88+
}
89+
}
90+
91+
pub fn handle_message(
92+
&self, window: &HWnd, message_code: u32, w_param: WPARAM, l_param: LPARAM,
93+
) -> Option<LRESULT> {
94+
if let Some(inner) = self.inner_impl.get() {
95+
inner.handle_message(window, message_code, w_param, l_param)
96+
} else {
97+
None
98+
}
99+
}
100+
}
101+
102+
pub(crate) unsafe extern "system" fn wnd_proc<W: WindowImpl>(
103+
window: HWND, message_code: u32, w_param: WPARAM, l_param: LPARAM,
104+
) -> LRESULT {
105+
let handle_default = || unsafe { DefWindowProcW(window, message_code, w_param, l_param) };
106+
let window = unsafe { HWnd::from_ref(&window) };
107+
108+
let message = unsafe { LifeCycleWindowMessage::parse(message_code, w_param, l_param) };
109+
110+
// Default handling for all other events
111+
let Some(message) = message else {
112+
return handle_default();
113+
};
114+
115+
match message {
116+
LifeCycleWindowMessage::Message(message, w_param, l_param) => {
117+
let Some(inner_ptr) = window.get_userdata_ptr::<WindowUserData<W>>() else {
118+
// TODO: log error
119+
return handle_default();
120+
};
121+
122+
let inner = unsafe { WindowUserData::from_raw(inner_ptr) };
123+
124+
let result = catch_unwind(AssertUnwindSafe(|| {
125+
inner.handle_message(window, message, w_param, l_param)
126+
}));
127+
128+
let _ = catch_unwind(AssertUnwindSafe(|| drop(inner)));
129+
130+
match result {
131+
Ok(result) => result.unwrap_or_else(handle_default),
132+
Err(_) => {
133+
// TODO: log error
134+
unsafe { DestroyWindow(window.as_raw()) }; // TODO: check error
135+
-1
136+
}
137+
}
138+
}
139+
LifeCycleWindowMessage::Create { create } => {
140+
let inner_ptr = create.lpCreateParams as *mut WindowUserData<W>;
141+
142+
let Some(inner_ptr) = NonNull::new(inner_ptr) else {
143+
// If the state pointer was null for some weird reason, we just abort.
144+
// TODO: log error
145+
return -1;
146+
};
147+
148+
if let Err(_e) = window.set_userdata_ptr(inner_ptr.as_ptr()) {
149+
// The call to SetWindowLongPtrW failed for some reason, we cannot continue.
150+
151+
// Try to recover and free the received pointer data. But if this also fails, better to leak
152+
// it than risk crashing
153+
let _ = catch_unwind(AssertUnwindSafe(|| {
154+
drop(WindowUserData::<W>::consume_from_raw(inner_ptr))
155+
}));
156+
157+
// TODO: log error
158+
return -1;
159+
}
160+
161+
// Now the fun begins
162+
let result = catch_unwind(AssertUnwindSafe(|| {
163+
let inner = unsafe { inner_ptr.as_ref() };
164+
165+
inner.initialize(window)
166+
}));
167+
168+
match result {
169+
// If successful, all good.
170+
// Ownership of the inner state has been passed to the window via the userdata ptr.
171+
Ok(Ok(())) => 0,
172+
173+
// If initializer failed or errored, abort.
174+
Ok(Err(())) | Err(_) => {
175+
// First, revoke ownership from the window, we don't want it to be used by any subsequent messages.
176+
let _ = window.set_userdata_ptr(core::ptr::null::<W>());
177+
178+
// Try to recover and free the received pointer data. But if this also fails, better to leak
179+
// it than risk crashing
180+
let _ = catch_unwind(AssertUnwindSafe(|| {
181+
drop(WindowUserData::<W>::consume_from_raw(inner_ptr))
182+
}));
183+
184+
// TODO: log error
185+
-1
186+
}
187+
}
188+
}
189+
LifeCycleWindowMessage::Destroy => {
190+
let Some(state_ptr) = window.get_userdata_ptr::<WindowUserData<W>>() else {
191+
// TODO: log error
192+
return handle_default();
193+
};
194+
195+
let state = unsafe { WindowUserData::<W>::consume_from_raw(state_ptr) };
196+
let _ = catch_unwind(AssertUnwindSafe(|| state.destroy_started(window)));
197+
let _ = window.set_userdata_ptr(core::ptr::null::<W>());
198+
let _ = catch_unwind(AssertUnwindSafe(|| drop(state)));
199+
200+
0
201+
}
202+
}
203+
}

‎src/wrappers/win32/h_wnd.rs‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use std::ptr::NonNull;
2+
use windows_core::{Error, Result, HRESULT};
3+
use windows_sys::Win32::Foundation::{SetLastError, HWND};
4+
use windows_sys::Win32::UI::WindowsAndMessaging::{
5+
GetWindowLongPtrW, GetWindowLongW, SetWindowLongPtrW, ShowWindow, GWLP_USERDATA, SW_SHOWNORMAL,
6+
WINDOW_LONG_PTR_INDEX,
7+
};
8+
9+
#[repr(transparent)]
10+
pub struct HWnd(HWND);
11+
12+
impl HWnd {
13+
pub unsafe fn from_ref(hwnd: &HWND) -> &Self {
14+
// SAFETY: HWnd is repr(transparent)
15+
unsafe { &*(hwnd as *const HWND as *const HWnd) }
16+
}
17+
18+
pub fn as_raw(&self) -> HWND {
19+
self.0
20+
}
21+
22+
pub fn get_long(&self, index: WINDOW_LONG_PTR_INDEX) -> Result<i32> {
23+
// SAFETY: This function is always safe to call
24+
unsafe { SetLastError(0) };
25+
// SAFETY: This type guarantees the HWND is still valid.
26+
let result = unsafe { GetWindowLongW(self.0, index) };
27+
if result != 0 {
28+
return Ok(result);
29+
}
30+
31+
// We can't know if a return value of 0 is indicative of an error, or if it's just because the
32+
// value was actually 0. So we check GetLastError instead (called by Error::from_win32).
33+
let error = Error::from_win32();
34+
if error.code() == HRESULT(0) {
35+
return Ok(result);
36+
}
37+
38+
Err(error)
39+
}
40+
41+
pub fn get_userdata_ptr<T>(&self) -> Option<NonNull<T>> {
42+
let ptr = unsafe { GetWindowLongPtrW(self.0, GWLP_USERDATA) };
43+
NonNull::new(ptr as *mut T)
44+
}
45+
46+
pub fn set_userdata_ptr<T>(&self, data: *const T) -> Result<()> {
47+
// SAFETY: This function is always safe to call
48+
unsafe { SetLastError(0) };
49+
// SAFETY: This type guarantees the HWND is still valid.
50+
let previous = unsafe { SetWindowLongPtrW(self.0, GWLP_USERDATA, data as isize) };
51+
if previous != 0 {
52+
return Ok(());
53+
}
54+
55+
// We can't know if a return value of 0 is indicative of an error, or if it's just because the
56+
// previous value was 0. So we check GetLastError instead (called by Error::from_win32).
57+
let error = Error::from_win32();
58+
if error.code() == HRESULT(0) {
59+
return Ok(());
60+
}
61+
62+
Err(error)
63+
}
64+
}

0 commit comments

Comments
 (0)