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        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}