5

バックグラウンド

KmCasterと呼ばれる、スクリーン キャスティング用の基本的なオープンソースのキーボードとマウスのオンスクリーン ディスプレイ デスクトップ アプリケーションの開発:

スクリーン キャスティング プレビュー

Swing のKeyおよびMouseリスナーは、アプリケーション自体に向けられたイベントの受信に制限されているため、アプリケーションはJNativeHookライブラリを使用してグローバルなキーボードおよびマウス イベントを受信します。

問題

アプリケーションがフォーカスを失うと、キーを押すたびにではなく、断続的にキーを押すようにユーザー インターフェイスに表示されます。それでもコンソールには、アプリケーションがすべてのキーが押されたことを示しています。

コード

短い自己完結型のコンパイル可能な例:

import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.jnativehook.keyboard.NativeKeyEvent;
import org.jnativehook.keyboard.NativeKeyListener;

import javax.swing.*;

import static java.util.logging.Level.OFF;
import static java.util.logging.Logger.getLogger;
import static javax.swing.SwingUtilities.invokeLater;
import static org.jnativehook.GlobalScreen.*;
import static org.jnativehook.keyboard.NativeKeyEvent.getKeyText;

public class Harness extends JFrame implements NativeKeyListener {

  private final JLabel mLabel = new JLabel( "Hello, world" );
  private int mCount;

  public void init() {
    getContentPane().add( mLabel );

    setDefaultCloseOperation( EXIT_ON_CLOSE );
    setLocationRelativeTo( null );
    setAlwaysOnTop( true );
    pack();
    setVisible( true );
  }

  @Override
  public void nativeKeyPressed( final NativeKeyEvent e ) {
    final var s = getKeyText( e.getKeyCode() );
    System.out.print( s + " " + (++mCount % 10 == 0 ? "\n" : "") );

    invokeLater( () -> mLabel.setText( s ) );
  }

  public static void main( final String[] args ) throws NativeHookException {
    disableNativeHookLogger();
    registerNativeHook();

    final var harness = new Harness();
    addNativeKeyListener( harness );

    invokeLater( harness::init );
  }

  private static void disableNativeHookLogger() {
    final var logger = getLogger( GlobalScreen.class.getPackage().getName() );
    logger.setLevel( OFF );
    logger.setUseParentHandlers( false );
  }

  @Override
  public void nativeKeyReleased( final NativeKeyEvent e ) {}

  @Override
  public void nativeKeyTyped( final NativeKeyEvent e ) {}
}

上記のコードは、実行時に問題を示す小さなウィンドウを生成します。

ハーネスのスクリーンショット

他のウィンドウに入力して、デモ アプリケーション内でキーを押しても途方に暮れることを確認してください。

環境

  • OpenJDK バージョン「14.0.1」 2020-04-14、64 ビット
  • XFCE
  • アーチ Linux
  • JNativeHook 2.1.0

詳細

JNativeHook は独自のスレッドで実行されますが、invokeLater(またはinvokeAndWait?) を使用すると、Swing のイベント スレッドで UI の更新が発行されます。

への呼び出しdisableNativeHookLogger()は関係ありません。デモを実行するときにコンソールをクリーンに保つだけです。

コンソール出力

アプリケーションにフォーカスがあるときのコンソール出力は次のとおりです。

Shift I Space A M Space I N S I 
D E Space T H E Space A P P 
L I C A T I O N Period

アプリケーションがフォーカスを失ったときのコンソール出力は次のとおりです。

Shift I Space A M Space O U T S
I D E Space T H E Space A P
P L I C A T I O N Period 

nativeKeyPressedしたがって、アプリケーションにフォーカスがあるかどうかに関係なく、 が呼び出されたときにキーボード イベントが失われないことは明らかです。つまり、JNativeHook も JNI を介したそのイベント バブリングも原因ではないようです。

質問

JLabelアプリケーションにフォーカスがあるかどうかに関係なく、キーを押すたびにテキストが更新されるようにするには、何を変更する必要がありますか?

アイデア

役立つアイテムには次のものがあります。

  • getDefaultToolkit().sync();レンダリング パイプラインを明示的にフラッシュするために呼び出します。
  • paintImmediately( getBounds() )ラベルを呼び出します。

最初の項目は大きな違いを生んでいるように見えますが、いくつかのキーがまだ欠けているように見えます (ただし、タイピングが速すぎる可能性があります)。レンダリング パイプラインがペイント要求をマージしないようにすることで、キー ストロークの損失を回避できることは理にかなっています。

リサーチ

この問題に関連するリソース:

4

1 に答える 1