1#![deny(unsafe_op_in_unsafe_fn)]
2// todo!("windows"): remove
3#![allow(unused_variables)]
4
5use std::{
6 any::Any,
7 cell::{Cell, RefCell},
8 ffi::c_void,
9 num::NonZeroIsize,
10 rc::{Rc, Weak},
11 sync::{Arc, Once},
12};
13
14use blade_graphics as gpu;
15use futures::channel::oneshot::Receiver;
16use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
17use windows::{
18 core::{w, HSTRING, PCWSTR},
19 Win32::{
20 Foundation::{HINSTANCE, HWND, LPARAM, LRESULT, WPARAM},
21 UI::WindowsAndMessaging::{
22 CreateWindowExW, DefWindowProcW, GetWindowLongPtrW, LoadCursorW, PostQuitMessage,
23 RegisterClassW, SetWindowLongPtrW, SetWindowTextW, ShowWindow, CREATESTRUCTW,
24 CW_USEDEFAULT, GWLP_USERDATA, HMENU, IDC_ARROW, SW_MAXIMIZE, SW_SHOW, WINDOW_EX_STYLE,
25 WINDOW_LONG_PTR_INDEX, WM_CLOSE, WM_DESTROY, WM_MOVE, WM_NCCREATE, WM_NCDESTROY,
26 WM_PAINT, WM_SIZE, WNDCLASSW, WS_OVERLAPPEDWINDOW, WS_VISIBLE,
27 },
28 },
29};
30
31use crate::{
32 platform::blade::BladeRenderer, AnyWindowHandle, Bounds, GlobalPixels, HiLoWord, Modifiers,
33 Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow,
34 Point, PromptLevel, Scene, Size, WindowAppearance, WindowBounds, WindowOptions, WindowsDisplay,
35 WindowsPlatformInner,
36};
37
38struct WindowsWindowInner {
39 hwnd: HWND,
40 origin: Cell<Point<GlobalPixels>>,
41 size: Cell<Size<GlobalPixels>>,
42 mouse_position: Cell<Point<Pixels>>,
43 input_handler: Cell<Option<PlatformInputHandler>>,
44 renderer: RefCell<BladeRenderer>,
45 callbacks: RefCell<Callbacks>,
46 platform_inner: Rc<WindowsPlatformInner>,
47 handle: AnyWindowHandle,
48}
49
50impl WindowsWindowInner {
51 fn new(
52 hwnd: HWND,
53 cs: &CREATESTRUCTW,
54 platform_inner: Rc<WindowsPlatformInner>,
55 handle: AnyWindowHandle,
56 ) -> Self {
57 let origin = Cell::new(Point::new((cs.x as f64).into(), (cs.y as f64).into()));
58 let size = Cell::new(Size {
59 width: (cs.cx as f64).into(),
60 height: (cs.cy as f64).into(),
61 });
62 let mouse_position = Cell::new(Point::default());
63 let input_handler = Cell::new(None);
64 struct RawWindow {
65 hwnd: *mut c_void,
66 }
67 unsafe impl blade_rwh::HasRawWindowHandle for RawWindow {
68 fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle {
69 let mut handle = blade_rwh::Win32WindowHandle::empty();
70 handle.hwnd = self.hwnd;
71 handle.into()
72 }
73 }
74 unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow {
75 fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle {
76 blade_rwh::WindowsDisplayHandle::empty().into()
77 }
78 }
79 let raw = RawWindow { hwnd: hwnd.0 as _ };
80 let gpu = Arc::new(
81 unsafe {
82 gpu::Context::init_windowed(
83 &raw,
84 gpu::ContextDesc {
85 validation: false,
86 capture: false,
87 },
88 )
89 }
90 .unwrap(),
91 );
92 let extent = gpu::Extent {
93 width: 1,
94 height: 1,
95 depth: 1,
96 };
97 let renderer = RefCell::new(BladeRenderer::new(gpu, extent));
98 let callbacks = RefCell::new(Callbacks::default());
99 Self {
100 hwnd,
101 origin,
102 size,
103 mouse_position,
104 input_handler,
105 renderer,
106 callbacks,
107 platform_inner,
108 handle,
109 }
110 }
111
112 fn handle_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
113 log::debug!("msg: {msg}, wparam: {}, lparam: {}", wparam.0, lparam.0);
114 match msg {
115 WM_MOVE => {
116 let x = lparam.loword() as f64;
117 let y = lparam.hiword() as f64;
118 self.origin.set(Point::new(x.into(), y.into()));
119 let mut callbacks = self.callbacks.borrow_mut();
120 if let Some(callback) = callbacks.moved.as_mut() {
121 callback()
122 }
123 }
124 WM_SIZE => {
125 // todo!("windows"): handle maximized or minimized
126 let width = lparam.loword().max(1) as f64;
127 let height = lparam.hiword().max(1) as f64;
128 self.renderer
129 .borrow_mut()
130 .update_drawable_size(Size { width, height });
131 let width = width.into();
132 let height = height.into();
133 self.size.set(Size { width, height });
134 let mut callbacks = self.callbacks.borrow_mut();
135 if let Some(callback) = callbacks.resize.as_mut() {
136 callback(
137 Size {
138 width: Pixels(width.0),
139 height: Pixels(height.0),
140 },
141 1.0,
142 )
143 }
144 }
145 WM_PAINT => {
146 let mut callbacks = self.callbacks.borrow_mut();
147 if let Some(callback) = callbacks.request_frame.as_mut() {
148 callback()
149 }
150 }
151 WM_CLOSE => {
152 let mut callbacks: std::cell::RefMut<'_, Callbacks> = self.callbacks.borrow_mut();
153 if let Some(callback) = callbacks.should_close.as_mut() {
154 if callback() {
155 return LRESULT(0);
156 }
157 }
158 drop(callbacks);
159 return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) };
160 }
161 WM_DESTROY => {
162 let mut callbacks: std::cell::RefMut<'_, Callbacks> = self.callbacks.borrow_mut();
163 if let Some(callback) = callbacks.close.take() {
164 callback()
165 }
166 let mut window_handles = self.platform_inner.window_handles.borrow_mut();
167 window_handles.remove(&self.handle);
168 if window_handles.is_empty() {
169 self.platform_inner
170 .foreground_executor
171 .spawn(async {
172 unsafe { PostQuitMessage(0) };
173 })
174 .detach();
175 }
176 return LRESULT(1);
177 }
178 _ => return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) },
179 }
180 LRESULT(0)
181 }
182}
183
184#[derive(Default)]
185struct Callbacks {
186 request_frame: Option<Box<dyn FnMut()>>,
187 input: Option<Box<dyn FnMut(crate::PlatformInput) -> bool>>,
188 active_status_change: Option<Box<dyn FnMut(bool)>>,
189 resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
190 fullscreen: Option<Box<dyn FnMut(bool)>>,
191 moved: Option<Box<dyn FnMut()>>,
192 should_close: Option<Box<dyn FnMut() -> bool>>,
193 close: Option<Box<dyn FnOnce()>>,
194 appearance_changed: Option<Box<dyn FnMut()>>,
195}
196
197pub(crate) struct WindowsWindow {
198 inner: Rc<WindowsWindowInner>,
199}
200
201struct WindowCreateContext {
202 inner: Option<Rc<WindowsWindowInner>>,
203 platform_inner: Rc<WindowsPlatformInner>,
204 handle: AnyWindowHandle,
205}
206
207impl WindowsWindow {
208 pub(crate) fn new(
209 platform_inner: Rc<WindowsPlatformInner>,
210 handle: AnyWindowHandle,
211 options: WindowOptions,
212 ) -> Self {
213 let dwexstyle = WINDOW_EX_STYLE::default();
214 let classname = register_wnd_class();
215 let windowname = HSTRING::from(
216 options
217 .titlebar
218 .as_ref()
219 .and_then(|titlebar| titlebar.title.as_ref())
220 .map(|title| title.as_ref())
221 .unwrap_or(""),
222 );
223 let dwstyle = WS_OVERLAPPEDWINDOW & !WS_VISIBLE;
224 let mut x = CW_USEDEFAULT;
225 let mut y = CW_USEDEFAULT;
226 let mut nwidth = CW_USEDEFAULT;
227 let mut nheight = CW_USEDEFAULT;
228 match options.bounds {
229 WindowBounds::Fullscreen => {}
230 WindowBounds::Maximized => {}
231 WindowBounds::Fixed(bounds) => {
232 x = bounds.origin.x.0 as i32;
233 y = bounds.origin.y.0 as i32;
234 nwidth = bounds.size.width.0 as i32;
235 nheight = bounds.size.height.0 as i32;
236 }
237 };
238 let hwndparent = HWND::default();
239 let hmenu = HMENU::default();
240 let hinstance = HINSTANCE::default();
241 let mut context = WindowCreateContext {
242 inner: None,
243 platform_inner: platform_inner.clone(),
244 handle,
245 };
246 let lpparam = Some(&context as *const _ as *const _);
247 unsafe {
248 CreateWindowExW(
249 dwexstyle,
250 classname,
251 &windowname,
252 dwstyle,
253 x,
254 y,
255 nwidth,
256 nheight,
257 hwndparent,
258 hmenu,
259 hinstance,
260 lpparam,
261 )
262 };
263 let wnd = Self {
264 inner: context.inner.unwrap(),
265 };
266 platform_inner.window_handles.borrow_mut().insert(handle);
267 match options.bounds {
268 WindowBounds::Fullscreen => wnd.toggle_full_screen(),
269 WindowBounds::Maximized => wnd.maximize(),
270 WindowBounds::Fixed(_) => {}
271 }
272 unsafe { ShowWindow(wnd.inner.hwnd, SW_SHOW) };
273 wnd
274 }
275
276 fn maximize(&self) {
277 unsafe { ShowWindow(self.inner.hwnd, SW_MAXIMIZE) };
278 }
279}
280
281impl HasWindowHandle for WindowsWindow {
282 fn window_handle(
283 &self,
284 ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
285 let raw = raw_window_handle::Win32WindowHandle::new(unsafe {
286 NonZeroIsize::new_unchecked(self.inner.hwnd.0)
287 })
288 .into();
289 Ok(unsafe { raw_window_handle::WindowHandle::borrow_raw(raw) })
290 }
291}
292
293// todo!("windows")
294impl HasDisplayHandle for WindowsWindow {
295 fn display_handle(
296 &self,
297 ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
298 unimplemented!()
299 }
300}
301
302impl PlatformWindow for WindowsWindow {
303 fn bounds(&self) -> WindowBounds {
304 WindowBounds::Fixed(Bounds {
305 origin: self.inner.origin.get(),
306 size: self.inner.size.get(),
307 })
308 }
309
310 // todo!("windows")
311 fn content_size(&self) -> Size<Pixels> {
312 let size = self.inner.size.get();
313 Size {
314 width: size.width.0.into(),
315 height: size.height.0.into(),
316 }
317 }
318
319 // todo!("windows")
320 fn scale_factor(&self) -> f32 {
321 1.0
322 }
323
324 // todo!("windows")
325 fn titlebar_height(&self) -> Pixels {
326 20.0.into()
327 }
328
329 // todo!("windows")
330 fn appearance(&self) -> WindowAppearance {
331 WindowAppearance::Dark
332 }
333
334 // todo!("windows")
335 fn display(&self) -> Rc<dyn PlatformDisplay> {
336 Rc::new(WindowsDisplay::new())
337 }
338
339 fn mouse_position(&self) -> Point<Pixels> {
340 self.inner.mouse_position.get()
341 }
342
343 // todo!("windows")
344 fn modifiers(&self) -> Modifiers {
345 Modifiers::none()
346 }
347
348 fn as_any_mut(&mut self) -> &mut dyn Any {
349 self
350 }
351
352 // todo!("windows")
353 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
354 self.inner.input_handler.set(Some(input_handler));
355 }
356
357 // todo!("windows")
358 fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
359 self.inner.input_handler.take()
360 }
361
362 // todo!("windows")
363 fn prompt(
364 &self,
365 level: PromptLevel,
366 msg: &str,
367 detail: Option<&str>,
368 answers: &[&str],
369 ) -> Receiver<usize> {
370 unimplemented!()
371 }
372
373 // todo!("windows")
374 fn activate(&self) {}
375
376 // todo!("windows")
377 fn set_title(&mut self, title: &str) {
378 unsafe { SetWindowTextW(self.inner.hwnd, &HSTRING::from(title)) }
379 .inspect_err(|e| log::error!("Set title failed: {e}"))
380 .ok();
381 }
382
383 // todo!("windows")
384 fn set_edited(&mut self, edited: bool) {}
385
386 // todo!("windows")
387 fn show_character_palette(&self) {}
388
389 // todo!("windows")
390 fn minimize(&self) {}
391
392 // todo!("windows")
393 fn zoom(&self) {}
394
395 // todo!("windows")
396 fn toggle_full_screen(&self) {}
397
398 // todo!("windows")
399 fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
400 self.inner.callbacks.borrow_mut().request_frame = Some(callback);
401 }
402
403 // todo!("windows")
404 fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>) {
405 self.inner.callbacks.borrow_mut().input = Some(callback);
406 }
407
408 // todo!("windows")
409 fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
410 self.inner.callbacks.borrow_mut().active_status_change = Some(callback);
411 }
412
413 // todo!("windows")
414 fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
415 self.inner.callbacks.borrow_mut().resize = Some(callback);
416 }
417
418 // todo!("windows")
419 fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
420 self.inner.callbacks.borrow_mut().fullscreen = Some(callback);
421 }
422
423 // todo!("windows")
424 fn on_moved(&self, callback: Box<dyn FnMut()>) {
425 self.inner.callbacks.borrow_mut().moved = Some(callback);
426 }
427
428 // todo!("windows")
429 fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
430 self.inner.callbacks.borrow_mut().should_close = Some(callback);
431 }
432
433 // todo!("windows")
434 fn on_close(&self, callback: Box<dyn FnOnce()>) {
435 self.inner.callbacks.borrow_mut().close = Some(callback);
436 }
437
438 // todo!("windows")
439 fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
440 self.inner.callbacks.borrow_mut().appearance_changed = Some(callback);
441 }
442
443 // todo!("windows")
444 fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
445 true
446 }
447
448 // todo!("windows")
449 fn draw(&self, scene: &Scene) {
450 self.inner.renderer.borrow_mut().draw(scene)
451 }
452
453 // todo!("windows")
454 fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
455 self.inner.renderer.borrow().sprite_atlas().clone()
456 }
457}
458
459fn register_wnd_class() -> PCWSTR {
460 const CLASS_NAME: PCWSTR = w!("Zed::Window");
461
462 static ONCE: Once = Once::new();
463 ONCE.call_once(|| {
464 let wc = WNDCLASSW {
465 lpfnWndProc: Some(wnd_proc),
466 hCursor: unsafe { LoadCursorW(None, IDC_ARROW).ok().unwrap() },
467 lpszClassName: PCWSTR(CLASS_NAME.as_ptr()),
468 ..Default::default()
469 };
470 unsafe { RegisterClassW(&wc) };
471 });
472
473 CLASS_NAME
474}
475
476unsafe extern "system" fn wnd_proc(
477 hwnd: HWND,
478 msg: u32,
479 wparam: WPARAM,
480 lparam: LPARAM,
481) -> LRESULT {
482 if msg == WM_NCCREATE {
483 let cs = lparam.0 as *const CREATESTRUCTW;
484 let cs = unsafe { &*cs };
485 let ctx = cs.lpCreateParams as *mut WindowCreateContext;
486 let ctx = unsafe { &mut *ctx };
487 let inner = Rc::new(WindowsWindowInner::new(
488 hwnd,
489 cs,
490 ctx.platform_inner.clone(),
491 ctx.handle,
492 ));
493 let weak = Box::new(Rc::downgrade(&inner));
494 unsafe { set_window_long(hwnd, GWLP_USERDATA, Box::into_raw(weak) as isize) };
495 ctx.inner = Some(inner);
496 return LRESULT(1);
497 }
498 let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsWindowInner>;
499 if ptr.is_null() {
500 return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) };
501 }
502 let inner = unsafe { &*ptr };
503 let r = if let Some(inner) = inner.upgrade() {
504 inner.handle_msg(msg, wparam, lparam)
505 } else {
506 unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
507 };
508 if msg == WM_NCDESTROY {
509 unsafe { set_window_long(hwnd, GWLP_USERDATA, 0) };
510 unsafe { std::mem::drop(Box::from_raw(ptr)) };
511 }
512 r
513}
514
515unsafe fn get_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX) -> isize {
516 #[cfg(target_pointer_width = "64")]
517 unsafe {
518 GetWindowLongPtrW(hwnd, nindex)
519 }
520 #[cfg(target_pointer_width = "32")]
521 unsafe {
522 GetWindowLongW(hwnd, nindex) as isize
523 }
524}
525
526unsafe fn set_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX, dwnewlong: isize) -> isize {
527 #[cfg(target_pointer_width = "64")]
528 unsafe {
529 SetWindowLongPtrW(hwnd, nindex, dwnewlong)
530 }
531 #[cfg(target_pointer_width = "32")]
532 unsafe {
533 SetWindowLongW(hwnd, nindex, dwnewlong as i32) as isize
534 }
535}