解释用于鼠标操作的input_event成员

时间:2016-07-05 07:22:28

标签: c linux mouseevent

我正在写一个基本上基于字符的程序,但是在xterm中运行,并希望使用鼠标滚轮向上/向下和左键单击作为键盘向上/向下和返回的同义词,仅用于一点额外的用户便利。

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

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

问题1:普遍存在吗?我没有成功地搜索任何有关它的内容。

但是除了EV之外,我在/usr/include/中看不到代码,值。我的实验显示了以下内容,我进一步询问下面的所有内容是否(普遍)都是正确的。或者甚至更好,关于这些东西的哪些(权威)文档?我认为google很容易,但找不到答案。

任何一个动作似乎都会生成两个或三个单独的input_event,最后一个(第二个或第三个)是一个“预告片”,其类型= code = value = 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每次读取只返回三个字节,而不是16包含input_event结构。至少那是我做的时候发生在我身上的事情。我想不出任何方法来“解码”这些字节来告诉我“event16”。

所以我原本会问这个问题,但我觉得我已经说得够了:有没有办法从/ dev / input / event16获得/ dev / input / mice的所有数据?或者有没有办法以编程方式确定哪个/ dev / input / event?是初始化期间的鼠标(没有解析/ proc /文件)?

4 个答案:

答案 0 :(得分:4)

您还可以使用旧式鼠标界面(/dev/mouse/dev/input/mouseN或连接到本机的所有鼠标/dev/input/mice)。您需要将设备切换为四字节ImPS协议以支持所有三个按钮和滚轮,但这很简单:只需写入六个字节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

答案 1 :(得分:2)

我假设您正在使用事件输入子系统(而不是/dev/input/mice),因为您希望直接从特定鼠标中读取,而不是从连接到机器的任何鼠标中读取。

规范文档位于Linux内核的doc/Documentation/input/event-codes.txt(文档)中。该链接将您带到最新的网页。

  • type=EV_REL, code=REL_WHEEL, value=1(2,8,1)表示(垂直)滚轮向前一个向前滚动。如果用户快速旋转滚轮,或者如果它是具有“快速”滚轮模式的可编程鼠标,则该值可能大于1.

  • type=EV_REL, code=REL_WHEEL, value=-1(2,8,-1)表示(垂直)向后滚动一个滚轮。如果用户快速旋转滚轮,或者它是具有“快速”滚轮模式的可编程鼠标,则该值可能小于-1。

  • 许多老鼠都有水平滚轮。它们的工作方式与垂直滚轮的工作方式相同,但代码为REL_HWHEEL

  • 其他有趣的type=EV_REL代码为REL_XREL_YREL_Z(相对移动,REL_Z为“高度”或距离表格的距离为3D老鼠); REL_RXREL_RYREL_RZ围绕每个轴旋转,适用于具有六轴加速度计的3D鼠标;和REL_DIAL用于慢跑轮。

  • type=EV_KEY, code=BTN_MOUSE, value=1(1,272,1)表示鼠标单击(左键单击多按钮鼠标),value=0(1,272,0)表示释放。

    code也可以是任何其他KEY_BTN_常量。发布时value为零,按下为非零。

    特别是,BTN_MOUSE=BTN_LEFT,鼠标右键为BTN_RIGHT,鼠标中键为BTN_MIDDLE,侧键为BTN_SIDE,额外按键为BTN_EXTRA,任务按钮为BTN_TASK,向前和向后按钮(如某些Logitech鼠标上的按钮)为BTN_FORWARDBTN_BACK

  • type=EV_MSC, code=MSC_SCAN(4,4,value)为未通过USB / HID标准化的键/按钮事件提供键盘扫描码。我相信你可以忽略这些(它们通常是由于一些奇怪的原因,实际事件的重复,可能是向后兼容Windows)。

  • type=EV_SYN, code=SYN_REPORT(0,0),是一个同步事件;这意味着此时输入事件状态已完全更新。

    对于“同时”发生的事件,您会收到零个或多个输入记录,然后是type=EV_SYN, code=SYN_REPORT(0,0)。

    通常,HID设备将报告所有轴上的更改以及一个块中的所有按钮,然后是其中一个。这很有意义,因为我们希望我们的指针根据真实的运动而移动,而不仅仅是水平/垂直...它看起来很奇怪。

