844 lines · 26.4 KB · Rust
RawDownload
use std::{    cell::RefCell,    mem::ManuallyDrop,    path::{Path, PathBuf},    rc::Rc,    sync::Arc,}; use ::util::{paths::SanitizedPath, ResultExt};use anyhow::{anyhow, Context as _, Result};use async_task::Runnable;use futures::channel::oneshot::{self, Receiver};use itertools::Itertools;use parking_lot::RwLock;use smallvec::SmallVec;use windows::{    core::*,    Win32::{        Foundation::*,        Graphics::{            Gdi::*,            Imaging::{CLSID_WICImagingFactory, IWICImagingFactory},        },        Security::Credentials::*,        System::{Com::*, LibraryLoader::*, Ole::*, SystemInformation::*, Threading::*},        UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*},    },    UI::{        StartScreen::{JumpList, JumpListItem},        ViewManagement::UISettings,    },}; use crate::{platform::blade::BladeContext, *}; pub(crate) struct WindowsPlatform {    state: RefCell<WindowsPlatformState>,    raw_window_handles: RwLock<SmallVec<[HWND; 4]>>,    gpu_context: BladeContext,    // The below members will never change throughout the entire lifecycle of the app.    icon: HICON,    main_receiver: flume::Receiver<Runnable>,    background_executor: BackgroundExecutor,    foreground_executor: ForegroundExecutor,    text_system: Arc<DirectWriteTextSystem>,    windows_version: WindowsVersion,    bitmap_factory: ManuallyDrop<IWICImagingFactory>,    validation_number: usize,    main_thread_id_win32: u32,} pub(crate) struct WindowsPlatformState {    callbacks: PlatformCallbacks,    menus: Vec<OwnedMenu>,    dock_menu_actions: Vec<Box<dyn Action>>,    // NOTE: standard cursor handles don't need to close.    pub(crate) current_cursor: HCURSOR,} #[derive(Default)]struct PlatformCallbacks {    open_urls: Option<Box<dyn FnMut(Vec<String>)>>,    quit: Option<Box<dyn FnMut()>>,    reopen: Option<Box<dyn FnMut()>>,    app_menu_action: Option<Box<dyn FnMut(&dyn Action)>>,    will_open_app_menu: Option<Box<dyn FnMut()>>,    validate_app_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,} impl WindowsPlatformState {    fn new() -> Self {        let callbacks = PlatformCallbacks::default();        let dock_menu_actions = Vec::new();        let current_cursor = load_cursor(CursorStyle::Arrow);         Self {            callbacks,            dock_menu_actions,            current_cursor,            menus: Vec::new(),        }    }} impl WindowsPlatform {    pub(crate) fn new() -> Self {        unsafe {            OleInitialize(None).expect("unable to initialize Windows OLE");        }        let (main_sender, main_receiver) = flume::unbounded::<Runnable>();        let main_thread_id_win32 = unsafe { GetCurrentThreadId() };        let validation_number = rand::random::<usize>();        let dispatcher = Arc::new(WindowsDispatcher::new(            main_sender,            main_thread_id_win32,            validation_number,        ));        let background_executor = BackgroundExecutor::new(dispatcher.clone());        let foreground_executor = ForegroundExecutor::new(dispatcher);        let bitmap_factory = ManuallyDrop::new(unsafe {            CoCreateInstance(&CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER)                .expect("Error creating bitmap factory.")        });        let text_system = Arc::new(            DirectWriteTextSystem::new(&bitmap_factory)                .expect("Error creating DirectWriteTextSystem"),        );        let icon = load_icon().unwrap_or_default();        let state = RefCell::new(WindowsPlatformState::new());        let raw_window_handles = RwLock::new(SmallVec::new());        let gpu_context = BladeContext::new().expect("Unable to init GPU context");        let windows_version = WindowsVersion::new().expect("Error retrieve windows version");         Self {            state,            raw_window_handles,            gpu_context,            icon,            main_receiver,            background_executor,            foreground_executor,            text_system,            windows_version,            bitmap_factory,            validation_number,            main_thread_id_win32,        }    }     fn redraw_all(&self) {        for handle in self.raw_window_handles.read().iter() {            unsafe {                RedrawWindow(                    *handle,                    None,                    HRGN::default(),                    RDW_INVALIDATE | RDW_UPDATENOW,                )                .ok()                .log_err();            }        }    }     pub fn try_get_windows_inner_from_hwnd(&self, hwnd: HWND) -> Option<Rc<WindowsWindowStatePtr>> {        self.raw_window_handles            .read()            .iter()            .find(|entry| *entry == &hwnd)            .and_then(|hwnd| try_get_window_inner(*hwnd))    }     #[inline]    fn post_message(&self, message: u32, wparam: WPARAM, lparam: LPARAM) {        self.raw_window_handles            .read()            .iter()            .for_each(|handle| unsafe {                PostMessageW(*handle, message, wparam, lparam).log_err();            });    }     fn close_one_window(&self, target_window: HWND) -> bool {        let mut lock = self.raw_window_handles.write();        let index = lock            .iter()            .position(|handle| *handle == target_window)            .unwrap();        lock.remove(index);         lock.is_empty()    }     #[inline]    fn run_foreground_task(&self) {        for runnable in self.main_receiver.drain() {            runnable.run();        }    }     fn generate_creation_info(&self) -> WindowCreationInfo {        WindowCreationInfo {            icon: self.icon,            executor: self.foreground_executor.clone(),            current_cursor: self.state.borrow().current_cursor,            windows_version: self.windows_version,            validation_number: self.validation_number,            main_receiver: self.main_receiver.clone(),            main_thread_id_win32: self.main_thread_id_win32,        }    }     fn handle_dock_action_event(&self, action_idx: usize) {        let mut lock = self.state.borrow_mut();        if let Some(mut callback) = lock.callbacks.app_menu_action.take() {            let Some(action) = lock                .dock_menu_actions                .get(action_idx)                .map(|action| action.boxed_clone())            else {                lock.callbacks.app_menu_action = Some(callback);                log::error!("Dock menu for index {action_idx} not found");                return;            };            drop(lock);            callback(&*action);            self.state.borrow_mut().callbacks.app_menu_action = Some(callback);        }    }     // Returns true if the app should quit.    fn handle_events(&self) -> bool {        let mut msg = MSG::default();        unsafe {            while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() {                match msg.message {                    WM_QUIT => return true,                    WM_GPUI_CLOSE_ONE_WINDOW                    | WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD                    | WM_GPUI_DOCK_MENU_ACTION => {                        if self.handle_gpui_evnets(msg.message, msg.wParam, msg.lParam, &msg) {                            return true;                        }                    }                    _ => {                        // todo(windows)                        // crate `windows 0.56` reports true as Err                        TranslateMessage(&msg).as_bool();                        DispatchMessageW(&msg);                    }                }            }        }        false    }     // Returns true if the app should quit.    fn handle_gpui_evnets(        &self,        message: u32,        wparam: WPARAM,        lparam: LPARAM,        msg: *const MSG,    ) -> bool {        if wparam.0 != self.validation_number {            unsafe { DispatchMessageW(msg) };            return false;        }        match message {            WM_GPUI_CLOSE_ONE_WINDOW => {                if self.close_one_window(HWND(lparam.0 as _)) {                    return true;                }            }            WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD => self.run_foreground_task(),            WM_GPUI_DOCK_MENU_ACTION => self.handle_dock_action_event(lparam.0 as _),            _ => unreachable!(),        }        false    }     fn configure_jump_list(&self, menus: Vec<MenuItem>) -> Result<()> {        let jump_list = JumpList::LoadCurrentAsync()?.get()?;        let items = jump_list.Items()?;        items.Clear()?;        let mut actions = Vec::new();        for item in menus.into_iter() {            let item = match item {                MenuItem::Separator => JumpListItem::CreateSeparator()?,                MenuItem::Submenu(_) => {                    log::error!("Set `MenuItemSubmenu` for dock menu on Windows is not supported.");                    continue;                }                MenuItem::Action { name, action, .. } => {                    let idx = actions.len();                    actions.push(action.boxed_clone());                    let item_args = format!("--dock-action {}", idx);                    JumpListItem::CreateWithArguments(                        &HSTRING::from(item_args),                        &HSTRING::from(name.as_ref()),                    )?                }            };            items.Append(&item)?;        }        jump_list.SaveAsync()?.get()?;        self.state.borrow_mut().dock_menu_actions = actions;        Ok(())    }} impl Platform for WindowsPlatform {    fn background_executor(&self) -> BackgroundExecutor {        self.background_executor.clone()    }     fn foreground_executor(&self) -> ForegroundExecutor {        self.foreground_executor.clone()    }     fn text_system(&self) -> Arc<dyn PlatformTextSystem> {        self.text_system.clone()    }     fn keyboard_layout(&self) -> String {        "unknown".into()    }     fn on_keyboard_layout_change(&self, _callback: Box<dyn FnMut()>) {        // todo(windows)    }     fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {        on_finish_launching();        let vsync_event = unsafe { Owned::new(CreateEventW(None, false, false, None).unwrap()) };        begin_vsync(*vsync_event);        'a: loop {            let wait_result = unsafe {                MsgWaitForMultipleObjects(Some(&[*vsync_event]), false, INFINITE, QS_ALLINPUT)            };             match wait_result {                // compositor clock ticked so we should draw a frame                WAIT_EVENT(0) => self.redraw_all(),                // Windows thread messages are posted                WAIT_EVENT(1) => {                    if self.handle_events() {                        break 'a;                    }                }                _ => {                    log::error!("Something went wrong while waiting {:?}", wait_result);                    break;                }            }        }         if let Some(ref mut callback) = self.state.borrow_mut().callbacks.quit {            callback();        }    }     fn quit(&self) {        self.foreground_executor()            .spawn(async { unsafe { PostQuitMessage(0) } })            .detach();    }     fn restart(&self, _: Option<PathBuf>) {        let pid = std::process::id();        let Some(app_path) = self.app_path().log_err() else {            return;        };        let script = format!(            r#"            $pidToWaitFor = {}            $exePath = "{}"             while ($true) {{                $process = Get-Process -Id $pidToWaitFor -ErrorAction SilentlyContinue                if (-not $process) {{                    Start-Process -FilePath $exePath                    break                }}                Start-Sleep -Seconds 0.1            }}            "#,            pid,            app_path.display(),        );        let restart_process = util::command::new_std_command("powershell.exe")            .arg("-command")            .arg(script)            .spawn();         match restart_process {            Ok(_) => self.quit(),            Err(e) => log::error!("failed to spawn restart script: {:?}", e),        }    }     fn activate(&self, _ignoring_other_apps: bool) {}     fn hide(&self) {}     // todo(windows)    fn hide_other_apps(&self) {        unimplemented!()    }     // todo(windows)    fn unhide_other_apps(&self) {        unimplemented!()    }     fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {        WindowsDisplay::displays()    }     fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {        WindowsDisplay::primary_monitor().map(|display| Rc::new(display) as Rc<dyn PlatformDisplay>)    }     fn screen_capture_sources(        &self,    ) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {        let (mut tx, rx) = oneshot::channel();        tx.send(Err(anyhow!("screen capture not implemented"))).ok();        rx    }     fn active_window(&self) -> Option<AnyWindowHandle> {        let active_window_hwnd = unsafe { GetActiveWindow() };        self.try_get_windows_inner_from_hwnd(active_window_hwnd)            .map(|inner| inner.handle)    }     fn open_window(        &self,        handle: AnyWindowHandle,        options: WindowParams,    ) -> Result<Box<dyn PlatformWindow>> {        let window = WindowsWindow::new(            handle,            options,            self.generate_creation_info(),            &self.gpu_context,        )?;        let handle = window.get_raw_handle();        self.raw_window_handles.write().push(handle);         Ok(Box::new(window))    }     fn window_appearance(&self) -> WindowAppearance {        system_appearance().log_err().unwrap_or_default()    }     fn open_url(&self, url: &str) {        let url_string = url.to_string();        self.background_executor()            .spawn(async move {                if url_string.is_empty() {                    return;                }                open_target(url_string.as_str());            })            .detach();    }     fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {        self.state.borrow_mut().callbacks.open_urls = Some(callback);    }     fn prompt_for_paths(        &self,        options: PathPromptOptions,    ) -> Receiver<Result<Option<Vec<PathBuf>>>> {        let (tx, rx) = oneshot::channel();        self.foreground_executor()            .spawn(async move {                let _ = tx.send(file_open_dialog(options));            })            .detach();         rx    }     fn prompt_for_new_path(&self, directory: &Path) -> Receiver<Result<Option<PathBuf>>> {        let directory = directory.to_owned();        let (tx, rx) = oneshot::channel();        self.foreground_executor()            .spawn(async move {                let _ = tx.send(file_save_dialog(directory));            })            .detach();         rx    }     fn can_select_mixed_files_and_dirs(&self) -> bool {        // The FOS_PICKFOLDERS flag toggles between "only files" and "only folders".        false    }     fn reveal_path(&self, path: &Path) {        let Ok(file_full_path) = path.canonicalize() else {            log::error!("unable to parse file path");            return;        };        self.background_executor()            .spawn(async move {                let Some(path) = file_full_path.to_str() else {                    return;                };                if path.is_empty() {                    return;                }                open_target_in_explorer(path);            })

Lines 1–500 of 844.