|
|
2013/9/5(Thu) 17:06:06|NO.56900
マウスを操作するソフトのバグ修正中です。
連打やマウス座標のキー操作などを行うプログラムですが、
mouse_event命令によるクリックでHSPウィンドウがドラッグされると
処理が停止。それによって予期せぬバグが発生した・・・
ところまでは分かりましたが、対処法が難しくてできません。
前回似たような質問を出させてもらったのですが、
最終的にwinAPIのタイマーを呼び出す・・・
もらったサンプルを色々試すのですが、winAPIの関数を調べたり
HSP上での動作の仕方。
1フレームでも予想からずれると先のバグが発生してしまう。
と僕には無理なのかな・・・と考えさせられます。
そもそもwinAPIまで手を出すつもりはなかったので迷ってます。
winAPIのタイマーを使う方法以外にないかなと考えて、
では、HSPウィンドウ内にマウスが位置しているかどうかを
判定できないかと思いまして。
(ややこしくて理解しきれない)
他にもタイマーを使わないmouse_eventのバグの解決方法があれば
ご教授お願いします。
|
|
2013/9/5(Thu) 18:48:13|NO.56901
質問の要約はこれであっていますか?
・自分のウインドウがドラッグされる間プログラム(ループ)が停止する
・対処法としてループをSetTimerによって実装する方法があるが挫折した
・代替手段としてマウスポジションによるウインドウ判定を考案したが方法が分からない
waitを挟んだループがドラッグによって止まるのはHSPの仕様なのでmouse_eventのバグではありません。
ただ、1フレームでも予想からずれると…のくだりが気になるので
念のため、再現性のあるソースコードを公開していただけると回答しやすいです。
マウス座標からウインドウを判定するにはWindowFromPointを使う方法がありますが、
WindowsAPIなので本末転倒になるでしょうか。先に書いたトラブルならSetTimerの使用を推奨します。
確認が取れ次第サンプルコードを書きましょうか。
|
|
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
|
|
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
| |
|
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
| |
|
2013/9/6(Fri) 01:19:58|NO.56911
(3dsからの書き込み
皆さん色々ありがとうございます。
僕の使えそうなものは2つほど
出ているみたいですね。
記述漏れでしたが、
winAPIを使うに当たって
僕のツールは枠無しで初期化して
長方形のwin8のような感じにしているので
多少プログラムも変わるのかなと思います
|
|
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
|
|
2013/9/7(Sat) 00:04:09|NO.56950
(3dsから
まだ、理解には至っていませんが、
色々な方法にサンプルも頂けたので
あとはなるたけ自力で頑張ってみたいです。
皆さんどうもありがとうございました
|
|