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