Linux」カテゴリーアーカイブ

Linux で Gamepad の値を読み込む & デバイスの判定

Linux では接続した Gamepad の情報を /dev/input/js* から読み取ることができます。
構造体などは /usr/include/linux/joystick.h で定義されており、
具体的な方法は下記ドキュメントの通り。

joystick-api.txt

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 のイベントコード

Android SmartWatch SmartQ ZWatch (5) debian

MIPS 版の debian があったので試してみました。

Linux on Android

Debian-MIPS.zip を解凍すると image file が 2GB になり
そのままでは内部ストレージから溢れます。
実際の容量は 200M くらいなので、500M の新しい image file を作って
中身を全部コピーしました。
/sdcard/debian の中に debian.img として入れています。

アプリは使いません。
起動スクリプト debian.sh (debian-script.zip) はそのままでは使えないので、
必要な箇所を書き換えて adb shell から直接実行しています。

adb shell では su で容易に root になれます。
最初から busybox も入っています。

system update 直後は /system が rw で mount されているので、
この状態なら chroot で Linux を起動できます。
Z Watch を再起動すると /system は ro に戻りますが、
下記公式ページのドキュメントにあるように rw で remount できます。

SmqrtQ Z Watch Programming Guide

device 名が異なるので、debian.sh の最初で mount (remount) している
命令を削除します。

#mount -o remount,rw /dev/block/mmcblk0p5 /system

cut, grep, sed, umount などの存在しない命令は、
前に busybox を挿入します。

perm=$(id|cut -b 5)
↓
perm=$(id|busybox cut -b 5)

image file のパスは直接変数 kit に代入します。

export kit=$(dirname $0)
↓
export kit=/sdcard/debian

あくまで動くだけ。
もしアプリが root 権限で走るなら、端末の terminal からも
実行できるでしょう。

関連エントリ
Android SmartWatch スマートウオッチのスペック比較表
Android SmartWatch SmartQ ZWatch (4) アプリの管理
Android SmartWatch SmartQ ZWatch (3) 腕に関数電卓
Android SmartWatch SmartQ ZWatch (2)
Android 4.1 SmartWatch SmartQ Z Watch

Nexus 7 (2012) Ubuntu desktop 上での開発とバッテリー設定

以前の記事以降、外出時の開発用 PC として Nexus 7 (2012) が活躍しています。
GUI 周りでは Unity の dash 画面 (半透明で全画面を覆うメニュー) が特に
重いですが、terminal を開いてコードを書いたり make する分には十分です。

nexus7_flview4.jpg

↑ 3D (OpenGL ES 2.0) も動きます。(開発中)

最近 OpenGL ES 3.0 のために Nexus 10 ばかり使っていて、
Ubuntu の Nexus 7 (2012) を放置していたら起動しなくなりました。
おそらくバッテリーの過放電でしょう。

きちんと確認しなかったのが悪いのですが、電源容量が低下した時の設定が
Power off ではなく、何もしない設定になっていたのだと思われます。
Sleep で放置していたので、使わない時は電源を切るか
Android でブートしなおしておいた方がよいかもしれません。

他の Nexus 7 (2012) に新たに Ubuntu を入れなおしました。
Android 4.3 に MultiROM で Ubuntu desktop 13.04 を入れましたが
いくつか問題がありました。

(1) factory image JWR66Y (Nexus 7 nakasi) で bootloader が書き込めない

 以前ダウンロードしておいた JWR66V が動いたのでこちらを使用しました。
 JWR66V の bootloader が書き込まれたあとは JWR66Y も適用出来るようです。

(2) TWRP_multirom_n7_20130830 で ubuntu desktop を install できない

 ubuntu の img.zip decode 時にエラーが出ています。
 一つ前のバージョン TWRP_multirom 20130825 ではうまくいきました。

電源容量低下時の設定は Power Off にしています。
その状態になってみないと確認できないので、ある程度は自己責任で使うことに
なるかと思います。

現在 Ubuntu desktop が動くのは Tegra3 の Nexus 7 2012年版だけです。

関連エントリ
Nexus 7 上に開発環境をつくる (3) Ubuntu
Nexus 7 上に開発環境をつくる (2) Bluetooth と OpenGL ES 2.0
Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)

