X11: XInput (2) - クラス

デバイス情報のクラス
前回、デバイス情報の一覧を表示しましたが、classes メンバの値は表示していませんでした。
ここでは、各デバイスのクラスの情報について説明します。

デバイスのクラスには、そのデバイスで操作できるボタン・キーや、取得できる値などの情報が格納されています。

classes は、XIAnyClassInfo 構造体のポインタの配列で構成されています。

ただし、実際のクラス構造体は、タイプによって情報が異なるため、まずは、XIAnyClassInfo 構造体の type メンバでクラスのタイプを確認し、その後、(XIAnyClassInfo *) のポインタを各タイプごとの構造体のポインタに型変換してから、値を参照する必要があります。
クラスタイプと、対応する構造体
XIKeyClassXIKeyClassInfo
XIButtonClassXIButtonClassInfo
XIValuatorClassXIValuatorClassInfo
XIScrollClass (ver 2.1)XIScrollClassInfo
XITouchClass (ver 2.1)XITouchClassInfo
XIGestureClass (ver 2.4)XIGestureClassInfo

※ここでは、タッチに関する情報は扱いません。
XIKeyClass
キーボードなどで、キーイベントとして送信できるキーがある場合、1つのキークラスがあります。

typedef struct
{
   int type;
   int sourceid;
   int num_keycodes;
   int *keycodes;
} XIKeyClassInfo;

num_keycodesこのデバイスで使用できるキーコード (物理ボタン) の数。1以上
keycodesデバイスが送信できるキーコードの配列 (num_keycodes 個)
XIButtonClass
マウスなどで、ポインタボタンとしてイベントを送信できる場合、1つのボタンクラスがあります。

※そのデバイスで実際に存在しているボタンの情報ではなく、X のポインタボタンとして扱う際のボタンの情報です。

typedef struct
{
   int    type;
   int    sourceid;
   int    num_buttons;
   Atom   *labels;
   XIButtonState state;
} XIButtonClassInfo;

typedef struct {
   int mask_len;
   unsigned char *mask;
} XIButtonState;

num_buttonsこのデバイスで使用できるボタンの数。1 以上
labels各ボタンのラベルを示すアトムの配列 (num_buttons 個)。
値が None の場合があります。
stateクライアントから見た時の、現在のボタンの押し状態。
※ボタンマッピングが適用された後 (Xlib の XSetPointerMapping 関数参考)

mask_len は、mask のバイト数。
mask は可変長の配列で、先頭バイト&下位ビットから順に、各ボタンが押されているかどうかのフラグがセットされています。
X のボタン番号がそのままビット位置に対応するため、Button1 (1) は先頭バイトの bit1 (0x02) になります。
一番最初のビットはボタン番号 0 となるため、実際は使用されません。
XIValuatorClass
キーやボタンの他に、値として送信できる情報がある場合、バリュエータクラスが、その個数分あります。
X・Y 座標やスクロール、筆圧などです。

typedef struct
{
   int         type;
   int         sourceid;
   int         number;
   Atom        label;
   double      min;
   double      max;
   double      value;
   int         resolution;
   int         mode;
} XIValuatorClassInfo;

numberこのデバイスでのバリュエータ番号 (0〜)
labelNone でない場合、種類を示すアトム
min,max許可されている最小値と最大値。
両方ともゼロの場合、このデバイスには最小値も最大値もない。
value現在の値
resolution解像度 (units/m)
modeXIModeAbsolute で、値は絶対値。
XIModeRelative で相対値。
XIScrollClassInfo (ver 2.1)
スクロール (マウスホイール) に関する情報です。

コアの X では、Button4 を垂直ホイール Up、Button5 を垂直ホイール Down、Button6 を水平ホイール Up、Button7 を水平ホイール Down として、ボタンとして扱いますが、XI2 では、ホイールを1単位分のスクロールとして扱うことができます。

デバイスのクラスに、スクロール用のバリュエータクラスがある場合 ("Rel Vert Scroll" など)、垂直/水平ごとに1つ、スクロールクラスがあります。

typedef struct
{
   int         type;
   int         sourceid;
   int         number;
   int         scroll_type;
   double      increment;
   int         flags;
} XIScrollClassInfo;

numberこのスクロールが適用されるバリュエータ番号。
("Rel Vert Scroll" などのバリュエータの number の値)
scroll_typeスクロールのタイプ。
XIScrollTypeVertical または XIScrollTypeHorizontal
incrementスクロールの1単位の値 (正の値)
flagsフラグ。

XIScrollFlagNoEmulation : サーバーは、このバリュエーターでの従来のボタンイベントをエミュレートしません。
XIScrollFlagPreferred : 同じスクロールタイプが複数ある場合、これが優先される。
プログラム
各デバイスの、クラス情報を表示するプログラムです。

