Created
August 31, 2011 15:26
-
-
Save rjeschke/1183826 to your computer and use it in GitHub Desktop.
Workaround for the Linux key repeat quirk.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package thoughts; | |
import java.awt.event.KeyEvent; | |
import java.awt.event.KeyListener; | |
import java.util.concurrent.ArrayBlockingQueue; | |
import java.util.concurrent.TimeUnit; | |
import javax.swing.SwingUtilities; | |
/** | |
* A simple, small and easy to use KeyListener wrapper to fix the famous Linux key repeat *bug*. | |
* | |
* @author René Jeschke (rene_jeschke@yahoo.de) | |
*/ | |
public final class KeyListenerWrapper implements KeyListener, Runnable | |
{ | |
/** The listener to delegate key events to. */ | |
final KeyListener wrappedListener; | |
/** Our background thread. */ | |
private final Thread watcher; | |
/** Our key event queue. */ | |
private ArrayBlockingQueue<KeyEvent> keyEvents = new ArrayBlockingQueue<KeyEvent>(2048); | |
/** Whether to post KeyEvents using invokeLater or not. */ | |
private final boolean useInvokeLater; | |
/** | |
* Ctor. | |
* | |
* @param wrapped The wrapped KeyListener | |
* @param useInvokeLater Whether to post KeyEvents using invokeLater or not. | |
*/ | |
private KeyListenerWrapper(final KeyListener wrapped, final boolean useInvokeLater) | |
{ | |
this.wrappedListener = wrapped; | |
this.useInvokeLater = useInvokeLater; | |
this.watcher = new Thread(this); | |
this.watcher.setDaemon(true); | |
} | |
/** | |
* Initializes this key listener wrapper (also starts the background thread). | |
* | |
* @param wrapped The KeyListener to wrap. | |
* @param useInvokeLater Whether to post KeyEvents using invokeLater or not. | |
* @return this | |
*/ | |
public static KeyListenerWrapper init(final KeyListener wrapped, final boolean useInvokeLater) | |
{ | |
final KeyListenerWrapper wrapper = new KeyListenerWrapper(wrapped, useInvokeLater); | |
wrapper.watcher.start(); | |
return wrapper; | |
} | |
/** | |
* Posts a key event. | |
* | |
* @param e The KeyEvent. | |
* @param invokeLater Whether to use invokeLater or not. | |
*/ | |
void postKeyEvent(final KeyEvent e, final boolean invokeLater) | |
{ | |
if(invokeLater) | |
{ | |
SwingUtilities.invokeLater(new Runnable() | |
{ | |
@Override | |
public void run() | |
{ | |
KeyListenerWrapper.this.postKeyEvent(e, false); | |
} | |
}); | |
} | |
else | |
{ | |
switch(e.getID()) | |
{ | |
case KeyEvent.KEY_PRESSED: | |
this.wrappedListener.keyPressed(e); | |
break; | |
case KeyEvent.KEY_RELEASED: | |
this.wrappedListener.keyReleased(e); | |
break; | |
case KeyEvent.KEY_TYPED: | |
this.wrappedListener.keyTyped(e); | |
break; | |
} | |
} | |
} | |
/** | |
* @see KeyListener#keyPressed(KeyEvent) | |
*/ | |
@Override | |
public void keyPressed(KeyEvent e) | |
{ | |
this.keyEvents.add(e); | |
} | |
/** | |
* @see KeyListener#keyReleased(KeyEvent) | |
*/ | |
@Override | |
public void keyReleased(KeyEvent e) | |
{ | |
this.keyEvents.add(e); | |
} | |
/** | |
* @see KeyListener#keyTyped(KeyEvent) | |
*/ | |
@Override | |
public void keyTyped(KeyEvent e) | |
{ | |
this.keyEvents.add(e); | |
} | |
/** | |
* @see Runnable#run() | |
*/ | |
@Override | |
public void run() | |
{ | |
KeyEvent last = null; | |
for(;;) /* I could do this all day long ... */ | |
{ | |
try | |
{ | |
if(last == null) | |
{ | |
last = this.keyEvents.take(); | |
} | |
switch(last.getID()) | |
{ | |
case KeyEvent.KEY_PRESSED: | |
case KeyEvent.KEY_TYPED: | |
this.postKeyEvent(last, this.useInvokeLater); | |
last = null; | |
break; | |
case KeyEvent.KEY_RELEASED: | |
{ | |
final KeyEvent next = this.keyEvents.poll(5, TimeUnit.MILLISECONDS); | |
if(next == null) | |
{ | |
this.postKeyEvent(last, this.useInvokeLater); | |
last = null; | |
} | |
else if(next.getID() == KeyEvent.KEY_PRESSED | |
&& next.getKeyCode() == last.getKeyCode() | |
&& next.getWhen() == last.getWhen()) | |
{ | |
last = next; | |
} | |
else | |
{ | |
this.postKeyEvent(last, this.useInvokeLater); | |
last = next; | |
} | |
} | |
break; | |
} | |
} | |
catch (InterruptedException eaten) | |
{ | |
// *munch* | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment