Skip to content

Commit

Permalink
win32: repurpose timer window into a more general message window
Browse files Browse the repository at this point in the history
  • Loading branch information
micahrj committed Oct 4, 2023
1 parent ef147bc commit 9ca2e10
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 125 deletions.
118 changes: 107 additions & 11 deletions src/backend/win32/app.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,102 @@
use std::any::Any;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;
use std::rc::{Rc, Weak};
use std::time::Duration;
use std::{mem, result};
use std::{mem, ptr, result};

use windows::core::PCWSTR;
use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM};
use windows::Win32::Graphics::Gdi::HBRUSH;
use windows::Win32::UI::WindowsAndMessaging::{
self as msg, DispatchMessageW, GetMessageW, PeekMessageW, PostQuitMessage, TranslateMessage,
MSG,
self as msg, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetMessageW,
GetWindowLongPtrW, PeekMessageW, PostQuitMessage, RegisterClassW, SetWindowLongPtrW,
TranslateMessage, UnregisterClassW, HCURSOR, HICON, HMENU, MSG, WINDOW_EX_STYLE, WINDOW_STYLE,
WNDCLASSW, WNDCLASS_STYLES,
};

use super::{class_name, hinstance, to_wstring};

use super::dpi::DpiFns;
use super::timer::{TimerHandleInner, Timers};
use super::window;
use crate::{App, AppContext, AppMode, AppOptions, Error, IntoInnerError, Result};

fn register_message_class() -> Result<PCWSTR> {
let class_name = to_wstring(&class_name("message-"));

let wnd_class = WNDCLASSW {
style: WNDCLASS_STYLES(0),
lpfnWndProc: Some(message_wnd_proc),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: hinstance(),
hIcon: HICON(0),
hCursor: HCURSOR(0),
hbrBackground: HBRUSH(0),
lpszMenuName: PCWSTR(ptr::null()),
lpszClassName: PCWSTR(class_name.as_ptr()),
};

let class = unsafe { RegisterClassW(&wnd_class) };
if class == 0 {
return Err(windows::core::Error::from_win32().into());
}

Ok(PCWSTR(class as *const u16))
}

unsafe fn unregister_message_class(class: PCWSTR) {
let _ = UnregisterClassW(class, hinstance());
}

pub unsafe extern "system" fn message_wnd_proc(
hwnd: HWND,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
let app_state_ptr = GetWindowLongPtrW(hwnd, msg::GWLP_USERDATA) as *mut AppState;
if !app_state_ptr.is_null() {
let app_state_weak = Weak::from_raw(app_state_ptr);
let app_state = app_state_weak.clone();
let _ = app_state_weak.into_raw();

match msg {
msg::WM_TIMER => {
if let Some(app_state) = app_state.upgrade() {
app_state.timers.handle_timer(&app_state, wparam.0);
}
}
msg::WM_DESTROY => {
drop(Weak::from_raw(app_state_ptr));
SetWindowLongPtrW(hwnd, msg::GWLP_USERDATA, 0);
}
_ => {}
}
}

DefWindowProcW(hwnd, msg, wparam, lparam)
}

pub struct AppState {
pub class: PCWSTR,
pub message_class: PCWSTR,
pub message_hwnd: HWND,
pub window_class: PCWSTR,
pub dpi: DpiFns,
pub timers: Timers,
pub data: RefCell<Option<Box<dyn Any>>>,
}

