HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2013
0905
roppyakukeeマウスがHSPウィンドウ内にいるか判定したい7解決


roppyakukee

リンク

2013/9/5(Thu) 17:06:06|NO.56900

マウスを操作するソフトのバグ修正中です。
連打やマウス座標のキー操作などを行うプログラムですが、
mouse_event命令によるクリックでHSPウィンドウがドラッグされると
処理が停止。それによって予期せぬバグが発生した・・・
ところまでは分かりましたが、対処法が難しくてできません。

前回似たような質問を出させてもらったのですが、
最終的にwinAPIのタイマーを呼び出す・・・
もらったサンプルを色々試すのですが、winAPIの関数を調べたり
HSP上での動作の仕方。
1フレームでも予想からずれると先のバグが発生してしまう。
と僕には無理なのかな・・・と考えさせられます。
そもそもwinAPIまで手を出すつもりはなかったので迷ってます。

winAPIのタイマーを使う方法以外にないかなと考えて、
では、HSPウィンドウ内にマウスが位置しているかどうかを
判定できないかと思いまして。
(ややこしくて理解しきれない)



他にもタイマーを使わないmouse_eventのバグの解決方法があれば
ご教授お願いします。



この記事に返信する


ht.

リンク

2013/9/5(Thu) 18:48:13|NO.56901

質問の要約はこれであっていますか?
・自分のウインドウがドラッグされる間プログラム(ループ)が停止する
・対処法としてループをSetTimerによって実装する方法があるが挫折した
・代替手段としてマウスポジションによるウインドウ判定を考案したが方法が分からない

waitを挟んだループがドラッグによって止まるのはHSPの仕様なのでmouse_eventのバグではありません。
ただ、1フレームでも予想からずれると…のくだりが気になるので
念のため、再現性のあるソースコードを公開していただけると回答しやすいです。

マウス座標からウインドウを判定するにはWindowFromPointを使う方法がありますが、
WindowsAPIなので本末転倒になるでしょうか。先に書いたトラブルならSetTimerの使用を推奨します。
確認が取れ次第サンプルコードを書きましょうか。



roppyakukee

リンク

2013/9/5(Thu) 19:24:24|NO.56902

質問の要約、それであってます。


まえに頂いたサンプルをそのまま掲載します。
このさんぷるは、wキーでクリック、qキーでマウス移動速度アップ
といった僕の作っているツールに似せて作ってもらったものです。
wキードラッグ中もHSPが動作を続行するものですが、色々と穴があったみたいで。

#include "user32.as" #define WM_TIMER 0x0113 #define TIMER_ID 1 ;getkey改良 #module #undef getkey #deffunc getkey var p1,int p2 getkey@hsp key(245),p2 p1=key(245)*2-key(p2) key(p2)=key(245) return #global ;windowにWM_TIMERが送られてきたらジャンプ ;windowにwm_timerを定期的に送るのはSETTIMER ;必要なくなればKILLTIMERで破棄すること ;TIMER_IDは任意の数値。複数タイマーを使う時に。 oncmd gosub *OnTimer, WM_TIMER onexit goto *OnQuit SetTimer hwnd, TIMER_ID, 10, 0 repeat getkey w,87 if w=2 {mouse_event $2,0,0,0,0} if w=-1 {mouse_event $4,0,0,0,0} getkey L,1 title strf("cnt=%d w=%d L=%d",cnt,w,L) if L=1 and ginfo_act=0 {sendmsg hwnd,$00A1,2,0}//ウィンドウを動かすか何かキー操作されるまでタイマー割り込みが停止する感じ await 16 loop stop *OnTimer if wparam = TIMER_ID { if L=1 { getkey w,87 if w=-1 {mouse_event $4,0,0,0,0} } } tim++ color boxf 0,0,200,20 color 255 pos 0,0 mes strf("TIM=%d w=%d L=%d",tim,w,L) return 0 *OnQuit KillTimer hwnd, TIMER_ID end



FunnyMaker

リンク

2013/9/5(Thu) 21:32:28|NO.56905

マウスがHSPウィンドウ内にいるか判定する方法は、こんな感じでどうでしょうか?
WinAPIを使っていますが、発想自体シンプルなもので、
マウスカーソルの直下にあるGUIオブジェクトのウィンドウハンドルを調べ、それがHSPの特定のウィンドウ
(ウィンドウ0,1,2,・・・ 各ウィンドウ個別に検出できるのでご心配なく。)のものと一致するかどうかを調べています。
もちろん、一致すれば「マウスがHSPのウィンドウ内にいる」ということです。

コメントアウトされている部分は不要なAPI関数や定数です。削除してかまいません。
下のスクリプトを理解できたら、あなたのプログラムにこの手法を取り込めばOKです。


;< API関数群の利用手続 > #uselib "user32.dll" #cfunc WindowFromPoint "WindowFromPoint" int,int ;#cfunc GetWindow "GetWindow" int,int ;#cfunc GetParent "GetParent" int #cfunc GetAncestor "GetAncestor" int,int ;< API関数オプション用定数群のセットアップ > ;#define GW_HWNDNEXT 2 ;#define GW_HWNDPREV 3 ;#define GW_CHILD 5 ;#define GW_HWNDFIRST 0 ;#define GW_HWNDLAST 1 ;#define GW_OWNER 4 ;#define GA_PARENT 1 ;親ウィンドウを返す #define GA_ROOT 2 ;親子関係を遡って、直近上位のトップレベルウィンドウを返す #define GA_ROOTOWNER 3 ;親子関係と所有関係を遡って、所有されていないトップレベルウィンドウを返す *sart screen 0,600,400,0 hwnd_scr0 = hwnd ;ID0ウィンドウのウィンドウハンドルを取得 font msgothic,50,16 ;< 初期描画 > color 0,0,0 pos 10,100 : mes ":-)" ;< 各種変数の初期化 > hwnd_under_mouse = 0 ;マウスカーソル直下のGUIオブジェクトのウィンドウハンドル hwnd_top = 0 ;↑のGUIオブジェクトが属するトップレベルウィンドウのウィンドウハンドル IsMouseOnScr0 = 0 ;マウスカーソルがID0のウィンドウを狙っているか? *loop1_main1 repeat x_mouse = ginfo(0) : y_mouse = ginfo(1) ;マウスカーソルの、デスクトップ座標系における座標を取得 hwnd_under_mouse = WindowFromPoint(x_mouse,y_mouse) ;マウスカーソル直下のGUIオブジェクトのウィンドウハンドルを取得 hwnd_top = GetAncestor(hwnd_under_mouse,GA_ROOT) ;そのGUIオブジェクトが属するトップレベルウィンドウのウィンドウハンドルを取得 ;※何で「マウスカーソル直下のGUIオブジェクトのウィンドウハンドル」そのままじゃだめなの?↓ ; [Answer] それはHSPのウィンドウに属しているボタンやリストボックスなどのGUIオブジェクトのものかもしれないでしょ?(※もちろん、マウスがウィンドウを直で触っている時ならそのままで良いのだが) ; それらのウィンドウハンドルはHSPのウィンドウのものとは違う。だから、それらの主人、すなわち目的のHSPのウィンドウのウィンドウハンドルまでちゃんと遡って調べる必要がある。 ; だから、GetAncestor関数を使う。 if hwnd_top = hwnd_scr0 { if IsMouseOnScr0 = 0 { ;狙われていることが判明した場合(※ hwnd_top = hwnd_scr0 かつ IsMouseOnScr0 = 0 ってことは、"今"「状況が変わった」ということ。) IsMouseOnScr0 = 1 ;フラグをコレクト(修正)する ;< 画面更新 > redraw 0 color 0,0,255 : boxf color 255,255,255 pos 10,100 : mes "何触ってんだよ?" redraw 1 } } else { if IsMouseOnScr0 = 1 { ;狙われていないことが判明した場合 IsMouseOnScr0 = 0 ;フラグをコレクトする ;< 画面更新 > redraw 0 color 255,255,255 : boxf color 0,0,0 pos 10,100 : mes ":-)" redraw 1 } } await 10 loop



ht.

リンク

2013/9/5(Thu) 22:58:02|NO.56910

