diff --git a/crates/gpui_linux/src/linux/platform.rs b/crates/gpui_linux/src/linux/platform.rs index 876f931663f..33307034aea 100644 --- a/crates/gpui_linux/src/linux/platform.rs +++ b/crates/gpui_linux/src/linux/platform.rs @@ -13,6 +13,8 @@ use std::{ time::Duration, }; +#[cfg(any(feature = "wayland", feature = "x11"))] +use anyhow::ensure; use anyhow::{Context as _, anyhow}; use calloop::{LoopSignal, channel::Sender}; use futures::channel::oneshot; @@ -832,6 +834,20 @@ pub(super) fn is_within_click_distance(a: Point, b: Point) -> bo diff.x.abs() <= DOUBLE_CLICK_DISTANCE && diff.y.abs() <= DOUBLE_CLICK_DISTANCE } +#[cfg(any(feature = "wayland", feature = "x11"))] +pub(super) fn new_xkb_context() -> anyhow::Result { + validate_xkb_context(xkb::Context::new(xkb::CONTEXT_NO_FLAGS)) +} + +#[cfg(any(feature = "wayland", feature = "x11"))] +fn validate_xkb_context(context: xkb::Context) -> anyhow::Result { + ensure!( + !context.get_raw_ptr().is_null(), + "libxkbcommon failed to create an XKB context" + ); + Ok(context) +} + #[cfg(any(feature = "wayland", feature = "x11"))] pub(super) fn get_xkb_compose_state(cx: &xkb::Context) -> Option { let mut locales = Vec::default(); @@ -1244,6 +1260,23 @@ mod tests { use super::*; use gpui::{Point, px}; + #[cfg(any(feature = "wayland", feature = "x11"))] + #[test] + fn rejects_null_xkb_context() { + let context = unsafe { + // libxkbcommon permits unref on null, matching the value returned by Context::new on failure. + xkb::Context::from_raw_ptr(std::ptr::null_mut()) + }; + let error = validate_xkb_context(context) + .err() + .expect("null XKB context should be rejected"); + + assert_eq!( + error.to_string(), + "libxkbcommon failed to create an XKB context" + ); + } + #[test] fn test_is_within_click_distance() { let zero = Point::new(px(0.0), px(0.0)); diff --git a/crates/gpui_linux/src/linux/wayland/client.rs b/crates/gpui_linux/src/linux/wayland/client.rs index 3d5f9029aa5..01863989cf0 100644 --- a/crates/gpui_linux/src/linux/wayland/client.rs +++ b/crates/gpui_linux/src/linux/wayland/client.rs @@ -83,7 +83,8 @@ use crate::linux::{ DOUBLE_CLICK_INTERVAL, LinuxClient, LinuxCommon, LinuxKeyboardLayout, PIPE_READ_TIMEOUT, SCROLL_LINES, capslock_from_xkb, cursor_style_to_icon_names, get_xkb_compose_state, is_within_click_distance, keystroke_from_xkb, keystroke_underlying_dead_key, - modifiers_from_xkb, open_uri_internal, read_fd_with_timeout, reveal_path_internal, + modifiers_from_xkb, new_xkb_context, open_uri_internal, read_fd_with_timeout, + reveal_path_internal, wayland::{ clipboard::{Clipboard, DataOffer, FILE_LIST_MIME_TYPE, TEXT_MIME_TYPES}, cursor::Cursor, @@ -1708,7 +1709,13 @@ impl Dispatch for WaylandClientStatePtr { log::error!("Received keymap format {:?}, expected XkbV1", format); return; } - let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + let xkb_context = match new_xkb_context() { + Ok(context) => context, + Err(error) => { + log::error!("Failed to process Wayland keymap: {error:#}"); + return; + } + }; let keymap = unsafe { xkb::Keymap::new_from_fd( &xkb_context, diff --git a/crates/gpui_linux/src/linux/x11/client.rs b/crates/gpui_linux/src/linux/x11/client.rs index a543cd66cdc..e9dd8f6a8f7 100644 --- a/crates/gpui_linux/src/linux/x11/client.rs +++ b/crates/gpui_linux/src/linux/x11/client.rs @@ -51,7 +51,8 @@ use super::{ use crate::linux::{ DEFAULT_CURSOR_ICON_NAME, LinuxClient, capslock_from_xkb, cursor_style_to_icon_names, get_xkb_compose_state, is_within_click_distance, keystroke_from_xkb, - keystroke_underlying_dead_key, log_cursor_icon_warning, modifiers_from_xkb, open_uri_internal, + keystroke_underlying_dead_key, log_cursor_icon_warning, modifiers_from_xkb, new_xkb_context, + open_uri_internal, platform::{DOUBLE_CLICK_INTERVAL, SCROLL_LINES}, reveal_path_internal, xdg_desktop_portal::{Event as XDPEvent, XDPEventSource}, @@ -420,7 +421,7 @@ impl X11Client { ), )?; - let xkb_context = xkbc::Context::new(xkbc::CONTEXT_NO_FLAGS); + let xkb_context = new_xkb_context()?; let xkb_device_id = xkbc::x11::get_core_keyboard_device_id(&xcb_connection); let xkb_state = { let xkb_keymap = xkbc::x11::keymap_new_from_device( @@ -2811,7 +2812,7 @@ mod tests { } fn test_keymap_with_variant(layouts: &str, variant: &str) -> xkbc::Keymap { - let context = xkbc::Context::new(xkbc::CONTEXT_NO_FLAGS); + let context = new_xkb_context().expect("test XKB context should initialize"); xkbc::Keymap::new_from_names( &context, "",