Windows7 Multitouch API その(2) WM_GESTURE 系

Windows7 のマルチタッチ系イベントには二通りあることを前回説明しました。
高度な方、WM_GESTURE 系についてもう少し詳しくさわってみます。

メッセージは WM_MOUSEMOVE のように座標の更新毎に多数送られてきます。
このとき一つの操作の組が GID_BEGIN と GID_END で囲まれます。
例えば PAN 操作の場合

GID_BEGIN
GID_PAN
GID_PAN
GID_PAN
GID_PAN
...
GID_END

といった感じに。
また別の操作が認識されれば、それらが GID_BEGIN~GID_END で囲まれます。
マウスでいえば LBUTTONDOWN / LBUTTONUP のようなもの。
タッチだと WM_TOUCHDOWN / WM_TOUCHUP でしょう。
開始と終了が明確にわかるようになっています。

ただ上の例だと最初と最後の GID_PAN 自身にも GF_BEGIN, GF_END フラグが
追加されているので、実は GID_BEGIN/GID_END を無視しても何も困りません。
フラグ値を追加して書き直してみます。

GID_BEGIN
GID_PAN  GF_BEGIN
GID_PAN
GID_PAN
...
GID_PAN
GID_PAN  GF_INERTIA
...
GID_PAN  GF_INERTIA
GID_PAN  GF_INERTIA|GF_END
GID_END

GF_INERTIA は PAN 操作のみ追加される慣性で、操作後もしばらくメッセージが
送られてきます。

前回も書きましたがマニュアルにはまだ間違いが含まれているので、仕様が確定
するまではヘッダファイルを見た方が確実です。ヘッダは WinUser.h です。

WM_GESTURE が送ってくる座標値はスクリーン座標です。
GESTUREINFO 構造体には対象となるウィンドウのハンドルが含まれていますが、
ウィンドウ内の座標系に変換する場合は自分で行う必要があります。

一本指のシングルタッチなどはマウスと同じ扱いなので、マウス系のメッセージを
処理する必要があります。よってフリック操作はマウス扱いです。

パンや回転、ズーム(2点間の距離)などの操作は結構誤動作というかノイズが入るようです。
極端に異なる値が来たときは無視するような処理が必要になるかもしれません。
また GID_ROTATE の最初のメッセージには回転値として不正な値が入っているようです。
GID_ROTATE かつ GF_BEGIN が立っている場合、ullArguments の下位 16bit を 0 と
見なした方がよいです。
回転しようとすると必ず反転してしまうおかしな症状に最初悩みました。

用途によるとは思いますが、やはり凝ったことを行いたい場合は WM_GESTURE ではなく
WM_TOUCH 系 (WM_TOUCHUP/WM_TOUCHDOWN/WM_TOUCHMOVE) を
使った方が良さそうです。
WM_GESTURE の場合操作判定が先に行われてしまうので、操作の位置を制限することが
難しくなります。
例えば特定のオブジェクトの上だけで回転やズームを行い、それ以外の座標では
常にパン操作と判定したい場合などあまりきれいに操作を区別することができません。

最初は簡単に見えたものの、実際にやってみるとまだまだ。
プログラムの方もいろいろと工夫が必要になりそうです。

関連エントリ
Windows7 Multitouch API

Windows7 Multitouch API

タッチ関連の API は 2種類あります。
WM_TOUCH 系と WM_GESTURE 系

WM_TOUCH 系の方が低レベルで、直接複数のタッチ座標を取り出すことが出来ます。
WM_GESTURE の方はいくつかの決まった操作を容易に受け取ることができます。

WM_TOUCH 系のメッセージを有効にするには RegisterTouchWindow() を呼び出します。
これを実行しておかないと WM_TOUCH~ が送られて来ません。

データを受け取るのは簡単です。

WM_TOUCHDOWN
WM_TOUCHUP
WM_TOUCHMOVE

などマウスとよく似ているメッセージが来るので、さらに
GetTouchInputInfo() を使って詳細な情報を読み取ります。

