Как перехватить события XKEYBOARD с расширением RECORD? - PullRequest
0 голосов
/ 21 июня 2019

клиент X11 ниже не видит события XKEYBOARD в подключении к данным для расширения RECORD.

Я уже успешно использовал расширение RECORD для захвата движения курсора и событий KeyPress с дисплея X.

Теперь я хочу декодировать события KeyPress в правильные символы utf-8.Для этого требуется XKB, так как я использую xcb (а XLookupString из Xlib в любом случае устарела).

Поэтому я настроил свой контекст RECORD соответствующим образом, например:

xcb_record_range_t rr[] = {
    //{.delivered_events = {XCB_KEY_PRESS,        XCB_KEY_RELEASE}},      // 2-3
    //{.delivered_events = {XCB_BUTTON_PRESS,     XCB_BUTTON_RELEASE}},   // 4-5
    //{.delivered_events = {XCB_SELECTION_NOTIFY, XCB_SELECTION_NOTIFY}}, // 31

    //{.device_events = {XCB_MOTION_NOTIFY, XCB_MOTION_NOTIFY}}, // 6

    // NOTE: variable xkb_base_event setup from XKB initialization.
    // (there is only one XKB event, with several 'subtypes').
    // Also, I don't know whether XKB events are 'delivered' or 'device events'.
    {.delivered_events = {xkb_base_event, xkb_base_event}},
    {.device_events = {xkb_base_event, xkb_base_event}},

    // "Event codes 64 through 127 are reserved for extensions"
    {.delivered_events = {64, 127}}, // all extensions.
    {.device_events = {64, 127}}, // all extensions.
};

Вот клиент RECORD:

// m0.c: RECORD client, reporting all extension events.
//
// compile with:
//
// cc -std=c99 -Wall -Wextra -g -O2 -fno-omit-frame-pointer -D_GNU_SOURCE -Wl,--as-needed  m0.c  -lxcb-record -lxcb-util -lxcb -o m0
//

#include <err.h>
#include <locale.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>

#include <xcb/xcb.h>
#include <xcb/record.h>
#include <xcb/xcb_util.h>

#define UNUSED(x) ((void)(x))
#define NELEMS(a) (sizeof(a) / sizeof(a[0]))

static void data_dump(size_t len, void* p)
{
    if (len == 0)
        return;

    uint8_t* data = p;

    for (size_t i = 0; i < len; i++) {
        if (i && i % 2 == 0) printf(" ");
        printf("%02X", data[i]);
    }
    printf("\n");
}

static size_t protocol_error_process(void* error)
{
    xcb_generic_error_t* err = error;

    // TODO: error for Tcl layer?
    printf("-- %s:: error_code=%d sequence=%d major=%d minor=%d",
        __func__, err->error_code, err->sequence, err->major_code, err->minor_code);

    // TODO: if error from request:
    // xcb_request_error_t* re = err;
    // re->error_code
    // re->sequence
    // re->bad_value
    // re->minor_opcode
    // re->major_opcode

    return sizeof(xcb_generic_error_t);
}

static size_t protocol_reply_process(void* reply)
{
    // 0.
    xcb_generic_reply_t* gr = reply;
    size_t length_in_bytes = 32 + gr->length * 4;

    if (1)
        printf("-- %s: sequence=%d bytes=%zu\n",
                __func__, gr->sequence, length_in_bytes);

    return length_in_bytes;
}

static size_t protocol_event_process(xcb_generic_event_t* event)
{
    uint8_t type = XCB_EVENT_RESPONSE_TYPE(event);

    switch (type) {
    case XCB_KEY_PRESS:
        printf("-- %s: KeyPress\n", __func__);
        break;
    case XCB_KEY_RELEASE:
        printf("-- %s: KeyRelease\n", __func__);
        break;
    case XCB_BUTTON_PRESS:
        printf("-- %s: ButtonPress\n", __func__);
        break;
    case XCB_BUTTON_RELEASE:
        printf("-- %s: ButtonRelease\n", __func__);
        break;
    case XCB_MOTION_NOTIFY:
        printf("-- %s: MotionNotify\n", __func__);
        break;
    case XCB_SELECTION_NOTIFY:
        printf("-- %s: SelectionNotify\n", __func__);
        break;
    default:
        if (64 <= type && type <= 127) {
            printf("-- %s: event %d from extension\n", __func__, type);
            printf("\t");
            data_dump(sizeof(event), event);
        } else {
            printf("unexpected event type=%s(%d) sequence=%d\n",
                   xcb_event_get_label(type), type, event->sequence);
        }
    }

    return sizeof(xcb_raw_generic_event_t);

}

static void data_from_server_process(size_t length, uint8_t* data)
{
    uint8_t* end = data + length;

    while (data < end) {
        xcb_generic_event_t* event = (xcb_generic_event_t*)data;

        switch (XCB_EVENT_RESPONSE_TYPE(event)) {
        case 0:
            data += protocol_error_process(data);
            break;

        case 1:
            data += protocol_reply_process(data);
            break;

        default:
            data += protocol_event_process(event);
            break;
        }
    }
}

