window.rs

   1#![deny(unsafe_op_in_unsafe_fn)]
   2
   3use std::{
   4    any::Any,
   5    cell::{Cell, RefCell},
   6    ffi::c_void,
   7    iter::once,
   8    num::NonZeroIsize,
   9    path::PathBuf,
  10    rc::{Rc, Weak},
  11    str::FromStr,
  12    sync::{Arc, Once},
  13};
  14
  15use ::util::ResultExt;
  16use blade_graphics as gpu;
  17use futures::channel::oneshot::{self, Receiver};
  18use itertools::Itertools;
  19use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
  20use smallvec::SmallVec;
  21use std::result::Result;
  22use windows::{
  23    core::*,
  24    Win32::{
  25        Foundation::*,
  26        Graphics::Gdi::*,
  27        System::{Com::*, Ole::*, SystemServices::*},
  28        UI::{
  29            Controls::*,
  30            HiDpi::*,
  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    pub(crate) handle: AnyWindowHandle,
  51    scale_factor: f32,
  52    display: RefCell<Rc<WindowsDisplay>>,
  53}
  54
  55impl WindowsWindowInner {
  56    fn new(
  57        hwnd: HWND,
  58        cs: &CREATESTRUCTW,
  59        platform_inner: Rc<WindowsPlatformInner>,
  60        handle: AnyWindowHandle,
  61        display: Rc<WindowsDisplay>,
  62    ) -> Self {
  63        let origin = Cell::new(Point::new((cs.x as f64).into(), (cs.y as f64).into()));
  64        let size = Cell::new(Size {
  65            width: (cs.cx as f64).into(),
  66            height: (cs.cy as f64).into(),
  67        });
  68        let mouse_position = Cell::new(Point::default());
  69        let input_handler = Cell::new(None);
  70        struct RawWindow {
  71            hwnd: *mut c_void,
  72        }
  73        unsafe impl blade_rwh::HasRawWindowHandle for RawWindow {
  74            fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle {
  75                let mut handle = blade_rwh::Win32WindowHandle::empty();
  76                handle.hwnd = self.hwnd;
  77                handle.into()
  78            }
  79        }
  80        unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow {
  81            fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle {
  82                blade_rwh::WindowsDisplayHandle::empty().into()
  83            }
  84        }
  85        let raw = RawWindow { hwnd: hwnd.0 as _ };
  86        let gpu = Arc::new(
  87            unsafe {
  88                gpu::Context::init_windowed(
  89                    &raw,
  90                    gpu::ContextDesc {
  91                        validation: false,
  92                        capture: false,
  93                        overlay: false,
  94                    },
  95                )
  96            }
  97            .unwrap(),
  98        );
  99        let extent = gpu::Extent {
 100            width: 1,
 101            height: 1,
 102            depth: 1,
 103        };
 104        let renderer = RefCell::new(BladeRenderer::new(gpu, extent));
 105        let callbacks = RefCell::new(Callbacks::default());
 106        let display = RefCell::new(display);
 107        Self {
 108            hwnd,
 109            origin,
 110            size,
 111            mouse_position,
 112            input_handler,
 113            renderer,
 114            callbacks,
 115            platform_inner,
 116            handle,
 117            scale_factor: 1.0,
 118            display,
 119        }
 120    }
 121
 122    fn is_maximized(&self) -> bool {
 123        let mut placement = WINDOWPLACEMENT::default();
 124        placement.length = std::mem::size_of::<WINDOWPLACEMENT>() as u32;
 125        if unsafe { GetWindowPlacement(self.hwnd, &mut placement) }.is_ok() {
 126            return placement.showCmd == SW_SHOWMAXIMIZED.0 as u32;
 127        }
 128        return false;
 129    }
 130
 131    fn get_titlebar_rect(&self) -> anyhow::Result<RECT> {
 132        let top_and_bottom_borders = 2;
 133        let theme = unsafe { OpenThemeData(self.hwnd, w!("WINDOW")) };
 134        let title_bar_size = unsafe {
 135            GetThemePartSize(
 136                theme,
 137                HDC::default(),
 138                WP_CAPTION.0,
 139                CS_ACTIVE.0,
 140                None,
 141                TS_TRUE,
 142            )
 143        }?;
 144        unsafe { CloseThemeData(theme) }?;
 145
 146        let mut height =
 147            (title_bar_size.cy as f32 * self.scale_factor).round() as i32 + top_and_bottom_borders;
 148
 149        if self.is_maximized() {
 150            let dpi = unsafe { GetDpiForWindow(self.hwnd) };
 151            height += unsafe { (GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) * 2) as i32 };
 152        }
 153
 154        let mut rect = RECT::default();
 155        unsafe { GetClientRect(self.hwnd, &mut rect) }?;
 156        rect.bottom = rect.top + height;
 157        Ok(rect)
 158    }
 159
 160    fn is_virtual_key_pressed(&self, vkey: VIRTUAL_KEY) -> bool {
 161        unsafe { GetKeyState(vkey.0 as i32) < 0 }
 162    }
 163
 164    fn current_modifiers(&self) -> Modifiers {
 165        Modifiers {
 166            control: self.is_virtual_key_pressed(VK_CONTROL),
 167            alt: self.is_virtual_key_pressed(VK_MENU),
 168            shift: self.is_virtual_key_pressed(VK_SHIFT),
 169            command: self.is_virtual_key_pressed(VK_LWIN) || self.is_virtual_key_pressed(VK_RWIN),
 170            function: false,
 171        }
 172    }
 173
 174    /// mark window client rect to be re-drawn
 175    /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-invalidaterect
 176    pub(crate) fn invalidate_client_area(&self) {
 177        unsafe { InvalidateRect(self.hwnd, None, FALSE) };
 178    }
 179
 180    fn handle_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 181        log::debug!("msg: {msg}, wparam: {}, lparam: {}", wparam.0, lparam.0);
 182        match msg {
 183            WM_ACTIVATE => self.handle_activate_msg(msg, wparam, lparam),
 184            WM_CREATE => self.handle_create_msg(lparam),
 185            WM_SETFOCUS => self.handle_set_focus_msg(msg, wparam, lparam),
 186            WM_MOVE => self.handle_move_msg(lparam),
 187            WM_SIZE => self.handle_size_msg(lparam),
 188            WM_NCCALCSIZE => self.handle_calc_client_size(msg, wparam, lparam),
 189            WM_DPICHANGED => self.handle_dpi_changed_msg(msg, wparam, lparam),
 190            WM_NCHITTEST => self.handle_hit_test_msg(msg, wparam, lparam),
 191            WM_PAINT => self.handle_paint_msg(),
 192            WM_CLOSE => self.handle_close_msg(msg, wparam, lparam),
 193            WM_DESTROY => self.handle_destroy_msg(),
 194            WM_MOUSEMOVE => self.handle_mouse_move_msg(lparam, wparam),
 195            WM_NCMOUSEMOVE => self.handle_nc_mouse_move_msg(msg, wparam, lparam),
 196            WM_NCLBUTTONDOWN => {
 197                self.handle_nc_mouse_down_msg(MouseButton::Left, msg, wparam, lparam)
 198            }
 199            WM_NCRBUTTONDOWN => {
 200                self.handle_nc_mouse_down_msg(MouseButton::Right, msg, wparam, lparam)
 201            }
 202            WM_NCMBUTTONDOWN => {
 203                self.handle_nc_mouse_down_msg(MouseButton::Middle, msg, wparam, lparam)
 204            }
 205            WM_NCLBUTTONUP => self.handle_nc_mouse_up_msg(MouseButton::Left, msg, wparam, lparam),
 206            WM_NCRBUTTONUP => self.handle_nc_mouse_up_msg(MouseButton::Right, msg, wparam, lparam),
 207            WM_NCMBUTTONUP => self.handle_nc_mouse_up_msg(MouseButton::Middle, msg, wparam, lparam),
 208            WM_LBUTTONDOWN => self.handle_mouse_down_msg(MouseButton::Left, lparam),
 209            WM_RBUTTONDOWN => self.handle_mouse_down_msg(MouseButton::Right, lparam),
 210            WM_MBUTTONDOWN => self.handle_mouse_down_msg(MouseButton::Middle, lparam),
 211            WM_XBUTTONDOWN => {
 212                let nav_dir = match wparam.hiword() {
 213                    XBUTTON1 => Some(NavigationDirection::Forward),
 214                    XBUTTON2 => Some(NavigationDirection::Back),
 215                    _ => None,
 216                };
 217
 218                if let Some(nav_dir) = nav_dir {
 219                    self.handle_mouse_down_msg(MouseButton::Navigate(nav_dir), lparam)
 220                } else {
 221                    LRESULT(1)
 222                }
 223            }
 224            WM_LBUTTONUP => self.handle_mouse_up_msg(MouseButton::Left, lparam),
 225            WM_RBUTTONUP => self.handle_mouse_up_msg(MouseButton::Right, lparam),
 226            WM_MBUTTONUP => self.handle_mouse_up_msg(MouseButton::Middle, lparam),
 227            WM_XBUTTONUP => {
 228                let nav_dir = match wparam.hiword() {
 229                    XBUTTON1 => Some(NavigationDirection::Back),
 230                    XBUTTON2 => Some(NavigationDirection::Forward),
 231                    _ => None,
 232                };
 233
 234                if let Some(nav_dir) = nav_dir {
 235                    self.handle_mouse_up_msg(MouseButton::Navigate(nav_dir), lparam)
 236                } else {
 237                    LRESULT(1)
 238                }
 239            }
 240            WM_MOUSEWHEEL => self.handle_mouse_wheel_msg(wparam, lparam),
 241            WM_MOUSEHWHEEL => self.handle_mouse_horizontal_wheel_msg(wparam, lparam),
 242            WM_SYSKEYDOWN => self.handle_syskeydown_msg(msg, wparam, lparam),
 243            WM_SYSKEYUP => self.handle_syskeyup_msg(msg, wparam, lparam),
 244            WM_KEYDOWN => self.handle_keydown_msg(msg, wparam, lparam),
 245            WM_KEYUP => self.handle_keyup_msg(msg, wparam),
 246            WM_CHAR => self.handle_char_msg(msg, wparam, lparam),
 247            WM_IME_STARTCOMPOSITION => self.handle_ime_position(),
 248            WM_IME_COMPOSITION => self.handle_ime_composition(msg, wparam, lparam),
 249            WM_IME_CHAR => self.handle_ime_char(wparam),
 250            _ => unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) },
 251        }
 252    }
 253
 254    fn handle_move_msg(&self, lparam: LPARAM) -> LRESULT {
 255        let x = lparam.signed_loword() as f64;
 256        let y = lparam.signed_hiword() as f64;
 257        self.origin.set(Point::new(x.into(), y.into()));
 258        let size = self.size.get();
 259        let center_x = x as f32 + size.width.0 / 2.0;
 260        let center_y = y as f32 + size.height.0 / 2.0;
 261        let monitor_bounds = self.display.borrow().bounds();
 262        if center_x < monitor_bounds.left().0
 263            || center_x > monitor_bounds.right().0
 264            || center_y < monitor_bounds.top().0
 265            || center_y > monitor_bounds.bottom().0
 266        {
 267            // center of the window may have moved to another monitor
 268            let monitor = unsafe { MonitorFromWindow(self.hwnd, MONITOR_DEFAULTTONULL) };
 269            if !monitor.is_invalid() && self.display.borrow().handle != monitor {
 270                // we will get the same monitor if we only have one
 271                (*self.display.borrow_mut()) = Rc::new(WindowsDisplay::new_with_handle(monitor));
 272            }
 273        }
 274        let mut callbacks = self.callbacks.borrow_mut();
 275        if let Some(callback) = callbacks.moved.as_mut() {
 276            callback()
 277        }
 278        LRESULT(0)
 279    }
 280
 281    fn handle_size_msg(&self, lparam: LPARAM) -> LRESULT {
 282        let width = lparam.loword().max(1) as f64;
 283        let height = lparam.hiword().max(1) as f64;
 284        self.renderer
 285            .borrow_mut()
 286            .update_drawable_size(Size { width, height });
 287        let width = width.into();
 288        let height = height.into();
 289        self.size.set(Size { width, height });
 290        let mut callbacks = self.callbacks.borrow_mut();
 291        if let Some(callback) = callbacks.resize.as_mut() {
 292            callback(
 293                Size {
 294                    width: Pixels(width.0),
 295                    height: Pixels(height.0),
 296                },
 297                1.0,
 298            );
 299        }
 300        self.invalidate_client_area();
 301        LRESULT(0)
 302    }
 303
 304    fn handle_paint_msg(&self) -> LRESULT {
 305        let mut paint_struct = PAINTSTRUCT::default();
 306        let _hdc = unsafe { BeginPaint(self.hwnd, &mut paint_struct) };
 307        let mut callbacks = self.callbacks.borrow_mut();
 308        if let Some(request_frame) = callbacks.request_frame.as_mut() {
 309            request_frame();
 310        }
 311        unsafe { EndPaint(self.hwnd, &paint_struct) };
 312        LRESULT(0)
 313    }
 314
 315    fn handle_close_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 316        let mut callbacks = self.callbacks.borrow_mut();
 317        if let Some(callback) = callbacks.should_close.as_mut() {
 318            if callback() {
 319                return LRESULT(0);
 320            }
 321        }
 322        drop(callbacks);
 323        unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }
 324    }
 325
 326    fn handle_destroy_msg(&self) -> LRESULT {
 327        let mut callbacks = self.callbacks.borrow_mut();
 328        if let Some(callback) = callbacks.close.take() {
 329            callback()
 330        }
 331        let index = self
 332            .platform_inner
 333            .raw_window_handles
 334            .read()
 335            .iter()
 336            .position(|handle| *handle == self.hwnd)
 337            .unwrap();
 338        self.platform_inner.raw_window_handles.write().remove(index);
 339        if self.platform_inner.raw_window_handles.read().is_empty() {
 340            self.platform_inner
 341                .foreground_executor
 342                .spawn(async {
 343                    unsafe { PostQuitMessage(0) };
 344                })
 345                .detach();
 346        }
 347        LRESULT(1)
 348    }
 349
 350    fn handle_mouse_move_msg(&self, lparam: LPARAM, wparam: WPARAM) -> LRESULT {
 351        let x = Pixels::from(lparam.signed_loword() as f32);
 352        let y = Pixels::from(lparam.signed_hiword() as f32);
 353        self.mouse_position.set(Point { x, y });
 354        let mut callbacks = self.callbacks.borrow_mut();
 355        if let Some(callback) = callbacks.input.as_mut() {
 356            let pressed_button = match MODIFIERKEYS_FLAGS(wparam.loword() as u32) {
 357                flags if flags.contains(MK_LBUTTON) => Some(MouseButton::Left),
 358                flags if flags.contains(MK_RBUTTON) => Some(MouseButton::Right),
 359                flags if flags.contains(MK_MBUTTON) => Some(MouseButton::Middle),
 360                flags if flags.contains(MK_XBUTTON1) => {
 361                    Some(MouseButton::Navigate(NavigationDirection::Back))
 362                }
 363                flags if flags.contains(MK_XBUTTON2) => {
 364                    Some(MouseButton::Navigate(NavigationDirection::Forward))
 365                }
 366                _ => None,
 367            };
 368            let event = MouseMoveEvent {
 369                position: Point { x, y },
 370                pressed_button,
 371                modifiers: self.current_modifiers(),
 372            };
 373            if callback(PlatformInput::MouseMove(event)).default_prevented {
 374                return LRESULT(0);
 375            }
 376        }
 377        LRESULT(1)
 378    }
 379
 380    fn parse_syskeydown_msg_keystroke(&self, wparam: WPARAM) -> Option<Keystroke> {
 381        let modifiers = self.current_modifiers();
 382        if !modifiers.alt {
 383            // on Windows, F10 can trigger this event, not just the alt key
 384            // and we just don't care about F10
 385            return None;
 386        }
 387
 388        let vk_code = wparam.loword();
 389        let basic_key = basic_vkcode_to_string(vk_code, modifiers);
 390        if basic_key.is_some() {
 391            return basic_key;
 392        }
 393
 394        let key = match VIRTUAL_KEY(vk_code) {
 395            VK_BACK => Some("backspace"),
 396            VK_RETURN => Some("enter"),
 397            VK_TAB => Some("tab"),
 398            VK_UP => Some("up"),
 399            VK_DOWN => Some("down"),
 400            VK_RIGHT => Some("right"),
 401            VK_LEFT => Some("left"),
 402            VK_HOME => Some("home"),
 403            VK_END => Some("end"),
 404            VK_PRIOR => Some("pageup"),
 405            VK_NEXT => Some("pagedown"),
 406            VK_ESCAPE => Some("escape"),
 407            VK_INSERT => Some("insert"),
 408            _ => None,
 409        };
 410
 411        if let Some(key) = key {
 412            Some(Keystroke {
 413                modifiers,
 414                key: key.to_string(),
 415                ime_key: None,
 416            })
 417        } else {
 418            None
 419        }
 420    }
 421
 422    fn parse_keydown_msg_keystroke(&self, wparam: WPARAM) -> Option<Keystroke> {
 423        let vk_code = wparam.loword();
 424
 425        let modifiers = self.current_modifiers();
 426        if modifiers.control || modifiers.alt {
 427            let basic_key = basic_vkcode_to_string(vk_code, modifiers);
 428            if basic_key.is_some() {
 429                return basic_key;
 430            }
 431        }
 432
 433        if vk_code >= VK_F1.0 && vk_code <= VK_F24.0 {
 434            let offset = vk_code - VK_F1.0;
 435            return Some(Keystroke {
 436                modifiers,
 437                key: format!("f{}", offset + 1),
 438                ime_key: None,
 439            });
 440        }
 441
 442        let key = match VIRTUAL_KEY(vk_code) {
 443            VK_BACK => Some("backspace"),
 444            VK_RETURN => Some("enter"),
 445            VK_TAB => Some("tab"),
 446            VK_UP => Some("up"),
 447            VK_DOWN => Some("down"),
 448            VK_RIGHT => Some("right"),
 449            VK_LEFT => Some("left"),
 450            VK_HOME => Some("home"),
 451            VK_END => Some("end"),
 452            VK_PRIOR => Some("pageup"),
 453            VK_NEXT => Some("pagedown"),
 454            VK_ESCAPE => Some("escape"),
 455            VK_INSERT => Some("insert"),
 456            _ => None,
 457        };
 458
 459        if let Some(key) = key {
 460            Some(Keystroke {
 461                modifiers,
 462                key: key.to_string(),
 463                ime_key: None,
 464            })
 465        } else {
 466            None
 467        }
 468    }
 469
 470    fn parse_char_msg_keystroke(&self, wparam: WPARAM) -> Option<Keystroke> {
 471        let src = [wparam.0 as u16];
 472        let Ok(first_char) = char::decode_utf16(src).collect::<Vec<_>>()[0] else {
 473            return None;
 474        };
 475        if first_char.is_control() {
 476            None
 477        } else {
 478            let mut modifiers = self.current_modifiers();
 479            // for characters that use 'shift' to type it is expected that the
 480            // shift is not reported if the uppercase/lowercase are the same and instead only the key is reported
 481            if first_char.to_lowercase().to_string() == first_char.to_uppercase().to_string() {
 482                modifiers.shift = false;
 483            }
 484            Some(Keystroke {
 485                modifiers,
 486                key: first_char.to_lowercase().to_string(),
 487                ime_key: Some(first_char.to_string()),
 488            })
 489        }
 490    }
 491
 492    fn handle_syskeydown_msg(&self, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 493        // we need to call `DefWindowProcW`, or we will lose the system-wide `Alt+F4`, `Alt+{other keys}`
 494        // shortcuts.
 495        let Some(keystroke) = self.parse_syskeydown_msg_keystroke(wparam) else {
 496            return unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) };
 497        };
 498        let Some(ref mut func) = self.callbacks.borrow_mut().input else {
 499            return unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) };
 500        };
 501        let event = KeyDownEvent {
 502            keystroke,
 503            is_held: lparam.0 & (0x1 << 30) > 0,
 504        };
 505        if func(PlatformInput::KeyDown(event)).default_prevented {
 506            self.invalidate_client_area();
 507            return LRESULT(0);
 508        }
 509        unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) }
 510    }
 511
 512    fn handle_syskeyup_msg(&self, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 513        // we need to call `DefWindowProcW`, or we will lose the system-wide `Alt+F4`, `Alt+{other keys}`
 514        // shortcuts.
 515        let Some(keystroke) = self.parse_syskeydown_msg_keystroke(wparam) else {
 516            return unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) };
 517        };
 518        let Some(ref mut func) = self.callbacks.borrow_mut().input else {
 519            return unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) };
 520        };
 521        let event = KeyUpEvent { keystroke };
 522        if func(PlatformInput::KeyUp(event)).default_prevented {
 523            self.invalidate_client_area();
 524            return LRESULT(0);
 525        }
 526        unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) }
 527    }
 528
 529    fn handle_keydown_msg(&self, _msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 530        let Some(keystroke) = self.parse_keydown_msg_keystroke(wparam) else {
 531            return LRESULT(1);
 532        };
 533        let Some(ref mut func) = self.callbacks.borrow_mut().input else {
 534            return LRESULT(1);
 535        };
 536        let event = KeyDownEvent {
 537            keystroke,
 538            is_held: lparam.0 & (0x1 << 30) > 0,
 539        };
 540        if func(PlatformInput::KeyDown(event)).default_prevented {
 541            self.invalidate_client_area();
 542            return LRESULT(0);
 543        }
 544        LRESULT(1)
 545    }
 546
 547    fn handle_keyup_msg(&self, _msg: u32, wparam: WPARAM) -> LRESULT {
 548        let Some(keystroke) = self.parse_keydown_msg_keystroke(wparam) else {
 549            return LRESULT(1);
 550        };
 551        let Some(ref mut func) = self.callbacks.borrow_mut().input else {
 552            return LRESULT(1);
 553        };
 554        let event = KeyUpEvent { keystroke };
 555        if func(PlatformInput::KeyUp(event)).default_prevented {
 556            self.invalidate_client_area();
 557            return LRESULT(0);
 558        }
 559        LRESULT(1)
 560    }
 561
 562    fn handle_char_msg(&self, _msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 563        let Some(keystroke) = self.parse_char_msg_keystroke(wparam) else {
 564            return LRESULT(1);
 565        };
 566        let mut callbacks = self.callbacks.borrow_mut();
 567        let Some(ref mut func) = callbacks.input else {
 568            return LRESULT(1);
 569        };
 570        let ime_key = keystroke.ime_key.clone();
 571        let event = KeyDownEvent {
 572            keystroke,
 573            is_held: lparam.0 & (0x1 << 30) > 0,
 574        };
 575
 576        let dispatch_event_result = func(PlatformInput::KeyDown(event));
 577        if dispatch_event_result.default_prevented || !dispatch_event_result.propagate {
 578            self.invalidate_client_area();
 579            return LRESULT(0);
 580        }
 581        drop(callbacks);
 582        let Some(ime_char) = ime_key else {
 583            return LRESULT(1);
 584        };
 585        let Some(mut input_handler) = self.input_handler.take() else {
 586            return LRESULT(1);
 587        };
 588        input_handler.replace_text_in_range(None, &ime_char);
 589        self.input_handler.set(Some(input_handler));
 590        self.invalidate_client_area();
 591        LRESULT(0)
 592    }
 593
 594    fn handle_mouse_down_msg(&self, button: MouseButton, lparam: LPARAM) -> LRESULT {
 595        let mut callbacks = self.callbacks.borrow_mut();
 596        if let Some(callback) = callbacks.input.as_mut() {
 597            let x = Pixels::from(lparam.signed_loword() as f32);
 598            let y = Pixels::from(lparam.signed_hiword() as f32);
 599            let event = MouseDownEvent {
 600                button,
 601                position: Point { x, y },
 602                modifiers: self.current_modifiers(),
 603                click_count: 1,
 604            };
 605            if callback(PlatformInput::MouseDown(event)).default_prevented {
 606                return LRESULT(0);
 607            }
 608        }
 609        LRESULT(1)
 610    }
 611
 612    fn handle_mouse_up_msg(&self, button: MouseButton, lparam: LPARAM) -> LRESULT {
 613        let mut callbacks = self.callbacks.borrow_mut();
 614        if let Some(callback) = callbacks.input.as_mut() {
 615            let x = Pixels::from(lparam.signed_loword() as f32);
 616            let y = Pixels::from(lparam.signed_hiword() as f32);
 617            let event = MouseUpEvent {
 618                button,
 619                position: Point { x, y },
 620                modifiers: self.current_modifiers(),
 621                click_count: 1,
 622            };
 623            if callback(PlatformInput::MouseUp(event)).default_prevented {
 624                return LRESULT(0);
 625            }
 626        }
 627        LRESULT(1)
 628    }
 629
 630    fn handle_mouse_wheel_msg(&self, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 631        let mut callbacks = self.callbacks.borrow_mut();
 632        if let Some(callback) = callbacks.input.as_mut() {
 633            let x = Pixels::from(lparam.signed_loword() as f32);
 634            let y = Pixels::from(lparam.signed_hiword() as f32);
 635            let wheel_distance = (wparam.signed_hiword() as f32 / WHEEL_DELTA as f32)
 636                * self.platform_inner.settings.borrow().wheel_scroll_lines as f32;
 637            let event = crate::ScrollWheelEvent {
 638                position: Point { x, y },
 639                delta: ScrollDelta::Lines(Point {
 640                    x: 0.0,
 641                    y: wheel_distance,
 642                }),
 643                modifiers: self.current_modifiers(),
 644                touch_phase: TouchPhase::Moved,
 645            };
 646            callback(PlatformInput::ScrollWheel(event));
 647            return LRESULT(0);
 648        }
 649        LRESULT(1)
 650    }
 651
 652    fn handle_mouse_horizontal_wheel_msg(&self, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 653        let mut callbacks = self.callbacks.borrow_mut();
 654        if let Some(callback) = callbacks.input.as_mut() {
 655            let x = Pixels::from(lparam.signed_loword() as f32);
 656            let y = Pixels::from(lparam.signed_hiword() as f32);
 657            let wheel_distance = (wparam.signed_hiword() as f32 / WHEEL_DELTA as f32)
 658                * self.platform_inner.settings.borrow().wheel_scroll_chars as f32;
 659            let event = crate::ScrollWheelEvent {
 660                position: Point { x, y },
 661                delta: ScrollDelta::Lines(Point {
 662                    x: wheel_distance,
 663                    y: 0.0,
 664                }),
 665                modifiers: self.current_modifiers(),
 666                touch_phase: TouchPhase::Moved,
 667            };
 668            if callback(PlatformInput::ScrollWheel(event)).default_prevented {
 669                return LRESULT(0);
 670            }
 671        }
 672        LRESULT(1)
 673    }
 674
 675    fn handle_ime_position(&self) -> LRESULT {
 676        unsafe {
 677            let ctx = ImmGetContext(self.hwnd);
 678            let Some(mut input_handler) = self.input_handler.take() else {
 679                return LRESULT(1);
 680            };
 681            // we are composing, this should never fail
 682            let caret_range = input_handler.selected_text_range().unwrap();
 683            let caret_position = input_handler.bounds_for_range(caret_range).unwrap();
 684            self.input_handler.set(Some(input_handler));
 685            let config = CANDIDATEFORM {
 686                dwStyle: CFS_CANDIDATEPOS,
 687                ptCurrentPos: POINT {
 688                    x: caret_position.origin.x.0 as i32,
 689                    y: caret_position.origin.y.0 as i32 + (caret_position.size.height.0 as i32 / 2),
 690                },
 691                ..Default::default()
 692            };
 693            ImmSetCandidateWindow(ctx, &config as _);
 694            ImmReleaseContext(self.hwnd, ctx);
 695            LRESULT(0)
 696        }
 697    }
 698
 699    fn parse_ime_compostion_string(&self) -> Option<(String, usize)> {
 700        unsafe {
 701            let ctx = ImmGetContext(self.hwnd);
 702            let string_len = ImmGetCompositionStringW(ctx, GCS_COMPSTR, None, 0);
 703            let result = if string_len >= 0 {
 704                let mut buffer = vec![0u8; string_len as usize + 2];
 705                // let mut buffer = [0u8; MAX_PATH as _];
 706                ImmGetCompositionStringW(
 707                    ctx,
 708                    GCS_COMPSTR,
 709                    Some(buffer.as_mut_ptr() as _),
 710                    string_len as _,
 711                );
 712                let wstring = std::slice::from_raw_parts::<u16>(
 713                    buffer.as_mut_ptr().cast::<u16>(),
 714                    string_len as usize / 2,
 715                );
 716                let string = String::from_utf16_lossy(wstring);
 717                Some((string, string_len as usize / 2))
 718            } else {
 719                None
 720            };
 721            ImmReleaseContext(self.hwnd, ctx);
 722            result
 723        }
 724    }
 725
 726    fn handle_ime_composition(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 727        if lparam.0 as u32 & GCS_COMPSTR.0 > 0 {
 728            let Some((string, string_len)) = self.parse_ime_compostion_string() else {
 729                return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) };
 730            };
 731            let Some(mut input_handler) = self.input_handler.take() else {
 732                return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) };
 733            };
 734            input_handler.replace_and_mark_text_in_range(
 735                None,
 736                string.as_str(),
 737                Some(0..string_len),
 738            );
 739            self.input_handler.set(Some(input_handler));
 740            unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }
 741        } else {
 742            // currently, we don't care other stuff
 743            unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }
 744        }
 745    }
 746
 747    fn parse_ime_char(&self, wparam: WPARAM) -> Option<String> {
 748        let src = [wparam.0 as u16];
 749        let Ok(first_char) = char::decode_utf16(src).collect::<Vec<_>>()[0] else {
 750            return None;
 751        };
 752        Some(first_char.to_string())
 753    }
 754
 755    fn handle_ime_char(&self, wparam: WPARAM) -> LRESULT {
 756        let Some(ime_char) = self.parse_ime_char(wparam) else {
 757            return LRESULT(1);
 758        };
 759        let Some(mut input_handler) = self.input_handler.take() else {
 760            return LRESULT(1);
 761        };
 762        input_handler.replace_text_in_range(None, &ime_char);
 763        self.input_handler.set(Some(input_handler));
 764        self.invalidate_client_area();
 765        LRESULT(0)
 766    }
 767
 768    fn handle_drag_drop(&self, input: PlatformInput) {
 769        let mut callbacks = self.callbacks.borrow_mut();
 770        let Some(ref mut func) = callbacks.input else {
 771            return;
 772        };
 773        func(input);
 774    }
 775
 776    /// SEE: https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize
 777    fn handle_calc_client_size(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 778        if wparam.0 == 0 {
 779            return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) };
 780        }
 781
 782        let dpi = unsafe { GetDpiForWindow(self.hwnd) };
 783
 784        let frame_x = unsafe { GetSystemMetricsForDpi(SM_CXFRAME, dpi) };
 785        let frame_y = unsafe { GetSystemMetricsForDpi(SM_CYFRAME, dpi) };
 786        let padding = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) };
 787
 788        // wparam is TRUE so lparam points to an NCCALCSIZE_PARAMS structure
 789        let mut params = lparam.0 as *mut NCCALCSIZE_PARAMS;
 790        let mut requested_client_rect = unsafe { &mut ((*params).rgrc) };
 791
 792        requested_client_rect[0].right -= frame_x + padding;
 793        requested_client_rect[0].left += frame_x + padding;
 794        requested_client_rect[0].bottom -= frame_y + padding;
 795
 796        LRESULT(0)
 797    }
 798
 799    fn handle_activate_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 800        if let Some(titlebar_rect) = self.get_titlebar_rect().log_err() {
 801            unsafe { InvalidateRect(self.hwnd, Some(&titlebar_rect), FALSE) };
 802        }
 803        return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) };
 804    }
 805
 806    fn handle_create_msg(&self, _lparam: LPARAM) -> LRESULT {
 807        let mut size_rect = RECT::default();
 808        unsafe { GetWindowRect(self.hwnd, &mut size_rect).log_err() };
 809        let width = size_rect.right - size_rect.left;
 810        let height = size_rect.bottom - size_rect.top;
 811
 812        self.size.set(Size {
 813            width: GlobalPixels::from(width as f64),
 814            height: GlobalPixels::from(height as f64),
 815        });
 816
 817        // Inform the application of the frame change to force redrawing with the new
 818        // client area that is extended into the title bar
 819        unsafe {
 820            SetWindowPos(
 821                self.hwnd,
 822                HWND::default(),
 823                size_rect.left,
 824                size_rect.top,
 825                width,
 826                height,
 827                SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE,
 828            )
 829            .log_err()
 830        };
 831        LRESULT(0)
 832    }
 833
 834    fn handle_dpi_changed_msg(&self, _msg: u32, _wparam: WPARAM, _lparam: LPARAM) -> LRESULT {
 835        LRESULT(1)
 836    }
 837
 838    fn handle_hit_test_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 839        // default handler for resize areas
 840        let hit = unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) };
 841        if matches!(
 842            hit.0 as u32,
 843            HTNOWHERE
 844                | HTRIGHT
 845                | HTLEFT
 846                | HTTOPLEFT
 847                | HTTOP
 848                | HTTOPRIGHT
 849                | HTBOTTOMRIGHT
 850                | HTBOTTOM
 851                | HTBOTTOMLEFT
 852        ) {
 853            return hit;
 854        }
 855
 856        let dpi = unsafe { GetDpiForWindow(self.hwnd) };
 857        let frame_y = unsafe { GetSystemMetricsForDpi(SM_CYFRAME, dpi) };
 858        let padding = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) };
 859
 860        let mut cursor_point = POINT {
 861            x: lparam.signed_loword().into(),
 862            y: lparam.signed_hiword().into(),
 863        };
 864        unsafe { ScreenToClient(self.hwnd, &mut cursor_point) };
 865        if cursor_point.y > 0 && cursor_point.y < frame_y + padding {
 866            return LRESULT(HTTOP as _);
 867        }
 868
 869        let titlebar_rect = self.get_titlebar_rect();
 870        if let Ok(titlebar_rect) = titlebar_rect {
 871            if cursor_point.y < titlebar_rect.bottom {
 872                let caption_btn_width = unsafe { GetSystemMetricsForDpi(SM_CXSIZE, dpi) };
 873                if cursor_point.x >= titlebar_rect.right - caption_btn_width {
 874                    return LRESULT(HTCLOSE as _);
 875                } else if cursor_point.x >= titlebar_rect.right - caption_btn_width * 2 {
 876                    return LRESULT(HTMAXBUTTON as _);
 877                } else if cursor_point.x >= titlebar_rect.right - caption_btn_width * 3 {
 878                    return LRESULT(HTMINBUTTON as _);
 879                }
 880
 881                return LRESULT(HTCAPTION as _);
 882            }
 883        }
 884
 885        LRESULT(HTCLIENT as _)
 886    }
 887
 888    fn handle_nc_mouse_move_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
 889        let mut cursor_point = POINT {
 890            x: lparam.signed_loword().into(),
 891            y: lparam.signed_hiword().into(),
 892        };
 893        unsafe { ScreenToClient(self.hwnd, &mut cursor_point) };
 894        let x = Pixels::from(cursor_point.x as f32);
 895        let y = Pixels::from(cursor_point.y as f32);
 896        self.mouse_position.set(Point { x, y });
 897        let mut callbacks = self.callbacks.borrow_mut();
 898        if let Some(callback) = callbacks.input.as_mut() {
 899            let event = MouseMoveEvent {
 900                position: Point { x, y },
 901                pressed_button: None,
 902                modifiers: self.current_modifiers(),
 903            };
 904            if callback(PlatformInput::MouseMove(event)).default_prevented {
 905                return LRESULT(0);
 906            }
 907        }
 908        drop(callbacks);
 909        unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }
 910    }
 911
 912    fn handle_nc_mouse_down_msg(
 913        &self,
 914        button: MouseButton,
 915        msg: u32,
 916        wparam: WPARAM,
 917        lparam: LPARAM,
 918    ) -> LRESULT {
 919        let mut callbacks = self.callbacks.borrow_mut();
 920        if let Some(callback) = callbacks.input.as_mut() {
 921            let mut cursor_point = POINT {
 922                x: lparam.signed_loword().into(),
 923                y: lparam.signed_hiword().into(),
 924            };
 925            unsafe { ScreenToClient(self.hwnd, &mut cursor_point) };
 926            let x = Pixels::from(cursor_point.x as f32);
 927            let y = Pixels::from(cursor_point.y as f32);
 928            let event = MouseDownEvent {
 929                button,
 930                position: Point { x, y },
 931                modifiers: self.current_modifiers(),
 932                click_count: 1,
 933            };
 934            if callback(PlatformInput::MouseDown(event)).default_prevented {
 935                return LRESULT(0);
 936            }
 937        }
 938        drop(callbacks);
 939
 940        match wparam.0 as u32 {
 941            // Since these are handled in handle_nc_mouse_up_msg we must prevent the default window proc
 942            HTMINBUTTON | HTMAXBUTTON | HTCLOSE => LRESULT(0),
 943            _ => unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) },
 944        }
 945    }
 946
 947    fn handle_nc_mouse_up_msg(
 948        &self,
 949        button: MouseButton,
 950        msg: u32,
 951        wparam: WPARAM,
 952        lparam: LPARAM,
 953    ) -> LRESULT {
 954        let mut callbacks = self.callbacks.borrow_mut();
 955        if let Some(callback) = callbacks.input.as_mut() {
 956            let mut cursor_point = POINT {
 957                x: lparam.signed_loword().into(),
 958                y: lparam.signed_hiword().into(),
 959            };
 960            unsafe { ScreenToClient(self.hwnd, &mut cursor_point) };
 961            let x = Pixels::from(cursor_point.x as f32);
 962            let y = Pixels::from(cursor_point.y as f32);
 963            let event = MouseUpEvent {
 964                button,
 965                position: Point { x, y },
 966                modifiers: self.current_modifiers(),
 967                click_count: 1,
 968            };
 969            if callback(PlatformInput::MouseUp(event)).default_prevented {
 970                return LRESULT(0);
 971            }
 972        }
 973        drop(callbacks);
 974
 975        if button == MouseButton::Left {
 976            match wparam.0 as u32 {
 977                HTMINBUTTON => unsafe {
 978                    ShowWindowAsync(self.hwnd, SW_MINIMIZE);
 979                    return LRESULT(0);
 980                },
 981                HTMAXBUTTON => unsafe {
 982                    if self.is_maximized() {
 983                        ShowWindowAsync(self.hwnd, SW_NORMAL);
 984                    } else {
 985                        ShowWindowAsync(self.hwnd, SW_MAXIMIZE);
 986                    }
 987                    return LRESULT(0);
 988                },
 989                HTCLOSE => unsafe {
 990                    PostMessageW(self.hwnd, WM_CLOSE, WPARAM::default(), LPARAM::default())
 991                        .log_err();
 992                    return LRESULT(0);
 993                },
 994                _ => {}
 995            };
 996        }
 997
 998        unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }
 999    }
