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