«前の日記(2005-09-12 [月]) 最新 次の日記(2005-10-01 [土])» 編集
RSS: href="http://endoh-namazu.tierra.ne.jp/diary/index.rdf"


半期 四半期 全カテゴリ

新・なまず日記


2005-09-16 [金]

_ [デスクトップ][カスタマイズ][AHK]高橋名人志願

昔、テレビゲームの名人で、高橋名人という方がいらっしゃいました。得意技は16連射。1秒間に16回もボタンが押せるのです。余りに激しい連射でスイカも割れるという。なまずはせいぜい一秒間に4回だから、高橋名人は素晴らしいですね。何の話かって?キーバインドの話です。

キーストロークに機能を割り当てるときは、大抵、Ctrlキーを押しながらfとか、Altキーを押しながらtなどのように、コンビネーションで割り当てますが、CtrlやAltやShiftのようないわゆる修飾キーを「素早く2度押す」「素早く3度押す」といったことに機能を割り当てるのも、なかなかオツなものです。そういった修飾キーは常に指にふれているものだし、カタカタッとやるだけですから操作がシンプルで習得が速く、また片手のみで操作できるという利点もあります。

AutoHotkey では、上記のような2度押し、3度押しを取り扱う特別な機能はありませんが、スクリプトをちょろっと書けば簡単に検出することができます。いろいろな検出方法があるのですが、以下はなまずが気に入っているやり方で、一つのタイマハンドラを使って任意の回数の複数押しを検出することができます。

; Altキーが押されたことを検出する
~Alt::
  SetTimer, Alt_CheckCnt, 300
  Alt_cnt += 1

if(Alt_cnt == 4) { ; 4回押しの時の機能を書く } return
; Altキーが押されてから一定時間押されていないことを検出する Alt_CheckCnt: Hotkey, ~Alt, Off ; ここでAltが押されるとややこしいことになるので
SetTimer, Alt_CheckCnt, Off
if(Alt_cnt == 2) { ; 2回押しの時の機能を書く } else if(Alt_cnt == 3) { ; 3回押しの時の機能を書く }
Alt_cnt = 0 Hotkey, ~Alt, On return

この例は、Altキーを素早く2回、3回、4回押したことを検出する処理を示したものです。先頭の、"SetTimer, Alt_CheckCnt, 300"の、"300"は、0.3秒ということで、Altキーを押した後、この時間内に、もう一度Altキーを押せば、n回押されたと認識します。0.3秒以上、次のAltキーが押されなかったら、それまでに押された回数に応じた機能を実行する、というわけです。

逆に言うなら、「3回押しじゃなくて2回押し」ということを認識するためには、2回押した後に、0.3秒待たなければならない、ということです。Altキーを2回カタカタッと押したあと、0.3秒待つと、2回押しに設定された機能が実行されるわけです。つまり、この秒数を短くすればするほど、キーをカタカタした後、反応よく機能が実行されるのですね。しかし、秒数を短くすれば、よっぽど素早くキーをカタカタしないと、複数押しと認識されないのです。

なまずの指では、この秒数は、0.23秒(230)が限界でした。どんなにがんばっても、これ以上連射できない。高橋名人だったら、0.063秒(63)まで設定できるのですから、名人がうらやましい!ちなみに、AutoHotkeyの仕様では、NT系OSだったら、0.01秒(10)が限界値だそうです。

「高橋名人にはなれないけれど、反応は早い方がいいなぁ」という方は、ちょっと発想を変えてみましょう。例えば、Altキーを2回カタカタした後、ひょっとしたら3回目が押されるかもしれないけれど、いいや、2回の機能を実行しちゃおう、ということにすれば、3回目が押されなかったことを確認するための秒数が必要なくなり、反応は良くなります。しかし、この場合、3回押し、4回押しいずれの場合も、とりあえず2回押しの機能は実行してしまう、というデメリットは当然避けられません。3回押しの場合は絶対に2回押しの機能を実行してくれては困る、という場合は、この方法は採用できません。複数押しに設定する機能による、ということですね。

