mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2026-04-21 07:40:36 +00:00
rewrote gamepad stuff to not use JInput
This commit is contained in:
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
25
src/com/limelight/input/Device.java
Normal file
25
src/com/limelight/input/Device.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/com/limelight/input/DeviceListener.java
Normal file
6
src/com/limelight/input/DeviceListener.java
Normal 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);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user