FC2ブログ

VS2017-はじめの1/10歩(11):Windowsタブレット上でピンチイン/アウト:拡大縮小

   プログラミング [2019/05/15]
なんか忙しいなー・・・・

実はタブレット向けのプログラムを作るにあたって、
出来て当たり前だと思っていたピンチイン/アウトで画面の内容が大きく表示されたり小さくなったりするアレ・・・
(ピンチイン/アウト:二本指で画面をタッチして指の間隔を広げたり/狭めたり画面上で指を滑らす動作。)

Windows10とかでも、普通にはアプリが対応してないとそんな動きはしないらしい。
え、OS側で勝手に制御してくれるんじゃねーの?

「ストアアプリ」対応?・・・ダウンロードして使えるようになるアプリ専用の開発環境(?)だとその機能を持たせることが容易?


実際、Windows10のタブレットを1年前の初売りで安く手に入れてたのでやってみた。

ホンマや。ほとんどのPC用アプリではそんなタッチへのアクセスには反応してくれない。
チャント拡大縮小するのは、ブラウザやフォトビューアくらいのもの?
テキストエディタとかで、字を大きくするくらいの対応をしている場合もあった。

「なーんだ、Windows10大したことねーな」 と一瞬思った。

でも、手持ちのスマフォを、改めて触ってみたら、
こっちも、全部のアプリが対応して拡大縮小するわけでもないのね。
勘違いしてた。どんな、スマフォアプリも、ピンチイン/アウトできるもんだと。

まあ、タブレットは画面が小さいけど、割と画質は高い(画角は広い)。
だから、普通のPC向けに作ったアプリをタブレットで見ると、かなり小さい字で表示される。

老眼間近の方々には、結構辛い。

というわけで、Windows10のピンチイン/アウトの機能はどうやって使えるのか調べてみた。





色々ぐぐった結果、いくつか解説してくれているところを見つけた。
(いつも、いつもすいません。勝手にURL載せてしまって。)

参考サイト
  https://code.msdn.microsoft.com/windowsdesktop/CVBXAML-WPF-4-850a6252
  http://www.atmarkit.co.jp/ait/articles/1004/02/news095.html
  http://m-miya.blog.jp/archives/1025547497.html

まずは、タッチパネルに対して行う指動作の説明から。(どこぞの解説の写しになります。)
◆タッチ操作の名前と意味
操作名説明マウスだと
タップ画面上に指1本で軽く触れて離す。クリック
プレス アンド ホールド指1本で画面上の対象に触れ続けて円が表示された後に指を離す。右クリック
プレスアンドタップ指1本で画面上に触れたまま、もう一本の指で画面をタップする。右クリック
ダブルタップ画面上を指で2回連続でタップします。ダブルクリック
ドラッグ画面上の対象に触れて指を離さずに目的の場所まで動かして指を離します。ドラッグ
パン指を画面上に軽くタッチした状態で動かします。スクロール
フリック画面に触れて指を払うように動かします。 指を動かした方向に表示内容がスクロールさせたり、ページをめくることができます。アプリケーションが対応している場合のみ有効。なし
ズーム(ピンチイン/アウト)指1本で画面に触れたまま、別のもう1本の指で画面に触れ、2本の指先を開くように動かす。逆に2本の指先でつまむように動かす。 拡大縮小は、アプリケーションが対応している場合のみ有効。[Ctrl]キー+スクロールホイール
回転指1本で画面に触れたまま、もう1本の指で画面に触れ、最初に触れた指を中心にして円を描くように動かします。 回転は、アプリケーションが対応している場合のみ有効です。なし



◆Windowsのズーム機能関連のトリビア?
○ディスクトップ全体をキー入力での拡大/縮小できる
 -[Windows]キー+[+]/[-]キーで操作可能(=拡大鏡アプリの全画面表示)
 -最小は元のディスクトップサイズで、最大は1600%
 -マウスポインタに追従してスクロールする。
 -[Windows]キー+[Esc]で拡大解除

×(それっっぽい名前の)仮想キーコードで何かできるか?
 -[左Windows]/[右Windows]キーの仮想キーコードは、VK_LWIN(0x5B)/VK_RWIN(0x5C)
 -VK_ZOOM(0xFB)は何か・・・>分からなかった。
 -プログラムでWM_KEYDOWNでVK_LWINを拾うことはできる
   が、SendMessage/WHND_DESKTOPでVK_ADD+VK_LWIN(SetKeyboardStatus)してみたが反応無し。

