拡大した画面上で、マウス操作を可能にするには?

[戻る]
とんぼ 2008/09/17(水) 16:47:21
VC++初心者のものです。

拡大鏡のような形で画面を拡大し、その拡大表示した画面上で
そのままマウス操作できるようにしたいのですが、
マウスの位置や動きを下のウィンドウに伝えるにはどうしたらいいでしょうか?

現在の状況としては、なんとか画面拡大はでき下のウィンドウにも伝わっているには伝
わっている状態なんですが、
拡大画面からだと、直接下のウィンドウへマウスメッセージがいってしまい、反映され
ません。

ちなみに拡大は、レイヤードウィンドウで真下のデスクトップ画面のDCを取得して
StretchBltで拡大表示している状態です。

どなたかご教授の程よろしくお願いします。

環境;WinXP, Visual Stadio2005, VC++ Win32

金魚ちゃん 2008/09/17(水) 17:23:20
>現在の状況としては、なんとか画面拡大はでき下のウィンドウにも伝わっているには伝
>わっている状態なんですが、
>拡大画面からだと、直接下のウィンドウへマウスメッセージがいってしまい、反映され
>ません。
クライアント座標とスクリーン座標に矛盾がありませんか?
つまりスクリーン座標なのにクライアント座標として使っている。
またはその逆のパターン。

とんぼ 2008/09/17(水) 17:54:46
>クライアント座標とスクリーン座標に矛盾がありませんか?
>つまりスクリーン座標なのにクライアント座標として使っている。

そうなんですよね。
スクリーン座標とクライアント座標で…というところだとは思うのですが^^;

実際、拡大画面上で操作はしていても、全てのマウスイベントが素通りして、
直接下のウィンドウに届くような感じで、

クライアント座標からスクリーン座標へと マウスイベントをどう伝えればいいのかわか
らない状態です。

質問の仕方が悪くて、すいません。

金魚ちゃん 2008/09/17(水) 18:08:05
>クライアント座標からスクリーン座標へと マウスイベントをどう伝えればいいのかわ>らない状態です。
単純な変換なら ClientToScreen がありますけど。
http://msdn.microsoft.com/ja-jp/library/cc428312.aspx

具体的にマウスイベントとは何?
WM_MOUSEMOVEとか?

とんぼ 2008/09/17(水) 18:32:17
>>金魚ちゃんさんへ

>具体的にマウスイベントとは何?
>WM_MOUSEMOVEとか?

主にクリック動作です。
拡大画面上でそのまま操作でき、下のウィンドウにも反映させるようにしたいので、
マウスの動きそのもの(?)が入ってくるのかもしれませんが。

金魚ちゃん 2008/09/18(木) 18:26:54
>拡大画面上でそのまま操作でき、下のウィンドウにも反映させる
1.マウス座標がウインドウ領域内に入ったら普通のウインドウに戻す
2.マウス処理を受け取ったら
 a.レイヤード・ウインドウに設定する
 b.SendInput などを使ってマウスイベントをウインドウ直下に伝える
 c.普通のウインドウに戻す
3.マウス座標がウインドウ領域外から出たらレイヤード・ウインドウに設定

こんな感じでどうですか。
(3)はタイマーなどを使ったウインドウ領域内/外を監視したり、
ウインドウ領域外は WM_MOUSELEAVE を使っても良いですね。

とんぼ 2008/09/19(金) 01:14:38
金魚ちゃんさん、ありがとうございます。

>1.マウス座標がウインドウ領域内に入ったら普通のウインドウに戻す
ということは、拡大ウィンドウ内に入ったら拡大画面は消えてしまうということです
か?


>2.マウス処理を受け取ったら
> a.レイヤード・ウインドウに設定する
> b.SendInput などを使ってマウスイベントをウインドウ直下に伝える

そして、その通常のウィンドウ上で何か操作したときに拡大表示→ウィンドウ直下に
伝える!!みたいな感じで、操作直前は拡大画面が見えなくなるのでしょうか?

rin 2008/09/19(金) 09:51:17
http://www.microsoft.com/japan/msdn/windows/windows2000/layerwin.aspx

