ONKYO TW317A5 の活用
国内初となる "スレート PC" ONKYO TW シリーズの TW317A5 に関する記事です。
Delphi によるスレート PC プログラミングも並行してやりたいと思います。
諸元
TW317 シリーズには初代の TW317A5 、au の TW317A7 、法人向けの TW317A7PH があります。
スペック
TW317A5 及びそのバリエーションのスペックは以下の通りです。
OS
Windows® 7 Home Premium 32ビット 正規版
(日本語版)
Windows® 7 Professional 32ビット 正規版
(日本語版)
Office ソフト
-
CPU
インテル® Atom™ プロセッサー N450
(1 コア / 1.66 Ghz / HT 対応 / 64bit 対応 / TDP 5.5W)
ディスプレイ
タッチパネル付 11.6型 ワイド TFT カラー液晶 (光沢パネル / LEDバックライト搭載)
最大1,366×768ドット (約 1,619 万色)
マルチタッチ対応 (静電容量方式 / 最大接触点数: 2)
グラフィックシステム
インテル® GMA 3150 Broadcom Crystal HD
サウンドシステム
Realtek ALC269 オーディオコーデック (HD Audio 準拠)
システムメモリー
PC2-5300/667MHz 1GB (DDR2 SDRAM 200pin SO-DIMM)
PC2-5300/667MHz 2GB (DDR2 SDRAM 200pin SO-DIMM)
ディスクドライブ
SSD 32GB (SanDisk SSD P4 32GB / mSATA)
光学ドライブ
-
無線 LAN
IEEE802.11 b/g/n (Atehros AR9002WB-1NG Wireless network Adapter)
Bluetooth®
Bluetooth® Ver.2.1 + EDR (Atheros AR9002WB-1NG Bluetooth Module)
Webカメラ
130万画素 CMOS センサー搭載 Web カメラ (前面)
スピーカー
内蔵ステレオスピーカー
マイク
内蔵モノラルマイク
メモリーカードスロット
SD / SDHC / SDXC / MMC 共用スロット
センサー
・3 軸加速度センサー
・照度センサー (AC 時は無効)
インターフェース
・ヘッドホン端子 (ステレオ ミニジャック)
・USB 2.0 ポート×2
・デジタルディスプレイ出力端子 (HDMI ミニ Type C)
・SIM スロット
・ドック用コネクタ
バッテリー
リチウムイオンポリマーバッテリー
(7.4V 4,800mAh)
動作時間
約 5 時間
約 4.4 時間
本体寸法
295×195×14mm (突起物は含まず)
液晶部: 256×144mm
重量
約 1.0 kg
価格
¥69,800
(au ショップでご確認ください)
???
¥79,800
付属品
ACアダプター,リカバリーディスクキット,各種マニュアル,他
ネットブック的なスペックですが、Windwos 7 の機能は一通り備えています。
兄弟機
TW317 シリーズには兄弟機があります。
これらの機種のリソースを流用できる事を覚えておくといいと思います。
後継機
実質的な後継機は TW3A-A1 シリーズ です。
TW317 シリーズとの大まかな違いは以下の通りです。
CPU が Celeron / i3 / i7 になっている。
バッテリー駆動時間が伸びている。
サイズが若干大きくなっている (299×209×18。TW317 は 295×195×14)。
100g 程重くなっている。
USB ポートは 1 つ。
音量キーと機能キーが物理キーとして存在する。
3G 用 SIM スロットはない。
Windows 8 のメトロ UI の実質的な最低画面解像度である 1366×768 もそのままです。
アクセサリ
...極端に少ないのですよね。
画像でマット代わりに敷いてあるのは Embarcadero デベロッパーキャンプ の際に頂いた資料入れです (非売品)。エンボスで滑り止めになって丁度いい感じです。
ケース
au 純正品 (スレート PC TW317 専用レザーケース: TW3AC01) があります。au の携帯を所持していなくとも購入可能です。また、ぷらっとオンライン でも購入できます。
兄弟機 用のも使えます。
また、キャリングケースやインナーケースなら MacBook Air (11インチ用) のものを流用できます (MacBook Air は 299.5×192×17mm なのでサイズがほぼ同じ )。
こちらなら種類が豊富にあります (インナーはピッチリしたものが多いので注意。短辺のみ MacBook Air の方が 3mm 短いです。
ドッキングステーション
ONKYO 純正の PDS317A01 があります。
兄弟機 のも使えます。
円高で送料を含めても安く購入できるのなら、兄弟機 のものを購入するのもテでしょう。
スタイラス / タッチペン
マウスを使わないのなら、とりあえずスタイラス / タッチペンの 1 本も持っておくとイライラしなくて済みます。TW317A5 のタッチパネルは静電容量タイプなので、スタイラス / タッチペンによっては反応しません。静電容量方式タッチパネル対応のスタイラス/タッチペンかどうかを確認しましょう。
液晶保護シート
傷うんぬんより、とにかく指紋がベッタリ付きます。
こればかりは専用でないと。
ダイソーにも液晶保護フィルムが売ってありますが、汎用品なので自分でカットしなくてはなりません (BOX: 液晶保護フィルム No.31 / JAN: 4984343938083)。
ダイソーフィルムの注意点は...
端は使わない。 フィルムの端は個体によっては粘着力が弱いものがあります。面倒でも四辺をカットした方がいいでしょう (288mm×188mm で、角を丸く落とす。裏の方眼は正確でないので注意!)。最初に付いていた梱包用の画面保護フィルムを型紙としてカットするといいと思います。
スライド系の操作時に突っかかる事がある。 あくまで "表面保護フィルム" なので、滑らかに指が滑らない事があります。
カメラ用の穴をキレイに開けるのが困難。 カメラ用の穴はパンチで開けるのが簡単でいいのですが、キレイに開くかどうかはパンチ次第です。使わないフィルムの端で事前に試す事をオススメします。
最初から折り目状の傷が付いている事がある。 こればかりは開封しないと判りません。ついでに購入した "他のダイソー商品" と一緒の袋に入れていると折り目状の傷が付いたりしますので注意して下さい。また、開封時は面倒でも袋をカッターやハサミで切ってから取り出した方がいいと思います。外袋のベロの部分の糊がフィルムに貼り付き、剥がそうとした時に折り目状の小さな傷が付くことがあります。
この 4 点です。コストパフォーマンス / ランニングコスト重視ならダイソー、操作性重視なら ELECOM 等のものがいいと思います。
シートを貼る時の注意点ですが、
ONKYO のシールと Intel Atom シールは剥がす (シール痕は、セロハンテープで取った後に無水エタノール で拭くとキレイになる)。
ホコリの少ない場所で作業する。
ゴム手袋またはポリエチレン手袋を着けて作業すると指紋が付かない。
画面表面の汚れはメガネ拭きで。ティッシュ等では糸状のホコリが付く。
指紋などの画面表面の汚れは無水エタノール (ベンゼン/シンナー/除光液は不可) で拭き取り、くもりはメガネ拭きで取る。
画面表面またはシートの貼付面についたホコリはセロハンテープで取る。
これを繰り返すとシートを気泡なくキレイに貼れます。消せない気泡がある場所には微細なホコリがあると思って間違いないです。
スタンド
お好みで。ドッキングステーション を使うか、iPad 用のスタンドが流用できると思います。ちなみに、上の方にある画像のスタンドはダイソーで買った "まな板立て" です。
ディスプレイアーム
サブ機としてデスク上に配置するのなら、サンコー iPad用フレキシブルアーム IPFRAR01 が便利です (回転も可能です)。プラスドライバーと小さなモンキースパナ (プライヤーでもいい) で、各関節のネジを増し締めしてやれば TW317 シリーズ でも普通に使えます。
Amazon.co.jp ウィジェット
付属アプリケーション
タスクトレイにある幾つかのアプリケーションはスレート PC を扱う上で重要なものとなっています。
Millenium
画面の自動回転、無線 LAN / Bluetooth の ON / OFF、画面輝度の自動調整を行います。
アプリケーション側からすると勝手にローテーションされては困る事も多いのと、切り替えがそうスムーズではないので OFF にする事をオススメします。
無線 LAN や Bluetooth はここで簡単に ON / OFF できます。
ディスプレイの輝度を照度センサーにより自動で調整します...が、AC 接続時には動作しません。
Intel GMA (Graphics Media Accelarator) Driver for Mobile
(キーボードの) ホットキーにより画面を手動で回転する事ができます。
〔Ctrl〕+〔Alt〕+〔↑〕 正位置にする
〔Ctrl〕+〔Alt〕+〔↓〕 180°回転する。
〔Ctrl〕+〔Alt〕+〔←〕 左 90°回転する。
〔Ctrl〕+〔Alt〕+〔→〕 右 90°回転する。
この 4 つだけ覚えておくといいでしょう。デスクトップ PC やノートパソコンではこの (キーボードの) ホットキーを無効にする事が多いです。
Broadcom High Definition Video Decoder Diagnostic Utility (DTS_Info.exe)
TW317 シリーズには動画再生支援用に "Broadcom CrystalHD (AzureWave AW-VD920H )" が搭載されています。"%ProgramFiles%\BroadCom\BroadCom CrystalHD Decoder" にある DTS_Info.exe (Broadcom High Definition Video Decoder Diagnostic Utility) を実行すると、動画再生支援によるアクセラレーションが有効かどうかを調べる事ができます。
DTS_Info.exe はタスクトレイに常駐します。
動画再生支援が利用されていない (利いていない)。
動画再生支援が利用されている (利いている)。
DTS_Info.exe はデフォルトでは常駐していません。必要であれば DTS_Info.exe をスタートアップに登録して下さい。
フリック
フリックを制御します。
これは Windows 7 の機能なので [コントロールパネル | ペンとタッチ] から開く事ができます ("フリック" タブ)。 [ファイル名を指定して実行] から "control.exe /name Microsoft.PenAndInputDevices /page Flicks" と入力すると直接ジャンプできます。
レビュー
個人的な感想を。
箱は小さい
小さいが広い。
実装密度が高いので重くはないがずっしりしている。
手触りはマットな感じ。
ディスプレイは光沢があるので、指紋がやたら付く。
発熱は大した事ない。
電源ボタンはちょいと長押しで。
AC アダプタが小さくてカワイイ。
AC アダプタのコネクタは奥まで入れないといけない (途中に段差がある感じ)。
SP1 と IE9 を入れても 17GB の空き容量がある。
ドライバ類は C:\ONKYO にある。 1.5GB 程あるので、SSD の空き容量が少なくなってきたら外部メディアにバックアップを取るといいかもしれない。
ケースが欲しい。
スタンドも欲しい。
...アレ?TW317A5 にもドッキングコネクタあるじゃん。
1GB でもソコソコ動く。
SSD 32GB なんだから、アプリは追加インストール形態の方がいいんじゃ?
ホットキー (左上の丸いの) を押さえながら電源ボタンを 1 秒程度押すと 〔Ctrl〕+〔Alt〕+〔Del〕が送信されるのね。
ちなみに指紋は定番のメガネ拭き用クロスで拭き取るのがいいのですが、脂分が多い場合には "無水エタノール" で拭いてからメガネ拭き用クロスで拭くといいでしょう。ティッシュは物にもよりますが、細かい擦り傷が付く事があります。
分解
TW317A5 のメモリ換装を行うには分解するしかありません。構造はシンプルですが、上部カバーのツメを折りやすいので注意が必要です。
矢印の割れ目の所に爪を掛け、上方に向かって水平にこじ開ける。
○ 印のネジを左右 2 本外す。左側のネジ穴には封印のシールがある。
そのまま背面を下方へ、写真で裏になっている液晶パネル側を上方へ並行にスライドさせる。
上部の方からゆっくりと 75°近くまで起こす。裏蓋と液晶パネル側はフィルムケーブルで繋がっているので、勢いよく開けない事。
赤い四角で囲まれた箇所が SO-DIMM スロット。シールで保護してあるので、シールをはがして換装する必要がある。
メモリを換装し終わったら、分解と逆の手順で組み立てる。
※画像はクリックすると拡大します。
※分解はあくまでも自己責任で行ってください。
さらに分解
メモリ換装だけならシールドを外す必要はありませんが、SSD の換装やモジュールの追加を行う場合にはシールドを外す必要があります。シールドを外す際にはフィルムケーブルを取り外す必要があります。画像はシールドを外した状態の TW317A5 です。
シールドを外すためにはタッチパネル側と繋がっている 3 つのケーブルを外してやらなければなりません。
2 つのフィルムケーブルはコネクタを 90°跳ね上げてやると抜く事ができます。もう一つのコネクタはプルタブが付いていますのでそれを持って上に引き抜くと外れます。
Mini PCI-E スロット #1 (Half)
Wireless / Bluetooth モジュール (Atheros AR9002WB-1NG Bluetooth Module) が組み込まれています。
Mini PCI-E スロット #2
空きスロットです。GPS モジュール等 を組み込むことができます。
空きスロットを利用するには、モジュールを固定するためのネジが必要ですが、モジュールにはネジが付属していない事が多いので、別途用意する必要があります。暫定的でよければ、シールドに使われているネジが使えます。
Mini PCI-E スロット #3 (mSATA 専用スロットかもしれない)
SSD モジュール (SanDisk SSD P4 32GB / mSATA) が組み込まれています。
Mini PCI-E スロット #4
動画再生支援 (Video Decoder) モジュール (Broadcom Crystal HD / AzureWave AW-VD920H ) が組み込まれています。
※画像はクリックすると拡大します。
※分解はあくまでも自己責任で行ってください。
Windows 7
1GB でも結構普通に動作します。決して速くはないですが、遅くもないです。
なお、TW317 シリーズに使われている CPU、Atom N450 は 64bit OS 対応なので、Windows 7 x64 に入れ替える事も可能ですが、殆どメリットはありません。N450 のメモリ上限が 2GB となっているからです。
快適に動作させるための環境整備
メインで使ってる PC がそれなりに非力なので、Windows 7 を快適に動作させるためのノウハウは (残念ながら) 若干持ち合わせています。危険を伴わない程度のライトチューニングですが効果は結構あります。
まず、SSD を劣化させにくくしましょう。SSD は書き込み回数に制限があるので、同じ領域に何度も書き込むべきではありません。
自動デフラグを停める
Windows 7 はスケシュールにより自動でデフラグを行いますので、これを停めます。
[コンピューター] を開き、C: ドライブを右クリックしてプロパティを表示します。
[ツール] タブを選択し、〔最適化する(D )...〕ボタンを押下します。
〔スケジュールの有効化(T )...〕ボタンを押下します。
[スケジュールに従って実行する(推奨)(R )] のチェックを外します。
復元を停める
SSD 容量も限られていますので復元も停めます。
([ファイル名を指定して実行] から "SystemPropertiesProtection.exe" を実行すると 3. へショートカットできます)
[コントロールパネル | システム] または [コンピューター] を右クリックしてプロパティを表示します。
左ペインの [システムの保護] をクリックします。
〔構成(O )...〕ボタンを押下します。
[システムの保護を無効にする] のチェックを外します。
インデックスの作成を停める
無駄にガリガリやっちゃうので、インデックスの作成も停めます。
([ファイル名を指定して実行] から "control.exe /name Microsoft.IndexingOptions" を実行すると 2. へショートカットできます)
[コントロールパネル | インデックスのオプション] を開きます。
〔変更(M )〕ボタンを押下します。
[選択された場所の変更] リスト内のすべてのチェックを外します。
インデックスのオプションダイアログに戻り、〔詳細設定(D )〕ボタンを押下します。
〔再構築(R )〕ボタンを押下します。
次に体感的なパフォーマンスを向上させます。
不要なアプリケーションを削除する
パフォーマンスに影響を及ぼすアプリケーションを削除します。ディスク容量の節約にも寄与します。
[コントロールパネル | プログラムと機能] から以下のアプリケーションを削除します。
([ファイル名を指定して実行] から "control.exe appwiz.cpl" を実行するとショートカットできます)
マカフィー・PCセキュリティセンター 90日期間限定版
i-フィルター 5.0 (90日間お試し版)
Yahoo!ツールバー
体験版なんざ不要です。
視覚効果を変更する
無駄なアニメーションを停めます。
([ファイル名を指定して実行] から "SystemPropertiesPerformance.exe" を実行すると 3. へショートカットできます)
[コントロールパネル | パフォーマンスの情報とツール] を開きます。
[視覚効果の調整] をクリックします。
チェックリストボックス内のチェックを幾つか外します。
オススメの設定は以下のようになります。
"Windows 7 らしさ" を残した設定となっています。意外にチェックが付いているように思われるかもしれませんが、これだけでも充分に体感速度は向上します。
入力パネル (ソフトウェアキーボード) のアイコンを自動的に表示しないようにする
文字入力可能エリアをタップすると "入力パネル (ソフトウェアキーボード)" のアイコンが表示されてウザったい事があります。
一旦、入力パネル (ソフトウェアキーボード)を表示する。
[ツール | オプション]
"入力パネル アイコンとタブを表示する場所の選択:" を以下のように設定する。
こうしておけば、ウザったいアイコンを消すことができ、"入力パネル (ソフトウェアキーボード)" が必要になった時にはタスクバーから呼び出す事ができるようになります。
アップデータの入手
アップデータは ONKYO のサポートサイトから入手できます 。また、兄弟機 である EXOPC のサイトのドライバも利用可能です。
こちらには ONKYO サポートサイト には存在しない、
タッチスクリーンファームウェア (YFO Touchscreen)
3G / GPS ドライバ (Huawei EM770W 3G/GPS) <- TW317A5 には 3G /GPS カードが装着されていないため利用不可。
等もあります。もちろん、ご利用は自己責任でお願いします。なお、現行のものは既にタッチスクリーンファームウェアが 1.006h にアップデートされているようです。
Windows 7 SP1
Windows 7 SP1 を適用する前に、必ず ONKYO のサポートサイト から Atheros の Bluetooth ドライバを DL してきて事前にインストールして下さい 。これをやらずに SP1 を適用すると Bluetooth が利用不能になります 。
Windows 7 SP1 を適用すると、Microsoft のドライバも更新されてしまい、そちらの方が新しいために専用のドライバがインストールできなくなってしまいます。詳しくは ONKYO サポートサイトの Q&A "Windows 7でWindows Update 時にドライバをインストールしない設定を教えてください。" を参照して下さい。
アップデートドライバは、以下の手順で適用します。
アップデータを TW317A5 へ保存します。 Bluetooth は無線 LAN とコンボになっており、Bluetooth のドライバをアンインストールすると無線 LAN のドライバもアンインストールされてしまう ため、アップデータを LAN からコピーする事が不可能となってしまいます。USB フラッシュメモリや SD カードをお持ちであれば話は別ですが。
[コントロールパネル | プログラムと機能] を開きます。
"Atheros WLAN and Bluetooth Client Installation Program" をアンインストールします。
TW317A5 をシャットダウン します。
TW317A5 の電源を入れます。
DL してきたアップデータをインストールします。
デバイスマネージャからではなく、必ずコントロールパネルからアンインストールして下さい。 わざわざシャットダウンして電源を入れているのは、私の環境では再起動だと何度試してもうまくいかなかったからです。
Windows 7 KB2571011
ONKYO のサポート情報 にもありますが、Windows 7 はそのままだとスクリーンキーボードで特定のキーコンビネーションを受け付けません。
これはキーボードレスなタッチパネル搭載機にはキツイ問題なので、KB2571011 の適用をオススメします。なお、この修正プログラムの入手にはメールアドレスが必要 です。
ハードウェア
TW317 シリーズは拡張性が乏しいので、2 つしかない USB ポートは有意義に使うべきでしょう。少なくとも 1 つは USB ポートの空きを作っておくべきかと。
Bluetooth キーボード (1)
手持ちの "リュウド アールボードフォーケイタイ 2100BTJ (Bluetooth HID. JIS配列) RBK-2100BTJ" を接続してみましたが、至って普通に使えるみたいです。
折り畳みの収納式スタンドが付属していますが、本来は PDA 用スタンドですから、とても TW317 シリーズを支えられる代物じゃありません。ペアリングの方法はマニュアルを参照して下さい。
W285×H98.5×D14.5mm (使用時)
W145×H98.5×D19.5mm (収納時)
JANコードは "4939895410211"
単 4 電池 2 本で駆動 (Eneloop 利用可能)
後継機や OEM 品もあります。
Bluetooth キーボード (2)
手持ちの "バトル&ゲット ポケモンタイピングDS" のキーボードを接続してみましたが、こちらも普通に使えるみたいです。
W264×H113×D20mm
キーピッチ: 17mm
JANコードは "4902370518795 (シロ)" / "4902370519143 (クロ)"
単 3 電池 2 本で駆動
付属のスタンドでは TW317 を支えられないと思います。
Bluetooth マウス
これまた、手持ちの "Bluetooth 光学マウス (3 ボタン / オプティカルマウス)" を接続してみましたが、こちらも普通に使えるみたいです。
ペアリングの方法はマニュアルを参照して下さい。
H80×W51×D30mm
EAN (?) コードは "8820051101196"
単 4 電池 2 本で駆動 (Eneloop 利用可能)
USB キーボード & マウス
普通に USB キーボードとマウスを接続してしまうと USB ポートをすべて使い切ってしまうし、コードが邪魔になります。かと言って、ワイヤレスタイプのものでも USB レシーバーが必要なタイプだと結局は USB ポートを消費してしまいます。
USB 接続ならキーボードとタッチパッドが一体化している SANWA SUPPLY SKB-TP01SV が良さそうです。サイズも TW317 シリーズとほぼ同じとなっています。カラーバリエーションがシルバーしかないのが少々残念ですが...。
W291×H197×D29mm
JANコードは "4969887663295"
ケーブル長は 1.5m
重量は 700g
USB キーボードだけでいいのなら、ケースと USB キーボードが一体となった Leather Folio Case With Integrated USB Keyboard が良さげです。但し英語キーボードになります。
メモリ
TW317A5 の場合、2GB に交換する (増設ではなく交換) に越した事はないですが、分解 は難しい部類だと思います (構造は簡単なのですが、ツメを折ったりフィルムケーブルを破損しやすいと思います)。SSD は書き込み回数に上限があるため、Windows を少ないメモリで稼働させるとスワップアウトした分は SSD に書かれてしまい、劣化は当然早くなってしまいますので、最初から TW317A7 / TW317A7PH を選択した方が良いでしょう。
前者は規格に合致したメモリで、後者は上位規格となっています。
当方もメモリを D2N800CQ-2GLZJ に換装していますが、Windows 7 の Windows エクスペリエンスインデックスのメモリのスコアは換装前の 4.5 から 4.6 へと微妙に上がっています。
SD / SDHC / SDXC カード
SSD 容量の少なさを補うためには、SDHC / SDXC カードを使う事になると思います。大容量で安い SDHC / SDXC カード を使うのもいいですが、
高速に読み書きできる SDHC を使うと、ファイルコピー等のストレスが低減できると思います。基本的には Class 10 規格の SDHC カードで充分 (UHS-I には対応していないハズ) だと思われますが、一口に Class 10 と言っても実測するとバラつきがありますので、SDHC カードのベンチマーク結果等を参考に購入される事をオススメします。
有線 LAN
TW317 シリーズには有線 LAN ポートがありませんので、有線 LAN 接続をしたければ USB タイプの有線 LAN アダプタかドッキングステーション を使う事になります。
100 BASE ですが、UE-200TX-G2 はコンパクトでいいですね。
3G & GPS
TW317 シリーズには3G スロットこそありますが、ここに SIM を突っ込んでも何も起こりません。3G を使うには、"3G & GPS Sierra Wireless 3G Card (MC8781)" が別途必要となります (兄弟機 である EXOPC の 3G + WiFi モデルには "Huawei EM770W 3G/GPS" が搭載されているようです)。なお、JustSlate.com で 3G + GPS モジュールを購入する場合には PayPal のアカウントが必要です (日本在住の場合)。
3G / GPS モジュールの取り付け方法や、動作状況については上記 Youtube の動画を参考にして下さい。上記動画は 兄弟機 である EXOPC のものです (3G & GPS Sierra Wireless 3G Card (MC8781) を購入する際は JustSlate.com の FAQ を必ずお読みください)。TW317 シリーズでの動作は未保証ですのでご了承ください。
残念ながら、3G & GPS Sierra Wireless 3G Card (MC8781) は Windows 7 のロケーション API に対応していません。また、MC8781 を購入の際はアンロックされているかどうか を必ず確認するようにして下さい (自前でのアンロックには追加で 10 英ポンド 掛かります)。
3G & GPS Sierra Wireless 3G Card (MC8781) の GPS
3G & GPS Sierra Wireless 3G Card (MC8781) の GPS はスタンドアロンで動作しますが、ファームウェアのバージョンによっては GPS 機能が使えません。私が JustSlate.com から購入した MC8781 のファームウェアは "F1_0_0_4CAP" で、そのままでは GPS 機能が使えませんでした。
DC Unlocker を DLし、ファームウェアのバージョンを確認する。
JustSlate.com の "3G Watcher" ではファームウェアのバージョンを確認できなかったような気がします。加えて GPS ツールも付いていなかった気がします。
F1_2_3_15AP ファームウェアアップデータ (http://www.sierrawireless.com/resources/software/88x/AC881U_F1_2_3_15ap.exe ) を DL し実行する。
"3G Watcher" ではなく "AirCard Watcher" を使う。
"AirCard Watcher" は DL したファイル名が "Watcher_Generic.msi" であれば、大抵どれでも動作します。例えば、2. でファームウェアを流用させてもらった AirCard 881U 用 のものが使えます。
ちなみに、DC Unlocker は MC8781 のアンロックを行う事もできます。但しアンロックは有料かつ PayPal のアカウントが必要です。
JustSlate.com のアンテナでは実用に耐えないかもしれません (GPS を有効にするので精一杯かも)...ですが、感度の良い内蔵できる GPS アンテナとなると入手が困難です。
Amazon.co.jp ウィジェット
ソフトウェア
TW317A5 に必要なアプリケーションをインストールしてみます。ヒトそれぞれ、好みのあるアプリケーションについては言及しません。
Internet Explorer 9
TW317A5 の CPU は Atom N450 で HT 対応とはいえシングルコアなので、描画をハードウェアに投げてしまう Internet Explorer 9 の方が快適に動作するように思います...しかしながら、あくまで CPU 負担の軽減が目的なので HTML5 アニメーションとかの過度な期待は禁物です。
Firefox (4 以降)
IE9 と同じ理屈です。Firefox はメモリ喰いなイメージがありましたが、リソースが限られたマシンではそれなりの動作をするようです。
Adobe Reader X
プリインストールされている Adobe Reader は 9.4.4 なので (TW317A7PH には "Adobe Reader X" がプリインストールされています)。但し、これを入れたらスタートアップから Adobe 関係の自動アップデートアラータを削除しておいた方が良いでしょう。自動アップデートのためだけに常駐アプリが走るというのはナンセンスですから。
Microsoft Office
微妙ですが、ノート PC 代わりに使えない事もないのでインストールしてみてもいいでしょう。もちろん、その際には キーボードとマウスが必須となります。
ただ、最近の MS-Office はサイズが大きいので、Office Personal 2010 (Word / Excel / Outlook) を最小インストールするか、無償の LibreOffice と Microsoft Office ビューア を併用するといいと思います。
アンチウィルスソフト
軽いのは AVG AntiVirus です。個人で使うなら Free Edition もあります。但し、インストール時にリンクスキャナは外しておいた方がいいです。簡単に AVG AntiVirus 2011 Free Edition のインストール方法を紹介します。
この画面では "基本的な保護" にチェックを入れます。こちらが Free Edition で、もう片方は体験版になります。
この画面では "カスタムインストール" にチェックを入れ、"AVG 2011 ガジェット" のチェックを外します。
この画面では "リンクスキャナ" のチェックを外します。
この画面では "AVG セキュリティツールバー" のチェックを外し、"AVG Secure Search を既定のプロバイダにする" のチェックを外します。
後は任意です。
これはアンチウィルスソフトウェア全般に言える事ですが、"%SystemRoot%\SoftwareDistribution\DataStore" 以下のファイルを監視対象外にすると Windows の起動や終了等が早くなる事があります。このフォルダ内にあるのは Windows Update のカタログで、起動時/終了時に Windows から頻繁にアクセスされるのですが、これにアンチウィルスソフトが反応してしまい動作が重くなってしまいます。
フォルダそのものを除外すると、ここにウィルスが入っても感知しなくなるので面倒でも配下のファイルを対象外に設定するようにして下さい。
ExTOUCH
TW317A7 に付属する ExTOUCH (PC Watch の記事 ) は TW317A5 / TW317A7PH に付属していませんが、ExTOUCH の facebook ページ で 「いいね」をクリックした人を対象に、個人向けExTOUCHが無償提供されています。 (配布終了の模様です)
HotKey2WindowsKey
Download: HotKey2WindowsKey_110.zip (2014/04/27)
TW317 シリーズで動作する常駐型アプリケーションです。ホットキー を押すと Windows キーを押した事にします。これを使えば "タスクバーを隠す" 設定が可能になるため、画面を幾らか広く使えます。
Windows 8 搭載スレート PC にはスマホの Home ボタンに相当するボタン (Windows ボタン) が大抵付いていますが、Windows 7 世代のスレート PC にはこれがないものが多いです。TW317 シリーズにも Home ボタンに相当するボタンがないのでこれをホットキーで代用するためのアプリケーションです。
スレート PC プログラミング
お仕着せのアプリケーションばっかり触っても楽しくないので自分でプログラムを組んでみる事にします。TW317 シリーズ...ネットブックにも言える事ですが、非力な PC ではネイティブアプリケーションの出番です。このトピックでは Delphi を使ったスレート PC アプリケーション開発の Tips を掲載していきます。
タッチアプリケーションの概要については
これらを事前に読んでおく事をオススメします。なお、使用する Delphi は Delphi XE を想定していますが、基本的には Delphi 2010 でも動作すると思います。
マウスジェスチャ対応画像ビューア
まずは肩慣らしに "マウスジェスチャ対応画像ビューア" を作ってみましょう。Delphi だと 10 分もあれば作れます (以下をコピペなら 5 分も掛らないハズです)。
とりあえず仕様を。
EXE のあるフォルダにある pic フォルダ内の画像ファイルを閲覧する。
ジェスチャで次の "次の画像 / 前の画像" を閲覧できる。
ボタンクリックとカーソルキー左右でも画像を送れる。
とりあえず画像は Jpeg のみとする
操作性や機能の向上は各自やって下さいね。では行ってみましょう。
Delphi で新規プロジェクトを作成します。
フォームに ActionManager を貼ります (ActionManager1)
フォームに GestureManager を貼ります (GestureManager1)
フォームに Button を貼ります (Button1)
フォームに Button を貼ります (Button2)
フォームに Panel を貼ります (Panel1)
Panel1 に Image を貼ります
ここまでの作業でこんな感じになると思います。
では次に進みましょう。
オブジェクトインスペクタでフォームのプロパティを変更します。
ActiveControl を Panel1 に (任意)
Caption を適当に変更 (任意)
DoubleBuffered を True に (任意)
Position を poScreenCenter に (任意)
Scaled を False に (任意)
※(任意) の所は変更しなくても支障はありません。
フィールド (クラス内プライベート変数) を追加します。
private
{ Private 宣言 }
FIndex: Integer;
FPicList: TStringList;
...
FIndex はページのインデックスを指すための変数で、FPicList は画像のフルパス名を保持するリストです。
上記変数の初期化処理/終了時処理を記述します。
procedure TForm1.FormCreate(Sender: TObject);
begin
FIndex := 1 ;
FPicList := TStringList.Create;
end ;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FPicList.Free;
end ;
FIndex の初期値が 1 になっている理由は後で解ります。
フォーム上の ActionManager をダブルクリックし、設定ダイアログを開きます。
"アクションの新規作成" でアクションを追加します。
Caption を "<<" に (任意)
Name を "Action_Left" に変更
ShortCut を "Left" に変更
"アクションの新規作成" でアクションを追加します。
Caption を ">>" に (任意)
Name を "Action_Right" に変更
ShortCut を "Right" に変更
設定ダイアログの "アクション" にある "<<" をダブルクリックしてイベントを記述します。
procedure TForm1.Action_LeftExecute(Sender: TObject);
begin
if FIndex > 0 then
begin
Dec(FIndex);
Image1.Picture.LoadFromFile(FPicList[FIndex]);
end ;
Panel1.SetFocus;
end ;
ページを戻る処理になります。
同様に、">>" をダブルクリックしてイベントを記述します。
procedure TForm1.Action_RightExecute(Sender: TObject);
begin
if FIndex < FPicList.Count-1 then
begin
Inc(FIndex);
Image1.Picture.LoadFromFile(FPicList[FIndex]);
end ;
Panel1.SetFocus;
end ;
ページを進む処理になります。
フォームの OnShow イベントを記述します。
procedure TForm1.FormShow(Sender: TObject);
var
FilePath: string ;
FileName: string ;
begin
OnShow := nil ;
FilePath := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0 ))) + 'pic' ;
if not DirectoryExists(FilePath) then
ForceDirectories(FilePath);
for FileName in TDirectory.GetFiles(FilePath, '*.jpg' , TSearchOption.soAllDirectories) do
FPicList.Add(FileName);
if FPicList.Count > 0 then
Action_LeftExecute(nil );
end ;
ファイル一覧を取得してリストに突っ込んでいます。
uses に IOUtils と Jpeg を追記します。
TDirectory が使えるようにするのと、TImage で Jpeg を使えるようにするためです。
Button1 のプロパティを変更します。
Action を "Action_Left" に変更
Align を "alLeft" に変更
Button2 のプロパティを変更します。
Action を "Action_Right" に変更
Align を "alRight" に変更
Panel1 のプロパティを変更します。
Align を "alClient" に変更
Caption を 空白に変更
Color を "clWhite" に (任意)
Image1 のプロパティを変更します。
Align を "alClient" に変更
Center を True に変更
Proportional を True に変更
Stretch を True に変更
上記設定で、"画像をフォームサイズに合わせてアスペクト比を保持しつつ拡大縮小し中央に置く" 事が可能になります。
ここまでの作業でこんな感じになると思います。
Image1 の Touch.GestureManager に GestureManager1 を指定します
Touch.Gestures.Standard にある基本ジェスチャにアクションを割り当てます
Touch.Gestures.Standard.Left にチェックを入れ、Action_Left を割り当てます
Touch.Gestures.Standard.Right にチェックを入れ、Action_Right を割り当てます
Touch の所は最終的にこうなります。
"マウスでクリックしたまま左へ移動しマウスを離す操作" が基本ジェスチャの "Left" です。逆が "Right" になります。
これにて完成です。一度実行してそのまま終了すると、EXE のあるフォルダの下に pic フォルダができますので、そこへ任意の Jpeg 画像を突っ込んでみて下さい。
このように、画像が変わってもアスペクト比を保ったまま可能な限り拡大して中央に画像が表示されます。左右のボタンまたはカーソルキーの左右、またはジェスチャで画像を進めたり戻したりできます。要は各種イベントが ActionManager で管理されているのなら GestureManager を貼って、適当なジェスチャを選び、そこへアクションを設定してやるだけでジェスチャーアプリケーションが実現できるという事です。
Jpeg 以外の画像ファイルを読み込む方法については、
に詳細がありますので、そちらを参照して下さい。
リモートデバッガをインストールする
TW317 シリーズに Delphi をインストールするのは容量や開発効率の面でオススメしません...とはいえ、マルチタッチやセンサー等、スレート PC 固有のデバッグを行う必要はあります。
そこでリモートデバッガの出番となります。Delphi のリモートデバッガのインストール方法については、
に詳細がありますので、そちらを参照して下さい。
タッチイベントを取得する
タッチイベントを取得できなければ何も始まりません。まずはタッチイベントを取得する所から始めてみます。
タップ & ダブルタップ
クリック / ダブルクリックと同じですから OnClick / OnDblClick イベントで取得できます。
procedure TForm1.FormClick(Sender: TObject);
begin
ShowMessage('タップ' );
end ;
procedure TForm1.FormDblClick(Sender: TObject);
begin
ShowMessage('ダブルタップ' );
end ;
何も難しい事はありません。ただ、OnMouseUp / OnMouseDown 等のイベントが発生しない事に注意が必要です。イベントがタッチによるものかとマウスによるものなのかを判別するには、GetMessageExtraInfo() を利用します。
function IsTouchMessage: Boolean;
const
MI_WP_SIGNATURE = $FF515700 ;
SIGNATURE_MASK = $FFFFFF00 ;
begin
result := ((GetMessageExtraInfo and SIGNATURE_MASK) = MI_WP_SIGNATURE);
end ;
このような関数を作っておくと便利です。
選択 / ドラッグ
OnMouseDown / OnMouseMove/ OnMouseUp イベントで実装します。"左右" と限定されているのは、上下は "パン" だからです。
プレス & ホールド
Touch.TabletOptions に toPressAndHold を含めて、右クリックを判定すればいいです。
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if (Button = mbRight) then
ShowMessage('プレス&ホールド' );
end ;
簡単ですね。
ズーム
回転と誤判定されやすいです。できるだけ回転との併用は控えましょう。2 本の指を同じ移動量で同時に動かすとズームと判定されやすいです。
Touch.InteractiveGestures に igZoom を含めると OnGesture イベントに飛んできますので、そこで判定します。
procedure TForm1.FormGesture(Sender: TObject;
const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
case EventInfo.GestureID of
igiZoom:
Memo1.Lines.Add('ズーム' );
else
Memo1.Lines.Add('その他' );
end ;
Handled := True;
end ;
OnGesture イベントの EventInfo パラメータの Distance に 2 点間の距離が格納されます。
対話型ジェスチャなので、両方の指を離さない限りイベントは継続します。
パン
パンには慣性を与える事ができます。この慣性の事を イナーシア (Inertia) と呼びます (そのまま "慣性" でいいような気もしますが...)。縦スクロールバーがあるとタップしたつもりでもパンと判定されてしまいます。タップとの併用は控えましょう。
Touch.InteractiveGestures に igPan を含めると OnGesture イベントに飛んできますので、そこで判定します。
procedure TForm1.FormGesture(Sender: TObject;
const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
case EventInfo.GestureID of
igiPan:
Memo1.Lines.Add('パン' );
else
Memo1.Lines.Add('その他' );
end ;
Handled := True;
end ;
OnGesture イベントの EventInfo パラメータの InertiaVector にイナーシア情報が格納されます。
対話型ジェスチャなので、両方の指を離さない限りイベントは継続します。
回転
ズームと誤判定されやすいです。できるだけズームとの併用は控えましょう。片方の指を動かさないと回転と判定されやすいです。
Touch.InteractiveGestures に igRotate を含めると OnGesture イベントに飛んできますので、そこで判定します。
procedure TForm1.FormGesture(Sender: TObject;
const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
case EventInfo.GestureID of
igiRotate:
Memo1.Lines.Add('回転' );
else
Memo1.Lines.Add('その他' );
end ;
Handled := True;
end ;
OnGesture イベントの EventInfo パラメータの Angle に初期位置からの回転角度 (±360°) がラジアン角で格納されます。ちなみに、ラジアン角から角度への変換は Math.RadToDeg() 、その逆には Math.DegToRad() を使います。
対話型ジェスチャなので、両方の指を離さない限りイベントは継続します。
二本指タップ
ズームや回転と誤判定されやすいです。指の間隔をやや広めにして同時にタップすると二本指タップと判定されやすいようです。
Touch.InteractiveGestures に igTwoFingerTap を含めると OnGesture イベントに飛んできますので、そこで判定します。
procedure TForm1.FormGesture(Sender: TObject;
const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
case EventInfo.GestureID of
igiTwoFingerTap:
Memo1.Lines.Add('二本指タップ' );
else
Memo1.Lines.Add('その他' );
end ;
Handled := True;
end ;
対話型ジェスチャですが、両方の指を離して確定するジェスチャなので開始と終了が同時となります。
プレス & タップ
Touch.InteractiveGestures に igPressAndTap を含めると OnGesture イベントに飛んできますので、そこで判定します。
procedure TForm1.FormGesture(Sender: TObject;
const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
case EventInfo.GestureID of
igiPressAndTap:
Memo1.Lines.Add('プレス & タップ' );
else
Memo1.Lines.Add('その他' );
end ;
Handled := True;
end ;
OnGesture イベントの EventInfo パラメータの TapLocation にタップ位置が格納されます (gfBegin 時以外は Location = TapLocation)。
対話型ジェスチャなので、両方の指を離さない限りイベントは継続します。連続のプレス & タップ (片方の指をタッチしたままの連続タップ) は検出できないので、
procedure TForm1.FormGesture(Sender: TObject;
const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
case EventInfo.GestureID of
igiPressAndTap:
begin
if (gfbegin in EventInfo.Flags) then
Memo1.Lines.Add('プレス & タップ' );
end ;
else
Memo1.Lines.Add('その他' );
end ;
Handled := True;
end ;
こちらのコードの方が良いでしょう。
フリック
ナビゲーションフリックは 8 方向に任意の機能を割り振る事ができます ([コントロールパネル | ペンとタッチ] の "フリック" タブで変更可能)。
アプリケーションで独自にフリックを検出するには Touch.TabletOptions に toFlicks を含めて、WM_TABLET_FLICK を処理します。
type
TForm1 = class (TForm)
...
private
{ Private 宣言 }
procedure TabletFlick(var msg: TMessage); message WM_TABLET_FLICK;
public
{ Public 宣言 }
end ;
procedure TForm1.TabletFlick(var msg: TMessage);
begin
ShowMessage('フリック' );
end ;
位置と加速度を伴わないフリックであれば、GestureManager の標準ジェスチャを使うのが簡単でしょう。話が前後しますが、[コントロールパネル | ペンとタッチ] で割り振られた機能のうち、進む / 戻る等については WM_APPCOMMAND を処理する事で検出可能です。詳細については、
を参照して下さい。
Windows 8 以降だと、マルチタッチディスプレイを使用していても WM_TABLET_FLICK メッセージは飛んでこない ようです。
マルチタッチ関連のイベントとプロパティ
対話型ジェスチャと OnGesture イベントの EventInfo パラメータは以下のような関係になっています。
ズーム
igiZoom
gfBegin = 開始 gfInertia = 慣性 gfEnd = 終了
○
○
○
-
-
-
-
-
パン
igiPan
-
-
-
-
○
○
回転
igiPan
-
-
-
○
-
-
二本指タップ
igiTwoFingerTap
-
-
-
-
-
-
プレス & タップ
igiPressAndTap
-
○
○
-
-
-
EventInfo.Distance と EventInfo.TapLocation は共用体になっている事に注意して下さい。
マルチタッチに関連するプロパティは以下の通りです。
Touch.InteractiveGestures
以下の値を設定する事ができます。
igZoom "ズーム" ジェスチャを受け付けます。2 本指での使用が必要です。
igPan "パン" ジェスチャを受け付けます。スクロールに使用されます。1 本指で使用できます。
igRotate "回転" ジェスチャを受け付けます。UI 要素を回転するのに使用されます。指が 2 本必要です。
igTwoFingerTap "二本指タップ" ジェスチャを受け付けます。指が 2 本必要です。
igPressAndTap "プレス & タップ" ジェスチャを受け付けます。押したままにする動作とタップにそれぞれ 1 本ずつの、2 本の指が必要です。
指定された対話型ジェスチャは OnGesture イベントで捕捉できます。
Touch.InteractiveGestureOptions
以下の値を設定する事ができます。
igoPanSingleFingerHorizontal 指を水平に移動したときに、"パン" ジェスチャが生成されます
igoPanSingleFingerVertical 指を垂直に移動したときに、"パン" ジェスチャが生成されます。
igoPanInertia 慣性に対しても "パン" ジェスチャが生成されます。画面から指を離しても、"パン" イベントは継続されますが、慣性効果をシミュレートしながら縮小します。
igoPanGutter "パン" ジェスチャをマージンで有効にします。X 軸を 0 にし、Y 軸のみの変化を返します。
igoParentPassthrough 未処理の対話型ジェスチャが親コントロールに渡されます。
主に、OnGesture イベントの EventInfo パラメータの値に影響を及ぼす設定となります。
Touch.TabletOptions
以下の値を設定する事ができます。
toPressAndHold "プレス & ホールド" ジェスチャを受け付けます。右クリックと同等です。
toPenTapFeedback タップされた時のアニメーション効果を有効にします。
toPenBarrelFeedback"ペンボタン (バレルボタン)" が押下された時のアニメーション効果を有効にします。
toTouchUIForceOff スクリーン上のアニメーション効果を無効にします。
toTouchUIForceOn スクリーン上のアニメーション効果を有効にします。
toTouchSwitch ???
toFlicks "フリック" ジェスチャを受け付けます。WM_TABLET_FLICK メッセージを捕捉して処理する事ができます (詳細は後述)。
toSmoothScrolling スムーススクロールを有効にします。
toFlickFallbackKeys ???
主に追加機能に関する設定となります。
Touch.ParentTabletOptions
True を設定すると、TabletOptions で指定されたオプションを親コントロールにリダイレクトします。
理屈上では確かにこのようになっていますが、実際にやってみるとうまくいかない事が多いのが悩みどころです。
フリックの挙動
[コントロールパネル | ペンとタッチ] の "フリック" でフリックを有効にしていないと WM_TABLET_FLICK が飛んでこないようです。ですが、これを有効にしたままだとシステム既定のフリックのアイコン が出てしまいますよ...?
先に述べたようにアプリケーションで独自にフリックを検出するには WM_TABLET_FLICK を処理すればいいのですが、フリックの方向や位置を調べるには wParam に FLICK_DATA 、lParam にFLICK_POINT が入ってきますので、これを調べます...が、構造体の定義で嫌な予感がするでしょうが、これは 32bit のビットフィールドになっています。
TFLICK_DATA
TFLICK_POINT
31
ActionArgument
SmallInt
30
29
28
27
26
25
24
23
22
21
20
19
18
17
16
15
OnInkingSurface
Boolean
14
Reserved
SmallInt
13
12
ShiftModifier
Boolean
11
WinModifier
Boolean
10
AltGRModifier
Boolean
9
MenuModifier
Boolean
8
ControlModifier
Boolean
7
FlickDirection
Byte
6
5
4
FlickActionCommandCode
Byte
3
2
1
0
31
Y
SmallInt
30
29
28
27
26
25
24
23
22
21
20
19
18
17
16
15
X
SmallInt
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
詳しくは述べませんが、Delphi はビットフィールドの扱いが苦手です。ビットフィールドなら...
type
{ TFLICK_DATA }
TFLICK_DATA =
record
FlickActionCommandCode: 0 ..31 ; // 5bit
FlickDirection: 0 ..7 ; // 3bit
ControlModifier: 0 ..1 ; // 1bit
MenuModifier: 0 ..1 ; // 1bit
AltGRModifier: 0 ..1 ; // 1bit
WinModifier: 0 ..1 ; // 1bit
ShiftModifier: 0 ..1 ; // 1bit
Reserved: 0 ..2 ; // 2bit
OnInkingSurface: 0 ..1 ; // 1bit
ActionArgument: -32768 ..32767 ; // 16bit
end ;
{ PFLICK_DATA }
PFLICK_DATA = ^TFLICK_DATA;
これでいいような気がしますが、SizeOf(TFLICK_DATA) をやってみて下さい。4 を返さないと思います。packed や {$A-} コンパイラ指令等の指定も無意味です。
wParam / lParam を DWORD でそのまま受けると後々面倒なので、各構造体には以下のユニットで定義されているレコードを使うといいでしょう。
unit TabFlicks;
interface
uses
Windows, Types;
const
FLICK_WM_HANDLED_MASK = 1 ;
FLICKACTION_COMMANDCODE_NULL = 0 ;
FLICKACTION_COMMANDCODE_SCROLL = 1 ;
FLICKACTION_COMMANDCODE_APPCOMMAND = 2 ;
FLICKACTION_COMMANDCODE_CUSTOMKEY = 3 ;
FLICKACTION_COMMANDCODE_KEYMODIFIER = 4 ;
FLICKDIRECTION_RIGHT = 0 ;
FLICKDIRECTION_UPRIGHT = 1 ;
FLICKDIRECTION_UP = 2 ;
FLICKDIRECTION_UPLEFT = 3 ;
FLICKDIRECTION_LEFT = 4 ;
FLICKDIRECTION_DOWNLEFT = 5 ;
FLICKDIRECTION_DOWN = 6 ;
FLICKDIRECTION_DOWNRIGHT = 7 ;
FLICKDIRECTION_INVALID = 8 ;
type
{ _FLICK_DATA }
_FLICK_DATA =
record
strict private
FBitField: DWORD;
function GetFlickActionCommandCode: Byte;
function GetFlickDirection: Byte;
function GetControlModifier: Boolean;
function GetMenuModifier: Boolean;
function GetAltGRModifier: Boolean;
function GetWinModifier: Boolean;
function GetShiftModifier: Boolean;
function GetReserved: SmallInt;
function GetOnInkingSurface: Boolean;
function GetActionArgument: SmallInt;
function GetValue: DWORD;
procedure SetFlickActionCommandCode(const Value: Byte);
procedure SetFlickDirection(const Value: Byte);
procedure SetControlModifier(const Value: Boolean);
procedure SetMenuModifier(const Value: Boolean);
procedure SetAltGRModifier(const Value: Boolean);
procedure SetWinModifier(const Value: Boolean);
procedure SetShiftModifier(const Value: Boolean);
procedure SetReserved(const Value: SmallInt);
procedure SetOnInkingSurface(const Value: Boolean);
procedure SetActionArgument(const Value: SmallInt);
procedure SetValue(const V: DWORD);
public
constructor Create(const InitValue: DWORD);
property FlickActionCommandCode: Byte read GetFlickActionCommandCode write SetFlickActionCommandCode;
property FlickDirection: Byte read GetFlickDirection write SetFlickDirection;
property ControlModifier: Boolean read GetControlModifier write SetControlModifier;
property MenuModifier: Boolean read GetMenuModifier write SetMenuModifier;
property AltGRModifier: Boolean read GetAltGRModifier write SetAltGRModifier;
property WinModifier: Boolean read GetWinModifier write SetWinModifier;
property ShiftModifier: Boolean read GetShiftModifier write SetShiftModifier;
property Reserved: SmallInt read GetReserved write SetReserved;
property OnInkingSurface: Boolean read GetOnInkingSurface write SetOnInkingSurface;
property ActionArgument: SmallInt read GetActionArgument write SetActionArgument;
property Value: DWORD read GetValue write SetValue;
end ;
{ TFLICK_DATA }
TFLICK_DATA = _FLICK_DATA;
{ PFLICK_DATA }
PFLICK_DATA = ^TFLICK_DATA;
{ _FLICK_DATA }
_FLICK_POINT =
record
strict private
FBitField: DWORD;
function GetX: SmallInt;
function GetY: SmallInt;
function GetValue: DWORD;
procedure SetX(const Value: SmallInt);
procedure SetY(const Value: SmallInt);
procedure SetValue(const V: DWORD);
public
constructor Create(const InitValue: DWORD);
property X: SmallInt read GetX write SetX;
property Y: SmallInt read GetY write SetY;
property Value: DWORD read GetValue write SetValue;
end ;
{ TFLICK_POINT }
TFLICK_POINT = _FLICK_POINT;
{ PFLICK_POINT }
PFLICK_POINT = ^TFLICK_POINT;
implementation
{ _FLICK_DATA }
constructor _FLICK_DATA.Create(const InitValue: DWORD);
begin
ZeroMemory(@Self, SizeOf(Self));
FBitField := InitValue;
end ;
function _FLICK_DATA.GetActionArgument: SmallInt;
begin
result := (FBitField and $FFFF0000 ) shr 16 ; // bit16 to bit31
end ;
function _FLICK_DATA.GetAltGRModifier: Boolean;
begin
result := ((FBitField and $00000400 ) = $00000400 ); // bit10
end ;
function _FLICK_DATA.GetControlModifier: Boolean;
begin
result := ((FBitField and $00000100 ) = $00000100 ); // bit8
end ;
function _FLICK_DATA.GetFlickActionCommandCode: Byte;
begin
result := (FBitField and $0000001F ); // bit0 to bit4
end ;
function _FLICK_DATA.GetFlickDirection: Byte;
begin
result := (FBitField and $000000E0 ) shr 5 ; // bit5 to bit7
end ;
function _FLICK_DATA.GetMenuModifier: Boolean;
begin
result := ((FBitField and $00000200 ) = $00000200 ); // bit9
end ;
function _FLICK_DATA.GetOnInkingSurface: Boolean;
begin
result := ((FBitField and $00008000 ) = $00008000 ); // bit15
end ;
function _FLICK_DATA.GetReserved: SmallInt;
begin
result := (FBitField and $00006000 ) shr 13 ; // bit13 to bit14
end ;
function _FLICK_DATA.GetShiftModifier: Boolean;
begin
result := ((FBitField and $00001000 ) = $00001000 ); // bit12
end ;
function _FLICK_DATA.GetValue: DWORD;
begin
result := FBitField;
end ;
function _FLICK_DATA.GetWinModifier: Boolean;
begin
result := ((FBitField and $00000800 ) = $00000800 ); // bit11
end ;
procedure _FLICK_DATA.SetActionArgument(const Value: SmallInt);
begin
// bit16 to bit31
FBitField := FBitField and (not $FFFF0000 );
FBitField := FBitField or (DWORD(Value) shl 16 );
end ;
procedure _FLICK_DATA.SetAltGRModifier(const Value: Boolean);
begin
// bit10
if Value then
FBitField := FBitField or $00000400
else
FBitField := FBitField and (not $00000400 );
end ;
procedure _FLICK_DATA.SetControlModifier(const Value: Boolean);
begin
// bit8
if Value then
FBitField := FBitField or $00000100
else
FBitField := FBitField and (not $00000100 );
end ;
procedure _FLICK_DATA.SetFlickActionCommandCode(const Value: Byte);
begin
// bit0 to bit4
FBitField := FBitField and (not $0000001F );
FBitField := FBitField or DWORD(Value);
end ;
procedure _FLICK_DATA.SetFlickDirection(const Value: Byte);
begin
// bit5 to bit7
FBitField := FBitField and (not $000000E0 );
FBitField := FBitField or (DWORD(Value) shl 5 );
end ;
procedure _FLICK_DATA.SetMenuModifier(const Value: Boolean);
begin
// bit9
if Value then
FBitField := FBitField or $00000200
else
FBitField := FBitField and (not $00000200 );
end ;
procedure _FLICK_DATA.SetOnInkingSurface(const Value: Boolean);
begin
// bit15
if Value then
FBitField := FBitField or $00008000
else
FBitField := FBitField and (not $00008000 );
end ;
procedure _FLICK_DATA.SetReserved(const Value: SmallInt);
begin
// bit13 to bit14
FBitField := FBitField and (not $00006000 );
FBitField := FBitField or (DWORD(Value) shl 13 );
end ;
procedure _FLICK_DATA.SetShiftModifier(const Value: Boolean);
begin
// bit12
if Value then
FBitField := FBitField or $00001000
else
FBitField := FBitField and (not $00001000 );
end ;
procedure _FLICK_DATA.SetValue(const V: DWORD);
begin
FBitField := V;
end ;
procedure _FLICK_DATA.SetWinModifier(const Value: Boolean);
begin
// bit11
if Value then
FBitField := FBitField or $00000800
else
FBitField := FBitField and (not $00000800 );
end ;
{ _FLICK_POINT }
constructor _FLICK_POINT.Create(const InitValue: DWORD);
begin
ZeroMemory(@Self, SizeOf(Self));
FBitField := InitValue;
end ;
function _FLICK_POINT.GetValue: DWORD;
begin
result := FBitField;
end ;
function _FLICK_POINT.GetX: SmallInt;
begin
result := (FBitField and $0000FFFF ); // bit0 to bit15
end ;
function _FLICK_POINT.GetY: SmallInt;
begin
result := (FBitField and $FFFF0000 ) shr 16 ; // bit16 to bit31
end ;
procedure _FLICK_POINT.SetValue(const V: DWORD);
begin
FBitField := V;
end ;
procedure _FLICK_POINT.SetX(const Value: SmallInt);
begin
// bit0 to bit15
FBitField := FBitField and (not $0000FFFF );
FBitField := FBitField or DWORD(Value);
end ;
procedure _FLICK_POINT.SetY(const Value: SmallInt);
begin
// bit16 to bit31
FBitField := FBitField and (not $FFFF0000 );
FBitField := FBitField or (DWORD(Value) shl 16 );
end ;
end .
もっと簡単な方法をご存じの方がいらっしゃいましたら、ご教示下さい m(_ _)m
使い方はこのようになります。
uses
..., TabFlicks;
type
TForm1 = class (TForm)
...
private
{ Private 宣言 }
procedure TabletFlick(var msg: TMessage); message WM_TABLET_FLICK;
public
{ Public 宣言 }
end ;
...
procedure TForm1.TabletFlick(var msg: TMessage);
var
s: String ;
FLICK_DATA: TFLICK_DATA;
FLICK_POINT: TFLICK_POINT;
begin
s := 'フリック' ;
// フリックの方向を取得
FLICK_DATA := TFLICK_DATA.Create(msg.WParam);
case FLICK_DATA.FlickDirection of
FLICKDIRECTION_RIGHT:
s := s + '(右)' ;
FLICKDIRECTION_UPRIGHT:
s := s + '(右上)' ;
FLICKDIRECTION_UP:
s := s + '(上)' ;
FLICKDIRECTION_UPLEFT:
s := s + '(左上)' ;
FLICKDIRECTION_LEFT:
s := s + '(左)' ;
FLICKDIRECTION_DOWNLEFT:
s := s + '(左下)' ;
FLICKDIRECTION_DOWN:
s := s + '(下)' ;
FLICKDIRECTION_DOWNRIGHT:
s := s + '(右下)' ;
FLICKDIRECTION_INVALID:
s := s + '(不正)' ;
end ;
// フリックの位置を取得
FLICK_POINT := TFLICK_POINT.Create(msg.lParam);
s := s + Format(' X=%d Y=%d' , [FLICK_POINT.X, FLICK_POINT.Y]);
Memo1.Lines.Add(s);
// システムデフォルトのフリックをキャンセルする
msg.Result := FLICK_WM_HANDLED_MASK;
end ;
「FlickDirection は 3bit しかないんだから、FLICKDIRECTION_INVALID が発生する事はないよね? 」 とかいうツッコミは Microsoft さんにお願いします。
戻り値として FLICK_WM_HANDLED_MASK を指定しないと、システムデフォルトのフリックが実行されてしまいます。どうやってもアイコン の件が解決しないような気がしますが...アプリケーションで独自のフリックをやりたければ "パン" とイナーシアでやれって事なのでしょうか?
Flicks Gestures (MSDN) に、
The flick feedback consists of two parts; an icon representing the action and a label containing the name of the action. The label is displayed below the icon. The feedback is displayed immediately after the flick is detected. Although applications can customize their behavior in response to flicks by handling the flick window message, the application cannot disable or modify the flick feedback.
とありますね...うーん。"アプリで制御はできるけど、フリックフィードバックは無効にできないよ" 何故、このようなアホな仕様になっているのでしょうか?言い訳としては "フリックが成功したかどうかわかんないじゃん?" という事のようですが、それを言うのならジェスチャだって成功したかどうか判らないと思うのですが?フリックフィードバックを消す事が出来たら何の不都合があると言うのでしょう?
WM_APPCOMMAND に頼らずに自力でフリックを制御したい理由ですが、フリックの左右に割り当てられている "進む/戻る" が Vista / 7 で逆になっているからです。Vista では右方向へのフリックが "進む" なのですが、7 では左方向へのフリックが "進む" になっています。Internet Explorer / Media Player 等の "進む" 方向は右ですよね?パンの操作に合わせて変更されたらしい のですが、これではキーボード等と併用する際に紛らわしくて仕方ありません。ナビゲーションフリックの設定をユーザが変更する可能性もあるため、フリックはアプリケーションで制御したいのです。
UI の最小サイズを試算する
タッチ操作の場合、あまりにも細かい UI は操作できません。ユーザーインターフェイスのガイドラインは タッチ (MSDN) にありますが、ちょっと待って下さい。この情報は本当に正しいのでしょうか?
ここに書かれているのはディスプレイが本当に 96dpi であると仮定した場合の事です...今時、ちゃんと 96dpi なディスプレイなんて存在しないのですし。嘘だとお思いなら、以下の表を元にお使いのディスプレイのサイズを測ってみて下さい。
800×600
21.17cm
15.87cm
1024×768
27.09cm
20.32cm
1280×600
33.86cm
15.87cm
1280×768
33.86cm
20.32cm
1280×800
33.86cm
21.17cm
1280×1024
33.86cm
27.09cm
1366×768
36.14cm
20.32cm
1400×1050
37.04cm
27.78cm
1600×1024
42.33cm
27.09cm
1600×1200
42.33cm
31.75cm
1920×1080
50.80cm
28.57cm
本来なら液晶パネルのサイズは上記の表の通りでなくてはなりません。TW317 シリーズの実測サイズは以下のようになっています (キリのいい数字になるように端数を調整しています)。
TW317 シリーズは HD 液晶なので、リアル 96dpi だと横幅は 361.4mm になってしまいます。ドットピッチが 0.1875mm なので、実際の dpi は "25.4 (1 inch) / 0.1875 = 135.46666..." となります。これを 96 で割ればリアル 96dpi との比率が出ます。1.411111... ですね。
procedure TForm1.Button1Click(Sender: TObject);
var
Value, Pixels, mmSize: Currency;
s: string ;
begin
s := '' ;
Pixels := StrToCurr(Edit1.Text); // 横ピクセル
mmSize := StrToCurr(Edit2.Text); // 実測した液晶パネルの横幅 (mm)
// 実 DPI を求める
Value := 25.4 / (mmSize / Pixels);
s := s + '実 DPI: ' + FormatCurr('#,##0.00' , Value) + #$0D #$0A ;
// 実 96dpi との比率を求める
Value := Value / 96 ;
s := s + '比率: ' + FormatCurr('#,##0.00' , Value);
ShowMessage(s);
end ;
実 DPI と実 96dpi との比率を表示するソースコードを掲載しておきます。Edit1 に横解像度 (ピクセル数)、Edit2 に液晶パネルの実測横幅 (mm) を入力してボタンを押してください。
以下は実測せずにカタログスペックから実 DPI と実 96dpi との比率を表示するソースコードです。
uses
..., Math;
procedure TForm1.Button2Click(Sender: TObject);
var
Value, Pixels, dWidth, dHeight, dInch, dDegree: Currency;
s: string ;
begin
s := '' ;
Pixels := StrToCurr(Edit1.Text); // 横ピクセル
dWidth := Pixels;
dHeight := StrToCurr(Edit2.Text); // 縦ピクセル
dInch := StrToCurr(Edit3.Text); // カタログでの液晶サイズ (Inch) = 斜辺
// 角度を求める
dDegree := ArcTan(dWidth / dHeight);
// 横幅を求める (Inch)
dWidth := dInch * Sin(dDegree);
// 縦幅を求める (Inch) - Unused
dHeight := dInch * Cos(dDegree);
// 実 DPI を求める
Value := Pixels / dWidth;
s := s + '実 DPI: ' + FormatCurr('#,##0.00' , Value) + #$0D #$0A ;
// 実 96dpi との比率を求める
Value := Value / 96 ;
s := s + '比率: ' + FormatCurr('#,##0.00' , Value);
ShowMessage(s);
end ;
Edit1 に横解像度 (ピクセル数)、Edit2 に縦解像度 (ピクセル数)、Edit3 に液晶パネルのインチ数 (inch) を入力してボタンを押してください。カタログでの液晶パネルのインチ数は結構あいまいなので (11.6インチを 12インチと表記したりするので) 上の実測バージョンより精度は劣りますが、大体の目安にはなるでしょう。なお、TW317 シリーズのパネルのインチサイズは実測で 11.57 です。
話を元に戻します。Microsoft のガイドライン でピクセル表記になっているものは、
コントロールの最小サイズは 23 × 23 ピクセル (13 × 13 DLU)、
最もよく使用されるコントロールは 40 × 40 ピクセル (23 × 22 DLU) 以上です。
TW317 シリーズの場合、1.4倍で読み替える必要がある という事になります。
コントロールの最小サイズは 32 × 32 ピクセル 、
最もよく使用されるコントロールは 56 × 56 ピクセル 以上です。
まぁ、そうなると TW317 シリーズでは殆どのコモンコントロールはデフォルトサイズだとアウト な訳ですが。なお、DLU については "レイアウト メトリック (MSDN)" を参照して下さい。Twips ...思えば、そんなのもありましたねぇ (´ー`)y-~~
iPhone や iPad の強みの一つはハードウェアが固定されている事です。このスレート PC や Andoroid 機は各メーカーが異なる仕様のものを発売しているため、描画一つにしたって決め打ちする事ができません。ラスター型の画像は拡大縮小すれば汚くなりますし、すべてをベクター画像でやろうとすると SVG や WMF / EMF を使うしかありません。画面サイズと解像度の問題を解決するには、異なる解像度用にリソース DLL を用意して対処するしかないのかもしれませんね。
マルチタッチが可能かどうかを調べる
操作性が全くと言っていいほど異なるため、個人的にはマルチタッチ対応アプリケーションと普通のデスクトップ用アプリケーションとは分けて作った方がいいと思いますが、代替手段で両対応させなければならない事もあるでしょう。このような場合には、マルチタッチ機能を有しているかどうかを判定する必要があります。
マルチタッチが可能かどうかは GetSystemMetrics(SM_DIGITIZER) で問い合わせる事ができます。
CanMultiTouch := (GetSystemMetrics(SM_DIGITIZER) and NID_MULTI_INPUT) > 0 ;
NID_MULTI_INPUT (0x40) が立っていればマルチタッチ可能です。
CanMultiTouch := CheckWin32Version(6 , 1 ) and ((GetSystemMetrics(SM_DIGITIZER) and NID_MULTI_INPUT) > 0 );
万全を期すならこのようなコードになりますが、マルチタッチではない機能...例えばフリック等は Vista でも可能です (ペンに対応していればいい ) ので SM_DIGITIZER の他のフラグを調べるなどして、実情に合ったコードを記述して下さい。
タッチパネルが利用可能な接触点の最大数を得る
Windows 7 は最大で 255 の接触点をサポートしていますが、ハードウェア毎に最大接触点数は異なります。最大接触点数は GetSystemMetrics() で SM_MAXIMUMTOUCHES を問い合わせる事によって取得できます。
var
MaxTP: Byte;
begin
MaxTP := GetSystemMetrics(SM_MAXIMUMTOUCHES);
end ;
TW317A5 の同時に処理できる接触点は 2 つですので、ピアノのようなアプリケーションを作るのは無理っぽいですね (ドミソが同時に押せない)。
Microsot のデモでよくある 5 本指を使ったマルチタッチアプリを実現するためにはハードウェアサポートが必要です。アプリケーションの目的によってはマルチタッチディスプレイを購入する際に最大接触点数を確認する必要があります。
アクションをジェスチャマネージャに動的に割り当てる
マルチタッチが使えない場合には、ジェスチャマネージャで代替する事も考えなくてはならないでしょう。以下はアクションをジェスチャマネージャに動的に割り当てたり解除するコードとなります。
// アクションを標準ジェスチャに動的に割り当てる
var
GestureItem: TCustomGestureCollectionItem;
begin
Form1.Touch.GestureManager := GestureManager1;
Form1.Touch.StandardGestures := [sgLeft, sgRight];
for GestureItem in GestureManager1.GestureList[Form1] do
begin
if GestureItem.Name = 'Left' then
GestureItem.Action := Action_Left
else if GestureItem.Name = 'Right' then
GestureItem.Action := Action_Right;
end ;
end ;
// 標準ジェスチャを解除する
var
GestureItem: TCustomGestureCollectionItem;
begin
Form1.Touch.StandardGestures := [];
//Form1.Touch.GestureManager := nil;
end ;
Form1 上のパネル (Panel1) に割り当てられたジェスチャの場合には上記コードの Form1 を Panel1 で置き換えて下さい。
「何故ジェスチャマネージャを動的に割り当てたり解除したりしなくてはならないのか? 」 と思われる事でしょう。もっともな疑問ですが、これは実際にマルチタッチと TGestureManager を同時に利用してみると解ります。例えば Standard Gesture の左右を有効にしたままマルチタッチが有効だと、フリックを始め殆どのマルチタッチ機能が誤動作してしまいます。要は機能がカブってしまうのです。
自動回転に応答させる
TW317 シリーズは Millenium というアプリケーションにより、自動で画面を回転させる事ができます。
自動回転を検出するには幾つか方法がありますが、最も簡単だと思われる方法は、
type
TForm1 = class (TForm)
...
FOWidth: Integer;
FOHeight Integer;
procedure WMSettingChange(var Msg: TWMSettingChange); message WM_SETTINGCHANGE;
...
end ;
...
procedure TfrmMain.FormCreate(Sender: TObject);
begin
//FOWidth := GetSystemMetrics(SM_CXSCREEN);
//FOHeight := GetSystemMetrics(SM_CYSCREEN);
FOWidth := Screen.Width;
FOHeight := Screen.Height;
...
end ;
procedure TForm1.WMSettingChange(var Msg: TWMSettingChange);
var
dWidth, dHeight: Integer;
begin
inherited ;
//dWidth := GetSystemMetrics(SM_CXSCREEN);
//dHeight := GetSystemMetrics(SM_CYSCREEN);
dWidth := Screen.Width;
dHeight := Screen.Height;
if (FOWidth <> dWidth) or (FOHeight <> dHeight) then
begin
FOWidth := dWidth;
FOHeight := dHeight;
// 回転されたか解像度が変更された
...
end ;
end ;
WM_SETTINGCHANGE メッセージを捕捉し、そこで Scrren 変数 の値または GetSystemMetrics(SM_CXSCREEN) / GetSystemMetrics(SM_CYSCREEN) を調べるやり方です。この方法だと画面の回転だけでなく解像度の変更にも対応します。
いずれにせよ、回転を検知したら UI を変更する必要があります。縦持ちと横持ちで同じ UI というのは使いにくい事が多いからです。
UI が変更される過程が見えてしまうのはみっともないので、
begin
LockWindowUpdate(Form1.Handle);
try
// UI 変更処理
finally
LockWindowUpdate(0 );
end ;
end ;
LockWindowUpdate() で画面の描画を一旦停めてしまいましょう。
画面の向きを変更する
Millenium や Intel GMA の機能 を使って画面を回転させる事は可能です。しかし、センサーによる自動回転はレスポンスが悪く、意図しない角度で自動回転してしまう事があります。Intel GMA のは (キーボードの) ホットキーを押下するか、またはタスクトレイを右クリックしなくてはなりません。スレート PC においてこの操作方法は致命的です。
...という訳でアプリケーションで画面の向きを制御してみましょう。画面の向きを変えるには "ChangeDisplaySettings() を使います。
ChangeDisplaySettings() は Delphi でも普通に使えるのですが、DEVMODE 構造体を書き換えないと少々面倒です。幸いな事に、Changing Screen Orientation Programmatically using Delphi (The Road to Delphi)" において、画面の向きを変更するサンプルが掲載されていますので、これをユニットにして使う事にしましょう。
// -----------------------------------------------------------------------------
// Changing Screen Orientation Programmatically using Delphi (The Road to Delphi)
// http://theroadtodelphi.wordpress.com/2011/02/11/changing-screen-orientation-programmatically-using-delphi/
// -----------------------------------------------------------------------------
unit ScreenRotation;
interface
uses
Windows, SysUtils;
type
_devicemode = record
dmDeviceName: array [0 ..CCHDEVICENAME - 1 ] of Char;
dmSpecVersion: WORD;
dmDriverVersion: WORD;
dmSize: WORD;
dmDriverExtra: WORD;
dmFields: DWORD;
union1: record
case Integer of
0 : (
dmOrientation: Smallint;
dmPaperSize: Smallint;
dmPaperLength: Smallint;
dmPaperWidth: Smallint;
dmScale: Smallint;
dmCopies: Smallint;
dmDefaultSource: Smallint;
dmPrintQuality: Smallint
);
1 : (
dmPosition: TPointL;
dmDisplayOrientation: DWORD;
dmDisplayFixedOutput: DWORD
);
end ;
dmColor: Shortint;
dmDuplex: Shortint;
dmYResolution: Shortint;
dmTTOption: Shortint;
dmCollate: Shortint;
dmFormName: array [0 ..CCHFORMNAME - 1 ] of Char;
dmLogPixels: WORD;
dmBitsPerPel: DWORD;
dmPelsWidth: DWORD;
dmPelsHeight: DWORD;
dmDiusplayFlags: DWORD;
dmDisplayFrequency: DWORD;
dmICMMethod: DWORD;
dmICMIntent: DWORD;
dmMediaType: DWORD;
dmDitherType: DWORD;
dmReserved1: DWORD;
dmReserved2: DWORD;
dmPanningWidth: DWORD;
dmPanningHeight: DWORD;
end ;
devicemode = _devicemode;
Pdevicemode = ^devicemode;
const
DM_DISPLAYORIENTATION = $00800000 ;
ENUM_CURRENT_SETTINGS = -1 ;
DMDO_DEFAULT: DWORD = 0 ;
DMDO_90: DWORD = 1 ;
DMDO_180: DWORD = 2 ;
DMDO_270: DWORD = 3 ;
procedure ChangeOrientation(NewOrientation: DWORD);
function ScreenOrientation: DWORD;
implementation
procedure ChangeOrientation(NewOrientation: DWORD);
var
dm: TDeviceMode;
dwTemp: DWORD;
begin
ZeroMemory(@dm, SizeOf(dm));
dm.dmSize := SizeOf(dm);
if EnumDisplaySettings(nil , DWORD(ENUM_CURRENT_SETTINGS), dm) then
begin
if Odd(Pdevicemode(@dm)^.union1.dmDisplayOrientation) <> Odd(NewOrientation) then
begin
dwTemp := dm.dmPelsHeight;
dm.dmPelsHeight := dm.dmPelsWidth;
dm.dmPelsWidth := dwTemp;
end ;
if Pdevicemode(@dm)^.union1.dmDisplayOrientation <> NewOrientation then
begin
Pdevicemode(@dm)^.union1.dmDisplayOrientation := NewOrientation;
if (ChangeDisplaySettings(dm, 0 ) <> DISP_CHANGE_SUCCESSFUL) then
RaiseLastOSError;
end ;
end ;
end ;
function ScreenOrientation: DWORD;
var
dm: TDeviceMode;
dwTemp: DWORD;
begin
ZeroMemory(@dm, SizeOf(dm));
dm.dmSize := SizeOf(dm);
if EnumDisplaySettings(nil , DWORD(ENUM_CURRENT_SETTINGS), dm) then
result := Pdevicemode(@dm)^.union1.dmDisplayOrientation
else
result := DMDO_DEFAULT;
end ;
end .
ScreenRotation を uses して ChangeOrientation() を呼び出すだけで簡単に画面の向きを変更する事ができます。このユニット内にある ScreenOrientation() という名のもう一つの関数は、現在の画面の向きを返すものです。
uses
..., ScreenRotation;
procedure TForm1.Button1Click(Sender: TObject);
begin
ChangeOrientation(DMDO_90); // 90°回転
end ;
procedure TForm1.Button2Click(Sender: TObject);
begin
ChangeOrientation(DMDO_DEFAULT); // 元に戻す
end ;
TW317A5 ではすべての方向に回転できる事を確認しており、Millenium による自動回転よりもレスポンスがいいようです。
なお、言うまでもないことかもしれませんが...画面の向きを標準に戻す機能を作ってから実行するようにしましょう 。さもないと画面を元に戻すのに難儀するハメになります (^^;A
さて、回転ができるようになって喜んでいると、困った場面に遭遇する事になると思います。"自動回転に応答させる" の項で書いた WM_SETTINGCHANGE が飛んでこない事があるからです。これを回避するには、WM_DISPLAYCHANGE メッセージに応答するようにします。
type
TForm1 = class (TForm)
...
procedure WMDisplayChange(var Msg: TWMDisplayChange); message WM_DISPLAYCHANGE;
...
end ;
...
procedure TForm1.WMDisplayChange(var Msg: TWMDisplayChange);
begin
inherited ;
// ここに処理を書く
end ;
TW317A5 の場合には、WM_DISPLAYCHANGE にだけ対応すればいいようですね。Millenium で自動回転させると、WM_SETTINGCHANGE と WM_DISPLAYCHANGE の両方が飛んでくるようです。
UI と画面の向き
TW317 シリーズにはハードウェアボタンがない (ホットキーはありますが) ため、画面上に何らかのボタンが必要となります。マルチタッチのジェスチャだけでは足りませんし、ジェスチャより確実に動作するボタンがあった方がいい事もあるでしょう。
一例として画像ビューアの UI を考えてみます。画像は 4:3 またはそれに近い比率のものが多く、ワイドディスプレイである TW317 シリーズ では両端がデッドスペースになる事はお解り頂けると思います。ここに操作ボタンを設置するとこんな感じになります。
横長の画像がアスペクト比を壊す事無く最大限に表示されていますね...いい感じです。最下段のボタンは見た目操作しやすそうですが、実際にはとても押しにくく、下から 2 段目あるいは 3 段目あたりが最も押しやすい位置のボタンとなります。ここによく使うボタンを割り当てるとよいでしょう。
今度は縦長の画像を用意してみましょう。
この画像、実際にはスクエアなのですが、縦長の画像だと思って下さい。左右に余白ができてしまいますね。縦長ですから画像を横に回転 (270°回転) させればいいような気がします。
ですが、これだと UI と画像の向きが一致しないため読みづらくて仕方ありません。では、左右の黒い操作パネルを Align = alLeft / alRight のまま、自動回転 (90°回転) に頼ってみる事にしましょう。
おやおや、これではさっきよりも悪くなってしまいました。デッドスペースを全く活かせていません。では、画像を横に回転 (270°回転) させた上で回転機構を OFF にして本体を縦 (90°回転) にしたらどうでしょう?
デッドスペースは活かせていますが、これだと持ち方によっては誤操作してしまいかねません。
よって、回転した時にはデッドスペースを考えた上で UI を変化させる必要があります。忘れがちですが、スレート PC 用アプリケーションは本体を回転させる事を前提 とした作りにしなくてはなりません。なお、本体を 90°回転させていたのは、270°回転だと ホットキー / 照度センサー / インジケータ を手で押さえてしまうからです。
スレート PC には、
物理的な本体の向き
スクリーン (画面) の向き
アプリケーション内での向き
平面軸だけで回転軸が 3 つある事になります。操作を簡略化させるには軸の数を減らさなくてはなりません。この画像ビューアでは画像の回転 (アプリケーション内での向き) をオミットし、
1 軸 (自動回転に対応させ、本体の向きとスクリーンの向きを一致させる)
2 軸 (自動回転 OFF であってもボタンでスクリーンを回転できる)
いずれかとする事で操作の簡略化を図っています。断っておきますが、この画像ビューアのデスクトップ PC 版を作る事になったとしたら、スクリーン回転ボタンは画像回転ボタンになるでしょう。デスクトップ用の回転できるディスプレイを所持している人はそう多くないハズですので、画像と UI の方向の不一致があったとしても幾らかは有用でしょう (画像の回転がなければ 0 軸ですから)。
ここで紹介した画像ビューアは "マルチタッチアプリケーションのサンプル (Embarcadero)" から DL する事ができます。画像ビューアの操作方法は以下のようになっています。
Windows へ戻る
〔Ctrl〕+〔F12〕
前の画像へ
左フリック
〔←〕
次の画像へ
右フリック
〔→〕
先頭の画像へ
〔Home〕
最後の画像へ
〔End〕
画像を拡大する
ズームジェスチャ
〔Ctrl〕+ マウスホイール(↑)
〔Ctrl〕+〔+〕
画像を縮小する
〔Ctrl〕+ マウスホイール(↓)
〔Ctrl〕+〔-〕
画像を元のサイズに戻す
二本指タップ (ツーフィンガータップ)
中ボタン
〔Ctrl〕+〔Enter〕
拡大した画像を上へ移動
ドラッグ
マウスホイール(↑)
〔Shift〕+〔↑〕
拡大した画像を下へ移動
マウスホイール(↓)
〔Shift〕+〔↓〕
拡大した画像を左へ移動
〔Shift〕+〔←〕
拡大した画像を右へ移動
〔Shift〕+〔→〕
画面を右へ 90° 回転させる
画面を左へ 90° 回転させる
画面を標準の位置に戻す
スレート PC 用に UI を特化させたアプリケーションですが、普通のデスクトップ PC で試す事も可能です。デスクトップ PC ではフリックが使えないので、StandardGestures の Left / Right で代用しています。このため、拡大時にはジェスチャーによる画像送りはできません (ドラッグと同じ操作であるため)。
マルチタッチのイベントを確認できるテスター (with ソースコード) も付属しています。左ペインのオプション を幾つか有効にして、水色の領域でタッチジェスチャーを行うと、右ペインのリストボックスに判定履歴が表示されます。
コンポーネントに Touch プロパティと OnGesture イベントを追加する
例えば、古いコンポーネントとかをジェスチャ対応にしたいとします。ジェスチャを使いたいのなら、下位クラスで...
...
published
...
property Touch;
...
property OnGesture;
...
end ;
このように Published にて再定義すればいいです。
しかし、上記をやったにも関わらず、"ズーム / 回転等は動作しても Touch.GestureManager に割り当てられた機能が動作しない" 場合があります。この際には、親コントロールを辿っていって...
procedure CMGestureManagerChanged(var Message : TMessage); message CM_GESTUREMANAGERCHANGED;
このようなメッセージ処理が存在しないのなら、StdCtrls.pas の TCustomEdit.CMGestureManagerChanged() のコードを丸パクリしてコンポーネントに実装するとうまく動作するようです。
生のタッチイベントを処理する
ジェスチャやフリックを使わず、接触点 (タッチポイント) 情報を自前で処理するには WM_TOUCH に応答します。
WM_TOUCH を処理するには、以下の API を使う必要があります。
具体的な実装例は以下のようになります。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TRawTouchEvent = procedure (Sender: TObject; const TI: TTouchInput) of object ;
TForm1 = class (TForm)
Memo1: TMemo;
procedure FormShow(Sender: TObject);
procedure RawTouchDown(Sender: TObject; const TI: TTouchInput);
procedure RawTouchUp(Sender: TObject; const TI: TTouchInput);
procedure RawTouchMove(Sender: TObject; const TI: TTouchInput);
private
{ Private 宣言 }
FOnRawTouchDown: TRawTouchEvent;
FOnRawTouchUp: TRawTouchEvent;
FOnRawTouchMove: TRawTouchEvent;
procedure WMTouch(var msg: TMessage); message WM_TOUCH;
public
{ Public 宣言 }
published
property OnRawTouchDown: TRawTouchEvent read FOnRawTouchDown write FOnRawTouchDown;
property OnRawTouchUp: TRawTouchEvent read FOnRawTouchUp write FOnRawTouchUp;
property OnRawTouchMove: TRawTouchEvent read FOnRawTouchMove write FOnRawTouchMove;
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// フォーム表示時
procedure TForm1.FormShow(Sender: TObject);
var
CanMultiTouch: Boolean;
begin
OnShow := nil ;
CanMultiTouch := (GetSystemMetrics(SM_DIGITIZER) and NID_MULTI_INPUT) > 0 ;
if CanMultiTouch then
begin
// タッチを有効にするウィンドウを指定
RegisterTouchWindow(Self.Handle, 0 );
// イベントを設定
OnRawTouchDown := RawTouchDown; // 接触点が追加された (押された)
// OnRawTouchMove := RawTouchMove; // 接触点が移動した
OnRawTouchUp := RawTouchUp; // 接触点が削除された (離された)
end ;
end ;
// タッチメッセージ (WM_TOUCH) 処理
procedure TForm1.WMTouch(var msg: TMessage);
var
cInputs: DWORD;
TouchInputs: array of TouchInput;
TI: TTouchInput;
begin
cInputs := LOWORD(msg.wParam); // 上位ワードは予約されている
if cInputs > 0 then
begin
// 接触点構造体を確保
SetLength(TouchInputs, cInputs);
// 接触点情報を取得
if GetTouchInputInfo(msg.lParam, cInputs, @TouchInputs[0 ], SizeOf(TouchInput)) then
begin
// 接触点毎にイベントを発生させる
for TI in TouchInputs do
begin
// TOUCHEVENTF_DOWN
if (TI.dwFlags and TOUCHEVENTF_DOWN) > 0 then
if Assigned(FOnRawTouchDown) then
OnRawTouchDown(Self, TI);
// TOUCHEVENTF_MOVE
if (TI.dwFlags and TOUCHEVENTF_MOVE) > 0 then
if Assigned(FOnRawTouchMove) then
OnRawTouchMove(Self, TI);
// TOUCHEVENTF_UP
if (TI.dwFlags and TOUCHEVENTF_UP ) > 0 then
if Assigned(FOnRawTouchUp) then
OnRawTouchUp(Self, TI);
end ;
// タッチ入力ハンドルを閉じる
CloseTouchInputHandle(msg.lParam);
msg.Result := 0 ; // WM_TOUCH を処理した
Exit;
end ;
end ;
DefWindowProc(Self.Handle, msg.Msg, msg.wParam, msg.lParam);
end ;
// タッチイベントハンドラ(接触点毎) - TOUCHEVENTF_DOWN
procedure TForm1.RawTouchDown(Sender: TObject; const TI: TTouchInput);
var
s: string ;
sPrimary: string ;
begin
if (TI.dwFlags and TOUCHEVENTF_PRIMARY) > 0 then
sPrimary := '*'
else
sPrimary := ' ' ;
s := Format('[DOWN] %s%.8x: (X=%d,Y=%d)' , [sPrimary, TI.dwID, TI.x div 100 , TI.y div 100 ]);
Memo1.Lines.Add(s);
end ;
// タッチイベントハンドラ(接触点毎) - TOUCHEVENTF_MOVE
procedure TForm1.RawTouchMove(Sender: TObject; const TI: TTouchInput);
var
s: string ;
sPrimary: string ;
begin
if (TI.dwFlags and TOUCHEVENTF_PRIMARY) > 0 then
sPrimary := '*'
else
sPrimary := ' ' ;
s := Format('[MOVE] %s%.8x: (X=%d,Y=%d)' , [sPrimary, TI.dwID, TI.x div 100 , TI.y div 100 ]);
Memo1.Lines.Add(s);
end ;
// タッチイベントハンドラ(接触点毎) - TOUCHEVENTF_UP
procedure TForm1.RawTouchUp(Sender: TObject; const TI: TTouchInput);
var
s: string ;
sPrimary: string ;
begin
if (TI.dwFlags and TOUCHEVENTF_PRIMARY) > 0 then
sPrimary := '*'
else
sPrimary := ' ' ;
s := Format('[UP] %s%.8x: (X=%d,Y=%d)' , [sPrimary, TI.dwID, TI.x div 100 , TI.y div 100 ]);
Memo1.Lines.Add(s);
end ;
end .
WM_TOUCH のメッセージの中ですべてを処理してもいいのですが、サンプルでは 3 つのイベントを発生させる事で OnMouseDown / OnMouseUp / OnMouseMove のような使い勝手を実現させています。各接触点の情報は TOUCHINPUT 構造体 に格納されます。
生のタッチイベントを処理するので、WM_TOUCH はジェスチャやフリックとは排他利用となります。
タッチキーボード
スレート PC での文字入力はあまりやりたくありませんが、それでも文字入力が必要な事があります。Delphi 2010 またはそれ以降では TTouchKeyborad というコンポーネントがあり、ソフトウェアキーボードを簡単に実現できます。
TTouchKeyboard はツールパレットの [Touch] にあります。
Layout プロパティを "NumPad" に変更するとテンキーにする事ができます。
Layout = 'Standard' なキーボードのレイアウトは 101 / 102 / 106 キーボードが用意されており、現在のロケールに応じたキーボードが自動的に選択されます。強制的に英語キーボードにしたい場合には、
uses
..., Keyboard;
type
TTempTouchKeyboard = class (TCustomTouchKeyboard);
...
このようにしておいて、
procedure TForm1.FormCreate(Sender: TObject);
begin
TTempTouchKeyboard(TouchKeyboard1).CreateKeyboard('en-US' ); // 英語キーボード
TouchKeyboard1.Redraw;
end ;
実行時に言語/国を指定します。
漢字変換関連キーのない英語キーボードになりました。
カスタムタッチキーボード
場合によってはキーを自前で描画したり、キー配列をカスタマイズしたい事もあると思います。キーの自前描画に関しては "TTouchKeyboardをいぢ(め)る。(全力わはー)" を参照して下さい。ここではキー配列のカスタマイズ方法を説明します。
キー配列のカスタマイズ方法は以下の通りです。
XML でキーボードレイアウトファイルを作成する 。
XML の記述の仕様は "Hacking the TTouchKeyboard Part IV" にて解説されていますが、イチから作るのは面倒なので既存のキーボードレイアウトから XML ファイルを生成します。
XML の生成には "Save Touch Keyboard Layout (SaveLayout.exe)" をコンパイルして使います。そのままだとちょっとだけ使いにくいので、SaveKeyboard.pas に軽微な修正を施します。
procedure SaveXmlFile(const FileName: string ; KeyboardLayout: TVirtualKeyLayout);
var
...
Dmy: String ; // 追加
begin
...
{ 出力 された XML を整形して読みやすくする }
{ ---------------------------------------- }
Dmy := XMLDoc.FormatXMLData(Document.XML.Text);
Document.XML.Text := Dmy;
Document.Active := True;
{ ---------------------------------------- }
Document.SaveToFile(FileName);
end ;
XML を整形して、
procedure TForm1.Button3Click(Sender: TObject);
...
begin
...
try
SaveDialog1.FileName := LayoutName + '.xml' ; // ファイル名のデフォルトを生成
if (Layout <> nil ) and SaveDialog1.Execute then
SaveXmlFile(SaveDialog1.FileName, Layout);
finally
Layout.Free;
end ;
end ;
保存用のファイル名を自動的に付与します。SavaDialog1 の Filter プロパティと DefaultExt プロパティも設定しておくといいでしょう。
このツールを使って吐かれる XML は UTF-8N (BOM 無し UTF-8) で保存されますので、UTF-8N を編集できるテキストエディタがあると便利です。
<keyboard keyboardname="CalcPad " keyboardtype="CalcPad " width="240" height="229" minwidth="180" minheight="150" rowheight="48">
<row topmargin="0" bottommargin="2">
<key caption="R・CM" vk="120" width="48" height="48" rightmargin="2" />
<key caption="M-" vk="121" width="48" height="48" leftmargin="2" rightmargin="2" />
<key caption="M+" vk="122" width="48" height="48" leftmargin="2" rightmargin="2" />
<key caption="+/-" vk="123" width="48" height="48" leftmargin="2" rightmargin="2" />
<key caption="CA" vk="128" width="48" height="48" leftmargin="2" stretch="true"/>
</row>
<row topmargin="2" bottommargin="2">
<key vk="103" width="48" height="48" rightmargin="2" />
<key vk="104" width="48" height="48" leftmargin="2" rightmargin="2" />
<key vk="105" width="48" height="48" leftmargin="2" rightmargin="2" />
<key caption="%" vk="125" width="48" height="48" leftmargin="2" rightmargin="2" />
<key caption="C・CE" vk="126" width="48" height="48" leftmargin="2" stretch="true"/>
</row>
<row topmargin="2" bottommargin="2">
<key vk="100" width="48" height="48" rightmargin="2" />
<key vk="101" width="48" height="48" leftmargin="2" rightmargin="2" />
<key vk="102" width="48" height="48" leftmargin="2" rightmargin="2" />
<key caption="×" vk="106" width="48" height="48" leftmargin="2" rightmargin="2" />
<key caption="÷" vk="111" width="48" height="48" leftmargin="2" stretch="true"/>
</row>
<row topmargin="2" bottommargin="2">
<key vk="97" width="48" height="48" rightmargin="2" />
<key vk="98" width="48" height="48" leftmargin="2" rightmargin="2" />
<key vk="99" width="48" height="48" leftmargin="2" rightmargin="2" />
<key vk="107" width="48" height="92" leftmargin="2" rightmargin="2" />
<key vk="109" width="48" height="48" leftmargin="2" stretch="true"/>
</row>
<row topmargin="2" bottommargin="0">
<key vk="96" width="96" height="48" rightmargin="2" />
<key vk="110" width="48" height="48" leftmargin="2" rightmargin="2" />
<key vk="0" width="48" height="48" leftmargin="2" rightmargin="2" />
<key caption="=" vk="127" width="48" height="48" leftmargin="2" stretch="true"/>
</row>
</keyboard>
例はテンキーパッドを改変して作った電卓用キー配列です。
R・CM VK_F9
M- VK_F10
M+ VK_F11
+/- VK_F12
CA VK_F17
7 VK_NUMPAD7
8 VK_NUMPAD8
9 VK_NUMPAD9
% VK_F14
C・CE VK_F15
4 VK_NUMPAD4
5 VK_NUMPAD5
6 VK_NUMPAD6
× VK_MULTIPLY
÷ VK_DIVIDE
1 VK_NUMPAD1
2 VK_NUMPAD2
3 VK_NUMPAD3
+ VK_ADD
- VK_SUBTRACT
0 VK_NUMPAD0
. VK_DECIMAL
= VK_F16
このような配置になります。
Keyboard Layout Compiler (kcc.exe) でキーボードレイアウトのバイナリを生成する
"Keyboard Layout Compiler (kcc.exe)" はソースコードで提供されていますので、一旦コンパイルしてバイナリを生成します。出来上がった kcc.exe はコンソールアプリケーションです。
使い方はとてもシンプルで、
とするだけです。正常にコンパイルされると、*.binary というファイルが生成されます。
このコンパイラは Delphi のコンパイラや RTL のバージョンに依存すると思われるため、一度 kcc.exe を作ったらすべての Delphi で使えるとは思わない方がいいでしょう。面倒でもコンパイラ毎に kcc.exe を生成する必要があります。
プロジェクトのリソースに RCDATA でキーボードレイアウトのバイナリを含める
キーボードレイアウトファイルのバイナリ (*.binary) ができたら、リソースとしてアプリケーションに埋め込みます。Delphi ビルトインのリソースマネージャ ([プロジェクト | リソースと画像]) を使ってもいいのですが、何故かうまく動作しない事があるので手動でリソースを追加します。
[ファイル | 新規作成 | その他...] から、テキストファイルを作成し、拡張子を *.rc にして保存します。
このリソーススクリプトファイルに、以下の一行を記述します。
CALCPADKEYBOARD RCDATA "CALCPADKEYBOARD.binary"
プロジェクトマネージャに *.rc があれば勝手にコンパイルされてリンクされます。リソース名は任意ですが、"KEYBOARD" という文字を含めて下さい。リソースの種類は RCDATA です。
TTouchKeyboard.Layout を変更する
アプリケーションにリソースを埋め込む事ができたら、コードで、
TouchKeyboard1.Layout := 'CalcPad' ;
キーボードレイアウトを変更します。
画像はカスタムキーボードを電卓アプリケーションに用いている所です。
レイアウト上、ダミーキーが必要なのだけれど、ダミーキーを表示させたくない場合には、以下を参考にして下さい。
TCustomKeyboardButton の派生クラスを作る
TKeyboardButtonEx = class (TCustomKeyboardButton)
public
procedure Paint(Canvas: TCustomCanvas = nil ); override ;
end ;
{ TKeyboardButtonEx }
procedure TKeyboardButtonEx.Paint(Canvas: TCustomCanvas);
begin
if key.Vk = 0 then
Exit;
inherited ;
end ;
Form の OnCreate イベントハンドラ等で以下のように記述する
TouchKeyboard1.DefaultButtonClass := TKeyboardButtonEx;
TouchKeyboard1.Layout := 'CalcPad' ;
このコードを書いておくと、VK = '0' に指定されているキーは描画されなくなり、VK = '0' に指定されているダミーキーをレイアウト計算用に使うことができます。
See Also:
スレート PC プログラミング向けの資料
日本語で書かれたものは、ほぼ Microsoft のサイトに存在します。
MSDN の資料は左ツリーの操作性が悪く、堂々巡りをしがちなので、MSDN エイリアス を用意しました。水平分割版 もあります。
ここにある情報が役に立って、「調べる手間が省けたからオマイに飯でもおごってやるよ」 というハートウォーミングな方がいらっしゃいましたら、下のボタンからどうぞ。