Multi-Touch APIをWPFで利用する方法①
2015年05月14日
今回から、Wacom feel™ Multi-Touch APIをWPFで利用する方法を紹介します。
まずは、APIの初期化処理やタッチデバイスの列挙、詳細情報の表示を実装します。
Multi-Touch APIとは、Wacomのマルチタッチデバイスの詳細なデータを取得するために利用するSDKです。
Windows標準のAPIでは、タッチ座標のみで詳しい情報を取得することはできませんが、Multi-Touch APIを利用することで、指とタッチパネルとの接触面の大きさなどを取得することができます。
また、タッチイベントをアプリケーション内のみで完結させ、OS側にタッチイベントを発生させないようにすることで、マウスとタッチを同時に利用することができます。
Multi-Touch APIを利用するためにインストールが必要なDLL(wacommt.dll)はドライバと一緒にインストールされるので、ドライバ以外をインストールする必要はありません。
1.プロジェクトの作成
サンプルのソースコードは、Visual Studio Express 2013 for Desktopを使って作成しています。
「WPFアプリケーション」でプロジェクトを作成します。
2.Wacom feel™ Multi-Touch APIの関数定義
Multi-Touch API用の関数定義用に「MTAPI.cs」を作成し、以下の定義を追加します。
class MTAPI { // APIバージョン public const int Version = 4; // 初期化処理 [DllImport("wacommt.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "WacomMTInitialize")] protected static extern WacomMTError _WacomMTInitialize(Int32 libraryAPIVersion); // 終了処理 [DllImport("wacommt.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] public static extern void WacomMTQuit(); // 接続しているタブレットデバイスのIDを収得 [DllImport("wacommt.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "WacomMTGetAttachedDeviceIDs")] protected static extern int _WacomMTGetAttachedDeviceIDs(IntPtr deviceArray, UInt32 bufferSize); // タブレットデバイスの情報取得 [DllImport("wacommt.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl, EntryPoint = "WacomMTGetDeviceCapabilities")] protected static extern WacomMTError _WacomMTGetDeviceCapabilities(int deviceID, IntPtr capabilityBuffer); }
上記のように、C#からMulti-Touch APIの関数を利用するには、DllImportを利用します。
この際に、ポインタはIntPtrに変更し、構造体なども独自に定義する必要があります。
3.定数・構造体の定義
Multi-Touch APIで利用する定数や構造体を定義します。
サンプルのソースコードでは「FeelMultiTouchAPIConst.cs」に追加しています。
定数の定義は必須ではありませんが、数値だけでは分かりにくいので定義しています。
数が多いので、定数についてはソースコードと以下のサイトを照らしあわせて確認してください。
データ構造体と列挙型
http://wdnet.jp/library/feelmulti-touch/wacom-feel-multi-touch#5
タッチデバイスの情報を表す構造体については以下のように定義しています。
// タブレット情報の構造体 [StructLayout(LayoutKind.Sequential)] public struct WacomMTCapability { public Int32 Version; public Int32 DeviceID; public WacomMTDeviceType Type; public float LogicalOriginX; public float LogicalOriginY; public float LogicalWidth; public float LogicalHeight; public float PhysicalSizeX; public float PhysicalSizeY; public Int32 ReportedSizeX; public Int32 ReportedSizeY; public Int32 ScanSizeX; public Int32 ScanSizeY; public Int32 FingerMax; public Int32 BlobMax; public Int32 BlobPointsMax; public WacomMTCapabilityFlags CapabilityFlags; }
Multi-Touch APIの関数と直接やり取りを行う構造体は、StructLayoutに「LayoutKind.Sequential」を指定する必要があります。
各項目について簡単にまとめましたが、詳しく知りたい方はこちらを参照してください。
・Version
構造体のバージョン番号です。
・DeviceID
タッチデバイスを識別するためのIDです。
このIDは接続ごとに変わる可能性があります。
・Type
タッチデバイスを識別するための値です。
この値をチェックすることで、タッチデバイスがモニタを持っているかを判別することができます。
・LogicalOriginX, LogicalOriginY
タッチデバイスの座標(ピクセル単位)の最小値です。
・LogicalWidth, LogicalHeight
タッチデバイスの横幅、縦幅(ピクセル単位)です。
・PhysicalSizeX, PhysicalSizeY
タッチデバイスの検知領域(mm単位)です。
・ReportedSizeX, ReportedSizeY
タッチデバイスの横幅、縦幅(ネイティブカウント単位)です。
・ScanSizeX, ScanSizeY
タッチデバイスの横幅、縦幅(スキャンコイル単位)です。
・FingerMax
同時に認識する指の数です。
・BlobMax
BLOB集合内の最大数です。
・BlobPointsMax
BLOBを構成するBLOBの最大数です。
・CapabilityFlags
タッチデバイスがサポートするデータの種類をビットで表しています。
4.ラップ関数の追加
IntPtrのままだと使い難いので、私の場合はDllImportした関数を以下のようにラップして利用しています。
class MTAPI { ・ ・ ・ // 初期化処理 public static WacomMTError WacomMTInitialize() { // 定義しているバージョンを指定する return _WacomMTInitialize(Version); } // 接続しているタブレットのID取得 public static int[] WacomMTGetAttachedDeviceIDs() { int[] idArray = new int[0]; int res = _WacomMTGetAttachedDeviceIDs(IntPtr.Zero, 0); if (res != 0) { IntPtr pIdArray = IntPtr.Zero; try { pIdArray = MemoryUtil.AllocUnmanagedBuffer(typeof(int), res); res = _WacomMTGetAttachedDeviceIDs(pIdArray, sizeof(int) * (UInt32)res); idArray = MemoryUtil.MarshalUnmanagedBufferArray<int>(pIdArray, (uint)res); } catch (Exception ex) { throw ex; } finally { MemoryUtil.FreeUnmanagedBuffer(pIdArray); } } return idArray; } // タブレットデバイスの情報取得 public static WacomMTCapability WacomMTGetDeviceCapabilities(int deviceID) { IntPtr capabilitiesPtr = IntPtr.Zero; try { capabilitiesPtr = MemoryUtil.AllocUnmanagedBuffer(typeof(WacomMTCapability)); WacomMTError res = _WacomMTGetDeviceCapabilities(deviceID, capabilitiesPtr); if (res != WacomMTError.WMTErrorSuccess) { // 失敗した場合は例外を発生させる MemoryUtil.FreeUnmanagedBuffer(capabilitiesPtr); throw new Exception("WacomMTGetDeviceCapabilities : " + res); } return MemoryUtil.MarshalUnmanagedBuffer<WacomMTCapability>(capabilitiesPtr); } catch (Exception ex) { throw ex; } finally { MemoryUtil.FreeUnmanagedBuffer(capabilitiesPtr); } } }
関数の引数にポインタを利用する場合、配列や構造体分のメモリを確保したIntPtrを指定する必要があります。
確保したメモリは自動的に開放されないので、最後に開放するよう処理をおこないましょう。
IntPtr関連の処理は「MemoryUtil.cs」にまとめてありますので、興味がある方はそちらも参照してみてください。
5.レイアウトの作成
画面上には、現在接続されているタッチデバイスをプルダウンで表示し、選択したタッチデバイスの情報をテキストで画面上に表示します。
以下の内容をMainWindow.xamlへ記載しています。
<Window x:Class="WacomFeelMultiTouchAPI_WPF.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> <Grid.RowDefinitions> <RowDefinition Height="23"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <ComboBox Name="devicesBox" Grid.Row="0" Height="23" Width="250" HorizontalAlignment="Left" VerticalAlignment="Top" SelectionChanged="devicesBox_SelectionChanged"/> <Canvas Grid.Row="1" Background="#EEEEEE"/> <TextBlock Name="capabilitiesText" Background="#DDDDDD" Grid.Row="1" VerticalAlignment="Top" HorizontalAlignment="Left" Foreground="Black" TextWrapping="Wrap"/> </Grid> </Window>
6.タッチデバイスの詳細表示
最後にMainWindow.csでタッチデバイスの詳細表示を行う処理を追加します。
/// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { // タブレットデバイスのID配列 private int[] mIdArray = null; // 選択しているタブレットデバイスの情報 private WacomMTCapability mCapability; /// <summary> /// コンストラクタ /// </summary> public MainWindow() { InitializeComponent(); // ウインドウのクローズイベント this.Closing += new System.ComponentModel.CancelEventHandler(MainWindow_Closing); // MultiTouchAPIの初期化 try { // TouchAPIの初期化処理 WacomMTError res = MTAPI.WacomMTInitialize(); if (res == WacomMTError.WMTErrorSuccess) { // タブレットデバイスのID取得 mIdArray = MTAPI.WacomMTGetAttachedDeviceIDs(); devicesBox.Items.Clear(); devicesBox.Items.Add("利用するタブレットを選択してください"); // IDからタブレットデバイスをリストアップする foreach (UInt32 i in mIdArray) { String devName = "Tablet Device #" + i; devicesBox.Items.Add(devName); } if (devicesBox.Items.Count != 0) { devicesBox.SelectedIndex = 0; } } else { // 初期化処理に失敗 throw new Exception("WacomMTInitialize error : " + res); } } catch (Exception ex) { // 例外が発生した場合はメッセージを表示し、アプリケーションを終了します MessageBox.Show(ex.Message); Environment.Exit(1); } } void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { try { // ウインドウのクローズ時にMultiTouchAPIを終了 MTAPI.WacomMTQuit(); } catch { } } private void devicesBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { capabilitiesText.Text = ""; if (devicesBox.SelectedIndex != 0) { try { // タブレットの情報を取得し、画面上に表示する mCapability = MTAPI.WacomMTGetDeviceCapabilities(mIdArray[devicesBox.SelectedIndex - 1]); String t = ""; t += "Version : " + mCapability.Version + "\n"; t += "DeviceID : " + mCapability.DeviceID + "\n"; t += "Type : " + mCapability.Type + "\n"; t += "LogicalOriginX : " + mCapability.LogicalOriginX + "\n"; t += "LogicalOriginY : " + mCapability.LogicalOriginY + "\n"; t += "LogicalWidth : " + mCapability.LogicalWidth + "\n"; t += "LogicalHeight : " + mCapability.LogicalHeight + "\n"; t += "PhysicalSizeX : " + mCapability.PhysicalSizeX + "\n"; t += "PhysicalSizeY : " + mCapability.PhysicalSizeY + "\n"; t += "ReportedSizeX : " + mCapability.ReportedSizeX + "\n"; t += "ReportedSizeY : " + mCapability.ReportedSizeY + "\n"; t += "ScanSizeX : " + mCapability.ScanSizeX + "\n"; t += "ScanSizeY : " + mCapability.ScanSizeY + "\n"; t += "FingerMax : " + mCapability.FingerMax + "\n"; t += "BlobMax : " + mCapability.BlobMax + "\n"; t += "BlobPointsMax : " + mCapability.BlobPointsMax + "\n"; t += "CapabilityFlags : " + mCapability.CapabilityFlags + "\n"; capabilitiesText.Text = t; } catch (Exception ex) { MessageBox.Show(ex.Message); } } } }
まず、MainWindow()内では、WacomMTInitialize()で初期化を行い、WacomMTGetAttachedDeviceIDs()でタッチデバイスを列挙したものをプルダウンに表示しています。
ドライバをインストールしていない場合などにAPIを実行すると例外が発生するため、例外をキャッチしアプリケーションを終了するようにしています。
また、アプリケーションの終了時には、WacomMTQuit()でMulti-Touch APIの終了処理を行っています。
プルダウンの内容が変更されると、devicesBox_SelectionChanged()が呼ばれるので、WacomMTGetDeviceCapabilities()を実行してタッチデバイスの詳細情報を取得して表示しています。
以上で実装は終了です。
実際に、DTH-2400を接続して実行した場合は以下のような情報が表示されました。
サンプルソースはこちらにアップしてありますので、興味のある方は実際に試して頂ければと思います。
次回は、タッチ情報を取得する処理について紹介します。