int main(int argc, char* argv[])
{
    UNUSED(argc);

    if (!setlocale(LC_ALL, ""))
        errx(1, "failed to set locale");

    setlinebuf(stdout);

    xcb_connection_t* c = xcb_connect(argv[1], 0);
    if (xcb_connection_has_error(c))
        errx(1, "failed to connect to display %s", argv[1]);

    xcb_prefetch_extension_data(c, &xcb_record_id);

    const xcb_query_extension_reply_t* reply = xcb_get_extension_data(c, &xcb_record_id);
    if (!reply->present)
        errx(1, "the RECORD extension is missing on display %s", argv[1]);

    {
        xcb_record_query_version_cookie_t cookie =
            xcb_record_query_version(c,
                                     XCB_RECORD_MAJOR_VERSION,
                                     XCB_RECORD_MINOR_VERSION);

        xcb_record_query_version_reply_t* reply =
            xcb_record_query_version_reply(c, cookie, 0);

        if (!reply)
            errx(1, "failed to query version of RECORD extension");

        free(reply);
    }

    xcb_record_context_t record_context = 0;
    {
        xcb_record_context_t rc = xcb_generate_id(c);

        xcb_record_element_header_t h = XCB_RECORD_H_TYPE_FROM_SERVER_TIME
                                      | XCB_RECORD_H_TYPE_FROM_CLIENT_SEQUENCE;
        h = 0; // no header for now.

        xcb_record_client_spec_t rcs[] = {XCB_RECORD_CS_ALL_CLIENTS};

        xcb_record_range_t rr[] = {
            //{.core_requests = {XCB_GET_PROPERTY, XCB_GET_PROPERTY}}, // 20

            //{.core_replies = {XCB_GET_PROPERTY, XCB_GET_PROPERTY}},

            {.delivered_events = {XCB_KEY_PRESS,        XCB_KEY_RELEASE}},      // 2-3
            {.delivered_events = {XCB_BUTTON_PRESS,     XCB_BUTTON_RELEASE}},   // 4-5
            //{.delivered_events = {XCB_SELECTION_NOTIFY, XCB_SELECTION_NOTIFY}}, // 31
            // "Event codes 64 through 127 are reserved for extensions"
            {.delivered_events = {64,                   127}},                  // all extensions.

            {.device_events = {XCB_MOTION_NOTIFY, XCB_MOTION_NOTIFY}}, // 6
            {.device_events = {64,                127}},                  // all extensions.
        };

        xcb_void_cookie_t cookie =
            xcb_record_create_context_checked(c, rc, h, NELEMS(rcs), NELEMS(rr), rcs, rr);

        xcb_generic_error_t* error = xcb_request_check(c, cookie);
        if (error)
            errx(1, "failed to create RECORD context");

        record_context = rc;
    }

    xcb_connection_t* d = xcb_connect(argv[1], 0);
    if (xcb_connection_has_error(d))
        errx(1, "failed to create data connection to display %s", argv[1]);

    xcb_record_enable_context_cookie_t record_cookie =
        xcb_record_enable_context(d, record_context);

    for (;;) {
        if (xcb_connection_has_error(d))
            break;

        xcb_record_enable_context_reply_t* reply =
            xcb_record_enable_context_reply(d, record_cookie, 0);

        if (!reply)
            continue;

        int      len  = xcb_record_enable_context_data_length(reply);
        uint8_t* data = xcb_record_enable_context_data(reply);

        switch (reply->category) {
        case 0: // XRecordFromServer: errors, replies and events.
            printf("XRecordFromServer: len=%d\n", len);
            data_from_server_process(len, data);
            break;
        case 1: // XRecordFromClient: requests.
            printf("XRecordFromClient\n");
            break;
        case 2: // XRecordClientStarted:
            errx(1, "category ClientStarted should never happen");
            break;
        case 3: // XRecordClientDied:
            errx(1, "category ClientDied should never happen");
            break;
        case 4: // XRecordStartOfData:
            printf("XRecordStartOfData\n");
            break;
        case 5: // XRecordEndOfData:
            printf("XRecordEndOfData\n");
            break;
        default:
            errx(1, "unknown reply category %d", reply->category);
            break;
        }

        free(reply);
    }
}


Для генерации событий XKB я использовал тестовую программу 'interactive-x11' из libxkbcommon (см. Каталог теста).

Когда обе программы работают на одном и том же $ DISPLAY, яожидайте увидеть клиент RECORD, сообщающий о событиях XKEYBOARD.

Это не то, что происходит.Хотя я вижу события XInputExtension, событий XKEYBOARD нет.

ПРИМЕЧАНИЕ: быстрый просмотр - xorg-server-1.19.1 / record / record.c - xorg-server-1.19.1 / dix /events.c не находит ничего, что объяснило бы мою проблему.

Итак: моя настройка расширения RECORD неверна или расширение RECORD не работает с XKEYBOARD?

...