1use {
2 compo::prelude::*,
3 tracing::{error, info},
4 windows::{
5 Win32::{
6 Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM},
7 Graphics::Gdi::HBRUSH,
8 System::LibraryLoader::GetModuleHandleW,
9 UI::{
10 Input::KeyboardAndMouse::EnableWindow,
11 WindowsAndMessaging::{
12 CREATESTRUCTW, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, CreateWindowExW,
13 DefWindowProcW, DestroyWindow, GWLP_USERDATA, GetClientRect, HCURSOR, HICON,
14 PostQuitMessage, RegisterClassW, SW_SHOW, SWP_NOZORDER, SetWindowLongPtrW,
15 SetWindowPos, SetWindowTextW, ShowWindow, WM_CREATE, WM_DESTROY, WNDCLASSW,
16 WS_EX_LEFT, WS_OVERLAPPEDWINDOW,
17 },
18 },
19 },
20 core::{PCWSTR, w},
21 },
22};
23
24unsafe extern "system" fn window_proc(
26 hwnd: HWND,
27 msg: u32,
28 wparam: WPARAM,
29 lparam: LPARAM,
30) -> LRESULT {
31 match msg {
32 WM_CREATE => {
33 let create_struct = lparam.0 as *const CREATESTRUCTW;
34 let window_ptr = unsafe { (*create_struct).lpCreateParams };
35 unsafe { SetWindowLongPtrW(hwnd, GWLP_USERDATA, window_ptr as isize) };
36 LRESULT::default()
37 }
38 WM_DESTROY => {
39 unsafe { PostQuitMessage(0) };
40 LRESULT::default()
41 }
42 _ => unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) },
43 }
44}
45
46#[component]
48pub async fn window(
49 #[default = "Window"] title: &str,
50 #[default = 800] width: i32,
51 #[default = 600] height: i32,
52 #[default = CW_USEDEFAULT] left: i32,
53 #[default = CW_USEDEFAULT] top: i32,
54 #[default = true] visible: bool,
55 #[default = true] enabled: bool,
56) {
57 #[field]
58 let hwnd: Option<HWND> = None;
60
61 if *visible {
62 if hwnd.is_none() {
63 let h_instance = match unsafe { GetModuleHandleW(PCWSTR::null()) } {
65 Err(e) => {
66 error!(?e, "Can't get module handle.");
67 return;
68 }
69 Ok(h) => h.into(),
70 };
71
72 let class_name = w!("CompoWindow");
73 let wc = WNDCLASSW {
74 style: CS_HREDRAW | CS_VREDRAW,
75 lpfnWndProc: Some(window_proc),
76 cbClsExtra: 0,
77 cbWndExtra: 0,
78 hInstance: h_instance,
79 hIcon: HICON::default(),
80 hCursor: HCURSOR::default(),
81 hbrBackground: HBRUSH::default(),
82 lpszMenuName: PCWSTR::null(),
83 lpszClassName: class_name,
84 };
85
86 unsafe {
87 RegisterClassW(&wc);
88
89 let mut window_title: Vec<u16> = title.encode_utf16().collect();
92 window_title.push(0); let hwnd_value = match CreateWindowExW(
94 WS_EX_LEFT,
95 class_name,
96 PCWSTR(window_title.as_ptr()),
97 WS_OVERLAPPEDWINDOW,
98 *left,
99 *top,
100 *width,
101 *height,
102 None,
103 None,
104 Some(h_instance),
105 None,
106 ) {
107 Ok(h) => h,
108 Err(e) => {
109 error!(?e, "Can't create window.");
110 return;
111 }
112 };
113
114 hwnd.replace(hwnd_value);
115 }
116 }
117
118 if let Some(hwnd) = hwnd {
120 let _ = unsafe { ShowWindow(*hwnd, SW_SHOW) };
121
122 let mut window_title: Vec<u16> = title.encode_utf16().collect();
124 window_title.push(0); let _ = unsafe { SetWindowTextW(*hwnd, PCWSTR(window_title.as_ptr())) };
126
127 let _ = unsafe {
129 SetWindowPos(
130 *hwnd,
131 None,
132 *left,
133 *top,
134 *width,
135 *height,
136 SWP_NOZORDER, )
138 };
139
140 let _ = unsafe { EnableWindow(*hwnd, *enabled) };
142
143 let mut rect = RECT {
145 left: 0,
146 top: 0,
147 right: 0,
148 bottom: 0,
149 };
150 let _ = unsafe { GetClientRect(*hwnd, &mut rect) };
151
152 info!(
153 "Window updated with client area: {}x{}",
154 rect.right - rect.left,
155 rect.bottom - rect.top
156 );
157 } else {
158 error!("Failed to create window.");
159 }
160 } else if let Some(hwnd) = hwnd.take() {
161 let _ = unsafe { DestroyWindow(hwnd) };
163 }
164}