切换布局时,如何在xcb-xkb中跟踪键盘的变化?

编程语言 2026-07-10

当我使用 setxkbmap -layout us,gr -option grp:alt_shift_toggle 时,我可以使用 Alt + Shift 在美式布局和希腊布局之间切换。现在我尝试了下面的代码(属于一个更大项目的一部分)来检测这种变化。我原本以为会得到一个 NewKeyboardNotify 事件(就像手动执行 setxkbmap -layout ussetxkbmap -layout gr 一样)。然而,每次布局切换时,我会收到一对事件:一个IndicatorStateNotify事件和一个ExtensionDeviceNotify事件。这对我来说似乎有点奇怪。

cc *.c -lxcb -lxcb-xkb -lxkbcommon -lxkbcommon-x11

#include <stdio.h>
#include <stdlib.h>

#include <xcb/xcb.h>
#include <xcb/xkb.h>

struct display {
    xcb_connection_t *xcb;
    uint8_t xkb_base_event, xkb_base_error;
    int32_t keyboard_device_id;
} display;

void open_display(void)
{
    int connection_error;
    xcb_xkb_use_extension_cookie_t xkb_cookie;
    xcb_xkb_get_device_info_cookie_t device_cookie;
    const xcb_query_extension_reply_t *extension_reply;
    xcb_xkb_use_extension_reply_t *xkb_reply;
    xcb_xkb_get_device_info_reply_t *device_reply;
    xcb_generic_error_t *error;

    display.xcb = xcb_connect(NULL, NULL);

    xcb_prefetch_extension_data(display.xcb, &xcb_xkb_id);

    xkb_cookie = xcb_xkb_use_extension(display.xcb,
            XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);

    device_cookie = xcb_xkb_get_device_info(display.xcb,
            XCB_XKB_ID_USE_CORE_KBD, 0, 0, 0, 0, 0, 0);

    extension_reply = xcb_get_extension_data(display.xcb, &xcb_xkb_id);

    display.xkb_base_event = extension_reply->first_event;
    display.xkb_base_error = extension_reply->first_error;

    xkb_reply = xcb_xkb_use_extension_reply(display.xcb, xkb_cookie, &error);
    free(xkb_reply);

    device_reply = xcb_xkb_get_device_info_reply(display.xcb, device_cookie,
            NULL);
    display.keyboard_device_id = device_reply->deviceID;
    free(device_reply);

    xcb_xkb_select_events(display.xcb, display.keyboard_device_id,
                                       ~0, 0, ~0, ~0, ~0, NULL);
}

static void handle_xkb_event(xcb_generic_event_t *generic_event)
{
    switch (((xcb_xkb_new_keyboard_notify_event_t*) generic_event)->xkbType) {
    default:
        printf("xkb: %u\n",((xcb_xkb_new_keyboard_notify_event_t*) generic_event)->xkbType);
        break;

    case XCB_XKB_NEW_KEYBOARD_NOTIFY: {
        xcb_xkb_new_keyboard_notify_event_t *event;

        event = (xcb_xkb_new_keyboard_notify_event_t*) generic_event;
        if (event->oldDeviceID == display.keyboard_device_id) {
            printf("xkb: NEW_KEYBOARD_NOTIFY\n");
            display.keyboard_device_id = event->deviceID;
        }
        break;
    }

    case XCB_XKB_MAP_NOTIFY: {
        xcb_xkb_map_notify_event_t *event;

        event = (xcb_xkb_map_notify_event_t*) generic_event;
        printf("xkb: MAP_NOTIFY\n");
        break;
    }

    case XCB_XKB_STATE_NOTIFY: {
        xcb_xkb_state_notify_event_t *event;

        event = (xcb_xkb_state_notify_event_t*) generic_event;
        printf("xkb: STATE_NOTIFY\n");
        break;
    }

    case XCB_XKB_INDICATOR_STATE_NOTIFY: {
        xcb_xkb_indicator_state_notify_event_t *event;

        event = (xcb_xkb_indicator_state_notify_event_t*) generic_event;
        printf("xkb: INDICATOR_STATE_NOTIFY\n");
        break;
    }


    case XCB_XKB_EXTENSION_DEVICE_NOTIFY: {
        xcb_xkb_extension_device_notify_event_t *event;

        event = (xcb_xkb_extension_device_notify_event_t*) generic_event;
        printf("xkb: EXTENSION_DEVICE_NOTIFY\n");
        break;
    }
    }
}

int main(void)
{
    xcb_generic_event_t *event;

    open_display();

    xcb_flush(display.xcb);
    while (event = xcb_wait_for_event(display.xcb), event != NULL) {
        if (event->response_type == display.xkb_base_event) {
            handle_xkb_event(event);
        }
        free(event);
    }

    return 0;
}

为什么服务器没有发送一个NewKeyboardNotify之类的事件?我应该理解这些事件吗?应该怎么理解?

解决方案

-layout us,gr 只需要一个keymap,它同时存储两种布局。这就是在切换布局时不会发送 NewKeyboardNotify/MapNotify 的原因。相反,简单的 StateNotify 事件表示布局已经改变:

case XCB_XKB_STATE_NOTIFY: {
    xcb_xkb_state_notify_event_t *event;

    event = (xcb_xkb_state_notify_event_t*) generic_event;
    printf("xkb: STATE_NOTIFY\n");
    printf("Change: %u\n", event->changed);
    if ((event->changed & XCB_XKB_STATE_PART_GROUP_STATE)) {
        printf("Layout changed\n");
    }
    break;
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章