Interprétation des membres input_event pour les actions de la souris

J’écris un programme essentiellement basé sur les caractères, mais tournant sous xterm, et je veux utiliser les molettes de défilement de la souris, haut / bas et gauche, comme synonymes de la flèche haut / bas du clavier et le retour, juste pour un peu la commodité de l’utilisateur.

J’ai un select() avec tout le fdset d’entrée fonctionne très bien, et je capture de manière asynchrone l’amende entrée brute très bien (semble si, de toute façon). Mais j’ai un peu de mal à interpréter sans ambiguïté les membres de type, code, value de la structure input_event. /usr/include/linux/input.h semble avoir quelques EV, et je vois

  • EV_REL=0x02 (position relative lorsque la souris est déplacée)
  • EV_MSC=0x04 (divers) pour toutes les autres actions de la souris.

Question 1: Est-ce universellement vrai ??? Je n’ai pas réussi à googler quoi que ce soit à ce sujet.

Mais au-delà des EV, je ne vois rien dans /usr/include/ pour les codes, les valeurs. Mes expériences montrent ce qui suit, et je demande en outre si tout ce qui est ci-dessous est vrai (universellement). Ou mieux encore, où est la documentation (définitive) sur ce sujet? J’aurais pensé qu’il serait facile de chercher sur Google, mais je n’ai pas trouvé de réponse.

Toute action semble générer deux ou trois input_event distincts, la dernière (la deuxième ou la troisième) constituant un “suiveur” de type = code = valeur = 0. J’écris ci-dessous input_event sous forme de sortingples (type, code, valeur) …

Pour un clic gauche, vous obtenez trois événements: (4,4,589825), (1,272,1), (0,0,0). Et pour le clic gauche, vous obtenez: (4,4,589825), (1,272,0), (0,0,0). Est-ce que tout est correct? Et que diable est 589825 ???

Pour la molette de défilement, vous obtenez deux événements: (2,8,1), (0,0,0). Et pour la molette de défilement, vous obtenez: (2,8, -1), (0,0,0). (Universellement) correct, encore?

Je ne me soucie pas particulièrement des clics droits ou des mouvements de souris, ce que je vais simplement ignorer. Donc, puis-je coder en dur (avec certains symboles # définis) les éléments précédents, ou est-ce plutôt comme termcap, où cela dépend d’une manière ou d’une autre de la capacité du périphérique? Et, encore une fois, où est ce matériel documenté pour de vrai? Merci.

Modification concernant les remarques de / dev / input / mice de NominalAnimal ci-dessous

Comme NominalAnimal l’a suggéré dans sa réponse géniale (merci encore, Nominal), je lis / dev / input / event16, ce que je pensais en regardant dans le fichier / proc / bus / input / devices. Je souhaitais (et souhaitais toujours) le coder de manière plus générale, mais essayer de lire / dev / input / mice ne renvoie que trois octets par lecture, au lieu des 16 comportant une structure input_event. Au moins c’est ce qui m’est arrivé quand je l’ai fait. Et je ne pouvais imaginer aucun moyen de “décoder” ces octets pour me dire “event16”.

J’aurais donc posé cette question à l’origine, mais je me suis dit que j’avais suffisamment parlé: existe-t-il un moyen de récupérer toutes les données de / dev / input / souris que je reçois maintenant de / dev / input / event16? Ou est-il possible de déterminer par programme quel / dev / input / event? est pour la souris lors de l’initialisation (sans parsingr ce fichier / proc / fichier)?

    Je suppose que vous utilisez le sous-système de saisie d’événement (au lieu de /dev/input/mice ) parce que vous souhaitez lire directement à partir d’une souris spécifique, et non à partir de souris connectées à la machine.

    La documentation canonique se trouve dans doc / Documentation / input / event-codes.txt dans (la documentation pour) le kernel Linux. Ce lien vous amène à la page Web mise à jour.

    • type=EV_REL, code=REL_WHEEL, value=1 (2,8,1) indique une molette de défilement (verticale) par une impulsion. La valeur peut être supérieure à 1 si l’utilisateur fait tourner rapidement la molette ou s’il s’agit d’une souris programmable avec le mode de défilement “rapide”.

    • type=EV_REL, code=REL_WHEEL, value=-1 (2,8, -1) indique la roulette de défilement (verticale) d’un cran en arrière. La valeur peut être inférieure à -1 si l’utilisateur fait pivoter rapidement la molette ou s’il s’agit d’une souris programmable avec le mode de défilement “rapide”.

    • De nombreuses souris ont des roues de défilement horizontales. Celles-ci fonctionnent de la même manière que la molette de défilement verticale, sauf que le code est REL_HWHEEL .

    • Les autres type=EV_REL codes intéressants type=EV_REL sont REL_X , REL_Y , REL_Z (mouvement relatif, REL_Z étant la “hauteur” ou la distance de la table pour les souris 3D); REL_RX , REL_RY , REL_RZ pour la rotation autour de chaque axe pour des choses comme les souris 3D avec accéléromètres à six axes; et REL_DIAL pour les jog wheels.

    • type=EV_KEY, code=BTN_MOUSE, value=1 ( type=EV_KEY, code=BTN_MOUSE, value=1 272,1) indique un clic de souris (clic gauche sur une souris à boutons multiples) et value=0 (1 272,0) un relâchement.

      Le code peut également être une autre constante KEY_ ou BTN_ . value est zéro pour la libération, différent de zéro pour la presse.

      En particulier, BTN_MOUSE=BTN_LEFT , le bouton droit de la souris est BTN_RIGHT , le bouton central de la souris est BTN_MIDDLE , le bouton latéral est BTN_SIDE , le bouton supplémentaire est BTN_EXTRA , le bouton de tâche est BTN_TASK et les boutons avant et arrière (comme sur certaines souris Logitech) sont BTN_FORWARD et BTN_BACK .

    • type=EV_MSC, code=MSC_SCAN (4,4, valeur) fournit des codes de balayage du clavier pour les événements de touche / bouton non normalisés par USB / HID. Je pense que vous pouvez simplement les ignorer (ce sont souvent des doublons d’événements réels pour une raison étrange, probablement avec une compatibilité Windows antérieure).

    • type=EV_SYN, code=SYN_REPORT (0,0), est un événement de synchronisation; cela signifie qu’à ce stade, l’état de l’événement d’entrée a été complètement mis à jour.

      Vous ne recevez aucun enregistrement d’entrée ou plus, suivi d’un type=EV_SYN, code=SYN_REPORT (0,0), pour les événements survenus “au même moment”.

      En règle générale, le périphérique HID signalera les modifications sur tous les axes et tous les boutons d’un bloc, suivis de l’un d’eux. Cela a beaucoup de sens, car nous voulons que nos pointeurs se déplacent en fonction du mouvement réel, et pas seulement horizontalement / verticalement … cela aurait l’air bizarre.

    Globalement, il s’agit du sous-système d’entrée Linux et est extrêmement stable; ça ne changera pas. (De nouvelles clés, boutons, etc. peuvent être ajoutés, mais les clés existantes ne doivent jamais changer.) Les articles I et II de Linux Input Subsystem du LinuxJournal (à partir de 2003) sont toujours pertinents en tant qu’informations générales.

    Vous pouvez également utiliser l’ancienne interface de souris ( /dev/mouse , /dev/input/mouseN ou toutes les souris connectées à la machine, /dev/input/mice ). Vous devez passer le périphérique au protocole ImPS à quatre octets pour prendre en charge les trois boutons et la molette, mais rien de plus simple: il suffit d’écrire les six octets 0xf3, 200, 0xf3, 100, 0xf3, 80 et lire l’octet ACK ( 0xfa ).

    Considérez l’exemple de programme suivant. Vous pouvez spécifier le périphérique mousedev à partir duquel il doit être lu; Si aucun n’est spécifié, sa valeur par défaut est /dev/input/mice :

     #define _POSIX_C_SOURCE 200809L #include  #include  #include  #include  #include  #include  #include  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 interestd 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; } 

    Voici un extrait de la sortie sur ma machine (et une souris Logitech pas chère). x fait référence aux modifications de la coordonnée x, y aux modifications de la coordonnée y, w aux modifications de l’état de la roue, etc.

     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 

    Remarque: ceci fait simplement suite à la réponse précédente de NominalAnimal contenant son programme de démonstration illustrant la gestion de bas niveau de la souris en C. Merci, Nominal.

    Son code d’origine fonctionnait parfaitement sur ma boîte, à l’exception du bouton Milieu vers le bas. Et j’ai commencé à jouer avec le code en essayant de comprendre cela, avant de réaliser que c’était une sorte d’artefact. X utilise Middle-down pour “coller” et était en quelque sorte (je ne comprends toujours pas exactement) en train de récupérer des portions des printf précédents et de les recoller dans le stream de sortie chaque fois que vous avez appuyé sur Middle-down. Le fflush au début de chaque passage dans la boucle while () semble avoir corrigé cela. Maintenant, appuyer sur Middle-down fonctionne aussi très bien (tant que votre tampon de pâte est vide) …

     #define _POSIX_C_SOURCE 200809L #include  #include  #include  #include  #include  #include  #include  #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 interestd 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() --- */ 

    Et quelques lignes de sortie de démonstration …

     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) 

    Vous trouverez ci-dessous une autre version du code précédent de Nominal Animal, reconfigurée dans une fonction appelable par l’utilisateur mouseread () qui fournit toutes les fonctionnalités nécessaires ouverture / fermeture / lecture / interprétation des événements de souris, comme illustré par le programme de démonstration de Nominal Animal ci-dessus. Plutôt que de couper-coller (certains commentaires ci-dessous sont mal alignés, peut-être en raison de parameters de tabulation stackexchange), j’ai également placé une copie sur 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 redissortingbute 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 dissortingbuted 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  * ( AKA Jouko Orava  ) * 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 comstack 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) /* ssortingng to switch mouse to ImPS/2 protocol, * unsigned char mousessortingng[] = {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  #include  #include  #include  #include  #include  #include  /* ------------------------------------------------------------------------- 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 *mousessortingng = MOUSESTRING; /* ImPS/2 initialization ssortingng */ unsigned char mbuffer[4]={0,0,0,0}; /* read to check initialization */ int mwrtlen=strlen(mousessortingng), /* #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,mousessortingng,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 interestd 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(), comstack 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 ========================================================================== */