You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:使用libinput获取键盘信息时完全无法接收任何事件

解决libinput无法获取设备事件的问题

问题背景

使用libinput编写C++程序获取键盘相关信息,但调用libinput_resume()和libinput_dispatch()后无法收到任何事件,运行日志显示多数输入设备打开失败,少数设备打开后立即被关闭。

原问题代码

#include <cerrno>
#include <iostream>
#include <libinput.h>
#include <libudev.h>
#include <unistd.h>
#include <fcntl.h>

template<typename... Args>
void log_debug(Args&&... args)
{
    (std::clog << ... << std::forward<Args>(args)) << '\n';
}
template<typename... Args>
void log_error(Args&&... args)
{
    (std::cerr << ... << std::forward<Args>(args)) << '\n';
}


int open_restricted(const char* path, int flags, void*)
{
    log_debug("Opening device at path ", path);
    int fd = open(path, flags);
    log_debug("Device FD result : ",fd);
    return fd;
}
void close_restricted(int fd, void*)
{
    log_debug("Closing device FD : ", fd);
    close(fd);
}

const static struct libinput_interface interface = {
    .open_restricted = open_restricted,
    .close_restricted = close_restricted,
};

int main()
{
    auto udev_ctx = udev_new();
    auto libinput_ctx = libinput_udev_create_context(&interface, nullptr, udev_ctx);
    if(!udev_ctx || !libinput_ctx) {
        log_error("libinput error");
    }
    int result = libinput_udev_assign_seat(libinput_ctx, "seat0");
    if( result < 0) {
        log_error("Could not aquire udev seat!\n errno: ", result);
    }
    result = libinput_resume(libinput_ctx);
    if(result < 0) {
        log_error("Could not acquire libinput");
    }
    result = libinput_dispatch(libinput_ctx);
    if(result < 0) {
        log_error("Could not dispatch libinput");
    }
    while(auto evt = libinput_get_event(libinput_ctx)) {
        switch(libinput_event_get_type(evt)) {

        case LIBINPUT_EVENT_NONE:
        case LIBINPUT_EVENT_DEVICE_ADDED:
        case LIBINPUT_EVENT_DEVICE_REMOVED:
        default:
            {
                log_debug("HOORAY");
            }
            break;
        }
        libinput_event_destroy(evt);
    }
}

原运行日志

[17:09:49][DEBUG]       Opening device at path /dev/input/event1
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event0
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event3
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event4
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event5
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event7
[17:09:49][DEBUG]       Device FD result : 6
[17:09:49][DEBUG]       Closing device FD : 6
[17:09:49][DEBUG]       Opening device at path /dev/input/event10
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event11
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event8
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event9
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event2
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event12
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event13
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event14
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event15
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event16
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event17
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event18
[17:09:49][DEBUG]       Device FD result : -1
[17:09:49][DEBUG]       Opening device at path /dev/input/event6
[17:09:49][DEBUG]       Device FD result : -1

修复后的代码

#include <cerrno>
#include <iostream>
#include <libinput.h>
#include <libudev.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <poll.h>

template<typename... Args>
void log_debug(Args&&... args)
{
    (std::clog << ... << std::forward<Args>(args)) << '\n';
}
template<typename... Args>
void log_error(Args&&... args)
{
    (std::cerr << ... << std::forward<Args>(args)) << '\n';
}


int open_restricted(const char* path, int flags, void*)
{
    log_debug("Opening device at path ", path);
    int fd = open(path, flags);
    if (fd == -1) {
        log_error("Failed to open ", path, ": ", strerror(errno));
    }
    log_debug("Device FD result : ",fd);
    return fd;
}
void close_restricted(int fd, void*)
{
    log_debug("Closing device FD : ", fd);
    close(fd);
}

const static struct libinput_interface interface = {
    .open_restricted = open_restricted,
    .close_restricted = close_restricted,
};

int main()
{
    auto udev_ctx = udev_new();
    if (!udev_ctx) {
        log_error("Failed to create udev context");
        return 1;
    }

    auto libinput_ctx = libinput_udev_create_context(&interface, nullptr, udev_ctx);
    if (!libinput_ctx) {
        log_error("Failed to create libinput context");
        udev_unref(udev_ctx);
        return 1;
    }

    int result = libinput_udev_assign_seat(libinput_ctx, "seat0");
    if( result < 0) {
        log_error("Could not acquire udev seat! Error code: ", result);
        libinput_unref(libinput_ctx);
        udev_unref(udev_ctx);
        return 1;
    }

    result = libinput_resume(libinput_ctx);
    if(result < 0) {
        log_error("Could not resume libinput");
        libinput_unref(libinput_ctx);
        udev_unref(udev_ctx);
        return 1;
    }

    struct pollfd fds;
    fds.fd = libinput_get_fd(libinput_ctx);
    fds.events = POLLIN;

    while (true) {
        int poll_result = poll(&fds, 1, -1);
        if (poll_result == -1) {
            if (errno == EINTR) continue;
            log_error("Poll failed: ", strerror(errno));
            break;
        }

        if (fds.revents & POLLIN) {
            result = libinput_dispatch(libinput_ctx);
            if (result < 0) {
                log_error("Could not dispatch libinput");
                break;
            }

            while(auto evt = libinput_get_event(libinput_ctx)) {
                switch(libinput_event_get_type(evt)) {
                    case LIBINPUT_EVENT_DEVICE_ADDED:
                        log_debug("Device added");
                        auto dev = libinput_event_get_device(evt);
                        if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_KEYBOARD)) {
                            log_debug("Found keyboard: ", libinput_device_get_name(dev));
                        }
                        break;
                    case LIBINPUT_EVENT_DEVICE_REMOVED:
                        log_debug("Device removed");
                        break;
                    case LIBINPUT_EVENT_KEYBOARD_KEY:
                        auto key_evt = libinput_event_get_keyboard_event(evt);
                        log_debug("Key ", libinput_event_keyboard_get_key(key_evt), 
                                 " state: ", libinput_event_keyboard_get_key_state(key_evt));
                        break;
                    default:
                        log_debug("Unhandled event type: ", libinput_event_get_type(evt));
                        break;
                }
                libinput_event_destroy(evt);
            }
        }
    }

    libinput_unref(libinput_ctx);
    udev_unref(udev_ctx);
    return 0;
}

关键修复点

  • 权限问题:日志中open返回-1是因为普通用户无输入设备访问权限,执行sudo usermod -aG input $USER后重新登录即可解决。
  • 事件循环逻辑:原代码仅调用一次libinput_dispatch()就尝试取事件,正确做法是用poll监听libinput的文件描述符,有事件到来时再调度并处理。
  • 错误处理完善:在open_restricted中添加errno打印,便于定位设备打开失败原因;同时完善资源释放逻辑,避免内存泄漏。
  • 事件类型覆盖:新增键盘按键事件处理,确保能捕获键盘输入;添加设备能力判断,精准识别键盘设备。

内容的提问来源于stack exchange,提问作者user3001150

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 17:35:54