$ cc -o run e08-xi2.c util.c -lXlib -lXi

<e08-xi2.c>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/extensions/XInput2.h>
#include "util.h"

//keyclass

static void _put_keyclass(XIKeyClassInfo *p)
{
    int i,*pcode;

    printf("<KeyClass> [srcid:%d] num:%d\n ", p->sourceid, p->num_keycodes);

    pcode = p->keycodes;

    for(i = 0; i < p->num_keycodes; i++)
        printf("%d,", *(pcode++));

    printf("\n");
}

//buttonclass

static void _put_buttonclass(XIButtonClassInfo *p)
{
    int i;

    printf("<ButtonClass> [srcid:%d] num:%d\n", p->sourceid, p->num_buttons);

    //labels

    for(i = 0; i < p->num_buttons; i++)
    {
        printf("btn%d: ", i + 1);

        if(p->labels[i])
        {
            put_atom_name(p->labels[i]);
            printf("\n");
        }
        else
            printf("<None>\n");
    }

    //state

    printf("state:(len:%d) ", p->state.mask_len);

    for(i = 0; i < p->state.mask_len; i++)
        printf("0x%02X ", p->state.mask[i]);

    printf("\n");
}

//valuator class

static void _put_valclass(XIValuatorClassInfo *p)
{
    printf("<ValuatorClass> [srcid:%d]\n"
    " number(%d) label(",
        p->sourceid, p->number);

    put_atom_name(p->label);
    printf(")\n");

    printf(" min(%.3f) max(%.3f) value(%.3f) reso(%d) mode(%s)\n",
        p->min, p->max, p->value, p->resolution,
        (p->mode == XIModeAbsolute)? "Abs": "Rel");
}

//scroll class

static void _put_scrollclass(XIScrollClassInfo *p)
{
    printf("<ScrollClass> [srcid:%d]\n"
    " number(%d) type(%s) inc(%.3f) flags(0x%X)\n",
        p->sourceid, p->number,
        (p->scroll_type == XIScrollTypeVertical)? "Vert": "Horz",
        p->increment, p->flags);
}

int main(int argc,char **argv)
{
    Display *disp;
    XIDeviceInfo *info,*pi;
    XIAnyClassInfo **ppclass,*pclass;
    int major,minor,dnum,i,j,cnum;
    
    disp = XOpenDisplay(NULL);
    if(!disp) return 1;

    set_display(disp);

    //version

    major = 2, minor = 4;

    if(XIQueryVersion(disp, &major, &minor) != Success)
    {
        printf("unsupported XI2\n");
        XCloseDisplay(disp);
        return 1;
    }

    //デバイス情報

    info = XIQueryDevice(disp, XIAllDevices, &dnum);

    for(i = 0; i < dnum; i++)
    {
        pi = info + i;

        printf("\n---- [%s] (%d) ---\n", pi->name, pi->deviceid);

        cnum = pi->num_classes;
        ppclass = pi->classes;

        for(j = 0; j < cnum; j++, ppclass++)
        {
            pclass = *ppclass;

            switch(pclass->type)
            {
                case XIKeyClass:
                    _put_keyclass((XIKeyClassInfo *)pclass);
                    break;
                case XIButtonClass:
                    _put_buttonclass((XIButtonClassInfo *)pclass);
                    break;
                case XIValuatorClass:
                    _put_valclass((XIValuatorClassInfo *)pclass);
                    break;
                case XIScrollClass:
                    _put_scrollclass((XIScrollClassInfo *)pclass);
                    break;
            }
        }
    }

    XIFreeDeviceInfo(info);

    //

    XCloseDisplay(disp);

    return 0;
}
出力例
---- [Wacom One by Wacom M Pen stylus] (10) ---
<ButtonClass> [srcid:10] num:8
btn1: <None>
btn2: <None>
btn3: <None>
btn4: <None>
btn5: <None>
btn6: <None>
btn7: <None>
btn8: <None>
state:(len:8) 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
<KeyClass> [srcid:10] num:248
  ...
<ValuatorClass> [srcid:10]
 number(0) label(Abs X)
 min(0.000) max(21600.000) value(0.000) reso(100000) mode(Abs)
<ValuatorClass> [srcid:10]
 number(1) label(Abs Y)
 min(0.000) max(13500.000) value(0.000) reso(100000) mode(Abs)
<ValuatorClass> [srcid:10]
 number(2) label(Abs Pressure)
 min(0.000) max(65536.000) value(0.000) reso(1) mode(Abs)
<ValuatorClass> [srcid:10]
 number(3) label(Abs Tilt X)
 min(-64.000) max(63.000) value(0.000) reso(57) mode(Abs)
