simple_window/
lib.rs

1//! A simple windowing library.
2mod utility;
3
4use std::{ffi::{c_uint, c_void}, num::NonZeroU32, os::raw::c_int, ptr::NonNull};
5
6use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
7
8#[cfg(target_os = "linux")]
9use raw_window_handle::{XcbDisplayHandle, XcbWindowHandle};
10
11#[cfg(target_os = "linux")]
12use xcb::{x, Xid};
13
14#[cfg(target_os = "windows")]
15use raw_window_handle::{Win32WindowHandle, WindowsDisplayHandle};
16
17#[cfg(target_os = "windows")]
18use std::{mem::MaybeUninit, num::NonZeroIsize, ptr};
19
20#[cfg(target_os = "windows")]
21use windows_sys::Win32::{
22    Foundation::{HWND, HINSTANCE, LPARAM, LRESULT, RECT, WPARAM},
23    System::LibraryLoader::GetModuleHandleA,
24    UI::WindowsAndMessaging::{
25        AdjustWindowRectEx, LoadCursorW, LoadIconW, MessageBoxA, ShowWindow, CreateWindowExW, DestroyWindow, 
26        DefWindowProcW, PeekMessageW, TranslateMessage, DispatchMessageW, GetClientRect,
27        RegisterClassW, WNDCLASSW, MSG,
28        CS_DBLCLKS, IDC_ARROW, IDI_APPLICATION, MB_ICONEXCLAMATION, MB_OK, SW_SHOW, SW_SHOWNOACTIVATE, 
29        WS_CAPTION, WS_EX_APPWINDOW, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_OVERLAPPED, WS_SYSMENU, WS_THICKFRAME,
30        WM_DESTROY, PM_REMOVE, WM_CLOSE, WM_ERASEBKGND, WM_EXITSIZEMOVE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN,
31        WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_RBUTTONDOWN, WM_RBUTTONUP,
32        WM_SYSKEYDOWN, WM_SYSKEYUP, WM_USER
33    },
34};
35
36pub enum WindowEvent {
37    Close,
38    Resize(u32, u32),
39    Input(WindowInputEvent),
40}
41
42pub enum WindowInputEvent {
43    KeyDown(Keys),
44    KeyUp(Keys),
45    MouseDown(MouseButton),
46    MouseUp(MouseButton),
47    MouseMove(i16, i16),
48    MouseWheelMove(i16),
49}
50
51/// A cross-platform window wrapper.
52/// 
53/// # Examples
54/// ```
55/// use simple_window::{Window, WindowEvent, WindowInputEvent};
56/// 
57/// fn main() {
58///     let mut is_running = true;
59/// 
60///     let mut window = Window::new("Example Window", 200, 200, 400, 600);
61/// 
62///     while is_running {
63///         window.poll_messages(|event| {
64///             match event {
65///                 WindowEvent::Close => is_running = false,
66///                 WindowEvent::Resize(width, height) => println!("Window resized: {}, {}", width, height),
67///                 WindowEvent::Input(event) => match event {
68///                     WindowInputEvent::MouseMove(x, y) => println!("Mouse moved!: {}, {}", x, y),
69///                     WindowInputEvent::KeyDown(key) => println!("Key pressed: {}", key.as_str()),
70///                     WindowInputEvent::KeyUp(key) => println!("Key released: {}", key.as_str()),
71///                     WindowInputEvent::MouseWheelMove(dz) => println!("Mouse wheel {}", if dz > 0 { "up" } else { "down" }),
72///                     WindowInputEvent::MouseDown(button) => println!("Mouse {} down.", button.as_str()),
73///                     WindowInputEvent::MouseUp(button) => println!("Mouse {} up.", button.as_str()),
74///                 },
75///             }
76///         });
77///     }
78/// }
79/// ```
80pub struct Window {
81    previous_size: (u32, u32),
82
83    #[cfg(target_os = "windows")]
84    h_instance: HINSTANCE,
85    #[cfg(target_os = "windows")]
86    hwnd: HWND,
87    
88    #[cfg(target_os = "linux")]
89    connection: xcb::Connection,
90    #[cfg(target_os = "linux")]
91    window: u32,
92    #[cfg(target_os = "linux")]
93    screen: c_int,
94    #[cfg(target_os = "linux")]
95    wm_del_window: x::Atom,
96}
97
98#[cfg(target_os = "windows")]
99const CUSTOM_CLOSE_MESSAGE: u32 = WM_USER + 0;
100#[cfg(target_os = "windows")]
101const CUSTOM_SIZE_MESSAGE: u32 = WM_USER + 1;
102
103#[cfg(target_os = "windows")]
104extern "system" fn win32_process_message(hwnd: HWND, msg: u32, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
105    use windows_sys::Win32::{Foundation::GetLastError, UI::WindowsAndMessaging::{PostMessageW, PostQuitMessage}};
106
107    match msg {
108        WM_ERASEBKGND => 1,
109        WM_CLOSE => {
110            unsafe { PostMessageW(hwnd, CUSTOM_CLOSE_MESSAGE, 0, 0); }
111            0
112        }
113        WM_DESTROY => {
114            unsafe { PostQuitMessage(0); }
115            0
116        },
117        WM_EXITSIZEMOVE => {
118            // println!("A");
119            if unsafe { PostMessageW(hwnd, CUSTOM_SIZE_MESSAGE, 0, 0) } == 0 {
120                println!("Failed to post. {}", unsafe { GetLastError() });
121            }
122
123            unsafe { DefWindowProcW(hwnd, msg, w_param, l_param) }
124        },
125        _ => unsafe { DefWindowProcW(hwnd, msg, w_param, l_param) },
126    }
127}
128
129impl Window {
130    /// Creates a new window at position (`x`, `y`), and name `window_name`.
131    pub fn new(
132        window_name: &str,
133        x: i32, y: i32,
134        width: i32, height: i32,
135    ) -> Self {
136        #[cfg(target_os = "windows")]
137        { Self::new_win32(window_name, x, y, width, height) }
138
139        #[cfg(target_os = "linux")]
140        { Self::new_linux_x(window_name, x, y, width, height) }
141    }
142
143    /// Polls and parses system messages directed at the window and passes them on to the `event_closure` closure.
144    pub fn poll_messages(&mut self, event_closure: impl FnMut(WindowEvent)) {
145        #[cfg(target_os = "windows")]
146        { self.poll_messages_win32(event_closure); }
147
148        #[cfg(target_os = "linux")]
149        { self.poll_messages_linux_x(event_closure); }
150    }
151
152    pub fn raw_window_handle(&self) -> RawWindowHandle {
153        #[cfg(target_os = "windows")]
154        { self.raw_window_handle_win32() }
155
156        #[cfg(target_os = "linux")]
157        { self.raw_window_handle_linux_x() }
158    }
159
160    pub fn raw_display_handle(&self) -> RawDisplayHandle {
161        #[cfg(target_os = "windows")]
162        { self.raw_display_handle_windows() }
163
164        #[cfg(target_os = "linux")]
165        { self.raw_display_handle_linux_x() }
166    }
167}
168
169#[cfg(target_os = "linux")]
170impl Window {
171    fn new_linux_x(
172        window_name: &str,
173        x: i32, y: i32,
174        width: i32, height: i32,
175    ) -> Self {
176        let (conn, screen_num) = xcb::Connection::connect_with_xlib_display().unwrap();
177
178        let setup = conn.get_setup();
179        let screen = setup.roots().nth(screen_num as usize).unwrap();
180
181        let window: x::Window = conn.generate_id();
182
183        let cookie = conn.send_request_checked(&x::CreateWindow {
184            depth: x::COPY_FROM_PARENT as u8,
185            wid: window,
186            parent: screen.root(),
187            x: x.try_into().unwrap(),
188            y: y.try_into().unwrap(),
189            width: width.try_into().unwrap(),
190            height: height.try_into().unwrap(),
191            border_width: 0,
192            class: x::WindowClass::InputOutput,
193            visual: screen.root_visual(),
194            value_list: &[
195                x::Cw::BackPixel(screen.white_pixel()),
196                x::Cw::EventMask(x::EventMask::BUTTON_PRESS | x::EventMask::BUTTON_RELEASE | x::EventMask::KEY_PRESS
197                    | x::EventMask::KEY_RELEASE | x::EventMask::EXPOSURE | x::EventMask::POINTER_MOTION
198                    | x::EventMask::STRUCTURE_NOTIFY
199                ),
200            ],
201        });
202        conn.check_request(cookie).unwrap();
203
204        let cookie = conn.send_request_checked(&x::ChangeProperty {
205            mode: x::PropMode::Replace,
206            window,
207            property: x::ATOM_WM_NAME,
208            r#type: x::ATOM_STRING,
209            data: window_name.as_bytes(),
210        });
211        conn.check_request(cookie).unwrap();
212
213        conn.send_request(&x::MapWindow {
214            window,
215        });
216
217        // Get atoms.
218        let (wm_protocols, wm_del_window) = {
219            let cookies = (
220                conn.send_request(&x::InternAtom {
221                    only_if_exists: true,
222                    name: b"WM_PROTOCOLS",
223                }),
224                conn.send_request(&x::InternAtom {
225                    only_if_exists: true,
226                    name: b"WM_DELETE_WINDOW",
227                }),
228            );
229
230            (
231                conn.wait_for_reply(cookies.0).unwrap().atom(),
232                conn.wait_for_reply(cookies.1).unwrap().atom(),
233            )
234        };
235
236        conn.check_request(conn.send_request_checked(&x::ChangeProperty {
237            mode: x::PropMode::Replace,
238            window,
239            property: wm_protocols,
240            r#type: x::ATOM_ATOM,
241            data: &[wm_del_window],
242        })).unwrap();
243
244        conn.flush().unwrap();
245
246        Self {
247            previous_size: (0, 0),
248            connection: conn,
249            screen: screen_num,
250            window: window.resource_id(),
251            wm_del_window,
252        }
253    }
254
255    fn poll_messages_linux_x(&mut self, mut event_closure: impl FnMut(WindowEvent)) {
256        while let Some(event) = self.connection.poll_for_event().unwrap() {
257            if let xcb::Event::X(event) = event { match event {
258                    x::Event::KeyPress(event) => {
259                        if let Some(key) = self.translate_key_code(event.detail()) {
260                            (event_closure)(WindowEvent::Input(WindowInputEvent::KeyDown(key)));
261                        }
262                    },
263                    x::Event::KeyRelease(event) => {
264                        if let Some(key) = self.translate_key_code(event.detail()) {
265                            (event_closure)(WindowEvent::Input(WindowInputEvent::KeyUp(key)));
266                        }
267                    },
268                    x::Event::ButtonPress(event) => {
269                        let button = match event.detail() as c_uint {
270                            x11::xlib::Button1 => MouseButton::Left,
271                            x11::xlib::Button2 => MouseButton::Middle,
272                            x11::xlib::Button3 => MouseButton::Right,
273                            x11::xlib::Button4 => continue,
274                            x11::xlib::Button5 => continue,
275                            _ => { log::warn!("Unrecognized mouse button pressed: {}.", event.detail()); continue; },
276                        };
277
278                        (event_closure)(WindowEvent::Input(WindowInputEvent::MouseDown(button)));
279                    },
280                    x::Event::ButtonRelease(event) => {
281                        match event.detail() as c_uint{
282                            event @ (x11::xlib::Button1 | x11::xlib::Button2 | x11::xlib::Button3) => {
283                                let button = match event {
284                                    x11::xlib::Button1 => MouseButton::Left,
285                                    x11::xlib::Button2 => MouseButton::Middle,
286                                    x11::xlib::Button3 => MouseButton::Right,
287                                    _ => panic!("Unrecognized mouse button x keycode.")
288                                };
289
290                                (event_closure)(WindowEvent::Input(WindowInputEvent::MouseUp(button)));
291                            },
292                            event @ (x11::xlib::Button4 | x11::xlib::Button5) => {
293                                let d = match event {
294                                    x11::xlib::Button4 => 1i16,
295                                    x11::xlib::Button5 => -1i16,
296                                    _ => panic!("Unrecognized mouse button x keycode.")
297                                };
298
299                                (event_closure)(WindowEvent::Input(WindowInputEvent::MouseWheelMove(d)));
300                            },
301                            _ => { log::warn!("Unrecognized mouse button released: {}.", event.detail()); continue; },
302                        };
303                    },
304                    x::Event::MotionNotify(event) => {
305                        let x = event.event_x();
306                        let y = event.event_x();
307                        
308                        (event_closure)(WindowEvent::Input(WindowInputEvent::MouseMove(x, y)));
309                    },
310                    x::Event::ConfigureNotify(event) => {
311                        // Window resize. Also triggered by window move.
312
313                        let x = event.width() as u32;
314                        let y = event.height() as u32;
315
316                        if self.previous_size != (x, y) {
317                            self.previous_size = (x, y);
318
319                            (event_closure)(WindowEvent::Resize(x, y));
320                        }
321                    },
322                    x::Event::ClientMessage(event) => {
323                        if let x::ClientMessageData::Data32([atom, ..]) = event.data() {
324                            if atom == self.wm_del_window.resource_id() {
325                                (event_closure)(WindowEvent::Close);
326                            }
327                        }
328                    },
329                    _ => {},
330                }
331            }
332        }
333    }
334
335    fn raw_window_handle_linux_x(&self) -> RawWindowHandle {
336        let handle = XcbWindowHandle::new(NonZeroU32::new(self.window).unwrap());
337
338        RawWindowHandle::Xcb(handle)
339    }
340
341    fn raw_display_handle_linux_x(&self) -> RawDisplayHandle {
342        let handle = XcbDisplayHandle::new(
343            Some(NonNull::new(self.connection.get_raw_conn() as *mut c_void).unwrap()), self.screen
344        );
345
346        RawDisplayHandle::Xcb(handle)
347    }
348
349    fn translate_key_code(&self, x_keycode: x::Keycode) -> Option<Keys> {
350
351        let key_sym = unsafe {
352            x11::xlib::XkbKeycodeToKeysym(
353                self.connection.get_raw_dpy(),
354                x_keycode as x11::xlib::KeyCode,
355                0,
356                if x_keycode as u32 & x11::xlib::ShiftMask == 0 { 1 } else { 0 }
357            )
358        };
359
360        match key_sym as c_uint {
361            x11::keysym::XK_BackSpace => Some(Keys::Backspace),
362            x11::keysym::XK_Return => Some(Keys::Enter),
363            x11::keysym::XK_Tab => Some(Keys::Tab),
364                //x11::keysym::XK_Shift: return keys::SHIFT),
365                //x11::keysym::XK_Control: return keys::CONTROL),
366
367            x11::keysym::XK_Pause => Some(Keys::Pause),
368            x11::keysym::XK_Caps_Lock => Some(Keys::Capital),
369
370            x11::keysym::XK_Escape => Some(Keys::Escape),
371
372                // Not supported
373                // case : return keys::CONVERT),
374                // case : return keys::NONCONVERT),
375                // case : return keys::ACCEPT),
376
377            x11::keysym::XK_Mode_switch => Some(Keys::Modechange),
378
379            x11::keysym::XK_space => Some(Keys::Space),
380            x11::keysym::XK_Prior => Some(Keys::Prior),
381            x11::keysym::XK_Next => Some(Keys::Next),
382            x11::keysym::XK_End => Some(Keys::End),
383            x11::keysym::XK_Home => Some(Keys::Home),
384            x11::keysym::XK_Left => Some(Keys::Left),
385            x11::keysym::XK_Up => Some(Keys::Up),
386            x11::keysym::XK_Right => Some(Keys::Right),
387            x11::keysym::XK_Down => Some(Keys::Down),
388            x11::keysym::XK_Select => Some(Keys::Select),
389            x11::keysym::XK_Print => Some(Keys::Print),
390            x11::keysym::XK_Execute => Some(Keys::Execute),
391            // x11::keysym::XK_snapshot: return keys::SNAPSHOT), // not supported
392            x11::keysym::XK_Insert => Some(Keys::Insert),
393            x11::keysym::XK_Delete => Some(Keys::Delete),
394            x11::keysym::XK_Help => Some(Keys::Help),
395
396            x11::keysym::XK_Super_L => Some(Keys::LWin),
397            x11::keysym::XK_Super_R => Some(Keys::RWin),
398                // x11::keysym::XK_apps: return keys::APPS), // not supported
399
400                // x11::keysym::XK_sleep: return keys::SLEEP), //not supported
401
402            x11::keysym::XK_KP_0 => Some(Keys::Numpad0),
403            x11::keysym::XK_KP_1 => Some(Keys::Numpad1),
404            x11::keysym::XK_KP_2 => Some(Keys::Numpad2),
405            x11::keysym::XK_KP_3 => Some(Keys::Numpad3),
406            x11::keysym::XK_KP_4 => Some(Keys::Numpad4),
407            x11::keysym::XK_KP_5 => Some(Keys::Numpad5),
408            x11::keysym::XK_KP_6 => Some(Keys::Numpad6),
409            x11::keysym::XK_KP_7 => Some(Keys::Numpad7),
410            x11::keysym::XK_KP_8 => Some(Keys::Numpad8),
411            x11::keysym::XK_KP_9 => Some(Keys::Numpad9),
412            x11::keysym::XK_multiply => Some(Keys::Multiply),
413            x11::keysym::XK_KP_Add => Some(Keys::Add),
414            x11::keysym::XK_KP_Separator => Some(Keys::Separator),
415            x11::keysym::XK_KP_Subtract => Some(Keys::Subtract),
416            x11::keysym::XK_KP_Decimal => Some(Keys::Decimal),
417            x11::keysym::XK_KP_Divide => Some(Keys::Divide),
418            x11::keysym::XK_F1 => Some(Keys::F1),
419            x11::keysym::XK_F2 => Some(Keys::F2),
420            x11::keysym::XK_F3 => Some(Keys::F3),
421            x11::keysym::XK_F4 => Some(Keys::F4),
422            x11::keysym::XK_F5 => Some(Keys::F5),
423            x11::keysym::XK_F6 => Some(Keys::F6),
424            x11::keysym::XK_F7 => Some(Keys::F7),
425            x11::keysym::XK_F8 => Some(Keys::F8),
426            x11::keysym::XK_F9 => Some(Keys::F9),
427            x11::keysym::XK_F10 => Some(Keys::F10),
428            x11::keysym::XK_F11 => Some(Keys::F11),
429            x11::keysym::XK_F12 => Some(Keys::F12),
430            x11::keysym::XK_F13 => Some(Keys::F13),
431            x11::keysym::XK_F14 => Some(Keys::F14),
432            x11::keysym::XK_F15 => Some(Keys::F15),
433            x11::keysym::XK_F16 => Some(Keys::F16),
434            x11::keysym::XK_F17 => Some(Keys::F17),
435            x11::keysym::XK_F18 => Some(Keys::F18),
436            x11::keysym::XK_F19 => Some(Keys::F19),
437            x11::keysym::XK_F20 => Some(Keys::F20),
438            x11::keysym::XK_F21 => Some(Keys::F21),
439            x11::keysym::XK_F22 => Some(Keys::F22),
440            x11::keysym::XK_F23 => Some(Keys::F23),
441            x11::keysym::XK_F24 => Some(Keys::F24),
442
443            x11::keysym::XK_Num_Lock => Some(Keys::Numlock),
444            x11::keysym::XK_Scroll_Lock => Some(Keys::Scroll),
445
446            x11::keysym::XK_KP_Equal => Some(Keys::NumpadEqual),
447
448            x11::keysym::XK_Shift_L => Some(Keys::LShift),
449            x11::keysym::XK_Shift_R => Some(Keys::RShift),
450            x11::keysym::XK_Control_L => Some(Keys::LControl),
451            x11::keysym::XK_Control_R => Some(Keys::RControl),
452            // x11::keysym::XK_Menu: return keys::LMENU),
453            x11::keysym::XK_Menu => Some(Keys::RMenu),
454
455            x11::keysym::XK_semicolon => Some(Keys::Semicolon),
456            x11::keysym::XK_plus => Some(Keys::Plus),
457            x11::keysym::XK_comma => Some(Keys::Comma),
458            x11::keysym::XK_minus => Some(Keys::Minus),
459            x11::keysym::XK_period => Some(Keys::Period),
460            x11::keysym::XK_slash => Some(Keys::Slash),
461            x11::keysym::XK_grave => Some(Keys::Grave),
462
463            x11::keysym::XK_a | x11::keysym::XK_A => Some(Keys::A),
464            x11::keysym::XK_b | x11::keysym::XK_B => Some(Keys::B),
465            x11::keysym::XK_c | x11::keysym::XK_C => Some(Keys::C),
466            x11::keysym::XK_d | x11::keysym::XK_D => Some(Keys::D),
467            x11::keysym::XK_e | x11::keysym::XK_E => Some(Keys::E),
468            x11::keysym::XK_f | x11::keysym::XK_F => Some(Keys::F),
469            x11::keysym::XK_g | x11::keysym::XK_G => Some(Keys::G),
470            x11::keysym::XK_h | x11::keysym::XK_H => Some(Keys::H),
471            x11::keysym::XK_i | x11::keysym::XK_I => Some(Keys::I),
472            x11::keysym::XK_j | x11::keysym::XK_J => Some(Keys::J),
473            x11::keysym::XK_k | x11::keysym::XK_K => Some(Keys::K),
474            x11::keysym::XK_l | x11::keysym::XK_L => Some(Keys::L),
475            x11::keysym::XK_m | x11::keysym::XK_M => Some(Keys::M),
476            x11::keysym::XK_n | x11::keysym::XK_N => Some(Keys::N),
477            x11::keysym::XK_o | x11::keysym::XK_O => Some(Keys::O),
478            x11::keysym::XK_p | x11::keysym::XK_P => Some(Keys::P),
479            x11::keysym::XK_q | x11::keysym::XK_Q => Some(Keys::Q),
480            x11::keysym::XK_r | x11::keysym::XK_R => Some(Keys::R),
481            x11::keysym::XK_s | x11::keysym::XK_S => Some(Keys::S),
482            x11::keysym::XK_t | x11::keysym::XK_T => Some(Keys::T),
483            x11::keysym::XK_u | x11::keysym::XK_U => Some(Keys::U),
484            x11::keysym::XK_v | x11::keysym::XK_V => Some(Keys::V),
485            x11::keysym::XK_w | x11::keysym::XK_W => Some(Keys::W),
486            x11::keysym::XK_x | x11::keysym::XK_X => Some(Keys::X),
487            x11::keysym::XK_y | x11::keysym::XK_Y => Some(Keys::Y),
488            x11::keysym::XK_z | x11::keysym::XK_Z => Some(Keys::Z),
489            _ => { log::warn!("Unrecognized x keysym: {}", key_sym); None }
490        }
491    }
492}
493
494
495#[cfg(target_os = "windows")]
496impl Window {
497    pub const WINDOW_CLASS_NAME: &'static str = "window_class";
498
499    fn new_win32(
500        window_name: &str,
501        x: i32, y: i32,
502        width: i32, height: i32,
503    ) -> Self {
504        let window_class_name_utf16 = Self::wide_null(Self::WINDOW_CLASS_NAME);
505        let application_name_utf16 = Self::wide_null(window_name);
506
507        let h_instance = unsafe { GetModuleHandleA(ptr::null()) };
508
509        let icon = unsafe { LoadIconW(h_instance, IDI_APPLICATION) };
510
511        let wc = WNDCLASSW {
512            style: CS_DBLCLKS,
513            lpfnWndProc: Some(win32_process_message),
514            cbClsExtra: 0,
515            cbWndExtra: 0,
516            hInstance: h_instance,
517            hIcon: icon,
518            hCursor: unsafe { LoadCursorW(0, IDC_ARROW) },
519            hbrBackground: 0,
520            lpszClassName: window_class_name_utf16.as_ptr(),
521            lpszMenuName: ptr::null(),
522        };
523
524        if unsafe { RegisterClassW(&wc) } == 0 {
525            unsafe {
526                MessageBoxA(
527                    0,
528                    "Window registration failed.".as_ptr(),
529                    "Error".as_ptr(),
530                    MB_ICONEXCLAMATION | MB_OK
531                );
532            }
533
534            log::error!("Window registration failed.");
535            panic!("Window registration failed.");
536        }
537
538        let client_x = x;
539        let client_y = y;
540        let client_width = width;
541        let client_height = height;
542
543        let mut window_x = client_x;
544        let mut window_y = client_y;
545        let mut window_width = client_width;
546        let mut window_height = client_height;
547
548        let window_style = WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME;
549        let window_ex_style = WS_EX_APPWINDOW;
550
551        let mut border_rect = RECT { left: 0, right: 0, top: 0, bottom: 0 };
552        unsafe { AdjustWindowRectEx(&mut border_rect, window_style, 0, window_ex_style); }
553
554        window_x += border_rect.left;
555        window_y += border_rect.top;
556        window_width += border_rect.right - border_rect.left;
557        window_height += border_rect.bottom - border_rect.top;
558
559        let handle = unsafe {
560            CreateWindowExW(
561                window_ex_style, window_class_name_utf16.as_ptr(), application_name_utf16.as_ptr(),
562                window_style, window_x, window_y, window_width, window_height,
563                0, 0, h_instance, ptr::null()
564            )
565        };
566
567        if handle == 0 {
568            unsafe {
569                MessageBoxA(
570                    0,
571                    "Window creation failed.".as_ptr(),
572                    "Error".as_ptr(),
573                    MB_ICONEXCLAMATION | MB_OK
574                );
575            }
576
577            log::error!("Window creation failed.");
578            panic!("Window creation failed.");
579        }
580
581        // Show the window.
582        let should_activate = true;
583        let show_window_command_flags = if should_activate { SW_SHOW } else { SW_SHOWNOACTIVATE };
584
585        unsafe { ShowWindow(handle, show_window_command_flags); }
586
587        Self {
588            previous_size: (window_width as u32, window_height as u32),
589            h_instance,
590            hwnd: handle,
591        }
592    }
593
594    fn poll_messages_win32(&mut self, mut event_closure: impl FnMut(WindowEvent)) {
595        let mut message = MaybeUninit::<MSG>::uninit();
596
597        while unsafe { PeekMessageW(message.as_mut_ptr(), self.hwnd, 0, 0, PM_REMOVE) } != 0 {
598            unsafe {
599                if !(message.assume_init().message == CUSTOM_CLOSE_MESSAGE
600                    || message.assume_init().message == CUSTOM_SIZE_MESSAGE) {
601                    TranslateMessage(message.as_mut_ptr());
602                    DispatchMessageW(message.as_mut_ptr());
603                }
604            }
605            
606            match unsafe { message.assume_init().message } {
607                CUSTOM_CLOSE_MESSAGE => {
608                    (event_closure)(WindowEvent::Close);
609                },
610                CUSTOM_SIZE_MESSAGE => {
611                    let mut r = MaybeUninit::<RECT>::uninit();
612                    unsafe { GetClientRect(self.hwnd, r.as_mut_ptr()); }
613
614
615                    let width = unsafe { r.assume_init().right - r.assume_init().left } as u32;
616                    let height = unsafe { r.assume_init().bottom - r.assume_init().top } as u32;
617                    
618                    if self.previous_size != (width, height) {
619                        self.previous_size = (width, height);
620                        (event_closure)(WindowEvent::Resize(width, height));
621                    }
622                },
623                WM_MOUSEMOVE => {
624                    let mouse_pos = utility::get_x_y_lparam(unsafe{ message.assume_init().lParam });
625                    (event_closure)(WindowEvent::Input(WindowInputEvent::MouseMove(mouse_pos.0, mouse_pos.1)));
626                },
627                WM_KEYDOWN | WM_SYSKEYDOWN => {
628                    // Check for repeats and prevent sending.
629                    if ((unsafe { message.assume_init().lParam } >> 30) & 1) as u8 == 0 {
630                        let key = Keys::from_usize(unsafe { message.assume_init().wParam });
631                        (event_closure)(WindowEvent::Input(WindowInputEvent::KeyDown(key)));
632                    }
633                },
634                WM_KEYUP | WM_SYSKEYUP => {
635                    let key = Keys::from_usize(unsafe { message.assume_init().wParam });
636                    (event_closure)(WindowEvent::Input(WindowInputEvent::KeyUp(key)));
637                },
638                WM_MOUSEWHEEL => {
639                    let dz = if utility::get_wheel_delta_wparam(unsafe { message.assume_init().wParam }) < 0 {
640                        -1i16
641                    } else {
642                        1i16
643                    };
644
645                    (event_closure)(WindowEvent::Input(WindowInputEvent::MouseWheelMove(dz)));
646                },
647                WM_LBUTTONDOWN => (event_closure)(WindowEvent::Input(WindowInputEvent::MouseDown(MouseButton::Left))),
648                WM_MBUTTONDOWN => (event_closure)(WindowEvent::Input(WindowInputEvent::MouseDown(MouseButton::Middle))),
649                WM_RBUTTONDOWN => (event_closure)(WindowEvent::Input(WindowInputEvent::MouseDown(MouseButton::Right))),
650                WM_LBUTTONUP => (event_closure)(WindowEvent::Input(WindowInputEvent::MouseUp(MouseButton::Left))),
651                WM_MBUTTONUP => (event_closure)(WindowEvent::Input(WindowInputEvent::MouseUp(MouseButton::Middle))),
652                WM_RBUTTONUP => (event_closure)(WindowEvent::Input(WindowInputEvent::MouseUp(MouseButton::Right))),
653                _ => (),
654            }
655
656        }
657    }
658
659    fn raw_window_handle_win32(&self) -> RawWindowHandle {
660        let mut handle = Win32WindowHandle::new(NonZeroIsize::new(self.hwnd).unwrap());
661        handle.hinstance = NonZeroIsize::new(self.h_instance);
662
663        RawWindowHandle::Win32(handle)
664    }
665
666    fn raw_display_handle_windows(&self) -> RawDisplayHandle {
667        RawDisplayHandle::Windows(WindowsDisplayHandle::new())
668    }
669
670    fn wide_null(s: &str) -> Vec<u16> {
671        s.encode_utf16().chain(Some(0)).collect()
672    }
673}
674
675#[cfg(target_os = "windows")]
676impl Drop for Window {
677    fn drop(&mut self) {
678        unsafe { DestroyWindow(self.hwnd); }
679    }
680}
681
682pub enum MouseButton {
683    Left,
684    Right,
685    Middle,
686}
687
688impl MouseButton {
689    pub fn as_str(&self) -> &str {
690        match self {
691            Self::Left => "Left",
692            Self::Middle => "Middle",
693            Self::Right => "Right",
694        }
695    }
696}
697
698pub enum Keys {
699    Backspace,
700    Enter,
701    Tab,
702    Shift,
703    Control,
704
705    Pause,
706    Capital,
707
708    Escape,
709
710    Convert,
711    Nonconvert,
712    Accept,
713    Modechange,
714
715    Space,
716    Prior,
717    Next,
718    End,
719    Home,
720    Left,
721    Up,
722    Right,
723    Down,
724    Select,
725    Print,
726    Execute,
727    Snapshot,
728    Insert,
729    Delete,
730    Help,
731
732    A,
733    B,
734    C,
735    D,
736    E,
737    F,
738    G,
739    H,
740    I,
741    J,
742    K,
743    L,
744    M,
745    N,
746    O,
747    P,
748    Q,
749    R,
750    S,
751    T,
752    U,
753    V,
754    W,
755    X,
756    Y,
757    Z,
758
759    LWin,
760    RWin,
761    Apps,
762
763    Sleep,
764
765    Numpad0,
766    Numpad1,
767    Numpad2,
768    Numpad3,
769    Numpad4,
770    Numpad5,
771    Numpad6,
772    Numpad7,
773    Numpad8,
774    Numpad9,
775    Multiply,
776    Add,
777    Separator,
778    Subtract,
779    Decimal,
780    Divide,
781    F1,
782    F2,
783    F3,
784    F4,
785    F5,
786    F6,
787    F7,
788    F8,
789    F9,
790    F10,
791    F11,
792    F12,
793    F13,
794    F14,
795    F15,
796    F16,
797    F17,
798    F18,
799    F19,
800    F20,
801    F21,
802    F22,
803    F23,
804    F24,
805
806    Numlock,
807    Scroll,
808
809    NumpadEqual,
810
811    LShift,
812    RShift,
813    LControl,
814    RControl,
815    LMenu,
816    RMenu,
817
818    Semicolon,
819    Plus,
820    Comma,
821    Minus,
822    Period,
823    Slash,
824    Grave,
825}
826
827impl Keys {
828    pub fn from_usize(s: usize) -> Self {
829        match s {
830            0x08 => Self::Backspace,
831            0x0D => Self::Enter,
832            0x09 => Self::Tab,
833            0x10 => Self::Shift,
834            0x11 => Self::Control,
835
836            0x13 => Self::Pause,
837            0x14 => Self::Capital,
838
839            0x1B => Self::Escape,
840
841            0x1C => Self::Convert,
842            0x1D => Self::Nonconvert,
843            0x1E => Self::Accept,
844            0x1F => Self::Modechange,
845
846            0x20 => Self::Space,
847            0x21 => Self::Prior,
848            0x22 => Self::Next,
849            0x23 => Self::End,
850            0x24 => Self::Home,
851            0x25 => Self::Left,
852            0x26 => Self::Up,
853            0x27 => Self::Right,
854            0x28 => Self::Down,
855            0x29 => Self::Select,
856            0x2A => Self::Print,
857            0x2B => Self::Execute,
858            0x2C => Self::Snapshot,
859            0x2D => Self::Insert,
860            0x2E => Self::Delete,
861            0x2F => Self::Help,
862
863            0x41 => Self::A,
864            0x42 => Self::B,
865            0x43 => Self::C,
866            0x44 => Self::D,
867            0x45 => Self::E,
868            0x46 => Self::F,
869            0x47 => Self::G,
870            0x48 => Self::H,
871            0x49 => Self::I,
872            0x4A => Self::J,
873            0x4B => Self::K,
874            0x4C => Self::L,
875            0x4D => Self::M,
876            0x4E => Self::N,
877            0x4F => Self::O,
878            0x50 => Self::P,
879            0x51 => Self::Q,
880            0x52 => Self::R,
881            0x53 => Self::S,
882            0x54 => Self::T,
883            0x55 => Self::U,
884            0x56 => Self::V,
885            0x57 => Self::W,
886            0x58 => Self::X,
887            0x59 => Self::Y,
888            0x5A => Self::Z,
889
890            0x5B => Self::LWin,
891            0x5C => Self::RWin,
892            0x5D => Self::Apps,
893
894            0x5F => Self::Sleep,
895
896            0x60 => Self::Numpad0,
897            0x61 => Self::Numpad1,
898            0x62 => Self::Numpad2,
899            0x63 => Self::Numpad3,
900            0x64 => Self::Numpad4,
901            0x65 => Self::Numpad5,
902            0x66 => Self::Numpad6,
903            0x67 => Self::Numpad7,
904            0x68 => Self::Numpad8,
905            0x69 => Self::Numpad9,
906            0x6A => Self::Multiply,
907            0x6B => Self::Add,
908            0x6C => Self::Separator,
909            0x6D => Self::Subtract,
910            0x6E => Self::Decimal,
911            0x6F => Self::Divide,
912            0x70 => Self::F1,
913            0x71 => Self::F2,
914            0x72 => Self::F3,
915            0x73 => Self::F4,
916            0x74 => Self::F5,
917            0x75 => Self::F6,
918            0x76 => Self::F7,
919            0x77 => Self::F8,
920            0x78 => Self::F9,
921            0x79 => Self::F10,
922            0x7A => Self::F11,
923            0x7B => Self::F12,
924            0x7C => Self::F13,
925            0x7D => Self::F14,
926            0x7E => Self::F15,
927            0x7F => Self::F16,
928            0x80 => Self::F17,
929            0x81 => Self::F18,
930            0x82 => Self::F19,
931            0x83 => Self::F20,
932            0x84 => Self::F21,
933            0x85 => Self::F22,
934            0x86 => Self::F23,
935            0x87 => Self::F24,
936
937            0x90 => Self::Numlock,
938            0x91 => Self::Scroll,
939
940            0x92 => Self::NumpadEqual,
941
942            0xA0 => Self::LShift,
943            0xA1 => Self::RShift,
944            0xA2 => Self::LControl,
945            0xA3 => Self::RControl,
946            0xA4 => Self::LMenu,
947            0xA5 => Self::RMenu,
948
949            0xBA => Self::Semicolon,
950            0xBB => Self::Plus,
951            0xBC => Self::Comma,
952            0xBD => Self::Minus,
953            0xBE => Self::Period,
954            0xBF => Self::Slash,
955            0xC0 => Self::Grave,
956            _ => panic!("Provided usize does not corrospond to a valid Key."),
957        }
958    }
959
960    pub fn as_str(&self) -> &str {
961        match self {
962            Self::Backspace => "Backspace",
963            Self::Enter => "Enter",
964            Self::Tab => "Tab",
965            Self::Shift => "Shift",
966            Self::Control => "Control",
967
968            Self::Pause => "Pause",
969            Self::Capital => "Capital",
970
971            Self::Escape => "Escape",
972
973            Self::Convert => "Convert",
974            Self::Nonconvert => "Nonconvert",
975            Self::Accept => "Accept",
976            Self::Modechange => "Modechange",
977
978            Self::Space => "Space",
979            Self::Prior => "Prior",
980            Self::Next => "Next",
981            Self::End => "End",
982            Self::Home => "Home",
983            Self::Left => "Left",
984            Self::Up => "Up",
985            Self::Right => "Right",
986            Self::Down => "Down",
987            Self::Select => "Select",
988            Self::Print => "Print",
989            Self::Execute => "Execute",
990            Self::Snapshot => "Snapshot",
991            Self::Insert => "Insert",
992            Self::Delete => "Delete",
993            Self::Help => "Help",
994
995            Self::A => "A",
996            Self::B => "B",
997            Self::C => "C",
998            Self::D => "D",
999            Self::E => "E",
1000            Self::F => "F",
1001            Self::G => "G",
1002            Self::H => "H",
1003            Self::I => "I",
1004            Self::J => "J",
1005            Self::K => "K",
1006            Self::L => "L",
1007            Self::M => "M",
1008            Self::N => "N",
1009            Self::O => "O",
1010            Self::P => "P",
1011            Self::Q => "Q",
1012            Self::R => "R",
1013            Self::S => "S",
1014            Self::T => "T",
1015            Self::U => "U",
1016            Self::V => "V",
1017            Self::W => "W",
1018            Self::X => "X",
1019            Self::Y => "Y",
1020            Self::Z => "Z",
1021
1022            Self::LWin => "LWin",
1023            Self::RWin => "RWin",
1024            Self::Apps => "Apps",
1025
1026            Self::Sleep => "Sleep",
1027
1028            Self::Numpad0 => "Numpad0",
1029            Self::Numpad1 => "Numpad1",
1030            Self::Numpad2 => "Numpad2",
1031            Self::Numpad3 => "Numpad3",
1032            Self::Numpad4 => "Numpad4",
1033            Self::Numpad5 => "Numpad5",
1034            Self::Numpad6 => "Numpad6",
1035            Self::Numpad7 => "Numpad7",
1036            Self::Numpad8 => "Numpad8",
1037            Self::Numpad9 => "Numpad9",
1038            Self::Multiply => "Multiply",
1039            Self::Add => "Add",
1040            Self::Separator => "Separator",
1041            Self::Subtract => "Subtract",
1042            Self::Decimal => "Decimal",
1043            Self::Divide => "Divide",
1044            Self::F1 => "F1",
1045            Self::F2 => "F2",
1046            Self::F3 => "F3",
1047            Self::F4 => "F4",
1048            Self::F5 => "F5",
1049            Self::F6 => "F6",
1050            Self::F7 => "F7",
1051            Self::F8 => "F8",
1052            Self::F9 => "F9",
1053            Self::F10 => "F10",
1054            Self::F11 => "F11",
1055            Self::F12 => "F12",
1056            Self::F13 => "F13",
1057            Self::F14 => "F14",
1058            Self::F15 => "F15",
1059            Self::F16 => "F16",
1060            Self::F17 => "F17",
1061            Self::F18 => "F18",
1062            Self::F19 => "F19",
1063            Self::F20 => "F20",
1064            Self::F21 => "F21",
1065            Self::F22 => "F22",
1066            Self::F23 => "F23",
1067            Self::F24 => "F24",
1068
1069            Self::Numlock => "Numlock",
1070            Self::Scroll => "Scroll",
1071
1072            Self::NumpadEqual => "NumpadEqual",
1073
1074            Self::LShift => "LShift",
1075            Self::RShift => "RShift",
1076            Self::LControl => "LControl",
1077            Self::RControl => "RControl",
1078            Self::LMenu => "LMenu",
1079            Self::RMenu => "RMenu",
1080
1081            Self::Semicolon => "Semicolon",
1082            Self::Plus => "Plus",
1083            Self::Comma => "Comma",
1084            Self::Minus => "Minus",
1085            Self::Period => "Period",
1086            Self::Slash => "Slash",
1087            Self::Grave => "Grave",
1088        }
1089    }
1090}
1091