Ecoute du clavier en JAVA

J’ai longtemps été confronté à un soucis en programmation de jeux en JAVA.

En effet, lorsque vous écoutez le clavier avec une implémentation de l’interface java.awt.event.KeyListener, vous obtenez des déclenchement par de système des évènements keyPressed keyTyped et keyReleased dont les noms sont suffisamment explicites pour la plupart des usages (KeyTyped n’étant pas vraiment utile en fait, juste un petit raccourcis).

Le problème se pose lorsque vous maintenez une touche enfoncée, vous vous attendez à n’avoir que l’événement keyPressed de déclenché et pensez que le keyReleased sera déclenché au moment ou vous relâcherez la touche.
C’est sans compter sur la répétition automatique de frappe qui est gérée par votre système et non pas par la machine virtuelle JAVA.

Sous Windows et MAC (et surement d’autres) vous obtenez une répétition des évènements keyPressed et keyTyped tant que la touche est enfoncée puis un unique keyReleased au moment du relachement.

Sous Linux (au moins avec Xorg) l’évènement keyReleased est lui aussi répété tant que la touche est enfoncée. Ce qui pose un problème tout particulier dans le cas de développement de jeux-vidéos dans lesquels le maintient de touche est monnaie courante.

J’ai longtemps cherché à contourné ce problème en vain, mes recherche me menaient à chaque fois vers un « Impossible à contourné en JAVA, le problème est au niveau natif ».

Et pourtant, découverte aujourd’hui un peu par hasard, un petit détail tout bête.
Le moment ou les évènements keyReleased superflus sont déclenchés ont une particularité que JAVA peut exploiter : Ils sont simultanés par rapport aux keyPressed.

En comparant les valeurs retournées par e.getWhen() on peut facilement distinguer un relâchement simulé par la répétition de frappe du système d’un réél relâchement de la touche.

J’ai honte, j’ai publié cette information un peu trop vite, dans l’euphorie du moment. Il est effet plus compliqué que je ne le pensais car ils ne sont pas toujours déclenchés dans le même ordre. Ce qui pose un gros problème lorsque l’évènement que l’on veut vérifié est déclenché avant celui qui devrait servir de référence.

Pour contourner le problème, voici donc une class abstraite dont vous devrez implémenter les méthodes :

public abstract void keyPressedOrRepeated(java.awt.event.KeyEvent e);
public abstract void keyReleasedOrRepeated(java.awt.event.KeyEvent e);
public abstract void keyReleasedReally(java.awt.event.KeyEvent e);

AbstractKeyAdapterAntiRepeat.java.tar
(Mis à jour le 28 juillet 2012 pour supprimer le bug en cas de relâchement simultané de plusieurs touches)

Son seul défaut connu est qu’elle peut introduire une latence pouvant aller jusqu’à trois millisecondes entre le relâchement réel d’une touche et le déclenchement de l’évènement.

Avis aux testeurs, merci d’avance pour les retours sur différentes plateformes.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.