c - 鼠标 Action 的input_event成员解释

标签 c linux mouseevent

我正在编写一个本质上基于字符的程序,但在 xterm 中运行,并且想使用鼠标向上/向下滚动和左键单击作为键盘向上/向下箭头和返回的同义词,只是为了一点额外的用户便利。

我有一个 select(),所有输入 fdset 都工作正常,并且正在异步捕获原始输入(无论如何看起来是这样)。但是我在明确解释 input_event 结构中的类型、代码、值成员时遇到了一些麻烦。 /usr/include/linux/input.h 似乎有一些 EV,我看到了

  • EV_REL=0x02(鼠标移动时的相对位置)
  • EV_MSC=0x04(杂项)用于所有其他鼠标操作。

问题 1:这是否普遍适用???我没有成功地用谷歌搜索任何具体的内容。

但除了 EV 之外,我在 /usr/include/ 中没有看到任何代码和值。我的实验显示了以下内容,我进一步询问以下所有内容是否(普遍)正确。或者更好的是,关于这些东西的(权威的)文档在哪里?我原以为用谷歌搜索会很容易,但找不到答案。

任何一个 Action 似乎都会生成两个或三个单独的 input_event,最后一个(第二个或第三个)是类型 = 代码 = 值 = 0 的“预告片”。我在下面将 input_event 写为三元组(类型、代码、值)...

对于左键单击,您会得到三个事件:(4,4,589825),(1,272,1),(0,0,0)。对于左键单击释放,您将获得:(4,4,589825),(1,272,0),(0,0,0)。这一切都正确吗? 589825 到底是什么东西???

对于向上滚动轮,您会得到两个事件:(2,8,1),(0,0,0)。对于向下滚动轮,您将得到:(2,8,-1),(0,0,0)。 (普遍)正确,又一次?

我并不特别关心右键单击或鼠标移动,我只是忽略它们。那么我可以对前面的内容进行硬编码(使用一些#define'ed 符号),还是更像 termcap,它在某种程度上依赖于设备功能?而且,再一次,这些东西在哪里真实记录?谢谢。

编辑下面关于 NominalAnimal 的/dev/input/mice 的评论

正如 NominalAnimal 在他出色的回答中所建议的(再次感谢,Nominal),我正在阅读/dev/input/event16,这是我通过查看/proc/bus/input/devices 文件得出的。我想要(并且仍然想要)更一般地编写代码,但是尝试读取/dev/input/mice 每次读取仅返回三个字节,而不是包含 input_event 结构的 16 个字节。至少那是我做的时候发生的事情。而且我想不出任何方法来“解码”这些字节以告诉我“event16”。

所以我最初会问这个问题,但我想我已经说得够多了:有什么方法可以从/dev/input/mice 获取我现在从/dev/input/event16 获取的所有数据?或者有没有办法以编程方式确定哪个/dev/input/event?是在初始化期间用于鼠标(不解析该/proc/文件)?

最佳答案

您也可以使用旧式鼠标接口(interface)(/dev/mouse/dev/input/mouseN,或者从连接到机器的所有鼠标,/dev/input/mice)。您确实需要将设备切换到四字节 ImPS 协议(protocol)以支持所有三个按钮和滚轮,但这很简单:只需写入六个字节 0xf3, 200, 0xf3, 100, 0xf3, 80 , 并读取 ACK 字节 (0xfa)。

考虑以下示例程序。您可以指定它应该从中读取的 mousedev 设备;如果未指定,则默认为 /dev/input/mice:

#define  _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

static const size_t        mousedev_seq_len    = 6;
static const unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };

static volatile sig_atomic_t  done = 0;

static void handle_done(int signum)
{
    done = 1;
}

static int install_done(const int signum)
{
    struct sigaction  act;

    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = handle_done;
    act.sa_flags = 0;
    if (sigaction(signum, &act, NULL) == -1)
        return errno;

    return 0;
}

static int bytetoint(const unsigned char c)
{
    if (c < 128)
        return c;
    else
        return c - 256;
}