○タッチの複数点入力(ピンチイン・アウト)はマウスホイールイベントに変換される
 -Windows7以降で有効
 -[Ctrl]キーを押してマウスホイール動作をした場合と同じ
   WM_MOUSEWHEEL+wParam下位(MK_CONTROL(0x0008))

○ディスクトップ全体の拡大をプログラムで行う場合はMagnification APIを使う
 -「magnify.exe」は拡大鏡アプリ
 -HKEY_CURRENT_USER\Software\Microsoft\ScreenMagnifier内のレジストリ設定で起動時デフォルトの設定が可能
 -Magnification APIはMagnification.dllで提供される拡大鏡みたいな効果をアプリから制御できる。
   https://docs.microsoft.com/ja-jp/previous-versions/windows/desktop/magapi/entry-magapi-sdk
 -Magnification.dllはWin Vista以降で利用可能。Win7ではC:\Windows\system32、sysWOW64下にそれぞれある
 -Windows SDK:Windows8以降でないとMagSetFullscreenTransform()等は機能しない。


◆Magnification API概説(ズームに必要な部分のみ)
○MagSetFullscreenTransform
 -ディスクトップ自体を表示倍率と表示位置(オフセット)を変更する。

            拡大後のディスクトップ画面
            ┌─────────────┐
            │             |
 元のスクリーン    │             |
  ┏━━┓      │  (x,y)      |
 H┃  ┃→→→→→→│    ┏━━┓     |H*f
  ┗━━┛  *f  │    ┃表示┃     |
   W        │    ┗━━┛     |
            │             |
            └─────────────┘
                 W*f

 -表示倍率は、1.0~4096.0まで。
 -表示位置は、ディスクトップ画面上の表示範囲矩形の左上座標を指定する。x,yとも-262144~262144が指定可。
 -オフセットは、表示している倍率(f)に影響されない元のスクリーン座標(WxH)を指定する。
  なので、現実的な表示位置の指定範囲は、拡大座標系で(0,0)~(W*f-W, H*f-H)。
  スクリーン座標系に直すと(0,0)~(W-W/f, H-H/f)。
 -表示矩形がディスクトップ矩形範囲からはみ出た場合、はみ出たところは黒だった。

○MagGetInputTransform
 -タッチ入力の変換の有無と、
  「有」(Enabled)の場合、変換時の元の矩形範囲(スクリーン座標)と拡大後の矩形範囲を取得する。
 -Enabledになっていない場合、元のスクリーン座標のままでタッチ入力座標が通知される。

○MagGetFullscreenTransform
 -セット(MagSetFullscreenTransform)された拡大倍率と表示位置を返す。

○MagSetInputTransform
 -タッチ入力の変換の有無と、
  「有」(Enabled)の場合、変換元の矩形範囲(スクリーン座標)と拡大後の矩形範囲を設定する。
 注)このAPIを普通に呼び出すとエラー(GetLastError():ERROR_ACCESS_DENIED)になる。
 使用するためにはUIアクセス特権が必要になる。


◆UIアクセス権限とパン動作について(初耳)※
○MagSetInputTransformのUIアクセス権限
 -Windows Vista以降、ユーザの権限に加え、アプリケーション自体にもUIアクセス権限というものが必要?
 -そのUIアクセス権限を調べているときに出てきた言葉の意味を以下に記述する。
  ・「分離アプリケーション」:独自のマニフェストを持ったアプリ
  ・「マニフェスト」:使用するアセンブリを決定するためのXMLファイル(.manifest)
  ・「アセンブリ」:Side-by-Sideアセンブリとか言う。DLLとかEXEとかのコンポーネント

 -マニフェストでUIアクセス権限を指定できるらしい。
  C/C++でexeを作るときは、マニフェストはexeに埋め込みできる。
  ▼マニフェストの埋め込み設定(VisualStudio2017)
  -プロジェクトのプロパティを開く:[プロジェクト]-[プロパティ]メニュー
  -右側「リンカー」-「マニフェストファイル」選択
  -左側「マニフェストの生成」=はい、
     「ユーザーアカウント制御(UAC)を有効にする」=はい
     「UACの実行レベル」=requireAdministrator、
     「UACによるUI保護のバイパス」=はい←これの名前が「UIAccess」
  -右側「マニフェストツール」-「入出力」選択
   左側「埋め込みマニフェスト」=はい

