マウスをカスタマイズしよう linux編の続きです。

実はゲームパッド Elecom F310についてずっと考えていました。笑

マウス同様ジョイパッドをカスタマイズしましょう。なんとかプログラムもできました。

ゲームパッドをマウスみたいにします。

前回同様にUSB接続を調べます。
$ 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

I: Bus=0003 Vendor=046d Product=c21d Version=4014
N: Name="Logitech Gamepad F310"
P: Phys=usb-3f980000.usb-1.3/input0
S: Sysfs=/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/input/input3
U: Uniq=
H: Handlers=event3 js0 
B: PROP=0
B: EV=20000b
B: KEY=7cdb0000 0 0 0 0 0 0 0 0 0
B: ABS=3003f
B: FF=1 7030000 0 0

私の環境ではマウスがevent0 キーボードがevent1, ゲームパッドがevent3です。
マウスが/dev/input/event0,
キーボードが /dev/input/event1
ゲームパッドが/dev/input/event3 となります。

次にゲームパッドのボタンの割り当てを調べます。

//input2.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>

#define GAMEPAD_EVENT "/dev/input/event3"
#define MOUSE_EVENT "/dev/input/event0"

int gamepadfd;
int mousefd;

int main(void)
{
gamepadfd = open(GAMEPAD_EVENT, O_RDWR);
mousefd = open(MOUSE_EVENT, O_RDWR);
  for (;;) {
    struct input_event event;

    if (read(gamepadfd, &event, sizeof(event)) != sizeof(event)) {
      exit(EXIT_FAILURE);
    }
    switch(event.type) {
      case EV_KEY:
printf("type:%x  code:%x value:%x\n", event.type, event.code, event.value);
        break;
      case EV_REL:
printf("type:%x  code:%x value:%x\n", event.type, event.code, event.value);
break;
case EV_ABS:
printf("type:%x  code:%x value:%x\n", event.type, event.code, event.value);
break;
      default:
 
        break;
    }
    fflush(stdout);
  }
  
  close(gamepadfd);
  close(mousefd);
}
ゲームパッドを適当に押すと数字が表示されますので、/usr/include/linux/input.hとにらめっこして調べます。

chrome用にゲームパッドを次のようにカスタマイズします。


プログラムの説明
前回のプログラムと同じような仕組みです。
十字キーと十字スティックはシグナルを使ってポーリングしています。つまり毎回入力されていないかデータを取得しにいっています。ぶさいくですがそれしかできませんでした。

要するにゲームパッドからマウスやキーボードにデータ変換しているだけです。変換する箇所が多いのでごちゃごちゃしています。だいぶ試行錯誤しました。

//gamepad.c
//ゲームパッドをchromeブラウザ用にカスタマイズするプログラム
#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>
#include <signal.h>

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

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

typedef struct _GamepadMouseState GamepadMouseState;
struct _GamepadMouseState 
{
  __s32 vx, vy;
  int wheelvalue;
  int wheeldirection;
};

static GamepadMouseState state;
static struct itimerval itimerval;
static struct itimerval old_itimerval;
static struct itimerval htimerval;
static struct itimerval old_htimerval;

int keyboardfd;
int gamepadfd;
int virtualfd;
int mousefd;
int i;



//イベントを発生させる関数
void send_event(int output, int type, int code, int value)
{
  struct input_event event;
  if (mousefd < 0) {
    return;
  }

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

//ゲームパッドのホイール移動
void wheel_handler()
{
//printf("wheel :%x  \n", state.wheelvalue);
if(state.wheeldirection == 0)
return;
send_event(mousefd, EV_REL, ABS_WHEEL, state.wheeldirection);
send_event(mousefd, EV_SYN, SYN_REPORT, 0);
}

//ゲームパッドの十字キーの状態を取得し、マウス移動させる
void alarm_handler(int signum)
{
if((i % 25) == 0) {
wheel_handler();
}
i++;
if(i > 5000)
i = 0;
  if (state.vx == 0 && state.vy == 0 ) {
    return;
  }
  if (state.vx!= 0) {
    send_event(mousefd, EV_REL, REL_X, state.vx);
  }

  if (state.vy!= 0) {
    send_event(mousefd, EV_REL, REL_Y, state.vy);
  }
}



//シグナルを設定する 2000マイクロ秒毎にalarm_handlerを実行する
void init_event_handler()
{
  itimerval.it_value.tv_sec = 0;
  itimerval.it_value.tv_usec = 2000;
  itimerval.it_interval.tv_sec = 0;
  itimerval.it_interval.tv_usec = 2000;
  signal(SIGALRM, alarm_handler);
  setitimer(ITIMER_REAL, &itimerval, &old_itimerval);
}


int main(void)
{
mousefd = open(MOUSE_EVENT, O_RDWR);
gamepadfd = open(GAMEPAD_EVENT, O_RDWR);
keyboardfd = open(KEYBOARD_EVENT, O_RDWR);
init_event_handler();
//ゲームパッドを無効化
ioctl(gamepadfd, EVIOCGRAB, 1);
  for (;;) {
    struct input_event event;

    if (read(gamepadfd, &event, sizeof(event)) != sizeof(event)) {
      exit(EXIT_FAILURE);
    }
    //printf("type:%x  code:%x value:%x\n", event.type, event.code, event.value);
    switch(event.type) {
case EV_KEY:
if(event.code == BTN_X) {
send_event(mousefd, EV_KEY, BTN_MOUSE, event.value);
send_event(mousefd, EV_SYN, SYN_REPORT, 0);
}else if(event.code == BTN_Y) {
send_event(mousefd, EV_KEY, BTN_MIDDLE, event.value);
send_event(mousefd, EV_SYN, SYN_REPORT, 0);
}else if(event.code == BTN_A) {
send_event(keyboardfd, EV_KEY, KEY_LEFTCTRL, event.value);
send_event(keyboardfd, EV_KEY, KEY_W, event.value);
send_event(keyboardfd, EV_SYN, SYN_REPORT, 0);
}else if(event.code == BTN_B) {
send_event(mousefd, EV_KEY, BTN_RIGHT, event.value);
send_event(mousefd, EV_SYN, SYN_REPORT, 0);
}else if(event.code == BTN_TL) {
send_event(keyboardfd, event.type, KEY_LEFTCTRL, event.value);
send_event(keyboardfd, event.type, KEY_T, event.value);
send_event(keyboardfd, EV_SYN, SYN_REPORT, 0);
}else if(event.code == BTN_TR) {
send_event(keyboardfd, event.type, KEY_LEFTCTRL, event.value);
send_event(keyboardfd, event.type, KEY_TAB, event.value);
send_event(keyboardfd, EV_SYN, SYN_REPORT, 0);
}
break;
      case EV_ABS:
//printf("type:%x  code:%x value:%x\n", event.type, event.code, event.value);
if(event.code == ABS_HAT0Y) {//上下ボタンを押したとき
state.vy = event.value;
}else if(event.code == ABS_HAT0X) {//左右ボタンを離したとき
state.vx = event.value;
}else if(event.code == ABS_RY) {//ホイール上下
//printf("type:%x  code:%x value:%d\n", event.type, event.code, event.value);
if(event.value > 10000) {
state.wheeldirection = 0xffffffff;
} else if(-8000 < event.value && event.value < 8000){
state.wheeldirection = 0;
}else {
state.wheeldirection = 1;
}
state.wheelvalue = event.value;
//send_event(mousefd, EV_SYN, SYN_REPORT, 0);
}else if(event.code == ABS_RX) {
}else {
}
        break;
      case EV_REL:
break;
 
      default:
        break;
    }
    fflush(stdout);
  }
  
  //ゲームパッドを有効に戻す
  ioctl(gamepadfd, EVIOCGRAB, 0);
  
  close(keyboardfd);
  close(gamepadfd);
  close(virtualfd);
}


$ gcc -o gamepad gamepad.c
$ sudo ./gamepad