>ヒット テスト
>レイヤード ウィンドウのヒット テストは、ウィンドウの形状と透明度に基づいていま
す。
>これは、色分けされているウィンドウの領域やアルファ値がゼロの領域は、
>マウス メッセージが透過することを意味します。

>レイヤード ウィンドウが WS_EX_TRANSPARENT 拡張ウィンドウ スタイルを持つ場合、
>レイヤード ウィンドウの形状は無視され、
>マウス イベントはレイヤード ウィンドウの下の他のウィンドウに渡されます。

これかな?

PATIO 2008/09/19(金) 10:12:16
ふと思ったんですが、拡大表示しているわけなので
実際の拡大表示用のウインドウ内の座標を拾った後、
元の画面ではどの位置になるのかを計算してから
マウスイベントを投げないと駄目なんじゃないかと言う気がしますね。

拡大表示上の位置ですから、その辺は考える必要があるのでは?
例えばですが、拡大画面の中心位置と下の元のウインドウ上の位置が
合っていて、拡大表示をしていると考えます。
2倍に拡大してい場合、拡大画面上での中心位置からの距離が18ピクセル
だったとすると、実際の画面上では9ピクセルの距離になるはずですよね。
なので、拡大画面上の位置を実際の画面上の位置に計算し直して
通知しないといけないのではないかと思います。

既にこの辺の考慮はしているよと言う話ならスルーしてください。

金魚ちゃん 2008/09/19(金) 15:17:02
次の参考ページを見つけました。
http://www.sutosoft.com/mt/devdiary/archives/000050.html
ここにレイヤード・ウインドウを2枚使って
マウス処理を受け取る方法が書かれています。

これで受け取ったら下のレイヤード・ウインドウのα値を 0 にして
座標の計算をPATIOさんのアドバイスを元に計算。
そして SendInput などでマウスイベントを発生させれば出来そうな気が…。

あと過去に似たような質問があったよ。
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200707/07070026.txt
座標のずれはこれかもね。

興味があるので上手くいったら結果報告をお願いします。
(明日は暇になるからちょっと作ってみるつもり)

とんぼ 2008/09/19(金) 18:10:59
rinさん、PATIOさん、金魚ちゃんさん、ご丁寧にありがとうございます。

まだまだ勉強不足でわからないことも多く…、
すぐにはちゃんと報告できないと思いますが、これから頑張ってやってみたいと思いま
す!!

皆さん、ありがとうございます><

とんぼ 2008/09/21(日) 12:14:00
遅くなってすいません。とんぼです。

金魚ちゃんさんのアドバイスどおり、
レイヤードウィンドウを2枚重ねてというところまではできました。

いちおその透明ウィンドウ上(α値が1のとき)で、左クリックをした場合など
条件をつけたマウス処理は受け取れていることが確認できました。

そこでなんですが、マウス処理全般を受け取ってイベントを発生させるには
どういった条件をつければいいのでしょうか?
(拡大画面上でもそのままの操作可能にするため)

例えば、透明ウィンドウ上でメッセージWM_MOUSEMOVEを受け取り
if(wp & MK_LBUTTON)だったらという形で上記の確認はとれましたが、
このような形でいいのでしょうか。

そうすると、ダブルクリックのとき…など、条件分けをいろいろしなければならないん
ですよね?

PATIO 2008/09/22(月) 17:32:16
単純にマウス系の処理を下のウインドウにスルーしたいだけなら
マウス系のウインドウメッセージを全て調べて対応するメッセージを
処理すれば良いだけだと思います。
WM_XXXの部分はどのウインドウでも同じはずですし。
特にこの操作のときは特別な処理をする必要があると言う話なら
その操作に対応したメッセージ処理を場合分けする必要があるでしょうけれど。

私のイメージでは処理する必要があるメッセージに関しては
マウスの座標を拾って拾った座標から下のウインドウ上の座標値を計算し、
ウインドウメッセージの種類はそのまま使ってメッセージを送れば良いのでは
ないかと思います。
この辺は実際にやって見て確認した方が良いと思います。