※上記の設定を何回か変更してexeを起動しようとすると「サーバーから紹介が帰ってきました。」のエラーが出て、exeを起動できない現象が出た。(ただし、メッセージ的には「紹介」ではなく「照会」では?)
 exeのプロパティで、[全てのユーザーの設定を変更]-「管理者としてこのプログラムを実行する」設定を変更することで、解消できる。

※ビルドしたものを実行したが、MagSetInputTransformは正常動作しなかった。
 \Windowsフォルダとか\Program Filesフォルダ下にないとダメなような記述もあったので試したがNG。

〇タッチでのスライド(パン)は、スクロールイベントに変換される。・・・とあるがタッチをなぞっても来ない。
  通常はタッチイベントを変換処理しないと、WM_VSCROLL/WM_HSCROLLイベントにはならない。


◆で?結局何がしたかったんでしょう 
・ピンチイン/アウト的な制御はC#でないとだめ?
 C#だとしても、ピンチイン/アウト的な制御はそう簡単ではなさそう。
 ※ただし、ストアアプリである必要はない。

・C++でのピンチイン/アウトを解説しているサイトは・・・MS発行のpdfがダウンロードできる
  http://download.microsoft.com/download/C/0/F/C0F99BC0-14A7-4E19-923C-022F312072E5/Windows7_TouchApplicationDevelopmentGuidance.pdf
  結局、これが一番分かりやすかった。

・TouchZoomDesktop(フリー/商用不可)でディスクトップのピンチイン・アウトが可能
 拡大時の文字のギザギザは軽減されている模様。
 これがあれば拡大しての利用には困らない。


◆結論
A)MagSetFullscreenTransformを使う
B)アプリ自体が自ウィンドウ内のみをズームアップ/ダウンさせる
 メニュー、コントロール等のサイズ、および使用フォント変更などをする必要がある。
C)TouchZoomDesktopを使う

Cですな。

◆結局
「TouchZoomDesktopを使えばいいさ」の結論に達するまで、
以下のようなッコードを作って試してみてました。
HelloWorldに付け足しただけだけど、拡大/縮小はできたみたい。

//-----------------------------------------------------------------------------
// zoominout.cpp: アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include "zoominout.h"

#define MAX_LOADSTRING 100

//**********************************************************************************
#include
#pragma comment(lib, "magnification.lib")
//**********************************************************************************

// グローバル変数:
HINSTANCE hInst; // 現在のインターフェイス
WCHAR szTitle[MAX_LOADSTRING]; // タイトル バーのテキスト
WCHAR szWindowClass[MAX_LOADSTRING]; // メイン ウィンドウ クラス名

//**********************************************************************************
float g_flZoomFact = 1.0; //拡大倍率
int g_iX = 0, g_iY = 0; //カレント座標
TCHAR g_szMsg[512]; //エラーメッセージバッファ
int g_iOsf_X, g_iOfs_Y; //表示位置座標
//**********************************************************************************

// このコード モジュールに含まれる関数の宣言を転送します:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
//**********************************************************************************
BOOL ZoomAndMoveScreen(HWND, float, int, int);
VOID DebugMessageBox(HWND);
INT_PTR CALLBACK DebugMessageProc(HWND, UINT, WPARAM, LPARAM);
//**********************************************************************************

//-----------------------------------------------------------------------------
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

// TODO: ここにコードを挿入してください。
//**********************************************************************************
MagInitialize();
//**********************************************************************************

// グローバル文字列を初期化しています。
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_ZOOMINOUT, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);

// アプリケーションの初期化を実行します:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}

HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_ZOOMINOUT));

MSG msg;

// メイン メッセージ ループ:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//**********************************************************************************
MagUninitialize();
//**********************************************************************************

return (int) msg.wParam;
}



//-----------------------------------------------------------------------------
// 関数: MyRegisterClass()
// 目的: ウィンドウ クラスを登録します。
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ZOOMINOUT));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_ZOOMINOUT);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

return RegisterClassExW(&wcex);
}

//-----------------------------------------------------------------------------
// 関数: InitInstance(HINSTANCE, int)
// 目的: インスタンス ハンドルを保存して、メイン ウィンドウを作成します。
// コメント:
// この関数で、グローバル変数でインスタンス ハンドルを保存し、
// メイン プログラム ウィンドウを作成および表示します。
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // グローバル変数にインスタンス処理を格納します。

HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
//CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
nullptr, nullptr, hInstance, nullptr);

if (!hWnd)
{
return FALSE;
}

ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

return TRUE;
}

