windowsの次はlinuxでマウスをカスタマイズします。

プログラムを作って、マウスを乗っ取ります。 

仕組みが分かれば簡単にプログラムで作ることができます。

なぜならlinuxはすべてファイル扱いで、 マウスもキーボードもゲームパッドもファイルだからです。

openして、readしてwriteすればいいわけです。

自由自在にカスタマイズできてしまいます。

たとえば、マウスを左クリックするとキーボードのAと入力させることもできます。

ですからゲームパッドをマウス替わりにカスタマイズすることもでてきしまうわけです。

この仕組みがわかればキーロガーも作れてしまいます。

まずはUSBに接続されているデバイスを調べます。
pi@raspberrypi:~ $ cat /proc/bus/input/devices
 
I: Bus=0003 Vendor=1bcf Product=0005 Version=0110
N: Name="USB Optical Mouse"
P: Phys=usb-3f980000.usb-1.2/input0
S: Sysfs=/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.2/1-1.2:1.0/0003:1BCF:0005.0001/input/input0
U: Uniq=
H: Handlers=mouse0 event0 
B: PROP=0
B: EV=17
B: KEY=1f0000 0 0 0 0 0 0 0 0
B: REL=143
B: MSC=10

I: Bus=0003 Vendor=0d62 Product=8070 Version=0111
N: Name="ELECOM Wired Keyboard"
P: Phys=usb-3f980000.usb-1.4/input0
S: Sysfs=/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.4/1-1.4:1.0/0003:0D62:8070.0002/input/input1
U: Uniq=
H: Handlers=sysrq kbd leds event1 
B: PROP=0
B: EV=120013
B: KEY=10000 7 ff9f207a c14057ff febeffdf ffefffff ffffffff fffffffe
B: MSC=10
B: LED=1f

私の場合はこんなかんじ。

マウスとキーボードが認識されています。重要なのはHandlersのeventです。マウスがevent0, キーボードがevent1です。これを記憶しておきます。先ほどのファイルというのがこれです。

マウスは/dev/input/event0, キーボードは/dev/input/event1に対応しています。

試しにマウスを監視してみましょう。
$hexdump /dev/input/event0

同様にキーボード
$hexdump /dev/input/event1

マウスやキーボードの定義は/usr/include/linux/input.hにされています。ここを開いてみてください。

キーボード系
#define KEY_Q                   16 キーボードのQ
#define KEY_W                   17 キーボードのW
#define KEY_E                   18
#define KEY_R                   19
#define KEY_T                   20

マウス系
#define BTN_LEFT                0x110 マウスの左クリック
#define BTN_RIGHT               0x111 マウスの右クリック 
#define BTN_MIDDLE              0x112 マウスのホイールクリック
こんなかんじでずらっと登録されています。

マウスやキーボードの入力があるとイベントが発生します。その構造体も定義されています。
struct input_event {
        struct timeval time;
        __u16 type;
        __u16 code;
        __s32 value;
};

event.type
  EV_KEY マウスやキーボードがクリックされたとき
  EV_REL マウスの位置移動、ホイールが回転したとき
event.code
  左クリックやらキーボードの入力キー
event.value
 1: キーを押したとき、 0: キーを離したとき
 他の数字 移動量

では早速マウスボタンを調べてみましょう。
input.c
//マウス入力を調べるプログラム
#include <stdio.h>
#include <stdlib.h>
#include <linux/input.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int keyboardfd;
int mousefd;

int main(void)
{
  mousefd = open("/dev/input/event0", O_RDWR);
  keyboardfd = open("/dev/input/event1", O_RDWR);
  for (;;) {
    struct input_event event;

    if (read(mousefd, &event, sizeof(event)) != sizeof(event)) {
      exit(EXIT_FAILURE);
    }
    switch(event.type) {
      case EV_KEY:
printf("code:%x  value:%x\n", event.code, event.value);
        break;
      case EV_REL:
//printf("rel:%x  ", event.code);
break;
  case EV_ABS:
//printf("abs:%x  ", event.code);
break;
      default:
 
        break;
    }
    fflush(stdout);
  }
  
  close(keyboardfd);
  close(mousefd);
}
$gcc -o input input.c
 
pi@raspberrypi:~/Documents $ sudo ./input
code:110  value:0
code:112  value:1
code:110  value:1
code:112  value:0
code:114  value:1
code:114  value:0
code:113  value:1
code:113  value:0
code:110  value:1
code:110  value:0

110だから左クリックだなとか、value 1は押したとき、value 0は離したとき。
判明したのが次のとおり。カスタマイズの内容も書いておきました。


 で、ここからが少しやっかい。
BTN_EXTRAをCTRL + Wにすると、
BTN_EXTRA + CTRL + Wになってしまいます。 
つまり、BTN_EXTRAとBTN_SIDEの機能を無効にしたいのです。

結論を書くとマウスを無効にし、User Inputという仮想デバイスでマウス機能を再登録します。

ちなみにマウスを無効にするのはたった1行
ioctl(マウスちゃん, EVIOCGRAB, 1);

マウスを元に戻すのも1行
ioctl(マウスちゃん, EVIOCGRAB, 0);

あとはUser Inputでマウス機能を登録すればできあがりです!
コメントを書いておくのでプログラムを読んでください。

5mouse.c
//マウスをchromeブラウザ用にカスタマイズするプログラム
//サイドボタンを
//現在のタブを閉じる Ctrl+w
//開いている次のタブに移動する Ctrl+Tab 
//に設定する
#include <stdio.h>
#include <stdlib.h>
#include <linux/input.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/uinput.h>
#include <string.h>

//ここでマウスとキーボードのイベントを設定してください。
#define MOUSE_EVENT "/dev/input/event0"
#define KEYBOARD_EVENT "/dev/input/event1"


#define die(str, args...) do { perror(str); exit(EXIT_FAILURE); } while(0)

int keyboardfd;
int mousefd;
int virtualfd;

void create_uinput_device (int fd) {
  struct uinput_user_dev uidev;
  memset(&uidev, 0, sizeof(uidev));

  snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "YUKIMOUSE");
  uidev.id.bustype = BUS_USB;
  uidev.id.vendor  = 0xAAAA;
  uidev.id.product = 0xBBBB;
  uidev.id.version = 1;

  if (write(fd, &uidev, sizeof(uidev)) < 0)
    die("create_uinput_device: write");

  if (ioctl(fd, UI_DEV_CREATE) < 0)
    die("create_uinput_device: ioctl");
}

void send_event(int output, int type, int code, int value)
{
  struct input_event event;

  event.type = type;
  event.code = code;
  event.value = value;
  gettimeofday(&event.time, NULL);
write(output, &event, sizeof(event));
}

int main(void)
{
mousefd = open(MOUSE_EVENT, O_RDWR);
keyboardfd = open(KEYBOARD_EVENT, O_RDWR);
virtualfd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
//マウス機能を登録し、User Inputデバイスを作成する
//ここで左クリックを有効にするよーとか、マウス動かすよーとか登録
ioctl(virtualfd, UI_SET_EVBIT, EV_REL);
ioctl(virtualfd, UI_SET_RELBIT, REL_X);
ioctl(virtualfd, UI_SET_RELBIT, REL_Y);
ioctl(virtualfd, UI_SET_RELBIT, REL_WHEEL);
ioctl(virtualfd, UI_SET_EVBIT, EV_KEY);
ioctl(virtualfd, UI_SET_KEYBIT, BTN_LEFT);
ioctl(virtualfd, UI_SET_KEYBIT, BTN_RIGHT);
ioctl(virtualfd, UI_SET_KEYBIT, BTN_MIDDLE);
create_uinput_device(virtualfd);
//マウスを無効化
ioctl(mousefd, EVIOCGRAB, 1);
  for (;;) {
    struct input_event event;

    if (read(mousefd, &event, sizeof(event)) != sizeof(event)) {
      exit(EXIT_FAILURE);
    }
    switch(event.type) {
      case EV_KEY:
if(event.code == BTN_EXTRA && event.value == 1) {//EXTRAボタンを押したときCTRL + W
send_event(keyboardfd, event.type, KEY_LEFTCTRL, 1);
send_event(keyboardfd, event.type, KEY_W, 1);
send_event(keyboardfd, EV_SYN, SYN_REPORT, 0);
}else if(event.code == BTN_EXTRA && event.value == 0) {//EXTRAボタンを離したとき
send_event(keyboardfd, event.type, KEY_W, 0);
send_event(keyboardfd, event.type, KEY_LEFTCTRL, 0);
send_event(keyboardfd, EV_SYN, SYN_REPORT, 0);
}else if(event.code == BTN_SIDE && event.value == 1) {//SIDEボタンを押したとき CTRL + TAB
send_event(keyboardfd, event.type, KEY_LEFTCTRL, 1);
send_event(keyboardfd, event.type, KEY_TAB, 1);
send_event(keyboardfd, EV_SYN, SYN_REPORT, 0);
}else if(event.code == BTN_SIDE && event.value == 0) {//SIDEボタンを離したとき
send_event(keyboardfd, event.type, KEY_TAB, 0);
send_event(keyboardfd, event.type, KEY_LEFTCTRL, 0);
send_event(keyboardfd, EV_SYN, SYN_REPORT, 0);
}else {
send_event(mousefd, event.type, event.code, event.value);
}
        break;
      case EV_REL:
//printf("%x  ", event.code);
send_event(mousefd, event.type, event.code, event.value);
break;
case EV_ABS:
send_event(mousefd, event.type, event.code, event.value);
break;
      default:
send_event(mousefd, event.type, event.code, event.value);
//printf("0");
        break;
    }
    fflush(stdout);
  }
  
  //マウスを有効に戻す
  ioctl(mousefd, EVIOCGRAB, 0);
  
  close(keyboardfd);
  close(mousefd);
  close(virtualfd);
}


一応マウスを無効にするので注意が必要かと
$gcc -o 5mouse 5mouse.c 
$sudo ./5mouse

思っていたようにマウスをカスタマイズすることができました。
あとはスクリプトを作るなり、起動時に自動実行すればいいかと思います。

参考サイト 超絶素晴らしいサイトです。
Linux Input Subsystemの使い方

特定のキーボードを ESC 入力のためのフットペダルにする 
サンプルプログラムが分かりやすいです。 

ゲームパッドをカスタマイズしよう linux編 に続きます。