int main(int argc, char *argv[])
{
    unsigned char buffer[4];
    ssize_t       len;
    const char   *devpath = "/dev/input/mice";
    int           devfd;
    int           wasleft, wasmiddle, wasright;

    if (argc < 1 || argc > 2 || (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s -h | --help\n", argv[0]);
        fprintf(stderr, "       %s /dev/input/mouseX\n", argv[0]);
        fprintf(stderr, "\n");
        return EXIT_FAILURE;
    }

    if (argc == 2)
        devpath = argv[1];

    if (install_done(SIGINT) ||
        install_done(SIGTERM) ||
        install_done(SIGHUP)) {
        fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    /* Open the mouse. */
    do {
        devfd = open(devpath, O_RDWR | O_NOCTTY);
    } while (devfd == -1 && errno == EINTR);
    if (devfd == -1) {
        fprintf(stderr, "Cannot open %s: %s.\n", devpath, strerror(errno));
        return EXIT_FAILURE;
    }

    /* Switch the mouse to ImPS/2 protocol. */
    if (write(devfd, mousedev_imps_seq, mousedev_seq_len) != (ssize_t)mousedev_seq_len) {
        fprintf(stderr, "Cannot switch to ImPS/2 protocol.\n");
        close(devfd);
        return EXIT_FAILURE;
    }
    if (read(devfd, buffer, sizeof buffer) != 1 || buffer[0] != 0xFA) {
        fprintf(stderr, "Failed to switch to ImPS/2 protocol.\n");
        close(devfd);
        return EXIT_FAILURE;
    }

    /* IntelliMouse protocol uses four byte reports:
     *      Bit   7     6     5     4     3     2     1     0
     * --------+-----+-----+-----+-----+-----+-----+-----+-----
     *  Byte 0 |  0     0   Neg-Y Neg-X   1    Mid  Right Left
     *  Byte 1 |  X     X     X     X     X     X     X     X
     *  Byte 2 |  Y     Y     Y     Y     Y     Y     Y     Y
     *  Byte 3 |  W     W     W     W     W     W     W     W
     *
     * XXXXXXXX, YYYYYYYY, and WWWWWWWW are 8-bit two's complement values
     * indicating changes in x-coordinate, y-coordinate, and scroll wheel.
     * That is, 0 = no change, 1..127 = positive change +1 to +127,
     * and 129..255 = negative change -127 to -1.
     *
     * Left, Right, and Mid are the three button states, 1 if being depressed.
     * Neg-X and Neg-Y are set if XXXXXXXX and YYYYYYYY are negative, respectively.
    */

    fprintf(stderr, "Mouse device %s opened successfully.\n", devpath);
    fprintf(stderr, "Press CTRL+C (or send INT, TERM, or HUP signal to process %d) to exit.\n",
                    (int)getpid());
    fflush(stderr);

    wasleft = 0;
    wasmiddle = 0;
    wasright = 0;

    while (!done) {
        int x, y, wheel, left, middle, right;

        len = read(devfd, buffer, 4);
        if (len == -1) {
            if (errno == EINTR)
                continue;
            fprintf(stderr, "%s.\n", strerror(errno));
            break;
        } else
        if (len != 4 || !(buffer[0] & 0x08)) {
            /* We are only interested in four-byte reports,
             * that have bit 3 set in the first byte. */
            fprintf(stderr, "Warning: Ignored a %d-byte report.\n", (int)len);
            continue;
        }

        /* Unpack. */
        left = buffer[0] & 1;
        middle = buffer[0] & 4;
        right = buffer[0] & 2;
        x = bytetoint(buffer[1]);
        y = bytetoint(buffer[2]);
        wheel = bytetoint(buffer[3]);

        /* Describe: */

        if (x)
            printf(" x%+d", x);

        if (y)
            printf(" y%+d", y);

        if (wheel)
            printf(" w%+d", wheel);

        if (left && !wasleft)
            printf(" LeftDown");
        else
        if (left && wasleft)
            printf(" Left");
        else
        if (!left && wasleft)
            printf(" LeftUp");

        if (middle && !wasmiddle)
            printf(" MiddleDown");
        else
        if (middle && wasmiddle)
            printf(" Middle");
        else
        if (!middle && wasmiddle)
            printf(" MiddleUp");

        if (right && !wasright)
            printf(" RightDown");
        else
        if (right && wasright)
            printf(" Right");
        else
        if (!right && wasright)
            printf(" RightUp");

        printf("\n");
        fflush(stdout);

        wasleft = left;
        wasmiddle = middle;
        wasright = right;
    }

    /* Done. */
    close(devfd);
    return EXIT_SUCCESS;
}

这是我的机器(和一个便宜的 Logitech 鼠标)上的输出片段。 x表示x坐标的变化,y表示y坐标的变化,w表示车轮状态的变化,等等。

Mouse device /dev/input/mice opened successfully.
Press CTRL+C (or send INT, TERM, or HUP signal to process 10356) to exit.
 x-1
 x-1 y-1
 x-1
 x-2
 x-1 y-1
 x-1
 x-1
 x-1 y-1
 y+1
 y+1
 y+1
 RightDown
 x-1 Right
 x-2 y+1 Right
 x-2 Right
 x-1 Right
 y+1 Right
 x-1 Right
 x-2 Right
 x-1 Right
 x-2 Right
 x-1 Right
 y+1 Right
 x-1 Right
 x-2 Right
 x-1 Right
 RightUp
 y-1
 y-1
 LeftDown
 y-1 Left
 x+1 Left
 x+1 Left
 x+1 Left
 x+1 Left
 LeftUp
 w+1
 w+1
 w-1
 w-2
 w-1
 w+1
 w+1
 w+2
 w+1
 w+1
 w+1
 w+1
 w-1
 w-1
 w-1
 w-1
 w-1
 w-1
 w-1
 w-1

关于c - 鼠标 Action 的input_event成员解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38197517/

相关文章:

javascript - Casper.js的sendEvent在哪里定义的?

c - 如何同步以下场景

尝试释放不再需要使用的内存时出现 C 段错误

linux - Camel 2.15.2/netty3 : "java.net.SocketException: Invalid argument" from DatagramChannelImpl. send0()

linux - 递归地创建文件夹的新 tar 文件,排除另一个 tar 文件中包含的文件

python - 使用 MouseMoveEvent 移动无框 QDialog

c - 查看 C 中的头文件

Char 数组在应该为空时不为空

android - pgs4a 中的构建错误 - Linux

javascript - Leaflet - 基于鼠标滚动而不是固定级别的缩放