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