「いいよ、2回押しの機能はいつも起動されても。やっぱり2回押しの機能は反応が良いほうがいい」という場合に、考えなければならないことは、普通にそのようなコードを書いてしまうと、2回押しの機能と、例えばその後の3回押しの機能とが、マルチスレッドで同時実行することになる、ということです。だって、2回押しの機能を実行している時に、次のAltキーが押されたかどうかを検出しなければならないのだから、この時点で既にマルチスレッドなのですね。もちろん、2回押しの機能と、3回押しの機能とが、まったく関係のない機能であれば、同時実行しても問題はありませんが、もし、二つの機能が、同じ変数を参照したり、その変数を変更したりすると、タイミングによってはかなりややこしいバグを引き起こします。特に、AutoHotkeyでは、マルチスレッドを調停するための、例えばセマフォのような機能がないので、この問題を回避するには容易ではありません。もうちょっと工夫が必要だということです。

と、いうことで、上記の事情を考慮したサンプルコードを書いてみました。

; Altキーが押されたことを検出する
~Alt::
  SetTimer, Alt_CheckCnt, 300
  Alt_cnt += 1

ALT_JobStackCnt := Mod(ALTE_Tail, 8) ; リングバッファの位置を設定
if(Alt_cnt == 2) { ALTE_JobStack_%ALT_JobStackCnt% := Alt_cnt ALTE_Tail += 1 } else if(Alt_cnt == 4) { ALTE_JobStack_%ALT_JobStackCnt% := Alt_cnt ALTE_Tail += 1 } SetTimer, Alt_Execute,0 ; 最短時間で機能実行スレッド(タイマハンドラ)を起動 return
; Altキーが押されてから一定時間押されていないことを検出する Alt_CheckCnt: Hotkey, ~Alt, Off ; ここでAltが押されるとややこしいことになるので
SetTimer, Alt_CheckCnt, Off
if(Alt_cnt == 3) { ; 3回押しの時の機能を書く }
Alt_cnt = 0 Hotkey, ~Alt, On return
; Altキー複数押しに設定された機能を実行する Alt_Execute: SetTimer, Alt_Execute, Off Loop { if(ALTE_Head == ALTE_Tail) break
ALTE_JobStackCnt := Mod(ALTE_Head, 8) ;リングバッファの位置を設定 ALTE_Job := ALTE_JobStack_%ALTE_JobStackCnt% ALTE_Head += 1
if(ALTE_Job == 2) { ; 2回押しの時の機能を書く } else if(ALTE_Job == 4) { ; 4回押しの時の機能を書く } } return

この例では、以下のような機能を実現できます。

  1. 2回押しの機能は、その後にAltキーが押されるかどうかにかかわらず、とりあえず実行。
  2. 3回押しの機能は、4回押しでないことをちゃんと確認してから実行。
  3. 2回押しと4回押しの機能は、同時実行せずに、ちゃんと片方が終わってから、もう片方を実行する。
  4. 3回押しの機能は、2回、4回の機能と同時実行する。

キー検出部とは独立して動作する、機能実行用のスレッド(タイマハンドラ)"Alt_Execute"を用意しました。"Alt_Execute"とキー検出部とは簡単なリングバッファで結合されており、キー検出部は、n回押しを検出したら、それに関連した機能の実行要求をリングバッファにつっこみ、すぐさまキー検出に戻ります。"Alt_Execute"は、リングバッファに溜まった実行要求を読み取り、それに応じた機能を実行します。この構成により、機能の実行が終わるのを待たなくても次のキー検出ができますし、"Alt_Execute"は同時に一つしか起動しませんので、"Alt_Execute"の中に機能を複数書いておけば、それらは同時実行されることなく、ちゃんと一つの機能が終わってから、次の機能が実行されるのです。

リングバッファは8スロット持っていて、8要求保持できますが、この"8"には特に意味はありません。また、バッファがあふれた時の処理は書いていません。気になる方は追加してください。

さて、一つ、応用例を紹介したいと思います。なまずは上記の仕組みを利用して、以下のような機能を割り当てて、毎日使っています。

  1. Altキー2回押しで、アクティブなウインドウを最小化。3回押しでないことを確認せずにすぐ実行。
  2. 1.で最小化したウインドウとその順番を覚えていて、Shiftを押しながらAltキー2回押しで、最小化したのと逆の順番で、ウインドウを元に戻す。
  3. Altキー3回押しで、全ウインドウを最小化。
  4. 3.の状態でもう一度Altキーを3回押しすると、3.で最小化したウインドウを全部元に戻す。

