com.brianziman.ui
Class UsefulKeyAdapter

java.lang.Object
  extended by com.brianziman.ui.UsefulKeyAdapter
All Implemented Interfaces:
java.awt.event.KeyListener, java.lang.Runnable, java.util.EventListener

public class UsefulKeyAdapter
extends java.lang.Object
implements java.awt.event.KeyListener, java.lang.Runnable

This class wraps your KeyAdapter and suppresses spurious repeated events.

Written by Brian Ziman and available for download and comments at http://www.brianziman.com/r/post/stuff-200910092100.

Rationale

When running in an X11 environment, it appears as though either Java or the windowing system stupidly generate lots of repeated key events when you hold a key down. As it is impractical to get users to reconfigure their systems, and Sun seems unable to fix this issue (having been reported nearly ten years ago). This adapter is an attempt to filter out the useless events.

Many people have whined about this, but few have fixed it. Some have tried, but their solutions are not as clean as my OCD nature requires. Here are some examples of what this is meant to handle:

The goal is to generate a KeyPressed event when a key is pressed, and to generate a KeyReleased event when a key is released. And ONLY when a key is pressed or released.

Compatibility

The class is tested with Java 1.6.0 on Ubuntu 8.10, but should theoretically work on any platform, even if that platform issues events properly anyway. That is, this SHOULD work in a cross platform manner. If it doesn't, please let me know and I may fix it.

Usage

You don't actually call any of the methods on this class, directly. When Java thinks it is receiving an event, it will call the methods in this class, which will, in turn, decide whether an event has actually occurred, and call YOUR methods when you probably intended them to be called.

Replace:

 addKeyListener(new KeyAdapter() {
      public void keyPressed(KeyEvent e) { ... }
      public void keyReleased(KeyEvent e) { ... }
 });
 

with

 addKeyListener(new UsefulKeyAdapter(new KeyAdapter() {
      public void keyPressed(KeyEvent e) { ... }
      public void keyReleased(KeyEvent e) { ... }
 }));
 

Don't use this for keyTyped() events. If you are interested in these sorts of events, the default behaviour is probably what you actually want.

See How to Write a Key Listener on Sun's web site for more details.

Algorithm:

This algorithm queues up keyboard events as the system reports them, and then uses a separate thread to handle the desired events, and disregard the spurious events.

Testing has indicated that there is zero delay between the key released event and the subsequent key pressed event when that pair are generated spuriously, and that is the case in which they are ignored. The default tolerance is 10 ms, as it is possible that the system will not always generate spurious key events instantly, but that is still substantially faster than a physical keyboard is able to perform multiple physical actions.

This code uses an ArrayBlockingQueue so the thread will not spin while waiting for input. Most events are handled immediately, and no event will be delayed more than the specified maximum age (default 10 ms).

License

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Copyright 2009-2010 Brian Ziman

Please contact me if you find flaws in this software, or have any other comments or suggestions.

Version History


Constructor Summary
UsefulKeyAdapter(java.awt.event.KeyListener keyListener)
          Creates a KeyListener implementation that delegates event handling to the specified KeyListener, after filtering out spurious key pressed and key released events.
UsefulKeyAdapter(java.awt.event.KeyListener keyListener, int maxAge)
          Creates a KeyListener implementation that delegates event handling to the specified KeyListener, after filtering out spurious key pressed and key released events.
 
Method Summary
 void finalize()
          Disable the background thread when the adapter goes out of scope.
 void keyPressed(java.awt.event.KeyEvent e)
          Delegates the key pressed event.
 void keyReleased(java.awt.event.KeyEvent e)
          Delegates the key released event.
 void keyTyped(java.awt.event.KeyEvent e)
          Don't use the UsefulKeyAdapter for keyTyped events.
 void run()
          The run loop processes events as they are queued up.
 
Methods inherited from class java.lang.Object
clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

UsefulKeyAdapter

public UsefulKeyAdapter(java.awt.event.KeyListener keyListener)
Creates a KeyListener implementation that delegates event handling to the specified KeyListener, after filtering out spurious key pressed and key released events. In a sequence of events, events are considered spurious if a key pressed event immediately follows a key released event, for the same key, and within the specified maximum age. The default maximum age is 10 ms.


UsefulKeyAdapter

public UsefulKeyAdapter(java.awt.event.KeyListener keyListener,
                        int maxAge)
Creates a KeyListener implementation that delegates event handling to the specified KeyListener, after filtering out spurious key pressed and key released events. In a sequence of events, events are considered spurious if a key pressed event immediately follows a key released event, for the same key, and within the specified maximum age. The default maximum age is 10 ms.

Method Detail

run

public void run()

The run loop processes events as they are queued up.

New algorithm:

All events get queued up in sequence.

A separate thread pulls events off in a tight loop.

If the head event is a key released event:

Specified by:
run in interface java.lang.Runnable

keyPressed

public void keyPressed(java.awt.event.KeyEvent e)
Delegates the key pressed event.

Specified by:
keyPressed in interface java.awt.event.KeyListener

keyReleased

public void keyReleased(java.awt.event.KeyEvent e)
Delegates the key released event.

Specified by:
keyReleased in interface java.awt.event.KeyListener

keyTyped

public void keyTyped(java.awt.event.KeyEvent e)
Don't use the UsefulKeyAdapter for keyTyped events.

Specified by:
keyTyped in interface java.awt.event.KeyListener

finalize

public void finalize()
Disable the background thread when the adapter goes out of scope. Not guaranteed to be called, but it's polite to clean up resources.

Overrides:
finalize in class java.lang.Object