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