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