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