1757 lines · 57.3 KB · Rust
RawDownload
use anyhow::{Context as _, anyhow};use x11rb::connection::RequestConnection; use crate::linux::X11ClientStatePtr;use gpui::{    AnyWindowHandle, Bounds, Decorations, DevicePixels, ForegroundExecutor, GpuSpecs, Modifiers,    Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow,    Point, PromptButton, PromptLevel, RequestFrameOptions, ResizeEdge, ScaledPixels, Scene, Size,    Tiling, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea,    WindowDecorations, WindowKind, WindowParams, px,};use gpui_wgpu::{WgpuContext, WgpuRenderer, WgpuSurfaceConfig}; use collections::FxHashSet;use raw_window_handle as rwh;use util::{ResultExt, maybe};use x11rb::{    connection::Connection,    cookie::{Cookie, VoidCookie},    errors::ConnectionError,    properties::WmSizeHints,    protocol::{        sync,        xinput::{self, ConnectionExt as _},        xproto::{self, ClientMessageEvent, ConnectionExt, TranslateCoordinatesReply},    },    wrapper::ConnectionExt as _,    xcb_ffi::XCBConnection,}; use std::{    cell::RefCell, ffi::c_void, fmt::Display, num::NonZeroU32, ptr::NonNull, rc::Rc, sync::Arc,}; use super::{X11Display, XINPUT_ALL_DEVICE_GROUPS, XINPUT_ALL_DEVICES}; x11rb::atom_manager! {    pub XcbAtoms: AtomsCookie {        XA_ATOM,        XdndAware,        XdndStatus,        XdndEnter,        XdndLeave,        XdndPosition,        XdndSelection,        XdndDrop,        XdndFinished,        XdndTypeList,        XdndActionCopy,        TextUriList: b"text/uri-list",        UTF8_STRING,        TEXT,        STRING,        TEXT_PLAIN_UTF8: b"text/plain;charset=utf-8",        TEXT_PLAIN: b"text/plain",        XDND_DATA,        WM_PROTOCOLS,        WM_DELETE_WINDOW,        WM_CHANGE_STATE,        WM_TRANSIENT_FOR,        _NET_WM_PID,        _NET_WM_NAME,        _NET_WM_STATE,        _NET_WM_STATE_MAXIMIZED_VERT,        _NET_WM_STATE_MAXIMIZED_HORZ,        _NET_WM_STATE_FULLSCREEN,        _NET_WM_STATE_HIDDEN,        _NET_WM_STATE_FOCUSED,        _NET_ACTIVE_WINDOW,        _NET_WM_SYNC_REQUEST,        _NET_WM_SYNC_REQUEST_COUNTER,        _NET_WM_BYPASS_COMPOSITOR,        _NET_WM_MOVERESIZE,        _NET_WM_WINDOW_TYPE,        _NET_WM_WINDOW_TYPE_NOTIFICATION,        _NET_WM_WINDOW_TYPE_DIALOG,        _NET_WM_STATE_MODAL,        _NET_WM_SYNC,        _NET_SUPPORTED,        _MOTIF_WM_HINTS,        _GTK_SHOW_WINDOW_MENU,        _GTK_FRAME_EXTENTS,        _GTK_EDGE_CONSTRAINTS,        _NET_CLIENT_LIST_STACKING,    }} fn query_render_extent(    xcb: &Rc<XCBConnection>,    x_window: xproto::Window,) -> anyhow::Result<Size<DevicePixels>> {    let reply = get_reply(|| "X11 GetGeometry failed.", xcb.get_geometry(x_window))?;    Ok(Size {        width: DevicePixels(reply.width as i32),        height: DevicePixels(reply.height as i32),    })} fn resize_edge_to_moveresize(edge: ResizeEdge) -> u32 {    match edge {        ResizeEdge::TopLeft => 0,        ResizeEdge::Top => 1,        ResizeEdge::TopRight => 2,        ResizeEdge::Right => 3,        ResizeEdge::BottomRight => 4,        ResizeEdge::Bottom => 5,        ResizeEdge::BottomLeft => 6,        ResizeEdge::Left => 7,    }} #[derive(Debug)]struct EdgeConstraints {    top_tiled: bool,    #[allow(dead_code)]    top_resizable: bool,     right_tiled: bool,    #[allow(dead_code)]    right_resizable: bool,     bottom_tiled: bool,    #[allow(dead_code)]    bottom_resizable: bool,     left_tiled: bool,    #[allow(dead_code)]    left_resizable: bool,} impl EdgeConstraints {    fn from_atom(atom: u32) -> Self {        EdgeConstraints {            top_tiled: (atom & (1 << 0)) != 0,            top_resizable: (atom & (1 << 1)) != 0,            right_tiled: (atom & (1 << 2)) != 0,            right_resizable: (atom & (1 << 3)) != 0,            bottom_tiled: (atom & (1 << 4)) != 0,            bottom_resizable: (atom & (1 << 5)) != 0,            left_tiled: (atom & (1 << 6)) != 0,            left_resizable: (atom & (1 << 7)) != 0,        }    }     fn to_tiling(&self) -> Tiling {        Tiling {            top: self.top_tiled,            right: self.right_tiled,            bottom: self.bottom_tiled,            left: self.left_tiled,        }    }} #[derive(Copy, Clone, Debug)]struct Visual {    id: xproto::Visualid,    colormap: u32,    depth: u8,} struct VisualSet {    inherit: Visual,    opaque: Option<Visual>,    transparent: Option<Visual>,    root: u32,    black_pixel: u32,} fn find_visuals(xcb: &XCBConnection, screen_index: usize) -> VisualSet {    let screen = &xcb.setup().roots[screen_index];    let mut set = VisualSet {        inherit: Visual {            id: screen.root_visual,            colormap: screen.default_colormap,            depth: screen.root_depth,        },        opaque: None,        transparent: None,        root: screen.root,        black_pixel: screen.black_pixel,    };     for depth_info in screen.allowed_depths.iter() {        for visual_type in depth_info.visuals.iter() {            let visual = Visual {                id: visual_type.visual_id,                colormap: 0,                depth: depth_info.depth,            };            log::debug!(                "Visual id: {}, class: {:?}, depth: {}, bits_per_value: {}, masks: 0x{:x} 0x{:x} 0x{:x}",                visual_type.visual_id,                visual_type.class,                depth_info.depth,                visual_type.bits_per_rgb_value,                visual_type.red_mask,                visual_type.green_mask,                visual_type.blue_mask,            );             if (                visual_type.red_mask,                visual_type.green_mask,                visual_type.blue_mask,            ) != (0xFF0000, 0xFF00, 0xFF)            {                continue;            }            let color_mask = visual_type.red_mask | visual_type.green_mask | visual_type.blue_mask;            let alpha_mask = color_mask as usize ^ ((1usize << depth_info.depth) - 1);             if alpha_mask == 0 {                if set.opaque.is_none() {                    set.opaque = Some(visual);                }            } else {                if set.transparent.is_none() {                    set.transparent = Some(visual);                }            }        }    }     set} struct RawWindow {    connection: *mut c_void,    screen_id: usize,    window_id: u32,    visual_id: u32,} // Safety: The raw pointers in RawWindow point to X11 connection// which is valid for the window's lifetime. These are used only for// passing to wgpu which needs Send+Sync for surface creation.unsafe impl Send for RawWindow {}unsafe impl Sync for RawWindow {} #[derive(Default)]pub struct Callbacks {    request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,    input: Option<Box<dyn FnMut(PlatformInput) -> gpui::DispatchEventResult>>,    active_status_change: Option<Box<dyn FnMut(bool)>>,    hovered_status_change: Option<Box<dyn FnMut(bool)>>,    resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,    moved: Option<Box<dyn FnMut()>>,    should_close: Option<Box<dyn FnMut() -> bool>>,    close: Option<Box<dyn FnOnce()>>,    appearance_changed: Option<Box<dyn FnMut()>>,} pub struct X11WindowState {    pub destroyed: bool,    parent: Option<X11WindowStatePtr>,    children: FxHashSet<xproto::Window>,    client: X11ClientStatePtr,    executor: ForegroundExecutor,    atoms: XcbAtoms,    x_root_window: xproto::Window,    pub(crate) counter_id: sync::Counter,    pub(crate) last_sync_counter: Option<sync::Int64>,    bounds: Bounds<Pixels>,    scale_factor: f32,    renderer: WgpuRenderer,    display: Rc<dyn PlatformDisplay>,    input_handler: Option<PlatformInputHandler>,    appearance: WindowAppearance,    background_appearance: WindowBackgroundAppearance,    maximized_vertical: bool,    maximized_horizontal: bool,    hidden: bool,    active: bool,    hovered: bool,    fullscreen: bool,    client_side_decorations_supported: bool,    decorations: WindowDecorations,    edge_constraints: Option<EdgeConstraints>,    pub handle: AnyWindowHandle,    last_insets: [u32; 4],} impl X11WindowState {    fn is_transparent(&self) -> bool {        self.background_appearance != WindowBackgroundAppearance::Opaque    }} #[derive(Clone)]pub(crate) struct X11WindowStatePtr {    pub state: Rc<RefCell<X11WindowState>>,    pub(crate) callbacks: Rc<RefCell<Callbacks>>,    xcb: Rc<XCBConnection>,    pub(crate) x_window: xproto::Window,} impl rwh::HasWindowHandle for RawWindow {    fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {        let Some(non_zero) = NonZeroU32::new(self.window_id) else {            log::error!("RawWindow.window_id zero when getting window handle.");            return Err(rwh::HandleError::Unavailable);        };        let mut handle = rwh::XcbWindowHandle::new(non_zero);        handle.visual_id = NonZeroU32::new(self.visual_id);        Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) })    }}impl rwh::HasDisplayHandle for RawWindow {    fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {        let Some(non_zero) = NonNull::new(self.connection) else {            log::error!("Null RawWindow.connection when getting display handle.");            return Err(rwh::HandleError::Unavailable);        };        let handle = rwh::XcbDisplayHandle::new(Some(non_zero), self.screen_id as i32);        Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) })    }} impl rwh::HasWindowHandle for X11Window {    fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {        unimplemented!()    }}impl rwh::HasDisplayHandle for X11Window {    fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {        unimplemented!()    }} pub(crate) fn xcb_flush(xcb: &XCBConnection) {    xcb.flush()        .map_err(handle_connection_error)        .context("X11 flush failed")        .log_err();} pub(crate) fn check_reply<E, F, C>(    failure_context: F,    result: Result<VoidCookie<'_, C>, ConnectionError>,) -> anyhow::Result<()>where    E: Display + Send + Sync + 'static,    F: FnOnce() -> E,    C: RequestConnection,{    result        .map_err(handle_connection_error)        .and_then(|response| response.check().map_err(|reply_error| anyhow!(reply_error)))        .with_context(failure_context)} pub(crate) fn get_reply<E, F, C, O>(    failure_context: F,    result: Result<Cookie<'_, C, O>, ConnectionError>,) -> anyhow::Result<O>where    E: Display + Send + Sync + 'static,    F: FnOnce() -> E,    C: RequestConnection,    O: x11rb::x11_utils::TryParse,{    result        .map_err(handle_connection_error)        .and_then(|response| response.reply().map_err(|reply_error| anyhow!(reply_error)))        .with_context(failure_context)} /// Convert X11 connection errors to `anyhow::Error` and panic for unrecoverable errors.pub(crate) fn handle_connection_error(err: ConnectionError) -> anyhow::Error {    match err {        ConnectionError::UnknownError => anyhow!("X11 connection: Unknown error"),        ConnectionError::UnsupportedExtension => anyhow!("X11 connection: Unsupported extension"),        ConnectionError::MaximumRequestLengthExceeded => {            anyhow!("X11 connection: Maximum request length exceeded")        }        ConnectionError::FdPassingFailed => {            panic!("X11 connection: File descriptor passing failed")        }        ConnectionError::ParseError(parse_error) => {            anyhow!(parse_error).context("Parse error in X11 response")        }        ConnectionError::InsufficientMemory => panic!("X11 connection: Insufficient memory"),        ConnectionError::IoError(err) => anyhow!(err).context("X11 connection: IOError"),        _ => anyhow!(err),    }} impl X11WindowState {    pub fn new(        handle: AnyWindowHandle,        client: X11ClientStatePtr,        executor: ForegroundExecutor,        gpu_context: &WgpuContext,        params: WindowParams,        xcb: &Rc<XCBConnection>,        client_side_decorations_supported: bool,        x_main_screen_index: usize,        x_window: xproto::Window,        atoms: &XcbAtoms,        scale_factor: f32,        appearance: WindowAppearance,        parent_window: Option<X11WindowStatePtr>,    ) -> anyhow::Result<Self> {        let x_screen_index = params            .display_id            .map_or(x_main_screen_index, |did| u32::from(did) as usize);         let visual_set = find_visuals(xcb, x_screen_index);         let visual = match visual_set.transparent {            Some(visual) => visual,            None => {                log::warn!("Unable to find a transparent visual",);                visual_set.inherit            }        };        log::info!("Using {:?}", visual);         let colormap = if visual.colormap != 0 {            visual.colormap        } else {            let id = xcb.generate_id()?;            log::info!("Creating colormap {}", id);            check_reply(                || format!("X11 CreateColormap failed. id: {}", id),                xcb.create_colormap(xproto::ColormapAlloc::NONE, id, visual_set.root, visual.id),            )?;            id        };         let win_aux = xproto::CreateWindowAux::new()            // https://stackoverflow.com/questions/43218127/x11-xlib-xcb-creating-a-window-requires-border-pixel-if-specifying-colormap-wh            .border_pixel(visual_set.black_pixel)            .colormap(colormap)            .override_redirect((params.kind == WindowKind::PopUp) as u32)            .event_mask(                xproto::EventMask::EXPOSURE                    | xproto::EventMask::STRUCTURE_NOTIFY                    | xproto::EventMask::FOCUS_CHANGE                    | xproto::EventMask::KEY_PRESS                    | xproto::EventMask::KEY_RELEASE                    | xproto::EventMask::PROPERTY_CHANGE                    | xproto::EventMask::VISIBILITY_CHANGE,            );         let mut bounds = params.bounds.to_device_pixels(scale_factor);        if bounds.size.width.0 == 0 || bounds.size.height.0 == 0 {            log::warn!(                "Window bounds contain a zero value. height={}, width={}. Falling back to defaults.",                bounds.size.height.0,                bounds.size.width.0            );            bounds.size.width = 800.into();            bounds.size.height = 600.into();        }         check_reply(            || {                format!(                    "X11 CreateWindow failed. depth: {}, x_window: {}, visual_set.root: {}, bounds.origin.x.0: {}, bounds.origin.y.0: {}, bounds.size.width.0: {}, bounds.size.height.0: {}",                    visual.depth,                    x_window,                    visual_set.root,                    bounds.origin.x.0 + 2,                    bounds.origin.y.0,                    bounds.size.width.0,                    bounds.size.height.0                )            },            xcb.create_window(                visual.depth,                x_window,                visual_set.root,                (bounds.origin.x.0 + 2) as i16,                bounds.origin.y.0 as i16,                bounds.size.width.0 as u16,                bounds.size.height.0 as u16,                0,                xproto::WindowClass::INPUT_OUTPUT,                visual.id,                &win_aux,            ),        )?;         // Collect errors during setup, so that window can be destroyed on failure.        let setup_result = maybe!({            let pid = std::process::id();            check_reply(                || "X11 ChangeProperty for _NET_WM_PID failed.",                xcb.change_property32(                    xproto::PropMode::REPLACE,                    x_window,                    atoms._NET_WM_PID,                    xproto::AtomEnum::CARDINAL,                    &[pid],                ),            )?;             if let Some(size) = params.window_min_size {

Lines 1–500 of 1757.