概要
AutoHotKey v2 を使って HHKB Studio (日本語配列) をカスタマイズした内容の備忘録です。
変換・無変換キー、マウスボタンへファンクションキーを割り当てて、それらに対してホットキーを割り当てています。
AutoHotKey のインストールとスクリプトの実行方法
公式サイトから "Latest Installer" を落としてインストールします。
この記事作成時点での作業環境は以下の通りです。
- Windows 11 23H2
- HHKB Studio B2.07 (日本語配列キーボードをUS配列の認識環境で利用)
- AutoHotKey v2.0.10
V2は公開されてから日が浅いため、V1よりも情報が少ないですが、公式リファレンスを参考にコンフィグを作っていきます。
適当なテキストエディタを使って「script.ahk」というファイルを作成しました。
拡張子が関連付けされているので、スクリプトをそのままダブルクリックすると、AHKが起動してスクリプトを読み込んでくれるのですが、タスクマネージャなどの管理者権限で起動しているアプリに対しては、AHK自身も管理者権限で起動しないと入力を受け付けてくれません。このためスクリプトへのショートカットファイルを作成して、ファイルプロパティから管理者権限で起動する設定を行いました。
機能の有効化、AHKの操作
まずはAutoHotkey自体の設定と操作するホットキーを定義します。
#Requires AutoHotkey v2.0
#SingleInstance Force
#UseHook
InstallKeybdHook ; キーボードフックの有効化
ProcessSetPriority "Realtime" ; プロセスの優先順位
; AHKの操作
#SuspendExempt
^!s::Suspend ; Ctrl+Alt+S - 一時停止
^!r::Reload ; Ctrl+Alt+R - リロード
^!e::Edit ; Ctrl+Alt+E - エディット
^!k::KeyHistory ; Ctrl+Alt+K - キーヒストリー
^!l::ListHotKeys ; Ctrl+Alt+L - ホットキーの一覧
^!Esc::ExitApp ; Ctrl+Alt+Esc - AHK終了
#SuspendExempt False
カーソル操作を行うためのホットキー
無変換キーへ配置したF18キーを押している間は、ホームポジションキーに対してカーソル操作系のホットキーを割り当てます。
単体でタップした場合は AppsKey
が送信されます。
; テキスト編集機能
F18::
{
OutputDebug ThisHotkey
inputKey := "F18"
tapKey := "{AppsKey}"
global SelectMode := 0
KeyWait(inputKey)
global SelectMode := 0
if (IsSingleTap(ThisHotkey)) {
Send(tapKey)
}
}
#HotIf GetKeyState("F18", "P")
q::!Left ; Back Page
w::SelectMode_Send("^{Home}") ; Head of Page
e::SelectMode_Send("^{End}") ; End of Page
r::!Right ; Next Page
t::return
y::^y
u:: global SelectMode += 1 ; Change SelectMode
i::SelectMode_Send("{Home}") ; Head of Line
o::SelectMode_Send("{End}") ; End of Line
p::return
a::SelectMode_Send("{PgUp}")
s::SelectMode_Send("{Up}")
d::SelectMode_Send("{Down}")
f::SelectMode_Send("{PgDn}")
g::Del
h::BackSpace
j::SelectMode_Send("^{Left}") ; Left Word
k::SelectMode_Send("{Left}")
l::SelectMode_Send("{Right}")
`;::SelectMode_Send("^{Right}") ; Right Word
z::^z
x::^x
c::^c
v::^v
b::return
n::return
m::Enter
,::^BackSpace ; Delete Left Word
.::^Del ; Delete Right Word
/::return
#HotIf
ホットキー単体でタップされたかどうかを判定する関数。
; シングルタップの判定
IsSingleTap(thisHotkey, tappingTerm := TAPPING_TERM)
{
if (A_ThisHotkey != thisHotkey) {
return false ; 異なるホットキーが押された
}
else if (tappingTerm < 0) {
return true
}
else if (A_TimeSinceThisHotkey > tappingTerm) {
return false ; 時間のしきい値を超えた
}
else {
return true
}
}
カーソル移動キーに対して選択モードを切り替えるための関数。SelectMode
が有効な場合は Shift
による範囲選択または Shift+Ctrl+Alt
による矩形選択が有効となります。
; カーソル範囲選択モード
SelectMode := 0
SelectMode_Send(keys)
{
mod_key := ""
if (SelectMode) {
Switch Mod(SelectMode, 2)
{
Case 1:
mod_key := "+" ; Shift
Default:
mod_key := "+^!" ; Shift+Ctrl+Alt
}
}
Send mod_key keys
}
ウィンドウ操作機能
変換キーへ配置したF19のホットキーでは、OS機能のウィンドウスナップと、AutoHotkeyによるウィンドウのリサイズ機能を割り当てています。
単体でタップした場合は Win キーを送信します。
; ウィンドウ操作機能
*F19::
{
OutputDebug ThisHotkey
inputKey := "F19"
tapKey := "{RWin}"
global WinLock := true
KeyWait(inputKey)
WinLock := false
if (IsSingleTap(ThisHotkey)) {
Send(tapKey)
}
}
#HotIf GetKeyState("F19", "P")
; Window Snap
w::Send "#{Up}"
e::Send "#{Down}"
i::Send "#{Left}"
o::Send "#{Right}"
a::WinResize("a", 0, -A_ScreenHeight/4)
s::WinResize("s", 0, -10)
d::WinResize("d", 0, 10)
f::WinResize("f", 0, A_ScreenHeight/4)
j::WinResize("j", -A_ScreenWidth/4, 0)
k::WinResize("k", -10, 0)
l::WinResize("l", 10, 0)
`;::WinResize(";", A_ScreenWidth/4, 0)
#HotIf
ウィンドウリサイズを行う関数
; ウィンドウをリサイズ
WinResize(inputKey, dw, dh)
{
OutputDebug "WinResize " inputKey " " dw " " dh
try {
; タスクバーの高さ(taskbarHeight)、アクティブなウィンドウタイトル(winTitle)を取得
WinGetPos(,,, &taskbarHeight, "ahk_class Shell_TrayWnd")
winTitle := WinGetTitle("A")
; ワンショットでリサイズした後、連続入力を受け付ける経過時間まで待機
WinResize()
KeyWait(inputKey, "T0.5")
; 連続リサイズ開始
if GetKeyState(inputKey, "P") {
SetTimer WinResize, 20
}
KeyWait(inputKey, "T1")
; リサイズ幅を大きくする
if GetKeyState(inputKey, "P") {
dw *= 2
dh *= 2
}
KeyWait(inputKey)
; 画面縁に内接するようにウィンドウサイズを変更する
WinResize()
{
WinGetPos &x, &y, &width, &height, winTitle
width := Min(width + dw, A_ScreenWidth)
height := Min(height + dh, A_ScreenHeight - taskbarHeight)
x := Min(x, A_ScreenWidth - width)
y := Min(y, A_ScreenHeight - height - taskbarHeight)
WinMove x, y, width, height, winTitle
}
}
catch as e {
OutputDebug " - catch as e.Message: " e.Message
}
SetTimer WinResize, 0
}
モディファイヤ状態をロックするための定義
モディファイヤのロック状態をグローバル変数に保存しておき、これらが true
状態のときにはモディファイヤキー付きでキー入力を行うための関数を定義。
; モディファイヤ状態を付加してキー送信
ShiftLock := false
CtrlLock := false
WinLock := false
pShiftLock := &ShiftLock
pCtrlLock := &CtrlLock
pWinLock := &WinLock
ModLock_Send(keys)
{
; モディファイヤキーのロック
mod_key := (ShiftLock ? "+" : "") . (CtrlLock ? "^" : "") . (WinLock ? "#" : "")
Send mod_key keys
}
"p"プレフィックスの付いた変数に、変数への参照を格納しておくことで、関数引数への参照渡しをglobal宣言せずに行うことができます。
; グローバル変数とその参照
ShiftLock := false
pShiftLock := &ShiftLock
; 宣言無しで参照演算子(&)を使うと、ローカル変数扱いとなるためエラー
a::Function(&ShiftLock)
; global宣言した後なら、グローバル変数への参照演算子(&)を使用可能
b::
{
global ShiftLock
Function(&ShiftLock)
}
; 演算子無しでグローバル変数を参照することは可能
c::Function(pShiftLock)
想定ローカル関数内の変数参照は、それが読み込まれるだけであれば、グローバル変数に解決される可能性があります。ただし、変数が代入や参照演算子(&)で使用された場合、デフォルトで自動的にローカルになります。これにより、関数は関数内で宣言することなく、グローバル変数の読み込みや、グローバル関数や組み込み関数の呼び出しを行うことができます。
主な文字入力キー全てに ModLock_Send
でホットキーを設定します。
; モディファイヤロック
#HotIf ShiftLock or CtrlLock or WinLock
1::
2::
3::
4::
5::
6::
7::
8::
9::
0::ModLock_Send(A_ThisHotkey)
q::
w::
e::
r::
t::
y::
u::
i::
o::
p::ModLock_Send(A_ThisHotkey)
a::
s::
d::
f::
g::
h::
j::
k::
l::
`;::ModLock_Send(A_ThisHotkey)
z::
x::
c::
v::
b::
n::
m::
,::
.::
/::ModLock_Send(A_ThisHotkey)
-::
=::
\::
[::
]::
'::
`::ModLock_Send(A_ThisHotkey)
Tab::
sc07D::
sc02B::
sc073::ModLock_Send("{" A_ThisHotkey "}")
#HotIf
左ボタン機能
左ボタンへ配置したF15キーには、ホールド中はShiftキー、タップやカーソル移動時は左クリックとして機能するホットキーを定義します。
; 左ボタン機能
+F15::+LButton
F15::DownLButton(ThisHotkey, "F15", pShiftLock)
; 左ボタンの操作
DownLButton(thisHotkey, inputKey, modLock)
{
OutputDebug "DownLButton " thisHotkey
static mouseBtn := "LButton"
static mouseBtn_Down := "{Blind}{LButton Down}"
static mouseBtn_Up := "{Blind}{LButton Up}"
static mouseBtn_Tap := "{Blind}{LButton}"
; modLockをホールド
%modLock% := True
; inputKeyホールド中にカーソル移動したらマウスボタンを押下
timeIdleMouse := A_TimeIdleMouse
while GetKeyState(inputKey, "P") {
if (A_TimeIdleMouse < timeIdleMouse) {
Send(mouseBtn_Down)
break
}
Sleep 0
}
KeyWait(inputKey)
%modLock% := False
; マウスボタンの後処理
if (GetKeyState(mouseBtn)) {
; マウスホールドを開放
OutputDebug " - HoldUp " inputKey
Send(mouseBtn_Up)
}
else if (IsSingleTap(thisHotkey, -1)) {
; マウスボタンのタップ
OutputDebug " - Tap " inputKey
Send(mouseBtn_Tap)
}
}
マウス操作関連の機能を使うため、下記定義もスクリプト先頭に追加しておきます。
; マウス関連の設定
InstallMouseHook ; マウスフックの有効化
CoordMode "Mouse", "Screen" ; マウス座標系の基準
左ボタン(F15)+中ボタン(F16)の同時押しでウィンドウをカーソルで掴むように移動させます。
#HotIf GetKeyState("F15", "P")
; ウィンドウをカーソル移動に追従
F16::
{
MouseGetPos &mx, &my, &wid
winTitle := "ahk_id " wid
WinGetPos &wx, &wy, &width, &height, winTitle
dx := wx - mx
dy := wy - my
while GetKeyState("F16", "P") {
MouseGetPos &mx, &my
wx := dx + mx
wy := dy + my
WinMove wx, wy, width, height, winTitle
Sleep 20
}
}
#HotIf
中ボタン機能
中ボタンへ配置したF16キーには、ホールド中はホイール操作、タップしたら中ボタンとして機能するホットキーを定義します。
; 中ボタン
F16::CursorToWheelMove(ThisHotkey, "F16", "MButton")
; カーソル移動によるホイール操作
CursorToWheelMove(thisHotkey, inputKey, tapKey)
{
OutputDebug "CursorToWheelMove " thisHotkey
static mousePerWheel := 20
mouseMoved := false
MouseGetPos &downX, &downY
integralX := 0
integralY := 0
wheelX := 0
wheelY := 0
; inputKeyホールド中にカーソル移動したらホイール操作
while GetKeyState(inputKey, "P") {
; マウス移動量を積算
MouseGetPos &mx, &my
DllCall("SetCursorPos", "int", downX, "int", downY)
integralX += mx - downX
integralY += my - downY
; マウス移動量からホイール操作量を計算
mouseMoved |= SendWheel(integralY, &wheelY, "WheelUp", "WheelDown")
SendWheel(integralPos, &wheelPos, wheelBackward, wheelForward)
{
targetWp := integralPos // mousePerWheel
if (targetWp = wheelPos) {
return false
}
dw := Abs(targetWp - wheelPos)
if (targetWp > wheelPos) {
MouseClick wheelForward,,, dw
wheelPos += dw
}
else {
MouseClick wheelBackward,,, dw
wheelPos += -dw
}
return true
}
Sleep 20
}
; ボタンのタップ
if (IsSingleTap(thisHotkey)) {
Send("{" tapKey "}")
}
}
中ボタン(F16)ホールド中のg h b n
キーへホイール操作を割り当てています。
#HotIf GetKeyState("F16", "P")
g::
h::MouseClick "WheelUP",,, 1
b::
n::MouseClick "WheelDown",,, 1
#HotIf
右ボタン機能
右ボタンへ配置したF17キーには、ホールド中はシフトキーのホールド及び、マウスカーソル移動が低速になるように SystemParametersInfo
を書き換える機能を割り当てました。
単体でタップした場合は、右ボタンが押下されます。
; 右ボタン
+F17::+RButton
F17::DownRButton(ThisHotkey, "F17", pShiftLock)
; 右ボタンの操作
DownRButton(thisHotkey, inputKey, modLock)
{
OutputDebug "DownLButton " thisHotkey
static mouseBtn_Down := "{Blind}{RButton Down}"
static mouseBtn_Up := "{Blind}{RButton Up}"
static mouseBtn_Tap := "{Blind}{RButton}"
; modLockをホールド
%modLock% := True
static SPI_GETMOUSESPEED := 0x70
static SPI_SETMOUSESPEED := 0x71
static OrigMouseSpeed := 0
; Retrieve the current speed so that it can be restored later:
DllCall("SystemParametersInfo", "UInt", SPI_GETMOUSESPEED, "UInt", 0, "Ptr*", &OrigMouseSpeed, "UInt", 0)
; Now set the mouse to the slower speed specified in the next-to-last parameter (the range is 1-20, 10 is default):
DllCall("SystemParametersInfo", "UInt", SPI_SETMOUSESPEED, "UInt", 0, "Ptr", 1, "UInt", 0)
; ホールド解除されるまで待機
KeyWait(inputKey)
%modLock% := False
; KeyWait inputKey ; This prevents keyboard auto-repeat from doing the DllCall repeatedly.
DllCall("SystemParametersInfo", "UInt", SPI_SETMOUSESPEED, "UInt", 0, "Ptr", OrigMouseSpeed, "UInt", 0) ; Restore the original speed.
if (IsSingleTap(thisHotkey))
{
; マウスボタンのタップ
OutputDebug " - Tap " inputKey
Send(mouseBtn_Tap)
}
}