1388 lines · 46.4 KB · Rust
RawDownload
#![deny(unsafe_op_in_unsafe_fn)] use std::{    cell::RefCell,    num::NonZeroIsize,    path::PathBuf,    rc::{Rc, Weak},    str::FromStr,    sync::{Arc, Once},    time::{Duration, Instant},}; use ::util::ResultExt;use anyhow::{Context as _, Result};use async_task::Runnable;use futures::channel::oneshot::{self, Receiver};use raw_window_handle as rwh;use smallvec::SmallVec;use windows::{    Win32::{        Foundation::*,        Graphics::Gdi::*,        System::{Com::*, LibraryLoader::*, Ole::*, SystemServices::*},        UI::{Controls::*, HiDpi::*, Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*},    },    core::*,}; use crate::platform::blade::{BladeContext, BladeRenderer};use crate::*; pub(crate) struct WindowsWindow(pub Rc<WindowsWindowStatePtr>); pub struct WindowsWindowState {    pub origin: Point<Pixels>,    pub logical_size: Size<Pixels>,    pub min_size: Option<Size<Pixels>>,    pub fullscreen_restore_bounds: Bounds<Pixels>,    pub border_offset: WindowBorderOffset,    pub scale_factor: f32,    pub restore_from_minimized: Option<Box<dyn FnMut(RequestFrameOptions)>>,     pub callbacks: Callbacks,    pub input_handler: Option<PlatformInputHandler>,    pub last_reported_modifiers: Option<Modifiers>,    pub last_reported_capslock: Option<Capslock>,    pub system_key_handled: bool,    pub hovered: bool,     pub renderer: BladeRenderer,     pub click_state: ClickState,    pub system_settings: WindowsSystemSettings,    pub current_cursor: Option<HCURSOR>,    pub nc_button_pressed: Option<u32>,     pub display: WindowsDisplay,    fullscreen: Option<StyleAndBounds>,    initial_placement: Option<WindowOpenStatus>,    hwnd: HWND,} pub(crate) struct WindowsWindowStatePtr {    hwnd: HWND,    this: Weak<Self>,    drop_target_helper: IDropTargetHelper,    pub(crate) state: RefCell<WindowsWindowState>,    pub(crate) handle: AnyWindowHandle,    pub(crate) hide_title_bar: bool,    pub(crate) is_movable: bool,    pub(crate) executor: ForegroundExecutor,    pub(crate) windows_version: WindowsVersion,    pub(crate) validation_number: usize,    pub(crate) main_receiver: flume::Receiver<Runnable>,    pub(crate) main_thread_id_win32: u32,} impl WindowsWindowState {    fn new(        hwnd: HWND,        transparent: bool,        cs: &CREATESTRUCTW,        current_cursor: Option<HCURSOR>,        display: WindowsDisplay,        gpu_context: &BladeContext,        min_size: Option<Size<Pixels>>,    ) -> Result<Self> {        let scale_factor = {            let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32;            monitor_dpi / USER_DEFAULT_SCREEN_DPI as f32        };        let origin = logical_point(cs.x as f32, cs.y as f32, scale_factor);        let logical_size = {            let physical_size = size(DevicePixels(cs.cx), DevicePixels(cs.cy));            physical_size.to_pixels(scale_factor)        };        let fullscreen_restore_bounds = Bounds {            origin,            size: logical_size,        };        let border_offset = WindowBorderOffset::default();        let restore_from_minimized = None;        let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?;        let callbacks = Callbacks::default();        let input_handler = None;        let last_reported_modifiers = None;        let last_reported_capslock = None;        let system_key_handled = false;        let hovered = false;        let click_state = ClickState::new();        let system_settings = WindowsSystemSettings::new(display);        let nc_button_pressed = None;        let fullscreen = None;        let initial_placement = None;         Ok(Self {            origin,            logical_size,            fullscreen_restore_bounds,            border_offset,            scale_factor,            restore_from_minimized,            min_size,            callbacks,            input_handler,            last_reported_modifiers,            last_reported_capslock,            system_key_handled,            hovered,            renderer,            click_state,            system_settings,            current_cursor,            nc_button_pressed,            display,            fullscreen,            initial_placement,            hwnd,        })    }     #[inline]    pub(crate) fn is_fullscreen(&self) -> bool {        self.fullscreen.is_some()    }     pub(crate) fn is_maximized(&self) -> bool {        !self.is_fullscreen() && unsafe { IsZoomed(self.hwnd) }.as_bool()    }     fn bounds(&self) -> Bounds<Pixels> {        Bounds {            origin: self.origin,            size: self.logical_size,        }    }     // Calculate the bounds used for saving and whether the window is maximized.    fn calculate_window_bounds(&self) -> (Bounds<Pixels>, bool) {        let placement = unsafe {            let mut placement = WINDOWPLACEMENT {                length: std::mem::size_of::<WINDOWPLACEMENT>() as u32,                ..Default::default()            };            GetWindowPlacement(self.hwnd, &mut placement).log_err();            placement        };        (            calculate_client_rect(                placement.rcNormalPosition,                self.border_offset,                self.scale_factor,            ),            placement.showCmd == SW_SHOWMAXIMIZED.0 as u32,        )    }     fn window_bounds(&self) -> WindowBounds {        let (bounds, maximized) = self.calculate_window_bounds();         if self.is_fullscreen() {            WindowBounds::Fullscreen(self.fullscreen_restore_bounds)        } else if maximized {            WindowBounds::Maximized(bounds)        } else {            WindowBounds::Windowed(bounds)        }    }     /// get the logical size of the app's drawable area.    ///    /// Currently, GPUI uses the logical size of the app to handle mouse interactions (such as    /// whether the mouse collides with other elements of GPUI).    fn content_size(&self) -> Size<Pixels> {        self.logical_size    }} impl WindowsWindowStatePtr {    fn new(context: &WindowCreateContext, hwnd: HWND, cs: &CREATESTRUCTW) -> Result<Rc<Self>> {        let state = RefCell::new(WindowsWindowState::new(            hwnd,            context.transparent,            cs,            context.current_cursor,            context.display,            context.gpu_context,            context.min_size,        )?);         Ok(Rc::new_cyclic(|this| Self {            hwnd,            this: this.clone(),            drop_target_helper: context.drop_target_helper.clone(),            state,            handle: context.handle,            hide_title_bar: context.hide_title_bar,            is_movable: context.is_movable,            executor: context.executor.clone(),            windows_version: context.windows_version,            validation_number: context.validation_number,            main_receiver: context.main_receiver.clone(),            main_thread_id_win32: context.main_thread_id_win32,        }))    }     fn toggle_fullscreen(&self) {        let Some(state_ptr) = self.this.upgrade() else {            log::error!("Unable to toggle fullscreen: window has been dropped");            return;        };        self.executor            .spawn(async move {                let mut lock = state_ptr.state.borrow_mut();                let StyleAndBounds {                    style,                    x,                    y,                    cx,                    cy,                } = if let Some(state) = lock.fullscreen.take() {                    state                } else {                    let (window_bounds, _) = lock.calculate_window_bounds();                    lock.fullscreen_restore_bounds = window_bounds;                    let style =                        WINDOW_STYLE(unsafe { get_window_long(state_ptr.hwnd, GWL_STYLE) } as _);                    let mut rc = RECT::default();                    unsafe { GetWindowRect(state_ptr.hwnd, &mut rc) }.log_err();                    let _ = lock.fullscreen.insert(StyleAndBounds {                        style,                        x: rc.left,                        y: rc.top,                        cx: rc.right - rc.left,                        cy: rc.bottom - rc.top,                    });                    let style = style                        & !(WS_THICKFRAME                            | WS_SYSMENU                            | WS_MAXIMIZEBOX                            | WS_MINIMIZEBOX                            | WS_CAPTION);                    let physical_bounds = lock.display.physical_bounds();                    StyleAndBounds {                        style,                        x: physical_bounds.left().0,                        y: physical_bounds.top().0,                        cx: physical_bounds.size.width.0,                        cy: physical_bounds.size.height.0,                    }                };                drop(lock);                unsafe { set_window_long(state_ptr.hwnd, GWL_STYLE, style.0 as isize) };                unsafe {                    SetWindowPos(                        state_ptr.hwnd,                        None,                        x,                        y,                        cx,                        cy,                        SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER,                    )                }                .log_err();            })            .detach();    }     fn set_window_placement(&self) -> Result<()> {        let Some(open_status) = self.state.borrow_mut().initial_placement.take() else {            return Ok(());        };        match open_status.state {            WindowOpenState::Maximized => unsafe {                SetWindowPlacement(self.hwnd, &open_status.placement)?;                ShowWindowAsync(self.hwnd, SW_MAXIMIZE).ok()?;            },            WindowOpenState::Fullscreen => {                unsafe { SetWindowPlacement(self.hwnd, &open_status.placement)? };                self.toggle_fullscreen();            }            WindowOpenState::Windowed => unsafe {                SetWindowPlacement(self.hwnd, &open_status.placement)?;            },        }        Ok(())    }} #[derive(Default)]pub(crate) struct Callbacks {    pub(crate) request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,    pub(crate) input: Option<Box<dyn FnMut(crate::PlatformInput) -> DispatchEventResult>>,    pub(crate) active_status_change: Option<Box<dyn FnMut(bool)>>,    pub(crate) hovered_status_change: Option<Box<dyn FnMut(bool)>>,    pub(crate) resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,    pub(crate) moved: Option<Box<dyn FnMut()>>,    pub(crate) should_close: Option<Box<dyn FnMut() -> bool>>,    pub(crate) close: Option<Box<dyn FnOnce()>>,    pub(crate) hit_test_window_control: Option<Box<dyn FnMut() -> Option<WindowControlArea>>>,    pub(crate) appearance_changed: Option<Box<dyn FnMut()>>,} struct WindowCreateContext<'a> {    inner: Option<Result<Rc<WindowsWindowStatePtr>>>,    handle: AnyWindowHandle,    hide_title_bar: bool,    display: WindowsDisplay,    transparent: bool,    is_movable: bool,    min_size: Option<Size<Pixels>>,    executor: ForegroundExecutor,    current_cursor: Option<HCURSOR>,    windows_version: WindowsVersion,    drop_target_helper: IDropTargetHelper,    validation_number: usize,    main_receiver: flume::Receiver<Runnable>,    gpu_context: &'a BladeContext,    main_thread_id_win32: u32,} impl WindowsWindow {    pub(crate) fn new(        handle: AnyWindowHandle,        params: WindowParams,        creation_info: WindowCreationInfo,        gpu_context: &BladeContext,    ) -> Result<Self> {        let WindowCreationInfo {            icon,            executor,            current_cursor,            windows_version,            drop_target_helper,            validation_number,            main_receiver,            main_thread_id_win32,        } = creation_info;        let classname = register_wnd_class(icon);        let hide_title_bar = params            .titlebar            .as_ref()            .map(|titlebar| titlebar.appears_transparent)            .unwrap_or(true);        let windowname = HSTRING::from(            params                .titlebar                .as_ref()                .and_then(|titlebar| titlebar.title.as_ref())                .map(|title| title.as_ref())                .unwrap_or(""),        );        let (dwexstyle, mut dwstyle) = if params.kind == WindowKind::PopUp {            (WS_EX_TOOLWINDOW | WS_EX_LAYERED, WINDOW_STYLE(0x0))        } else {            (                WS_EX_APPWINDOW | WS_EX_LAYERED,                WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,            )        };         let hinstance = get_module_handle();        let display = if let Some(display_id) = params.display_id {            // if we obtain a display_id, then this ID must be valid.            WindowsDisplay::new(display_id).unwrap()        } else {            WindowsDisplay::primary_monitor().unwrap()        };        let mut context = WindowCreateContext {            inner: None,            handle,            hide_title_bar,            display,            transparent: true,            is_movable: params.is_movable,            min_size: params.window_min_size,            executor,            current_cursor,            windows_version,            drop_target_helper,            validation_number,            main_receiver,            gpu_context,            main_thread_id_win32,        };        let lpparam = Some(&context as *const _ as *const _);        let creation_result = unsafe {            CreateWindowExW(                dwexstyle,                classname,                &windowname,                dwstyle,                CW_USEDEFAULT,                CW_USEDEFAULT,                CW_USEDEFAULT,                CW_USEDEFAULT,                None,                None,                Some(hinstance.into()),                lpparam,            )        };        // We should call `?` on state_ptr first, then call `?` on hwnd.        // Or, we will lose the error info reported by `WindowsWindowState::new`        let state_ptr = context.inner.take().unwrap()?;        let hwnd = creation_result?;        register_drag_drop(state_ptr.clone())?;        configure_dwm_dark_mode(hwnd);        state_ptr.state.borrow_mut().border_offset.update(hwnd)?;        let placement = retrieve_window_placement(            hwnd,            display,            params.bounds,            state_ptr.state.borrow().scale_factor,            state_ptr.state.borrow().border_offset,        )?;        if params.show {            unsafe { SetWindowPlacement(hwnd, &placement)? };        } else {            state_ptr.state.borrow_mut().initial_placement = Some(WindowOpenStatus {                placement,                state: WindowOpenState::Windowed,            });        }        // The render pipeline will perform compositing on the GPU when the        // swapchain is configured correctly (see downstream of        // update_transparency).        // The following configuration is a one-time setup to ensure that the        // window is going to be composited with per-pixel alpha, but the render        // pipeline is responsible for effectively calling UpdateLayeredWindow        // at the appropriate time.        unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 255, LWA_ALPHA)? };         Ok(Self(state_ptr))    }} impl rwh::HasWindowHandle for WindowsWindow {    fn window_handle(&self) -> std::result::Result<rwh::WindowHandle<'_>, rwh::HandleError> {        let raw = rwh::Win32WindowHandle::new(unsafe {            NonZeroIsize::new_unchecked(self.0.hwnd.0 as isize)        })        .into();        Ok(unsafe { rwh::WindowHandle::borrow_raw(raw) })    }} // todo(windows)impl rwh::HasDisplayHandle for WindowsWindow {    fn display_handle(&self) -> std::result::Result<rwh::DisplayHandle<'_>, rwh::HandleError> {        unimplemented!()    }} impl Drop for WindowsWindow {    fn drop(&mut self) {        self.0.state.borrow_mut().renderer.destroy();        // clone this `Rc` to prevent early release of the pointer        let this = self.0.clone();        self.0            .executor            .spawn(async move {                let handle = this.hwnd;                unsafe {                    RevokeDragDrop(handle).log_err();                    DestroyWindow(handle).log_err();                }            })            .detach();    }} impl PlatformWindow for WindowsWindow {    fn bounds(&self) -> Bounds<Pixels> {        self.0.state.borrow().bounds()    }     fn is_maximized(&self) -> bool {        self.0.state.borrow().is_maximized()

Lines 1–500 of 1388.