package com.sijobe.spc.util;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.lwjgl.input.Keyboard;
/**
* Handles Keyboard events using keyboard listeners. The class allows the
* addKeyPressed and addKeyReleased methods to be called with a passed key
* value that gets listened to. If that key gets a key released or pressed
* event then the appropriate listeners are called.
*
* @author simo_415
* @version 1.0
* @see KeyListener
* @see org.lwjgl.input.Keyboard
*/
public class KeyboardHandler extends Thread {
/**
* The delay in milliseconds that the keyboard events are checked
*/
public static final long DELAY = 10;
/**
* The singleton instance of the class
*/
private static KeyboardHandler KEYBOARD = new KeyboardHandler();
/**
* The singleton method that returns the instance
*
* @return The instance of the class to use
*/
public static KeyboardHandler getInstance() {
return KEYBOARD;
}
/**
* Holds the value of whether the instance is currently listening or not
*/
private boolean listening;
/**
* A List of keys that are currently registered with this listener
*/
private List<Integer> keys;
/**
* A List of keys that have currently been pressed
*/
private List<Integer> pressed;
/**
* A Map containing the registered pressed event listeners
*/
private Map<Integer, List<KeyListener>> registeredPressed;
/**
* A Map containing the registered released event listeners
*/
private Map<Integer, List<KeyListener>> registeredReleased;
/**
* Default private constructor sets up the instance
*/
private KeyboardHandler() {
registeredPressed = new ConcurrentHashMap<Integer, List<KeyListener>>();
registeredReleased = new ConcurrentHashMap<Integer, List<KeyListener>>();
pressed = new CopyOnWriteArrayList<Integer>();
keys = new CopyOnWriteArrayList<Integer>();
listening = false;
}
/**
* Checks for key press and key release events
*
* @see java.lang.Thread#run()
*/
@Override
public void run() {
try {
while (listening) {
if (!Keyboard.isCreated()) {
listening = false;
break;
}
// Check if pressed have been released
for (Integer press : pressed) {
if (!Keyboard.isKeyDown(press)) {
keyReleased(press);
}
}
// Check if key is pressed
for (Integer key : keys) {
if (Keyboard.isKeyDown(key) && !pressed.contains(key)) {
keyPressed(key);
}
}
try {
Thread.sleep(DELAY);
} catch (Exception e) {
}
}
} catch (Throwable e) {
e.printStackTrace();
listening = false;
}
}
/**
* Calls all of the necessary listeners based off the key pressed
*
* @param key - The key that was pressed
*/
private void keyPressed(int key) {
pressed.add(key);
if (registeredPressed.get(key) == null) {
return;
}
for (KeyListener listener : registeredPressed.get(key)) {
listener.keyPressed(key);
}
}
/**
* Calls all of the necessary listeners based off the key released
*
* @param key - The key that was released
*/
private void keyReleased(int key) {
pressed.remove((Object)key);
if (registeredReleased.get(key) == null) {
return;
}
for (KeyListener listener : registeredReleased.get(key)) {
listener.keyReleased(key);
}
}
/**
* Stops the instance from listening
*/
public void stopListening() {
listening = false;
}
/**
* Adds a key press listener to the specified key. See the Keyboard class
* for details on the key codes.
*
* @param key - The key to add the listener to
* @param listener - The listener to call on event
* @return True if the listener was successfully added
* @see org.lwjgl.input.Keyboard
*/
public boolean addKeyPressedListener(int key, KeyListener listener) {
return addListener(key, listener, registeredPressed);
}
/**
* Adds a key release listener to the specified key. See the Keyboard class
* for details on the key codes.
*
* @param key - The key to add the listener to
* @param listener - The listener to call on event
* @return True if the listener was successfully added
* @see org.lwjgl.input.Keyboard
*/
public boolean addKeyReleasedListener(int key, KeyListener listener) {
return addListener(key, listener, registeredReleased);
}
/**
* Generic implementation that adds the specified key and listener to the
* provided internal listener map.
*
* @param key - The key to add
* @param listener - The listener to call
* @param internal - The map to add to
* @return True if the listener was added successfully
*/
private boolean addListener(int key, KeyListener listener, Map<Integer, List<KeyListener>> internal) {
if (Keyboard.getKeyName(key) == null) {
return false;
}
List<KeyListener> keylist = internal.get(key);
if (keylist == null) {
keylist = new CopyOnWriteArrayList<KeyListener>();
internal.put(key, keylist);
}
keylist.add(listener);
if (keys.indexOf(key) == -1) {
keys.add(key);
}
if (!listening) {
listening = true;
start();
}
return true;
}
/**
* Removes the specified pressed listener and key from being listened to
* by this class.
*
* @param key - The key to remove
* @param listener - The listener to remove
*/
public void removeKeyPressedListener(int key, KeyListener listener) {
removeListener(key, listener, registeredPressed);
}
/**
* Removes the specified released listener and key from being listened to
* by this class.
*
* @param key - The key to remove
* @param listener - The listener to remove
*/
public void removeKeyReleasedListener(int key, KeyListener listener) {
removeListener(key, listener, registeredReleased);
}
/**
* Removes the specified key and listener instance from the specified Map
* listener.
*
* @param key - The key to remove based on the listener
* @param listener - The listener to remove
* @param internal - The listener Map to remove the key from
*/
private void removeListener(int key, KeyListener listener, Map<Integer, List<KeyListener>> internal) {
// Key hasn't been added, ignore
if (!keys.contains(key)) {
return;
}
// Remove from List
List<KeyListener> list = internal.get(key);
if (list != null) {
list.remove(listener);
}
// Check if the key is referenced anywhere
checkKeyUsage(key);
}
/**
* Checks the specified key whether it is still used by a listener or not.
* If the key is not used it is removed from the instance so that it isn't
* listened to anymore.
*
* @param key - The key to check
*/
private void checkKeyUsage(int key) {
List<KeyListener> pressed = registeredPressed.get(key);
boolean pressedEmpty = false;
if (pressed != null && pressed.size() == 0) {
registeredPressed.remove(key);
pressedEmpty = true;
}
List<KeyListener> released = registeredReleased.get(key);
boolean releasedEmpty = false;
if (released != null && released.size() == 0) {
registeredReleased.remove(key);
releasedEmpty = true;
}
if (pressedEmpty && releasedEmpty) {
keys.remove((Object)key);
pressed.remove((Object)key);
}
}
/**
* Gets the ID of the specified key string, this ID can be used to register
* key listeners. If the specified String is an invalid key type then -1 is
* returned.
*
* @param key - The key to retrieve the ID for
* @return The ID of the specified key, or -1 if not found
*/
public static int getKeyCode(String key) {
if (key == null) {
return -1;
}
return Keyboard.getKeyIndex(key.toUpperCase()) == Keyboard.KEY_NONE ? -1 : Keyboard.getKeyIndex(key.toUpperCase());
}
}