追伸:
メッセージの種類によって座標を取り出す処理が異なる時は
場合分けも必要になるかもしれませんね。
この辺は御自分で調べて見てください。

とんぼ 2008/10/09(木) 06:22:36
遅くなった上にまだ出来ていないのですが…

イベントメッセージを送る際、SendInputの使い方がよくわからず
SendMessage()で送っているのですが、どうしてもその座標にメッセージを送ることが出
来ません。

拡大画面上でクリックを検知後、レイヤードウィンドウのα値を0にし、
対応する座標(fact.x, fact.y)を計算して↓
SendMessage(NULL, WM_LBUTTONDOWN, 0, MAKELPARAM(fact.x, fact.y));
SendMessage(NULL, WM_LBUTTONUP, 0, MAKELPARAM(fact.x, fact.y));
という形にしているのですが、これでは送れないのでしょうか?

また、画面の中心の座標なんですが
GetSystemMetrics(SM_CXMAXTRACK)、GetSystemMetrics(SM_CYMAXTRACK)で
取得した画面のサイズ÷2で中心の座標にもなるんですよね?

初歩的な質問ですみません。

PATIO 2008/10/10(金) 16:04:20
SendMessageがWin32APIならウインドウハンドルはちゃんと指定しないと
狙い撃ちには出来ないと思います。
送付先のウインドウハンドルが必要なのでは?

PATIO 2008/10/10(金) 16:10:49
画面中心座標についてですが、
拡大画面と元の画面との関係で元の画面のどの位置が拡大画面のどの位置に一致するか
と言う部分が問題になります。
なのでそのPCの画面の中心を基準にして良いのかが今の書き込まれた情報からでは
判断出来ません。拡大画面はPCの画面上の中心位置に固定なのですか?
自由に動かせるのであれば、PCの画面の中心を求めても意味がない事になりますが。
これはこの辺の処理を書いているご本人にしか分からない部分になりますので
よく確認してみてください。
絵で書くとわかりやすいんですが、こういう掲示板では無理なので。

とんぼ 2008/10/10(金) 23:35:16
PATIOさん、お返事ありがとうございます。

>送付先のウインドウハンドルが必要なのでは?

そうですよね…^^;
でもそのウィンドウハンドルというのは、拡大画面の直下にあるウィンドウに
メッセージを伝える場合は、直下にあるウィンドウハンドルを調べて指定するというこ
とですか?
それとも透明ウィンドウのハンドルを指定して、メッセージを送りつつ
α値を0にしてしまえば、そのメッセージがそのまま通り抜けて下へいくということはな
いですか…ね?

見当違いなこと言ってたらすみません。


あと、画面の中心座標についてですが
今現在は拡大画面は全画面表示の固定してある状態で、元の画面をクリックした際に
そのクリックした座標を中心として、拡大画面を表示するようにしています。

つまり元の画面のクリック位置が、拡大画面の中心座標となって表示されている状態で
す。

PATIO 2008/10/14(火) 10:28:02
> でもそのウィンドウハンドルというのは、拡大画面の直下にあるウィンドウに
> メッセージを伝える場合は、直下にあるウィンドウハンドルを調べて指定する
> ということですか?

そうなりますね。基本的に送付先のウインドウハンドルが必要になると思います。
透過の時に云々と言う部分はOS内部の話ではないかと思うのでプログラムから
ウインドウメッセージを送信する場合は多分関係無いのではないかと思います。


> 今現在は拡大画面は全画面表示の固定してある状態で、元の画面をクリックした際に
> そのクリックした座標を中心として、拡大画面を表示するようにしています。
> 
> つまり元の画面のクリック位置が、拡大画面の中心座標となって表示されている状態です。

拡大画面が全画面表示になっていても拡大画面自身にウインドウサイズを問合せるように
した方が良いと思います。拡大画面のサイズを可変にした場合でもサイズ取得の部分の
処理を変更しなくて良いようになりますから。
あと、クリックする度に元の画面上の位置が更新されるのであれば、
クリック後はクリックした所が中心位置に来ているはずなので
画面の移動処理を行ってからその時の中心位置に向かって投げると言う手もあるかなと
思います。

