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