总的来说,这是Linux输入子系统,非常稳定;它不会改变。 (可以添加新的键,按钮等,但现有的键不应该更改。)LinuxJournal(2003年)的Linux输入子系统文章III仍然作为背景信息。

答案 2 :(得分:1)

注意:这只是NominalAnimal前面答案的后续内容,其中包含他的演示程序,演示了C语言中的低级鼠标处理。感谢,Nominal。

他的原始代码在我的盒子上完美运行,除了中下按钮。在开始意识到它是一种神器之前,我开始搞乱代码试图解决这个问题。 X使用Middle-down进行&#34;粘贴&#34;并且以某种方式(我仍然不完全理解)抓住前面的部分printf并在每次按下Middle-down时将它们重新粘贴到输出流中。每次通过while()循环开始时的fflush似乎已经修复了。现在按下Middle-down也可以正常工作(只要你的粘贴缓冲区空了)......

#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>
#define RECOGNIZED(s) { printf(s); fflush(stdout); recognized++; }

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

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

int main(int argc, char *argv[]) {
    char          *devpath = (argc>1?argv[1]:"/dev/input/mice");
    int           msglevel = (argc>2?atoi(argv[2]):3);
    int           devfd = (-1);
    unsigned char buffer[4];
    ssize_t       len = (-1);
    int           nactions=0, wasleft=0, wasmiddle=0, wasright=0;

    /* Open the mouse. */
    fflush(NULL);
    do { devfd = open(devpath, O_RDWR | O_NOCTTY);
       } while (devfd == -1 && errno == EINTR);
    if (devfd == -1) {
        printf("Cannot open %s: %s.\n", devpath, strerror(errno));
        goto end_of_job; }

    /* Switch the mouse to ImPS/2 protocol. */
    if (write(devfd, mousedev_imps_seq, mousedev_seq_len)
    != (ssize_t)mousedev_seq_len) {
        printf("Cannot switch to ImPS/2 protocol.\n");
        goto end_of_job; }

    if (read(devfd, buffer, sizeof buffer) != 1 || buffer[0] != 0xFA) {
        printf("Failed to switch to ImPS/2 protocol.\n");
        goto end_of_job; }

    printf("Mouse device %s opened successfully.\n", devpath);
    printf("Press CTRL+C to exit.\n" /*, (int)getpid()*/ );

    /* 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.  */

    while (1) {
        int x, y, wheel, left, middle, right;
        int recognized = 0;
        nactions++;
        fflush(stdout);

        len = read(devfd, buffer, 4);
        if (len == -1) {
          if (errno == EINTR) continue;
          printf("%s.\n", strerror(errno));
          break; }
        /* We are only interested in four-byte reports,
         * that have bit 3 set in the first byte. */
        if (len != 4 || !(buffer[0] & 0x08)) {
          printf("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]);
        if ( msglevel >= 1 ) {
          printf("(%d) buffer=%02x,%02x,%02x,%02x"
                 ", xy=%d,%d, lmrw=%d,%d,%d,%d:  ",
                 nactions, buffer[0],buffer[1],buffer[2],buffer[3],
                 x,y, left,middle,right,wheel); fflush(stdout); }

        /* --- Describe: --- */
        if (x) { RECOGNIZED(" x"); printf("%+d", x); }
        if (y) { RECOGNIZED(" y"); printf("%+d", y); }
        if (wheel) { RECOGNIZED(" w"); printf("%+d", wheel); }

        if (left && !wasleft) { RECOGNIZED(" LeftDown"); }
        else if (left && wasleft) { RECOGNIZED(" Left"); }
             else if (!left && wasleft) { RECOGNIZED(" LeftUp"); }

        if (middle && !wasmiddle) { RECOGNIZED(" MiddleDown"); }
        else if (middle && wasmiddle) { RECOGNIZED(" Middle"); }
             else if (!middle && wasmiddle) { RECOGNIZED(" MiddleUp"); }

        if (right && !wasright) { RECOGNIZED(" RightDown"); }
        else if (right && wasright) { RECOGNIZED(" Right"); }
             else if (!right && wasright) { RECOGNIZED(" RightUp"); }

        printf(" (recognized %d)\n",recognized);
        wasleft=left;  wasmiddle=middle;  wasright=right;
        } /* --- end-of-while(1) --- */

    /* Done. */
    end_of_job: if ( devfd!=(-1) ) close(devfd);
    fflush(NULL);
    return EXIT_SUCCESS;
    } /* --- end-of-main() --- */

几行演示输出......

Mouse device /dev/input/mice opened successfully.
Press CTRL+C to exit.
(1) buffer=0c,00,00,00, xy=0,0, lmrw=0,4,0,0:   MiddleDown (recognized 1)
(2) buffer=08,00,00,00, xy=0,0, lmrw=0,0,0,0:   MiddleUp (recognized 1)
(3) buffer=0c,00,00,00, xy=0,0, lmrw=0,4,0,0:   MiddleDown (recognized 1)
(4) buffer=08,00,00,00, xy=0,0, lmrw=0,0,0,0:   MiddleUp (recognized 1)
(5) buffer=09,00,00,00, xy=0,0, lmrw=1,0,0,0:   LeftDown (recognized 1)
(6) buffer=08,00,00,00, xy=0,0, lmrw=0,0,0,0:   LeftUp (recognized 1)
(7) buffer=09,00,00,00, xy=0,0, lmrw=1,0,0,0:   LeftDown (recognized 1)
(8) buffer=08,00,00,00, xy=0,0, lmrw=0,0,0,0:   LeftUp (recognized 1)
(9) buffer=0a,01,00,00, xy=1,0, lmrw=0,0,2,0: x+1 RightDown(recognized 2)
(10) buffer=08,00,00,00, xy=0,0, lmrw=0,0,0,0:   RightUp (recognized 1)
(11) buffer=0a,00,00,00, xy=0,0, lmrw=0,0,2,0:   RightDown (recognized 1)
(12) buffer=08,00,00,00, xy=0,0, lmrw=0,0,0,0:   RightUp (recognized 1)
(13) buffer=18,ff,00,00, xy=-1,0, lmrw=0,0,0,0:   x-1 (recognized 1)
(14) buffer=18,ff,00,00, xy=-1,0, lmrw=0,0,0,0:   x-1 (recognized 1)
(15) buffer=18,fe,00,00, xy=-2,0, lmrw=0,0,0,0:   x-2 (recognized 1)
(16) buffer=18,fd,00,00, xy=-3,0, lmrw=0,0,0,0:   x-3 (recognized 1)
(17) buffer=18,fd,00,00, xy=-3,0, lmrw=0,0,0,0:   x-3 (recognized 1)

答案 3 :(得分:1)

下面是Nominal Animal之前代码的另一个版本,重新计入用户可调用函数mouseread(),它提供所有必要功能打开/关闭/读取/解释鼠标事件,如Nominal Animal&#所示39;上面的演示程序。而不是剪切和粘贴(下面的一些评论未对齐,可能是由于stackexchange选项卡设置),我还在http://www.forkosh.com/mouseread.c

放置了副本
/****************************************************************************
 *
 * Copyright(c) 2016-2016, John Forkosh Associates, Inc. All rights reserved.
 *           http://www.forkosh.com   mailto: john@forkosh.com
 * --------------------------------------------------------------------------
 * This file is mouseread.c, which is free software.
 * You may redistribute and/or modify mouseread.c under the
 * terms of the GNU General Public License, version 3 or later,
 * as published by the Free Software Foundation.
 *      mouseread.c is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY, not even the implied warranty of MERCHANTABILITY.
 * See the GNU General Public License for specific details.
 *      By using mouseread.c, you warrant that you have read, understood
 * and agreed to these terms and conditions, and that you possess the legal
 * right and ability to enter into this agreement and to use mouseread.c
 * in accordance with it.
 *      To read the GNU General Public License, version 3, point your
 * browser to  http://www.gnu.org/licenses/  or write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330,  Boston, MA 02111-1307 USA.
 * --------------------------------------------------------------------------
 *
 * Purpose:   o mouseread.c, licensed under the gpl, reads and interprets
 *      mouse events for the calling application-level program
 *      (see Notes below for further usage instructions).
 *         Thanks: mouseread.c is based on code supplied by
 *      Nominal Animal <question@nominal-animal.net>
 *      ( AKA Jouko Orava <jouko.orava@iki.fi> )
 *      in  http://stackoverflow.com/questions/38197517/
 *
 * Functions:   The following "table of contents" lists each function
 *      comprising mouseread.c in the order it appears in this file.
 *      See individual function entry points for specific comments
 *      about its purpose, calling sequence, side effects, etc.
 *      =============================================================
 *              --- primary user-callable function ---
 *      mouseread(action)                   open/close/read/etc mouse
 *              ---  helper (static in this module) functions ---
 *      mouseopen(void)                             open mouse device
 *      mouseget(fd)                  read and interpret mouse device
 *              --- main() test driver ---
 *      #if defined(TESTMOUSEREAD)
 *        main(argc,argv)                                 test driver
 *      #endif
 *      =============================================================
 *
 * Source:  mouseread.c
 *
 * --------------------------------------------------------------------------
 * Notes      o See individual function entry points for specific comments
 *      about the purpose, calling sequence, side effects, etc
 *      of each mouseread.c function listed above.
 *        o compile as
 *         cc yourprogram.c mouseread.c -o yourprogram
 *      or for test driver
 *         cc -DTESTMOUSEREAD mouseread.c -o mouseread
 *        o mouseread() opens /dev/input/mice by default
 *      make sure /dev/input/mice is chmod o+rw,
 *      or else mouseread() will fail. To use another device,
 *         cc -DMOUSEDEV=\"/dev/input/mouse0\" etc.
 *      (which must also be chmod o+rw)
 *        o in yourprogram.c write the two lines
 *         #define _MOUSEHEADERS
 *         #include "mouseread.c"
 *      to get the defined symbols for recognized mouseread() actions.
 *      there's no separate mouseread.h file.
 *        o --- usage instructions ---
 *      Initialization:
 *         First make sure to follow the instructions immediately
 *       above to include the header information defining recognized
 *       mouseread() actions.
 *         Then the basic declaration your program should contain
 *       is of the form
 *           int fd=(-1), result=(-1), mouseread(int);
 *       And initialization consists of the single line
 *           fd = mouseread(_MOUSE_OPEN);
 *       which should be issued just once, and which returns
 *       the file descriptor of the open()'ed MOUSEDEV, or -1=error.
 *       You can combine declaration/initialization into one line
 *           int result=(-1), mouseread(int), fd=mouseread(_MOUSE_OPEN);
 *       And you won't need fd unless issuing a select(), or using
 *       it with other non-blocking i/o mechanisms, etc.
 *      Action _MOUSE_GET:
 *           result = mouseread(_MOUSE_GET);
 *       reads the next queued mouse event, returning
 *       +1=successful read, -1=some_i/o_error.
 *       The nature of that event is determined by interrogations
 *       performed by the following calls. You can issue as many
 *       of these calls as you like against the current event.
 *       The next event isn't read until you issue
 *       the next _MOUSE_GET action.
 *      Action _MOUSE_LEFT:
 *           result = mouseread(_MOUSE_LEFT);
 *       interrogates the left mouse button, returning
 *        0 = button was up, and button remains up,
 *       +1 = button was up, and was just pressed down,
 *       -1 = button was down, and was just released,
 *       99 = button was down, and remains down.
 *      Actions _MOUSE_RIGHT, _MOUSE_MIDDLE:
 *           result = mouseread(_MOUSE_RIGHT);
 *           result = mouseread(_MOUSE_MIDDLE);
 *       interrogates the right or middle mouse button, returning
 *       the same results as above for _MOUSE_LEFT.
 *      Action _MOUSE_X:
 *           result = mouseread(_MOUSE_X);
 *        0 = no x-axis movement,
 *       +1,+2,+3,+etc = right x-axis movement by 1,2,3,etc pixels
 *       -1,-2,-3,-etc =  left x-axis movement by 1,2,3,etc pixels
 *      Action _MOUSE_Y:
 *           result = mouseread(_MOUSE_Y);
 *        0 = no y-axis movement,
 *       +1,+2,+3,+etc =    up y-axis movement by 1,2,3,etc pixels
 *       -1,-2,-3,-etc =  down y-axis movement by 1,2,3,etc pixels
 *      Action _MOUSE_WHEEL:
 *           result = mouseread(_MOUSE_WHEEL);
 *        0 = no wheel movement,
 *       +1,+2,+3,+etc =  down  wheel movement by 1,2,3,etc pixels
 *       -1,-2,-3,-etc =    up  wheel movement by 1,2,3,etc pixels
 *       note: +/- for wheel has opposite meaning as for y-axis
 *       (that appears to be the standard).
 *      Exit:
 *       Just issue the single call
 *           mousread(_MOUSE_CLOSE);
 * --------------------------------------------------------------------------
 * Revision History:
 * 07/05/16 J.Forkosh   Installation.
 * 07/09/16 J.Forkosh   Most recent revision
 *
 ****************************************************************************/

/* ---
 * header information: no mouseread.h file, instead...
 *  #define _MOUSEHEADERS
 *  #include "mouseread.c"
 * ----------------------------------------------------------------------- */
/* --- recognized mousread() actions --- */
#define _MOUSE_GET (128)        /* read (wait for) next mouse event */
#define _MOUSE_OPEN (1)         /* initialize mouse */
#define _MOUSE_CLOSE ((_MOUSE_OPEN)+1)  /* close mouse device file */
#define _MOUSE_LEFT ((_MOUSE_CLOSE)+1)  /* +1,-1,0 if left pressed,released */
#define _MOUSE_RIGHT ((_MOUSE_LEFT)+1)  /* +1,-1,0 if right pressed,released*/
#define _MOUSE_MIDDLE ((_MOUSE_RIGHT)+1)/* +1,-1,0 if middle pressed,released*/
#define _MOUSE_X ((_MOUSE_MIDDLE)+1)    /* +,-,0  right,left x-axis movement */
#define _MOUSE_Y ((_MOUSE_X)+1)     /* +,-,0  up,down y-axis movement */
#define _MOUSE_WHEEL ((_MOUSE_Y)+1) /* +,-,0  down,up wheel movement */
/* note: the LEFT,RIGHT,MIDDLE have an additional return value 99
   if the corresponding button was pressed down and remains pressed down */
/* --- end-of-header-information --- */

#if !defined(_MOUSEHEADERS)
/* -------------------------------------------------------------------------
Possibly device/installation-dependent constants
-------------------------------------------------------------------------- */
#if !defined(MOUSESTRING)
  /* string to switch mouse to ImPS/2 protocol,
   * unsigned char mousestring[] = {0xf3,200, 0xf3,100, 0xf3,80};... */
  #define MOUSESTRING "\xf3\xc8\xf3\x64\xf3\x50"
#endif
#if !defined(MOUSEDEV)
  /* must be chmod 666, i.e., o+rw... */
  #define MOUSEDEV "/dev/input/mice"
#endif

/* -------------------------------------------------------------------------
standard headers
-------------------------------------------------------------------------- */
#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>

/* -------------------------------------------------------------------------
helper functions
-------------------------------------------------------------------------- */
#define bytetoint(c) ( ( ((int)(c)) < 128? ((int)(c)) : ((int)(c))-256 ) )
static  int mouseopen(void);        /* open mouse device file */
static  int mouseget(int);      /* get mouse device */
/* -------------------------------------------------------------------------
global data -- read/interpreted by mouseget(), returned to user by mouseread()
-------------------------------------------------------------------------- */
static  int x=0,y=0,wheel=0, left=0,right=0,middle=0; /* coord,button states */
static  int wasleft=0, wasright=0, wasmiddle=0; /* previous button states */

/* ==========================================================================
 * Function:    mouseread ( int action )
 * Purpose: open/read/close mouse device
 * --------------------------------------------------------------------------
 * Arguments:   action (I)  int specifying action,
 *              see Notes comments above for complete
 *              usage instructions.
 * --------------------------------------------------------------------------
 * Returns: ( int )     result of action (see Notes above)
 * --------------------------------------------------------------------------
 * Notes:     o caller can |OR (or +add) _MOUSE_GET to any interrogation
 *      request to read the next packet >>before<< checking, e.g.,
 *        instead of: mouseread(_MOUSE_GET); mouseread(_MOUSE_LEFT);
 *        just write: mouseread(_MOUSE_GET|_MOUSE_LEFT);
 *      You can >>only<< OR (or +add) _MOUSE_GET with one other action.
 *        o _MOUSE_GET will first perform a _MOUSE_OPEN if the mouse
 *      device has not already been open()'ed. But you won't get
 *      back the mouse fd. So if you need that for a select() or
 *      other purpose, make sure to issue a separate _MOUSE_OPEN
 *      and save the returned fd.
 * ======================================================================= */
/* --- entry point --- */
int mouseread ( int action  ) {
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
static  int fd = (-1);          /* fd of mouse device file after open */
int isget  = 0;         /* set true if _MOUSE_GET requested */
int result = (-1);          /* result of action, -1=error */
/* -------------------------------------------------------------------------
check if _MOUSE_GET has been |or'ed or +added to action
-------------------------------------------------------------------------- */
if ( action >= _MOUSE_GET ) {       /* _MOUSE_GET requested */
  isget = 1;                /* set flag to call mouseget() */
  action -= _MOUSE_GET; }       /* any remaining action */
/* -------------------------------------------------------------------------
open/close mouse device
-------------------------------------------------------------------------- */
if ( action == _MOUSE_OPEN ) {      /* open request */
  if ( fd == (-1) )         /* not yet opened */
    fd = mouseopen();           /* try to open it */
  result = fd;              /* return fd to caller */
  /* --- re-init global variables --- */
  x=y=wheel=0; left=right=middle=0; /* reset coord,button states */
  wasleft=wasright=wasmiddle = 0;   /* reset previous button states */
  } /* --- end-of-if(action==_MOUSE_OPEN) --- */
if ( action == _MOUSE_CLOSE ) {     /* close request */
  if ( fd != (-1) )         /* opened */
    close(fd);              /* close it */
  fd = (-1);                /* reset fd to signal closed */
  result = 1;               /* signal success to caller */
  } /* --- end-of-if(action==_MOUSE_CLOSE) --- */
/* -------------------------------------------------------------------------
read mouse device
-------------------------------------------------------------------------- */
if ( isget ) {              /* read mouse event */
  if ( fd == (-1) )         /* caller maybe forgot _MOUSE_OPEN */
    fd = mouseopen();           /* try to open it */
  if ( fd != (-1) )         /* opened */
    result = mouseget(fd);      /* read */
  result = 1;               /* signal success to caller */
  } /* --- end-of-if(action==_MOUSE_GET) --- */
/* -------------------------------------------------------------------------
determine current state of mouse buttons
-------------------------------------------------------------------------- */
/* --- left button --- */
if ( action == _MOUSE_LEFT ) {      /* check left mouse button */
  result = 0;               /* left button up and remains up */
  if (left && !wasleft) result=(+1);    /* left button pressed down */
  else if (left && wasleft) result=99;  /* left down and remains down */
  else if (!left && wasleft) result=(-1); /* left button released */
  } /* --- end-of-if(action==_MOUSE_LEFT) --- */
/* --- right button --- */
if ( action == _MOUSE_RIGHT ) {     /* check right mouse button */
  result = 0;               /* right button up and remains up */
  if (right && !wasright) result=(+1);  /* right button pressed down */
  else if (right && wasright) result=99; /* right down and remains down */
  else if (!right && wasright) result=(-1); /* right button released */
  } /* --- end-of-if(action==_MOUSE_RIGHT) --- */
/* --- middle button --- */
if ( action == _MOUSE_MIDDLE ) {    /* check middle mouse button */
  result = 0;               /* middle button up and remains up */
  if (middle && !wasmiddle) result=(+1); /* middle button pressed down */
  else if (middle && wasmiddle) result=99; /* middle down and remains down */
  else if (!middle && wasmiddle) result=(-1); /* middle button released */
  } /* --- end-of-if(action==_MOUSE_MIDDLE) --- */
/* -------------------------------------------------------------------------
determine current x,y,z(wheel)-axis movements
-------------------------------------------------------------------------- */
if ( action == _MOUSE_X ) {     /* check for x-axis movement */
  result = x;               /* 0=none, or +/- x-axis movement */
  } /* --- end-of-if(action==_MOUSE_X) --- */
if ( action == _MOUSE_Y ) {     /* check for y-axis movement */
  result = y;               /* 0=none, or +/- y-axis movement */
  } /* --- end-of-if(action==_MOUSE_Y) --- */
if ( action == _MOUSE_WHEEL ) {     /* check for z-axis/wheel movement */
  result = wheel;           /* 0=none, or +/- wheel movement */
  } /* --- end-of-if(action==_MOUSE_WHEEL) --- */
end_of_job:
  return ( result );            /* result of action back to caller */
} /* --- end-of-function mouseread() --- */


/* ==========================================================================
 * Function:    mouseopen ( void )
 * Purpose: open mouse device
 * --------------------------------------------------------------------------
 * Arguments:   void (I)    no args
 * --------------------------------------------------------------------------
 * Returns: ( int )     fd of open()'ed mouse, or -1=error
 * --------------------------------------------------------------------------
 * Notes:     o MOUSEDEV, e.g., "/dev/input/mice", must be chmod o+rw
 * ======================================================================= */
/* --- entry point --- */
static  int mouseopen ( void  ) {
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
char    *mousedev = MOUSEDEV;       /* path to mouse device file */
unsigned char *mousestring = MOUSESTRING; /* ImPS/2 initialization string */
unsigned char mbuffer[4]={0,0,0,0}; /* read to check initialization */
int mwrtlen=strlen(mousestring),    /* #initialization bytes to write */
    mrdlen = (-1);          /* #bytes read from mouse device */
int fd = (-1);          /* fd of open()'ed device, -1=error */
/* --- open the mouse device file --- */
do { fd = open(mousedev, O_RDWR | O_NOCTTY); /* open for read/write */
  } while ( fd==(-1) && errno==EINTR ); /* retry if interrupted */
if ( fd==(-1) ) goto end_of_job;    /* failed to open mouse device file */
/* --- switch mouse to ImPS/2 protocol --- */
if ( write(fd,mousestring,mwrtlen) == mwrtlen ) /* write the request, */
   mrdlen = read(fd,mbuffer,4);             /*   read the reply */
if ( mrdlen != 1 || mbuffer[0] != 0xFA) {       /* check for success, */
   close(fd); fd=(-1); goto end_of_job; }       /*   if failed then die :) */
/* --- back to caller --- */
end_of_job:
  return ( fd );            /* fd or -1=error back to caller */
} /* --- end-of-function mouseopen() --- */


/* ==========================================================================
 * Function:    mouseget ( int fd )
 * Purpose: read and interpret mouse device
 * --------------------------------------------------------------------------
 * Arguments:   fd (I)      fd of open()'ed mouse device file
 * --------------------------------------------------------------------------
 * Returns: ( int )     get status
 * --------------------------------------------------------------------------
 * Notes:     o 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.
 * ======================================================================= */
/* --- entry point --- */
static  int mouseget ( int fd  ) {
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
unsigned char mbuffer[4]={0,0,0,0}; /* read buffer */
int mrdlen = (-1);          /* #bytes read from mouse device */
int status = (-1);          /* i/o status */
/* --- reset previous button states --- */
wasleft=left;  wasmiddle=middle;  wasright=right; /* previous button states */
/* -------------------------------------------------------------------------
read four bytes into mbuffer
-------------------------------------------------------------------------- */
/* --- read --- */
if ( fd == (-1) ) goto end_of_job;  /* invalid/not_open fd */
do { mrdlen = read(fd,mbuffer,4);   /* read four-byte protocol */
  } while ( mrdlen==(-1) && errno==EINTR ); /* retry if interrupted */
if ( mrdlen == (-1) ) goto end_of_job;  /* return -1 error if read failed */
/* --- only interested in four-byte reports with bit 3 set in 1st byte --- */
if ( mrdlen != 4 || !(mbuffer[0] & 0x08)) { /* unwanted packet */
  status=0; goto end_of_job; }      /* signal 0="nothing" to caller */
/* -------------------------------------------------------------------------
interpret the fields of the four-byte report packet
-------------------------------------------------------------------------- */
x=y=wheel=0; left=right=middle=0;   /* reset global coord,button states */
left   = mbuffer[0] & 1;        /* bit#0 (low-order bit) of 1st byte */
right  = mbuffer[0] & 2;        /* bit#1 of 1st byte */
middle = mbuffer[0] & 4;        /* bit#2 of 1st byte */
x      = bytetoint(mbuffer[1]);     /* 2nd byte */
y      = bytetoint(mbuffer[2]);     /* 3rd byte */
wheel  = bytetoint(mbuffer[3]);     /* 4th byte */
/* --- back to caller --- */
status = 1;             /* success */
end_of_job:
  return ( status );            /* read status */
} /* --- end-of-function mouseget() --- */
#endif  /* --- #if !defined(_MOUSEHEADERS) --- */


#if defined(TESTMOUSEREAD)
/* ==========================================================================
 * Function:    main ( int argc, char *argv[] )
 * Purpose: test driver for mouseread()
 * --------------------------------------------------------------------------
 * Arguments:   argc (I)    (int) containing the usual...  unused
 *      argv (I)    (char **) containing...        unused
 * --------------------------------------------------------------------------
 * Returns: ( int )     exit(1) status
 * --------------------------------------------------------------------------
 * Notes:     o exercises mouseread(), compile as
 *      cc -DTESTMOUSEREAD mouseread.c -o mouseread
 * ======================================================================= */
/* --- entry point --- */
int main ( int argc, char *argv[] ) {
/* -------------------------------------------------------------------------
Allocations and Declarations
-------------------------------------------------------------------------- */
int msglevel = (argc>1?atoi(argv[1]):3);  /* currently unused */
int mouseread(int), fd = mouseread(_MOUSE_OPEN);
int result=0, nactions=0;
/* -------------------------------------------------------------------------
interrogate mouse
-------------------------------------------------------------------------- */
if ( fd < 0 ) {             /* probably forgot to chmod o+rw */
  if(msglevel>0) printf("Unable to open %s\n",MOUSEDEV);  goto end_of_job; }
if(msglevel>0) printf("Move,click,etc mouse. Ctrl-C to exit...\n");
while ( 1 ) {
  nactions++;
  printf("(%d) ",nactions);
  result = mouseread(_MOUSE_GET|_MOUSE_X);  /*GET the next queued mouse event*/
    if (result!=0) printf(" x%+d", result); /* | and check for x-axis movement*/
  result = mouseread(_MOUSE_Y);             /* check for y-axis movement */
    if (result!=0) printf(" y%+d", result); /* 0=none, + = up, - = down */
  result = mouseread(_MOUSE_WHEEL);         /* check for wheel movement */
    if (result!=0) printf(" w%+d", result); /* 0=none, + = up, - = down */
  result = mouseread(_MOUSE_LEFT);          /* check left button */
    if (result!=0) printf( " %s",           /* see Notes for return values */
      (result==1?"LEFT-PRESSED":(result<0?"LEFT-RELEASED":"LEFT-DOWN")) );
  result = mouseread(_MOUSE_RIGHT);         /* check right button */
    if (result!=0) printf( " %s",
      (result==1?"RIGHT-PRESSED":(result<0?"RIGHT-RELEASED":"RIGHT-DOWN")) );
  result = mouseread(_MOUSE_MIDDLE);        /* check middle button */
    if (result!=0) printf( " %s",
      (result==1?"MIDDLE-PRESSED":(result<0?"MIDDLE-RELEASED":"MIDDLE-DOWN")) );
  printf("\n"); fflush(stdout);             /* ready to GET next event */
  } /* --- end-of-while(1) --- */
end_of_job:
  mouseread(_MOUSE_CLOSE);
  exit(1);
} /* --- end-of-function main() --- */
#endif  /* --- #if defined(TESTMOUSEREAD) --- */
/* =======================================================================
END-OF-FILE MOUSEREAD.C
========================================================================== */