impl Drop for AppState {
fn drop(&mut self) {
self.timers.kill_timers(&self);

unsafe {
window::unregister_class(self.class);
window::unregister_class(self.window_class);

let _ = DestroyWindow(self.message_hwnd);
unregister_message_class(self.message_class);
}
}
}
Expand All @@ -43,23 +112,50 @@ impl<T: 'static> AppInner<T> {
F: FnOnce(&AppContext<T>) -> Result<T>,
T: 'static,
{
let class = window::register_class()?;
let message_class = register_message_class()?;

let message_hwnd = unsafe {
CreateWindowExW(
WINDOW_EX_STYLE(0),
message_class,
PCWSTR(ptr::null()),
WINDOW_STYLE(0),
msg::CW_USEDEFAULT,
msg::CW_USEDEFAULT,
0,
0,
HWND(0),
HMENU(0),
hinstance(),
None,
)
};
if message_hwnd == HWND(0) {
return Err(windows::core::Error::from_win32().into());
}

let window_class = window::register_class()?;

let dpi = DpiFns::load();
if options.mode == AppMode::Owner {
dpi.set_dpi_aware();
}

let timers = Timers::new()?;
let timers = Timers::new();

let state = Rc::new(AppState {
class,
message_class,
message_hwnd,
window_class,
dpi,
timers,
data: RefCell::new(None),
});

state.timers.set_app_state(&state);
let state_ptr = Weak::into_raw(Rc::downgrade(&state));
unsafe {
SetWindowLongPtrW(message_hwnd, msg::GWLP_USERDATA, state_ptr as isize);
}

let cx = AppContext::from_inner(AppContextInner {
state: &state,
Expand Down
131 changes: 18 additions & 113 deletions src/backend/win32/timer.rs
Original file line number Diff line number Diff line change
@@ -1,127 +1,28 @@
use std::any::Any;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::ptr;
use std::rc::{Rc, Weak};
use std::time::Duration;

use windows::core::PCWSTR;
use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM};
use windows::Win32::Graphics::Gdi::HBRUSH;
use windows::Win32::UI::WindowsAndMessaging::{
self as msg, CreateWindowExW, DefWindowProcW, DestroyWindow, GetWindowLongPtrW, KillTimer,
RegisterClassW, SetTimer, SetWindowLongPtrW, UnregisterClassW, HCURSOR, HICON, HMENU,
WINDOW_EX_STYLE, WINDOW_STYLE, WNDCLASSW, WNDCLASS_STYLES,
};
use windows::Win32::UI::WindowsAndMessaging::{KillTimer, SetTimer};

use super::app::{AppContextInner, AppState};
use super::{class_name, hinstance, to_wstring};
use crate::AppContext;
use crate::Result;

pub unsafe extern "system" fn wnd_proc(
hwnd: HWND,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
let app_state_ptr = GetWindowLongPtrW(hwnd, msg::GWLP_USERDATA) as *mut AppState;
if !app_state_ptr.is_null() {
let app_state_weak = Weak::from_raw(app_state_ptr);
let app_state = app_state_weak.clone();
let _ = app_state_weak.into_raw();

match msg {
msg::WM_TIMER => {
if let Some(app_state) = app_state.upgrade() {
let timer_state = app_state.timers.timers.borrow().get(&wparam.0).cloned();
if let Some(timer_state) = timer_state {
if let Ok(mut data) = app_state.data.try_borrow_mut() {
if let Some(data) = &mut *data {
timer_state.handler.borrow_mut()(&mut **data, &app_state);
}
}
}
}
}
msg::WM_DESTROY => {
drop(Weak::from_raw(app_state_ptr));
SetWindowLongPtrW(hwnd, msg::GWLP_USERDATA, 0);
}
_ => {}
}
}

DefWindowProcW(hwnd, msg, wparam, lparam)
}

struct TimerState {
handler: RefCell<Box<dyn FnMut(&mut dyn Any, &Rc<AppState>)>>,
}

pub struct Timers {
class: PCWSTR,
hwnd: HWND,
next_id: Cell<usize>,
timers: RefCell<HashMap<usize, Rc<TimerState>>>,
}

impl Timers {
pub fn new() -> Result<Timers> {
let class_name = to_wstring(&class_name("timers-"));

let wnd_class = WNDCLASSW {
style: WNDCLASS_STYLES(0),
lpfnWndProc: Some(wnd_proc),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: hinstance(),
hIcon: HICON(0),
hCursor: HCURSOR(0),
hbrBackground: HBRUSH(0),
lpszMenuName: PCWSTR(ptr::null()),
lpszClassName: PCWSTR(class_name.as_ptr()),
};

let class = unsafe { RegisterClassW(&wnd_class) };
if class == 0 {
return Err(windows::core::Error::from_win32().into());
}
let class = PCWSTR(class as *const u16);

let hwnd = unsafe {
CreateWindowExW(
WINDOW_EX_STYLE(0),
class,
PCWSTR(ptr::null()),
WINDOW_STYLE(0),
msg::CW_USEDEFAULT,
msg::CW_USEDEFAULT,
0,
0,
HWND(0),
HMENU(0),
hinstance(),
None,
)
};
if hwnd == HWND(0) {
return Err(windows::core::Error::from_win32().into());
}

Ok(Timers {
class,
hwnd,
pub fn new() -> Timers {
Timers {
next_id: Cell::new(0),
timers: RefCell::new(HashMap::new()),
})
}

// Should only be called once during setup. Calling this multiple times will result in leaks
pub fn set_app_state(&self, app_state: &Rc<AppState>) {
let state_ptr = Weak::into_raw(Rc::downgrade(app_state));
unsafe {
SetWindowLongPtrW(self.hwnd, msg::GWLP_USERDATA, state_ptr as isize);
}
}

Expand Down Expand Up @@ -155,27 +56,31 @@ impl Timers {

unsafe {
let millis = duration.as_millis() as u32;
SetTimer(self.hwnd, timer_id, millis, None);
SetTimer(app_state.message_hwnd, timer_id, millis, None);
}

TimerHandleInner {
app_state: Rc::downgrade(app_state),
timer_id,
}
}
}

impl Drop for Timers {
fn drop(&mut self) {
for (timer_id, _timer) in self.timers.take() {
unsafe {
let _ = KillTimer(self.hwnd, timer_id);
pub fn handle_timer(&self, app_state: &Rc<AppState>, timer_id: usize) {
let timer_state = app_state.timers.timers.borrow().get(&timer_id).cloned();
if let Some(timer_state) = timer_state {
if let Ok(mut data) = app_state.data.try_borrow_mut() {
if let Some(data) = &mut *data {
timer_state.handler.borrow_mut()(&mut **data, &app_state);
}
}
}
}

unsafe {
let _ = DestroyWindow(self.hwnd);
let _ = UnregisterClassW(self.class, hinstance());
pub fn kill_timers(&self, app_state: &AppState) {
for (timer_id, _timer) in self.timers.take() {
unsafe {
let _ = KillTimer(app_state.message_hwnd, timer_id);
}
}
}
}
Expand All @@ -190,7 +95,7 @@ impl TimerHandleInner {
if let Some(app_state) = self.app_state.upgrade() {
if let Some(_) = app_state.timers.timers.borrow_mut().remove(&self.timer_id) {
unsafe {
let _ = KillTimer(app_state.timers.hwnd, self.timer_id);
let _ = KillTimer(app_state.message_hwnd, self.timer_id);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/backend/win32/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ impl WindowInner {

let hwnd = CreateWindowExW(
WINDOW_EX_STYLE(0),
cx.inner.state.class,
cx.inner.state.window_class,
PCWSTR(window_name.as_ptr()),
style,
x,
Expand Down

0 comments on commit 9ca2e10

Please sign in to comment.