Wintabを学ぶ(Delphiに実装編)

あっちゃー.ネタは作っていたが更新するの忘れてたー!!
ヽ(´ー`)ノ ついにゲームキューブ(黒)を買っちゃったー!!!
シャインあと2つだー!!! (;゚Д゚)青コインどこだー!!!!


さて,とりあえずDelphiでタブレットからの情報を得ることができるようになった.
マズは,Wintabで使用する定数とか構造体とかをDelphi用に作り直すことになる.
定数はWintab.hを見ながらそのまんま移植,構造体はデータサイズに気をつけながら移植しまっしょい.
・・・で,ある程度必要なものを移植したのが,
↓のファイル.

WintabH.pas

ふぃー,結構あるにゃー・・・(;´Д`)

さて,実際にWintabを使うには幾つかポイントがあります.
マズは,「PACKET構造体」.
PACKET構造体は,タブレットから送られてくるデータを格納するために使用します.
使えるデータは全部で↓.
typedef struct tagPACKET{
  HCTX         pkContext;
  UINT         pkStatus;
  LONG         pkTime;
  WTPKT        pkChanged;
  UINT         pkSerialNumber;
  UINT         pkCursor;
  DWORD        pkButtons;
  DWORD        pkX;
  DWORD        pkY;
  DWORD        pkZ;
  UINT         pkNormalPressure;
  UINT         pkTangentPressure;
  ORIENTATION  pkOrientation;
  ROTATION     pkRotation;
}PACKET;

このうち,取得したいデータだけを選択してPACKET構造体を作りなおし.
Pascalらしく,名前をTPacketに変更.
今回は,タブレットの「X,Y座標」と「筆圧」のデータだけを取得したいので,こんな感じになりました.
const PACKETDATA = PK_X or PK_Y or PK_NORMAL_PRESSURE ;

type TPacket = record
  pkX, pkY         : integer ;
  pkNormalPressure : UINT ;
end;
ここで,PACKETDATAという定数は後で使用しますが,TPacketで取得したいデータのフラグを組み合わせて宣言しておきます.
このフラグの組み合わせと,実際のTPacket構造体のデータは当然一緒じゃないといけません.
あと,TPacket構造体のメンバの並びは,PACKETで示されている順になってる必要があります.

むぅー? こういう仕組みはどっかで見たにゃー・・・DirectXで使う「柔軟な頂点フォーマット (FVF)」に似てる・・・.
ちなみに,C言語とかで開発するときは,Wintabのサンプルについてくる「PKTDEF.H」を使うなら,PACKETDATA構造体に取得したいデータのフラグを宣言することで,かってにPACKET構造体を作ってくれます.
むぅーん,Delphiの場合やどうやればいいか分からんので,今回はTPacket構造体は自作・・・(TдT)


あと,WintabAPIも移植してくないとねー.
実際には,DLLを呼び出すように,インタフェースを宣言すればOK.
今回使うものだけ宣言しとくか.この辺は,Wintab.hを見ながら移植.

  function WTInfo( wCategory, nIndex : DWORD ; var lpOutput ) : DWORD ;
    stdcall ; external 'WINTAB32.DLL' name 'WTInfoA' ;
  function WTOpen( handle : HWND ; var LOGCONTEXT ; AutoInitialize : BOOL ) : THCTX ;
    stdcall ; external 'WINTAB32.DLL' name 'WTOpenA' ;
  function WTClose( Context : THandle ) : LongBool ;
    stdcall ; external 'WINTAB32.DLL' ;
  function WTQueueSizeSet( FContext : THCTX ; nPkts : Integer ) : LongBool ;
    stdcall ; external 'WINTAB32.DLL' ;
  function WTOverlap( Context:THCTX ; fToTop:LongBool ) : LongBool ;
    stdcall ; external 'WINTAB32.DLL' ;
  function WTPacketsGet( Context:THCTX ; MacPackets:Integer ; PacketBuf:Pointer ) : Integer ;
    stdcall ; external 'WINTAB32.DLL' ;