//
// 関数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: メイン ウィンドウのメッセージを処理します。
//
// WM_COMMAND - アプリケーション メニューの処理
// WM_PAINT - メイン ウィンドウの描画
// WM_DESTROY - 中止メッセージを表示して戻る
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
//*TEST*********************************************************************
case WM_KEYDOWN:
if (wParam == VK_F2) {
/*
GetKeyboardState(keystates);
truekeystate = keystates[VK_ADD];
keystates[VK_LWIN] = 1;
keystates[VK_ADD] = 1;
SetKeyboardState(keystates);
SendMessage(HWND_DESKTOP, WM_KEYDOWN, VK_ADD, lParam);
keystates[VK_LWIN] = 0;
keystates[VK_ADD] = truekeystate;
SetKeyboardState(keystates);
*/
if (g_flZoomFact > 1) {
g_iX = min(1000, g_iX + 100);
ZoomAndMoveScreen(hWnd, g_flZoomFact, g_iX, g_iY);
InvalidateRect(hWnd, NULL, TRUE);
}
}
else if (wParam == VK_F3) {
/*
GetKeyboardState(keystates);
truekeystate = keystates[VK_SUBTRACT];
keystates[VK_LWIN] = 1;
keystates[VK_SUBTRACT] = 1;
SetKeyboardState(keystates);
SendMessage(HWND_DESKTOP, WM_KEYDOWN, VK_SUBTRACT, lParam);
keystates[VK_LWIN] = 0;
keystates[VK_SUBTRACT] = truekeystate;
SetKeyboardState(keystates);
*/
if (g_flZoomFact > 1) {
g_iX = max(0, g_iX - 100);
ZoomAndMoveScreen(hWnd, g_flZoomFact, g_iX, g_iY);
InvalidateRect(hWnd, NULL, TRUE);
}
}
else if (wParam == VK_F4) {
if (g_flZoomFact > 1) {
g_iY = min(800, g_iY + 100);
ZoomAndMoveScreen(hWnd, g_flZoomFact, g_iX, g_iY);
InvalidateRect(hWnd, NULL, TRUE);
}
}
else if (wParam == VK_F5) {
if (g_flZoomFact > 1) {
g_iY = max(0, g_iY - 100);
ZoomAndMoveScreen(hWnd, g_flZoomFact, g_iX, g_iY);
InvalidateRect(hWnd, NULL, TRUE);
}
}
//else if (wParam == VK_LWIN) {
// PostMessage(HWND_DESKTOP, WM_KEYDOWN, wParam, lParam);
//}
//else if (wParam == VK_ZOOM) {
// PostMessage(HWND_DESKTOP, WM_KEYDOWN, wParam, lParam);
//}
break;
//*TEST*********************************************************************

//*SAMPLE*******************************************************************
//ピンチイン/アウトは、WM_MOUSEWHEEL+ MK_CONTROLになる。
case WM_MOUSEWHEEL:
if ((SHORT) LOWORD(wParam) == MK_CONTROL) {
int z = int ((SHORT) HIWORD(wParam));
//****TEST->
wsprintf(g_szMsg, "WM_MOUSEWHEEL: Delta=%d, (X,Y)=(%d,%d)", z, LOWORD(lParam), HIWORD(lParam));
DebugMessageBox(hWnd);
//****TEST<-
if (z > 0) {
if (g_flZoomFact < 16) {
g_flZoomFact += (float) z / 100;
g_flZoomFact = min(16, g_flZoomFact);
ZoomAndMoveScreen(hWnd, g_flZoomFact, g_iX, g_iY);
}
}
else {
if (g_flZoomFact > 1) {
g_flZoomFact += (float)z / 100;
g_flZoomFact = max(1, g_flZoomFact);
ZoomAndMoveScreen(hWnd, g_flZoomFact, g_iX, g_iY);
}
}
InvalidateRect(hWnd, NULL, TRUE); //****TEST
}
break;

//パン操作は、WM_VSCROLL/WM_HSCROLLになる。
case WM_VSCROLL:
switch (LOWORD(wParam)) {
case SB_LINEDOWN:
g_iX -= 100;
break;
case SB_LINEUP:
g_iX += 100;
break;
case SB_PAGEDOWN:
g_iX -= 500;
break;
case SB_PAGEUP:
g_iX += 500;
break;
}
g_iX = min(1000, max(0, g_iX));
ZoomAndMoveScreen(hWnd, g_flZoomFact, g_iX, g_iY);
InvalidateRect(hWnd, NULL, TRUE); //****TEST
break;

