1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use windows::{
    core::*,
    Win32::{Foundation::*, System::LibraryLoader::*, UI::WindowsAndMessaging::*},
};

pub trait Window {
    fn new(hwnd: HWND) -> Result<Self>
    where
        Self: Sized;
    fn on_paint(&mut self);
    fn on_size(&mut self, size: (u32, u32));
}

pub fn run<T: Window>(width: u32, height: u32, title: &str) -> Result<()> {
    unsafe {
        RegisterClassExW(&WNDCLASSEXW {
            cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
            style: CS_HREDRAW | CS_VREDRAW,
            lpfnWndProc: Some(wndproc::<T>),
            hInstance: GetModuleHandleW(None)?.into(),
            hCursor: LoadCursorW(None, IDC_ARROW).unwrap(),
            lpszClassName: w!("ClassName"),
            ..Default::default()
        });

        let mut window_rect = RECT {
            left: 0,
            top: 0,
            right: width as i32,
            bottom: height as i32,
        };
        AdjustWindowRect(&mut window_rect, WS_OVERLAPPEDWINDOW, false)?;

        let title = HSTRING::from(title);
        let hwnd = CreateWindowExW(
            WINDOW_EX_STYLE(0),
            w!("ClassName"),
            &title,
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            window_rect.right - window_rect.left,
            window_rect.bottom - window_rect.top,
            None,
            None,
            GetModuleHandleW(None)?,
            None,
        );

        let mut window = T::new(hwnd)?;

        SetWindowLongPtrW(hwnd, GWLP_USERDATA, &mut window as *mut T as isize);

        let _ = ShowWindow(hwnd, SW_SHOW);

        let mut msg = MSG::default();
        loop {
            match GetMessageW(&mut msg, None, 0, 0).0 {
                -1 => return Err(Error::from_win32()),
                0 => break,
                _ => {
                    DispatchMessageW(&msg);
                }
            }
        }
    }

    Ok(())
}

extern "system" fn wndproc<T: Window>(
    hwnd: HWND,
    msg: u32,
    wparam: WPARAM,
    lparam: LPARAM,
) -> LRESULT {
    unsafe {
        if msg == WM_DESTROY {
            PostQuitMessage(0);
            return LRESULT(0);
        }

        if let Some(mut ptr) =
            std::ptr::NonNull::<T>::new(GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut T)
        {
            match msg {
                WM_PAINT => {
                    ptr.as_mut().on_paint();
                    return LRESULT(0);
                }
                WM_SIZE => {
                    ptr.as_mut().on_size((
                        (lparam.0 & 0xFFFF) as u32,
                        ((lparam.0 >> 16) & 0xFFFF) as u32,
                    ));
                    return LRESULT(0);
                }
                _ => {}
            }
        }

        return DefWindowProcW(hwnd, msg, wparam, lparam);
    }
}