window.rs

  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        System::SystemServices::{
 22            MK_LBUTTON, MK_MBUTTON, MK_RBUTTON, MK_XBUTTON1, MK_XBUTTON2, MODIFIERKEYS_FLAGS,
 23        },
 24        UI::{
 25            Input::KeyboardAndMouse::{
 26                GetKeyState, VIRTUAL_KEY, VK_BACK, VK_CONTROL, VK_DOWN, VK_END, VK_ESCAPE, VK_F1,
 27                VK_F24, VK_HOME, VK_INSERT, VK_LEFT, VK_LWIN, VK_MENU, VK_NEXT, VK_PRIOR,
 28                VK_RETURN, VK_RIGHT, VK_RWIN, VK_SHIFT, VK_SPACE, VK_TAB, VK_UP,
 29            },
 30            WindowsAndMessaging::{
 31                CreateWindowExW, DefWindowProcW, GetWindowLongPtrW, LoadCursorW, PostQuitMessage,
 32                RegisterClassW, SetWindowLongPtrW, SetWindowTextW, ShowWindow, CREATESTRUCTW,
 33                CW_USEDEFAULT, GWLP_USERDATA, HMENU, IDC_ARROW, SW_MAXIMIZE, SW_SHOW, WHEEL_DELTA,
 34                WINDOW_EX_STYLE, WINDOW_LONG_PTR_INDEX, WM_CHAR, WM_CLOSE, WM_DESTROY, WM_KEYDOWN,
 35                WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP,
 36                WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOVE, WM_NCCREATE, WM_NCDESTROY,
 37                WM_PAINT, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN,
 38                WM_SYSKEYUP, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_OVERLAPPEDWINDOW,
 39                WS_VISIBLE, XBUTTON1, XBUTTON2,
 40            },
 41        },
 42    },
 43};
 44
 45use crate::{
 46    platform::blade::BladeRenderer, AnyWindowHandle, Bounds, GlobalPixels, HiLoWord, KeyDownEvent,
 47    KeyUpEvent, Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
 48    NavigationDirection, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
 49    PlatformInputHandler, PlatformWindow, Point, PromptLevel, Scene, ScrollDelta, Size, TouchPhase,
 50    WindowAppearance, WindowBounds, WindowOptions, WindowsDisplay, WindowsPlatformInner,
 51};
 52
 53#[derive(PartialEq)]
 54pub(crate) enum CallbackResult {
 55    /// handled by system or user callback
 56    Handled {
 57        /// `true` if user callback handled event
 58        by_callback: bool,
 59    },
 60    Unhandled,
 61}
 62
 63impl CallbackResult {
 64    pub fn is_handled(&self) -> bool {
 65        match self {
 66            Self::Handled { by_callback: _ } => true,
 67            _ => false,
 68        }
 69    }
 70}
 71
 72pub(crate) struct WindowsWindowInner {
 73    hwnd: HWND,
 74    origin: Cell<Point<GlobalPixels>>,
 75    size: Cell<Size<GlobalPixels>>,
 76    mouse_position: Cell<Point<Pixels>>,
 77    input_handler: Cell<Option<PlatformInputHandler>>,
 78    renderer: RefCell<BladeRenderer>,
 79    callbacks: RefCell<Callbacks>,
 80    platform_inner: Rc<WindowsPlatformInner>,
 81    handle: AnyWindowHandle,
 82}
 83
 84impl WindowsWindowInner {
 85    fn new(
 86        hwnd: HWND,
 87        cs: &CREATESTRUCTW,
 88        platform_inner: Rc<WindowsPlatformInner>,
 89        handle: AnyWindowHandle,
 90    ) -> Self {
 91        let origin = Cell::new(Point::new((cs.x as f64).into(), (cs.y as f64).into()));
 92        let size = Cell::new(Size {
 93            width: (cs.cx as f64).into(),
 94            height: (cs.cy as f64).into(),
 95        });
 96        let mouse_position = Cell::new(Point::default());
 97        let input_handler = Cell::new(None);
 98        struct RawWindow {
 99            hwnd: *mut c_void,
100        }
101        unsafe impl blade_rwh::HasRawWindowHandle for RawWindow {
102            fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle {
103                let mut handle = blade_rwh::Win32WindowHandle::empty();
104                handle.hwnd = self.hwnd;
105                handle.into()
106            }
107        }
108        unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow {
109            fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle {
110                blade_rwh::WindowsDisplayHandle::empty().into()
111            }
112        }
113        let raw = RawWindow { hwnd: hwnd.0 as _ };
114        let gpu = Arc::new(
115            unsafe {
116                gpu::Context::init_windowed(
117                    &raw,
118                    gpu::ContextDesc {
119                        validation: false,
120                        capture: false,
121                        overlay: false,
122                    },
123                )
124            }
125            .unwrap(),
126        );
127        let extent = gpu::Extent {
128            width: 1,
129            height: 1,
130            depth: 1,
131        };
132        let renderer = RefCell::new(BladeRenderer::new(gpu, extent));
133        let callbacks = RefCell::new(Callbacks::default());
134        Self {
135            hwnd,
136            origin,
137            size,
138            mouse_position,
139            input_handler,
140            renderer,
141            callbacks,
142            platform_inner,
143            handle,
144        }
145    }
146
147    fn is_virtual_key_pressed(&self, vkey: VIRTUAL_KEY) -> bool {
148        unsafe { GetKeyState(vkey.0 as i32) < 0 }
149    }
150
151    fn current_modifiers(&self) -> Modifiers {
152        Modifiers {
153            control: self.is_virtual_key_pressed(VK_CONTROL),
154            alt: self.is_virtual_key_pressed(VK_MENU),
155            shift: self.is_virtual_key_pressed(VK_SHIFT),
156            command: self.is_virtual_key_pressed(VK_LWIN) || self.is_virtual_key_pressed(VK_RWIN),
157            function: false,
158        }
159    }
160
161    /// returns true if message is handled and should not dispatch
162    pub(crate) fn handle_immediate_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> bool {
163        match msg {
164            WM_KEYDOWN | WM_SYSKEYDOWN => self.handle_keydown_msg(wparam).is_handled(),
165            WM_KEYUP | WM_SYSKEYUP => self.handle_keyup_msg(wparam).is_handled(),
166            _ => false,
167        }
168    }
169
170    fn handle_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
171        log::debug!("msg: {msg}, wparam: {}, lparam: {}", wparam.0, lparam.0);
172        match msg {
173            WM_MOVE => self.handle_move_msg(lparam),
174            WM_SIZE => self.handle_size_msg(lparam),
175            WM_PAINT => self.handle_paint_msg(),
176            WM_CLOSE => self.handle_close_msg(msg, wparam, lparam),
177            WM_DESTROY => self.handle_destroy_msg(),
178            WM_MOUSEMOVE => self.handle_mouse_move_msg(lparam, wparam),
179            WM_LBUTTONDOWN => self.handle_mouse_down_msg(MouseButton::Left, lparam),
180            WM_RBUTTONDOWN => self.handle_mouse_down_msg(MouseButton::Right, lparam),
181            WM_MBUTTONDOWN => self.handle_mouse_down_msg(MouseButton::Middle, lparam),
182            WM_XBUTTONDOWN => {
183                let nav_dir = match wparam.hiword() {
184                    XBUTTON1 => Some(NavigationDirection::Forward),
185                    XBUTTON2 => Some(NavigationDirection::Back),
186                    _ => None,
187                };
188
189                if let Some(nav_dir) = nav_dir {
190                    self.handle_mouse_down_msg(MouseButton::Navigate(nav_dir), lparam)
191                } else {
192                    LRESULT(1)
193                }
194            }
195            WM_LBUTTONUP => self.handle_mouse_up_msg(MouseButton::Left, lparam),
196            WM_RBUTTONUP => self.handle_mouse_up_msg(MouseButton::Right, lparam),
197            WM_MBUTTONUP => self.handle_mouse_up_msg(MouseButton::Middle, lparam),
198            WM_XBUTTONUP => {
199                let nav_dir = match wparam.hiword() {
200                    XBUTTON1 => Some(NavigationDirection::Back),
201                    XBUTTON2 => Some(NavigationDirection::Forward),
202                    _ => None,
203                };
204
205                if let Some(nav_dir) = nav_dir {
206                    self.handle_mouse_up_msg(MouseButton::Navigate(nav_dir), lparam)
207                } else {
208                    LRESULT(1)
209                }
210            }
211            WM_MOUSEWHEEL => self.handle_mouse_wheel_msg(wparam, lparam),
212            WM_MOUSEHWHEEL => self.handle_mouse_horizontal_wheel_msg(wparam, lparam),
213            WM_CHAR | WM_SYSCHAR => self.handle_char_msg(wparam),
214            // These events are handled by the immediate handler
215            WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP => LRESULT(0),
216            _ => unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) },
217        }
218    }
219
220    fn handle_move_msg(&self, lparam: LPARAM) -> LRESULT {
221        let x = lparam.signed_loword() as f64;
222        let y = lparam.signed_hiword() as f64;
223        self.origin.set(Point::new(x.into(), y.into()));
224        let mut callbacks = self.callbacks.borrow_mut();
225        if let Some(callback) = callbacks.moved.as_mut() {
226            callback()
227        }
228        LRESULT(0)
229    }
230
231    fn handle_size_msg(&self, lparam: LPARAM) -> LRESULT {
232        let width = lparam.loword().max(1) as f64;
233        let height = lparam.hiword().max(1) as f64;
234        self.renderer
235            .borrow_mut()
236            .update_drawable_size(Size { width, height });
237        let width = width.into();
238        let height = height.into();
239        self.size.set(Size { width, height });
240        let mut callbacks = self.callbacks.borrow_mut();
241        if let Some(callback) = callbacks.resize.as_mut() {
242            callback(
243                Size {
244                    width: Pixels(width.0),
245                    height: Pixels(height.0),
246                },
247                1.0,
248            )
249        }
250        LRESULT(0)
251    }
252
253    fn handle_paint_msg(&self) -> LRESULT {
254        let mut callbacks = self.callbacks.borrow_mut();
255        if let Some(callback) = callbacks.request_frame.as_mut() {
256            callback()
257        }
258        LRESULT(0)
259    }
260
261    fn handle_close_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
262        let mut callbacks = self.callbacks.borrow_mut();
263        if let Some(callback) = callbacks.should_close.as_mut() {
264            if callback() {
265                return LRESULT(0);
266            }
267        }
268        drop(callbacks);
269        unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }
270    }
271
272    fn handle_destroy_msg(&self) -> LRESULT {
273        let mut callbacks = self.callbacks.borrow_mut();
274        if let Some(callback) = callbacks.close.take() {
275            callback()
276        }
277        let mut window_handles = self.platform_inner.window_handles.borrow_mut();
278        window_handles.remove(&self.handle);
279        if window_handles.is_empty() {
280            self.platform_inner
281                .foreground_executor
282                .spawn(async {
283                    unsafe { PostQuitMessage(0) };
284                })
285                .detach();
286        }
287        LRESULT(1)
288    }
289
290    fn handle_mouse_move_msg(&self, lparam: LPARAM, wparam: WPARAM) -> LRESULT {
291        let x = Pixels::from(lparam.signed_loword() as f32);
292        let y = Pixels::from(lparam.signed_hiword() as f32);
293        self.mouse_position.set(Point { x, y });
294        let mut callbacks = self.callbacks.borrow_mut();
295        if let Some(callback) = callbacks.input.as_mut() {
296            let pressed_button = match MODIFIERKEYS_FLAGS(wparam.loword() as u32) {
297                flags if flags.contains(MK_LBUTTON) => Some(MouseButton::Left),
298                flags if flags.contains(MK_RBUTTON) => Some(MouseButton::Right),
299                flags if flags.contains(MK_MBUTTON) => Some(MouseButton::Middle),
300                flags if flags.contains(MK_XBUTTON1) => {
301                    Some(MouseButton::Navigate(NavigationDirection::Back))
302                }
303                flags if flags.contains(MK_XBUTTON2) => {
304                    Some(MouseButton::Navigate(NavigationDirection::Forward))
305                }
306                _ => None,
307            };
308            let event = MouseMoveEvent {
309                position: Point { x, y },
310                pressed_button,
311                modifiers: self.current_modifiers(),
312            };
313            if callback(PlatformInput::MouseMove(event)) {
314                return LRESULT(0);
315            }
316        }
317        LRESULT(1)
318    }
319
320    fn parse_key_msg_keystroke(&self, wparam: WPARAM) -> Option<Keystroke> {
321        let vk_code = wparam.loword();
322
323        // 0-9 https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
324        if vk_code >= 0x30 && vk_code <= 0x39 {
325            let modifiers = self.current_modifiers();
326
327            if modifiers.shift {
328                return None;
329            }
330
331            let digit_char = (b'0' + ((vk_code - 0x30) as u8)) as char;
332            return Some(Keystroke {
333                modifiers,
334                key: digit_char.to_string(),
335                ime_key: Some(digit_char.to_string()),
336            });
337        }
338
339        // A-Z https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
340        if vk_code >= 0x41 && vk_code <= 0x5A {
341            let offset = (vk_code - 0x41) as u8;
342            let alpha_char = (b'a' + offset) as char;
343            let alpha_char_upper = (b'A' + offset) as char;
344            let modifiers = self.current_modifiers();
345            return Some(Keystroke {
346                modifiers,
347                key: alpha_char.to_string(),
348                ime_key: Some(if modifiers.shift {
349                    alpha_char_upper.to_string()
350                } else {
351                    alpha_char.to_string()
352                }),
353            });
354        }
355
356        if vk_code >= VK_F1.0 && vk_code <= VK_F24.0 {
357            let offset = vk_code - VK_F1.0;
358            return Some(Keystroke {
359                modifiers: self.current_modifiers(),
360                key: format!("f{}", offset + 1),
361                ime_key: None,
362            });
363        }
364
365        let key = match VIRTUAL_KEY(vk_code) {
366            VK_SPACE => Some(("space", Some(" "))),
367            VK_TAB => Some(("tab", Some("\t"))),
368            VK_BACK => Some(("backspace", None)),
369            VK_RETURN => Some(("enter", None)),
370            VK_UP => Some(("up", None)),
371            VK_DOWN => Some(("down", None)),
372            VK_RIGHT => Some(("right", None)),
373            VK_LEFT => Some(("left", None)),
374            VK_HOME => Some(("home", None)),
375            VK_END => Some(("end", None)),
376            VK_PRIOR => Some(("pageup", None)),
377            VK_NEXT => Some(("pagedown", None)),
378            VK_ESCAPE => Some(("escape", None)),
379            VK_INSERT => Some(("insert", None)),
380            _ => None,
381        };
382
383        if let Some((key, ime_key)) = key {
384            Some(Keystroke {
385                modifiers: self.current_modifiers(),
386                key: key.to_string(),
387                ime_key: ime_key.map(|k| k.to_string()),
388            })
389        } else {
390            None
391        }
392    }
393
394    fn handle_keydown_msg(&self, wparam: WPARAM) -> CallbackResult {
395        let mut callbacks = self.callbacks.borrow_mut();
396        let keystroke = self.parse_key_msg_keystroke(wparam);
397        if let Some(keystroke) = keystroke {
398            if let Some(callback) = callbacks.input.as_mut() {
399                let ime_key = keystroke.ime_key.clone();
400                let event = KeyDownEvent {
401                    keystroke,
402                    is_held: true,
403                };
404
405                if callback(PlatformInput::KeyDown(event)) {
406                    if let Some(request_frame) = callbacks.request_frame.as_mut() {
407                        request_frame();
408                    }
409                    CallbackResult::Handled { by_callback: true }
410                } else if let Some(mut input_handler) = self.input_handler.take() {
411                    if let Some(ime_key) = ime_key {
412                        input_handler.replace_text_in_range(None, &ime_key);
413                    }
414                    self.input_handler.set(Some(input_handler));
415                    if let Some(request_frame) = callbacks.request_frame.as_mut() {
416                        request_frame();
417                    }
418                    CallbackResult::Handled { by_callback: true }
419                } else {
420                    CallbackResult::Handled { by_callback: false }
421                }
422            } else {
423                CallbackResult::Handled { by_callback: false }
424            }
425        } else {
426            CallbackResult::Unhandled
427        }
428    }
429
430    fn handle_keyup_msg(&self, wparam: WPARAM) -> CallbackResult {
431        let mut callbacks = self.callbacks.borrow_mut();
432        let keystroke = self.parse_key_msg_keystroke(wparam);
433        if let Some(keystroke) = keystroke {
434            if let Some(callback) = callbacks.input.as_mut() {
435                let event = KeyUpEvent { keystroke };
436                CallbackResult::Handled {
437                    by_callback: callback(PlatformInput::KeyUp(event)),
438                }
439            } else {
440                CallbackResult::Handled { by_callback: false }
441            }
442        } else {
443            CallbackResult::Unhandled
444        }
445    }
446
447    fn handle_char_msg(&self, wparam: WPARAM) -> LRESULT {
448        let mut callbacks = self.callbacks.borrow_mut();
449        if let Some(callback) = callbacks.input.as_mut() {
450            let modifiers = self.current_modifiers();
451            let msg_char = wparam.0 as u8 as char;
452            let keystroke = Keystroke {
453                modifiers,
454                key: msg_char.to_string(),
455                ime_key: Some(msg_char.to_string()),
456            };
457            let ime_key = keystroke.ime_key.clone();
458            let event = KeyDownEvent {
459                keystroke,
460                is_held: false,
461            };
462
463            if callback(PlatformInput::KeyDown(event)) {
464                return LRESULT(0);
465            }
466
467            if let Some(mut input_handler) = self.input_handler.take() {
468                if let Some(ime_key) = ime_key {
469                    input_handler.replace_text_in_range(None, &ime_key);
470                }
471                self.input_handler.set(Some(input_handler));
472                return LRESULT(0);
473            }
474        }
475        return LRESULT(1);
476    }
477
478    fn handle_mouse_down_msg(&self, button: MouseButton, lparam: LPARAM) -> LRESULT {
479        let mut callbacks = self.callbacks.borrow_mut();
480        if let Some(callback) = callbacks.input.as_mut() {
481            let x = Pixels::from(lparam.signed_loword() as f32);
482            let y = Pixels::from(lparam.signed_hiword() as f32);
483            let event = MouseDownEvent {
484                button,
485                position: Point { x, y },
486                modifiers: self.current_modifiers(),
487                click_count: 1,
488            };
489            if callback(PlatformInput::MouseDown(event)) {
490                return LRESULT(0);
491            }
492        }
493        LRESULT(1)
494    }
495
496    fn handle_mouse_up_msg(&self, button: MouseButton, lparam: LPARAM) -> LRESULT {
497        let mut callbacks = self.callbacks.borrow_mut();
498        if let Some(callback) = callbacks.input.as_mut() {
499            let x = Pixels::from(lparam.signed_loword() as f32);
500            let y = Pixels::from(lparam.signed_hiword() as f32);
501            let event = MouseUpEvent {
502                button,
503                position: Point { x, y },
504                modifiers: self.current_modifiers(),
505                click_count: 1,
506            };
507            if callback(PlatformInput::MouseUp(event)) {
508                return LRESULT(0);
509            }
510        }
511        LRESULT(1)
512    }
513
514    fn handle_mouse_wheel_msg(&self, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
515        let mut callbacks = self.callbacks.borrow_mut();
516        if let Some(callback) = callbacks.input.as_mut() {
517            let x = Pixels::from(lparam.signed_loword() as f32);
518            let y = Pixels::from(lparam.signed_hiword() as f32);
519            let wheel_distance = (wparam.signed_hiword() as f32 / WHEEL_DELTA as f32)
520                * self.platform_inner.settings.borrow().wheel_scroll_lines as f32;
521            let event = crate::ScrollWheelEvent {
522                position: Point { x, y },
523                delta: ScrollDelta::Lines(Point {
524                    x: 0.0,
525                    y: wheel_distance,
526                }),
527                modifiers: self.current_modifiers(),
528                touch_phase: TouchPhase::Moved,
529            };
530            if callback(PlatformInput::ScrollWheel(event)) {
531                if let Some(request_frame) = callbacks.request_frame.as_mut() {
532                    request_frame();
533                }
534                return LRESULT(0);
535            }
536        }
537        LRESULT(1)
538    }
539
540    fn handle_mouse_horizontal_wheel_msg(&self, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
541        let mut callbacks = self.callbacks.borrow_mut();
542        if let Some(callback) = callbacks.input.as_mut() {
543            let x = Pixels::from(lparam.signed_loword() as f32);
544            let y = Pixels::from(lparam.signed_hiword() as f32);
545            let wheel_distance = (wparam.signed_hiword() as f32 / WHEEL_DELTA as f32)
546                * self.platform_inner.settings.borrow().wheel_scroll_chars as f32;
547            let event = crate::ScrollWheelEvent {
548                position: Point { x, y },
549                delta: ScrollDelta::Lines(Point {
550                    x: wheel_distance,
551                    y: 0.0,
552                }),
553                modifiers: self.current_modifiers(),
554                touch_phase: TouchPhase::Moved,
555            };
556            if callback(PlatformInput::ScrollWheel(event)) {
557                if let Some(request_frame) = callbacks.request_frame.as_mut() {
558                    request_frame();
559                }
560                return LRESULT(0);
561            }
562        }
563        LRESULT(1)
564    }
565}
566
567#[derive(Default)]
568struct Callbacks {
569    request_frame: Option<Box<dyn FnMut()>>,
570    input: Option<Box<dyn FnMut(crate::PlatformInput) -> bool>>,
571    active_status_change: Option<Box<dyn FnMut(bool)>>,
572    resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
573    fullscreen: Option<Box<dyn FnMut(bool)>>,
574    moved: Option<Box<dyn FnMut()>>,
575    should_close: Option<Box<dyn FnMut() -> bool>>,
576    close: Option<Box<dyn FnOnce()>>,
577    appearance_changed: Option<Box<dyn FnMut()>>,
578}
579
580pub(crate) struct WindowsWindow {
581    inner: Rc<WindowsWindowInner>,
582}
583
584struct WindowCreateContext {
585    inner: Option<Rc<WindowsWindowInner>>,
586    platform_inner: Rc<WindowsPlatformInner>,
587    handle: AnyWindowHandle,
588}
589
590impl WindowsWindow {
591    pub(crate) fn new(
592        platform_inner: Rc<WindowsPlatformInner>,
593        handle: AnyWindowHandle,
594        options: WindowOptions,
595    ) -> Self {
596        let dwexstyle = WINDOW_EX_STYLE::default();
597        let classname = register_wnd_class();
598        let windowname = HSTRING::from(
599            options
600                .titlebar
601                .as_ref()
602                .and_then(|titlebar| titlebar.title.as_ref())
603                .map(|title| title.as_ref())
604                .unwrap_or(""),
605        );
606        let dwstyle = WS_OVERLAPPEDWINDOW & !WS_VISIBLE;
607        let mut x = CW_USEDEFAULT;
608        let mut y = CW_USEDEFAULT;
609        let mut nwidth = CW_USEDEFAULT;
610        let mut nheight = CW_USEDEFAULT;
611        match options.bounds {
612            WindowBounds::Fullscreen => {}
613            WindowBounds::Maximized => {}
614            WindowBounds::Fixed(bounds) => {
615                x = bounds.origin.x.0 as i32;
616                y = bounds.origin.y.0 as i32;
617                nwidth = bounds.size.width.0 as i32;
618                nheight = bounds.size.height.0 as i32;
619            }
620        };
621        let hwndparent = HWND::default();
622        let hmenu = HMENU::default();
623        let hinstance = HINSTANCE::default();
624        let mut context = WindowCreateContext {
625            inner: None,
626            platform_inner: platform_inner.clone(),
627            handle,
628        };
629        let lpparam = Some(&context as *const _ as *const _);
630        unsafe {
631            CreateWindowExW(
632                dwexstyle,
633                classname,
634                &windowname,
635                dwstyle,
636                x,
637                y,
638                nwidth,
639                nheight,
640                hwndparent,
641                hmenu,
642                hinstance,
643                lpparam,
644            )
645        };
646        let wnd = Self {
647            inner: context.inner.unwrap(),
648        };
649        platform_inner.window_handles.borrow_mut().insert(handle);
650        match options.bounds {
651            WindowBounds::Fullscreen => wnd.toggle_full_screen(),
652            WindowBounds::Maximized => wnd.maximize(),
653            WindowBounds::Fixed(_) => {}
654        }
655        unsafe { ShowWindow(wnd.inner.hwnd, SW_SHOW) };
656        wnd
657    }
658
659    fn maximize(&self) {
660        unsafe { ShowWindow(self.inner.hwnd, SW_MAXIMIZE) };
661    }
662}
663
664impl HasWindowHandle for WindowsWindow {
665    fn window_handle(
666        &self,
667    ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
668        let raw = raw_window_handle::Win32WindowHandle::new(unsafe {
669            NonZeroIsize::new_unchecked(self.inner.hwnd.0)
670        })
671        .into();
672        Ok(unsafe { raw_window_handle::WindowHandle::borrow_raw(raw) })
673    }
674}
675
676// todo!("windows")
677impl HasDisplayHandle for WindowsWindow {
678    fn display_handle(
679        &self,
680    ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
681        unimplemented!()
682    }
683}
684
685impl PlatformWindow for WindowsWindow {
686    fn bounds(&self) -> WindowBounds {
687        WindowBounds::Fixed(Bounds {
688            origin: self.inner.origin.get(),
689            size: self.inner.size.get(),
690        })
691    }
692
693    // todo!("windows")
694    fn content_size(&self) -> Size<Pixels> {
695        let size = self.inner.size.get();
696        Size {
697            width: size.width.0.into(),
698            height: size.height.0.into(),
699        }
700    }
701
702    // todo!("windows")
703    fn scale_factor(&self) -> f32 {
704        1.0
705    }
706
707    // todo!("windows")
708    fn titlebar_height(&self) -> Pixels {
709        20.0.into()
710    }
711
712    // todo!("windows")
713    fn appearance(&self) -> WindowAppearance {
714        WindowAppearance::Dark
715    }
716
717    // todo!("windows")
718    fn display(&self) -> Rc<dyn PlatformDisplay> {
719        Rc::new(WindowsDisplay::new())
720    }
721
722    fn mouse_position(&self) -> Point<Pixels> {
723        self.inner.mouse_position.get()
724    }
725
726    // todo!("windows")
727    fn modifiers(&self) -> Modifiers {
728        Modifiers::none()
729    }
730
731    fn as_any_mut(&mut self) -> &mut dyn Any {
732        self
733    }
734
735    // todo!("windows")
736    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
737        self.inner.input_handler.set(Some(input_handler));
738    }
739
740    // todo!("windows")
741    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
742        self.inner.input_handler.take()
743    }
744
745    // todo!("windows")
746    fn prompt(
747        &self,
748        level: PromptLevel,
749        msg: &str,
750        detail: Option<&str>,
751        answers: &[&str],
752    ) -> Receiver<usize> {
753        unimplemented!()
754    }
755
756    // todo!("windows")
757    fn activate(&self) {}
758
759    // todo!("windows")
760    fn set_title(&mut self, title: &str) {
761        unsafe { SetWindowTextW(self.inner.hwnd, &HSTRING::from(title)) }
762            .inspect_err(|e| log::error!("Set title failed: {e}"))
763            .ok();
764    }
765
766    // todo!("windows")
767    fn set_edited(&mut self, edited: bool) {}
768
769    // todo!("windows")
770    fn show_character_palette(&self) {}
771
772    // todo!("windows")
773    fn minimize(&self) {}
774
775    // todo!("windows")
776    fn zoom(&self) {}
777
778    // todo!("windows")
779    fn toggle_full_screen(&self) {}
780
781    // todo!("windows")
782    fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
783        self.inner.callbacks.borrow_mut().request_frame = Some(callback);
784    }
785
786    // todo!("windows")
787    fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>) {
788        self.inner.callbacks.borrow_mut().input = Some(callback);
789    }
790
791    // todo!("windows")
792    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
793        self.inner.callbacks.borrow_mut().active_status_change = Some(callback);
794    }
795
796    // todo!("windows")
797    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
798        self.inner.callbacks.borrow_mut().resize = Some(callback);
799    }
800
801    // todo!("windows")
802    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
803        self.inner.callbacks.borrow_mut().fullscreen = Some(callback);
804    }
805
806    // todo!("windows")
807    fn on_moved(&self, callback: Box<dyn FnMut()>) {
808        self.inner.callbacks.borrow_mut().moved = Some(callback);
809    }
810
811    // todo!("windows")
812    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
813        self.inner.callbacks.borrow_mut().should_close = Some(callback);
814    }
815
816    // todo!("windows")
817    fn on_close(&self, callback: Box<dyn FnOnce()>) {
818        self.inner.callbacks.borrow_mut().close = Some(callback);
819    }
820
821    // todo!("windows")
822    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
823        self.inner.callbacks.borrow_mut().appearance_changed = Some(callback);
824    }
825
826    // todo!("windows")
827    fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
828        true
829    }
830
831    // todo!("windows")
832    fn draw(&self, scene: &Scene) {
833        self.inner.renderer.borrow_mut().draw(scene)
834    }
835
836    // todo!("windows")
837    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
838        self.inner.renderer.borrow().sprite_atlas().clone()
839    }
840}
841
842fn register_wnd_class() -> PCWSTR {
843    const CLASS_NAME: PCWSTR = w!("Zed::Window");
844
845    static ONCE: Once = Once::new();
846    ONCE.call_once(|| {
847        let wc = WNDCLASSW {
848            lpfnWndProc: Some(wnd_proc),
849            hCursor: unsafe { LoadCursorW(None, IDC_ARROW).ok().unwrap() },
850            lpszClassName: PCWSTR(CLASS_NAME.as_ptr()),
851            ..Default::default()
852        };
853        unsafe { RegisterClassW(&wc) };
854    });
855
856    CLASS_NAME
857}
858
859unsafe extern "system" fn wnd_proc(
860    hwnd: HWND,
861    msg: u32,
862    wparam: WPARAM,
863    lparam: LPARAM,
864) -> LRESULT {
865    if msg == WM_NCCREATE {
866        let cs = lparam.0 as *const CREATESTRUCTW;
867        let cs = unsafe { &*cs };
868        let ctx = cs.lpCreateParams as *mut WindowCreateContext;
869        let ctx = unsafe { &mut *ctx };
870        let inner = Rc::new(WindowsWindowInner::new(
871            hwnd,
872            cs,
873            ctx.platform_inner.clone(),
874            ctx.handle,
875        ));
876        let weak = Box::new(Rc::downgrade(&inner));
877        unsafe { set_window_long(hwnd, GWLP_USERDATA, Box::into_raw(weak) as isize) };
878        ctx.inner = Some(inner);
879        return LRESULT(1);
880    }
881    let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsWindowInner>;
882    if ptr.is_null() {
883        return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) };
884    }
885    let inner = unsafe { &*ptr };
886    let r = if let Some(inner) = inner.upgrade() {
887        inner.handle_msg(msg, wparam, lparam)
888    } else {
889        unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
890    };
891    if msg == WM_NCDESTROY {
892        unsafe { set_window_long(hwnd, GWLP_USERDATA, 0) };
893        unsafe { std::mem::drop(Box::from_raw(ptr)) };
894    }
895    r
896}
897
898pub(crate) fn try_get_window_inner(hwnd: HWND) -> Option<Rc<WindowsWindowInner>> {
899    let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsWindowInner>;
900    if !ptr.is_null() {
901        let inner = unsafe { &*ptr };
902        inner.upgrade()
903    } else {
904        None
905    }
906}
907
908unsafe fn get_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX) -> isize {
909    #[cfg(target_pointer_width = "64")]
910    unsafe {
911        GetWindowLongPtrW(hwnd, nindex)
912    }
913    #[cfg(target_pointer_width = "32")]
914    unsafe {
915        GetWindowLongW(hwnd, nindex) as isize
916    }
917}
918
919unsafe fn set_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX, dwnewlong: isize) -> isize {
920    #[cfg(target_pointer_width = "64")]
921    unsafe {
922        SetWindowLongPtrW(hwnd, nindex, dwnewlong)
923    }
924    #[cfg(target_pointer_width = "32")]
925    unsafe {
926        SetWindowLongW(hwnd, nindex, dwnewlong as i32) as isize
927    }
928}