case WM_HSCROLL:
switch (LOWORD(wParam)) {
case SB_LINELEFT:
g_iY -= 100;
break;
case SB_LINERIGHT:
g_iY += 100;
break;
case SB_PAGELEFT:
g_iY -= 500;
break;
case SB_PAGERIGHT:
g_iY += 500;
break;
}
g_iY = min(800, max(0, g_iY));
ZoomAndMoveScreen(hWnd, g_flZoomFact, g_iX, g_iY);
InvalidateRect(hWnd, NULL, TRUE); //****TEST
break;

case WM_SETFOCUS:
ZoomAndMoveScreen(hWnd, g_flZoomFact, g_iX, g_iY);
break;

case WM_KILLFOCUS:
ZoomAndMoveScreen(hWnd, 1.0, g_iX, g_iY);
break;

//*SAMPLE*******************************************************************

case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 選択されたメニューの解析:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: HDC を使用する描画コードをここに追加してください...
//*TEST*********************************************************************
RECT rt;
POINT pos;
WCHAR szFixed[64];
GetCursorPos(&pos);
GetClientRect(hWnd, &rt);
swprintf(szFixed, 64, L"Zoom=%1.2f, Cursor(X,Y)=(%d,%d), Screen(W,H)=(%d,%d)",
g_flZoomFact,
pos.x, pos.y,
GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
DrawText(hdc, szFixed, wcslen(szFixed), &rt, DT_LEFT);
//*TEST*********************************************************************
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
//*SAMPLE*******************************************************************
ZoomAndMoveScreen(hWnd, 1.0, g_iX, g_iY);
//*SAMPLE*******************************************************************
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

// バージョン情報ボックスのメッセージ ハンドラーです。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;

case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}

//-----------------------------------------------------------------------------
//name :ZoomAndMoveScreen
//function :ディスクトップを拡大して、入力座標を中心にして表示枠を指定し、
// タッチ入力座標を表示枠に合せる。
//parameter :hWnd -制御のオーナーウィンドウ
// flZoomFact -拡大倍率
// iX -カレント位置x座標
// iY -同y座標
//global :[u]g_szMsg
//return :TRUE-ズーム成功、FALSE-失敗
//history :2019/01/24 -create
BOOL ZoomAndMoveScreen(
HWND hWnd,
float flZoomFact,
int iX,
int iY)
{
BOOL fSuccess; //成否フラグ
int iosf_x, iofs_y; //表示位置座標
int ifs_w, ifs_h; //スクリーンサイズ
BOOL fInputTransEnabled; //タッチ入力座標変換有無
RECT rectSource; //入力変換元矩形
RECT rectDest; //入力変換先矩形

if (flZoomFact >= 1.0) {
//ディスクトップの表示倍率と表示位置(オフセット)を変更する。
//表示倍率は、1.0~4096.0まで。
//表示位置は、ディスクトップ画面上の表示範囲矩形の左上座標を指定する。
//
//            拡大後のディスクトップ画面
//            ┌─────────────┐
//            │             |
// 元のスクリーン    │             |
//  ┏━━┓      │  (x,y)      |
// H┃  ┃→→→→→→│    ┏━━┓     |H*f
//  ┗━━┛  *f  │    ┃表示┃     |
//   W        │    ┗━━┛     |
//            │             |
//            └─────────────┘
//                 W*f
//
//オフセットは、表示している倍率(f)に影響されない元のスクリーン座標
//(max WxH)を指定する。
//現実的な表示位置の指定範囲は、拡大座標系で(0,0)~(W*F-W, H*F-H)。
//スクリーン座標系に直すと(0,0)~(W-W/f, H-H/f)。
ifs_w = GetSystemMetrics(SM_CXSCREEN);
ifs_h = GetSystemMetrics(SM_CYSCREEN);

iofs_x = (int) ((float) iX - (ifs_w / flZoomFact) / 2);
iofs_y = (int) ((float) iY - (ifs_h / flZoomFact) / 2);

iofs_x = min((int) ((float)ifs_w - ifs_w / flZoomFact), max(0, iofs_x));
iofs_y = min((int) ((float)ifs_h - ifs_h / flZoomFact), max(0, iofs_y));

fSuccess = MagSetFullscreenTransform(flZoomFact, iofs_x, iofs_y);
if (fSuccess) {
//タッチ入力の変換の有無と、「有」(Enabled)の場合の変換時の
//元矩形範囲(スクリーン座標)と拡大後の矩形範囲を取得する。
//Enabledになっていない場合、元のスクリーン座標のままでタッチ入力座標が
//通知される。
//MSサイトのサンプルでは、GetしてEnableだったらSetし直すコードになって
//いたが、そもそも最初にEnableするシーケンスが不明なため作り変える。
/*
if (MagGetInputTransform(&fInputTransEnabled, &rectSource, &rectDest)) {
//タッチ入力変換ありの場合は、倍率に応じて、矩形範囲指定をしなおす。
if (fTransformEnabled) {
SetInputTransform(hWnd, fInputTransEnabled);
}
}
*/
rectSource.left = iofs_x;
rectSource.top = iofs_y;
rectSource.right = iofs_x + (int)(ifs_w / flZoomFact);
rectSource.bottom = iofs_y + (int)(ifs_h / flZoomFact);
rectDest.left = 0;
rectDest.top = 0;
rectDest.right = ifs_w;
rectDest.bottom = ifs_h;
fSuccess = MagSetInputTransform(TRUE, &rectSource, &rectDest);
if (fSuccess
&& MagGetInputTransform(&fInputTransEnabled, &rectSource, &rectDest)) {
if (!fInputTransEnabled) {
//ERROR:"タッチ入力が実スクリーン座標のままです。"
wprintf(g_szMsg, _T("タッチ入力が実スクリーン座標のままです。"));
DebugMessageBox(hWnd);
}
}
else {
//ERROR:"タッチ入力モードの変更に失敗した可能性があります。"
wprintf(g_szMsg, _T("タッチ入力モードの変更に失敗した可能性があります。"));
DebugMessageBox(hWnd);
}
}
}
return FALSE;
}


