package bs;
import java.awt.AWTException;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
/**
* The InputManager manages input of key and mouse events.
* <p>
* Het gegoochel met de release timers is nodig vanwege een bug in de key events
* op Linux. Bij ingedrukt houden van een toets stuurt het systeem telkens naast
* de verwachte keyPressed ook een keyReleased event. Het blijkt dat het
* released event het pressed event voorafgaat, en wel 0-5 millisec. De kern van
* de workaround is dan om na een released event te wachten op een eventueel
* volgend pressed event. Als dit pressed event niet komt binnen enkele
* milliseconden dan nemen we het released event serieus.
* <p>
* Deze class implementeert deze oplossing als volgt. De keyReleased handler
* start een timer voor een specifieke toets, met een actionhandler voor als de
* timer afgaat. De keyPressed handler stopt de timer, mits deze bestaat voor de
* betreffende toets. Dit zorgt er dan voor dat de timer de actionhandler niet
* activeert, waarmee het vorige onterechte released event genegeerd wordt.
*/
public class InputManager implements KeyListener, MouseListener,
MouseMotionListener, MouseWheelListener {
/**
* An invisible cursor.
*/
public static final Cursor INVISIBLE_CURSOR = Toolkit.getDefaultToolkit()
.createCustomCursor(Toolkit.getDefaultToolkit().getImage(""),
new Point(0, 0), "invisible");
// mouse codes
public static final int MOUSE_MOVE_LEFT = 0;
public static final int MOUSE_MOVE_RIGHT = 1;
public static final int MOUSE_MOVE_UP = 2;
public static final int MOUSE_MOVE_DOWN = 3;
public static final int MOUSE_WHEEL_UP = 4;
public static final int MOUSE_WHEEL_DOWN = 5;
public static final int MOUSE_BUTTON_1 = 6;
public static final int MOUSE_BUTTON_2 = 7;
public static final int MOUSE_BUTTON_3 = 8;
private static final int NUM_MOUSE_CODES = 9;
// key codes are defined in java.awt.KeyEvent.
// most of the codes (except for some rare ones like
// "alt graph") are less than 600.
private static final int NUM_KEY_CODES = 600;
private PressAndHoldKeyGameAction[] keyActions = new PressAndHoldKeyGameAction[NUM_KEY_CODES];
private PressKeyGameAction[] pressActions = new PressKeyGameAction[NUM_KEY_CODES];
private GameAction[] mouseActions = new GameAction[NUM_MOUSE_CODES];
private Timer[] releaseTimers = new Timer[NUM_KEY_CODES];
private Point mouseLocation;
private Point centerLocation;
private Component comp;
private Robot robot;
private boolean isRecentering;
/**
* Creates a new InputManager that listens to input from the specified
* component.
*/
public InputManager(Component comp) {
this.comp = comp;
mouseLocation = new Point();
centerLocation = new Point();
// register key and mouse listeners
comp.addKeyListener(this);
comp.addMouseListener(this);
comp.addMouseMotionListener(this);
comp.addMouseWheelListener(this);
// allow input of the TAB key and other keys normally
// used for focus traversal
comp.setFocusTraversalKeysEnabled(false);
}
/**
* Sets the cursor on this InputManager's input component.
*/
public void setCursor(Cursor cursor) {
comp.setCursor(cursor);
}
/**
* Sets whether relative mouse mode is on or not. For relative mouse mode,
* the mouse is "locked" in the center of the screen, and only the changed
* in mouse movement is measured. In normal mode, the mouse is free to move
* about the screen.
*/
public void setRelativeMouseMode(boolean mode) {
if (mode == isRelativeMouseMode()) {
return;
}
if (mode) {
try {
robot = new Robot();
recenterMouse();
} catch (AWTException ex) {
// couldn't create robot!
robot = null;
}
} else {
robot = null;
}
}
/**
* Returns whether or not relative mouse mode is on.
*/
public boolean isRelativeMouseMode() {
return (robot != null);
}
/**
* Maps a GameAction to a specific key. The key codes are defined in
* java.awt.KeyEvent. If the key already has a GameAction mapped to it, the
* new GameAction overwrites it.
*/
public void mapToKey(PressAndHoldKeyGameAction gameAction, final int keyCode) {
keyActions[keyCode] = gameAction;
Timer releaseTimer = new Timer(10, createReleaseActionListener(keyCode));
releaseTimer.setRepeats(false);
releaseTimers[keyCode] = releaseTimer;
}
public void mapToPress(PressKeyGameAction gameAction, final int keyCode) {
pressActions[keyCode] = gameAction;
}
/**
* Maps a GameAction to a specific mouse action. The mouse codes are defined
* here in InputManager (MOUSE_MOVE_LEFT, MOUSE_BUTTON_1, etc). If the mouse
* action already has a GameAction mapped to it, the new GameAction
* overwrites it.
*/
public void mapToMouse(GameAction gameAction, int mouseCode) {
mouseActions[mouseCode] = gameAction;
}
/**
* Resets all GameActions so they appear like they haven't been pressed.
*/
public void resetAllGameActions() {
for (int i = 0; i < keyActions.length; i++) {
if (keyActions[i] != null) {
keyActions[i].reset();
}
}
for (int i = 0; i < mouseActions.length; i++) {
if (mouseActions[i] != null) {
mouseActions[i].reset();
}
}
}
/**
* Gets the name of a key code.
*/
public static String getKeyName(int keyCode) {
return KeyEvent.getKeyText(keyCode);
}
/**
* Gets the name of a mouse code.
*/
public static String getMouseName(int mouseCode) {
switch (mouseCode) {
case MOUSE_MOVE_LEFT:
return "Mouse Left";
case MOUSE_MOVE_RIGHT:
return "Mouse Right";
case MOUSE_MOVE_UP:
return "Mouse Up";
case MOUSE_MOVE_DOWN:
return "Mouse Down";
case MOUSE_WHEEL_UP:
return "Mouse Wheel Up";
case MOUSE_WHEEL_DOWN:
return "Mouse Wheel Down";
case MOUSE_BUTTON_1:
return "Mouse Button 1";
case MOUSE_BUTTON_2:
return "Mouse Button 2";
case MOUSE_BUTTON_3:
return "Mouse Button 3";
default:
return "Unknown mouse code " + mouseCode;
}
}
/**
* Gets the x position of the mouse.
*/
public int getMouseX() {
return mouseLocation.x;
}
/**
* Gets the y position of the mouse.
*/
public int getMouseY() {
return mouseLocation.y;
}
/**
* Uses the Robot class to try to position the mouse in the center of the
* screen.
* <p>
* Note that use of the Robot class may not be available on all platforms.
*/
private synchronized void recenterMouse() {
if (robot != null && comp.isShowing()) {
centerLocation.x = comp.getWidth() / 2;
centerLocation.y = comp.getHeight() / 2;
SwingUtilities.convertPointToScreen(centerLocation, comp);
isRecentering = true;
robot.mouseMove(centerLocation.x, centerLocation.y);
}
}
private PressAndHoldKeyGameAction getKeyAction(int keyCode) {
if (keyCode < keyActions.length) {
return keyActions[keyCode];
} else {
return null;
}
}
private PressKeyGameAction getPressAction(int keyCode) {
if (keyCode < pressActions.length) {
return pressActions[keyCode];
} else {
return null;
}
}
private Timer getReleaseTimer(int keyCode) {
if (keyCode < releaseTimers.length) {
return releaseTimers[keyCode];
} else {
return null;
}
}
/**
* Gets the mouse code for the button specified in this MouseEvent.
*/
public static int getMouseButtonCode(MouseEvent e) {
switch (e.getButton()) {
case MouseEvent.BUTTON1:
return MOUSE_BUTTON_1;
case MouseEvent.BUTTON2:
return MOUSE_BUTTON_2;
case MouseEvent.BUTTON3:
return MOUSE_BUTTON_3;
default:
return -1;
}
}
private GameAction getMouseButtonAction(MouseEvent e) {
int mouseCode = getMouseButtonCode(e);
if (mouseCode != -1) {
return mouseActions[mouseCode];
} else {
return null;
}
}
private ActionListener createReleaseActionListener(final int keyCode) {
return new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
releaseKey(keyCode);
}
};
}
private void releaseKey(int keyCode) {
PressAndHoldKeyGameAction gameAction = getKeyAction(keyCode);
if (gameAction != null && gameAction.isPressed()) {
gameAction.release(System.currentTimeMillis());
}
}
public void keyPressed(KeyEvent e) {
// Press and hold actions
PressAndHoldKeyGameAction gameAction = getKeyAction(e.getKeyCode());
if (gameAction != null && !gameAction.isPressed()) {
gameAction.press(System.currentTimeMillis());
}
Timer releaseTimer = getReleaseTimer(e.getKeyCode());
if (releaseTimer != null) {
releaseTimer.stop();
}
// Press actions
PressKeyGameAction pressAction=getPressAction(e.getKeyCode());
if(pressAction!=null){
pressAction.press();
}
e.consume();
}
public void keyReleased(KeyEvent e) {
Timer releaseTimer = getReleaseTimer(e.getKeyCode());
if (releaseTimer != null) {
releaseTimer.start();
}
e.consume();
}
public void keyTyped(KeyEvent e) {
e.consume();
}
public void mousePressed(MouseEvent e) {
GameAction gameAction = getMouseButtonAction(e);
if (gameAction != null) {
gameAction.press();
}
}
public void mouseReleased(MouseEvent e) {
GameAction gameAction = getMouseButtonAction(e);
if (gameAction != null) {
gameAction.release();
}
}
public void mouseClicked(MouseEvent e) {
// do nothing
}
public void mouseEntered(MouseEvent e) {
mouseMoved(e);
}
public void mouseExited(MouseEvent e) {
mouseMoved(e);
}
// from the MouseMotionListener interface
public void mouseDragged(MouseEvent e) {
mouseMoved(e);
}
public synchronized void mouseMoved(MouseEvent e) {
// this event is from re-centering the mouse - ignore it
if (isRecentering && centerLocation.x == e.getX()
&& centerLocation.y == e.getY()) {
isRecentering = false;
} else {
int dx = e.getX() - mouseLocation.x;
int dy = e.getY() - mouseLocation.y;
mouseHelper(MOUSE_MOVE_LEFT, MOUSE_MOVE_RIGHT, dx);
mouseHelper(MOUSE_MOVE_UP, MOUSE_MOVE_DOWN, dy);
if (isRelativeMouseMode()) {
recenterMouse();
}
}
mouseLocation.x = e.getX();
mouseLocation.y = e.getY();
}
public void mouseWheelMoved(MouseWheelEvent e) {
mouseHelper(MOUSE_WHEEL_UP, MOUSE_WHEEL_DOWN, e.getWheelRotation());
}
private void mouseHelper(int codeNeg, int codePos, int amount) {
GameAction gameAction;
if (amount < 0) {
gameAction = mouseActions[codeNeg];
} else {
gameAction = mouseActions[codePos];
}
if (gameAction != null) {
gameAction.press(Math.abs(amount));
gameAction.release();
}
}
}