<ValuatorClass> [srcid:10]
 number(4) label(Abs Tilt Y)
 min(-64.000) max(63.000) value(0.000) reso(57) mode(Abs)
<ValuatorClass> [srcid:10]
 number(5) label(Abs Wheel)
 min(-900.000) max(899.000) value(0.000) reso(1) mode(Abs)
<ValuatorClass> [srcid:10]
 number(6) label(Rel Horiz Scroll)
 min(-1.000) max(-1.000) value(0.000) reso(1) mode(Abs)
<ValuatorClass> [srcid:10]
 number(7) label(Rel Vert Scroll)
 min(-1.000) max(-1.000) value(0.000) reso(1) mode(Abs)
<ScrollClass> [srcid:10]
 number(6) type(Horz) inc(65535.000) flags(0x0)
<ScrollClass> [srcid:10]
 number(7) type(Vert) inc(65535.000) flags(0x0)

---- [ELECOM ELECOM BlueLED Mouse] (11) ---
<ButtonClass> [srcid:11] num:9
btn1: Button Left
btn2: Button Middle
btn3: Button Right
btn4: Button Wheel Up
btn5: Button Wheel Down
btn6: Button Horiz Wheel Left
btn7: Button Horiz Wheel Right
btn8: Button Side
btn9: Button Extra
state:(len:8) 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
<ValuatorClass> [srcid:11]
 number(0) label(Rel X)
 min(-1.000) max(-1.000) value(1752.576) reso(0) mode(Rel)
<ValuatorClass> [srcid:11]
 number(1) label(Rel Y)
 min(-1.000) max(-1.000) value(834.192) reso(0) mode(Rel)
<ValuatorClass> [srcid:11]
 number(2) label(Rel Horiz Scroll)
 min(-1.000) max(-1.000) value(0.000) reso(0) mode(Rel)
<ValuatorClass> [srcid:11]
 number(3) label(Rel Vert Scroll)
 min(-1.000) max(-1.000) value(29880.000) reso(0) mode(Rel)
<ScrollClass> [srcid:11]
 number(2) type(Horz) inc(120.000) flags(0x0)
<ScrollClass> [srcid:11]
 number(3) type(Vert) inc(120.000) flags(0x0)

ボタンの state は、ボタンを押した状態でプログラムを実行すると、値を確認できます。
マウス
マウスは、垂直ホイールと、サイドボタンが2つあるタイプです。
X のポインタボタンでは、垂直ホイール・水平ホイールまでの機能は固定されているため、水平ホイールは実際のデバイスには存在しませんが、機能としては存在する形になります。

state は可変長のデータですが、すべてのデバイスで 8byte になっていました。最大で 63 個のボタンに対応する形です。

マウスのバリュエータには、X 座標、Y 座標、水平スクロール、垂直スクロールがあります。
なお、値が相対モードの場合、min,max の値は意味がないので、-1 になっています。
ペンタブレット
ペンタブレットでは、キークラスもあります。
ドライバの設定によっては、ボタンをキー操作として扱うこともできるので、ペンタブレットからキーイベントを生成できる形になっています。

バリュエータには、X 座標、Y 座標、筆圧、傾き X、傾き Y、ホイール(円形の)、垂直スクロール、水平スクロールがあります。
いくつかの値は実際のデバイスには存在しませんが、ペンタブレットで使用できる一般的な値はすべて存在している形になります。

筆圧の情報を取得したい場合は、ラベルが "Abs Pressure" の値を確認することになります。
デバイスに関する情報
デバイスのクラスは、基本的に XI イベントに関連する情報ですが、ドライバが管理しているような、デバイスに関する詳細な情報は、デバイスに関連付けられたプロパティから、読み込み&変更することができます。

XIListProperties, XIGetProperty, XIChangeProperty, XIDeleteProperty が、プロパティに関する関数です。

例えばペンタブレットのデバイスでは、以下のようなプロパティが存在します。

Wacom Debug Levels
Device Product ID
Wacom Panscroll Threshold
Wacom Pressure Recalibration
Wacom Button Actions
Wacom button action 3
Wacom button action 2
Wacom button action 1
Wacom button action 0
Wacom Tool Type
Wacom Touch Gesture Parameters
Wacom Enable Touch Gesture
Wacom Hover Click
Wacom Enable Touch
Wacom Sample and Suppress
Wacom Pressure Threshold
Wacom Proximity Threshold
Wacom Serial ID binding
Wacom Serial IDs
Wacom Pressurecurve
Wacom Rotation
Wacom Tablet Area
Device Node
Device Accel Velocity Scaling
Device Accel Adaptive Deceleration
Device Accel Constant Deceleration
Device Accel Profile
Coordinate Transformation Matrix
Device Enabled

それぞれがどのようなデータかはデバイスによるので、このような情報が必要な場合は、個別に処理してください。