/* * HalfNES by Andrew Hoffman * Licensed under the GNU GPL Version 3. See LICENSE file */ package com.grapeshot.halfnes.ui; import com.grapeshot.halfnes.PrefsSingleton; import static com.grapeshot.halfnes.utils.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.prefs.Preferences; import javafx.scene.Scene; import net.java.games.input.Component; import net.java.games.input.Controller; import net.java.games.input.ControllerEnvironment; import net.java.games.input.Event; import net.java.games.input.EventQueue; /** * * @author Andrew, Zlika This class uses the JInput Java game controller API * (cf. http://java.net/projects/jinput). */ public class ControllerImpl implements ControllerInterface, KeyListener { //private final java.awt.Component parent; private Controller gameController; private Component[] buttons; private final ScheduledExecutorService thread = Executors.newSingleThreadScheduledExecutor(); private int latchbyte = 0, controllerbyte = 0, prevbyte = 0, outbyte = 0, gamepadbyte = 0; private final HashMap<Integer, Integer> m = new HashMap<>(10); private final int controllernum; public ControllerImpl(final java.awt.Component parent, final int controllernum) { this(controllernum); //this.parent = parent; parent.addKeyListener(this); } public ControllerImpl(final Scene scene, final int controllernum) { this(controllernum); scene.addEventHandler(javafx.scene.input.KeyEvent.KEY_PRESSED, e -> pressKey(e.getCode().impl_getCode())); scene.addEventHandler(javafx.scene.input.KeyEvent.KEY_RELEASED, e -> releaseKey(e.getCode().impl_getCode())); } public ControllerImpl(final int controllernum) { if ((controllernum != 0) && (controllernum != 1)) { throw new IllegalArgumentException("controllerNum must be 0 or 1"); } this.controllernum = controllernum; setButtons(); } @Override public void keyPressed(final KeyEvent keyEvent) { pressKey(keyEvent.getKeyCode()); } private void pressKey(int keyCode) { //enable the byte of whatever is found prevbyte = controllerbyte; if (!m.containsKey(keyCode)) { return; } //enable the corresponding bit to the key controllerbyte |= m.get(keyCode); //special case: if up and down are pressed at once, use whichever was pressed previously if ((controllerbyte & (BIT4 | BIT5)) == (BIT4 | BIT5)) { controllerbyte &= ~(BIT4 | BIT5); controllerbyte |= (prevbyte & ~(BIT4 | BIT5)); } //same for left and right if ((controllerbyte & (BIT6 | BIT7)) == (BIT6 | BIT7)) { controllerbyte &= ~(BIT6 | BIT7); controllerbyte |= (prevbyte & ~(BIT6 | BIT7)); } } @Override public void keyReleased(final KeyEvent keyEvent) { releaseKey(keyEvent.getKeyCode()); } private void releaseKey(int keyCode) { prevbyte = controllerbyte; if (!m.containsKey(keyCode)) { return; } controllerbyte &= ~m.get(keyCode); } @Override public int getbyte() { return outbyte; } @Override public int peekOutput() { return latchbyte; } @Override public void keyTyped(final KeyEvent arg0) { // TODO Auto-generated method stub } public void strobe() { //shifts a byte out outbyte = latchbyte & 1; latchbyte = ((latchbyte >> 1) | 0x100); } public void output(final boolean state) { latchbyte = gamepadbyte | controllerbyte; } /** * Start in a separate thread the processing of the controller event queue. * Must be called after construction of the class to enable the processing * of the joystick / gamepad events. */ public void startEventQueue() { // if (System.getProperty("java.class.path").contains("jinput")) { thread.execute(eventQueueLoop()); // } } double threshold = 0.25; private Runnable eventQueueLoop() { return new Runnable() { @Override public void run() { if (gameController != null) { Event event = new Event(); while (!Thread.interrupted()) { gameController.poll(); EventQueue queue = gameController.getEventQueue(); while (queue.getNextEvent(event)) { Component component = event.getComponent(); if (component.getIdentifier() == Component.Identifier.Axis.X) { if (event.getValue() > threshold) { gamepadbyte |= BIT7;//left on, right off gamepadbyte &= ~BIT6; } else if (event.getValue() < -threshold) { gamepadbyte |= BIT6; gamepadbyte &= ~BIT7; } else { gamepadbyte &= ~(BIT7 | BIT6); } } else if (component.getIdentifier() == Component.Identifier.Axis.Y) { if (event.getValue() > threshold) { gamepadbyte |= BIT5;//up on, down off gamepadbyte &= ~BIT4; } else if (event.getValue() < -threshold) { gamepadbyte |= BIT4;//down on, up off gamepadbyte &= ~BIT5; } else { gamepadbyte &= ~(BIT4 | BIT5); } } else if (component == buttons[0]) { if (isPressed(event)) { gamepadbyte |= BIT0; } else { gamepadbyte &= ~BIT0; } } else if (component == buttons[1]) { if (isPressed(event)) { gamepadbyte |= BIT1; } else { gamepadbyte &= ~BIT1; } } else if (component == buttons[2]) { if (isPressed(event)) { gamepadbyte |= BIT2; } else { gamepadbyte &= ~BIT2; } } else if (component == buttons[3]) { if (isPressed(event)) { gamepadbyte |= BIT3; } else { gamepadbyte &= ~BIT3; } } } try { Thread.sleep(5); } catch (InterruptedException e) { // Preserve interrupt status Thread.currentThread().interrupt(); } } } } }; } private boolean isPressed(Event event) { Component component = event.getComponent(); if (component.isAnalog()) { if (Math.abs(event.getValue()) > 0.2f) { return true; } else { return false; } } else if (event.getValue() == 0) { return false; } else { return true; } } /** * Stop the controller event queue thread. Must be called before closing the * application. */ public void stopEventQueue() { thread.shutdownNow(); } /** * This method detects the available joysticks / gamepads on the computer * and return them in a list. * * @return List of available joysticks / gamepads connected to the computer */ private static Controller[] getAvailablePadControllers() { List<Controller> gameControllers = new ArrayList<>(); // Get a list of the controllers JInput knows about and can interact // with Controller[] controllers = ControllerEnvironment.getDefaultEnvironment().getControllers(); // Check the useable controllers (gamepads or joysticks with at least 2 // axis and 2 buttons) for (Controller controller : controllers) { if ((controller.getType() == Controller.Type.GAMEPAD) || (controller.getType() == Controller.Type.STICK)) { int nbOfAxis = 0; // Get this controllers components (buttons and axis) Component[] components = controller.getComponents(); // Check the availability of X/Y axis and at least 2 buttons // (for A and B, because select and start can use the keyboard) for (Component component : components) { if ((component.getIdentifier() == Component.Identifier.Axis.X) || (component.getIdentifier() == Component.Identifier.Axis.Y)) { nbOfAxis++; } } if ((nbOfAxis >= 2) && (getButtons(controller).length >= 2)) { // Valid game controller gameControllers.add(controller); } } } return gameControllers.toArray(new Controller[0]); } /** * Return the available buttons on this controller (by priority order). */ private static Component[] getButtons(Controller controller) { List<Component> buttons = new ArrayList<>(); // Get this controllers components (buttons and axis) Component[] components = controller.getComponents(); for (Component component : components) { if (component.getIdentifier() instanceof Component.Identifier.Button) { buttons.add(component); } } return buttons.toArray(new Component[0]); } public final void setButtons() { Preferences prefs = PrefsSingleton.get(); //reset the buttons from prefs m.clear(); switch (controllernum) { case 0: m.put(prefs.getInt("keyUp1", KeyEvent.VK_UP), BIT4); m.put(prefs.getInt("keyDown1", KeyEvent.VK_DOWN), BIT5); m.put(prefs.getInt("keyLeft1", KeyEvent.VK_LEFT), BIT6); m.put(prefs.getInt("keyRight1", KeyEvent.VK_RIGHT), BIT7); m.put(prefs.getInt("keyA1", KeyEvent.VK_X), BIT0); m.put(prefs.getInt("keyB1", KeyEvent.VK_Z), BIT1); m.put(prefs.getInt("keySelect1", KeyEvent.VK_SHIFT), BIT2); m.put(prefs.getInt("keyStart1", KeyEvent.VK_ENTER), BIT3); break; case 1: default: m.put(prefs.getInt("keyUp2", KeyEvent.VK_W), BIT4); m.put(prefs.getInt("keyDown2", KeyEvent.VK_S), BIT5); m.put(prefs.getInt("keyLeft2", KeyEvent.VK_A), BIT6); m.put(prefs.getInt("keyRight2", KeyEvent.VK_D), BIT7); m.put(prefs.getInt("keyA2", KeyEvent.VK_G), BIT0); m.put(prefs.getInt("keyB2", KeyEvent.VK_F), BIT1); m.put(prefs.getInt("keySelect2", KeyEvent.VK_R), BIT2); m.put(prefs.getInt("keyStart2", KeyEvent.VK_T), BIT3); break; } Controller[] controllers = getAvailablePadControllers(); if (controllers.length > controllernum) { this.gameController = controllers[controllernum]; PrefsSingleton.get().put("controller" + controllernum, gameController.getName()); System.err.println(controllernum + 1 + ". " + gameController.getName()); this.buttons = getButtons(controllers[controllernum]); } else { PrefsSingleton.get().put("controller" + controllernum, ""); this.gameController = null; this.buttons = null; } } }