platform.rs

   1use std::{
   2    cell::{Cell, RefCell},
   3    ffi::OsStr,
   4    path::{Path, PathBuf},
   5    rc::{Rc, Weak},
   6    sync::{
   7        Arc,
   8        atomic::{AtomicBool, Ordering},
   9    },
  10};
  11
  12use ::util::{ResultExt, paths::SanitizedPath};
  13use anyhow::{Context as _, Result, anyhow};
  14use futures::channel::oneshot::{self, Receiver};
  15use itertools::Itertools;
  16use parking_lot::RwLock;
  17use smallvec::SmallVec;
  18use windows::{
  19    UI::ViewManagement::UISettings,
  20    Win32::{
  21        Foundation::*,
  22        Graphics::{Direct3D11::ID3D11Device, Gdi::*},
  23        Media::{timeBeginPeriod, timeEndPeriod},
  24        Security::Credentials::*,
  25        System::{Com::*, LibraryLoader::*, Ole::*, SystemInformation::*},
  26        UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*},
  27    },
  28    core::*,
  29};
  30
  31use crate::*;
  32
  33pub(crate) struct WindowsPlatform {
  34    inner: Rc<WindowsPlatformInner>,
  35    raw_window_handles: Arc<RwLock<SmallVec<[SafeHwnd; 4]>>>,
  36    // The below members will never change throughout the entire lifecycle of the app.
  37    headless: bool,
  38    icon: HICON,
  39    background_executor: BackgroundExecutor,
  40    foreground_executor: ForegroundExecutor,
  41    text_system: Arc<dyn PlatformTextSystem>,
  42    direct_write_text_system: Option<Arc<DirectWriteTextSystem>>,
  43    windows_version: WindowsVersion,
  44    drop_target_helper: Option<IDropTargetHelper>,
  45    /// Flag to instruct the `VSyncProvider` thread to invalidate the directx devices
  46    /// as resizing them has failed, causing us to have lost at least the render target.
  47    invalidate_devices: Arc<AtomicBool>,
  48    handle: HWND,
  49    disable_direct_composition: bool,
  50}
  51
  52struct WindowsPlatformInner {
  53    state: WindowsPlatformState,
  54    raw_window_handles: std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
  55    // The below members will never change throughout the entire lifecycle of the app.
  56    validation_number: usize,
  57    main_receiver: PriorityQueueReceiver<RunnableVariant>,
  58    dispatcher: Arc<WindowsDispatcher>,
  59}
  60
  61pub(crate) struct WindowsPlatformState {
  62    callbacks: PlatformCallbacks,
  63    menus: RefCell<Vec<OwnedMenu>>,
  64    jump_list: RefCell<JumpList>,
  65    // NOTE: standard cursor handles don't need to close.
  66    pub(crate) current_cursor: Cell<Option<HCURSOR>>,
  67    directx_devices: RefCell<Option<DirectXDevices>>,
  68}
  69
  70#[derive(Default)]
  71struct PlatformCallbacks {
  72    open_urls: Cell<Option<Box<dyn FnMut(Vec<String>)>>>,
  73    quit: Cell<Option<Box<dyn FnMut()>>>,
  74    reopen: Cell<Option<Box<dyn FnMut()>>>,
  75    app_menu_action: Cell<Option<Box<dyn FnMut(&dyn Action)>>>,
  76    will_open_app_menu: Cell<Option<Box<dyn FnMut()>>>,
  77    validate_app_menu_command: Cell<Option<Box<dyn FnMut(&dyn Action) -> bool>>>,
  78    keyboard_layout_change: Cell<Option<Box<dyn FnMut()>>>,
  79}
  80
  81impl WindowsPlatformState {
  82    fn new(directx_devices: Option<DirectXDevices>) -> Self {
  83        let callbacks = PlatformCallbacks::default();
  84        let jump_list = JumpList::new();
  85        let current_cursor = load_cursor(CursorStyle::Arrow);
  86
  87        Self {
  88            callbacks,
  89            jump_list: RefCell::new(jump_list),
  90            current_cursor: Cell::new(current_cursor),
  91            directx_devices: RefCell::new(directx_devices),
  92            menus: RefCell::new(Vec::new()),
  93        }
  94    }
  95}
  96
  97impl WindowsPlatform {
  98    pub(crate) fn new(headless: bool) -> Result<Self> {
  99        unsafe {
 100            OleInitialize(None).context("unable to initialize Windows OLE")?;
 101            // Set the system timer resolution to 1ms so that short timeouts
 102            // (e.g. in Scheduler::block) are not rounded up to the default
 103            // ~15.6ms tick interval.
 104            timeBeginPeriod(1);
 105        }
 106        let (directx_devices, text_system, direct_write_text_system) = if !headless {
 107            let devices = DirectXDevices::new().context("Creating DirectX devices")?;
 108            let dw_text_system = Arc::new(
 109                DirectWriteTextSystem::new(&devices)
 110                    .context("Error creating DirectWriteTextSystem")?,
 111            );
 112            (
 113                Some(devices),
 114                dw_text_system.clone() as Arc<dyn PlatformTextSystem>,
 115                Some(dw_text_system),
 116            )
 117        } else {
 118            (
 119                None,
 120                Arc::new(crate::NoopTextSystem::new()) as Arc<dyn PlatformTextSystem>,
 121                None,
 122            )
 123        };
 124
 125        let (main_sender, main_receiver) = PriorityQueueReceiver::new();
 126        let validation_number = if usize::BITS == 64 {
 127            rand::random::<u64>() as usize
 128        } else {
 129            rand::random::<u32>() as usize
 130        };
 131        let raw_window_handles = Arc::new(RwLock::new(SmallVec::new()));
 132
 133        register_platform_window_class();
 134        let mut context = PlatformWindowCreateContext {
 135            inner: None,
 136            raw_window_handles: Arc::downgrade(&raw_window_handles),
 137            validation_number,
 138            main_sender: Some(main_sender),
 139            main_receiver: Some(main_receiver),
 140            directx_devices,
 141            dispatcher: None,
 142        };
 143        let result = unsafe {
 144            CreateWindowExW(
 145                WINDOW_EX_STYLE(0),
 146                PLATFORM_WINDOW_CLASS_NAME,
 147                None,
 148                WINDOW_STYLE(0),
 149                0,
 150                0,
 151                0,
 152                0,
 153                Some(HWND_MESSAGE),
 154                None,
 155                None,
 156                Some(&raw const context as *const _),
 157            )
 158        };
 159        let inner = context
 160            .inner
 161            .take()
 162            .context("CreateWindowExW did not run correctly")??;
 163        let dispatcher = context
 164            .dispatcher
 165            .take()
 166            .context("CreateWindowExW did not run correctly")?;
 167        let handle = result?;
 168
 169        let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION)
 170            .is_ok_and(|value| value == "true" || value == "1");
 171        let background_executor = BackgroundExecutor::new(dispatcher.clone());
 172        let foreground_executor = ForegroundExecutor::new(dispatcher);
 173
 174        let drop_target_helper: Option<IDropTargetHelper> = if !headless {
 175            Some(unsafe {
 176                CoCreateInstance(&CLSID_DragDropHelper, None, CLSCTX_INPROC_SERVER)
 177                    .context("Error creating drop target helper.")?
 178            })
 179        } else {
 180            None
 181        };
 182        let icon = if !headless {
 183            load_icon().unwrap_or_default()
 184        } else {
 185            HICON::default()
 186        };
 187        let windows_version = WindowsVersion::new().context("Error retrieve windows version")?;
 188
 189        Ok(Self {
 190            inner,
 191            handle,
 192            raw_window_handles,
 193            headless,
 194            icon,
 195            background_executor,
 196            foreground_executor,
 197            text_system,
 198            direct_write_text_system,
 199            disable_direct_composition,
 200            windows_version,
 201            drop_target_helper,
 202            invalidate_devices: Arc::new(AtomicBool::new(false)),
 203        })
 204    }
 205
 206    pub fn window_from_hwnd(&self, hwnd: HWND) -> Option<Rc<WindowsWindowInner>> {
 207        self.raw_window_handles
 208            .read()
 209            .iter()
 210            .find(|entry| entry.as_raw() == hwnd)
 211            .and_then(|hwnd| window_from_hwnd(hwnd.as_raw()))
 212    }
 213
 214    #[inline]
 215    fn post_message(&self, message: u32, wparam: WPARAM, lparam: LPARAM) {
 216        self.raw_window_handles
 217            .read()
 218            .iter()
 219            .for_each(|handle| unsafe {
 220                PostMessageW(Some(handle.as_raw()), message, wparam, lparam).log_err();
 221            });
 222    }
 223
 224    fn generate_creation_info(&self) -> WindowCreationInfo {
 225        WindowCreationInfo {
 226            icon: self.icon,
 227            executor: self.foreground_executor.clone(),
 228            current_cursor: self.inner.state.current_cursor.get(),
 229            windows_version: self.windows_version,
 230            drop_target_helper: self.drop_target_helper.clone().unwrap(),
 231            validation_number: self.inner.validation_number,
 232            main_receiver: self.inner.main_receiver.clone(),
 233            platform_window_handle: self.handle,
 234            disable_direct_composition: self.disable_direct_composition,
 235            directx_devices: self.inner.state.directx_devices.borrow().clone().unwrap(),
 236            invalidate_devices: self.invalidate_devices.clone(),
 237        }
 238    }
 239
 240    fn set_dock_menus(&self, menus: Vec<MenuItem>) {
 241        let mut actions = Vec::new();
 242        menus.into_iter().for_each(|menu| {
 243            if let Some(dock_menu) = DockMenuItem::new(menu).log_err() {
 244                actions.push(dock_menu);
 245            }
 246        });
 247        self.inner.state.jump_list.borrow_mut().dock_menus = actions;
 248        let borrow = self.inner.state.jump_list.borrow();
 249        let dock_menus = borrow
 250            .dock_menus
 251            .iter()
 252            .map(|menu| (menu.name.clone(), menu.description.clone()))
 253            .collect::<Vec<_>>();
 254        let recent_workspaces = borrow.recent_workspaces.clone();
 255        self.background_executor
 256            .spawn(async move {
 257                update_jump_list(&recent_workspaces, &dock_menus).log_err();
 258            })
 259            .detach();
 260    }
 261
 262    fn update_jump_list(
 263        &self,
 264        menus: Vec<MenuItem>,
 265        entries: Vec<SmallVec<[PathBuf; 2]>>,
 266    ) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
 267        let mut actions = Vec::new();
 268        menus.into_iter().for_each(|menu| {
 269            if let Some(dock_menu) = DockMenuItem::new(menu).log_err() {
 270                actions.push(dock_menu);
 271            }
 272        });
 273        let mut jump_list = self.inner.state.jump_list.borrow_mut();
 274        jump_list.dock_menus = actions;
 275        jump_list.recent_workspaces = entries.into();
 276        let dock_menus = jump_list
 277            .dock_menus
 278            .iter()
 279            .map(|menu| (menu.name.clone(), menu.description.clone()))
 280            .collect::<Vec<_>>();
 281        let recent_workspaces = jump_list.recent_workspaces.clone();
 282        self.background_executor.spawn(async move {
 283            update_jump_list(&recent_workspaces, &dock_menus)
 284                .log_err()
 285                .unwrap_or_default()
 286        })
 287    }
 288
 289    fn find_current_active_window(&self) -> Option<HWND> {
 290        let active_window_hwnd = unsafe { GetActiveWindow() };
 291        if active_window_hwnd.is_invalid() {
 292            return None;
 293        }
 294        self.raw_window_handles
 295            .read()
 296            .iter()
 297            .find(|hwnd| hwnd.as_raw() == active_window_hwnd)
 298            .map(|hwnd| hwnd.as_raw())
 299    }
 300
 301    fn begin_vsync_thread(&self) {
 302        let Some(directx_devices) = self.inner.state.directx_devices.borrow().clone() else {
 303            return;
 304        };
 305        let Some(direct_write_text_system) = &self.direct_write_text_system else {
 306            return;
 307        };
 308        let mut directx_device = directx_devices;
 309        let platform_window: SafeHwnd = self.handle.into();
 310        let validation_number = self.inner.validation_number;
 311        let all_windows = Arc::downgrade(&self.raw_window_handles);
 312        let text_system = Arc::downgrade(direct_write_text_system);
 313        let invalidate_devices = self.invalidate_devices.clone();
 314
 315        std::thread::Builder::new()
 316            .name("VSyncProvider".to_owned())
 317            .spawn(move || {
 318                let vsync_provider = VSyncProvider::new();
 319                loop {
 320                    vsync_provider.wait_for_vsync();
 321                    if check_device_lost(&directx_device.device)
 322                        || invalidate_devices.fetch_and(false, Ordering::Acquire)
 323                    {
 324                        if let Err(err) = handle_gpu_device_lost(
 325                            &mut directx_device,
 326                            platform_window.as_raw(),
 327                            validation_number,
 328                            &all_windows,
 329                            &text_system,
 330                        ) {
 331                            panic!("Device lost: {err}");
 332                        }
 333                    }
 334                    let Some(all_windows) = all_windows.upgrade() else {
 335                        break;
 336                    };
 337                    for hwnd in all_windows.read().iter() {
 338                        unsafe {
 339                            let _ = RedrawWindow(Some(hwnd.as_raw()), None, None, RDW_INVALIDATE);
 340                        }
 341                    }
 342                }
 343            })
 344            .unwrap();
 345    }
 346}
 347
 348fn translate_accelerator(msg: &MSG) -> Option<()> {
 349    if msg.message != WM_KEYDOWN && msg.message != WM_SYSKEYDOWN {
 350        return None;
 351    }
 352
 353    let result = unsafe {
 354        SendMessageW(
 355            msg.hwnd,
 356            WM_GPUI_KEYDOWN,
 357            Some(msg.wParam),
 358            Some(msg.lParam),
 359        )
 360    };
 361    (result.0 == 0).then_some(())
 362}
 363
 364impl Platform for WindowsPlatform {
 365    fn background_executor(&self) -> BackgroundExecutor {
 366        self.background_executor.clone()
 367    }
 368
 369    fn foreground_executor(&self) -> ForegroundExecutor {
 370        self.foreground_executor.clone()
 371    }
 372
 373    fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
 374        self.text_system.clone()
 375    }
 376
 377    fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
 378        Box::new(
 379            WindowsKeyboardLayout::new()
 380                .log_err()
 381                .unwrap_or(WindowsKeyboardLayout::unknown()),
 382        )
 383    }
 384
 385    fn keyboard_mapper(&self) -> Rc<dyn PlatformKeyboardMapper> {
 386        Rc::new(WindowsKeyboardMapper::new())
 387    }
 388
 389    fn on_keyboard_layout_change(&self, callback: Box<dyn FnMut()>) {
 390        self.inner
 391            .state
 392            .callbacks
 393            .keyboard_layout_change
 394            .set(Some(callback));
 395    }
 396
 397    fn on_thermal_state_change(&self, _callback: Box<dyn FnMut()>) {}
 398
 399    fn thermal_state(&self) -> ThermalState {
 400        ThermalState::Nominal
 401    }
 402
 403    fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {
 404        on_finish_launching();
 405        if !self.headless {
 406            self.begin_vsync_thread();
 407        }
 408
 409        let mut msg = MSG::default();
 410        unsafe {
 411            while GetMessageW(&mut msg, None, 0, 0).as_bool() {
 412                if translate_accelerator(&msg).is_none() {
 413                    _ = TranslateMessage(&msg);
 414                    DispatchMessageW(&msg);
 415                }
 416            }
 417        }
 418
 419        self.inner
 420            .with_callback(|callbacks| &callbacks.quit, |callback| callback());
 421    }
 422
 423    fn quit(&self) {
 424        self.foreground_executor()
 425            .spawn(async { unsafe { PostQuitMessage(0) } })
 426            .detach();
 427    }
 428
 429    fn restart(&self, binary_path: Option<PathBuf>) {
 430        let pid = std::process::id();
 431        let Some(app_path) = binary_path.or(self.app_path().log_err()) else {
 432            return;
 433        };
 434        let script = format!(
 435            r#"
 436            $pidToWaitFor = {}
 437            $exePath = "{}"
 438
 439            while ($true) {{
 440                $process = Get-Process -Id $pidToWaitFor -ErrorAction SilentlyContinue
 441                if (-not $process) {{
 442                    Start-Process -FilePath $exePath
 443                    break
 444                }}
 445                Start-Sleep -Seconds 0.1
 446            }}
 447            "#,
 448            pid,
 449            app_path.display(),
 450        );
 451
 452        #[allow(
 453            clippy::disallowed_methods,
 454            reason = "We are restarting ourselves, using std command thus is fine"
 455        )] // todo(shell): There might be no powershell on the system
 456        let restart_process =
 457            util::command::new_std_command(util::shell::get_windows_system_shell())
 458                .arg("-command")
 459                .arg(script)
 460                .spawn();
 461
 462        match restart_process {
 463            Ok(_) => self.quit(),
 464            Err(e) => log::error!("failed to spawn restart script: {:?}", e),
 465        }
 466    }
 467
 468    fn activate(&self, _ignoring_other_apps: bool) {}
 469
 470    fn hide(&self) {}
 471
 472    // todo(windows)
 473    fn hide_other_apps(&self) {
 474        unimplemented!()
 475    }
 476
 477    // todo(windows)
 478    fn unhide_other_apps(&self) {
 479        unimplemented!()
 480    }
 481
 482    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
 483        WindowsDisplay::displays()
 484    }
 485
 486    fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
 487        WindowsDisplay::primary_monitor().map(|display| Rc::new(display) as Rc<dyn PlatformDisplay>)
 488    }
 489
 490    #[cfg(feature = "screen-capture")]
 491    fn is_screen_capture_supported(&self) -> bool {
 492        true
 493    }
 494
 495    #[cfg(feature = "screen-capture")]
 496    fn screen_capture_sources(
 497        &self,
 498    ) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
 499        crate::platform::scap_screen_capture::scap_screen_sources(&self.foreground_executor)
 500    }
 501
 502    fn active_window(&self) -> Option<AnyWindowHandle> {
 503        let active_window_hwnd = unsafe { GetActiveWindow() };
 504        self.window_from_hwnd(active_window_hwnd)
 505            .map(|inner| inner.handle)
 506    }
 507
 508    fn open_window(
 509        &self,
 510        handle: AnyWindowHandle,
 511        options: WindowParams,
 512    ) -> Result<Box<dyn PlatformWindow>> {
 513        let window = WindowsWindow::new(handle, options, self.generate_creation_info())?;
 514        let handle = window.get_raw_handle();
 515        self.raw_window_handles.write().push(handle.into());
 516
 517        Ok(Box::new(window))
 518    }
 519
 520    fn window_appearance(&self) -> WindowAppearance {
 521        system_appearance().log_err().unwrap_or_default()
 522    }
 523
 524    fn open_url(&self, url: &str) {
 525        if url.is_empty() {
 526            return;
 527        }
 528        let url_string = url.to_string();
 529        self.background_executor()
 530            .spawn(async move {
 531                open_target(&url_string)
 532                    .with_context(|| format!("Opening url: {}", url_string))
 533                    .log_err();
 534            })
 535            .detach();
 536    }
 537
 538    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
 539        self.inner.state.callbacks.open_urls.set(Some(callback));
 540    }
 541
 542    fn prompt_for_paths(
 543        &self,
 544        options: PathPromptOptions,
 545    ) -> Receiver<Result<Option<Vec<PathBuf>>>> {
 546        let (tx, rx) = oneshot::channel();
 547        let window = self.find_current_active_window();
 548        self.foreground_executor()
 549            .spawn(async move {
 550                let _ = tx.send(file_open_dialog(options, window));
 551            })
 552            .detach();
 553
 554        rx
 555    }
 556
 557    fn prompt_for_new_path(
 558        &self,
 559        directory: &Path,
 560        suggested_name: Option<&str>,
 561    ) -> Receiver<Result<Option<PathBuf>>> {
 562        let directory = directory.to_owned();
 563        let suggested_name = suggested_name.map(|s| s.to_owned());
 564        let (tx, rx) = oneshot::channel();
 565        let window = self.find_current_active_window();
 566        self.foreground_executor()
 567            .spawn(async move {
 568                let _ = tx.send(file_save_dialog(directory, suggested_name, window));
 569            })
 570            .detach();
 571
 572        rx
 573    }
 574
 575    fn can_select_mixed_files_and_dirs(&self) -> bool {
 576        // The FOS_PICKFOLDERS flag toggles between "only files" and "only folders".
 577        false
 578    }
 579
 580    fn reveal_path(&self, path: &Path) {
 581        if path.as_os_str().is_empty() {
 582            return;
 583        }
 584        let path = path.to_path_buf();
 585        self.background_executor()
 586            .spawn(async move {
 587                open_target_in_explorer(&path)
 588                    .with_context(|| format!("Revealing path {} in explorer", path.display()))
 589                    .log_err();
 590            })
 591            .detach();
 592    }
 593
 594    fn open_with_system(&self, path: &Path) {
 595        if path.as_os_str().is_empty() {
 596            return;
 597        }
 598        let path = path.to_path_buf();
 599        self.background_executor()
 600            .spawn(async move {
 601                open_target(&path)
 602                    .with_context(|| format!("Opening {} with system", path.display()))
 603                    .log_err();
 604            })
 605            .detach();
 606    }
 607
 608    fn on_quit(&self, callback: Box<dyn FnMut()>) {
 609        self.inner.state.callbacks.quit.set(Some(callback));
 610    }
 611
 612    fn on_reopen(&self, callback: Box<dyn FnMut()>) {
 613        self.inner.state.callbacks.reopen.set(Some(callback));
 614    }
 615
 616    fn set_menus(&self, menus: Vec<Menu>, _keymap: &Keymap) {
 617        *self.inner.state.menus.borrow_mut() = menus.into_iter().map(|menu| menu.owned()).collect();
 618    }
 619
 620    fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
 621        Some(self.inner.state.menus.borrow().clone())
 622    }
 623
 624    fn set_dock_menu(&self, menus: Vec<MenuItem>, _keymap: &Keymap) {
 625        self.set_dock_menus(menus);
 626    }
 627
 628    fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
 629        self.inner
 630            .state
 631            .callbacks
 632            .app_menu_action
 633            .set(Some(callback));
 634    }
 635
 636    fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
 637        self.inner
 638            .state
 639            .callbacks
 640            .will_open_app_menu
 641            .set(Some(callback));
 642    }
 643
 644    fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
 645        self.inner
 646            .state
 647            .callbacks
 648            .validate_app_menu_command
 649            .set(Some(callback));
 650    }
 651
 652    fn app_path(&self) -> Result<PathBuf> {
 653        Ok(std::env::current_exe()?)
 654    }
 655
 656    // todo(windows)
 657    fn path_for_auxiliary_executable(&self, _name: &str) -> Result<PathBuf> {
 658        anyhow::bail!("not yet implemented");
 659    }
 660
 661    fn set_cursor_style(&self, style: CursorStyle) {
 662        let hcursor = load_cursor(style);
 663        if self.inner.state.current_cursor.get().map(|c| c.0) != hcursor.map(|c| c.0) {
 664            self.post_message(
 665                WM_GPUI_CURSOR_STYLE_CHANGED,
 666                WPARAM(0),
 667                LPARAM(hcursor.map_or(0, |c| c.0 as isize)),
 668            );
 669            self.inner.state.current_cursor.set(hcursor);
 670        }
 671    }
 672
 673    fn should_auto_hide_scrollbars(&self) -> bool {
 674        should_auto_hide_scrollbars().log_err().unwrap_or(false)
 675    }
 676
 677    fn write_to_clipboard(&self, item: ClipboardItem) {
 678        write_to_clipboard(item);
 679    }
 680
 681    fn read_from_clipboard(&self) -> Option<ClipboardItem> {
 682        read_from_clipboard()
 683    }
 684
 685    fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>> {
 686        let mut password = password.to_vec();
 687        let mut username = username.encode_utf16().chain(Some(0)).collect_vec();
 688        let mut target_name = windows_credentials_target_name(url)
 689            .encode_utf16()
 690            .chain(Some(0))
 691            .collect_vec();
 692        self.foreground_executor().spawn(async move {
 693            let credentials = CREDENTIALW {
 694                LastWritten: unsafe { GetSystemTimeAsFileTime() },
 695                Flags: CRED_FLAGS(0),
 696                Type: CRED_TYPE_GENERIC,
 697                TargetName: PWSTR::from_raw(target_name.as_mut_ptr()),
 698                CredentialBlobSize: password.len() as u32,
 699                CredentialBlob: password.as_ptr() as *mut _,
 700                Persist: CRED_PERSIST_LOCAL_MACHINE,
 701                UserName: PWSTR::from_raw(username.as_mut_ptr()),
 702                ..CREDENTIALW::default()
 703            };
 704            unsafe {
 705                CredWriteW(&credentials, 0).map_err(|err| {
 706                    anyhow!(
 707                        "Failed to write credentials to Windows Credential Manager: {}",
 708                        err,
 709                    )
 710                })?;
 711            }
 712            Ok(())
 713        })
 714    }
 715
 716    fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
 717        let mut target_name = windows_credentials_target_name(url)
 718            .encode_utf16()
 719            .chain(Some(0))
 720            .collect_vec();
 721        self.foreground_executor().spawn(async move {
 722            let mut credentials: *mut CREDENTIALW = std::ptr::null_mut();
 723            let result = unsafe {
 724                CredReadW(
 725                    PCWSTR::from_raw(target_name.as_ptr()),
 726                    CRED_TYPE_GENERIC,
 727                    None,
 728                    &mut credentials,
 729                )
 730            };
 731
 732            if let Err(err) = result {
 733                // ERROR_NOT_FOUND means the credential doesn't exist.
 734                // Return Ok(None) to match macOS and Linux behavior.
 735                if err.code() == ERROR_NOT_FOUND.to_hresult() {
 736                    return Ok(None);
 737                }
 738                return Err(err.into());
 739            }
 740
 741            if credentials.is_null() {
 742                Ok(None)
 743            } else {
 744                let username: String = unsafe { (*credentials).UserName.to_string()? };
 745                let credential_blob = unsafe {
 746                    std::slice::from_raw_parts(
 747                        (*credentials).CredentialBlob,
 748                        (*credentials).CredentialBlobSize as usize,
 749                    )
 750                };
 751                let password = credential_blob.to_vec();
 752                unsafe { CredFree(credentials as *const _ as _) };
 753                Ok(Some((username, password)))
 754            }
 755        })
 756    }
 757
 758    fn delete_credentials(&self, url: &str) -> Task<Result<()>> {
 759        let mut target_name = windows_credentials_target_name(url)
 760            .encode_utf16()
 761            .chain(Some(0))
 762            .collect_vec();
 763        self.foreground_executor().spawn(async move {
 764            unsafe {
 765                CredDeleteW(
 766                    PCWSTR::from_raw(target_name.as_ptr()),
 767                    CRED_TYPE_GENERIC,
 768                    None,
 769                )?
 770            };
 771            Ok(())
 772        })
 773    }
 774
 775    fn register_url_scheme(&self, _: &str) -> Task<anyhow::Result<()>> {
 776        Task::ready(Err(anyhow!("register_url_scheme unimplemented")))
 777    }
 778
 779    fn perform_dock_menu_action(&self, action: usize) {
 780        unsafe {
 781            PostMessageW(
 782                Some(self.handle),
 783                WM_GPUI_DOCK_MENU_ACTION,
 784                WPARAM(self.inner.validation_number),
 785                LPARAM(action as isize),
 786            )
 787            .log_err();
 788        }
 789    }
 790
 791    fn update_jump_list(
 792        &self,
 793        menus: Vec<MenuItem>,
 794        entries: Vec<SmallVec<[PathBuf; 2]>>,
 795    ) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
 796        self.update_jump_list(menus, entries)
 797    }
 798}
 799
 800impl WindowsPlatformInner {
 801    fn new(context: &mut PlatformWindowCreateContext) -> Result<Rc<Self>> {
 802        let state = WindowsPlatformState::new(context.directx_devices.take());
 803        Ok(Rc::new(Self {
 804            state,
 805            raw_window_handles: context.raw_window_handles.clone(),
 806            dispatcher: context
 807                .dispatcher
 808                .as_ref()
 809                .context("missing dispatcher")?
 810                .clone(),
 811            validation_number: context.validation_number,
 812            main_receiver: context
 813                .main_receiver
 814                .take()
 815                .context("missing main receiver")?,
 816        }))
 817    }
 818
 819    /// Calls `project` to project to the corresponding callback field, removes it from callbacks, calls `f` with the callback and then puts the callback back.
 820    fn with_callback<T>(
 821        &self,
 822        project: impl Fn(&PlatformCallbacks) -> &Cell<Option<T>>,
 823        f: impl FnOnce(&mut T),
 824    ) {
 825        let callback = project(&self.state.callbacks).take();
 826        if let Some(mut callback) = callback {
 827            f(&mut callback);
 828            project(&self.state.callbacks).set(Some(callback));
 829        }
 830    }
 831
 832    fn handle_msg(
 833        self: &Rc<Self>,
 834        handle: HWND,
 835        msg: u32,
 836        wparam: WPARAM,
 837        lparam: LPARAM,
 838    ) -> LRESULT {
 839        let handled = match msg {
 840            WM_GPUI_CLOSE_ONE_WINDOW
 841            | WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD
 842            | WM_GPUI_DOCK_MENU_ACTION
 843            | WM_GPUI_KEYBOARD_LAYOUT_CHANGED
 844            | WM_GPUI_GPU_DEVICE_LOST => self.handle_gpui_events(msg, wparam, lparam),
 845            _ => None,
 846        };
 847        if let Some(result) = handled {
 848            LRESULT(result)
 849        } else {
 850            unsafe { DefWindowProcW(handle, msg, wparam, lparam) }
 851        }
 852    }
 853
 854    fn handle_gpui_events(&self, message: u32, wparam: WPARAM, lparam: LPARAM) -> Option<isize> {
 855        if wparam.0 != self.validation_number {
 856            log::error!("Wrong validation number while processing message: {message}");
 857            return None;
 858        }
 859        match message {
 860            WM_GPUI_CLOSE_ONE_WINDOW => {
 861                self.close_one_window(HWND(lparam.0 as _));
 862                Some(0)
 863            }
 864            WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD => self.run_foreground_task(),
 865            WM_GPUI_DOCK_MENU_ACTION => self.handle_dock_action_event(lparam.0 as _),
 866            WM_GPUI_KEYBOARD_LAYOUT_CHANGED => self.handle_keyboard_layout_change(),
 867            WM_GPUI_GPU_DEVICE_LOST => self.handle_device_lost(lparam),
 868            _ => unreachable!(),
 869        }
 870    }
 871
 872    fn close_one_window(&self, target_window: HWND) -> bool {
 873        let Some(all_windows) = self.raw_window_handles.upgrade() else {
 874            log::error!("Failed to upgrade raw window handles");
 875            return false;
 876        };
 877        let mut lock = all_windows.write();
 878        let index = lock
 879            .iter()
 880            .position(|handle| handle.as_raw() == target_window)
 881            .unwrap();
 882        lock.remove(index);
 883
 884        lock.is_empty()
 885    }
 886
 887    #[inline]
 888    fn run_foreground_task(&self) -> Option<isize> {
 889        const MAIN_TASK_TIMEOUT: u128 = 10;
 890
 891        let start = std::time::Instant::now();
 892        'tasks: loop {
 893            'timeout_loop: loop {
 894                if start.elapsed().as_millis() >= MAIN_TASK_TIMEOUT {
 895                    log::debug!("foreground task timeout reached");
 896                    // we spent our budget on gpui tasks, we likely have a lot of work queued so drain system events first to stay responsive
 897                    // then quit out of foreground work to allow us to process other gpui events first before returning back to foreground task work
 898                    // if we don't we might not for example process window quit events
 899                    let mut msg = MSG::default();
 900                    let process_message = |msg: &_| {
 901                        if translate_accelerator(msg).is_none() {
 902                            _ = unsafe { TranslateMessage(msg) };
 903                            unsafe { DispatchMessageW(msg) };
 904                        }
 905                    };
 906                    let peek_msg = |msg: &mut _, msg_kind| unsafe {
 907                        PeekMessageW(msg, None, 0, 0, PM_REMOVE | msg_kind).as_bool()
 908                    };
 909                    // We need to process a paint message here as otherwise we will re-enter `run_foreground_task` before painting if we have work remaining.
 910                    // The reason for this is that windows prefers custom application message processing over system messages.
 911                    if peek_msg(&mut msg, PM_QS_PAINT) {
 912                        process_message(&msg);
 913                    }
 914                    while peek_msg(&mut msg, PM_QS_INPUT) {
 915                        process_message(&msg);
 916                    }
 917                    // Allow the main loop to process other gpui events before going back into `run_foreground_task`
 918                    unsafe {
 919                        if let Err(_) = PostMessageW(
 920                            Some(self.dispatcher.platform_window_handle.as_raw()),
 921                            WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD,
 922                            WPARAM(self.validation_number),
 923                            LPARAM(0),
 924                        ) {
 925                            self.dispatcher.wake_posted.store(false, Ordering::Release);
 926                        };
 927                    }
 928                    break 'tasks;
 929                }
 930                let mut main_receiver = self.main_receiver.clone();
 931                match main_receiver.try_pop() {
 932                    Ok(Some(runnable)) => WindowsDispatcher::execute_runnable(runnable),
 933                    _ => break 'timeout_loop,
 934                }
 935            }
 936
 937            // Someone could enqueue a Runnable here. The flag is still true, so they will not PostMessage.
 938            // We need to check for those Runnables after we clear the flag.
 939            self.dispatcher.wake_posted.store(false, Ordering::Release);
 940            let mut main_receiver = self.main_receiver.clone();
 941            match main_receiver.try_pop() {
 942                Ok(Some(runnable)) => {
 943                    self.dispatcher.wake_posted.store(true, Ordering::Release);
 944
 945                    WindowsDispatcher::execute_runnable(runnable);
 946                }
 947                _ => break 'tasks,
 948            }
 949        }
 950
 951        Some(0)
 952    }
 953
 954    fn handle_dock_action_event(&self, action_idx: usize) -> Option<isize> {
 955        let Some(action) = self
 956            .state
 957            .jump_list
 958            .borrow()
 959            .dock_menus
 960            .get(action_idx)
 961            .map(|dock_menu| dock_menu.action.boxed_clone())
 962        else {
 963            log::error!("Dock menu for index {action_idx} not found");
 964            return Some(1);
 965        };
 966        self.with_callback(
 967            |callbacks| &callbacks.app_menu_action,
 968            |callback| callback(&*action),
 969        );
 970        Some(0)
 971    }
 972
 973    fn handle_keyboard_layout_change(&self) -> Option<isize> {
 974        self.with_callback(
 975            |callbacks| &callbacks.keyboard_layout_change,
 976            |callback| callback(),
 977        );
 978        Some(0)
 979    }
 980
 981    fn handle_device_lost(&self, lparam: LPARAM) -> Option<isize> {
 982        let directx_devices = lparam.0 as *const DirectXDevices;
 983        let directx_devices = unsafe { &*directx_devices };
 984        self.state.directx_devices.borrow_mut().take();
 985        *self.state.directx_devices.borrow_mut() = Some(directx_devices.clone());
 986
 987        Some(0)
 988    }
 989}
 990
 991impl Drop for WindowsPlatform {
 992    fn drop(&mut self) {
 993        unsafe {
 994            timeEndPeriod(1);
 995            DestroyWindow(self.handle)
 996                .context("Destroying platform window")
 997                .log_err();
 998            OleUninitialize();
 999        }
1000    }
1001}
1002
1003pub(crate) struct WindowCreationInfo {
1004    pub(crate) icon: HICON,
1005    pub(crate) executor: ForegroundExecutor,
1006    pub(crate) current_cursor: Option<HCURSOR>,
1007    pub(crate) windows_version: WindowsVersion,
1008    pub(crate) drop_target_helper: IDropTargetHelper,
1009    pub(crate) validation_number: usize,
1010    pub(crate) main_receiver: PriorityQueueReceiver<RunnableVariant>,
1011    pub(crate) platform_window_handle: HWND,
1012    pub(crate) disable_direct_composition: bool,
1013    pub(crate) directx_devices: DirectXDevices,
1014    /// Flag to instruct the `VSyncProvider` thread to invalidate the directx devices
1015    /// as resizing them has failed, causing us to have lost at least the render target.
1016    pub(crate) invalidate_devices: Arc<AtomicBool>,
1017}
1018
1019struct PlatformWindowCreateContext {
1020    inner: Option<Result<Rc<WindowsPlatformInner>>>,
1021    raw_window_handles: std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
1022    validation_number: usize,
1023    main_sender: Option<PriorityQueueSender<RunnableVariant>>,
1024    main_receiver: Option<PriorityQueueReceiver<RunnableVariant>>,
1025    directx_devices: Option<DirectXDevices>,
1026    dispatcher: Option<Arc<WindowsDispatcher>>,
1027}
1028
1029fn open_target(target: impl AsRef<OsStr>) -> Result<()> {
1030    let target = target.as_ref();
1031    let ret = unsafe {
1032        ShellExecuteW(
1033            None,
1034            windows::core::w!("open"),
1035            &HSTRING::from(target),
1036            None,
1037            None,
1038            SW_SHOWDEFAULT,
1039        )
1040    };
1041    if ret.0 as isize <= 32 {
1042        Err(anyhow::anyhow!(
1043            "Unable to open target: {}",
1044            std::io::Error::last_os_error()
1045        ))
1046    } else {
1047        Ok(())
1048    }
1049}
1050
1051fn open_target_in_explorer(target: &Path) -> Result<()> {
1052    let dir = target.parent().context("No parent folder found")?;
1053    let desktop = unsafe { SHGetDesktopFolder()? };
1054
1055    let mut dir_item = std::ptr::null_mut();
1056    unsafe {
1057        desktop.ParseDisplayName(
1058            HWND::default(),
1059            None,
1060            &HSTRING::from(dir),
1061            None,
1062            &mut dir_item,
1063            std::ptr::null_mut(),
1064        )?;
1065    }
1066
1067    let mut file_item = std::ptr::null_mut();
1068    unsafe {
1069        desktop.ParseDisplayName(
1070            HWND::default(),
1071            None,
1072            &HSTRING::from(target),
1073            None,
1074            &mut file_item,
1075            std::ptr::null_mut(),
1076        )?;
1077    }
1078
1079    let highlight = [file_item as *const _];
1080    unsafe { SHOpenFolderAndSelectItems(dir_item as _, Some(&highlight), 0) }.or_else(|err| {
1081        if err.code().0 == ERROR_FILE_NOT_FOUND.0 as i32 {
1082            // On some systems, the above call mysteriously fails with "file not
1083            // found" even though the file is there.  In these cases, ShellExecute()
1084            // seems to work as a fallback (although it won't select the file).
1085            open_target(dir).context("Opening target parent folder")
1086        } else {
1087            Err(anyhow::anyhow!("Can not open target path: {}", err))
1088        }
1089    })
1090}
1091
1092fn file_open_dialog(
1093    options: PathPromptOptions,
1094    window: Option<HWND>,
1095) -> Result<Option<Vec<PathBuf>>> {
1096    let folder_dialog: IFileOpenDialog =
1097        unsafe { CoCreateInstance(&FileOpenDialog, None, CLSCTX_ALL)? };
1098
1099    let mut dialog_options = FOS_FILEMUSTEXIST;
1100    if options.multiple {
1101        dialog_options |= FOS_ALLOWMULTISELECT;
1102    }
1103    if options.directories {
1104        dialog_options |= FOS_PICKFOLDERS;
1105    }
1106
1107    unsafe {
1108        folder_dialog.SetOptions(dialog_options)?;
1109
1110        if let Some(prompt) = options.prompt {
1111            let prompt: &str = &prompt;
1112            folder_dialog.SetOkButtonLabel(&HSTRING::from(prompt))?;
1113        }
1114
1115        if folder_dialog.Show(window).is_err() {
1116            // User cancelled
1117            return Ok(None);
1118        }
1119    }
1120
1121    let results = unsafe { folder_dialog.GetResults()? };
1122    let file_count = unsafe { results.GetCount()? };
1123    if file_count == 0 {
1124        return Ok(None);
1125    }
1126
1127    let mut paths = Vec::with_capacity(file_count as usize);
1128    for i in 0..file_count {
1129        let item = unsafe { results.GetItemAt(i)? };
1130        let path = unsafe { item.GetDisplayName(SIGDN_FILESYSPATH)?.to_string()? };
1131        paths.push(PathBuf::from(path));
1132    }
1133
1134    Ok(Some(paths))
1135}
1136
1137fn file_save_dialog(
1138    directory: PathBuf,
1139    suggested_name: Option<String>,
1140    window: Option<HWND>,
1141) -> Result<Option<PathBuf>> {
1142    let dialog: IFileSaveDialog = unsafe { CoCreateInstance(&FileSaveDialog, None, CLSCTX_ALL)? };
1143    if !directory.to_string_lossy().is_empty()
1144        && let Some(full_path) = directory
1145            .canonicalize()
1146            .context("failed to canonicalize directory")
1147            .log_err()
1148    {
1149        let full_path = SanitizedPath::new(&full_path);
1150        let full_path_string = full_path.to_string();
1151        let path_item: IShellItem =
1152            unsafe { SHCreateItemFromParsingName(&HSTRING::from(full_path_string), None)? };
1153        unsafe {
1154            dialog
1155                .SetFolder(&path_item)
1156                .context("failed to set dialog folder")
1157                .log_err()
1158        };
1159    }
1160
1161    if let Some(suggested_name) = suggested_name {
1162        unsafe {
1163            dialog
1164                .SetFileName(&HSTRING::from(suggested_name))
1165                .context("failed to set file name")
1166                .log_err()
1167        };
1168    }
1169
1170    unsafe {
1171        dialog.SetFileTypes(&[Common::COMDLG_FILTERSPEC {
1172            pszName: windows::core::w!("All files"),
1173            pszSpec: windows::core::w!("*.*"),
1174        }])?;
1175        if dialog.Show(window).is_err() {
1176            // User cancelled
1177            return Ok(None);
1178        }
1179    }
1180    let shell_item = unsafe { dialog.GetResult()? };
1181    let file_path_string = unsafe {
1182        let pwstr = shell_item.GetDisplayName(SIGDN_FILESYSPATH)?;
1183        let string = pwstr.to_string()?;
1184        CoTaskMemFree(Some(pwstr.0 as _));
1185        string
1186    };
1187    Ok(Some(PathBuf::from(file_path_string)))
1188}
1189
1190fn load_icon() -> Result<HICON> {
1191    let module = unsafe { GetModuleHandleW(None).context("unable to get module handle")? };
1192    let handle = unsafe {
1193        LoadImageW(
1194            Some(module.into()),
1195            windows::core::PCWSTR(1 as _),
1196            IMAGE_ICON,
1197            0,
1198            0,
1199            LR_DEFAULTSIZE | LR_SHARED,
1200        )
1201        .context("unable to load icon file")?
1202    };
1203    Ok(HICON(handle.0))
1204}
1205
1206#[inline]
1207fn should_auto_hide_scrollbars() -> Result<bool> {
1208    let ui_settings = UISettings::new()?;
1209    Ok(ui_settings.AutoHideScrollBars()?)
1210}
1211
1212fn check_device_lost(device: &ID3D11Device) -> bool {
1213    let device_state = unsafe { device.GetDeviceRemovedReason() };
1214    match device_state {
1215        Ok(_) => false,
1216        Err(err) => {
1217            log::error!("DirectX device lost detected: {:?}", err);
1218            true
1219        }
1220    }
1221}
1222
1223fn handle_gpu_device_lost(
1224    directx_devices: &mut DirectXDevices,
1225    platform_window: HWND,
1226    validation_number: usize,
1227    all_windows: &std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
1228    text_system: &std::sync::Weak<DirectWriteTextSystem>,
1229) -> Result<()> {
1230    // Here we wait a bit to ensure the system has time to recover from the device lost state.
1231    // If we don't wait, the final drawing result will be blank.
1232    std::thread::sleep(std::time::Duration::from_millis(350));
1233
1234    *directx_devices = try_to_recover_from_device_lost(|| {
1235        DirectXDevices::new().context("Failed to recreate new DirectX devices after device lost")
1236    })?;
1237    log::info!("DirectX devices successfully recreated.");
1238
1239    let lparam = LPARAM(directx_devices as *const _ as _);
1240    unsafe {
1241        SendMessageW(
1242            platform_window,
1243            WM_GPUI_GPU_DEVICE_LOST,
1244            Some(WPARAM(validation_number)),
1245            Some(lparam),
1246        );
1247    }
1248
1249    if let Some(text_system) = text_system.upgrade() {
1250        text_system.handle_gpu_lost(&directx_devices)?;
1251    }
1252    if let Some(all_windows) = all_windows.upgrade() {
1253        for window in all_windows.read().iter() {
1254            unsafe {
1255                SendMessageW(
1256                    window.as_raw(),
1257                    WM_GPUI_GPU_DEVICE_LOST,
1258                    Some(WPARAM(validation_number)),
1259                    Some(lparam),
1260                );
1261            }
1262        }
1263        std::thread::sleep(std::time::Duration::from_millis(200));
1264        for window in all_windows.read().iter() {
1265            unsafe {
1266                SendMessageW(
1267                    window.as_raw(),
1268                    WM_GPUI_FORCE_UPDATE_WINDOW,
1269                    Some(WPARAM(validation_number)),
1270                    None,
1271                );
1272            }
1273        }
1274    }
1275    Ok(())
1276}
1277
1278const PLATFORM_WINDOW_CLASS_NAME: PCWSTR = w!("Zed::PlatformWindow");
1279
1280fn register_platform_window_class() {
1281    let wc = WNDCLASSW {
1282        lpfnWndProc: Some(window_procedure),
1283        lpszClassName: PCWSTR(PLATFORM_WINDOW_CLASS_NAME.as_ptr()),
1284        ..Default::default()
1285    };
1286    unsafe { RegisterClassW(&wc) };
1287}
1288
1289unsafe extern "system" fn window_procedure(
1290    hwnd: HWND,
1291    msg: u32,
1292    wparam: WPARAM,
1293    lparam: LPARAM,
1294) -> LRESULT {
1295    if msg == WM_NCCREATE {
1296        let params = unsafe { &*(lparam.0 as *const CREATESTRUCTW) };
1297        let creation_context = params.lpCreateParams as *mut PlatformWindowCreateContext;
1298        let creation_context = unsafe { &mut *creation_context };
1299
1300        let Some(main_sender) = creation_context.main_sender.take() else {
1301            creation_context.inner = Some(Err(anyhow!("missing main sender")));
1302            return LRESULT(0);
1303        };
1304        creation_context.dispatcher = Some(Arc::new(WindowsDispatcher::new(
1305            main_sender,
1306            hwnd,
1307            creation_context.validation_number,
1308        )));
1309
1310        return match WindowsPlatformInner::new(creation_context) {
1311            Ok(inner) => {
1312                let weak = Box::new(Rc::downgrade(&inner));
1313                unsafe { set_window_long(hwnd, GWLP_USERDATA, Box::into_raw(weak) as isize) };
1314                creation_context.inner = Some(Ok(inner));
1315                unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
1316            }
1317            Err(error) => {
1318                creation_context.inner = Some(Err(error));
1319                LRESULT(0)
1320            }
1321        };
1322    }
1323
1324    let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsPlatformInner>;
1325    if ptr.is_null() {
1326        return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) };
1327    }
1328    let inner = unsafe { &*ptr };
1329    let result = if let Some(inner) = inner.upgrade() {
1330        inner.handle_msg(hwnd, msg, wparam, lparam)
1331    } else {
1332        unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
1333    };
1334
1335    if msg == WM_NCDESTROY {
1336        unsafe { set_window_long(hwnd, GWLP_USERDATA, 0) };
1337        unsafe { drop(Box::from_raw(ptr)) };
1338    }
1339
1340    result
1341}
1342
1343#[cfg(test)]
1344mod tests {
1345    use crate::{ClipboardItem, read_from_clipboard, write_to_clipboard};
1346
1347    #[test]
1348    fn test_clipboard() {
1349        let item = ClipboardItem::new_string("你好,我是张小白".to_string());
1350        write_to_clipboard(item.clone());
1351        assert_eq!(read_from_clipboard(), Some(item));
1352
1353        let item = ClipboardItem::new_string("12345".to_string());
1354        write_to_clipboard(item.clone());
1355        assert_eq!(read_from_clipboard(), Some(item));
1356
1357        let item = ClipboardItem::new_string_with_json_metadata("abcdef".to_string(), vec![3, 4]);
1358        write_to_clipboard(item.clone());
1359        assert_eq!(read_from_clipboard(), Some(item));
1360    }
1361}