起動時の判定

// ハードがマルチタッチをサポートしているかどうか
int  value= ~GetSystemMetrics( SM_DIGITIZER );
if( !(value & 0xc0) ){
    RegisterTouchWindow( hwnd, 0 );
}

メッセージ

    case WM_TOUCHDOWN:
    case WM_TOUCHUP:
    case WM_TOUCHMOVE:
	WM_Touch( mes, wparam, lparam );
	return	FALSE;

読み出しの例

void WM_Touch( UINT mes, WPARAM wparam, LPARAM lparam )
{
    int	inputs= LOWORD( wparam );
    TOUCHINPUT	tbuf[TOUCHMAX];
    HANDLE	hinput= reinterpret_cast( lparam );
    if( GetTouchInputInfo( hinput, inputs, tbuf, sizeof(TOUCHINPUT) ) ){
	TOUCHINPUT*	tp= tbuf;
	for( int i= 0 ; i< inputs ; i++, tp++ ){
	    ...
	}
    }
    CloseTouchInputHandle( hinput );
}

座標値そのままなので、これを元にズームや回転などの操作を検出するには
さらに一手間いります。

WM_GESTURE 系はそのあたりを簡単にしてくれます。
下記の操作が定義されています。

GID_ZOOM
GID_PAN
GID_ROTATE
GID_TWOFINGERTAP
GID_ROLLOVER

これらのメッセージは RegisterTouchWindow() を実行すると来なくなるので
WM_GESTURE 系を使う場合は RegisterTouchWindow() を実行してはいけません。

WM_GESTURE が送られてきたらさらに追加情報を受け取ります。

    case WM_GESTURE:
	WM_Gesture( mes, wparam, lparam );
	return	FALSE;
void WM_Gesture( UINT mes, WPARAM wparam, LPARAM lparam )
{
    GESTUREINFO	ginfo;
    memset( &ginfo, 0, sizeof(GESTUREINFO) );
    ginfo.cbSize= sizeof(GESTUREINFO);
    HGESTUREINFO	hgesture= reinterpret_cast( lparam );

    if( GetGestureInfo( hgesture, &ginfo ) ){
        ...
    }
    CloseGestureInfoHandle( hgesture );
}

WM_GESTURE 系の仕様は若干変更があったようで、資料によっては記載内容が
異なっていることがあります。例えば今でも

MSDN WM_GESTURE Message

このページにある dwCommand は存在しておらず GESTUREINFO 構造体の
dwID のことだと思われます。
同じように dwArgument も 64bit の ullArguments に変更されているようです。

・dwCommand → GESTUREINFO dwID
・dwArgument → GESTUREINFO ullArguments
・lParam → GESTUREINFO ptsLocation

その他いくつか気が付いた点。

初期状態では GID_ROTATE などのメッセージが来ませんでした。
SetGestureConfig() を使って送って欲しいメッセージの登録が出来るようです。

const int	ConfigCount= 5;
GESTURECONFIG	config[ConfigCount];
memset( config, 0, sizeof(GESTURECONFIG) * ConfigCount );

config[0].dwID= GID_ZOOM;
config[0].dwWant= GC_ZOOM;

config[1].dwID= GID_PAN;
config[1].dwWant= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY
	|GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY
	|GC_PAN_WITH_GUTTER
	|GC_PAN_WITH_INERTIA;

config[2].dwID= GID_ROTATE;
config[2].dwWant= GC_ROTATE;

config[3].dwID= GID_TWOFINGERTAP;
config[3].dwWant= GC_TWOFINGERTAP;

config[4].dwID= GID_ROLLOVER;
config[4].dwWant= GC_ROLLOVER;

SetGestureConfig( hwnd, 0, 5, config, sizeof(GESTURECONFIG) );

これで全部のメッセージとオプションが有効になるはずです。

