#include "arch.h" #ifdef LINUX #include <dirent.h> #include <errno.h> #include <stdio.h> #include <stdio_ext.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <linux/input.h> #include <sys/select.h> #include <poll.h> #include <sys/signal.h> #include <sys/types.h> #include <termios.h> #include <pthread.h> #include "inputevent.h" #include "util/vt100.h" struct sigaction sigint_action, sighup_action, sigterm_action; static void (*sig_exit_function)(void)=NULL; // Tätä kutsutaan kun Ctrl+C painettu, jos määritelty static pthread_attr_t attr; static pthread_t thread; static int num_event_devices; struct pollfd epollfd\[20\]; struct einfo { int fd; int devices; // 1=key, 2=mouse, 4=led, 8=joy, 16=syn } evdev_info\[20\]; static int ledfd=0; static unsigned int ledstates=0; int evmousex=0; // voidaan lukea suoraan ilman funktiokutsua int evmousey=0; int evmwheel=0; int evmlbutton=0; int evmrbutton=0; int evmmbutton=0; unsigned int eventdebug=0; static unsigned char keystate\[255\]={0}; //static struct sigaction saio,oldsaio; static struct termios tios; //void (*evfunction)(int , int )=NULL; static unsigned int evtype_bitmask\[EV_MAX/8 + 1\]; //static int oldrep\[2\]; static int eventsdisabled=0; // ASCII-koodien mappaukset näppäimistön scankoodeihin // Saatetaan ehkä joskus tarvita // myös toisin päin olisi tarpeen tehdä vastaava taulukko //static unsigned char keytable\[128\] = //{ //0,0,0,0,0,0,0,0,0,0, //0,0,0,0,0,0,0,0,0,0, //0,0,0,0,0,0,0,0,0,0, //0,0,57,0,0,0,0,0,0,0, //0,0,0,0,51,12,52,0,11,2, // 1,0 //3,4,5,6,7,8,9,10,0,0, // 2-9 //0,0,0,0,0,30,48,46,32,18, //-E //33,34,35,23,36,37,38,50,49,24, //F-O //25,16,19,31,20,22,47,17,45,21, //P-Y //44,0,0,0,0,0,0,30,48,46, //Z-c //32,18,33,34,35,23,36,37,38,50, //d-m //49,24,25,16,19,31,20,22,47,17, //n-w //45,21,44,0,0,0,0,0 //x-z //}; /// Rekisteröidään exit funktio kun esim. ohjelmassa painetaan Ctrl+C. /// Funktiota kutsutaan tällöin autimaattisesti kun Ctrl+C:tä on painettu. /// Koska Ctrl+C lopettaa ohjelman toiminnan voidaan lopetus hoitaa siististi tämän avulla. void register_signal_exit(void (f) (void)) { sig_exit_function = f; } // Palauttaa mousen X-koordinaatin arvon int getmousex(void) { return evmousex; } // Palauttaa mousen Y-koordinaatin arvon int getmousey(void) { return evmousey; } // Palauttaa mousen wheelin laskurin arvon int getmousewheel(void) { return evmwheel; } /// Asetetaan eventtien debuggaus. /// Bittien arvot: 0=none 1=key 2=mouse 4=joystic. void eventdebugmode(unsigned int mode) { eventdebug=mode; } /// Asetetaan mousen x ja y positio arvot halutuiksi. void resetmousepos(int x, int y) { evmousex=x; evmousey=y; } /// Palauttaa true jos näppäin on painettuna. bool key(keymap key) { return keystate\[key\] & 1; } /// Palauttaa true jos näppäin on painettuna. bool key(unsigned char key) { return keystate\[key\] & 1; } /// Palauttaa true vain kerran kun näppäimen tila on vaihtuu alas tilaan bool keydown(keymap key) { if (keystate\[key\]&2) { // Nollataan alaspainamistieto keystate\[key\] &= \~2; return true; } return false; } /// Palauttaa true vain kerran kun näppäimen tila on vaihtuu ylös tilaan. bool keyup(keymap key) { if (keystate\[key\]&4) { // Nollataan ylösnousu tieto keystate\[key\] &= \~4; return true; } return false; } /// Palauttaa true vain kerran kun näppäimen tila on vaihtuu alas tilaan. bool keydown(unsigned char key) { if (keystate\[key\]&2) { // Nollataan alaspainamistieto keystate\[key\] &= \~2; return true; } return false; } /// Palauttaa true vain kerran kun näppäimen tila on vaihtuu ylös tilaan. bool keyup(unsigned char key) { if (keystate\[key\]&4) { // Nollataan ylösnousu tieto keystate\[key\] &= \~4; return true; } return false; } /// Voidaan asettaa näppäimistön ledit. /// Bittiarvot 1=NumLock 2=CapsLoc 4=ScrollLock 8=Compose. void setled(unsigned int ledparam) { int retval; struct input_event ev; // if (ledfd \!= keyfd) { printf("NO keyboard LED support \!\n"); return; } ev.type = EV_LED; if ((ledparam&1) \!= (ledstates&1)) { ev.code = LED_NUML; ledparam&1 ? ev.value = 1 : ev.value = 0 ; retval = write(ledfd, &ev, sizeof(struct input_event)); } if ((ledparam&2) \!= (ledstates&2)) { ev.code = LED_CAPSL; ledparam&2 ? ev.value = 1 : ev.value = 0 ; retval = write(ledfd, &ev, sizeof(struct input_event)); } if ((ledparam&4) \!= (ledstates&4)) { ev.code = LED_SCROLLL; ledparam&4 ? ev.value = 1 : ev.value = 0 ; retval = write(ledfd, &ev, sizeof(struct input_event)); } if ((ledparam&8) \!= (ledstates&8)) { ev.code = LED_COMPOSE; ledparam&8 ? ev.value = 1 : ev.value = 0 ; retval = write(ledfd, &ev, sizeof(struct input_event)); } } /// Haetaan näppäimistön ledien tämän hetkiset tilat. void getled(unsigned int &ledparam) { ledparam=ledstates; } /// Käännetään haluttujen näppäimistön ledien tila käänteiseksi. /// Voidaan helposti vilkuttaa niitä. void negled(unsigned int ledparam) { ledparam=ledstates ^ ledparam ; setled(ledparam); } /// Linuxin blokkaava eventtien luku Thread. /// Luetaan Linuxin kernelin Event-interfacesta näppäimien, mousen, joystikin ja näppäimistön ledien tilat. /// Käytämme tätä funktiota useiden eri oheislaitteiden eventtien samanaikaiseen lukemiseen. \n /// Signaalien käyttö saattaa aiheuttaa interrupted system calls erroreita muissa saman prosessin funktioissa. /// Siksi käytämme Linuxin threadia ja tämä funktio toimii erillisenä threadina joka lukee blokkaavasti koko /// ajan mahdollisia näppäimistön, mousen tai joystikin tapahtumia. /// static void readevent(int sig, siginfo_t * si , void * data) // jos halutaan käyttää realiaika-signaaleja static void \*readevent(void * data) // Tämä funktio on linuxin thread { int r; struct input_event ev\[17\]; int num; int eventcount; printf("inputevent: Thread started\n"); while (true) { do { r = poll(epollfd, num_event_devices, \-1); } while (r < 0); /// Eventtien käsittely for ( int i=0; i<num_event_devices; i++) if (epollfd\[i\].revents == POLLIN) { epollfd\[i\].revents = 0; r = read(epollfd\[i\].fd, ev, sizeof(input_event)*16); if (r < 0) continue; eventcount = r / sizeof(input_event); for(num=0; num<eventcount ; num++) { if(eventdebug & 1) { VT100::set_fg_color(VT100::MAGENTA); printf("code=%3i ",ev\[num\].code); printf("value=%3i ",ev\[num\].value); printf("type=%3i \n",ev\[num\].type); \_flushlbf(); // Varmistetaan tulostus } // if (ev.value > 0) printf("DOWN "); else printf("UP "); // ev.value == 0 ; näppäin ylös // ev.value == 1 ; näppäin alas // ev.value == 2 ; näppäin autorepeat switch (ev\[num\].type) { case 1 : if (ev\[num\].value == 1) // Key down { if (ev\[num\].code == 272) evmlbutton = ev\[num\].value; if (ev\[num\].code == 273) evmrbutton = ev\[num\].value; if (ev\[num\].code == 274) evmmbutton = ev\[num\].value; if (eventsdisabled == 0) { keystate\[ev\[num\].code\] \|=1; // näppäin painettuna alas keystate\[ev\[num\].code\] \|= 2; // Talletetaan tieto näppäimen alaspainamisesta; tcflush(fileno(stdin), TCIFLUSH); // tyhjennetään näppäimistöpuskuri } } else if (ev\[num\].value == 0) // Key up { keystate\[ev\[num\].code\] &= \~1; // näppäin vapaassa tilassa keystate\[ev\[num\].code\] \|= 4; // Talletetaan tieto näppäimen ylösnousemisesta; } else if (ev\[num\].value == 2) // Keyboard Repeat event { if (eventsdisabled == 0) { tcflush(fileno(stdin), TCIFLUSH); // tyhjennetään näppäimistöpuskuri } } break; case 2 : // mouse event if (ev\[num\].code == 0) evmousex \+= ev\[num\].value; if (ev\[num\].code == 1) evmousey \+= ev\[num\].value; if (ev\[num\].code == 8) evmwheel \+= ev\[num\].value; // if(eventdebug&2) { // setfgcolor(CYAN); // printf("mousex=%5i, mousey=%5i, wheel=%5i %1i %1i %1i \n",mousex,mousey,wheel,lbutton,rbutton,mbutton); // \_flushlbf(); break; case 17 : // LED event, ylläpidämme tietoa ledien tilasta if (ev\[num\].code == LED_NUML) ev\[num\].value ? ledstates \|= 1 : ledstates &= \~1; else if (ev\[num\].code == LED_CAPSL) ev\[num\].value ? ledstates \|= 2 : ledstates &= \~2; else if (ev\[num\].code == LED_SCROLLL) ev\[num\].value ? ledstates \|= 4 : ledstates &= \~4; else if (ev\[num\].code == LED_COMPOSE) ev\[num\].value ? ledstates \|= 8 : ledstates &= \~8; break; case 4 : // LED event, ylläpidämme tietoa ledien tilasta break; case 0 : // Event tiedon viimeinen osa break; default: printf("inputevent: Unknown Event Type %i\n", ev\[num\].type); break; } // Jos halutaan vielä erillinen keyboard handler funktio // if (evfunction) evfunction(ev\[1\].value, ev\[1\].code); // Tällöin funktiota kutsutaan automaattisesti näppäinpainalluksen yhteydessä. // Ja välitetään painetun näppäimen tiedot funktiolle. } } } } /// Kun eventtien käsittely lopetetaan, tätä kutsutaan automaattisesti(man atexit) kun ohjelman suoritus loppuu. /// Voidaan kutsua halutessa muulloinkin. static void eventclose(void) { // int rep\[2\]; // rep\[0\] = 250; // rep\[1\] = 100; // if(ioctl(keyfd, EVIOCSREP, oldrep)) { // keyboard repeatrate alkuperäiseksi // perror("evdev ioctl repeatrate set"); // } // \__fpurge(stdout); // \_flushlbf(); setled(0); for ( int i=0; i<num_event_devices; i++) if (epollfd\[i\].fd) close(evdev_info\[i\].fd); // sigaction(SIGIO, &oldsaio, NULL); // Palautetaan alkuperäiset signaaliasetukset usleep(10000); eventdisable(); int s = pthread_cancel(thread); if (s) { perror("pthread_cancel"); } } /// Lopetussignaalien käsittelijä. static void evsighandler(int signum) { printf("\n\n********************************************\n"); if (signum == SIGTERM) printf("got SIGTERM"); else if (signum == SIGHUP) printf("got SIGHUP"); else if (signum == SIGINT) printf("got SIGINT"); printf(", we will leave the program now "); printf("\n********************************************\n"); if (sig_exit_function \!= NULL) (*sig_exit_function)(); eventclose(); if ( signum == SIGINT ) (*sigint_action.sa_handler)(signum); // Kutsutaan lopuksi edellistä action handleriä else if ( signum == SIGHUP ) (*sighup_action.sa_handler)(signum); // Kutsutaan lopuksi edellistä action handleriä else if ( signum == SIGTERM ) (*sigterm_action.sa_handler)(signum); // Kutsutaan lopuksi edellistä action handleriä exit(0); } /// Napataan lopetus signaalit jotta saadaan lopetetuksi siististi. static void evsigterm() { struct sigaction new_action; new_action.sa_handler = evsighandler; sigemptyset (&new_action.sa_mask); new_action.sa_flags = 0; sigaction (SIGINT, &new_action, &sigint_action); // we will catch Ctrl-C sigaction (SIGHUP, &new_action, &sighup_action); sigaction (SIGTERM, &new_action, &sigterm_action); // if (old_action.sa_handler == SIG_DFL) sigaction (SIGTERM, &new_action, NULL); } /// Käynnistää eventtien käsittelyn. /// Kutsutaan ohjelman alussa tai siinä vaihessa kun halutaan käyttää Event-toiminnallisuuksia, /// kuten näppäiten painallusten lukemista, mousen tai joystikin lukemista. void eventinit() { int fd; int i; int s; unsigned int devnum; unsigned int led_b; struct input_id device_info; char name\[256\]= "Unknown"; // evfunction=NULL; for (int i=0 ; i<20; i++) { epollfd\[i\].fd = 0; epollfd\[i\].events = POLLIN; epollfd\[i\].revents = 0; evdev_info\[i\].fd = 0; evdev_info\[i\].devices = 0; } char evdevlist\[\]\[50\]={ "/dev/event0", "/dev/event1", "/dev/event2", "/dev/event3", "/dev/event4", "/dev/event5", "/dev/input/event0", "/dev/input/event1", "/dev/input/event2", "/dev/input/event3", "/dev/input/event4", "/dev/input/event5" }; for (devnum=0; devnum < (sizeof(evdevlist)/sizeof(evdevlist\[0\])); devnum++) { fd=open(evdevlist\[devnum\],O_RDWR\|O_NOCTTY); if (fd < 0) continue; \*evtype_bitmask=0; if (ioctl(fd, EVIOCGBIT(0,EV_MAX), evtype_bitmask) < 0) { perror("evdev ioctl"); printf("dev %s \n", evdevlist\[devnum\]); close(fd); continue; } // Tsekataan josko tämä device tukee näitä eventtejä printf("\n"); if ( \*evtype_bitmask & (1<<EV_SYN) ) { printf("SYN events available %s\n", evdevlist\[devnum\]); evdev_info\[num_event_devices\].devices \|= 16; } if ( \*evtype_bitmask & (1<<EV_SND) ) { printf("SOUND events available %s\n", evdevlist\[devnum\]); evdev_info\[num_event_devices\].devices \|= 32; } if ( \*evtype_bitmask & (1<<EV_MSC) ) { printf("MISC events available %s\n", evdevlist\[devnum\]); evdev_info\[num_event_devices\].devices \|= 64; } if ( \*evtype_bitmask & (1<<EV_SW) ) { printf("SW events available %s\n", evdevlist\[devnum\]); evdev_info\[num_event_devices\].devices \|= 128; } if ( \*evtype_bitmask & (1<<EV_REP) ) { printf("Repeat events available %s\n", evdevlist\[devnum\]); evdev_info\[num_event_devices\].devices \|= 256; } if ( \*evtype_bitmask & (1<<EV_FF) ) { printf("ForceFeedback events available %s\n", evdevlist\[devnum\]); evdev_info\[num_event_devices\].devices \|= 512; } if ( \*evtype_bitmask & (1<<EV_PWR) ) { printf("PWR events available %s\n", evdevlist\[devnum\]); evdev_info\[num_event_devices\].devices \|= 1024; } if ( \*evtype_bitmask & (1<<EV_FF_STATUS) ) { printf("ForceFeedback status events available %s\n", evdevlist\[devnum\]); evdev_info\[num_event_devices\].devices \|= 2048; } if ( \*evtype_bitmask & (1<<EV_LED) ) { ledfd = fd; printf("LED events available %s\n", evdevlist\[devnum\]); evdev_info\[num_event_devices\].devices \|= 4; } if ( (*evtype_bitmask & (1<<EV_KEY)) ) { printf("Keyboard/Button events available %s\n", evdevlist\[devnum\]); evdev_info\[num_event_devices\].devices \|= 1; } if ( (*evtype_bitmask & (1<<EV_REL)) ) { printf("Mouse events available %s\n", evdevlist\[devnum\]); evdev_info\[num_event_devices\].devices \|= 2; } if ( (*evtype_bitmask & (1<<EV_ABS)) ) { printf("Joystick events available %s\n", evdevlist\[devnum\]); evdev_info\[num_event_devices\].devices \|= 8; } epollfd\[num_event_devices\].fd = fd; evdev_info\[num_event_devices\].fd = fd; num_event_devices++; if(ioctl(fd, EVIOCGID, &device_info)) { perror("evdev ioctl"); } // Kysytään tietoa laitteesta else { printf("vendor %04hx product %04hx version %04hx", device_info.vendor, device_info.product, device_info.version); switch ( device_info.bustype) { case BUS_PCI : printf(" is on a PCI bus\n"); break; case BUS_ISAPNP : printf(" is on a ISAPNP Bus\n"); break; case BUS_USB : printf(" is on a Universal Serial Bus\n"); break; case BUS_HIL : printf(" is on a HIL Bus\n"); break; case BUS_BLUETOOTH : printf(" is on a BLUETOOTH Bus\n"); break; case BUS_ISA : printf(" is on a ISA Bus\n"); break; case BUS_I8042 : printf(" is on a I8042 Bus\n"); break; case BUS_XTKBD : printf(" is on a XTKBD Bus\n"); break; case BUS_RS232 : printf(" is on a RS232 Serial Bus\n"); break; case BUS_GAMEPORT : printf(" is on a Gameport Bus\n"); break; case BUS_I2C : printf(" is on a I2C Bus\n"); break; case BUS_HOST : printf(" is on a HOST Bus\n"); break; case BUS_GSC : printf(" is on a GSC Bus\n"); break; default: printf(" is on a Unknown Bus\n"); break; } if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) { perror("evdev ioctl"); } // Kysytään laitteen nimi else printf("The device says its name is %s", name); } printf("\n"); } if (ledfd) { ioctl(ledfd, EVIOCGLED(sizeof(led_b)), &led_b); // Haetaan näppäimistön ledien tilat ledstates=led_b&255; } // int rep\[2\]; // if(ioctl(keyfd, EVIOCGREP, oldrep)) { // Talletamme näppäimistön nykyisen repeatraten // perror("evdev ioctl repeatrate save"); // } // printf("Old reapeatrate = %i %i\n",oldrep\[0\],oldrep\[1\]); // // rep\[0\] = 2500; // rep\[1\] = 2500; // if(ioctl(keyfd, EVIOCSREP, rep)) { // Näppäimistön repeatrate mahdollisimman hitaaksi // perror("evdev ioctl repeatrate set"); // } eventenable(); i = atexit(eventclose); // Lopetusfunktio hoituu automaattisesti if (i \!= 0) { fprintf(stderr, "inputevent atexit: cannot set exit function\n"); exit(EXIT_FAILURE); } pthread_attr_init(&attr); // threadin attribuuttien initialisointi // pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) { printf("inputevent:pthread_attr_setschedpolicy failed\n"); } sched_param schedulingParameters; int maxPriority = sched_get_priority_max(SCHED_FIFO); schedulingParameters.sched_priority = maxPriority; if (pthread_attr_setschedparam(&attr, &schedulingParameters)) { printf("inputevent:pthread_attr_setschedparam failed\n"); } s=pthread_create(&thread, &attr , (void * (*) (void \*))readevent, NULL); evsigterm(); // Napataan INT KILL HUP signaalit niin poistutaan siististi } /// Disabloidaan väliaikaisesti eventtien käsittely. /// Kutsutaan jos halutaan käyttää välissä normaaleja posixin näppäimistön käsittelyfunktioita kuten getchar(). void eventdisable(void) { eventsdisabled=1; tios.c_lflag \|= ECHO \| ICANON; tcsetattr(fileno(stdin), TCSANOW, &tios); // Palautetaan alkuperäiset asetukset tcflush(fileno(stdin), TCIFLUSH); // tyhjennetään puskurit \__fpurge(stdin); // Tämä tyhjentää C-kirjaston stream puskurit VT100::showcursor(); } /// Palataan takaisin eventtien käsittelyyn ja disabloidaan normaali näppäimistön toiminnallisuus. void eventenable(void) { tcgetattr(fileno(stdin), &tios); // vanhat terminaaliasetukset talteen tios.c_lflag &= \~(ECHO); // Emme halua kaiuttaa painalluksia tcsetattr(fileno(stdin), TCSANOW, &tios); tcflush(fileno(stdin), TCIFLUSH); // tyhjennetään puskurit \__fpurge(stdin); // Tämä tyhjentää C-kirjaston stream puskurit VT100::hidecursor(); eventsdisabled=0; } \#endif // LINUX \#ifndef LINUX // Linux tarvitsee tämän funktion jotta näppäimistö, mouse ja joystick toimivat. // Windowsissa tämä ei tee mitään, ja tämä funktio on vain yhteensopivuuden takia. void eventinit() { return; } bool key(unsigned char k) { int key_state; //return (GetKeyState(k) < 0); key_state = GetAsyncKeyState(k); return (key_state < 0); } bool keydown(unsigned char k) { int key_state; //return (GetKeyState(k) < 0); key_state = GetAsyncKeyState(k); if ((key_state < 0 ) && (key_state & 1)) return 1; return 0; } \#endif // \!LINUX