Nexus 7 Ubuntu Touch Developer Preview (2)

Nexus 7 の MultiROM が Ubuntu touch 対応となっています。

xda: [WiFi&3G] MultiROM v8 (new recovery for Ubuntu Touch)

TWRP の新しいバージョン (2013/02/22以降) で

xda: Ubuntu touch preview

この手順に従い install 可能となりました。
Android, Ubuntu Desktop, Ubuntu Touch を共存させて
起動時に選択することができます。

これでタブレットの利用には Android を、
単体でのプログラミングなら Ubuntu Desktop を、
Ubuntu Touch UI やアプリのテスト時に Desktop Preview を起動できます。

Ubuntu Touch Developer preview は adb 経由で chroot することからもわかるように、
Android 上に install した chroot 版 Linux に近い構造となっているようです。
HW アクセラレート対応の UI が乗っており、
Android SDK の AVD のようにアプリケーションの動作確認に利用することができます。

Ubuntu をベースとした Tablet/Phone 向け OS なので、
Android のようにタッチによる操作がしやすくなっています。
ただし Touch UI 上で Linux アプリがそのまま使えるわけではないので、
新しいプラットフォームと考えた方が良いでしょう。
UI 上で Linux らしさを期待するなら、今のところは Desktop 版との使い分けが
ベストだと思います。

関連エントリ
Nexus 7 Ubuntu Touch Developer Preview
Nexus 7 上に開発環境をつくる (4) Ubuntu 13.04
Nexus 7 上に開発環境をつくる (3) Ubuntu
Nexus 7 上に開発環境をつくる (2) Bluetooth と OpenGL ES 2.0
Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)

Nexus 7 Ubuntu Touch Developer Preview

Nexus 7 等のモバイルデバイスで動く Ubuntu Touch を試してみました。
開発者向けです。

Ubuntu Touch

Nexus 7 には下記のように通常の desktop 版 Ubuntu もあります。

Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)

↓この二つは別物です。

◎ Nexus 7 で動く Desktop 版 Ubuntu
◎ Nexus 7 で動く Ubuntu Touch Developer Preview

Desktop 版は PC と同一の豊富なアプリケーションを動かすことができる反面、
タッチだけの操作は必ずしも使いやすいとはいえませんでした。
キーボードやマウスを繋げば快適で、超小型のパソコンになります。

Ubuntu Touch は Android や iOS のように、タッチ操作に特化した UI が入ります。
まだ開発者向けで、操作に HOST PC が必要となることがあります。(2013/02/22現在)

● install

Desktop 版 Ubuntu の native install と同じように、本体の Android OS を置き換える形になります。
(Android のデータは全部消えます。)
MultiROM が対応すれば、他の環境と共存できるようになるかもしれません。

Install

Ubuntu 上で走る installer が用意されています。
PC に Ubuntu が入っている状態なら上記の Install 手順に従うだけです。

コマンドは python で書かれているので、
手順がわかれば Windows から手動で入れることもできるかもしれません。

bootloader の unlock が必要です。
installer は fastboot だけでなく adb も使うので、
Android を起動し USB デバッグにチェックを入れておきます。

● UI

インストールが完了すると、端末上で Ubuntu Touch の UI を試すことが可能となります。

ReleaseNotes

UI 上でできることがまだ少ないですが、ReleaseNotes に
書かれているように USB + adb 経由でシェルにログインできます。

adb root
adb shell

端末上で shell が動いたらさらにコマンドを実行します。

ubuntu_chroot shell

これで ARM 版 Ubuntu 12.10 としてシェルが動きます。
ReleaseNotes にあるように ssh を入れておけば Wi-Fi 経由で login できます。

apt-get update 
apt-get install openssh-server 

普通の Linux コマンドを install できるようです。コンパイラとか動きます。
残念ながら ubuntu-sdk は入れられませんでした。

関連エントリ
Nexus 7 上に開発環境をつくる (4) Ubuntu 13.04
Nexus 7 上に開発環境をつくる (3) Ubuntu
Nexus 7 上に開発環境をつくる (2) Bluetooth と OpenGL ES 2.0
Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)