【问题标题】:linux uinput: simple example?linux uinput:简单的例子?
【发布时间】:2014-11-01 21:11:11
【问题描述】:

我在使用uinput 使代码的双方工作时遇到了一些问题。

基于Getting started with uinput: the user level input subsystem[死链接; archived] 我将以下 writer 放在一起(减去错误处理):

int main(int ac, char **av)
{
    int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
    int ret = ioctl(fd, UI_SET_EVBIT, EV_ABS);
    ret = ioctl(fd, UI_SET_ABSBIT, ABS_X);

    struct uinput_user_dev uidev = {0};
    snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-rotary");
    uidev.absmin[ABS_X] = 0;
    uidev.absmax[ABS_X] = 255;
    ret = write(fd, &uidev, sizeof(uidev));
    ret = ioctl(fd, UI_DEV_CREATE);

    struct input_event ev = {0};
    ev.type = EV_ABS;
    ev.code = ABS_X;
    ev.value = 42;

    ret = write(fd, &ev, sizeof(ev));

    getchar();

    ret = ioctl(fd, UI_DEV_DESTROY);
    return EXIT_SUCCESS;
}

这似乎可行,至少完整的input_event 结构似乎已经写好了。

然后我写了我能想到的最天真的读者事件:

int main(int ac, char **av)
{
    int fd = open(av[1], O_RDONLY);

    char name[256] = "unknown";
    ioctl(fd, EVIOCGNAME(sizeof(name)), name);
    printf("reading from %s\n", name);

    struct input_event ev = {0};
    int ret = read(fd, &ev, sizeof(ev));
    printf("Read an event! %i\n", ret);
    printf("ev.time.tv_sec: %li\n", ev.time.tv_sec);
    printf("ev.time.tv_usec: %li\n", ev.time.tv_usec);
    printf("ev.type: %hi\n", ev.type);
    printf("ev.code: %hi\n", ev.code);
    printf("ev.value: %li\n", ev.value);

    return EXIT_SUCCESS;
}

不幸的是,阅读器方面根本不起作用;每次只能读取 8 个字节,这几乎不是一个完整的 input_event 结构。

我犯了什么愚蠢的错误?

【问题讨论】:

    标签: linux uinput


    【解决方案1】:

    您还应该在实际事件之后编写同步事件。在您的作家端代码中:

    struct input_event ev = {0};
    ev.type = EV_ABS;
    ev.code = ABS_X;
    ev.value = 42;
    
    usleep(1500);
    
    memset(&ev, 0, sizeof(ev));
    ev.type = EV_SYN;
    ev.code = 0;
    ev.value = 0;
    
    ret = write(fd, &ev, sizeof(ev));
    
    getchar();
    

    【讨论】:

    • 为什么需要同步事件?
    • @dtmland 希望this 有所帮助。
    【解决方案2】:

    TL;DR:内核期望代码为SYN_REPORTEV_SYN 事件,因为单个事件可能被组合在一起,即当它们发生在同一时间点时。


    您可以将其视为解释一组事件的内核,而不是struct input_event 中指定的单个事件。 EV_SYN 事件以 SYN_REPORT 作为代码分隔这些事件组,即,这为同时发生的事件提供了一种分组机制。

    例如,假设您用手指触摸触摸板的表面:

    鉴于struct input_eventlinux/input.h中的定义:

    struct input_event {
    /* ... */
    __u16 type;   /* e.g., EV_ABS, EV_REL */
    __u16 code;   /* e.g., ABS_X for EV_ABS */
    __s32 value;  /* e.g., the value of the x coordinate for ABS_X */
    };
    

    不可能创建一个单一的EV_ABS 事件来同时保存代码ABS_XABS_Y,以及用于指定x 的值y 坐标,在 struct input_event1 中只有一个 code 成员。

    相反,将创建 两个 EV_ABS 事件2

    • 代码为ABS_Xx 坐标为value
    • 另一个代码为ABS_Yy 坐标为value

    将这两个事件解释为时间上分开的两个事件是不完全正确的,即一个表示 x 坐标的变化,另一个表示时间较晚并表示 y 坐标的变化。

    应该将这些事件组合在一起并由内核解释为单个输入数据更改单元,而不是两个在时间上分离的事件,这表明x y坐标同时。这就是为什么存在上述EV_SYN 机制的原因:通过在这两个EV_ABS 事件(即ABS_XABS_Y)之后生成一个带有SYN_REPORT 代码的EV_SYN 事件,我们可以进行分组他们作为一个单位。


    1这直接关系到struct input_event的大小是固定的,不能随意增长。

    2从技术上讲,可能会创建更多事件。例如,可能会创建另一个类型为 EV_KEY 且代码为 BTN_TOUCH 的事件。但是,这与我要表达的观点无关。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-18
      • 2017-08-16
      • 2016-10-28
      • 2013-04-19
      • 1970-01-01
      • 2017-03-11
      • 2014-07-18
      相关资源
      最近更新 更多