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::{CompositorGpuHint, 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_ICON, _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} #[derive(Debug, Clone, Copy)]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()>>, button_layout_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, x_screen_index: usize, visual_id: u32, 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, pub(crate) force_render_after_recovery: bool, fullscreen: bool, client_side_decorations_supported: bool, decorations: WindowDecorations, edge_constraints: Option<EdgeConstraints>, pub handle: AnyWindowHandle, last_insets: [u32; 4], accesskit_adapter: Option<accesskit_unix::Adapter>,} 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> { let Some(non_zero) = NonZeroU32::new(self.0.x_window) else { return Err(rwh::HandleError::Unavailable); }; let handle = rwh::XcbWindowHandle::new(non_zero); Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) }) }} impl rwh::HasDisplayHandle for X11Window { fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> { let connection = as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection(&*self.0.xcb) as *mut _; let Some(non_zero) = NonNull::new(connection) else { return Err(rwh::HandleError::Unavailable); }; let screen_id = { let state = self.0.state.borrow(); u64::from(state.display.id()) as i32 }; let handle = rwh::XcbDisplayHandle::new(Some(non_zero), screen_id); Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) }) }} 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: gpui_wgpu::GpuContext, compositor_gpu: Option<CompositorGpuHint>, 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>, supports_xinput_gestures: bool, is_bgr: bool, ) -> anyhow::Result<Self> { let x_screen_index = params .display_id .map_or(x_main_screen_index, |did| u64::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,Lines 1–500 of 1974. Load more
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::{CompositorGpuHint, 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_ICON, _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 } #[derive(Debug, Clone, Copy)] 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()>>, button_layout_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, x_screen_index: usize, visual_id: u32, 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, pub(crate) force_render_after_recovery: bool, fullscreen: bool, client_side_decorations_supported: bool, decorations: WindowDecorations, edge_constraints: Option<EdgeConstraints>, pub handle: AnyWindowHandle, last_insets: [u32; 4], accesskit_adapter: Option<accesskit_unix::Adapter>, } 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> { let Some(non_zero) = NonZeroU32::new(self.0.x_window) else { return Err(rwh::HandleError::Unavailable); }; let handle = rwh::XcbWindowHandle::new(non_zero); Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) }) } } impl rwh::HasDisplayHandle for X11Window { fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> { let connection = as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection(&*self.0.xcb) as *mut _; let Some(non_zero) = NonNull::new(connection) else { return Err(rwh::HandleError::Unavailable); }; let screen_id = { let state = self.0.state.borrow(); u64::from(state.display.id()) as i32 }; let handle = rwh::XcbDisplayHandle::new(Some(non_zero), screen_id); Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) }) } } 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: gpui_wgpu::GpuContext, compositor_gpu: Option<CompositorGpuHint>, 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>, supports_xinput_gestures: bool, is_bgr: bool, ) -> anyhow::Result<Self> { let x_screen_index = params .display_id .map_or(x_main_screen_index, |did| u64::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,