//-----------------------------------------------------------------------------
//name :DebugMessageBox
//function :拡大状態でMessageBox()を使うと表示位置が任意のため、見えない位置に
// 表示されることがある。よって、拡大状態で見える位置にメッセージ
// ダイアログを表示する。
//parameter :hWnd -制御のオーナーウィンドウ
//global :[u]g_iOfs_X, [u]g_iOfs_Y
//return :なし
VOID DebugMessageBox(
HWND hWnd)
{
BOOL fSuccess; //成否フラグ

fSuccess = MagSetFullscreenTransform(&flZoomFact, &g_iOfs_X, &g_iOfs_Y);
if (!fSuccess) {
g_iOfs_X = 0;
g_iOfs_Y = 0;
}

DialogBox(hInst, IDD_DBGMSG, hWnd, DebugMessageProc);

return;
}


//-----------------------------------------------------------------------------
//name :DebugMessageProc
//function :拡大状態でMessageBox()を使うと表示位置が任意のため、見えない位置に
// 表示されることがある。よって、拡大状態で見える位置にメッセージ
// ダイアログを表示する。
//parameter :hDlg -ダイアログハンドル
// message -メッセージID
// wParam, lParam -メッセージパラメタ
//global :[r]g_szMsg, [r]g_iOfs_X, [r]g_iOfs_Y
//return :なし
//history :2019/01/24 -create
INT_PTR CALLBACK DebugMessageProc(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message) {
case WM_INITDIALOG:
SetWindowPos(hDlg, HWND_TOP, g_iOfs_X, g_iOfs_Y, 0, 0, SWP_NOSIZE);
SetWindowText(GetDlgItem(hDlg, IDC_STDBGMSG), g_szMsg);
return (INT_PTR)TRUE;

case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
//-----------------------------------------------------------------------------


今日はこの辺で。
m(__)m


スポンサーサイト





コメントの投稿

非公開コメント

カレンダー
01 | 2024/02 | 03
- - - - 1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 - -
プロフィール

さるもすなる

Author:さるもすなる
さるです。別HPサイト「さるもすなる」から侵食してきました。 山菜/きのこ、それとタイトルにしたPPバンド籠のことをメインに徒然に・・・・暇を持て余したさるの手仕事:男手芸のブログってことで。

最新記事
最新コメント
月別アーカイブ
カテゴリ
天気予報

-天気予報コム- -FC2-
本家のHPのトップ
山菜や茸の話です
PPバンドの籠作品と作り方です
投稿をお待ちしております



PVアクセスランキング にほんブログ村 にほんブログ村 ハンドメイドブログへ



マニュアルのお申し込み



検索フォーム
リンク
RSSリンクの表示
ブロとも申請フォーム

この人とブロともになる

QRコード
QR