keyhacでCapsLockをモディファイアキー定義して無理矢理使う

keyhacの話。ある2chのスレを見ていたら、keyhacでCtrlとCapsLockキーを入れ替えようとしたが無理だった、というやりとりを見かけました。
CapsLockというキーは他のキーとは違いDownのイベントは発生するけど、Upは発生しない(ドライバがマスクしているらしい)ため、Ctrlと入れ替えた場合、押されっぱなしの状態になるとか。
試しにkeyhacで内部ログON*1にして、CtrlとCapsキーを押してみると

# Ctrlを押して離した場合

IN  : D-LCtrl
TRU : D-LCtrl
IN  : U-LCtrl
TRU : U-LCtrl
IN  : O-LCtrl

# CapsLockを押して離した場合

IN  : D-(240)
TRU : D-(240)

という風にCapsLockは「U-(240)」が出力されていません。だから入れ替えは無理、か。確かにこれは無理だなと思う反面、出来ないと言われると、何とかしたくなるのが人情。
で、色々考えた末、入れ替えは無理でもユーザー定義のモディファイアキー*2としてなら使えるかもと思い、書いてみました。

■ コード

# config.py

from keyhac import *


def configure(keymap):

    ## 関数実行時にモディファイアの状態をリセットするデコレータ
    def reset_modifier(func):
        import functools

        @functools.wraps(func)
        def _reset_modifier(*args, **kw):
            # モディファイアの状態を無理矢理リセット
            keymap.modifier = 0
            # 関数実行
            return func(*args, **kw)
        return _reset_modifier

    ## JobQueue/JobItem でサブスレッド処理にするデコレータ
    def job_queue(func):
        import functools

        @functools.wraps(func)
        def _job_queue(*args, **kw):

            num_items = JobQueue.defaultQueue().numItems()
            if num_items:   # 処理待ちアイテムがある場合は、その数を表示
                print u"JobQueue.defaultQueue().numItems() :", num_items

            def __job_queue_1(job_item):
                return func(*args, **kw)

            def __job_queue_2(job_item):
                # print "job_queue : ", func.__name__, args, kw
                pass

            job_item = JobItem(__job_queue_1, __job_queue_2)
            JobQueue.defaultQueue().enqueue(job_item)

        return _job_queue

    ## 一定時間経過後にモディファイアの状態をリセット
    @job_queue
    def auto_reset_modifier():
        import time
        WAIT_TIME = 0.5  # 秒
        time.sleep(WAIT_TIME)
        # モディファイアの状態を無理矢理リセット
        # if keymap.modifier: print keymap.modifier
        keymap.modifier = 0

    @reset_modifier
    def minimize():
        keymap.getTopLevelWindow().minimize()

    # どのウインドウにフォーカスがあっても効くキーマップ
    keymap_global = keymap.defineWindowKeymap()

    # ユーザモディファイアキーの定義:CapsLock(240) --> U1
    keymap.defineModifier("(240)", "U1")

    # 単体押しは一定時間後にモディファイアの状態をリセット
    # keymap_global["U1"] = auto_reset_modifier     # これだとキー表記エラー
    keymap_global["(240)"] = auto_reset_modifier

    # Caps + a : ウィンドウ最小化
    keymap_global["U1-a"] = minimize

関数定義で@reset_modifierのデコレータを使うと、関数の実行時にモディファイアキーの状態をリセットします。これによりCapsLockキーの押されっぱなしを防止しています。
これで一応モディファイアっぽく使えます。ただしタイトルに「無理矢理」と書いたように同時押しでなくても

CapsLockを押す → 離す → (0.5秒以内に)Aを押す

でも認識されてしまいます。また、CapsLockを押したままA、Aと2回押すと、2回目のAは同時押しではないAになってしまう等の欠点があります。

■ さいごに

日頃からCapsLockキーって押しやすい場所にあるのに全然使ってないなと思っている方はどうぞ。

*1:トレイアイコンを右クリック→内部ログONをクリック

*2:Ctrl・Shift・Alt・Winキーのような装飾キー