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