ただ,この方法でDLL呼び出すと,DLLが無い(タブレットが使えない)環境ではアプリケーションが落ちるなぁ・・・
やっぱ,ちゃんとLoadLibraryとか使って動的にDLLを呼び出さないといけないにゃー.
上手いこと使い易いコンポーネントでも出来ないかなー.


さて,タブレットからデータを取得する方法は大きく2つ用意されているようです.
 ・WTPacketsGet関数を使ってポーリングする.
 ・タブレットからWT_PACKETメッセージを送信させる.

目標としては,マウスでもタブレットでもどっちでも動くアプリケーションを目指すので,近い感じのサンプルがポーリングの方を つかってたので,今回はポーリングの方を使うゾ!
サンプルをみた感じ,ポーリングのタイミングは,各種マウスイベントの発生時でよさそうな感じ♪


いよいよ実装.
アプリケーションで使用する変数を宣言しておきます.
初期化後はFContextを介してタブレットにアクセスすることになります.
{ Private 宣言 }
FContext   : THctx ;
FPacketBuf : array [0..PACKETNUM-1] of TPacket ;

とりあえず初期化部分を作成.
WTInfoでデフォルトのパラメタを取得してから,タブレットから取得したいデータのフラグをセットします.
で,いろいろパラメタセットし終わったら,WTOpenでタブレットコンテキストをオープンします.
{
  フォーム作成時

  このときタブレットの初期化する
}
procedure TForm1.FormCreate(Sender: TObject);
var
  lc : TLogContext ;
begin

  // デフォルトシステムコンテキストを得る

  WTInfo( WTI_DEFSYSCTX, 0, lc ) ;

  // 各パラメタを変更する

  lc.lcPktData  := PACKETDATA ;
  lc.lcPktMode  := 0 ;
  lc.lcMoveMask := PACKETDATA ;

  lc.lcBtnUpMask := lc.lcBtnDnMask ;

  lc.lcOutOrgX  := 0 ;
  lc.lcOutExtX  := GetSystemMetrics( SM_CXSCREEN ) ;
  lc.lcOutOrgY  := 0 ;
  lc.lcOutExtY  := -GetSystemMetrics( SM_CYSCREEN ) ;


  // コンテキストオープン

  FContext := WTOpen( handle, lc, true ) ;

  // コンテキストのキューのサイズを指定する

  WTQueueSizeSet( FContext, 32 ) ;

end;


とりあえず,終了時にコンテキストを閉じておきます.
{
  フォーム破棄(アプリケーション終了)時

  コンテキストを閉じておく
}
procedure TForm1.FormDestroy(Sender: TObject);
begin
 WTClose( FContext );
end;

あとは,マウスイベント発生時に,タブレットコンテキストのキューをチェックします.
マウスイベントハンドラで,↓の関数を呼び出してみました.
{
  キューのチェック

  キューに格納されているパケットを取り出して,
  パラメタをフォームに表示する.
}
procedure TForm1.CheckQueue();
var
  packet_num, i : Integer ;

begin

  WTOverlap( FContext, true );


  // 格納されているパケットを全て取り出す!

  packet_num := WTPacketsGet( FContext, PACKETNUM, @FPacketBuf );

  if ( packet_num > 0 ) then begin

    for i:=0 to packet_num -1 do begin
      // FPacketBuf[i].pkX;                    X座標を得る
      // FPacketBuf[i].pkY;                    Y座標を得る
      // FPacketBuf[i].pkNormalPressure;       Z圧力を得る
    end;

  end;

end;

とまぁ,問題山積みですが,こんな感じでタブレットからのデータを取得できました!
それにしても,どんなコンポーネントを作れば使いやすいかなー・・・
TCanvasを継承するのもなぁ・・・
むぅーん,どんなクラスを継承すれば・・・・<(TдT)> 悩むぜ!


今回作成したサンプル.
Delphi6 Personal (タダのやつ)で作りました.
TabletSample.zip(215KB)
ただし,DLLを静的にリンクしているので,タブレットの無い環境の実行は保証できませんがね.
ψ(`∀´)ψ けっけっけ




<< Back to Diary...

[PR]女性が輝く公文の先生募集中!:全国で教室開設説明会開催