見たところ、ドラッグされる間停止する問題は既に解決しているようで
> ウィンドウを動かすか何かキー操作されるまでタイマー割り込みが停止する感じ
タイトルバーでキーが押された後に発生するこの挙動が悩みの種というわけですね。
SetTimerが働かないレベルの停止状態については正直原因が分からないのですが、
WM_NCLBUTTONDOWNが処理されずにDefWindowProcに渡されるとこのような遅延が起きる仕様っぽいですね。
そうなると、私が思い当たる限りの対処法は次の二つです。
・ウインドウを表示するスレッド(プロセス)とキー操作を行うスレッドを別々にする
・キー操作している間は自分のウインドウのタイトルバーへのクリックを禁止する
もっとスマートな方法があるのかもしれませんが。

前者は多少高度な技術が必要になるので後者のサンプルを書いてみました。
wキーで実際にマウス押下できるようにしています。

#include "user32.as" #define WM_TIMER 0x0113 #define WM_NCLBUTTONDOWN 0x00A1 #define TIMER_ID 1 ;getkey改良 #module #undef getkey #deffunc getkey var p1,int p2 getkey@hsp key(245),p2 p1=key(245)*2-key(p2) key(p2)=key(245) return #global oncmd gosub *OnTimer, WM_TIMER oncmd gosub *OnNCLButtonDown, WM_NCLBUTTONDOWN onexit goto *OnQuit SetTimer hwnd, TIMER_ID, 10, 0 pos 10, 100 : button gosub "button", *OnButton stop *OnButton title "button:"+btncnt btncnt++ return *OnNCLButtonDown title "タイトルバーがクリックされた:"+ncldcnt ncldcnt++ if kill_drug = 1 { w_on_caption = 1 // 自分のウインドウのタイトルバーがクリックされた return 0// これでドラッグを受け付けないことはできる } else { return } *OnTimer if wparam = TIMER_ID { getkey L,1 getkey w,87 if w=2 { mouse_event $2,0,0,0,0 // left down kill_drug = 1 } if w=1 & w_on_caption = 1 { // ここを有効にするとwキーによる擬似的なドラッグを自前で行うようにする ;x = ginfo_mx - mx ;y = ginfo_my - my ;width -1, -1, ginfo_wx1 + x, ginfo_wy1 + y } if w=-1 { mouse_event $4,0,0,0,0 // left up kill_drug = 0 w_on_caption = 0 } mx = ginfo_mx my = ginfo_my tim++ color boxf 0,0,200,20 color 255 pos 0,0 mes strf("TIM=%d w=%d L=%d",tim,w,L) } return 0 *OnQuit KillTimer hwnd, TIMER_ID end



roppyakukee

リンク

2013/9/6(Fri) 01:19:58|NO.56911

(3dsからの書き込み
皆さん色々ありがとうございます。
僕の使えそうなものは2つほど
出ているみたいですね。


記述漏れでしたが、
winAPIを使うに当たって
僕のツールは枠無しで初期化して
長方形のwin8のような感じにしているので
多少プログラムも変わるのかなと思います



fortunehill

リンク

2013/9/6(Fri) 01:33:48|NO.56912

/*ご参考*/

#uselib "user32.dll" #func global TrackMouseEvent "TrackMouseEvent" sptr oncmd gosub,*label_1,$200 ;WM_MOUSEMOVE oncmd gosub,*label_2,$2A3 ;WM_MOUSELEAVE oncmd gosub,*label_3,$2A1 ;WM_MOUSEHOVER stop *label_1 : nn = 16,1,hwnd,1000 :TrackMouseEvent varptr(nn) ;TME_LEAVE : nn = 16,2,hwnd,1000 :TrackMouseEvent varptr(nn) ;TME_HOVER : title strf("X = %4d,Y = %4d",mousex,mousey) :return *label_2 : title "[OutSide !]" :return *label_3 : title "[Stopping!]" :return



roppyakukee

リンク

2013/9/7(Sat) 00:04:09|NO.56950

(3dsから

まだ、理解には至っていませんが、
色々な方法にサンプルも頂けたので
あとはなるたけ自力で頑張ってみたいです。

皆さんどうもありがとうございました



ONION software Copyright 1997-2023(c) All rights reserved.