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

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

我有一个select()与所有inputfdset工作正常,并asynchronous捕获原始input罚款(似乎是这样,无论如何)。 但是,我明确地解释了input_event结构中的types,代码和值成员,我有点麻烦。 /usr/include/linux/input.h似乎有一些电动汽车,我看到了

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

问题1:这是普遍真实的吗? 我没有成功search任何关于这方面的具体内容。

但是除了EV之外,我在/usr/include/不到任何代码,值。 我的实验显示以下内容,并且我进一步询问下面的所有内容是否(普遍)是真实的。 或者甚至更好,哪里(明确的)关于这个东西的文档? 我以为这会很容易谷歌,但无法find答案。

任何一个动作似乎都会生成两个或三个单独的input_event,最后一个(第二个或第三个)types= code = value = 0的“尾部”。 我正在写input_event下面的三元组(types,代码,值)…

对于左键点击,你会得到三个事件:(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)。 (普遍)正确,再次?

我不特别关心右键单击或鼠标移动,我只是将其忽略。 所以,我可以硬编码(用一些#定义的符号)前面的东西,还是更像termcap,它的设备能力依赖于某种方式? 而且,这个东西在哪里logging下来呢? 谢谢。

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

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

所以我原本会问这个问题,但是我觉得我已经说得够多了:有没有办法从/ dev / input / event16中获取所有从/ dev / input / 还是有什么办法来编程确定哪个/ dev / input / event? 是初始化期间的鼠标(不parsing/ proc /文件)?

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

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

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

  • type=EV_REL, code=REL_WHEEL, value=-1 (2,8,-1)指示(垂直)滚轮后退一个type=EV_REL, code=REL_WHEEL, value=-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 ,前进和后退键(比如一些罗技鼠标)是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输入子系统文章I和II仍作为背景信息相关。

您还可以使用旧式鼠标接口( /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 | XXXXXXXX * Byte 2 | YYYYYYYY * Byte 3 | WWWWWWWW * * 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; } 

这里是我的机器上的输出(和一个便宜的罗技鼠标)的片段。 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 

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

他的原始代码在我的盒子上完美工作,除了中下按钮。 我开始搞乱了代码,试图弄清楚,然后才意识到这是一种神器。 X使用Middle-down作为“粘贴”,并以某种方式(我仍然不明白)抓住前面的printf的部分,并在每次按下中间按钮时将它们重新粘贴到输出流中。 在while()循环的每次传递开始时,fflush似乎已经修复了这个问题。 现在按下中下也工作正常(只要你的粘贴缓冲区的空)…

 #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 | XXXXXXXX * Byte 2 | YYYYYYYY * Byte 3 | WWWWWWWW * * 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) 

下面是Nominal Animal之前代码的另一个版本,重新分解为一个用户可调用函数mouseread(),提供所有必要的功能open / close / read / interpret鼠标事件,如上面的Nominal Animal的演示程序所示。 而不是剪切和粘贴(下面的一些评论是错位的,也许是由于stackexchange标签设置),我也放在http://www.forkosh.com/mouseread.c复&#x5236;

 /**************************************************************************** * * 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, ie, 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, eg, * 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, eg, "/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 | XXXXXXXX * Byte 2 | YYYYYYYY * Byte 3 | WWWWWWWW * 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 ========================================================================== */