今さらAutoHotKeyのすごさを知る

AutoHotKeyというツールの存在は以前から知っていたけど,実際に試してみるまでそのすごさを全然わかっていなかった.これはすごいものだ.デスクトップユーティリティ系のツールがいとも簡単に作れてしまう.

基本的にはahkの拡張子を持つというスクリプトを1つ書いて,そいつを常駐しているAutoHotKey本体に読み込ませて動かすんだけど,このahkスクリプトを実行ファイル(exeファイル)にしてくれるというのだから驚愕だ.

UWSCといったスクリプトを用いるユーティリティは自分の環境をちょっとカスタマイズするには便利だけど,誰かが作ったスクリプトを使おうとしたらUWSC本体も導入しなければならない.

AutoHotKeyは実行ファイルまで作れてしまうので,本来自分専用のスクリプト系ユーティリティがアプリケーションの開発環境にまでなってしまうってんだから脱帽である.

とっかかりはあまり良くないけど,実は簡単に書けるのがうれしい.ユーティリティ系でよく使いそうな機能はたいてい用意されているし,用意されていなくてもDLLから直接APIをコールできるので実質ほとんどの操作ができる.

サンプルスクリプト

僕もちょっと書いてみたので公開.画面端にマウスを持って行ったときに指定したウィンドウをアクティブにして,画面上部に持って行ったときは最小化するというもの.よくありそうなツールである.

画面下をファイラ(X-Finder),画面左をブラウザ(Firefox)としている.ちなみに右はランチャー(nrLaunch)が出てくるのでここでは指定していない.

下半分は配列の簡単な実装.これだけ気合いが入ったツールなのに配列が無いのはちょっと意外.そして,配列やハッシュのライブラリを誰か作っていないかとググってみたけど見つからない.これまた意外.

; スクリプト概要
; - マウスカーソルが画面端から一定の距離になったときウィンドウをアクティブ化
; 履歴
; - 2008/04/13 作成
;

; 永続的に動かす
#Persistent

; MouseGetPosの座標をスクリーン座標にする
CoordMode,Mouse,Screen

; スクリーンのサイズを取得
;WinGetPos, , , screen_width, screen_height , Program Manager
screen_width := A_ScreenWidth
screen_height := A_ScreenHeight

; ウィンドウを探すときのマッチの方法を「指定した文字列を含む」に変更
SetTitleMatchMode,2

; 複数のウィンドウを同時にチェックするための配列作成
window_list := array("window_list")
margin_list := array("margin_list")
direct_list := array("direct_list")

; 画面上部に持って行ったときに対象ウィンドウを最小化する
top_margin := 5

xfinder := FindWindow("X-Finder", "Firefox")
firefox := FindWindow("- Mozilla Firefox", "")

; ウィンドウハンドル,画面端からの距離,方向(1:右,2:下,3:左)
SetWatchWindow(xfinder, 5, 2)
SetWatchWindow(firefox, 5, 3)

; 100ミリ秒ごとにマウス座標をチェックする
SetTimer, WatchCursor, 100

FindWindow(window_text, exclude_text){
	window := WinExist(window_text,"",exclude_text)
	if(window == 0)
		MsgBox, %window_text% が見つかりません
	return window
}

SetWatchWindow(window_id, margin, direct){
	global
	arrpush(window_list, window_id)
	arrpush(margin_list, margin)
	arrpush(direct_list, direct)
	return
}

WatchCursor:
	MouseGetPos,x,y,id,control,3
	SendMessage,0x84,0,%lp%,,ahk_id %control%
	If ErrorLevel=4294967295
		MouseGetPos,,,,control,2
	Loop, %window_list@length%
	{
		index := A_Index - 1
		window := window_list[%index%]
		direct := direct_list[%index%]
		margin := margin_list[%index%]
		CheckWindow(x,y,window,direct,margin)
	}
	CheckHide(x,y)
	return

CheckWindow(x, y, window, direct, margin){
	global screen_width
	global screen_height
	global last_activated
	if(direct == 1 && x > screen_width - margin
		|| direct == 2 && y > screen_height - margin
		|| direct == 3 && x < margin)
	{
		WinActivate, ahk_id %window%
	}
	return
}

CheckHide(x, y){
	global
	if(y < top_margin){
		Loop, %window_list@length%
		{
			local index := A_Index - 1
			local window := arrget(window_list, index)
			WinMinimize, ahk_id %window%
		}
	}
	return
}

; ここから配列操作関係
array(var_name){
	global
	%var_name%@length := 0
	return %var_name%
}

arrpop(var_name){
	global
	local length := 0
	local value :=
	if(%var_name%@length > 0){
		%var_name%@length := %var_name%@length - 1
		length := %var_name%@length
		value := %var_name%[%length%]
	}
	return value
}

arrpush(var_name, value){
	global
	local next := %var_name%@length
	%var_name%[%next%] := value
	%var_name%@length += 1
	return next
}

arrget(var_name, index){
	global
	return %var_name%[%index%]
}

arrlen(var_name){
	global
	return %var_name%@length
}

arrbegin(var_name){
	global
	; 要素が存在しないときは空文字が返る
	return %var_name%[0]
}

arrend(var_name){
	global
	; 要素が存在しないときは空文字が返る
	endpos := %var_name%@length - 1
	return %var_name%[%endpos%]
}