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