WintabをWPFで利用する方法③
2014年02月07日
今回は、ペンの基本的な機能となるボタンの状態取得、消しゴムかどうかのチェックする方法を紹介します。
また、Intuosシリーズのペンには一意なIDが割り振られており、Wintabを利用することでペンのIDを取得することができます。
この一意なIDを取得する方法も紹介します。
1.消しゴムの実装
Intuosシリーズのペンは、ペンの反対側が消しゴムになっています。
消しゴムかどうか判定する方法は、以下の3パターンがあります。
- ・パケットのpkOrientation.orAltitudeからペンの向きを判定する。
- ・パケットのpkCursorからカーソルの種別を判定する。
- ・パケットのpkStatusでTPS_INVERTビットが立っているか判定する。(Wintab 1.1以降で利用可能です)
今回は3個目のpkStatusを利用して、消しゴムかどうかを判定します。
まずはパケットのステータスを、WintabStructsに定義します。
/// <summary> /// パケットのステータス /// </summary> public enum EWintabPacketStatusValue { TPS_PROXIMITY = 0x0001, TPS_QUEUE_ERR = 0x0002, TPS_MARGIN = 0x0004, TPS_GRAB = 0x0008, TPS_INVERT = 0x0010 }
パケット受信時に、pkStatusをチェックし、TPS_INVERTのビットが立っていた場合は、背景色で線を引くように修正します。
if (packet.pkNormalPressure.pkAbsolutePressure != 0) { // アプリケーション内の位置を取得 Point screenPoint = PointToScreen(new Point(0, 0)); int x = (int)(packet.pkX - screenPoint.X); int y = (int)(packet.pkY - screenPoint.Y); // 描画に使用するブラシ SolidColorBrush brush = Brushes.Black; // TPS_INVERTのビットをチェックして消しゴムかどうか判断 if ((packet.pkStatus & (uint)EWintabPacketStatusValue.TPS_INVERT) != 0) { // 消しゴムの場合はブラシの色を変更する brush = Brushes.White; } if (m_Point.X == -1) { // 始点なので座標を保存する m_Point.X = x; m_Point.Y = y; } else { // 線の追加 addPath((int)m_Point.X, (int)m_Point.Y, x, y, (int)packet.pkNormalPressure.pkAbsolutePressure, brush); // 位置の保存 m_Point.X = x; m_Point.Y = y; } } else { m_Point.X = -1; }
また、addPathメソッドは、線を引くのに使用するSolidColorBrushを指定するよう変更しています。
// 線をCanvasに追加する private void addPath(int x, int y, int x2, int y2, int pressure, SolidColorBrush brush) { Line l = new Line(); // 始点と終点の設定 l.X1 = x; l.Y1 = y; l.X2 = x2; l.Y2 = y2; // 線のスタイル設定 l.Stroke = brush; l.StrokeStartLineCap = PenLineCap.Round; // 線の太さは筆圧によって変更する l.StrokeThickness = (double)pressure / (double)m_MaxPressure * 10.0; // Canvasへ線を追加 screenCanvas.Children.Add(l); }
以上で、消しゴムの実装は完了です。
アプリケーションを実行して試してみましょう。
線を引き、ペンの反対側では線を消せるようになっているかと思います。
もちろん、消しゴム側でも筆圧は有効なので、筆圧によって消される範囲が変化します。
2.ボタン処理の実装
1個目のボタンを押している間、線の色が変わる処理を実装します。
まずは、ボタンの状態をWintabStructsに定義します。
/// <summary> /// ボタンの状態 /// </summary> public enum EWintabPacketButtonCode { TBN_NONE = 0, TBN_UP = 1, TBN_DOWN = 2 }
ボタンの状態は、「変化なし」・「ボタンが離された」・「ボタンが押された」の3つのみです。
Wintabでは、ボタンの状態が変化した時のみイベントが発行されるので、ボタンが押されている状態なのかは、アプリケーション側で保持する必要があります。
ボタンの状態は、パケットのpkButtonsに入っていますが、上位ワード・下位ワードで入っている情報が異なり、下位ワードにはボタンの番号、上位ワードにはボタンの状態が入ります。
1個目のボタンが押されている場合に色を変更しますので、下位ワードで0×0001のビットが立っている時にボタンの状態をチェックします。
// ウインドウプロシージャ private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == (int)EWintabEventMessage.WT_PACKET) { WintabPacket packet = WintabManager.GetPacket(lParam, (UInt32)wParam.ToInt32()); // ボタンが押されているかチェック // 下位ワードにはボタン番号、上位ワードにはボタンの状態が入る uint lw = packet.pkButtons & 0x0000FFFF; uint hw = (packet.pkButtons & 0xFFFF0000) >> 16; if ((lw & 0x0001) != 0) { // ボタンを押している間のみ色を変更する if ((hw & (uint)EWintabPacketButtonCode.TBN_DOWN) != 0) { m_PushButton = true; } else if ((hw & (uint)EWintabPacketButtonCode.TBN_UP) != 0) { m_PushButton = false; } } if (packet.pkNormalPressure.pkAbsolutePressure != 0) { // アプリケーション内の位置を取得 Point screenPoint = PointToScreen(new Point(0, 0)); int x = (int)(packet.pkX - screenPoint.X); int y = (int)(packet.pkY - screenPoint.Y); // 描画に使用するブラシ SolidColorBrush brush = Brushes.Black; // TPS_INVERTのビットをチェックして消しゴムかどうか判断 if ((packet.pkStatus & (uint)EWintabPacketStatusValue.TPS_INVERT) != 0) { // 消しゴムの場合はブラシの色を変更する brush = Brushes.White; } else { // ボタンの状態によって線の色を変える if (m_PushButton == true) { brush = Brushes.Red; } } if (m_Point.X == -1) { // 始点なので座標を保存する m_Point.X = x; m_Point.Y = y; } else { // 線の追加 addPath((int)m_Point.X, (int)m_Point.Y, x, y, (int)packet.pkNormalPressure.pkAbsolutePressure, brush); // 位置の保存 m_Point.X = x; m_Point.Y = y; } } else { m_Point.X = -1; } } return IntPtr.Zero; }
以上で、ボタンの処理実装は完了です。
アプリケーションを実行して試してみましょう。
Intuos4のペンでは下側のボタンが1個目のボタンとなります。
このボタンを押しながら線を書くと、線が赤で描画されるようになり、ボタンを離すと、黒で線が描画されます。
3.ペンのIDを取得する方法
Intuosシリーズには、ペンに一意なIDが割り振られており、以下の2種類のIDを組み合わせることで、全てのデバイスで一意なIDとなります。
- ・CSR_TYPE:デバイスの種別に割り振られた一意なID
- ・CSR_PHYSID:デバイスの種別内で一意なID
上記のIDをパケットを受信した時に、アプリケーション上に表示する機能を実装します。
まずは、WTI_CURSORSのインデックスをWintabStructsに定義します。
/// <summary> /// WTI_CURSORSのインデックス定義 /// </summary> public enum EWTICursorsIndex { CSR_NAME = 1, CSR_ACTIVE = 2, CSR_PKTDATA = 3, CSR_BUTTONS = 4, CSR_BUTTONBITS = 5, CSR_BTNNAMES = 6, CSR_BUTTONMAP = 7, CSR_SYSBTNMAP = 8, CSR_NPBUTTON = 9, CSR_NPBTNMARKS = 10, CSR_NPRESPONSE = 11, CSR_TPBUTTON = 12, CSR_TPBTNMARKS = 13, CSR_TPRESPONSE = 14, CSR_PHYSID = 15, CSR_MODE = 16, CSR_MINPKTDATA = 17, CSR_MINBUTTONS = 18, CSR_CAPABILITIES = 19, CSR_TYPE = 20 }
次に、IDを表示するTextBlockをXAML上に設置します。
<Window x:Class="WpfWintabSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <TextBlock x:Name="uniqueIdText" TextAlignment="Right"/> <TextBlock x:Name="screenText"/> </Grid> </Window>
最後に、パケットを受信した時に、画面上にIDを表示する処理を追加します。
// ウインドウプロシージャ private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == (int)EWintabEventMessage.WT_PACKET) { WintabPacket packet = WintabManager.GetPacket(lParam, (UInt32)wParam.ToInt32()); // 端末の種別、IDを組み合わせてユニークなIDを生成する ulong deviceType = WintabManager.GetDeviceType(1); ulong deviceId = WintabManager.GetDeviceID(1); ulong uniqueId = (deviceType << 32) + deviceId; uniqueIdText.Text = "UniqueId : " + uniqueId.ToString("x16"); ・ ・ ・ } return IntPtr.Zero; }
以上で、ペンのIDを表示する処理の実装が完了しました。
実際にアプリケーションを実行してみましょう。
ペンをタブレット上に持っていくことで、アプリケーション上にIDが表示されます。
表示されているIDの下8桁は、タブレットのドライバに付属する「ワコム タブレットのプロパティ」アプリケーションからも確認することができます。
「ワコム タブレットのプロパティ」の「タブレットについて」→「診断」を実行すると、ペンの「シリアルNO」がアプリケーション上に表示されます。
このシリアルNOと、IDの下8桁に同じものが表示されることを確認してみてください。
Wintab関連の実装については、一旦これで終了となります。
サンプルソースはこちらにアップしてありますので、興味のある方は実際に試して頂ければと思います。
次回は、Androidに関する情報をご紹介する予定ですので、お楽しみに!