gpui_linux: Handle XKB context initialization failure (#62868)

Release Notes:

- Added check for XKB context initialization failure
This commit is contained in:
Jakub Konka 2026-08-19 11:19:10 +00:00 committed by GitHub
parent 99f4c21c03
commit c43e2d9734
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 46 additions and 5 deletions

View file

@ -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<Pixels>, b: Point<Pixels>) -> 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<xkb::Context> {
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<xkb::Context> {
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<xkb::compose::State> {
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));

View file

@ -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<wl_keyboard::WlKeyboard, ()> 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,

View file

@ -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,
"",