rewrote gamepad stuff to not use JInput

This commit is contained in:
Diego Waxemberg
2013-12-30 22:13:25 -05:00
parent 8bd6a9d07f
commit 5396ce03ed
10 changed files with 373 additions and 708 deletions

View File

@@ -8,6 +8,7 @@ import com.limelight.binding.LibraryHelper;
import com.limelight.binding.PlatformBinding; import com.limelight.binding.PlatformBinding;
import com.limelight.gui.MainFrame; import com.limelight.gui.MainFrame;
import com.limelight.gui.StreamFrame; import com.limelight.gui.StreamFrame;
import com.limelight.input.gamepad.Gamepad;
import com.limelight.input.gamepad.GamepadListener; import com.limelight.input.gamepad.GamepadListener;
import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.NvConnectionListener; import com.limelight.nvstream.NvConnectionListener;
@@ -55,9 +56,7 @@ public class Limelight implements NvConnectionListener {
VideoDecoderRenderer.FLAG_PREFER_QUALITY, VideoDecoderRenderer.FLAG_PREFER_QUALITY,
PlatformBinding.getAudioRenderer(), PlatformBinding.getAudioRenderer(),
PlatformBinding.getVideoDecoderRenderer()); PlatformBinding.getVideoDecoderRenderer());
GamepadListener.getInstance().addDeviceListener(new Gamepad(conn));
GamepadListener.startSendingInput(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. * Creates the main frame for the application.
*/ */
@@ -94,7 +86,6 @@ public class Limelight implements NvConnectionListener {
MainFrame main = new MainFrame(); MainFrame main = new MainFrame();
main.build(); main.build();
limeFrame = main.getLimeFrame(); limeFrame = main.getLimeFrame();
startControllerListener();
} }
/** /**
@@ -177,7 +168,6 @@ public class Limelight implements NvConnectionListener {
@Override @Override
public void connectionStarted() { public void connectionStarted() {
streamFrame.hideSpinner(); streamFrame.hideSpinner();
GamepadListener.startSendingInput(conn);
} }
/** /**

View File

@@ -13,7 +13,6 @@ import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.awt.event.WindowListener; import java.awt.event.WindowListener;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import javax.swing.Box; import javax.swing.Box;
@@ -24,17 +23,14 @@ import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.border.LineBorder; import javax.swing.border.LineBorder;
import net.java.games.input.Component; import com.limelight.input.Device;
import net.java.games.input.Event; import com.limelight.input.DeviceListener;
import net.java.games.input.EventQueue;
import com.limelight.input.gamepad.GamepadComponent; import com.limelight.input.gamepad.GamepadComponent;
import com.limelight.input.gamepad.GamepadListener; 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;
import com.limelight.input.gamepad.GamepadMapping.Mapping; import com.limelight.input.gamepad.GamepadMapping.Mapping;
import com.limelight.input.gamepad.SourceComponent; import com.limelight.input.gamepad.SourceComponent;
import com.limelight.input.gamepad.SourceComponent.Type;
import com.limelight.settings.GamepadSettingsManager; import com.limelight.settings.GamepadSettingsManager;
/** /**
@@ -45,9 +41,8 @@ public class GamepadConfigFrame extends JFrame {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private boolean configChanged = false; private boolean configChanged = false;
private boolean shouldStartHandler = false;
private Thread mappingThread; private MappingThread mappingThread;
private GamepadMapping config; private GamepadMapping config;
private HashMap<Box, Mapping> componentMap; private HashMap<Box, Mapping> componentMap;
@@ -120,7 +115,8 @@ public class GamepadConfigFrame extends JFrame {
mapButton.setMinimumSize(buttonSize); mapButton.setMinimumSize(buttonSize);
mapButton.setPreferredSize(buttonSize); mapButton.setPreferredSize(buttonSize);
mapButton.addActionListener(createMapListener()); mapButton.addActionListener(createMapListener());
mapButton.setText(config.getMapping(mapping.contComp));
setButtonText(mapButton, config.getMapping(mapping.contComp));
invertBox.setSelected(mapping.invert); invertBox.setSelected(mapping.invert);
invertBox.addItemListener(createInvertListener()); invertBox.addItemListener(createInvertListener());
@@ -189,10 +185,6 @@ public class GamepadConfigFrame extends JFrame {
} }
if (configChanged) { if (configChanged) {
updateConfigs(); updateConfigs();
GamepadListener.startUp();
}
if (shouldStartHandler) {
GamepadHandler.startUp();
} }
dispose(); dispose();
} }
@@ -208,14 +200,12 @@ public class GamepadConfigFrame extends JFrame {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
Box toMap = (Box)((JButton)e.getSource()).getParent(); Box toMap = (Box)((JButton)e.getSource()).getParent();
List<Gamepad> gamepads = GamepadHandler.getGamepads(); if (GamepadListener.getInstance().deviceCount() == 0) {
if (gamepads.isEmpty()) {
JOptionPane.showMessageDialog(GamepadConfigFrame.this, "No Gamepad Detected"); JOptionPane.showMessageDialog(GamepadConfigFrame.this, "No Gamepad Detected");
return; 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 * 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()) { if (mappingThread == null || !mappingThread.isAlive()) {
//a little janky, could probably be fixed up a bit //a little janky, could probably be fixed up a bit
final JButton buttonPressed = getButton(toMap); final JButton buttonPressed = getButton(toMap);
final Mapping mappingToMap = componentMap.get(toMap); final Mapping mappingToMap = componentMap.get(toMap);
buttonPressed.setSelected(true); buttonPressed.setSelected(true);
GamepadListener.stopListening();
if (GamepadHandler.isRunning()) {
GamepadHandler.stopHandler();
shouldStartHandler = true;
}
buttonPressed.setText("Select Input"); buttonPressed.setText("Select Input");
mappingThread = new Thread(new Runnable() { mappingThread = new MappingThread(buttonPressed, mappingToMap);
@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.start(); 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 GamepadListener.getInstance().addDeviceListener(mappingThread);
while (queue.getNextEvent(event) && pad.poll());
// Give the queue a bit of time to fill again
try {
Thread.sleep(50);
} catch (InterruptedException e) {}
} }
} }
/* /*
@@ -358,4 +261,63 @@ public class GamepadConfigFrame extends JFrame {
private void updateConfigs() { private void updateConfigs() {
GamepadSettingsManager.writeSettings(config); 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);
}
}
}
} }

View File

@@ -28,7 +28,6 @@ import org.xmlpull.v1.XmlPullParserException;
import com.limelight.Limelight; import com.limelight.Limelight;
import com.limelight.binding.PlatformBinding; import com.limelight.binding.PlatformBinding;
import com.limelight.input.gamepad.GamepadListener;
import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.http.NvHTTP; import com.limelight.nvstream.http.NvHTTP;
import com.limelight.settings.PreferencesManager; import com.limelight.settings.PreferencesManager;
@@ -63,7 +62,6 @@ public class MainFrame {
@Override @Override
public void windowClosing(WindowEvent e) { public void windowClosing(WindowEvent e) {
super.windowClosing(e); super.windowClosing(e);
GamepadListener.stopListening();
} }
}); });
Container mainPane = limeFrame.getContentPane(); Container mainPane = limeFrame.getContentPane();
@@ -136,7 +134,6 @@ public class MainFrame {
JMenu optionsMenu = new JMenu("Options"); JMenu optionsMenu = new JMenu("Options");
JMenuItem gamepadSettings = new JMenuItem("Gamepad Settings"); JMenuItem gamepadSettings = new JMenuItem("Gamepad Settings");
JMenuItem generalSettings = new JMenuItem("Preferences"); JMenuItem generalSettings = new JMenuItem("Preferences");
JMenuItem scanForGamepads = new JMenuItem("Scan for Gamepads");
gamepadSettings.addActionListener(new ActionListener() { gamepadSettings.addActionListener(new ActionListener() {
@Override @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(gamepadSettings);
optionsMenu.add(generalSettings); optionsMenu.add(generalSettings);
optionsMenu.add(scanForGamepads);
menuBar.add(optionsMenu); menuBar.add(optionsMenu);

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -1,23 +1,19 @@
package com.limelight.input.gamepad; 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.GamepadMapping.Mapping;
import com.limelight.input.gamepad.SourceComponent.Type;
import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.input.ControllerPacket; import com.limelight.nvstream.input.ControllerPacket;
import com.limelight.settings.GamepadSettingsManager;
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;
/** /**
* Represents a gamepad connected to the system * Represents a gamepad connected to the system
* @author Diego Waxemberg * @author Diego Waxemberg
*/ */
public class Gamepad { public class Gamepad implements DeviceListener {
private Controller pad;
private GamepadMapping config;
private short inputMap = 0x0000; private short inputMap = 0x0000;
private byte leftTrigger = 0x00; private byte leftTrigger = 0x00;
private byte rightTrigger = 0x00; private byte rightTrigger = 0x00;
@@ -26,231 +22,78 @@ public class Gamepad {
private short leftStickX = 0x0000; private short leftStickX = 0x0000;
private short leftStickY = 0x0000; private short leftStickY = 0x0000;
/** private NvConnection conn;
* Constructs a new gamepad from the specified controller that has the given mappings
* @param pad the controller to be used as a gamepad public Gamepad(NvConnection conn) {
* @param settings the mappings for the gamepad this.conn = conn;
*/
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;
} }
/* public Gamepad() {
* Toggles a flag that indicates the specified button was pressed or released this(null);
*/
private void toggle(short button, boolean pressed) {
if (pressed) {
inputMap |= button;
} else {
inputMap &= ~button;
}
} }
/* public void setConnection(NvConnection conn) {
* Handles POV component input this.conn = conn;
*/
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);
}
} }
/* @Override
* Handles analog component input public void handleButton(Device device, int buttonId, boolean pressed) {
*/ GamepadMapping mapping = GamepadSettingsManager.getSettings();
private void handleAnalog(GamepadComponent contComp, float value) {
switch (contComp) { 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: case LS_X:
leftStickX = (short)Math.round(value * 0x7FFF); leftStickX = (short)Math.round(value * 0x7FFF);
break; break;
@@ -270,70 +113,107 @@ public class Gamepad {
rightTrigger = (byte)Math.round(value * 0xFF); rightTrigger = (byte)Math.round(value * 0xFF);
break; break;
default: default:
System.out.println("A mapping error has occured. Ignoring: " + contComp.name()); System.out.println("A mapping error has occured. Ignoring: " + padComp.name());
break; 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) { private void sendControllerPacket() {
boolean press = false; if (conn != null) {
conn.sendControllerInput(inputMap, leftTrigger, rightTrigger,
if (value > 0.5F) { leftStickX, leftStickY, rightStickX, rightStickY);
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;
} }
} }
/*
* 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;
}
}
} }

View File

@@ -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<Gamepad> gamepads = new LinkedList<Gamepad>();
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<Controller> pads) {
LinkedList<Gamepad> newPadList = new LinkedList<Gamepad>();
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<Gamepad> 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());
}
}

View File

@@ -1,145 +1,83 @@
package com.limelight.input.gamepad; package com.limelight.input.gamepad;
import java.lang.reflect.Constructor; import java.util.Collections;
import java.lang.reflect.InvocationTargetException; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.logging.Level; import java.util.List;
import java.util.logging.Logger;
import com.limelight.nvstream.NvConnection; import com.limelight.input.Device;
import com.limelight.input.DeviceListener;
import net.java.games.input.Controller;
import net.java.games.input.ControllerEnvironment;
/** /**
* Listens to <code>Controller</code>s connected to this computer and gives any gamepad to the gamepad handler * Listens to <code>Controller</code>s connected to this computer and gives any gamepad to the gamepad handler
* @author Diego Waxemberg * @author Diego Waxemberg
*/ */
public class GamepadListener { public class GamepadListener implements NativeGamepadListener {
private static Thread listenerThread; private HashMap<Integer, Device> devices;
private static NvConnection conn; private List<DeviceListener> listeners;
private static ControllerEnvironment defaultEnv;
/** private static GamepadListener singleton;
* starts a thread to listen to controllers
* @return true if it started a thread, false if the thread is already running. public static GamepadListener getInstance() {
*/ if (singleton == null) {
public static boolean startUp() { singleton = new GamepadListener();
// Suppress spam from jinput log warnings in DefaultControllerEnvironment }
Logger.getLogger(ControllerEnvironment.getDefaultEnvironment().getClass().getName()).setLevel(Level.SEVERE); return singleton;
}
if (listenerThread == null || !listenerThread.isAlive()) {
System.out.println("Controller Listener thread starting up"); private GamepadListener() {
listenerThread = new Thread() { devices = new HashMap<Integer, Device>();
@Override listeners = new LinkedList<DeviceListener>();
public void run() { }
try {
public int deviceCount() {
defaultEnv = ControllerEnvironment.getDefaultEnvironment(); return devices.size();
}
while(!isInterrupted()) {
public void addDeviceListener(DeviceListener listener) {
Controller[] ca = defaultEnv.getControllers(); listeners.add(listener);
LinkedList<Controller> gamepads = new LinkedList<Controller>(); }
public List<DeviceListener> 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));
}
/* @Override
* iterates through the controllers and adds gamepads and ps3 controller to the list public void deviceRemoved(int deviceId) {
* NOTE: JInput does not consider a PS3 controller to be a gamepad (it thinks it's a "STICK") so we must use the devices.remove(deviceId);
* 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]);
}
}
GamepadHandler.addGamepads(gamepads); @Override
GamepadHandler.setConnection(conn); public void buttonDown(int deviceId, int buttonId) {
Device dev = devices.get(deviceId);
try { for (DeviceListener listener : listeners) {
Thread.sleep(1000); listener.handleButton(dev, buttonId, true);
} 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<? extends ControllerEnvironment> 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
* Tells the handler to start sending gamepad events to the specified connection public void buttonUp(int deviceId, int buttonId) {
* @param connection the connection to the host that will receive gamepad events Device dev = devices.get(deviceId);
*/ for (DeviceListener listener : listeners) {
public static void startSendingInput(NvConnection connection) { listener.handleButton(dev, buttonId, false);
System.out.println("Starting to send controller input");
conn = connection;
if (!GamepadHandler.isRunning()) {
GamepadHandler.startUp();
} }
} }
/** @Override
* Tells the handler to stop sending events to the host public void axisMoved(int deviceId, int axisId, float value, float lastValue) {
*/ Device dev = devices.get(deviceId);
public static void stopSendingInput() { for (DeviceListener listener : listeners) {
System.out.println("Stopping sending controller input"); listener.handleAxis(dev, axisId, value, lastValue);
conn = null; }
} }
} }

View File

@@ -4,9 +4,6 @@ import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map.Entry; import java.util.Map.Entry;
import net.java.games.input.Component;
/** /**
* Mappings for gamepad components * Mappings for gamepad components
* @author Diego Waxemberg * @author Diego Waxemberg
@@ -14,13 +11,13 @@ import net.java.games.input.Component;
public class GamepadMapping implements Serializable { public class GamepadMapping implements Serializable {
private static final long serialVersionUID = -185035113915743149L; private static final long serialVersionUID = -185035113915743149L;
private HashMap<String, Mapping> mapping; private HashMap<SourceComponent, Mapping> mapping;
/** /**
* Constructs a new mapping that has nothing mapped. * Constructs a new mapping that has nothing mapped.
*/ */
public GamepadMapping() { public GamepadMapping() {
mapping = new HashMap<String, Mapping>(); mapping = new HashMap<SourceComponent, Mapping>();
} }
/** /**
@@ -29,11 +26,7 @@ public class GamepadMapping implements Serializable {
* @param comp the gamepad component to map to. * @param comp the gamepad component to map to.
*/ */
public void insertMapping(Mapping toMap, SourceComponent comp) { public void insertMapping(Mapping toMap, SourceComponent comp) {
// This is the base mapping for components with multiple "buttons" mapping.put(comp, toMap);
mapping.put(comp.getComponentId(), toMap);
// This is the more-specific mapping for the specific buttons
mapping.put(comp.getFullUniqueId(), toMap);
} }
/** /**
@@ -41,17 +34,8 @@ public class GamepadMapping implements Serializable {
* @param comp the gamepad component to get a mapping for * @param comp the gamepad component to get a mapping for
* @return a mapping for the requested component * @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) { 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. * @param comp the component to no longer be mapped.
*/ */
public void remove(SourceComponent comp) { public void remove(SourceComponent comp) {
// Remove the most specific mapping mapping.remove(comp);
mapping.remove(comp.getFullUniqueId());
for (Entry<String, Mapping> entry : mapping.entrySet()) {
if (entry.getKey().startsWith(comp.getComponentId())) {
return;
}
}
// Remove the common mapping if no more specific mappings remain
mapping.remove(comp.getComponentId());
} }
/** /**
* Gets the mapped ControllerComponent for the specified ControllerComponent.</br> * Gets the mapped ControllerComponent for the specified ControllerComponent.</br>
* <b>NOTE: Iterates a hashmap, use sparingly</b>
* @param contComp the component to get a mapping for * @param contComp the component to get a mapping for
* @return a mapping or an null if there is none * @return a mapping or an null if there is none
*/ */
public Mapping get(GamepadComponent contComp) { public Mapping get(GamepadComponent contComp) {
//#allTheJank //#allTheJank
for (Entry<String, Mapping> entry : mapping.entrySet()) { for (Entry<SourceComponent, Mapping> entry : mapping.entrySet()) {
if (entry.getValue().contComp == contComp) { if (entry.getValue().contComp == contComp) {
return entry.getValue(); return entry.getValue();
} }
@@ -89,16 +64,17 @@ public class GamepadMapping implements Serializable {
/** /**
* Gets the mapping for the specified component.</br> * Gets the mapping for the specified component.</br>
* <b>NOTE: Iterates a hashmap, use sparingly</b>
* @param contComp the component to get a mapping for * @param contComp the component to get a mapping for
* @return a mapping or an empty string if there is none * @return a mapping or an empty string if there is none
*/ */
public String getMapping(GamepadComponent contComp) { public SourceComponent getMapping(GamepadComponent contComp) {
for (Entry<String, Mapping> entry : mapping.entrySet()) { for (Entry<SourceComponent, Mapping> entry : mapping.entrySet()) {
if (entry.getValue().contComp == contComp) { if (entry.getValue().contComp == contComp) {
return entry.getKey(); return entry.getKey();
} }
} }
return ""; return null;
} }
/** /**

View File

@@ -1,29 +1,42 @@
package com.limelight.input.gamepad; package com.limelight.input.gamepad;
import net.java.games.input.Component;
public class SourceComponent { public class SourceComponent {
private Component component; public enum Type { AXIS, BUTTON }
private String id; private Type type;
private int id;
public SourceComponent(Component component, String id) { public SourceComponent(Type type, int id) {
this.component = component; this.type = type;
this.id = id; this.id = id;
} }
public Component getComponent() { public Type getType() {
return component; return type;
} }
public String getId() { public int getId() {
return id; return id;
} }
public String getFullUniqueId() { @Override
return getComponentId() + " " + getId(); 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 id == other.id && type == other.type;
return component.getIdentifier().getName();
} }
} }