下記が、上記の機能を実現するコードです。

; Altキーが押されたことを検出する
~Alt::
  SetTimer, Alt_CheckCnt, 300
  Alt_cnt += 1

ALT_JobStackCnt := Mod(ALTE_Tail, 8)
if(Alt_cnt == 2) { ALTE_JobStack_%ALT_JobStackCnt% := Alt_cnt ALTE_Tail += 1 } else if(Alt_cnt == 3) { ALTE_JobStack_%ALT_JobStackCnt% := Alt_cnt ALTE_Tail += 1 } SetTimer, Alt_Execute,0 return
; Altキーが押されてから一定時間押されていないことを検出する Alt_CheckCnt: Hotkey, ~Alt, Off ; ここでAltが押されるとややこしいことになるので
SetTimer, Alt_CheckCnt, Off
Alt_cnt = 0 Hotkey, ~Alt, On return
; Altキー複数押しに設定された機能を実行する Alt_Execute: SetTimer, Alt_Execute, Off Loop { if(ALTE_Head == ALTE_Tail) break
ALTE_JobStackCnt := Mod(ALTE_Head, 8) ALTE_Job := ALTE_JobStack_%ALTE_JobStackCnt% ALTE_Head += 1
if(ALTE_Job == 2) { if(!ALTE_AllMin) { WinGet, ALTE_MinWin, ID, A WinMinimize, A ALTCC_MinWinStack = %ALTE_MinWin%,%ALTCC_MinWinStack% } } else if(ALTE_Job == 3) { if(ALTE_AllMin) { WinMinimizeAllUndo ALTE_AllMin := false } else { StringGetPos, ALTE_tmp,ALTCC_MinWinStack,`, StringLeft, ALTE_MinWin, ALTCC_MinWinStack, %ALTE_tmp% WinRestore, ahk_id %ALTE_MinWin% ALTE_tmp +=1 StringTrimLeft, ALTCC_MinWinStack, ALTCC_MinWinStack, %ALTE_tmp% WinMinimizeAll ALTE_AllMin := true } } } return
; Shift+Altキーが押されたことを検出する ~+Alt:: Alt_cnt += 1 SetTimer, SAlt_CheckCnt, 300
if Alt_cnt = 2 { if(ALTCC_MinWinStack == "") return
StringGetPos, SALT_tmp,ALTCC_MinWinStack,`, StringLeft, ALTCC_MinWin, ALTCC_MinWinStack, %SALT_tmp% WinRestore, ahk_id %ALTCC_MinWin% SALT_tmp +=1 StringTrimLeft, ALTCC_MinWinStack, ALTCC_MinWinStack, %SALT_tmp% } KeyWait, Alt ; <<<< 05/10/6追記 return
; Shift+Altキーが押されてから一定時間押されていないことを検出する SAlt_CheckCnt: SetTimer, SAlt_CheckCnt, Off Alt_cnt = 0 return

上記のプログラムはなまずの著作物ですが、複製や改変は自由に行っていただいて 結構です。また、再頒布も、「なまず」の名前と、 URL(http://www.tierra.ne.jp/~aki/diary/)を添えていただければ、ご自由に配布 していただいて結構です。

ヤボな話ですが、このプログラムを利用して生じたいかなる損害に対しても、なまずは責任を負いません。あしからずご了承ください。

あなたのデスクトップでも使っていただけたら望外の幸せです。ではまた!




※05/10/6 追記
上記の、「Shiftを押しながらAlt2度押し」のコードにバグがありました。2度押ししなくても、Shiftを押しながらAltを押し続けていると、2度押しと認識されてしまいます。このため、「KeyWait, Alt」の一行を入れました。

本日のツッコミ(全2件) [ツッコミを入れる]
_ Jkbtnnik (2010-02-26 [金] 06:18)

utERrH この間も俊太郎の詩をお http://www.stlouisbusinesslist.com/business/5021837.htm?info=viagra viagra, 273860015,

_ viagra (2010-03-08 [月] 10:57)

この間も俊太郎の詩をお http://www.stlouisbusinesslist.com/business/5021837.htm?info=viagra viagra %-DDD

[]