通过 XCB 的 GetDeviceInfo XKB 请求产生 BadAccess 错误

GetDeviceInfo XKB request through XCB yields BadAccess error

我正在尝试使用 XCB 的 XKB 扩展来捕获键盘输入。我正在编写的特定实现是生锈的,但是代码的错误部分主要是通过 FFI 调用。我的目标是能够处理键盘事件,例如按下、释放等键,并且能够将键码转换为 unicode。目前我正在尝试从 libxkbcommon 快速指南中获取 this example 工作。

下面是我试过的代码:

let (conn, screen_num) = Connection::connect(None)?;
// Other code...
let raw_conn = conn.get_raw_conn() as *mut xkbcommon_sys::xcb_connection_t;

let context = unsafe { xkb_context_new(XKB_CONTEXT_NO_FLAGS) };

if context.is_null() {
    return Err(Error::XkbContextInit);
}

let device_id = unsafe { xkb_x11_get_core_keyboard_device_id(raw_conn) };

// This branch is taken
if device_id == -1 {
    unsafe { xkb_context_unref(context); }
    return Err(Error::XkbGetCoreKbDev);
}

我希望能够获取核心键盘的设备 ID,但是函数返回 -1,表示错误。

经过一些试验和挖掘,我发现 xkb_x11_get_core_keyboard_device_id 函数特别失败 on this request,它使用 XCB_XKB_ID_USE_CORE_KBD 作为设备规范的值。由于我没有从这个函数中得到太多的错误信息,我将对X服务器的请求翻译成了rust,得到了以下错误:

// Output from debugging the returned error enum
Protocol(
  X(
    Access(
      RequestError {
        response_type: 0,
        error_code: 10,
        sequence: 3,
        bad_value: 2003,
        minor_opcode: 1,
        major_opcode: 130,
        pad: 1
      }
    )
  )
)

我还尝试了列出的设备规格的其他一些值 here,但收到了同样的错误。

不确定这是否是 X 服务器的问题,我在安装 Kali 以及 Ubuntu 20.04 VM 时尝试了它,两者都产生了相同的错误(尽管bad_value 字段)。此外,每当我在需要设备规格的地方使用 XCB_XKB_ID_USE_CORE_KBD 时,我都会收到此错误。我不太确定是什么导致了这个问题。这似乎与 XKB 特别相关,因为我也在使用 XCB 捕获屏幕,到目前为止我还没有遇到任何错误。任何帮助或指导将不胜感激。

搜索了一段时间后,我发现 this post 链接到一些文档,这让我意识到我从未设置 XKB 扩展。添加以下代码修复了问题:

let res = unsafe {
    xkb_x11_setup_xkb_extension(
        raw_conn,
        MAJOR_VERSION as u16,
        MINOR_VERSION as u16,
        XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
        ptr::null_mut(),
        ptr::null_mut(),
        ptr::null_mut(),
        ptr::null_mut()
    )
};
if res == 0 {
    return Err(Error::XkbInit);
}

如果您使用的是 xcb 和 xkbcommon crate,他们有一个使用(当前)beta 版本的 crate 的示例,该 crate 使用 API 而不是不安全的函数:

let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
let device_id = xkb::x11::get_core_keyboard_device_id(&connection);
let keymap = xkb::x11::keymap_new_from_device(
    &context,
    &connection,
    device_id,
    xkb::KEYMAP_COMPILE_NO_FLAGS,
);

let state = xkb::x11::state_new_from_device(&keymap, &connection, device_id);

https://github.com/rust-x-bindings/toy_xcb/blob/master/src/keyboard.rs