Skip to content

Instantly share code, notes, and snippets.

@lharding
Last active December 15, 2015 00:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lharding/5176082 to your computer and use it in GitHub Desktop.
Save lharding/5176082 to your computer and use it in GitHub Desktop.
Example Processing code for getting around AWT's terrible key repeat handling on Linux. Takes advantage of the fact that autorepeat keypresses come inhumanly soon after the virtual key release to filter out keyup->keydown pairs that happen too fast. This example is from a project of mine that uses ultimately sends out key events as strings, but …
private static class NonsimultaneousTimeSeries<T> extends TreeMap<Long, T> {
@Override
public T put(Long when, T what) {
//If something already happened on this millisecond, try the next one:
while(containsKey(when)) when++;
return super.put(when, what);
}
}
private final StringBuilder keysDown = new StringBuilder();
private final NonsimultaneousTimeSeries<String> inputEventQ = new NonsimultaneousTimeSeries<String>();
private final NonsimultaneousTimeSeries<KEv> keyQ = new NonsimultaneousTimeSeries<KEv>();
private final Map<Integer, KEv> lastUps = new HashMap<Integer, KEv>();
private final Map<Integer, KEv> heldKeys = new HashMap<Integer, KEv>();
private void sendInput() {
//send list as final input event for this frame
sendInputEvent(System.currentTimeMillis(), "KS", keysDown.toString());
synchronized (inputEventQ) {
for(String s : inputEventQ.values()) {
try {
//System.out.println("Sending: " + s);
sendMessage(s);
} catch (IOException e) {
e.printStackTrace();
}
}
inputEventQ.clear();
}
try {
sendMessage("P\n");
outWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
//System.out.println("All input sent.");
}
private void debounceKeys() {
final int DEBOUNCE = 3;
long now = System.currentTimeMillis();
for(KEv e : keyEvents) {
if(e.press) {
if((!lastUps.containsKey(e.keyCode)) || (lastUps.get(e.keyCode).when < (e.when - DEBOUNCE))) {
// If the last time this key went up is long enough ago, send a new down event
sendInputEvent(e.when, "KD", e.key, e.keyCode);
keyQ.put(e.when, e);
}
else {
// otherwise, send no down event, and get rid of the up event:
lastUps.remove(e.keyCode);
}
}
else {
lastUps.put(e.keyCode, e);
}
}
keyEvents.clear();
// Check each recently upped key to see if it's stay up long enough to report:
List<KEv> toRemove = new ArrayList<KEv>(lastUps.size());
for(KEv u : lastUps.values()) {
if(u.when < now-DEBOUNCE) {
toRemove.add(u);
sendInputEvent(u.when, "KU", u.key, u.keyCode);
keyQ.put(u.when, u);
}
}
for(KEv e : toRemove) {
lastUps.remove(e.keyCode);
}
//1. scan keyQ and build key state array
for(KEv k : keyQ.values()) {
if(k.press)
heldKeys.put(k.keyCode, k);
else
heldKeys.remove(k.keyCode);
}
//2. convert key state array to list of held keys
keysDown.delete(0, keysDown.length());
for(KEv k : heldKeys.values()) {
keysDown.append(k.key);
keysDown.append(k.keyCode);
keysDown.append(" ");
}
//4. clear keyQ
keyQ.clear();
}
static class KEv {
public boolean press;
public long when;
public char key;
public int keyCode;
KEv(boolean press, long when, char key, int keyCode) {
this.press = press;
this.when = when;
this.key = key;
this.keyCode = keyCode;
}
}
Queue<KEv> keyEvents = new ConcurrentLinkedQueue<KEv>();
@Override
public void keyPressed(processing.event.KeyEvent event) {
keyEvents.add(new KEv(true, event.getMillis(), event.getKey(), event.getKeyCode()));
}
@Override
public void keyReleased(processing.event.KeyEvent event) {
keyEvents.add(new KEv(false, event.getMillis(), event.getKey(), event.getKeyCode()));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment