Linux では接続した Gamepad の情報を /dev/input/js* から読み取ることができます。
構造体などは /usr/include/linux/joystick.h で定義されており、
具体的な方法は下記ドキュメントの通り。
int fd= open( "/dev/input/js0", O_RDONLY ); unsigned char ButtonData[BUTTON_DATA_MAX]; signed int StickData[STICK_DATA_MAX]; for(;;){ struct js_event event; if( read( fd, &event, sizeof(struct js_event) ) >= sizeof(struct js_event) ){ switch( event.type & 0x7f ){ case JS_EVENT_BUTTON: if( event.number < BUTTON_DATA_MAX ){ ButtonData[ event.number ]= event.value != 0; } break; case JS_EVENT_AXIS: if( event.number < STICK_DATA_MAX ){ StickData[ event.number ]= event.value; } break; } } } close( fd );
read() はイベントが発生するまで block するので、スレッドを使うか
他の IO のように select() を用いることが可能です。
ボタンやスティックの配列はコントローラ毎に異なっています。
アプリケーション側で配列の変更が必要です。
Windows の DirectInput で認識する配列とも異なっているようです。
とりあえず下記コントローラで動作を確認しました。
・PS3 コントローラ (USB 接続)
・Xbox 360 USB コントローラ
・PS4 DUALSHOCK4 (USB 接続)
PS3コントローラは各ボタンの感圧情報も送られてきます。
使用した環境は下記の通り。
Ubuntu 13.10 x86_x64 (64bit)
● デバイスの種類の判定
自分の環境では Gamepad だけでなく、ワイヤレスマウスのレシーバーも
/dev/input/js* に列挙されていました。
そのままでは不便なので、Gamepad かどうかの識別を行います。
udevadm info /dev/input/js0
上記コマンドでは正しく ID_INPUT_JOYSTICK と識別されているので、
udevadm の手法を調べてみました。
まず stat() で /dev/input/js* の device 番号を調べます。
struct stat file_stat; stat( "/dev/input/js0", &file_stat ); assert( S_ISCHR( file_stat.st_mode ) ); int dev_major= major( file_stat.st_rdev ); int dev_minor= minor( file_stat.st_rdev );
キャラクタデバイスなら "/sys/dev/char/" 以下に "major:minor" という
名前でリンクが作られているので、それを開きます。
その中の device/capabilities 以下に、対応しているボタンなどの
情報が格納されています。
例えば major,minor が 13,0 なら
/sys/dev/char/13:0/device/capabilities/key
中身は text で long 型の bit mask です。
udevadm では、この bitmask を読み取ってゲームコントローラ用のボタンが
存在してるかどうかで判別を行っていました。
ボタンなどのシンボルは /usr/include/linux/input.h で定義されています。
static bool ReadCaps( int dev_major, int dev_minor, const char* file_name, CapsType& caps ) { caps.Clear(); char cap_name[CAP_NAME_MAX]; sprintf_s( cap_name, CAP_NAME_MAX, "/sys/dev/char/%d:%d/device/capabilities/%s", file_name, dev_major, dev_minor ); int fd= open( cap_name, O_RDONLY ); if( fd < 0 ){ return false; } char caps_buffer[STAT_MAX_SIZE]; memset( caps_buffer, 0, STAT_MAX_SIZE ); read( fd, caps_buffer, STAT_MAX_SIZE-1 ); close( fd ); const char* cp= caps_buffer; for(; *cp && *cp != '\n' ;){ unsigned long value= strtoul( cp, NULL, 16 ); for(; *cp && *cp != ' ' ; cp++ ); for(; *cp == ' ' || *cp == '\n' ; cp++ ); caps.Push( value ); } return true; } bool IsJoystick( const char* device_path ) { struct stat file_stat; stat( device_path, &file_stat ); assert( S_ISCHR( file_stat.st_mode ) ); int dev_major= major( file_stat.st_rdev ); int dev_minor= minor( file_stat.st_rdev ); CapsType caps_ev; CapsType caps_key; CapsType caps_abs; ReadCaps( dev_major, dev_minor, "ev", caps_key ); ReadCaps( dev_major, dev_minor, "key", caps_key ); ReadCaps( dev_major, dev_minor, "abs", caps_key ); if( caps_ev.TestBit( EV_ABS ) && caps_abs.TestBit( ABS_X ) && caps_abs.TestBit( ABS_Y ) ){ if( cap_key.TestBit( BTN_A ) || caps_key.TestBit( BTN_TRIGGER ) || caps_key.TestBit( BTN_1 ) ){ return true; } } return false; }
abs に ABS_X / ABS_Y のアナログの絶対座標データが含まれており、
かつ key に BTN_A / BTN_TRIGGER / BTN_1 のいずれかが存在している場合に
JOYSTICK とみなしています。
class CapsType { public: enum { CAPS_BUFFER_MAX= 32, }; private: unsigned long CapsBuffer[CAPS_BUFFER_MAX]; unsigned int Index; public: CapsType() : Index( 0 ) { Clear(); } void Clear() { memset( CapsBuffer, 0, sizeof(unsigned long) * CAPS_BUFFER_MAX ); Index= 0; } void Push( unsigned long value ) { if( Index < CAPS_BUFFER_MAX ){ CapsBuffer[Index++]= value; } } bool TestBit( unsigned int bit_id ) const { const int LONG_BIT_SIZE= sizeof(unsigned long) * 8; unsigned int bit_no= bit_id & (LONG_BIT_SIZE-1); unsigned int data_no= bit_id / LONG_BIT_SIZE; if( data_no < Index ){ return ( CapsBuffer[Index - data_no -1] & (1UL<Linux は LP64 なので long 型のサイズは可変です。
64bit OS なら 64bit, 32bit なら 32bit となる点に注意。
32bit 時は CAPS_BUFFER_MAX が不足する可能性があるため、厳密には
/usr/include/linux/input.h で定義されている EV_MAX, KEY_MAX, ABS_MAX
からバッファサイズを求めた方が安全です。
bitmask は上位データから並んでいるので、読み込んだあとのテーブルが
逆順になる点も注意が必要です。関連エントリ
・Linux 他 X11 で OpenGL 4 を使う
・Android 3.1 と GamePad のイベントの詳細 (2)
・Android 3.1 と GamePad のイベントコード