0
Другие записи за это число:
2018/12/26 - А вы уже придумали, что дарить друзьям и близким на Новый год?
<< предыдущая заметкаследующая заметка >>
26 декабря 2018
Linux: перехват клавиатуры приложением

С 2016 года я неспешно интересовался вопросом, как подключить к беспилотному unix-серверу клавиатурку так, чтобы с нее шли управляющие команды в нужное приложение. И вот после недавнего обсуждения я наконец состряпал нужное и поставил на боевое дежурство - исправно работает уже несколько недель. Огромное спасибо за помощь Byte и Dmitry Shmidt! Теперь поделюсь итоговыми результатами, может, кому пригодится.

Софтинка запускается как крохотный демон, почти не занимающий оперативной памяти, который потом (при нажатии кнопки, что ожидается редко) вызывает скрипт-обработчик и передает ему, что было нажато, а тот уж пусть разбирается, как поступить. Поэтому в качестве первого аргумента софтинке передается Vendor/Product нужного USB-устройства через двоеточие (например "1C4F:0002), либо путь до устройства в системе (вида "/dev/input ..." - я не тестировал, но и это должно работать тоже). В качестве второго аргумента в кавычках - передам команду, которая будет выполнена в системе, конструкция %c в ней будет заменена на введенный символ. Например: "echo \"%c\" >> /tmp/111.txt" или "notify-send -t 300 \"KeyCode: %c\"" или так:

<?php
sudo
/home/lleo/daemons/kyeboardnoid 1C4F:0002"/home/lleo/do_commands.php \"SCANCODE=%c\""

При отваливании из USB устройства и появлении его заново - корректно обрабатывает эти неполадки, терпеливо ждёт, поглядывая. Полный код софтинки:

<?php#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <dirent.h>
#include <linux/input.h>

#define VENDORID 0x1c4f
#define PRODUCTID 0x0002int16_t fd= -1;char path[300];int init_keyboard(unsigned int VENDOR,unsigned int PRODUCT) {fd=-1;int count=0;struct dirent**files=NULL;struct input_id id;count=scandir("/dev/input",&files,0,0)-1;

while(count>=0) {

if (fd==-1&&strncmp(files[count]->d_name,"event",5)==0) {sprintf(path,"/dev/input/%s",files[count]->d_name);fd=open(path,O_RDONLY);

if(fd>=0) {

if(ioctl(fd,EVIOCGID,(void*)&id)<0)perror("ioctl EVIOCGID");

else {

if(id.vendor==VENDOR&&id.product==PRODUCT&&id.bustype==BUS_USB) {printf("Device found at %s ",path); }

else {close(fd);fd= -1;

}

}

} else {fprintf(stderr,"Error opening %s:\t",path);perror("");

}

}free(files[count--]);

}free(files);int grab=1;

if(fd>=0)ioctl(fd,EVIOCGRAB,&grab);

else {fprintf(stderr,"Device not found or access denied. ");

returnEXIT_FAILURE;

}

return0;
}
char remap_codes(int scancode){char r=' ';

switch(scancode) {// NumLock = off + N // NumLock = oncase0x45:r=0; break;// 000 -> 0 0 0 // 000 -> --- NO ---case0x52:r='0'; break; case0x6E:r='0'; break;

case0x4f:r='1'; break; case0x6B:r='1'; break;

case0x50:r='2'; break; case0x6C:r='2'; break;

case0x51:r='3'; break; case0x6D:r='3'; break;

case0x4b:r='4'; break; case0x69:r='4'; break;

case0x4c:r='5'; break;//--- NO ---case0x4d:r='6'; break; case0x6A:r='6'; break;

case0x47:r='7'; break; case0x66:r='7'; break;

case0x48:r='8'; break; case0x67:r='8'; break;

case0x49:r='9'; break; case0x68:r='9'; break;

case0x53:r='.'; break; case0x6F:r='.'; break;

case0x60:r='E'; break;// Entercase0x62:r='C'; break;// /case0x37:r='Z'; break;// *case0x4a:r='M'; break;// -case0x4e:r='P'; break;// +case0x0E:r='B'; break;// Backspacedefault:printf("Scancode:0x%X - [%c]\n",scancode,(unsigned char)scancode);r=(unsigned char)scancode;

}

returnr;
}
char read_keyboard() {struct input_event ev;ssize_t n;

while(true) {n=read(fd, &ev,sizeof(struct input_event));

if(n== (ssize_t)-1) {

if (errno==EINTR) continue;

else break;

} else if(n!=sizeof ev) {errno=EIO;

break;

}

if(ev.type==EV_KEY&&ev.value==EV_KEY) returnremap_codes(ev.code);

}

return'!';
}
int connect_keyboard(int argc,char*arg) {char name[256] ="Unknown";

if(argc>1) {// пробуем отсканировать VEND:PRODunsigned int VEND=VENDORID,PROD=PRODUCTID;

if(2==sscanf(arg,"%x:%x",&VEND,&PROD)) {printf("\nConnecting to device %04x:%04x... ",VEND,PROD);

if(init_keyboard(VEND,PROD)!=0) {printf("FAILED\n"); return(1); }printf("OK\n");

} else {// пробуем найти pathsnprintf(path,200,"%s",arg);printf("\nConnecting to device %s ... ",path);

if((fd=open(path,O_RDONLY)) == -1) {printf("FAILED\n"); return(1); }printf("OK\n");

}

} else {// откроем дефолтный девайсprintf("\nConnecting to default device %04x:%04x... ",VENDORID,PRODUCTID);

if(init_keyboard(VENDORID,PRODUCTID)!=0) {printf("FAILED\n"); return(1); }printf("OK\n");

}ioctl(fd,EVIOCGNAME(sizeof(name)),name);fprintf(stderr,"Reading from: %s (%s)\n",path,name);

return0;
}
int main(int argc,char*argv[]) {printf("Runnung... (Example: \"sudo ./keyboardnoid 1C4F:0002 \"echo \\\"%%c\\\" >> /tmp/111.txt\")");char r=0;char action[300]="notify-send -t 300 \"KeyCode: %c\"";

if(argc>2)snprintf(action,1024,"%s",argv[2]);// скопировать строку акции

// коннектимсяwhile(1) { if(0==connect_keyboard(argc,argv[1])) break;sleep(2); }// 1 секwhile(true) {

if(!(r=read_keyboard())) continue;// printf("Scancode:0x%X - [%c]\n",r,(unsigned char)r);if(r=='!') {// обрыв связиsleep(1);close(fd);printf("port_disconnected\n");snprintf(path,100,action,'?');system(path);// подать сигналwhile(true) { if(0==connect_keyboard(argc,argv[1])) break;sleep(2); }// 10 секsnprintf(path,100,action,'!');system(path);// подать сигнал что все в порядкеcontinue;

}

if(r==0x0D||r==0x0A||r=='"'||r=='\'')r='@';// не надо нам энтеров и кавычек!snprintf(path,100,action,r);printf("ACTION: `%s`\n",path);system(path);

}
}


PS: Что любопытно: говорят, вот эти мелкие USB-карточки, которые имеют на борту кнопки, тоже определяются как устройство USB-клавиатуры. А значит, их можно втыкать во всякие там домашние роутеры и raspberry pi чтобы не только поиметь звуковые оповещения, но и запрограммировать в системе полезные команды по нажатию внешних кнопок, чего у таких устройств вечно не хватает. Но сам я эти звуковушки пока не тестировал, заказал на Алиэкспрессе сейчас (63 руб), как дойдет - потестирую.

<< предыдущая заметка следующая заметка >>
пожаловаться на эту публикацию администрации портала
архив понравившихся мне ссылок

Комментарии к этой заметке скрываются - они будут видны только вам и мне.

Оставить комментарий