とんぼ 2008/10/19(日) 16:00:44
PATIOさん、ありがとうございます。

送付先のウィンドウハンドルは、WindowFromPoint(point)で
座標(point.x,point.y)にあるウィンドウを取得してメッセージを送るようにしました。


>拡大画面が全画面表示になっていても拡大画面自身にウインドウサイズを問合せるよう
>にした方が良いと思います。拡大画面のサイズを可変にした場合でもサイズ取得の部分
>の処理を変更しなくて良いようになりますから。

というのは
#define WND_W GetSystemMetrics(SM_CXMAXTRACK)
#define WND_H GetSystemMetrics(SM_CYMAXTRACK)
でサイズを定義してあるので、これでも大丈夫でしょうか?


>あと、クリックする度に元の画面上の位置が更新されるのであれば、
>クリック後はクリックした所が中心位置に来ているはずなので
>画面の移動処理を行ってからその時の中心位置に向かって投げると言う手もあるかなと
>思います。

そうですね!そういう手もありますね!
でも1回目のクリックで画面を拡大表示後は、クリックしても画面の中心位置は変わら
ないようになっています。

今は、計算があっていないのかうまくその座標へメッセージを飛ばせてないんですが、
ちゃんと反映するように頑張ってみます。

PATIO 2008/10/20(月) 10:32:16
>> 拡大画面が全画面表示になっていても拡大画面自身にウインドウサイズを問合せるよう
>> にした方が良いと思います。拡大画面のサイズを可変にした場合でもサイズ取得の部分
>> の処理を変更しなくて良いようになりますから。
>
> というのは
> #define WND_W GetSystemMetrics(SM_CXMAXTRACK)
> #define WND_H GetSystemMetrics(SM_CYMAXTRACK)
> でサイズを定義してあるので、これでも大丈夫でしょうか?

いえ、これだとシステムの値を取得していますよね。
これだと拡大画面のサイズをユーザーが調整できるようにした時に
実際の拡大画面のサイズと上記の値は違っているはずなので拡大画面のサイズを
取得したいと言う目的を達成できません。
拡大画面に対しての問い合わせになりますから
拡大画面のウインドウハンドルを使ってGetWindowRectで問合せてはどうですか
と言う話です。拡大画面は最大化された状態で固定なら今能状態でも良いと思いますが、
拡大画面のサイズを可変にしたりするのであれば、拡大画面自身に問い合わせた方が
柔軟になるかなと考えただけです。
これに関しては現状、拡大画面が最大化サイズで固定なら現状でも結果は同じになるので
その辺はどうしたいかで判断してもらって良いと思います。

PATIO 2008/10/20(月) 10:33:51
誤字(^^;

誤)
>と言う話です。拡大画面は最大化された状態で固定なら今能状態でも良いと思いますが、

正)
と言う話です。拡大画面は最大化された状態で固定なら今の状態でも良いと思いますが、

とんぼ 2008/10/21(火) 15:55:37
PATIOさん、ご丁寧にありがとうございます。

今のところは拡大画面を最大化した状態で固定しておこうと思っていたので
まだ考えていませんが、余裕ができたらやってみようと思います^^


あと、メッセージも無理やりのような気がしますが、直下のウィンドウに送ることがで
きました。

拡大ウィンドウ上でクリック等のメッセージを受け取り、
元の画面に対応する座標(fact.x, fact.y)を計算後↓

SetLayeredWindowAttributes(hwnd,0,0,LWA_ALPHA);
LPARAM dwExtraInfo = GetMessageExtraInfo();
SetCursorPos(fact.x, fact.y);
mouse_event(MOUSEEVENTF_LEFTDOWN, fact.x, fact.y, 0, dwExtraInfo);
mouse_event(MOUSEEVENTF_LEFTUP, fact.x, fact.y, 0, dwExtraInfo);
SetCursorPos(p.x, p.y);

のような感じで、ポインタを移動させて…。
mouse_event()でも座標を指定しているはずなのですがSetCursorPosをやらないと
うまくいかず、反応も少し遅いです。

