バックグラウンド
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() )
ラベルを呼び出します。
最初の項目は大きな違いを生んでいるように見えますが、いくつかのキーがまだ欠けているように見えます (ただし、タイピングが速すぎる可能性があります)。レンダリング パイプラインがペイント要求をマージしないようにすることで、キー ストロークの損失を回避できることは理にかなっています。
リサーチ
この問題に関連するリソース: