LINEで送る

今回は、ペンの基本的な機能となるボタンの状態取得、消しゴムかどうかのチェックする方法を紹介します。
また、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に関する情報をご紹介する予定ですので、お楽しみに!

Top