座標は ptsLocation に、追加パラメータは ullArguments に入ります。
GID_PAN + GF_INERTIA フラグが ON の場合は、ullArguments の上位 32bit に
慣性のパラメータが格納されているようです。
GID_INERTIA というのは存在していません。

なお、これらの動作を確認するには

・Windows7 Beta
・Windows SDK for Windows 7 BETA
・MultiTouch 対応 PC

が必要です。Vista では起動時に DLL の互換性が無くエラーになります。
HP の TouchSmart PC IQ800 を使いました。

Windows7 SDK の設定には少々注意が必要です。
新しい DirectX SDK もリリースされているので、DirectX SDK March 2009 を
併用する場合は特に。基本的にあとからリリースされた方を上にします。

・DirectX SDK March 2009
・Windows SDK for Windows 7 BETA
・VisualStudio 2008

VisualStudio の Tools → Options → Projects and Solutions → VC++ DIrectories の設定

Include files

$(DXSDK_DIR)include
$(WindowsSdkDir)\include
$(VCInstallDir)include
~

Library files (x64)

$(DXSDK_DIR)lib\x64
$(WindowsSdkDir)lib\x64
$(VCInstallDir)lib\amd64
~

Library files (x86)

$(DXSDK_DIR)lib\x86
$(WindowsSdkDir)lib
$(VCInstallDir)lib
~

またあらかじめスタートメニュー Microsoft Windows SDK v7.0 から
Windows SDK Configuration Tool を起動して v7.0 を選択しておきます。

関連エントリ
DirectX SDK March 2009
Windows7 とマルチタッチ / HP TouchSmart PC IQ800
Direct2D と Direct3D10.1 の下位互換

DirectX SDK March 2009

2009年初の新しい SDK です。

DirectX SDK (March 2009)

大きなトピックは Direct2D/DirectWrite の Technical Preview が含まれたこと。
Windows7 beta + Windows7 SDK beta でないと試すことが出来なかった
Direct2D / DirectWrite が Vista でも使えるようになりました。
ドキュメント類は以前から MSDN に上がっているのでそちらを参照することになります。
インストールによって d2d1_beta.dll, dwrite_beta.dll が入りました。

これでおそらく D2D/DWrite だけなら Windows7 SDK が無くてもテストできると思います。
ヘッダ類もこちらの方が新しいようです。コメントが増えて新しい定義もあります。

その他 DXGI1.1 も入りました。元々ヘッダは含まれてましたが、dxgi_beta.dll / lib が
追加されてドキュメントも統合されたようです。

Direct3D11 TechPreview の方はあまり大きな変化があったようには見えませんが、
細かく変更が入っています。
たとえばヘッダには CD3D11_RECT, CD3D11_DEPTH_STENCIL_DESC,
CD3D11_BLEND_DESC 等のヘルパークラスが多数追加されているようです。
Direct2D と同じようにデフォルト値による初期化がサポートされています。
デフォルト値を示すオーバーロード用の type は CD3D11_DEFAULT です。

マニュアルでは Shader Model 5 の Attributes に attrib, maxtessfactor,
earlydepthstencil が追加。Intrinsic Functions には
countbits, EvaluateAttributeAtControid, EvaluateAttributeAtSample
が増えています。
またヘッダを見ないと使い方が分からなかった D3DCompiler のマニュアルも入りました。

Shader の項目には Reflection 関連のインターフェースが追加されています。
このあたりヘッダを見てもかなり手が入っていることが分かります。
まだ Effect (fx) はありませんが進展は見られます。

その他マニュアルには載ってませんが D3D11_CREATE_DEVICE_BGRA_SUPPORT
も目新しいところです。

関連エントリ
Direct2D (7) Tessellate の 2
Direct2D と Direct3D10.1 の下位互換
Windows7 リモートデスクトップと Direct3D
Direct3D11/DirectX11 (18) GPU を使ったアウトラインフォントの描画の(6)
Direct3D11 Technical Preview D3D11の互換性、WARP Driver

SSD OCZ Vertex