MOUSEEVENTF_ABSOLUTEではという前スレもありましたが、そうするとクリック動作は指
定できないんですよね?
何かいい方法等ありましたらご教授お願いします。

PATIO 2008/10/21(火) 17:18:42
多分ですが、mouse_eventだとそのウインドウにフォーカスが無いと
メッセージが行かないと思います。
なのでそのウインドウにフォーカスを当てると送れるとは思いますけれど、
遅いのはなぜだろう?

あと、MOUSEEVENTF_ABSOLUTEを指定しないなら直前の位置からの移動量を
指定する事になっているようなのでうまくいっているのかなぁと。

今提示されている範囲では分からないです。
何処の処理で滞っているのかが見えないので。

ちなみにですが、PostMessageで送っても駄目でした?
WM_LBUTTONDOWNとWM_LBUTTONUPで同じような事は出来ませんか?
これだとウインドウを狙い撃ちに出来ると思うのですけれど。
非同期でメッセージを送るならPostMessageでも良いと思います。
SendMessageは同期になるので。

この辺はmouse_eventの仕様もあわせて確認した方が良いですね。

とんぼ 2008/10/21(火) 20:10:46
遅くなるのはもしかしたら、拡大画面を表示するときに
ミリ秒単位で描画表示し続けているせいかもしれないです。


>ちなみにですが、PostMessageで送っても駄目でした?
>WM_LBUTTONDOWNとWM_LBUTTONUPで同じような事は出来ませんか?
>これだとウインドウを狙い撃ちに出来ると思うのですけれど。
>非同期でメッセージを送るならPostMessageでも良いと思います。
>SendMessageは同期になるので。

PostMessage(WindowFromPoint(fact), WM_LBUTTONDOWN, 0, MAKELPARAM(fact.x, 
fact.y));
PostMessage(WindowFromPoint(fact), WM_LBUTTONUP, 0, MAKELPARAM(fact.x, 
fact.y));
こんな風にしてもダメでした。
というか、なぜか対応するはずの座標がずれてしまい…座標は合ってると思うのです
が。

でも自分でも、もう少し調べてみます。

PATIO 2008/10/22(水) 11:53:05
mouse_eventの場合、MOUSEEVENTF_ABSOLUTEが無いと直前のマウスの位置からの
移動量になると思います。同じ値をmouse_eventに使ってうまく行っているのであれば、
値的には間違っている可能性があると思います。

WM_LBUTTONDOWN等の場合、クライアント座標で見た時の位置になるはずなので
下のウインドウのクライアント座標を計算する必要が有ります。
クライアント座標の基準はクライアント領域の左上を(0,0)としますから
それに合っているかですね。

とんぼ 2008/10/22(水) 14:15:01
[[解決]]
>WM_LBUTTONDOWN等の場合、クライアント座標で見た時の位置になるはずなので
>下のウインドウのクライアント座標を計算する必要が有ります。
>クライアント座標の基準はクライアント領域の左上を(0,0)としますから
>それに合っているかですね。

PATIOさん、その通りにやったらできました!

Q.x = fact.x;
Q.y = fact.y;
//スクリーン⇒クライアント座標へと変換
ScreenToClient(WindowFromPoint(fact), &Q);//クライアント座標へと変換
PostMessage(WindowFromPoint(fact), WM_LBUTTONDOWN, 0, MAKELPARAM(Q.x, Q.y));
PostMessage(WindowFromPoint(fact), WM_LBUTTONUP, 0, MAKELPARAM(Q.x, Q.y));

と、このような感じでちゃんと対応する座標にもメッセージ転送することもできまして
^^
これからクリック以外のメッセージも場合分けして送れるようやってみるつもりです。
rinさん、PATIOさん、金魚ちゃんさん、コメント頂きありがとうございました!

そして長々と最後までお付き合い頂いたPATIOさんには本当に感謝しておりま
す!!
また何かご質問させていただくこともあるかと思いますが、その時はよろしくお願い致
しますm(__)m

古いスレッドには追加発言できません。