diff --git a/src/com/limelight/Limelight.java b/src/com/limelight/Limelight.java index 01a9a45..53db4e8 100644 --- a/src/com/limelight/Limelight.java +++ b/src/com/limelight/Limelight.java @@ -8,6 +8,7 @@ import com.limelight.binding.LibraryHelper; import com.limelight.binding.PlatformBinding; import com.limelight.gui.MainFrame; import com.limelight.gui.StreamFrame; +import com.limelight.input.gamepad.Gamepad; import com.limelight.input.gamepad.GamepadListener; import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnectionListener; @@ -55,9 +56,7 @@ public class Limelight implements NvConnectionListener { VideoDecoderRenderer.FLAG_PREFER_QUALITY, PlatformBinding.getAudioRenderer(), PlatformBinding.getVideoDecoderRenderer()); - - GamepadListener.startSendingInput(conn); - + GamepadListener.getInstance().addDeviceListener(new Gamepad(conn)); } /* @@ -80,13 +79,6 @@ public class Limelight implements NvConnectionListener { } } - /* - * Starts up a thread that listens for gamepads connected to the system. - */ - private static void startControllerListener() { - GamepadListener.startUp(); - } - /* * Creates the main frame for the application. */ @@ -94,7 +86,6 @@ public class Limelight implements NvConnectionListener { MainFrame main = new MainFrame(); main.build(); limeFrame = main.getLimeFrame(); - startControllerListener(); } /** @@ -177,7 +168,6 @@ public class Limelight implements NvConnectionListener { @Override public void connectionStarted() { streamFrame.hideSpinner(); - GamepadListener.startSendingInput(conn); } /** diff --git a/src/com/limelight/gui/GamepadConfigFrame.java b/src/com/limelight/gui/GamepadConfigFrame.java index 085eb0b..97e04b9 100644 --- a/src/com/limelight/gui/GamepadConfigFrame.java +++ b/src/com/limelight/gui/GamepadConfigFrame.java @@ -13,7 +13,6 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.HashMap; -import java.util.List; import java.util.Map.Entry; import javax.swing.Box; @@ -24,17 +23,14 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.border.LineBorder; -import net.java.games.input.Component; -import net.java.games.input.Event; -import net.java.games.input.EventQueue; - +import com.limelight.input.Device; +import com.limelight.input.DeviceListener; import com.limelight.input.gamepad.GamepadComponent; import com.limelight.input.gamepad.GamepadListener; -import com.limelight.input.gamepad.Gamepad; -import com.limelight.input.gamepad.GamepadHandler; import com.limelight.input.gamepad.GamepadMapping; import com.limelight.input.gamepad.GamepadMapping.Mapping; import com.limelight.input.gamepad.SourceComponent; +import com.limelight.input.gamepad.SourceComponent.Type; import com.limelight.settings.GamepadSettingsManager; /** @@ -45,9 +41,8 @@ public class GamepadConfigFrame extends JFrame { private static final long serialVersionUID = 1L; private boolean configChanged = false; - private boolean shouldStartHandler = false; - private Thread mappingThread; + private MappingThread mappingThread; private GamepadMapping config; private HashMap componentMap; @@ -120,7 +115,8 @@ public class GamepadConfigFrame extends JFrame { mapButton.setMinimumSize(buttonSize); mapButton.setPreferredSize(buttonSize); mapButton.addActionListener(createMapListener()); - mapButton.setText(config.getMapping(mapping.contComp)); + + setButtonText(mapButton, config.getMapping(mapping.contComp)); invertBox.setSelected(mapping.invert); invertBox.addItemListener(createInvertListener()); @@ -189,10 +185,6 @@ public class GamepadConfigFrame extends JFrame { } if (configChanged) { updateConfigs(); - GamepadListener.startUp(); - } - if (shouldStartHandler) { - GamepadHandler.startUp(); } dispose(); } @@ -208,14 +200,12 @@ public class GamepadConfigFrame extends JFrame { public void actionPerformed(ActionEvent e) { Box toMap = (Box)((JButton)e.getSource()).getParent(); - List gamepads = GamepadHandler.getGamepads(); - - if (gamepads.isEmpty()) { + if (GamepadListener.getInstance().deviceCount() == 0) { JOptionPane.showMessageDialog(GamepadConfigFrame.this, "No Gamepad Detected"); return; } - map(toMap, gamepads.get(0)); + map(toMap); } }; } @@ -223,110 +213,23 @@ public class GamepadConfigFrame extends JFrame { /* * Maps a gamepad component to the clicked component */ - private void map(final Box toMap, final Gamepad pad) { + private void map(final Box toMap) { if (mappingThread == null || !mappingThread.isAlive()) { - + //a little janky, could probably be fixed up a bit final JButton buttonPressed = getButton(toMap); final Mapping mappingToMap = componentMap.get(toMap); buttonPressed.setSelected(true); - GamepadListener.stopListening(); - - if (GamepadHandler.isRunning()) { - GamepadHandler.stopHandler(); - shouldStartHandler = true; - } - buttonPressed.setText("Select Input"); - mappingThread = new Thread(new Runnable() { - @Override - public void run() { - - SourceComponent newComponent = waitForNewMapping(pad); - consumeEvents(pad); - - if (newComponent != null) { - Mapping oldConfig = config.get(newComponent); - if (oldConfig != null) { - getButton(getBox(oldConfig)).setText(""); - } - - config.insertMapping(mappingToMap, newComponent); - - buttonPressed.setText(newComponent.getComponent().getName()); - configChanged = true; - - } else { - buttonPressed.setText(config.getMapping(mappingToMap.contComp)); - } - - buttonPressed.setSelected(false); - } - }); - mappingThread.setName("Gamepad Mapping Thread"); + mappingThread = new MappingThread(buttonPressed, mappingToMap); mappingThread.start(); - } - - } - - /* - * Waits until the user chooses what to map to the clicked component - */ - private SourceComponent waitForNewMapping(Gamepad pad) { - SourceComponent newMapping = null; - - while (newMapping == null) { - if (pad.poll()) { - EventQueue queue = pad.getEvents(); - Event event = new Event(); - - while (queue.getNextEvent(event)) { - if (!pad.poll()) { - break; - } - - if (event.getComponent().getIdentifier() == Component.Identifier.Axis.POV) { - newMapping = new SourceComponent(event.getComponent(), ""+event.getValue()); - break; - } - else if (!event.getComponent().isAnalog() || Math.abs(event.getValue()) > .75F) { - newMapping = new SourceComponent(event.getComponent(), ""); - break; - } - } - } else { - break; - } - try { - Thread.sleep(100); - } catch (InterruptedException e) {} - } - return newMapping; - } - - /* - * Consumes any events left in the queue after the mapping has been made - */ - private void consumeEvents(final Gamepad pad) { - EventQueue queue = pad.getEvents(); - Event event = new Event(); - - for (int i = 0; i < 5; i++) { - if (!pad.poll()) { - break; - } - // Drop all events currently in the queue - while (queue.getNextEvent(event) && pad.poll()); - - // Give the queue a bit of time to fill again - try { - Thread.sleep(50); - } catch (InterruptedException e) {} + GamepadListener.getInstance().addDeviceListener(mappingThread); } + } /* @@ -358,4 +261,63 @@ public class GamepadConfigFrame extends JFrame { private void updateConfigs() { GamepadSettingsManager.writeSettings(config); } + + private void setButtonText(JButton button, SourceComponent comp) { + button.setText(comp.getType().name() + " " + comp.getId()); + } + + private class MappingThread extends Thread implements DeviceListener { + private SourceComponent newMapping = null; + private JButton buttonPressed; + private Mapping mappingToMap; + + public MappingThread(JButton buttonPressed, Mapping mappingToMap) { + super("Gamepad Mapping Thread"); + this.buttonPressed = buttonPressed; + this.mappingToMap = mappingToMap; + } + + @Override + public void run() { + + while (newMapping == null) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + setButtonText(buttonPressed, config.getMapping(mappingToMap.contComp)); + GamepadListener.getInstance().removeListener(this); + return; + } + } + + Mapping oldConfig = config.get(newMapping); + if (oldConfig != null) { + getButton(getBox(oldConfig)).setText(""); + } + + config.insertMapping(mappingToMap, newMapping); + + setButtonText(buttonPressed, newMapping); + configChanged = true; + + GamepadListener.getInstance().removeListener(this); + + } + + @Override + public void handleButton(Device device, int buttonId, boolean pressed) { + if (pressed) { + newMapping = new SourceComponent(Type.BUTTON, buttonId); + } + } + + @Override + public void handleAxis(Device device, int axisId, float newValue, + float lastValue) { + if (newValue > 0.75) { + newMapping = new SourceComponent(Type.AXIS, axisId); + } + } + + } } diff --git a/src/com/limelight/gui/MainFrame.java b/src/com/limelight/gui/MainFrame.java index 4b015bb..a2e8eb5 100644 --- a/src/com/limelight/gui/MainFrame.java +++ b/src/com/limelight/gui/MainFrame.java @@ -28,7 +28,6 @@ import org.xmlpull.v1.XmlPullParserException; import com.limelight.Limelight; import com.limelight.binding.PlatformBinding; -import com.limelight.input.gamepad.GamepadListener; import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.http.NvHTTP; import com.limelight.settings.PreferencesManager; @@ -63,7 +62,6 @@ public class MainFrame { @Override public void windowClosing(WindowEvent e) { super.windowClosing(e); - GamepadListener.stopListening(); } }); Container mainPane = limeFrame.getContentPane(); @@ -136,7 +134,6 @@ public class MainFrame { JMenu optionsMenu = new JMenu("Options"); JMenuItem gamepadSettings = new JMenuItem("Gamepad Settings"); JMenuItem generalSettings = new JMenuItem("Preferences"); - JMenuItem scanForGamepads = new JMenuItem("Scan for Gamepads"); gamepadSettings.addActionListener(new ActionListener() { @Override @@ -152,16 +149,8 @@ public class MainFrame { } }); - scanForGamepads.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - GamepadListener.rescanControllers(); - } - }); - optionsMenu.add(gamepadSettings); optionsMenu.add(generalSettings); - optionsMenu.add(scanForGamepads); menuBar.add(optionsMenu); diff --git a/src/com/limelight/input/Device.java b/src/com/limelight/input/Device.java new file mode 100644 index 0000000..901e332 --- /dev/null +++ b/src/com/limelight/input/Device.java @@ -0,0 +1,25 @@ +package com.limelight.input; + +public class Device { + private int id; + private int numButtons; + private int numAxes; + + public Device(int deviceID, int numButtons, int numAxes) { + this.id = deviceID; + this.numButtons = numButtons; + this.numAxes = numAxes; + } + + public int getId() { + return id; + } + + public int getNumButtons() { + return numButtons; + } + + public int getNumAxes() { + return numAxes; + } +} diff --git a/src/com/limelight/input/DeviceListener.java b/src/com/limelight/input/DeviceListener.java new file mode 100644 index 0000000..d344beb --- /dev/null +++ b/src/com/limelight/input/DeviceListener.java @@ -0,0 +1,6 @@ +package com.limelight.input; + +public interface DeviceListener { + public void handleButton(Device device, int buttonId, boolean pressed); + public void handleAxis(Device device, int axisId, float newValue, float lastValue); +} diff --git a/src/com/limelight/input/gamepad/Gamepad.java b/src/com/limelight/input/gamepad/Gamepad.java index 58dc732..aa08977 100644 --- a/src/com/limelight/input/gamepad/Gamepad.java +++ b/src/com/limelight/input/gamepad/Gamepad.java @@ -1,23 +1,19 @@ package com.limelight.input.gamepad; +import com.limelight.input.Device; +import com.limelight.input.DeviceListener; import com.limelight.input.gamepad.GamepadMapping.Mapping; +import com.limelight.input.gamepad.SourceComponent.Type; import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.input.ControllerPacket; - -import net.java.games.input.Component; -import net.java.games.input.Component.Identifier; -import net.java.games.input.Controller; -import net.java.games.input.Event; -import net.java.games.input.EventQueue; +import com.limelight.settings.GamepadSettingsManager; /** * Represents a gamepad connected to the system * @author Diego Waxemberg */ -public class Gamepad { - private Controller pad; - private GamepadMapping config; - +public class Gamepad implements DeviceListener { + private short inputMap = 0x0000; private byte leftTrigger = 0x00; private byte rightTrigger = 0x00; @@ -26,231 +22,78 @@ public class Gamepad { private short leftStickX = 0x0000; private short leftStickY = 0x0000; - /** - * Constructs a new gamepad from the specified controller that has the given mappings - * @param pad the controller to be used as a gamepad - * @param settings the mappings for the gamepad - */ - public Gamepad(Controller pad, GamepadMapping settings) { - this.config = settings; - this.pad = pad; - - for (Component comp : pad.getComponents()) { - initValue(comp); - } - } - - /* - * Initializes the value of the given component to its current state - */ - private void initValue(Component comp) { - handleComponent(comp, comp.getPollData()); - } - - /** - * Polls the gamepad for component values. - * @return true if the gamepad was polled successfully, false otherwise - */ - public boolean poll() { - return pad.poll(); - } - - /* - * Sends a controller packet to the specified connection containing the current gamepad values - */ - private void sendControllerPacket(NvConnection conn) { - if (conn != null) { - conn.sendControllerInput(inputMap, leftTrigger, rightTrigger, - leftStickX, leftStickY, rightStickX, rightStickY); - } - } - - /** - * Gets the event queue for this gamepad - * @return this gamepad's event queue - */ - public EventQueue getEvents() { - return pad.getEventQueue(); - } - - /** - * Handles the events in this gamepad's event queue and sends them - * to the specified connection - * @param conn the connection to the host that will receive the events - */ - public void handleEvents(NvConnection conn) { - EventQueue queue = pad.getEventQueue(); - Event event = new Event(); - - while(queue.getNextEvent(event)) { - - /* uncommented for debugging */ - //printInfo(pad, event); - - handleEvent(event); - - sendControllerPacket(conn); - } - - } - - /* - * Prints out the specified event information for the given gamepad - * used for debugging, normally unused. - */ - public static void printInfo(Controller gamepad, Event event) { - Component comp = event.getComponent(); - - StringBuilder builder = new StringBuilder(gamepad.getName()); - - builder.append(" at "); - builder.append(event.getNanos()).append(": "); - builder.append(comp.getName()).append(" changed to "); - - - float value = event.getValue(); - if(comp.isAnalog()) { - builder.append(value); - } else if (comp.getIdentifier() == Identifier.Axis.POV) { - if (value == Component.POV.DOWN) { - builder.append("Down"); - } - else if (value == Component.POV.UP) { - builder.append("Up"); - } - else if (value == Component.POV.LEFT) { - builder.append("Left"); - } - else if (value == Component.POV.RIGHT) { - builder.append("Right"); - } - else if (value == Component.POV.CENTER) { - builder.append("Center"); - } - else if (value == Component.POV.DOWN_LEFT) { - builder.append("Down-left"); - } - else if (value == Component.POV.DOWN_RIGHT) { - builder.append("Down-right"); - } - else if (value == Component.POV.UP_LEFT) { - builder.append("Up-left"); - } - else if (value == Component.POV.UP_RIGHT) { - builder.append("Up-right"); - } - else { - builder.append("Unknown"); - } - } else { - if(value==1.0f) { - builder.append("On"); - } else { - builder.append("Off"); - } - } - - System.out.println(builder.toString()); - } - - /* - * Handles a given event - */ - private void handleEvent(Event event) { - Component comp = event.getComponent(); - float value = event.getValue(); - - handleComponent(comp, value); - } - - /* - * Handles the component that an event occurred on - */ - private void handleComponent(Component comp, float value) { - Mapping mapping = config.get(comp); - if (mapping != null) { - if (mapping.contComp.isAnalog()) { - handleAnalog(mapping.contComp, sanitizeValue(mapping, value)); - } else if (comp.getIdentifier() == Component.Identifier.Axis.POV) { - // The values are directional constants so they cannot be sanitized - handlePOV(value); - } else { - handleButtons(mapping.contComp, sanitizeValue(mapping, value)); - } - } - } - - /* - * Fixes the value as specified in the mapping, that is inverts if needed, etc. - */ - private float sanitizeValue(Mapping mapping, float value) { - float sanitized = value; - if (mapping.invert) { - sanitized = -sanitized; - } - if (mapping.trigger) { - sanitized = (sanitized + 1)/2; - } - return sanitized; + private NvConnection conn; + + public Gamepad(NvConnection conn) { + this.conn = conn; } - /* - * Toggles a flag that indicates the specified button was pressed or released - */ - private void toggle(short button, boolean pressed) { - if (pressed) { - inputMap |= button; - } else { - inputMap &= ~button; - } + public Gamepad() { + this(null); } - /* - * Handles POV component input - */ - private void handlePOV(float value) { - if (value == Component.POV.UP || - value == Component.POV.UP_LEFT || - value == Component.POV.UP_RIGHT) { - toggle(ControllerPacket.UP_FLAG, true); - } - else { - toggle(ControllerPacket.UP_FLAG, false); - } - - if (value == Component.POV.DOWN || - value == Component.POV.DOWN_LEFT || - value == Component.POV.DOWN_RIGHT) { - toggle(ControllerPacket.DOWN_FLAG, true); - } - else { - toggle(ControllerPacket.DOWN_FLAG, false); - } - - if (value == Component.POV.LEFT || - value == Component.POV.DOWN_LEFT || - value == Component.POV.UP_LEFT) { - toggle(ControllerPacket.LEFT_FLAG, true); - } - else { - toggle(ControllerPacket.LEFT_FLAG, false); - } - - if (value == Component.POV.RIGHT || - value == Component.POV.UP_RIGHT || - value == Component.POV.DOWN_RIGHT) { - toggle(ControllerPacket.RIGHT_FLAG, true); - } - else { - toggle(ControllerPacket.RIGHT_FLAG, false); - } + public void setConnection(NvConnection conn) { + this.conn = conn; } - /* - * Handles analog component input - */ - private void handleAnalog(GamepadComponent contComp, float value) { - switch (contComp) { + @Override + public void handleButton(Device device, int buttonId, boolean pressed) { + GamepadMapping mapping = GamepadSettingsManager.getSettings(); + + Mapping mapped = mapping.get(new SourceComponent(Type.BUTTON, buttonId)); + if (mapped == null) { + System.out.println("Unmapped button pressed: " + buttonId); + return; + } + + if (!mapped.contComp.isAnalog()) { + handleDigitalComponent(mapped, pressed); + } else { + handleAnalogComponent(mapped.contComp, sanitizeValue(mapped, pressed)); + } + + //printInfo(device, new SourceComponent(Type.BUTTON, buttonId), mapped.contComp, pressed ? 1F : 0F); + } + + @Override + public void handleAxis(Device device, int axisId, float newValue, float lastValue) { + GamepadMapping mapping = GamepadSettingsManager.getSettings(); + + Mapping mapped = mapping.get(new SourceComponent(Type.AXIS, axisId)); + if (mapped == null) { + System.out.println("Unmapped axis moved: " + axisId); + return; + } + + float value = sanitizeValue(mapped, newValue); + + if (mapped.contComp.isAnalog()) { + handleAnalogComponent(mapped.contComp, value); + } else { + handleDigitalComponent(mapped, (value > 0.5)); + } + + //printInfo(device, new SourceComponent(Type.AXIS, axisId), mapped.contComp, newValue); + } + + private float sanitizeValue(Mapping mapped, boolean value) { + if (mapped.invert) { + return value ? 0F : 1F; + } else { + return value ? 1F : 0F; + } + } + + private float sanitizeValue(Mapping mapped, float value) { + if (mapped.invert) { + return -value; + } else { + return value; + } + } + + private void handleAnalogComponent(GamepadComponent padComp, float value) { + switch (padComp) { case LS_X: leftStickX = (short)Math.round(value * 0x7FFF); break; @@ -270,70 +113,107 @@ public class Gamepad { rightTrigger = (byte)Math.round(value * 0xFF); break; default: - System.out.println("A mapping error has occured. Ignoring: " + contComp.name()); + System.out.println("A mapping error has occured. Ignoring: " + padComp.name()); break; } + if (conn != null) { + sendControllerPacket(); + } + } + + + private void handleDigitalComponent(Mapping mapped, boolean pressed) { + switch (mapped.contComp) { + case BTN_A: + toggle(ControllerPacket.A_FLAG, pressed); + break; + case BTN_X: + toggle(ControllerPacket.X_FLAG, pressed); + break; + case BTN_Y: + toggle(ControllerPacket.Y_FLAG, pressed); + break; + case BTN_B: + toggle(ControllerPacket.B_FLAG, pressed); + break; + case DPAD_UP: + toggle(ControllerPacket.UP_FLAG, pressed); + break; + case DPAD_DOWN: + toggle(ControllerPacket.DOWN_FLAG, pressed); + break; + case DPAD_LEFT: + toggle(ControllerPacket.LEFT_FLAG, pressed); + break; + case DPAD_RIGHT: + toggle(ControllerPacket.RIGHT_FLAG, pressed); + break; + case LS_THUMB: + toggle(ControllerPacket.LS_CLK_FLAG, pressed); + break; + case RS_THUMB: + toggle(ControllerPacket.RS_CLK_FLAG, pressed); + break; + case LB: + toggle(ControllerPacket.LB_FLAG, pressed); + break; + case RB: + toggle(ControllerPacket.RB_FLAG, pressed); + break; + case BTN_START: + toggle(ControllerPacket.PLAY_FLAG, pressed); + break; + case BTN_BACK: + toggle(ControllerPacket.BACK_FLAG, pressed); + break; + case BTN_SPECIAL: + toggle(ControllerPacket.SPECIAL_BUTTON_FLAG, pressed); + break; + default: + System.out.println("A mapping error has occured. Ignoring: " + mapped.contComp.name()); + return; + } + if (conn != null) { + sendControllerPacket(); + } } /* - * Handles button input + * Sends a controller packet to the specified connection containing the current gamepad values */ - private void handleButtons(GamepadComponent contComp, float value) { - boolean press = false; - - if (value > 0.5F) { - press = true; - } - - switch (contComp) { - case BTN_A: - toggle(ControllerPacket.A_FLAG, press); - break; - case BTN_X: - toggle(ControllerPacket.X_FLAG, press); - break; - case BTN_Y: - toggle(ControllerPacket.Y_FLAG, press); - break; - case BTN_B: - toggle(ControllerPacket.B_FLAG, press); - break; - case DPAD_UP: - toggle(ControllerPacket.UP_FLAG, press); - break; - case DPAD_DOWN: - toggle(ControllerPacket.DOWN_FLAG, press); - break; - case DPAD_LEFT: - toggle(ControllerPacket.LEFT_FLAG, press); - break; - case DPAD_RIGHT: - toggle(ControllerPacket.RIGHT_FLAG, press); - break; - case LS_THUMB: - toggle(ControllerPacket.LS_CLK_FLAG, press); - break; - case RS_THUMB: - toggle(ControllerPacket.RS_CLK_FLAG, press); - break; - case LB: - toggle(ControllerPacket.LB_FLAG, press); - break; - case RB: - toggle(ControllerPacket.RB_FLAG, press); - break; - case BTN_START: - toggle(ControllerPacket.PLAY_FLAG, press); - break; - case BTN_BACK: - toggle(ControllerPacket.BACK_FLAG, press); - break; - case BTN_SPECIAL: - toggle(ControllerPacket.SPECIAL_BUTTON_FLAG, press); - break; - default: - System.out.println("A mapping error has occured. Ignoring: " + contComp.name()); - break; + private void sendControllerPacket() { + if (conn != null) { + conn.sendControllerInput(inputMap, leftTrigger, rightTrigger, + leftStickX, leftStickY, rightStickX, rightStickY); } } + + /* + * Prints out the specified event information for the given gamepad + * used for debugging, normally unused. + */ + @SuppressWarnings("unused") + private void printInfo(Device device, SourceComponent sourceComp, GamepadComponent padComp, float value) { + + StringBuilder builder = new StringBuilder(); + + builder.append(sourceComp.getType().name() + ": "); + builder.append(sourceComp.getId() + " "); + builder.append("mapped to: " + padComp + " "); + builder.append("changed to "); + + System.out.println(builder.toString()); + } + + /* + * Toggles a flag that indicates the specified button was pressed or released + */ + private void toggle(short button, boolean pressed) { + if (pressed) { + inputMap |= button; + } else { + inputMap &= ~button; + } + } + } diff --git a/src/com/limelight/input/gamepad/GamepadHandler.java b/src/com/limelight/input/gamepad/GamepadHandler.java deleted file mode 100644 index 4410853..0000000 --- a/src/com/limelight/input/gamepad/GamepadHandler.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.limelight.input.gamepad; - - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import com.limelight.nvstream.NvConnection; -import com.limelight.settings.GamepadSettingsManager; - -import net.java.games.input.Controller; - -/** - * A handler for all the gamepads attached to this system - * @author Diego Waxemberg - */ -public class GamepadHandler { - private static LinkedList gamepads = new LinkedList(); - private static Lock gamepadLock = new ReentrantLock(); - private static NvConnection conn; - private static Thread handler; - private static boolean run = true; - - /** - * Adds the list of controllers as gamepads to be handled - * @param pads the controllers to be handled as gamepads - */ - public static void addGamepads(List pads) { - LinkedList newPadList = new LinkedList(); - - GamepadMapping settings = GamepadSettingsManager.getSettings(); - for (Controller pad : pads) { - - newPadList.add(new Gamepad(pad, settings)); - } - - gamepadLock.lock(); - gamepads = newPadList; - gamepadLock.unlock(); - } - - /** - * Sets the connection to which the handled gamepads should send events - * @param connection the connection to the host - */ - public static void setConnection(NvConnection connection) { - conn = connection; - } - - /** - * Gets a list of the currently handled gamepads - * @return an unmodifiable list of gamepads this handler handles - */ - public static List getGamepads() { - return Collections.unmodifiableList(gamepads); - } - - /** - * Starts up a thread that handles the gamepads - */ - public static void startUp() { - if (handler == null || !handler.isAlive()) { - run = true; - System.out.println("Gamepad Handler thread starting up"); - handler = new Thread(new Runnable() { - @Override - public void run() { - while (run) { - try { - gamepadLock.lockInterruptibly(); - } catch (InterruptedException e1) { - run = false; - } - for (Gamepad gamepad : gamepads) { - if (!gamepad.poll()) { - break; - } - gamepad.handleEvents(conn); - } - gamepadLock.unlock(); - try { - Thread.sleep(20); - } catch (InterruptedException e) { - run = false; - } - } - } - }); - handler.start(); - } - } - - /** - * Stops handling the gamepads - */ - public static void stopHandler() { - if (handler != null && handler.isAlive()) { - System.out.println("Stopping Gamepad Handler thread"); - handler.interrupt(); - conn = null; - } - } - - /** - * Checks if the handler is handling the gamepads. - * @return whether this handler is running - */ - public static boolean isRunning() { - return (handler != null && handler.isAlive()); - } - -} diff --git a/src/com/limelight/input/gamepad/GamepadListener.java b/src/com/limelight/input/gamepad/GamepadListener.java index 7fa2ebf..b764469 100644 --- a/src/com/limelight/input/gamepad/GamepadListener.java +++ b/src/com/limelight/input/gamepad/GamepadListener.java @@ -1,145 +1,83 @@ package com.limelight.input.gamepad; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; +import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.List; -import com.limelight.nvstream.NvConnection; - -import net.java.games.input.Controller; -import net.java.games.input.ControllerEnvironment; +import com.limelight.input.Device; +import com.limelight.input.DeviceListener; /** * Listens to Controllers connected to this computer and gives any gamepad to the gamepad handler * @author Diego Waxemberg */ -public class GamepadListener { - private static Thread listenerThread; - private static NvConnection conn; - private static ControllerEnvironment defaultEnv; +public class GamepadListener implements NativeGamepadListener { + private HashMap devices; + private List listeners; - /** - * starts a thread to listen to controllers - * @return true if it started a thread, false if the thread is already running. - */ - public static boolean startUp() { - // Suppress spam from jinput log warnings in DefaultControllerEnvironment - Logger.getLogger(ControllerEnvironment.getDefaultEnvironment().getClass().getName()).setLevel(Level.SEVERE); - - if (listenerThread == null || !listenerThread.isAlive()) { - System.out.println("Controller Listener thread starting up"); - listenerThread = new Thread() { - @Override - public void run() { - try { - - defaultEnv = ControllerEnvironment.getDefaultEnvironment(); - - while(!isInterrupted()) { - - Controller[] ca = defaultEnv.getControllers(); - LinkedList gamepads = new LinkedList(); + private static GamepadListener singleton; + + public static GamepadListener getInstance() { + if (singleton == null) { + singleton = new GamepadListener(); + } + return singleton; + } + + private GamepadListener() { + devices = new HashMap(); + listeners = new LinkedList(); + } + + public int deviceCount() { + return devices.size(); + } + + public void addDeviceListener(DeviceListener listener) { + listeners.add(listener); + } + + public List getListeners() { + return Collections.unmodifiableList(listeners); + } + + public void removeListener(DeviceListener listener) { + listeners.remove(listener); + } + + @Override + public void deviceAttached(int deviceId, int numButtons, int numAxes) { + devices.put(deviceId, new Device(deviceId, numButtons, numAxes)); + } - /* - * iterates through the controllers and adds gamepads and ps3 controller to the list - * NOTE: JInput does not consider a PS3 controller to be a gamepad (it thinks it's a "STICK") so we must use the - * name of it. - */ - for(int i = 0; i < ca.length; i++){ - if (ca[i].getType() == Controller.Type.GAMEPAD) { - gamepads.add(ca[i]); - } else if (ca[i].getName().contains("PLAYSTATION")) { - gamepads.add(ca[i]); - } - } + @Override + public void deviceRemoved(int deviceId) { + devices.remove(deviceId); + } - GamepadHandler.addGamepads(gamepads); - GamepadHandler.setConnection(conn); - - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - return; - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - }; - listenerThread.start(); - return true; - } - return false; - } - - /** - * This is really janky, but it is currently the only way to rescan for controllers. - * The DefaultControllerEnvironment class caches the results of scanning and if a controller is - * unplugged or plugged in, it will not detect it. Since DefaultControllerEnvironment is package-protected - * we have to use reflections in order to create a new instance to scan for controllers - */ - public static void rescanControllers() { - try { - System.out.println("Rescanning for controllers..."); - Constructor envConst = ControllerEnvironment.getDefaultEnvironment().getClass().getDeclaredConstructor((Class[])null); - envConst.setAccessible(true); - defaultEnv = (ControllerEnvironment) envConst.newInstance((Object[])null); - } catch (SecurityException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (NoSuchMethodException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InstantiationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalAccessException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvocationTargetException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } - - /** - * Stops listening for controllers, ie. stops the thread if it is running - */ - public static void stopListening() { - if (listenerThread != null && listenerThread.isAlive()) { - System.out.println("Stopping Controller Listener thread"); - listenerThread.interrupt(); - } - if (GamepadHandler.isRunning()) { - GamepadHandler.stopHandler(); + @Override + public void buttonDown(int deviceId, int buttonId) { + Device dev = devices.get(deviceId); + for (DeviceListener listener : listeners) { + listener.handleButton(dev, buttonId, true); } } - - /** - * Tells the handler to start sending gamepad events to the specified connection - * @param connection the connection to the host that will receive gamepad events - */ - public static void startSendingInput(NvConnection connection) { - System.out.println("Starting to send controller input"); - conn = connection; - if (!GamepadHandler.isRunning()) { - GamepadHandler.startUp(); + + @Override + public void buttonUp(int deviceId, int buttonId) { + Device dev = devices.get(deviceId); + for (DeviceListener listener : listeners) { + listener.handleButton(dev, buttonId, false); } } - - /** - * Tells the handler to stop sending events to the host - */ - public static void stopSendingInput() { - System.out.println("Stopping sending controller input"); - conn = null; + + @Override + public void axisMoved(int deviceId, int axisId, float value, float lastValue) { + Device dev = devices.get(deviceId); + for (DeviceListener listener : listeners) { + listener.handleAxis(dev, axisId, value, lastValue); + } } } diff --git a/src/com/limelight/input/gamepad/GamepadMapping.java b/src/com/limelight/input/gamepad/GamepadMapping.java index 1525b2a..f3a3138 100644 --- a/src/com/limelight/input/gamepad/GamepadMapping.java +++ b/src/com/limelight/input/gamepad/GamepadMapping.java @@ -4,9 +4,6 @@ import java.io.Serializable; import java.util.HashMap; import java.util.Map.Entry; - -import net.java.games.input.Component; - /** * Mappings for gamepad components * @author Diego Waxemberg @@ -14,13 +11,13 @@ import net.java.games.input.Component; public class GamepadMapping implements Serializable { private static final long serialVersionUID = -185035113915743149L; - private HashMap mapping; + private HashMap mapping; /** * Constructs a new mapping that has nothing mapped. */ public GamepadMapping() { - mapping = new HashMap(); + mapping = new HashMap(); } /** @@ -29,11 +26,7 @@ public class GamepadMapping implements Serializable { * @param comp the gamepad component to map to. */ public void insertMapping(Mapping toMap, SourceComponent comp) { - // This is the base mapping for components with multiple "buttons" - mapping.put(comp.getComponentId(), toMap); - - // This is the more-specific mapping for the specific buttons - mapping.put(comp.getFullUniqueId(), toMap); + mapping.put(comp, toMap); } /** @@ -41,17 +34,8 @@ public class GamepadMapping implements Serializable { * @param comp the gamepad component to get a mapping for * @return a mapping for the requested component */ - public Mapping get(Component comp) { - return mapping.get(comp.getIdentifier().getName()); - } - - /** - * Gets the mapping for the specified source component - * @param comp the source component to get a mapping for - * @return a mapping for the requested component - */ public Mapping get(SourceComponent comp) { - return mapping.get(comp.getFullUniqueId()); + return mapping.get(comp); } /** @@ -59,27 +43,18 @@ public class GamepadMapping implements Serializable { * @param comp the component to no longer be mapped. */ public void remove(SourceComponent comp) { - // Remove the most specific mapping - mapping.remove(comp.getFullUniqueId()); - - for (Entry entry : mapping.entrySet()) { - if (entry.getKey().startsWith(comp.getComponentId())) { - return; - } - } - - // Remove the common mapping if no more specific mappings remain - mapping.remove(comp.getComponentId()); + mapping.remove(comp); } /** * Gets the mapped ControllerComponent for the specified ControllerComponent.
+ * NOTE: Iterates a hashmap, use sparingly * @param contComp the component to get a mapping for * @return a mapping or an null if there is none */ public Mapping get(GamepadComponent contComp) { //#allTheJank - for (Entry entry : mapping.entrySet()) { + for (Entry entry : mapping.entrySet()) { if (entry.getValue().contComp == contComp) { return entry.getValue(); } @@ -89,16 +64,17 @@ public class GamepadMapping implements Serializable { /** * Gets the mapping for the specified component.
+ * NOTE: Iterates a hashmap, use sparingly * @param contComp the component to get a mapping for * @return a mapping or an empty string if there is none */ - public String getMapping(GamepadComponent contComp) { - for (Entry entry : mapping.entrySet()) { + public SourceComponent getMapping(GamepadComponent contComp) { + for (Entry entry : mapping.entrySet()) { if (entry.getValue().contComp == contComp) { return entry.getKey(); } } - return ""; + return null; } /** diff --git a/src/com/limelight/input/gamepad/SourceComponent.java b/src/com/limelight/input/gamepad/SourceComponent.java index 0474eca..26a04f7 100644 --- a/src/com/limelight/input/gamepad/SourceComponent.java +++ b/src/com/limelight/input/gamepad/SourceComponent.java @@ -1,29 +1,42 @@ package com.limelight.input.gamepad; -import net.java.games.input.Component; - public class SourceComponent { - private Component component; - private String id; + public enum Type { AXIS, BUTTON } + private Type type; + private int id; - public SourceComponent(Component component, String id) { - this.component = component; + public SourceComponent(Type type, int id) { + this.type = type; this.id = id; } - public Component getComponent() { - return component; + public Type getType() { + return type; } - public String getId() { + public int getId() { return id; } - public String getFullUniqueId() { - return getComponentId() + " " + getId(); + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || !(obj instanceof SourceComponent)) { + return false; + } + SourceComponent other = (SourceComponent) obj; - public String getComponentId() { - return component.getIdentifier().getName(); + return id == other.id && type == other.type; } }