Java

Javaでネイティブにキーボード/マウスイベントを取得する

More than 3 years have passed since last update.

Javaでキーボード入力をキャプチャしたい! でもGUIフレームワークの機能はフォーカスがないと駄目だからバックグラウンドからじゃ使えないし・・・・・・って思ったこと、ありませんか? 私はあります。

そこで使えるのがJNativeHookというライブラリです。プラットフォームに依存したネイティブでいい感じにアレしてくれるらしいです。日本語の解説がなかったのでざっと書いてみます。

動作確認環境

Windows7 64bit
IntelliJ IDEA 13.1.4
JDK 1.8

準備

Githubで管理されているようなので、releasesページから適当にダウンロードしてクラスパスを通します。クラスパスの通し方とかは他のサイトを見てください。ダウンロードしたzipはそのままではなく解凍してjarとかを取り出す必要があるので注意。

共通

org.jnativehook.GlobalScreen.registerNativeHook()を呼び出してネイティブへのフックを有効にして、org.jnativehook.GlobalScreen.getInstance()で得たGlobalScreenインスタンスに適切なインターフェースを実装したクラスのインスタンスを登録する、という流れです。

イベントの文字列表現はtoString()ではなくparamString()です。何故。

あと、そのままだとライブラリが吐き出すログがスゴイので動作確認ができたら抑制しておきましょう。java.util.Loggerを使っているので、JNativeHookが使っているLoggerのインスタンスを取得してログレベルをWARNINGにします。ただ、時間差でログレベルが再度セットされたりするっぽいので、少し待ってからのほうがいいかも。以下に例を示します。

/**
 * JNativeHookのロギングを抑制する。
 *
 * mainで一度設定してもそのあとでJNativeHook側でセットされるっぽいので別スレッドで2秒待ってセット。
 */
private static void suppressLogger() {
    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1, r -> {
        Thread thread = new Thread(r);
        thread.setDaemon(true);
        return thread;
    });

    executorService.schedule(() -> {
        final Logger jNativeHookLogger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
        if (jNativeHookLogger.getLevel() != Level.WARNING) {
            synchronized (jNativeHookLogger) {
                jNativeHookLogger.setLevel(Level.WARNING);
            }
        }
    }, 2, TimeUnit.SECONDS);
}

キーボードイベント

キーボードイベントはorg.jnativehook.keyboard.NativeKeyListenerインターフェースを実装して使用します。

public class Main implements NativeKeyListener {

    // キーが押された
    @Override
    public void nativeKeyPressed(NativeKeyEvent e) {
        System.out.println(e.paramString());
    }

    // キーが離された
    @Override
    public void nativeKeyReleased(NativeKeyEvent e) {
        System.out.println(e.paramString());
    }

    // キーをタイプした
    @Override
    public void nativeKeyTyped(NativeKeyEvent e) {
        System.out.println(e.paramString());
    }

    public static void main(String[] args) {
        // フックしてなかったらフック
        if (!GlobalScreen.isNativeHookRegistered()) {
            try {
                GlobalScreen.registerNativeHook();
            } catch (NativeHookException e) {
                e.printStackTrace();
                System.exit(-1);
            }
        }

        // ログを抑制してハンドラを登録
        suppressLogger();
        GlobalScreen.getInstance().addNativeKeyListener(new Main());
    }

    // suppressLogger() は省略
}

マウスイベント

各マウスイベントとインターフェースの対応表

イベント クラス名
クリック org.jnativehook.mouse.NativeMouseListener
移動・ドラッグ org.jnativehook.mouse.NativeMouseMotionListener
上記2つのmixin org.jnativehook.mouse.NativeMouseInputListener
ホイール org.jnativehook.mouse.NativeMouseWheelListener

上記から使いたいインターフェースを実装して使用します。ホイールイベントが仲間外れだったりインスタンスの登録が3つに分かれてたりして地味に面倒。

public class Main implements NativeMouseInputListener, NativeMouseWheelListener {

    // マウスをクリックした
    @Override
    public void nativeMouseClicked(NativeMouseEvent e) {
        System.out.println(e.paramString());
    }

    // マウスを押した
    @Override
    public void nativeMousePressed(NativeMouseEvent e) {
        System.out.println(e.paramString());
    }

    // マウスを離した
    @Override
    public void nativeMouseReleased(NativeMouseEvent e) {
        System.out.println(e.paramString());
    }

    // マウスを移動した
    @Override
    public void nativeMouseMoved(NativeMouseEvent e) {
        System.out.println(e.paramString());
    }

    // マウスをドラッグした
    @Override
    public void nativeMouseDragged(NativeMouseEvent e) {
        System.out.println(e.paramString());
    }

    // マウスホイールを動かした
    @Override
    public void nativeMouseWheelMoved(NativeMouseWheelEvent e) {
        System.out.println(e.paramString());
    }

    public static void main(String[] args) {
        // フックしてなかったらフック
        if (!GlobalScreen.isNativeHookRegistered()) {
            try {
                GlobalScreen.registerNativeHook();
            } catch (NativeHookException e) {
                e.printStackTrace();
                System.exit(-1);
            }
        }

        // ログを抑制してハンドラを登録
        suppressLogger();

        final Main handler = new Main();
        final GlobalScreen globalScreen = GlobalScreen.getInstance();
        globalScreen.addNativeMouseListener(handler);
        globalScreen.addNativeMouseMotionListener(handler);
        globalScreen.addNativeMouseWheelListener(handler);
    }

    // suppressLogger() は省略
}