1000
1001    fn handle_set_focus_msg(&self, _msg: u32, wparam: WPARAM, _lparam: LPARAM) -> LRESULT {
1002        // wparam is the window that just lost focus (may be null)
1003        // SEE: https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-setfocus
1004        let lost_focus_hwnd = HWND(wparam.0 as isize);
1005        if let Some(lost_focus_window) = self
1006            .platform_inner
1007            .try_get_windows_inner_from_hwnd(lost_focus_hwnd)
1008        {
1009            let mut callbacks = lost_focus_window.callbacks.borrow_mut();
1010            if let Some(mut cb) = callbacks.active_status_change.as_mut() {
1011                cb(false);
1012            }
1013        }
1014
1015        let mut callbacks = self.callbacks.borrow_mut();
1016        if let Some(mut cb) = callbacks.active_status_change.as_mut() {
1017            cb(true);
1018        }
1019
1020        LRESULT(0)
1021    }
1022}
1023
1024#[derive(Default)]
1025struct Callbacks {
1026    request_frame: Option<Box<dyn FnMut()>>,
1027    input: Option<Box<dyn FnMut(crate::PlatformInput) -> DispatchEventResult>>,
1028    active_status_change: Option<Box<dyn FnMut(bool)>>,
1029    resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
1030    fullscreen: Option<Box<dyn FnMut(bool)>>,
1031    moved: Option<Box<dyn FnMut()>>,
1032    should_close: Option<Box<dyn FnMut() -> bool>>,
1033    close: Option<Box<dyn FnOnce()>>,
1034    appearance_changed: Option<Box<dyn FnMut()>>,
1035}
1036
1037pub(crate) struct WindowsWindow {
1038    inner: Rc<WindowsWindowInner>,
1039    drag_drop_handler: IDropTarget,
1040}
1041
1042struct WindowCreateContext {
1043    inner: Option<Rc<WindowsWindowInner>>,
1044    platform_inner: Rc<WindowsPlatformInner>,
1045    handle: AnyWindowHandle,
1046    display: Rc<WindowsDisplay>,
1047}
1048
1049impl WindowsWindow {
1050    pub(crate) fn new(
1051        platform_inner: Rc<WindowsPlatformInner>,
1052        handle: AnyWindowHandle,
1053        options: WindowParams,
1054    ) -> Self {
1055        let classname = register_wnd_class();
1056        let windowname = HSTRING::from(
1057            options
1058                .titlebar
1059                .as_ref()
1060                .and_then(|titlebar| titlebar.title.as_ref())
1061                .map(|title| title.as_ref())
1062                .unwrap_or(""),
1063        );
1064        let dwstyle = WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX;
1065        let x = options.bounds.origin.x.0 as i32;
1066        let y = options.bounds.origin.y.0 as i32;
1067        let nwidth = options.bounds.size.width.0 as i32;
1068        let nheight = options.bounds.size.height.0 as i32;
1069        let hwndparent = HWND::default();
1070        let hmenu = HMENU::default();
1071        let hinstance = HINSTANCE::default();
1072        let mut context = WindowCreateContext {
1073            inner: None,
1074            platform_inner: platform_inner.clone(),
1075            handle,
1076            // todo(windows) move window to target monitor
1077            // options.display_id
1078            display: Rc::new(WindowsDisplay::primary_monitor().unwrap()),
1079        };
1080        let lpparam = Some(&context as *const _ as *const _);
1081        unsafe {
1082            CreateWindowExW(
1083                WS_EX_APPWINDOW,
1084                classname,
1085                &windowname,
1086                dwstyle,
1087                x,
1088                y,
1089                nwidth,
1090                nheight,
1091                hwndparent,
1092                hmenu,
1093                hinstance,
1094                lpparam,
1095            )
1096        };
1097        let drag_drop_handler = {
1098            let inner = context.inner.as_ref().unwrap();
1099            let handler = WindowsDragDropHandler(Rc::clone(inner));
1100            let drag_drop_handler: IDropTarget = handler.into();
1101            unsafe {
1102                RegisterDragDrop(inner.hwnd, &drag_drop_handler)
1103                    .expect("unable to register drag-drop event")
1104            };
1105            drag_drop_handler
1106        };
1107        let wnd = Self {
1108            inner: context.inner.unwrap(),
1109            drag_drop_handler,
1110        };
1111        platform_inner
1112            .raw_window_handles
1113            .write()
1114            .push(wnd.inner.hwnd);
1115
1116        unsafe { ShowWindow(wnd.inner.hwnd, SW_SHOW) };
1117        wnd
1118    }
1119
1120    fn maximize(&self) {
1121        unsafe { ShowWindowAsync(self.inner.hwnd, SW_MAXIMIZE) };
1122    }
1123}
1124
1125impl HasWindowHandle for WindowsWindow {
1126    fn window_handle(
1127        &self,
1128    ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
1129        let raw = raw_window_handle::Win32WindowHandle::new(unsafe {
1130            NonZeroIsize::new_unchecked(self.inner.hwnd.0)
1131        })
1132        .into();
1133        Ok(unsafe { raw_window_handle::WindowHandle::borrow_raw(raw) })
1134    }
1135}
1136
1137// todo(windows)
1138impl HasDisplayHandle for WindowsWindow {
1139    fn display_handle(
1140        &self,
1141    ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
1142        unimplemented!()
1143    }
1144}
1145
1146impl Drop for WindowsWindow {
1147    fn drop(&mut self) {
1148        unsafe {
1149            let _ = RevokeDragDrop(self.inner.hwnd);
1150        }
1151    }
1152}
1153
1154impl PlatformWindow for WindowsWindow {
1155    fn bounds(&self) -> Bounds<GlobalPixels> {
1156        Bounds {
1157            origin: self.inner.origin.get(),
1158            size: self.inner.size.get(),
1159        }
1160    }
1161
1162    fn is_maximized(&self) -> bool {
1163        self.inner.is_maximized()
1164    }
1165
1166    // todo(windows)
1167    fn content_size(&self) -> Size<Pixels> {
1168        let size = self.inner.size.get();
1169        Size {
1170            width: size.width.0.into(),
1171            height: size.height.0.into(),
1172        }
1173    }
1174
1175    // todo(windows)
1176    fn scale_factor(&self) -> f32 {
1177        self.inner.scale_factor
1178    }
1179
1180    // todo(windows)
1181    fn appearance(&self) -> WindowAppearance {
1182        WindowAppearance::Dark
1183    }
1184
1185    fn display(&self) -> Rc<dyn PlatformDisplay> {
1186        self.inner.display.borrow().clone()
1187    }
1188
1189    fn mouse_position(&self) -> Point<Pixels> {
1190        self.inner.mouse_position.get()
1191    }
1192
1193    // todo(windows)
1194    fn modifiers(&self) -> Modifiers {
1195        Modifiers::none()
1196    }
1197
1198    fn as_any_mut(&mut self) -> &mut dyn Any {
1199        self
1200    }
1201
1202    // todo(windows)
1203    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
1204        self.inner.input_handler.set(Some(input_handler));
1205    }
1206
1207    // todo(windows)
1208    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
1209        self.inner.input_handler.take()
1210    }
1211
1212    fn prompt(
1213        &self,
1214        level: PromptLevel,
1215        msg: &str,
1216        detail: Option<&str>,
1217        answers: &[&str],
1218    ) -> Option<Receiver<usize>> {
1219        let (done_tx, done_rx) = oneshot::channel();
1220        let msg = msg.to_string();
1221        let detail_string = match detail {
1222            Some(info) => Some(info.to_string()),
1223            None => None,
1224        };
1225        let answers = answers.iter().map(|s| s.to_string()).collect::<Vec<_>>();
1226        let handle = self.inner.hwnd;
1227        self.inner
1228            .platform_inner
1229            .foreground_executor
1230            .spawn(async move {
1231                unsafe {
1232                    let mut config;
1233                    config = std::mem::zeroed::<TASKDIALOGCONFIG>();
1234                    config.cbSize = std::mem::size_of::<TASKDIALOGCONFIG>() as _;
1235                    config.hwndParent = handle;
1236                    let title;
1237                    let main_icon;
1238                    match level {
1239                        crate::PromptLevel::Info => {
1240                            title = windows::core::w!("Info");
1241                            main_icon = TD_INFORMATION_ICON;
1242                        }
1243                        crate::PromptLevel::Warning => {
1244                            title = windows::core::w!("Warning");
1245                            main_icon = TD_WARNING_ICON;
1246                        }
1247                        crate::PromptLevel::Critical => {
1248                            title = windows::core::w!("Critical");
1249                            main_icon = TD_ERROR_ICON;
1250                        }
1251                    };
1252                    config.pszWindowTitle = title;
1253                    config.Anonymous1.pszMainIcon = main_icon;
1254                    let instruction = msg.encode_utf16().chain(once(0)).collect_vec();
1255                    config.pszMainInstruction = PCWSTR::from_raw(instruction.as_ptr());
1256                    let hints_encoded;
1257                    if let Some(ref hints) = detail_string {
1258                        hints_encoded = hints.encode_utf16().chain(once(0)).collect_vec();
1259                        config.pszContent = PCWSTR::from_raw(hints_encoded.as_ptr());
1260                    };
1261                    let mut buttons = Vec::new();
1262                    let mut btn_encoded = Vec::new();
1263                    for (index, btn_string) in answers.iter().enumerate() {
1264                        let encoded = btn_string.encode_utf16().chain(once(0)).collect_vec();
1265                        buttons.push(TASKDIALOG_BUTTON {
1266                            nButtonID: index as _,
1267                            pszButtonText: PCWSTR::from_raw(encoded.as_ptr()),
1268                        });
1269                        btn_encoded.push(encoded);
1270                    }
1271                    config.cButtons = buttons.len() as _;
1272                    config.pButtons = buttons.as_ptr();
1273
1274                    config.pfCallback = None;
1275                    let mut res = std::mem::zeroed();
1276                    let _ = TaskDialogIndirect(&config, Some(&mut res), None, None)
1277                        .inspect_err(|e| log::error!("unable to create task dialog: {}", e));
1278
1279                    let _ = done_tx.send(res as usize);
1280                }
1281            })
1282            .detach();
1283
1284        Some(done_rx)
1285    }
1286
1287    fn activate(&self) {
1288        unsafe { SetActiveWindow(self.inner.hwnd) };
1289        unsafe { SetFocus(self.inner.hwnd) };
1290        unsafe { SetForegroundWindow(self.inner.hwnd) };
1291    }
1292
1293    // todo(windows)
1294    fn set_title(&mut self, title: &str) {
1295        unsafe { SetWindowTextW(self.inner.hwnd, &HSTRING::from(title)) }
1296            .inspect_err(|e| log::error!("Set title failed: {e}"))
1297            .ok();
1298    }
1299
1300    // todo(windows)
1301    fn set_edited(&mut self, _edited: bool) {}
1302
1303    // todo(windows)
1304    fn show_character_palette(&self) {}
1305
1306    fn minimize(&self) {
1307        unsafe { ShowWindowAsync(self.inner.hwnd, SW_MINIMIZE) };
1308    }
1309
1310    fn zoom(&self) {
1311        unsafe { ShowWindowAsync(self.inner.hwnd, SW_MAXIMIZE) };
1312    }
1313
1314    // todo(windows)
1315    fn toggle_fullscreen(&self) {}
1316
1317    // todo(windows)
1318    fn is_fullscreen(&self) -> bool {
1319        false
1320    }
1321
1322    // todo(windows)
1323    fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
1324        self.inner.callbacks.borrow_mut().request_frame = Some(callback);
1325    }
1326
1327    // todo(windows)
1328    fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> DispatchEventResult>) {
1329        self.inner.callbacks.borrow_mut().input = Some(callback);
1330    }
1331
1332    // todo(windows)
1333    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
1334        self.inner.callbacks.borrow_mut().active_status_change = Some(callback);
1335    }
1336
1337    // todo(windows)
1338    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
1339        self.inner.callbacks.borrow_mut().resize = Some(callback);
1340    }
1341
1342    // todo(windows)
1343    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
1344        self.inner.callbacks.borrow_mut().fullscreen = Some(callback);
1345    }
1346
1347    // todo(windows)
1348    fn on_moved(&self, callback: Box<dyn FnMut()>) {
1349        self.inner.callbacks.borrow_mut().moved = Some(callback);
1350    }
1351
1352    // todo(windows)
1353    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
1354        self.inner.callbacks.borrow_mut().should_close = Some(callback);
1355    }
1356
1357    // todo(windows)
1358    fn on_close(&self, callback: Box<dyn FnOnce()>) {
1359        self.inner.callbacks.borrow_mut().close = Some(callback);
1360    }
1361
1362    // todo(windows)
1363    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
1364        self.inner.callbacks.borrow_mut().appearance_changed = Some(callback);
1365    }
1366
1367    // todo(windows)
1368    fn is_topmost_for_position(&self, _position: Point<Pixels>) -> bool {
1369        true
1370    }
1371
1372    // todo(windows)
1373    fn draw(&self, scene: &Scene) {
1374        self.inner.renderer.borrow_mut().draw(scene)
1375    }
1376
1377    // todo(windows)
1378    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
1379        self.inner.renderer.borrow().sprite_atlas().clone()
1380    }
1381
1382    fn get_raw_handle(&self) -> HWND {
1383        self.inner.hwnd
1384    }
1385}
1386
1387#[implement(IDropTarget)]
1388struct WindowsDragDropHandler(pub Rc<WindowsWindowInner>);
1389
1390#[allow(non_snake_case)]
1391impl IDropTarget_Impl for WindowsDragDropHandler {
1392    fn DragEnter(
1393        &self,
1394        pdataobj: Option<&IDataObject>,
1395        _grfkeystate: MODIFIERKEYS_FLAGS,
1396        pt: &POINTL,
1397        pdweffect: *mut DROPEFFECT,
1398    ) -> windows::core::Result<()> {
1399        unsafe {
1400            let Some(idata_obj) = pdataobj else {
1401                log::info!("no dragging file or directory detected");
1402                return Ok(());
1403            };
1404            let config = FORMATETC {
1405                cfFormat: CF_HDROP.0,
1406                ptd: std::ptr::null_mut() as _,
1407                dwAspect: DVASPECT_CONTENT.0,
1408                lindex: -1,
1409                tymed: TYMED_HGLOBAL.0 as _,
1410            };
1411            let mut paths = SmallVec::<[PathBuf; 2]>::new();
1412            if idata_obj.QueryGetData(&config as _) == S_OK {
1413                *pdweffect = DROPEFFECT_LINK;
1414                let Ok(mut idata) = idata_obj.GetData(&config as _) else {
1415                    return Ok(());
1416                };
1417                if idata.u.hGlobal.is_invalid() {
1418                    return Ok(());
1419                }
1420                let hdrop = idata.u.hGlobal.0 as *mut HDROP;
1421                let file_count = DragQueryFileW(*hdrop, DRAGDROP_GET_FILES_COUNT, None);
1422                for file_index in 0..file_count {
1423                    let filename_length = DragQueryFileW(*hdrop, file_index, None) as usize;
1424                    let mut buffer = vec![0u16; filename_length + 1];
1425                    let ret = DragQueryFileW(*hdrop, file_index, Some(buffer.as_mut_slice()));
1426                    if ret == 0 {
1427                        log::error!("unable to read file name");
1428                        continue;
1429                    }
1430                    if let Ok(file_name) = String::from_utf16(&buffer[0..filename_length]) {
1431                        if let Ok(path) = PathBuf::from_str(&file_name) {
1432                            paths.push(path);
1433                        }
1434                    }
1435                }
1436                ReleaseStgMedium(&mut idata);
1437                let input = PlatformInput::FileDrop(crate::FileDropEvent::Entered {
1438                    position: Point {
1439                        x: Pixels(pt.x as _),
1440                        y: Pixels(pt.y as _),
1441                    },
1442                    paths: crate::ExternalPaths(paths),
1443                });
1444                self.0.handle_drag_drop(input);
1445            } else {
1446                *pdweffect = DROPEFFECT_NONE;
1447            }
1448        }
1449        Ok(())
1450    }
1451
1452    fn DragOver(
1453        &self,
1454        _grfkeystate: MODIFIERKEYS_FLAGS,
1455        pt: &POINTL,
1456        _pdweffect: *mut DROPEFFECT,
1457    ) -> windows::core::Result<()> {
1458        let input = PlatformInput::FileDrop(crate::FileDropEvent::Pending {
1459            position: Point {
1460                x: Pixels(pt.x as _),
1461                y: Pixels(pt.y as _),
1462            },
1463        });
1464        self.0.handle_drag_drop(input);
1465
1466        Ok(())
1467    }
1468
1469    fn DragLeave(&self) -> windows::core::Result<()> {
1470        let input = PlatformInput::FileDrop(crate::FileDropEvent::Exited);
1471        self.0.handle_drag_drop(input);
1472
1473        Ok(())
1474    }
1475
1476    fn Drop(
1477        &self,
1478        _pdataobj: Option<&IDataObject>,
1479        _grfkeystate: MODIFIERKEYS_FLAGS,
1480        pt: &POINTL,
1481        _pdweffect: *mut DROPEFFECT,
1482    ) -> windows::core::Result<()> {
1483        let input = PlatformInput::FileDrop(crate::FileDropEvent::Submit {
1484            position: Point {
1485                x: Pixels(pt.x as _),
1486                y: Pixels(pt.y as _),
1487            },
1488        });
1489        self.0.handle_drag_drop(input);
1490
1491        Ok(())
1492    }
1493}
1494
1495fn register_wnd_class() -> PCWSTR {
1496    const CLASS_NAME: PCWSTR = w!("Zed::Window");
1497
1498    static ONCE: Once = Once::new();
1499    ONCE.call_once(|| {
1500        let wc = WNDCLASSW {
1501            lpfnWndProc: Some(wnd_proc),
1502            hCursor: unsafe { LoadCursorW(None, IDC_ARROW).ok().unwrap() },
1503            lpszClassName: PCWSTR(CLASS_NAME.as_ptr()),
1504            style: CS_HREDRAW | CS_VREDRAW,
1505            ..Default::default()
1506        };
1507        unsafe { RegisterClassW(&wc) };
1508    });
1509
1510    CLASS_NAME
1511}
1512
1513unsafe extern "system" fn wnd_proc(
1514    hwnd: HWND,
1515    msg: u32,
1516    wparam: WPARAM,
1517    lparam: LPARAM,
1518) -> LRESULT {
1519    if msg == WM_NCCREATE {
1520        let cs = lparam.0 as *const CREATESTRUCTW;
1521        let cs = unsafe { &*cs };
1522        let ctx = cs.lpCreateParams as *mut WindowCreateContext;
1523        let ctx = unsafe { &mut *ctx };
1524        let inner = Rc::new(WindowsWindowInner::new(
1525            hwnd,
1526            cs,
1527            ctx.platform_inner.clone(),
1528            ctx.handle,
1529            ctx.display.clone(),
1530        ));
1531        let weak = Box::new(Rc::downgrade(&inner));
1532        unsafe { set_window_long(hwnd, GWLP_USERDATA, Box::into_raw(weak) as isize) };
1533        ctx.inner = Some(inner);
1534        return LRESULT(1);
1535    }
1536    let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsWindowInner>;
1537    if ptr.is_null() {
1538        return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) };
1539    }
1540    let inner = unsafe { &*ptr };
1541    let r = if let Some(inner) = inner.upgrade() {
1542        inner.handle_msg(msg, wparam, lparam)
1543    } else {
1544        unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
1545    };
1546    if msg == WM_NCDESTROY {
1547        unsafe { set_window_long(hwnd, GWLP_USERDATA, 0) };
1548        unsafe { std::mem::drop(Box::from_raw(ptr)) };
1549    }
1550    r
1551}
1552
1553pub(crate) fn try_get_window_inner(hwnd: HWND) -> Option<Rc<WindowsWindowInner>> {
1554    if hwnd == HWND(0) {
1555        return None;
1556    }
1557
1558    let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsWindowInner>;
1559    if !ptr.is_null() {
1560        let inner = unsafe { &*ptr };
1561        inner.upgrade()
1562    } else {
1563        None
1564    }
1565}
1566
1567fn basic_vkcode_to_string(code: u16, modifiers: Modifiers) -> Option<Keystroke> {
1568    match code {
1569        // VK_0 - VK_9
1570        48..=57 => Some(Keystroke {
1571            modifiers,
1572            key: format!("{}", code - VK_0.0),
1573            ime_key: None,
1574        }),
1575        // VK_A - VK_Z
1576        65..=90 => Some(Keystroke {
1577            modifiers,
1578            key: format!("{}", (b'a' + code as u8 - VK_A.0 as u8) as char),
1579            ime_key: None,
1580        }),
1581        // VK_F1 - VK_F24
1582        112..=135 => Some(Keystroke {
1583            modifiers,
1584            key: format!("f{}", code - VK_F1.0 + 1),
1585            ime_key: None,
1586        }),
1587        // OEM3: `/~, OEM_MINUS: -/_, OEM_PLUS: =/+, ...
1588        _ => {
1589            if let Some(key) = oemkey_vkcode_to_string(code) {
1590                Some(Keystroke {
1591                    modifiers,
1592                    key,
1593                    ime_key: None,
1594                })
1595            } else {
1596                None
1597            }
1598        }
1599    }
1600}
1601
1602fn oemkey_vkcode_to_string(code: u16) -> Option<String> {
1603    match code {
1604        186 => Some(";".to_string()), // VK_OEM_1
1605        187 => Some("=".to_string()), // VK_OEM_PLUS
1606        188 => Some(",".to_string()), // VK_OEM_COMMA
1607        189 => Some("-".to_string()), // VK_OEM_MINUS
1608        190 => Some(".".to_string()), // VK_OEM_PERIOD
1609        // https://kbdlayout.info/features/virtualkeys/VK_ABNT_C1
1610        191 | 193 => Some("/".to_string()), // VK_OEM_2 VK_ABNT_C1
1611        192 => Some("`".to_string()),       // VK_OEM_3
1612        219 => Some("[".to_string()),       // VK_OEM_4
1613        220 => Some("\\".to_string()),      // VK_OEM_5
1614        221 => Some("]".to_string()),       // VK_OEM_6
1615        222 => Some("'".to_string()),       // VK_OEM_7
1616        _ => None,
1617    }
1618}
1619
1620// https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragqueryfilew
1621const DRAGDROP_GET_FILES_COUNT: u32 = 0xFFFFFFFF;