先週末、運良く店頭で入手できたので OS を入れ替えて一週間ほど使いました。

・OCZSSD2-1VTX60G

60GB の 2.5inch SSD です。22800円
RAM 8GB + Win7 beta x64 なので、ページングファイル無しにして休止状態ファイル
クリーナーで消して OS のみ 10GB くらい。

特に速度を実感できるのはソフトウエアのインストールで、残り時間が正確に
表示されないくらい。Windows の起動や終了も速いし、重いアプリケーションの
立ち上げも違いが分かるほどに短縮しています。

プログラミング作業はどうかというと、元々そんなに編集もコンパイル時間も
困ってなかったのであんまり速い感じはしていません。
その代わり作業中、非常に静かなのが心地よいです。

ファンの一定の音はありますが不定期に鳴り出す音が消えました。
ファイルに書き込んでるな、とか裏で何か動き出したな、とか無いので結構違います。
HDD が寝ていて、いざアクセスしようとしたらスピンアップ待ちで起動まで待たされる
こともないわけです。動作中の配置換えなんかも気にせず出来そう。

プロセッサは年々速くなっていくのでストレージとの速度差は離れる一方でしたが
ここに来て急に差が縮まり出したようです。
OS やアプリケーションも HDD のシークが遅いという前提で作られていますが、
そのうち全く気にしなくても良くなるかもしれません。
ファイルアクセスを速くするためのいくつかの工夫も、きっと古い技術となって
忘れていくのでしょう。

机の奥から古いレシートが出てきました。
1999年7月9日に 13GB の HDD を 21480円で買ってました。

タカラトミー Q-TRAIN 赤外線コマンドの 解析

PC-OP-RS1 (KURO-RS) を使って ROBO-Q と同じように調べてみました。
Q-TRAIN のコントローラの赤外線信号です。

TAKARA TOMY Q-TRAIN

●コマンドフォーマット

コマンドは 8bit 分
下記の順番で送信されています

先頭
  C C A 0 R L B F


 CC = BAND A~D (00,01,10,11)
 A = 自動移動の指定かどうか (左サイドのスイッチが on なら 1)
 0 = 常にゼロ
 R = 右ボタン (→)
 L = 左ボタン (←)
 B = 後退ボタン (↓)
 F = 前進ボタン (↑)

R L B F の各ボタンは同時押しが出来ます。
例えば前進+右旋回だと F + R が同時に ON になります。


コマンド組み合わせの例

BAND A 前進	00000001
BAND B 前進	01000001
BAND C 前進	10000001
BAND D 前進	11000001

BAND A 前進自動	00100001
BAND A 後退	00000010
BAND A 左	00000100
BAND A 右	00001000
BAND A 前進+左	00000101

●コマンドエンコード

ヘッダや 1bit 分の波形も長く取られており ROBO-Q とは異なっています。

ヘッダ常に H 6.3msec

bit
 0 = L 0.5msec + H 1.7msec
 1 = L 1.5msec + H 0.7msec

エンコードされた bit データは ROBO-Q と違い、0 でも 1 でも同じ長さになります。
よって 1コマンドはだいたい 23.9msec


例 BAND D 前進 + 左 で受け取った波形 (0.1msec 単位のサンプリング)

// ヘッダ
11111111111111111111111111111111111111111111111111111111111111
// 1
000000000000000 1111111
// 1
000000000000000 111111
// 0
00000 11111111111111111
// 0
00000 11111111111111111
// 0
00000 11111111111111111
// 1 (左ボタン)
000000000000000 1111111
// 0
00000 11111111111111111
// 1 (前進ボタン)
0000000000000000 111111

使用したツールは ROBO-Q 同様「スーの道具箱/分解してみよう/PC-OP-RS1」の
IrReceiver です。

関連エントリ
タカラトミー ROBO-Q (3) PC から操作する実験
タカラトミー ROBO-Q (2) 赤外線コマンドの解析
タカラトミー ROBO-Q