diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..b922ecb
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "purejavahidapi"]
+ path = purejavahidapi
+ url = https://github.com/nyholku/purejavahidapi
diff --git a/pom.xml b/pom.xml
index e8f7344..7fff789 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,11 +16,30 @@
+
+
+ jitpack.io
+ https://jitpack.io
+
+
+
- org.hid4java
- hid4java
- 0.4.0
+ org.projectlombok
+ lombok
+ 1.16.12
+ provided
+
+ com.github.strikerx3
+ jxinput
+ 1eb4087
+
+
+
+ purejavahidapi
+ purejavahidapi
+ 0.0.1
+
\ No newline at end of file
diff --git a/purejavahidapi b/purejavahidapi
new file mode 160000
index 0000000..9c9fc20
--- /dev/null
+++ b/purejavahidapi
@@ -0,0 +1 @@
+Subproject commit 9c9fc20fe3c90603aabc32ad66c9df6bbf696c8b
diff --git a/src/net/ash/HIDToVPADNetworkClient/Main.java b/src/net/ash/HIDToVPADNetworkClient/Main.java
index bfb8759..bdffeea 100644
--- a/src/net/ash/HIDToVPADNetworkClient/Main.java
+++ b/src/net/ash/HIDToVPADNetworkClient/Main.java
@@ -23,47 +23,39 @@ package net.ash.HIDToVPADNetworkClient;
import javax.swing.SwingUtilities;
-import org.hid4java.HidManager;
-
-import net.ash.HIDToVPADNetworkClient.controller.ControllerManager;
-import net.ash.HIDToVPADNetworkClient.gui.ControllerDetector;
import net.ash.HIDToVPADNetworkClient.gui.GuiMain;
+import net.ash.HIDToVPADNetworkClient.manager.ActiveControllerManager;
import net.ash.HIDToVPADNetworkClient.network.NetworkManager;
/* Ash's todo list
- * TODO finish Hid4JavaController
+ * TODO finish HidController
* TODO locale
*/
public class Main {
- public static void main(String[] args) {
+ public static void main(String[] args) {
System.out.println("Hello World!");
-
try {
- new ControllerManager();
- new NetworkManager();
+ new Thread(ActiveControllerManager.getInstance()).start();
+ new Thread(NetworkManager.getInstance()).start();
} catch (Exception e) {
e.printStackTrace();
fatal();
- }
-
+ }
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GuiMain.createGUI();
}
});
-
- new ControllerDetector().start();
}
-
- public static void fatal() {
+
+ public static void fatal() {
System.err.println("HID To VPAD Network Client encountered an irrecoverable error.");
System.err.println("Exiting...");
System.exit(1);
}
public static void initiateShutdown() {
- HidManager.getHidServices().shutdown();
System.exit(0);
}
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java b/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java
index b95ab9a..3f007c4 100644
--- a/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java
+++ b/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java
@@ -21,13 +21,84 @@
*******************************************************************************/
package net.ash.HIDToVPADNetworkClient.controller;
+import lombok.Getter;
+import lombok.Synchronized;
+import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
+import net.ash.HIDToVPADNetworkClient.manager.ControllerManager;
+import net.ash.HIDToVPADNetworkClient.util.Settings;
+import net.ash.HIDToVPADNetworkClient.util.Utilities;
+
/**
* Main controller interface, extended by controller drivers.
*
* See {@link LinuxDevInputController} for a full implementation.
* @author ash
*/
-public interface Controller {
+public abstract class Controller implements Runnable{
+ private boolean active;
+ @Getter private final ControllerType type;
+ @Getter private final String identifier;
+ private byte[] latestData = null;
+
+ boolean shutdown = false;
+ boolean shutdownDone = false;
+ private Object dataLock = new Object();
+ private Object shutdownLock = new Object();
+
+ private Object rumbleLock = new Object();
+ private boolean rumble = false;
+
+ public Controller(ControllerType type, String identifier) throws ControllerInitializationFailedException{
+ this.type = type;
+ this.identifier = identifier;
+ if(!initController(identifier)){
+ throw new ControllerInitializationFailedException();
+ }
+ }
+
+ @Override
+ public void run() {
+ boolean shutdownState = shutdown;
+ while(!shutdownState){
+ Utilities.sleep(Settings.DETECT_CONTROLLER_INTERVAL);
+ while(isActive()) {
+ byte[] newData = pollLatestData();
+ if(newData != null){
+ setLatestData(newData);
+ }
+ doSleepAfterPollingData();
+ }
+ synchronized (shutdownLock) {
+ shutdownState = shutdown;
+ }
+ }
+ synchronized (shutdownLock) {
+ shutdownDone = true;
+ }
+ }
+
+ protected void doSleepAfterPollingData() {
+ Utilities.sleep(Settings.SLEEP_AFER_POLLING);
+ }
+
+ @Synchronized("dataLock")
+ private void setLatestData(byte[] newData) {
+ this.latestData = newData;
+ }
+
+ @Synchronized("dataLock")
+ public byte[] getLatestData() {
+ if(latestData != null){
+ byte[] data = this.latestData.clone();
+ this.latestData = null;
+ return data;
+ }else{
+ return null;
+ }
+ }
+
+ public abstract byte[] pollLatestData();
+
/**
* Sets up the driver.
*
@@ -36,64 +107,110 @@ public interface Controller {
* @param arg Driver-specific init argument, see {@link ControllerManager} and {@link ControllerDetector}.
* @return Whether initialization was successful.
*/
- public boolean initController(Object arg);
+ public abstract boolean initController(String identifier);
+
/**
- * Allocates and returns a copy of the latest data available from the controller.
- *
- * @return A ControllerData instance containing the latest controller data.
+ * Destroys the controller driver and ends the polling thread.
*/
- public ControllerData getLatestData();
+ public void destroyAll(){
+ destroyDriver();
+ endThread();
+ }
/**
- * Used to tell a driver whether or not to poll the controller.
- *
- * Is currently only ever used to initialize a driver (poll=true).
- * destroy() is called for deinitialization.
- *
- * Candidate to be removed during refactoring.
- *
- * @param poll Whether or not the driver should poll the controller.
- */
- public void setPollingState(boolean poll);
+ * Destroys the controller driver.
+ */
+ public abstract void destroyDriver();
+
- /**
- * Destroys the controller driver.
- *
- * Will not return until all threads are stopped and resources are freed.
- */
- public void destroy();
-
- /**
- * Sets the deviceSlot and padSlot to be returned by {@link #getDeviceSlot() getDeviceSlot} and {@link #getPadSlot() getPadSlot}.
- * @param deviceSlot Value to be returned by {@link #getDeviceSlot() getDeviceSlot}
- * @param padSlot Value to be returned by {@link #getPadSlot() getPadSlot}
- */
- public void setSlotData(short deviceSlot, byte padSlot);
-
- /**
- * Gets the previously set device slot (see {@link #setSlotData(short, byte) setSlotData})
- * @return The controller's device slot.
- */
- public short getDeviceSlot();
-
- /**
- * Gets the previously set pad slot (see {@link #setSlotData(short, byte) setSlotData})
- * @return The controller's pad slot.
- */
- public byte getPadSlot();
-
- /**
- * Returns a unique handle for this controller driver.
- *
- * Please note that this is unique to the driver, not the controller it's connected to.
- * @return The driver's handle.
- */
- public int getHandle();
-
- /**
- * Gets the controller's ID. This is often identical to the argument to {@link #initController(Object) initController}.
- * @return The controller's ID.
- */
- public String getID();
-}
+ private void endThread() {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ setActive(false);
+
+ synchronized (shutdownLock) {
+ shutdown = true;
+ }
+
+ boolean done = false;
+ int i = 0;
+ while(!done){
+ synchronized (shutdownLock) {
+ done = shutdownDone;
+ }
+ Utilities.sleep(50);
+ if(i++ > 50) System.out.println("Thread doesn't stop!!");
+ }
+ }
+ }).start();
+ }
+
+ public abstract short getVID();
+
+ public abstract short getPID();
+
+ @Synchronized("shutdownLock")
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ @Override
+ public String toString(){
+ return getType() + " " + getIdentifier();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((identifier == null) ? 0 : identifier.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Controller other = (Controller) obj;
+ if (identifier == null) {
+ if (other.identifier != null)
+ return false;
+ } else if (!identifier.equals(other.identifier))
+ return false;
+ if (type != other.type)
+ return false;
+ return true;
+ }
+
+ @Synchronized("rumbleLock")
+ public boolean isRumble() {
+ return rumble;
+ }
+
+ @Synchronized("rumbleLock")
+ public void startRumble() {
+ this.rumble = true;
+ }
+
+ @Synchronized("rumbleLock")
+ public void stopRumble() {
+ this.rumble = false;
+ }
+
+ public enum ControllerType {
+ PureJAVAHid, LINUX, XINPUT13,XINPUT14
+ }
+
+ public abstract String getInfoText();
+}
\ No newline at end of file
diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/ControllerManager.java b/src/net/ash/HIDToVPADNetworkClient/controller/ControllerManager.java
deleted file mode 100644
index 3ddf81e..0000000
--- a/src/net/ash/HIDToVPADNetworkClient/controller/ControllerManager.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *******************************************************************************/
-package net.ash.HIDToVPADNetworkClient.controller;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-
-import net.ash.HIDToVPADNetworkClient.gui.GuiController;
-import net.ash.HIDToVPADNetworkClient.network.NetworkManager;
-
-public class ControllerManager {
- private static ControllerManager instance;
-
- public ControllerManager() throws Exception {
- if (instance != null) {
- throw new Exception("ControllerManager already has an instance!");
- }
- instance = this;
- }
-
- public boolean startConnection(String ip, List controllersIn) {
- List controllers = setupControllers(controllersIn);
-
- //Boot up all drivers
- for (Controller c : controllers) {
- c.setPollingState(true);
-
- NetworkManager.instance().addController(c);
- }
-
- NetworkManager.instance().connect(ip);
- return NetworkManager.instance().isConnected();
- }
-
- public void stopConnection() {
- NetworkManager.instance().removeAllControllers();
- NetworkManager.instance().disconnect();
- }
-
- public void updateControllers(List controllersIn) {
- HashSet m = new HashSet();
- for (GuiController g : controllersIn) {
- if (!g.getActiveState()) continue;
- m.add(g.getId().hashCode());
- }
-
- HashMap d = NetworkManager.instance().getControllers();
- for (Controller c : d.values()) {
- if (!m.contains(c.getID().hashCode())) {
- NetworkManager.instance().removeController(c);
- }
- }
-
- List list = new ArrayList();
- for (GuiController g : controllersIn) {
- if (!d.containsKey(g.getId().hashCode())) {
- list.add(g);
- }
- }
-
- for (Controller c : setupControllers(list)) {
- c.setPollingState(true);
- NetworkManager.instance().addController(c);
- }
- }
-
- private List setupControllers(List controllers) {
- List out = new ArrayList();
-
- for (GuiController g : controllers) {
- if (!g.getActiveState()) continue;
-
- Controller ctrl;
-
- switch (g.getType()) {
- case HID4JAVA:
- ctrl = new Hid4JavaController();
- break;
- case LINUX:
- ctrl = new LinuxDevInputController();
- break;
- default:
- System.out.println("[ControllerManager] Unsupported controller type " + g.getType().name());
- continue;
- }
-
- ctrl.initController(g.getId());
-
- out.add(ctrl);
- }
-
- return out;
- }
-
- public void detachController(Controller c) {
- HashMap m = NetworkManager.instance().getControllers();
- if (m.containsKey(c.getID().hashCode())) {
- NetworkManager.instance().removeController(c);
- }
- }
-
- public static ControllerManager instance() {
- return instance;
- }
-}
diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/Hid4JavaController.java b/src/net/ash/HIDToVPADNetworkClient/controller/Hid4JavaController.java
deleted file mode 100644
index 8b9948c..0000000
--- a/src/net/ash/HIDToVPADNetworkClient/controller/Hid4JavaController.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *******************************************************************************/
-package net.ash.HIDToVPADNetworkClient.controller;
-
-import org.hid4java.HidDevice;
-import org.hid4java.HidManager;
-
-import net.ash.HIDToVPADNetworkClient.util.HandleFoundry;
-
-public class Hid4JavaController implements Controller {
- static final int PACKET_LENGTH = 64;
-
- private String path = null;
- private HidDevice controller = null;
-
- @Override
- public boolean initController(Object arg) {
- for (HidDevice device : HidManager.getHidServices().getAttachedHidDevices()) {
- if (device.getPath().equals(arg.toString())) {
- controller = device;
- controller.setNonBlocking(true);
-
- path = arg.toString();
-
- break;
- }
- }
-
- System.out.println("ctrl: " + controller.open() + " " + controller.getLastErrorMessage());
- if (controller == null | !controller.isOpen()) return false;
- return true;
- }
-
- @Override
- public void setPollingState(boolean poll) {
-
- }
-
- @Override
- public ControllerData getLatestData() {
- ControllerData data = new ControllerData(getVID(), getPID(), getHandle(), new byte[1200]);
- System.out.println("Data size: " + controller.read(data.data, 500));
- System.out.println("hrm: " + controller.getLastErrorMessage());
- return data;
- }
-
- @Override
- public void destroy() {
- controller.close();
- }
-
- private short getVID() {
- return controller.getVendorId();
- }
-
- private short getPID() {
- return controller.getProductId();
- }
-
- private short deviceSlot = 0;
- private byte padSlot = 0;
-
- @Override
- public void setSlotData(short deviceSlot, byte padSlot) {
- this.deviceSlot = deviceSlot;
- this.padSlot = padSlot;
- }
-
- @Override
- public short getDeviceSlot() {
- return deviceSlot;
- }
-
- @Override
- public byte getPadSlot() {
- return padSlot;
- }
-
- private int handle = 0;
- @Override
- public int getHandle() {
- if (handle == 0) {
- handle = HandleFoundry.next();
- }
- return handle;
- }
-
- @Override
- public String getID() {
- return path;
- }
-}
diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java b/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java
index d0ff86b..3092337 100644
--- a/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java
+++ b/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java
@@ -26,67 +26,65 @@ import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
-import net.ash.HIDToVPADNetworkClient.util.HandleFoundry;
+import lombok.Getter;
+import lombok.Setter;
+import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
-public class LinuxDevInputController extends Thread implements Controller {
- public static final int NUM_SUPPORTED_AXIS = 10; //possibly off-by-one
+public class LinuxDevInputController extends Controller implements Runnable{
+ public LinuxDevInputController(String identifier) throws ControllerInitializationFailedException {
+ super(ControllerType.LINUX, identifier);
+ }
+
+ public static final int NUM_SUPPORTED_AXIS = 10; //possibly off-by-one
public static final int CONTROLLER_DATA_SIZE = Long.BYTES + (Byte.BYTES * NUM_SUPPORTED_AXIS);
private static final byte JS_EVENT_BUTTON = 0x01;
private static final byte JS_EVENT_INIT = (byte)0x80;
private static final byte JS_EVENT_AXIS = 0x02;
- private DataInputStream controller;
- private ControllerData controllerData;
+ @Getter @Setter private DataInputStream controller;
- private String id;
-
- private short deviceSlot = 0;
- private byte padSlot = 0;
-
- private boolean shouldProcessEvents = false;
- private boolean shutdown = false;
-
- public LinuxDevInputController() {
- super("LinuxDevInputController");
- controllerData = new ControllerData((short)0, (short)0, 0, new byte[CONTROLLER_DATA_SIZE]);
- }
+ @Getter @Setter private short VID;
+ @Getter @Setter private short PID;
@Override
- public void run() {
- for (;;) {
- for (;;) {
- if (!shouldProcessEvents) break;
-
- processNextControllerEvent();
- }
-
- if (shutdown) break;
- /* Not polling. Wait for setPollingState to wake us up. */
- try {
- this.wait();
- } catch (InterruptedException e) {}
- } //for (;;)
- }
-
+ public boolean initController(String identifier) {
+ try {
+ controller = new DataInputStream(new BufferedInputStream(new FileInputStream(identifier)));
+ } catch (Exception e) {
+ System.err.println("[LinuxDevInputController] Couldn't open " + identifier + " as file!");
+ e.printStackTrace();
+ return false;
+ }
+
+ setVID((short)(identifier.hashCode() & 0xFFFF));
+ setPID((short)((identifier.hashCode() >> Short.BYTES) & 0xFFFF));
+ System.out.println("[LinuxDevInputController] " + identifier.toString() + " fakevid: " + Integer.toHexString((int)getVID() & 0xFFFF) + " fakepid: " + Integer.toHexString((int)getPID() & 0xFFFF));
+
+ return true;
+ }
+
private long buttonState = 0;
private byte[] axisState = new byte[NUM_SUPPORTED_AXIS];
- private void processNextControllerEvent() {
+
+ @Override
+ public byte[] pollLatestData() {
+ DataInputStream inputStream = getController();
//Read out next event from controller
/*int time;*/
short value;
byte type, number;
try {
- /*time = */controller.readInt();
- value = controller.readShort();
- type = controller.readByte();
- number = controller.readByte();
+ /*time = */inputStream.readInt();
+ value = inputStream.readShort();
+ type = inputStream.readByte();
+ number = inputStream.readByte();
} catch (IOException e) {
System.err.println("[LinuxDevInputController] Couldn't read from controller!");
e.printStackTrace();
System.out.println("[LinuxDevInputController] Detaching...");
- ControllerManager.instance().detachController(this);
- return;
+ setActive(false);
+ return null;
}
//Treat init events as normal (clear init bit)
@@ -95,7 +93,7 @@ public class LinuxDevInputController extends Thread implements Controller {
if (type == JS_EVENT_BUTTON) {
if (number >= Long.SIZE) {
System.out.println("[LinuxDevInputController] Button number " + number + " out of range; ignoring");
- return;
+ return null;
}
if (value != 0) {
@@ -108,7 +106,7 @@ public class LinuxDevInputController extends Thread implements Controller {
} else if (type == JS_EVENT_AXIS) {
if (number >= NUM_SUPPORTED_AXIS) {
System.out.println("[LinuxDevInputController] Axis number " + number + " out of range; ignoring");
- return;
+ return null;
}
//Do byteswap
value = (short)(((value & 0xFF) << Byte.SIZE) | ((value & 0xFF00) >> Byte.SIZE));
@@ -125,103 +123,30 @@ public class LinuxDevInputController extends Thread implements Controller {
for (int i = Long.BYTES; i < CONTROLLER_DATA_SIZE; i++) {
newData[i] = axisState[i - Long.BYTES];
}
- synchronized (controllerData) {
- controllerData.data = newData;
- }
+ return newData;
}
@Override
- public ControllerData getLatestData() {
- synchronized (controllerData) {
- return new ControllerData(getVID(), getPID(), getHandle(), controllerData.getData());
- }
- }
-
+ protected void doSleepAfterPollingData() {
+ //This is event driven (aka pollLatestData() is blocking anyway until we have data), we don't need to sleep it all.
+ }
+
@Override
- public boolean initController(Object arg) {
+ public void destroyDriver() {
try {
- controller = new DataInputStream(new BufferedInputStream(new FileInputStream(arg.toString())));
- } catch (Exception e) {
- System.err.println("[LinuxDevInputController] Couldn't open " + arg.toString() + " as file!");
- e.printStackTrace();
- return false;
- }
-
- fakevid = (short)(arg.hashCode() & 0xFFFF);
- fakepid = (short)((arg.hashCode() >> Short.BYTES) & 0xFFFF);
- System.out.println("[LinuxDevInputController] " + arg.toString() + " fakevid: " + Integer.toHexString((int)fakevid & 0xFFFF) + " fakepid: " + Integer.toHexString((int)fakepid & 0xFFFF));
-
- id = arg.toString();
-
- return true;
- }
-
- @Override
- public void setPollingState(boolean poll) {
- shouldProcessEvents = poll;
- if (this.getState() == Thread.State.NEW) {
- this.start();
- } else if (this.getState() == Thread.State.WAITING){
- this.notify();
- }
- }
-
- @Override
- public void destroy() {
- shutdown = true;
- setPollingState(false);
-
- if (!this.equals(Thread.currentThread())) {
- while (this.getState() != Thread.State.TERMINATED) {}
- }
-
- try {
- controller.close();
- } catch (IOException e) {}
- }
-
- private short fakevid;
- private short getVID() {
- return fakevid;
- }
-
- private short fakepid;
- private short getPID() {
- return fakepid;
- }
-
- @Override
- public String getID() {
- return id;
- }
-
- private int handle = 0;
- @Override
- public int getHandle() {
- if (handle == 0) {
- handle = HandleFoundry.next();
- }
- return handle;
- }
-
- @Override
- public void setSlotData(short deviceSlot, byte padSlot) {
- this.deviceSlot = deviceSlot;
- this.padSlot = padSlot;
- }
-
- @Override
- public short getDeviceSlot() {
- return deviceSlot;
- }
-
- @Override
- public byte getPadSlot() {
- return padSlot;
+ controller.close();
+ } catch (IOException e) {
+ }
}
@Override
public String toString() {
- return "[" + super.toString() + ";VID," + Integer.toHexString((int)getVID() & 0xFFFF) + ";PID," + Integer.toHexString((int)getPID() & 0xFFFF) + ";run," + shouldProcessEvents + ((controller == null) ? ";uninitialised]" : ";initialised]");
+ return "[" + super.toString() + ";VID," + Integer.toHexString((int)getVID() & 0xFFFF) + ";PID," + Integer.toHexString((int)getPID() & 0xFFFF) + ";run," + isActive() + ((controller == null) ? ";uninitialised]" : ";initialised]");
}
+
+ @Override
+ public String getInfoText() {
+ return "Linux controller on " + getIdentifier();
+ }
+
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputControllerLegacyDriver.java b/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputControllerLegacyDriver.java
deleted file mode 100644
index ce7c694..0000000
--- a/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputControllerLegacyDriver.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *******************************************************************************/
-//This file is only here so I can remember just how shocking my first attempt
-//at a driver like this was. It should never, NEVER be used.
-
-/* I'M SO SORRY
- * Please do not use this
- */
-
-package net.ash.HIDToVPADNetworkClient.controller;
-
-import java.io.DataInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import net.ash.HIDToVPADNetworkClient.util.BlockingIOStabbifier;
-import net.ash.HIDToVPADNetworkClient.util.WakeupThread;
-
-@Deprecated
-public class LinuxDevInputControllerLegacyDriver extends Thread implements Controller {
- private DataInputStream controller;
- private String path;
-
- @Override
- public boolean initController(Object arg) {
- try {
- path = (String)arg;
- } catch (ClassCastException e) {
- System.err.println("LinuxDevInputController recieved bad argument!");
- e.printStackTrace();
- return false;
- }
-
- try {
- controller = new DataInputStream(new BlockingIOStabbifier(path));
- } catch (FileNotFoundException e) {
- System.err.println("Could not open " + path + "!");
- e.printStackTrace();
- return false;
- }
- data = new ControllerData();
- data.data = new byte[100]; //100 BUTTON MAX
- return true;
- }
-
- private boolean shouldPoll = false;
- WakeupThread wakeup;
- //@Override
- public void setPollingState(boolean poll, int pollInterval) {
- if (poll) {
- if (this.getState() == Thread.State.NEW) {
- shouldPoll = poll;
- this.start();
- }
- wakeup = new WakeupThread(LinuxDevInputControllerThreadLock);
- wakeup.setTimeout(pollInterval);
- wakeup.start();
- }
- shouldPoll = poll;
- }
-
- Object LinuxDevInputControllerThreadLock = new Object();
- @Override
- public void run() {
- for (;;) {
- while (shouldPoll) {
- updateControllerData();
-
- synchronized (LinuxDevInputControllerThreadLock) {
- try {
- LinuxDevInputControllerThreadLock.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- wakeup.tryStop();
- synchronized (LinuxDevInputControllerThreadLock) {
- try {
- LinuxDevInputControllerThreadLock.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- private ControllerData data;
-
- private void updateControllerData() {
- try {
- while (controller.available() > 8) { /* 8 bytes to a joystick packet */
- /*int time = */controller.readInt();
- short val = controller.readShort();
- byte type = controller.readByte();
- byte num = controller.readByte();
- num *= 2;
- if (type == 0x1 && !(num >= 100)) {
- synchronized (data) {
- data.data[num] = (byte)(val & 0xFF);
- data.data[num + 1] = (byte)((val >> 8) & 0xFF);
- }
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public ControllerData getLatestData() {
- synchronized (data) {
- return data;
- }
- }
-
- public short getPID() {
- return data.getPID();
- }
-
- public short getVID() {
- return data.getVID();
- }
-
- public void setFakePid(short pid) {
- //this.data.pid = pid;
- }
-
- public void setFakeVid(short vid) {
- //this.data.vid = vid;
- }
-
- @Override
- public void setSlotData(short deviceSlot, byte padSlot) {
- // Auto-generated method stub
-
- }
-
- @Override
- public short getDeviceSlot() {
- // Auto-generated method stub
- return 0;
- }
-
- @Override
- public byte getPadSlot() {
- // Auto-generated method stub
- return 0;
- }
-
- @Override
- public int getHandle() {
- // Auto-generated method stub
- return 0;
- }
-
- @Override
- public String getID() {
- // Auto-generated method stub
- return null;
- }
-
- @Override
- public void setPollingState(boolean poll) {
- // Auto-generated method stub
-
- }
-}
diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/PureJavaHidController.java b/src/net/ash/HIDToVPADNetworkClient/controller/PureJavaHidController.java
new file mode 100644
index 0000000..74a8caa
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/controller/PureJavaHidController.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+package net.ash.HIDToVPADNetworkClient.controller;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.Synchronized;
+import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
+import net.ash.HIDToVPADNetworkClient.util.PureJavaHidApiManager;
+import purejavahidapi.HidDevice;
+import purejavahidapi.InputReportListener;
+
+public class PureJavaHidController extends Controller implements InputReportListener {
+ public static Controller getInstance(String deviceIdentifier) throws IOException, ControllerInitializationFailedException {
+ HidDevice device = PureJavaHidApiManager.getDeviceByPath(deviceIdentifier);
+ //We use a special version to optimize the data for the switch pro controller
+ if(device.getHidDeviceInfo().getVendorId() == SwitchProController.SWITCH_PRO_CONTROLLER_VID &&
+ device.getHidDeviceInfo().getProductId() == SwitchProController.SWITCH_PRO_CONTROLLER_PID){
+ return new SwitchProController(deviceIdentifier);
+ }else{
+ return new PureJavaHidController(deviceIdentifier);
+ }
+ }
+
+ public PureJavaHidController(String identifier) throws ControllerInitializationFailedException {
+ super(ControllerType.PureJAVAHid, identifier);
+ }
+
+ private Object dataLock = new Object();
+ protected byte[] currentData = new byte[1];
+
+ protected int PACKET_LENGTH = 64;
+
+ @Getter @Setter(AccessLevel.PRIVATE)
+ private HidDevice hidDevice;
+
+ @Override
+ public boolean initController(String identifier) {
+ HidDevice device;
+ try {
+ device = PureJavaHidApiManager.getDeviceByPath(identifier);
+
+ device.setInputReportListener(this);
+ setHidDevice(device);
+ return true;
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ @Override
+ @Synchronized("dataLock")
+ public byte[] pollLatestData() {
+ return currentData.clone();
+ }
+
+ @Override
+ public void destroyDriver() {
+ getHidDevice().close();
+ }
+
+ @Override
+ public short getVID() {
+ return getHidDevice().getHidDeviceInfo().getVendorId();
+ }
+
+ @Override
+ public short getPID() {
+ return getHidDevice().getHidDeviceInfo().getProductId();
+ }
+
+ @Override
+ @Synchronized("dataLock")
+ public void onInputReport(HidDevice source, byte reportID, byte[] reportData, int reportLength) {
+ if(isActive()){
+ int length = PACKET_LENGTH;
+ if(reportLength < length){
+ length = reportLength;
+ }
+ currentData = Arrays.copyOfRange(reportData, 0, length);
+ }
+ }
+
+ @Override
+ public String getInfoText() {
+ //TODO:
+ if(getVID() == 0x57e){
+ if(getPID() == 0x2006){
+ return "Joy-Con (L) on " + getIdentifier();
+ }else if(getPID() == 0x2007){
+ return "Joy-Con (R) on " + getIdentifier();
+ }
+ }
+
+ return "USB HID on " + getIdentifier();
+ }
+}
\ No newline at end of file
diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/SwitchProController.java b/src/net/ash/HIDToVPADNetworkClient/controller/SwitchProController.java
new file mode 100644
index 0000000..f8fa377
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/controller/SwitchProController.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+package net.ash.HIDToVPADNetworkClient.controller;
+
+import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
+
+public class SwitchProController extends PureJavaHidController {
+ public static final short SWITCH_PRO_CONTROLLER_VID = 0x57e;
+ public static final short SWITCH_PRO_CONTROLLER_PID = 0x2009;
+
+
+ public SwitchProController(String identifier) throws ControllerInitializationFailedException {
+ super(identifier);
+ //truncate package to 11;
+ this.PACKET_LENGTH = 11;
+ }
+
+ @Override
+ public byte[] pollLatestData() {
+ //remove unused data (because only changed data will be sent)
+ currentData[3] = 0;
+ currentData[5] = 0;
+ currentData[7] = 0;
+ currentData[9] = 0;
+ return currentData.clone();
+ }
+
+ @Override
+ public String getInfoText(){
+ return "Switch Pro Controller on " + getIdentifier();
+ }
+}
diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/ControllerData.java b/src/net/ash/HIDToVPADNetworkClient/controller/XInput13Controller.java
similarity index 76%
rename from src/net/ash/HIDToVPADNetworkClient/controller/ControllerData.java
rename to src/net/ash/HIDToVPADNetworkClient/controller/XInput13Controller.java
index cb00c16..454740e 100644
--- a/src/net/ash/HIDToVPADNetworkClient/controller/ControllerData.java
+++ b/src/net/ash/HIDToVPADNetworkClient/controller/XInput13Controller.java
@@ -21,28 +21,15 @@
*******************************************************************************/
package net.ash.HIDToVPADNetworkClient.controller;
-public class ControllerData {
- private short vid;
- private short pid;
- protected byte[] data;
-
- public ControllerData(short vid, short pid, int handle, byte[] data) {
- this.vid = vid;
- this.pid = pid;
- this.data = data;
- }
-
- public ControllerData() {}
-
- public short getVID() {
- return vid;
- }
-
- public short getPID() {
- return pid;
- }
-
- public byte[] getData() {
- return data;
- }
+import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
+
+public class XInput13Controller extends XInputController {
+ public XInput13Controller(String identifier) throws ControllerInitializationFailedException {
+ super(ControllerType.XINPUT13, identifier);
+ }
+
+ @Override
+ public String getInfoText(){
+ return "XInput 1.3 on " + getIdentifier();
+ }
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/XInput14Controller.java b/src/net/ash/HIDToVPADNetworkClient/controller/XInput14Controller.java
new file mode 100644
index 0000000..1fe49a8
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/controller/XInput14Controller.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+package net.ash.HIDToVPADNetworkClient.controller;
+
+import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
+
+public class XInput14Controller extends XInputController {
+ public XInput14Controller(String identifier) throws ControllerInitializationFailedException {
+ super(ControllerType.XINPUT14, identifier);
+ }
+
+ @Override
+ public String getInfoText(){
+ return "XInput 1.4 on " + getIdentifier();
+ }
+}
diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/XInputController.java b/src/net/ash/HIDToVPADNetworkClient/controller/XInputController.java
new file mode 100644
index 0000000..8b090fd
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/controller/XInputController.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+package net.ash.HIDToVPADNetworkClient.controller;
+
+import java.nio.ByteBuffer;
+
+import com.ivan.xinput.XInputAxes;
+import com.ivan.xinput.XInputButtons;
+import com.ivan.xinput.XInputComponents;
+import com.ivan.xinput.XInputDevice;
+import com.ivan.xinput.exceptions.XInputNotLoadedException;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
+import net.ash.HIDToVPADNetworkClient.util.Utilities;
+
+public class XInputController extends Controller {
+ //the pad number will be appended to this String.
+ public static final String XINPUT_INDENTIFER = "\\\\?\\XINPUT\\";
+
+ @Getter @Setter(AccessLevel.PRIVATE) private XInputDevice device;
+
+ public XInputController(ControllerType type, String identifier) throws ControllerInitializationFailedException {
+ super(type, identifier);
+ }
+
+ @Override
+ public boolean initController(String identifier) {
+ int pad = Integer.parseInt(identifier.substring(XINPUT_INDENTIFER.length()));
+ XInputDevice device = null;
+ try {
+ device = XInputDevice.getDeviceFor(pad);
+ } catch (XInputNotLoadedException e) {
+ //TODO: Log?
+ }
+ if(device == null) return false;
+ setDevice(device);
+ return true;
+ }
+
+ @Override
+ public byte[] pollLatestData() {
+ if(device.poll()){
+ ByteBuffer data = ByteBuffer.allocate(8);
+ XInputComponents components = device.getComponents();
+
+ XInputButtons buttons = components.getButtons();
+
+ int buttonState = 0;
+ if(buttons.a) buttonState |= (1 << 0);
+ if(buttons.b) buttonState |= (1 << 1);
+ if(buttons.x) buttonState |= (1 << 2);
+ if(buttons.y) buttonState |= (1 << 3);
+
+ if(buttons.left) buttonState |= (1 << 4);
+ if(buttons.up) buttonState |= (1 << 5);
+ if(buttons.right) buttonState |= (1 << 6);
+ if(buttons.down) buttonState |= (1 << 7);
+
+ if(buttons.back) buttonState |= (1 << 8);
+ if(buttons.start) buttonState |= (1 << 9);
+ if(buttons.lShoulder) buttonState |= (1 << 10);
+ if(buttons.rShoulder) buttonState |= (1 << 11);
+ if(buttons.lThumb) buttonState |= (1 << 12);
+ if(buttons.rThumb) buttonState |= (1 << 13);
+ if(buttons.unknown) buttonState |= (1 << 14);
+ if (XInputDevice.isGuideButtonSupported()) {
+ if (buttons.guide) buttonState |= (1 << 15);
+ }
+
+ XInputAxes axes = components.getAxes();
+ int axesData = 0;
+
+ axesData |= Utilities.signedShortToByte(axes.lxRaw) << 24;
+ axesData |= Utilities.signedShortToByte(axes.lyRaw) << 16;
+ axesData |= Utilities.signedShortToByte(axes.rxRaw) << 8;
+ axesData |= Utilities.signedShortToByte(axes.ryRaw) << 0;
+
+ short axesDataShoulderButtons = 0;
+
+ axesDataShoulderButtons |= axes.ltRaw << 8;
+ axesDataShoulderButtons |= axes.rtRaw << 0;
+
+ buttonState |= axesDataShoulderButtons << 16;
+ data.putInt(axesData).putInt(buttonState);
+
+ return(data.array());
+ }
+ return null;
+ }
+
+ @Override
+ public void destroyDriver() {
+ //not needed
+ }
+
+ //TODO: Other values for VID/PID? I guess other people had this idea too...
+ @Override
+ public short getVID() {
+ return 0x7331;
+ }
+
+ @Override
+ public short getPID() {
+ return 0x1337;
+ }
+
+ @Override
+ public String getInfoText(){
+ return "XInput on " + getIdentifier();
+ }
+}
diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerType.java b/src/net/ash/HIDToVPADNetworkClient/exeption/ControllerInitializationFailedException.java
similarity index 86%
rename from src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerType.java
rename to src/net/ash/HIDToVPADNetworkClient/exeption/ControllerInitializationFailedException.java
index 297aa43..bc09711 100644
--- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerType.java
+++ b/src/net/ash/HIDToVPADNetworkClient/exeption/ControllerInitializationFailedException.java
@@ -19,9 +19,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*******************************************************************************/
-package net.ash.HIDToVPADNetworkClient.gui;
+package net.ash.HIDToVPADNetworkClient.exeption;
-public enum GuiControllerType {
- HID4JAVA,
- LINUX
+public class ControllerInitializationFailedException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/ControllerDetector.java b/src/net/ash/HIDToVPADNetworkClient/gui/ControllerDetector.java
deleted file mode 100644
index 4fa7527..0000000
--- a/src/net/ash/HIDToVPADNetworkClient/gui/ControllerDetector.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *******************************************************************************/
-package net.ash.HIDToVPADNetworkClient.gui;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.hid4java.HidDevice;
-import org.hid4java.HidManager;
-
-public class ControllerDetector extends Thread {
- private static final int POLL_TIMEOUT = 1000;
- private static final int SHORT_POLL_TIMEOUT = 100;
-
- private int lastHashCode = 0;
- @Override
- public void run() {
- for (;;) {
- if (GuiMain.instance() != null) {
- List tmp = detectControllers();
- if (lastHashCode != tmp.hashCode()) {
- lastHashCode = tmp.hashCode();
- GuiMain.instance().updateControllerList(tmp);
- }
- try {
- Thread.sleep(POLL_TIMEOUT);
- } catch (InterruptedException e) {}
- } else {
- try {
- Thread.sleep(SHORT_POLL_TIMEOUT);
- } catch (InterruptedException e) {}
- }
- }
- }
-
- private static List detectControllers() {
- List controllers = new ArrayList();
-
- String os = System.getProperty("os.name");
- //System.out.println("[ControllerDetector] OS: " + os);
-
- if (os.contains("Linux")) {
- detectLinuxControllers(controllers);
- } else if (os.contains("Windows")) {
- System.out.println("Running on Windows! XInput coming soon."); //XXX debug text (win32)
- }
-
- for (HidDevice device : HidManager.getHidServices().getAttachedHidDevices()) {
- controllers.add(new GuiController(GuiControllerType.HID4JAVA, device.getPath()));
- }
- return controllers;
- }
-
- private static void detectLinuxControllers(List controllers) {
- File devInput = new File("/dev/input");
- if (!devInput.exists()) return;
-
- File[] linuxControllers = devInput.listFiles(new FilenameFilter() {
- public boolean accept(File dir, String name) {
- return name.startsWith("js"); //js0, js1, etc...
- }
- });
-
- for (File controller : linuxControllers) {
- controllers.add(new GuiController(GuiControllerType.LINUX, controller.getAbsolutePath()));
- }
- }
-}
diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/GuiCloseListener.java b/src/net/ash/HIDToVPADNetworkClient/gui/GuiCloseListener.java
index 41148eb..7e42d36 100644
--- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiCloseListener.java
+++ b/src/net/ash/HIDToVPADNetworkClient/gui/GuiCloseListener.java
@@ -25,29 +25,29 @@ import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import net.ash.HIDToVPADNetworkClient.Main;
-
public class GuiCloseListener implements WindowListener {
- @Override
- public void windowClosing(WindowEvent arg0) {
- Main.initiateShutdown();
- }
-
- @Override
- public void windowActivated(WindowEvent arg0) {}
+ @Override
+ public void windowClosing(WindowEvent arg0) {
+ Main.initiateShutdown();
+ }
+
+ @Override
+ public void windowActivated(WindowEvent arg0) {}
- @Override
- public void windowClosed(WindowEvent arg0) {}
+ @Override
+ public void windowClosed(WindowEvent arg0) {}
- @Override
- public void windowDeactivated(WindowEvent arg0) {}
+ @Override
+ public void windowDeactivated(WindowEvent arg0) {}
- @Override
- public void windowDeiconified(WindowEvent arg0) {}
+ @Override
+ public void windowDeiconified(WindowEvent arg0) {}
- @Override
- public void windowIconified(WindowEvent arg0) {}
+ @Override
+ public void windowIconified(WindowEvent arg0) {}
- @Override
- public void windowOpened(WindowEvent arg0) {}
+ @Override
+ public void windowOpened(WindowEvent arg0) {}
}
+
diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerList.java b/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerList.java
index 03402cd..51ba72d 100644
--- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerList.java
+++ b/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerList.java
@@ -23,21 +23,25 @@ package net.ash.HIDToVPADNetworkClient.gui;
import java.awt.BorderLayout;
import java.awt.Component;
+import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
-import javax.swing.AbstractButton;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
+import javax.swing.Timer;
+
+import net.ash.HIDToVPADNetworkClient.controller.Controller;
+import net.ash.HIDToVPADNetworkClient.manager.ControllerManager;
public class GuiControllerList extends JPanel {
private static final long serialVersionUID = 1L;
private JPanel innerScrollPanel;
- private ActionListener currentActionListener = null;
public GuiControllerList() {
super(new BorderLayout());
@@ -45,62 +49,60 @@ public class GuiControllerList extends JPanel {
innerScrollPanel = new JPanel();
innerScrollPanel.setLayout(new BoxLayout(innerScrollPanel, BoxLayout.PAGE_AXIS));
add(new JScrollPane(innerScrollPanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), BorderLayout.CENTER);
+
+ int delay = 1000; //milliseconds
+ ActionListener taskPerformer = new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ updateControllerList();
+ }
+ };
+ new Timer(delay, taskPerformer).start();
}
- public synchronized List getControllers() {
- List controllers = new ArrayList();
+ public synchronized void updateControllerList() {
+ //System.out.println("[GuiControllerList] Updating controller list..."); //XXX debug text
- for (Component component : innerScrollPanel.getComponents()) {
- if (component instanceof GuiControllerListItem) {
- controllers.add(((GuiControllerListItem)component).getData());
- }
- }
-
- return controllers;
- }
-
- public synchronized void updateControllerList(List controllers) {
- System.out.println("[GuiControllerList] Updating controller list..."); //XXX debug text
-
- HashMap components = new HashMap();
- for (Component component : innerScrollPanel.getComponents()) {
- if (component instanceof GuiControllerListItem) {
- components.put(component.hashCode(), ((GuiControllerListItem)component).getData());
- }
- }
+ boolean repaintNeeded = false;
+
+ List attachedControllers = ControllerManager.getAttachedControllers();
List newComponents = new ArrayList();
- for (GuiController controller : controllers) {
- GuiControllerListItem i;
- if (components.containsKey(controller.hashCode())) {
- i = new GuiControllerListItem(components.get(controller.hashCode()));
- } else {
+
+ Map components = new HashMap<>();
+ for (Component component : innerScrollPanel.getComponents()) {
+ if (component instanceof GuiControllerListItem) {
+ GuiControllerListItem comp = (GuiControllerListItem) component;
+ Controller cont = comp.getController();
+ if(attachedControllers.contains(cont)){
+ components.put(cont,comp);
+ }else{//Controller removed
+ repaintNeeded = true;
+ }
+ }
+ }
+
+ //Build new list of components.
+ for (Controller controller : attachedControllers) {
+ GuiControllerListItem i = null;
+ if (components.containsKey(controller)) {
+ newComponents.add(components.get(controller));
+ }else{ //New controller was added
+ repaintNeeded = true;
i = new GuiControllerListItem(controller);
+ newComponents.add(i);
}
- newComponents.add(i);
}
- innerScrollPanel.removeAll();
- for (GuiControllerListItem component : newComponents) {
- component.addActionListener(currentActionListener);
- innerScrollPanel.add(component);
- }
-
- //TODO research performance impact - 300+ms on swing.RepaintManager?
- innerScrollPanel.revalidate();
- revalidate();
- innerScrollPanel.repaint();
- repaint();
- }
-
- public synchronized void setActionListener(ActionListener l) {
- currentActionListener = l;
- for (Component c : innerScrollPanel.getComponents()) {
- try {
- ((AbstractButton)c).addActionListener(l);
- } catch (ClassCastException e) {
- System.out.println("[GuiControllerList] Bad cast on " + c.getClass().getName() + " to AbstractButton!");
- }
+ if(repaintNeeded){
+ innerScrollPanel.removeAll();
+ for (GuiControllerListItem component : newComponents) {
+ innerScrollPanel.add(component);
+ }
+
+ innerScrollPanel.revalidate();
+ revalidate();
+ innerScrollPanel.repaint();
+ repaint();
}
}
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerListItem.java b/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerListItem.java
index ef6db03..7b254d0 100644
--- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerListItem.java
+++ b/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerListItem.java
@@ -23,52 +23,80 @@ package net.ash.HIDToVPADNetworkClient.gui;
import java.awt.BorderLayout;
import java.awt.Dimension;
+import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
+import javax.swing.Timer;
-public class GuiControllerListItem extends JPanel {
+import lombok.Getter;
+import net.ash.HIDToVPADNetworkClient.controller.Controller;
+import net.ash.HIDToVPADNetworkClient.network.NetworkManager;
+
+public class GuiControllerListItem extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
- private GuiController data = null;
+ @Getter private final Controller controller;
private JCheckBox checkbox;
- public GuiControllerListItem(GuiController data) {
+ public GuiControllerListItem(Controller data) {
super(new BorderLayout());
setMinimumSize(new Dimension (300, 30));
setPreferredSize(new Dimension(300, 30));
setMaximumSize(new Dimension(2000, 30));
- this.data = data;
+ this.controller = data;
checkbox = new JCheckBox(getFlavorText());
- checkbox.setSelected(data.getActiveState());
+ checkbox.setSelected(data.isActive());
+ checkbox.addActionListener(this);
add(checkbox);
+
+ int delay = 100; //milliseconds
+ ActionListener taskPerformer = new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ checkbox.setEnabled(NetworkManager.getInstance().isConnected());
+ checkbox.setSelected(controller.isActive());
+ }
+ };
+ new Timer(delay, taskPerformer).start();
}
private String getFlavorText() {
- switch (data.getType()) {
- case HID4JAVA:
- return "USB HID on " + data.getId().toString();
- case LINUX:
- return "Linux controller on " + data.getId().toString();
- default:
- return data.toString();
- }
- }
-
- public void addActionListener(ActionListener l) {
- checkbox.addActionListener(l);
- }
-
- public GuiController getData() {
- return data;
- }
-
- @Override
- public int hashCode() {
- return data.hashCode();
+ return controller.getInfoText();
}
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ boolean selected = ((JCheckBox) e.getSource()).isSelected();
+ controller.setActive(selected);
+ checkbox.setSelected(controller.isActive());
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((controller == null) ? 0 : controller.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ GuiControllerListItem other = (GuiControllerListItem) obj;
+ if (controller == null) {
+ if (other.controller != null)
+ return false;
+ } else if (!controller.equals(other.controller))
+ return false;
+ return true;
+ }
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java b/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java
index 999a7f3..47a0da1 100644
--- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java
+++ b/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java
@@ -24,6 +24,8 @@ package net.ash.HIDToVPADNetworkClient.gui;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
@@ -31,19 +33,25 @@ import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
-public class GuiInputControls extends JPanel {
+import net.ash.HIDToVPADNetworkClient.network.NetworkManager;
+
+public class GuiInputControls extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
private static GuiInputControls instance = null;
- private static final String DEFAULT_PACKET_INTERVAL = "1000";
+ private static final String CONNECT = "Connect";
+ private static final String DISCONNECT = "Disconnect";
+ private static final String RECONNECTING = "Reconnecting";
private JButton connectButton;
private JTextField ipTextBox;
private JPanel ipTextBoxWrap;
private JTextField packetIntervalTextBox;
- private JPanel piTextBoxWrap;
private JLabel statusLabel;
+
public GuiInputControls() throws Exception {
super();
if (instance != null) {
@@ -54,33 +62,24 @@ public class GuiInputControls extends JPanel {
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
setPreferredSize(new Dimension(220, 150));
- connectButton = new JButton("Connect");
+ connectButton = new JButton(CONNECT);
connectButton.setAlignmentX(Component.CENTER_ALIGNMENT);
ipTextBox = new JTextField();
ipTextBox.setColumns(15);
+ ipTextBox.setText("192.168.0.35");
ipTextBoxWrap = new JPanel(new FlowLayout());
ipTextBoxWrap.add(new JLabel("IP: "));
ipTextBoxWrap.add(ipTextBox);
ipTextBoxWrap.setMaximumSize(new Dimension(1000, 20));
-
- packetIntervalTextBox = new JTextField();
- packetIntervalTextBox.setColumns(3);
- packetIntervalTextBox.setText(DEFAULT_PACKET_INTERVAL);
- //TODO sanitize input
- piTextBoxWrap = new JPanel(new FlowLayout());
- piTextBoxWrap.add(new JLabel("Packet interval: "));
- piTextBoxWrap.add(packetIntervalTextBox);
- piTextBoxWrap.setMaximumSize(new Dimension(1000, 20));
-
+
statusLabel = new JLabel("Ready.");
statusLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
add(Box.createVerticalGlue());
- add(ipTextBoxWrap);
- add(piTextBoxWrap);
+ add(ipTextBoxWrap);
add(Box.createRigidArea(new Dimension(1, 4)));
add(connectButton);
@@ -90,7 +89,24 @@ public class GuiInputControls extends JPanel {
add(Box.createVerticalGlue());
- connectButton.addActionListener(GuiInteractionManager.instance());
+ connectButton.addActionListener(this);
+
+ int delay = 100; //milliseconds
+ ActionListener taskPerformer = new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ if(NetworkManager.getInstance().isReconnecting()){
+ connectButton.setText(RECONNECTING);
+ connectButton.setEnabled(false);
+ }else if(NetworkManager.getInstance().isConnected()){
+ connectButton.setText(DISCONNECT);
+ connectButton.setEnabled(true);
+ }else{
+ connectButton.setText(CONNECT);
+ connectButton.setEnabled(true);
+ }
+ }
+ };
+ new Timer(delay, taskPerformer).start();
}
public static GuiInputControls instance() {
@@ -112,4 +128,22 @@ public class GuiInputControls extends JPanel {
public JLabel getStatusLabel() {
return statusLabel;
}
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ if(NetworkManager.getInstance().isReconnecting()){
+
+ }else{
+ if(NetworkManager.getInstance().isConnected()){
+ NetworkManager.getInstance().disconnect();
+ }else{
+ NetworkManager.getInstance().connect(ipTextBox.getText());
+ }
+ }
+ }
+ });
+
+ }
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/GuiInteractionManager.java b/src/net/ash/HIDToVPADNetworkClient/gui/GuiInteractionManager.java
deleted file mode 100644
index 3f3ef75..0000000
--- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiInteractionManager.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *******************************************************************************/
-package net.ash.HIDToVPADNetworkClient.gui;
-
-import java.awt.Component;
-import java.awt.Container;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-
-import net.ash.HIDToVPADNetworkClient.controller.ControllerManager;
-import net.ash.HIDToVPADNetworkClient.network.NetworkManager;
-
-public class GuiInteractionManager implements ActionListener {
- private static GuiInteractionManager instance = null;
-
- public GuiInteractionManager() throws Exception {
- if (instance != null) {
- throw new Exception("GuiInputControls already has an instance!");
- }
- instance = this;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- /*
- * Swing GUI events
- */
- if (e.getSource() instanceof Component) {
- Component source = (Component)e.getSource();
- Container parent = source.getParent();
-
- /*
- * Action handler code for GuiControllerListItem
- */
- if (parent instanceof GuiControllerListItem) {
- GuiControllerListItem rparent = (GuiControllerListItem)parent;
- JCheckBox rsource = (JCheckBox)source;
-
- rparent.getData().setActiveState(rsource.isSelected());
-
- if (NetworkManager.instance().isRunning()) {
- ControllerManager.instance().updateControllers(GuiMain.instance().getControllers());
- }
- /*
- * Action handler for GuiInputControls
- */
- } else if (parent instanceof GuiInputControls) {
- GuiInputControls rparent = (GuiInputControls)parent;
- /*
- * (Dis)connect button
- */
- if (source instanceof JButton) {
- if (NetworkManager.instance().isRunning()) {
- disconnect();
- } else {
- NetworkManager.instance().setPacketInterval(Integer.parseInt(rparent.getPacketIntervalTextBox().getText()));
- if (ControllerManager.instance().startConnection(GuiMain.instance().getIPText(), GuiMain.instance().getControllers())) {
- rparent.getIpTextBox().setEnabled(false);
- rparent.getPacketIntervalTextBox().setEnabled(false);
- rparent.getConnectButton().setText("Disconnect");
- GuiInputControls.instance().getStatusLabel().setText("Connected!");
- } else {
- ControllerManager.instance().stopConnection();
- GuiInputControls.instance().getStatusLabel().setText("Connection Failed!");
- }
- }
- }
- }
- }
- }
-
- public void disconnect() {
- ControllerManager.instance().stopConnection();
- GuiInputControls.instance().getIpTextBox().setEnabled(true);
- GuiInputControls.instance().getPacketIntervalTextBox().setEnabled(true);
- GuiInputControls.instance().getConnectButton().setText("Connect");
- GuiInputControls.instance().getStatusLabel().setText("Disconnected.");
- }
-
- public static GuiInteractionManager instance() {
- return instance;
- }
-}
diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/GuiMain.java b/src/net/ash/HIDToVPADNetworkClient/gui/GuiMain.java
index bb36d4d..ebd08e8 100644
--- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiMain.java
+++ b/src/net/ash/HIDToVPADNetworkClient/gui/GuiMain.java
@@ -23,7 +23,6 @@ package net.ash.HIDToVPADNetworkClient.gui;
import java.awt.BorderLayout;
import java.awt.Dimension;
-import java.util.List;
import javax.swing.JComponent;
import javax.swing.JFrame;
@@ -54,18 +53,9 @@ public class GuiMain extends JPanel {
public GuiMain() {
super(new BorderLayout());
- try {
- new GuiInteractionManager();
- } catch (Exception e) {
- e.printStackTrace();
- Main.fatal();
- }
-
-
leftControllerList = new GuiControllerList();
leftControllerList.setPreferredSize(new Dimension(300, 100));
add(leftControllerList, BorderLayout.CENTER);
- leftControllerList.setActionListener(GuiInteractionManager.instance());
try {
rightSideControls = new GuiInputControls();
@@ -76,18 +66,6 @@ public class GuiMain extends JPanel {
add(rightSideControls, BorderLayout.LINE_END);
}
- public void updateControllerList(List controllers) {
- leftControllerList.updateControllerList(controllers);
- }
-
- public List getControllers() {
- return leftControllerList.getControllers();
- }
-
- public String getIPText() {
- return rightSideControls.getIpTextBox().getText();
- }
-
public static GuiMain instance() {
return instance;
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java b/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java
new file mode 100644
index 0000000..65d3118
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+package net.ash.HIDToVPADNetworkClient.manager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import lombok.extern.java.Log;
+import net.ash.HIDToVPADNetworkClient.controller.Controller;
+import net.ash.HIDToVPADNetworkClient.network.NetworkHIDDevice;
+import net.ash.HIDToVPADNetworkClient.network.NetworkManager;
+import net.ash.HIDToVPADNetworkClient.util.Settings;
+import net.ash.HIDToVPADNetworkClient.util.Utilities;
+
+@Log
+public class ActiveControllerManager implements Runnable{
+ private static ActiveControllerManager instance = new ActiveControllerManager();
+
+ private ActiveControllerManager(){
+ }
+
+ public static ActiveControllerManager getInstance(){
+ return instance;
+ }
+
+ @Override
+ public void run() { //TODO: Add mechanism to stop these threads?
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while(true){
+ updateControllerStates();
+ ControllerManager.detectControllers();
+ Utilities.sleep(Settings.DETECT_CONTROLLER_INTERVAL);
+ }
+ }
+ }).start();
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while(true){
+ handleControllerInputs();
+ Utilities.sleep(Settings.HANDLE_INPUTS_INTERVAL);
+ }
+ }
+ }).start();
+ }
+
+ private Map activeControllers = new HashMap<>();
+ public void updateControllerStates() {
+ List currentControllers = ControllerManager.getActiveControllers();
+
+ List toAdd = new ArrayList<>();
+ List toRemove = new ArrayList<>();
+ synchronized (activeControllers) {
+ //Adding all missing.
+
+ for(Controller c: currentControllers){
+ if(!activeControllers.containsKey(c)){
+ log.info("Added " + c);
+ toAdd.add(c);
+ }
+ }
+
+ //removing all old
+ for(Controller c : activeControllers.keySet()){
+ if(!currentControllers.contains(c)){
+ log.info("Removed " + c);
+ toRemove.add(c);
+ }
+ }
+ }
+
+ addController(toAdd);
+ removeController(toRemove);
+ }
+
+ private void removeController(List toRemove) {
+ synchronized (activeControllers) {
+ for(Controller c : toRemove){
+ NetworkManager.getInstance().removeHIDDevice(activeControllers.get(c));
+ c.destroyDriver();
+ activeControllers.remove(c);
+ }
+ }
+ }
+
+ private void addController(List toAdd) {
+ synchronized (activeControllers) {
+ for(Controller c : toAdd){
+ NetworkHIDDevice hiddevice = new NetworkHIDDevice(c.getVID(), c.getPID());
+ hiddevice.sendAttach();
+ NetworkManager.getInstance().addHIDDevice(hiddevice);
+ activeControllers.put(c,hiddevice);
+ }
+ }
+ }
+
+ private void handleControllerInputs() {
+ synchronized (activeControllers) {
+ for(Entry entry : activeControllers.entrySet()){
+ byte[] data = entry.getKey().getLatestData();
+ if(data != null){
+ NetworkHIDDevice device = entry.getValue();
+ device.sendRead(data);
+ }
+ }
+ }
+ }
+
+ public void attachAllActiveControllers() {
+ synchronized (activeControllers) {
+ for(Entry entry : activeControllers.entrySet()){
+ NetworkHIDDevice device = entry.getValue();
+ device.sendAttach();
+ }
+ }
+ }
+
+ /**
+ *
+ * @param HIDhandle
+ * @return returns the controller for the given handle. returns null if the controller with the given handle is not found.
+ */
+ public Controller getControllerByHIDHandle(int HIDhandle) {
+ for(Entry entry: activeControllers.entrySet()){
+ if(entry.getValue().getHidHandle() == HIDhandle){
+ return entry.getKey();
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java b/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java
new file mode 100644
index 0000000..931b529
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+package net.ash.HIDToVPADNetworkClient.manager;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.ivan.xinput.XInputDevice;
+import com.ivan.xinput.XInputDevice14;
+import com.ivan.xinput.exceptions.XInputNotLoadedException;
+
+import lombok.Synchronized;
+import net.ash.HIDToVPADNetworkClient.controller.Controller;
+import net.ash.HIDToVPADNetworkClient.controller.Controller.ControllerType;
+import net.ash.HIDToVPADNetworkClient.controller.PureJavaHidController;
+import net.ash.HIDToVPADNetworkClient.controller.LinuxDevInputController;
+import net.ash.HIDToVPADNetworkClient.controller.XInput13Controller;
+import net.ash.HIDToVPADNetworkClient.controller.XInput14Controller;
+import net.ash.HIDToVPADNetworkClient.controller.XInputController;
+import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
+import purejavahidapi.HidDeviceInfo;
+import purejavahidapi.PureJavaHidApi;
+
+public class ControllerManager{
+ private static Map attachedControllers = new HashMap<>();
+
+ /**
+ * Detects all attached controller.
+ */
+ @Synchronized("attachedControllers")
+ public static void detectControllers() {
+ String os = System.getProperty("os.name");
+ //System.out.println("[ControllerDetector] OS: " + os);
+
+ Map connectedDevices = new HashMap<>();
+
+ if (os.contains("Linux")) {
+ connectedDevices.putAll(detectLinuxControllers());
+ } else if (os.contains("Windows")) {
+ connectedDevices.putAll(detectWindowsControllers());
+ }
+
+ connectedDevices.putAll(detectHIDDevices());
+
+ //Remove detached devices
+ List toRemove = new ArrayList<>();
+ for(String s : attachedControllers.keySet()){
+ if(!connectedDevices.containsKey(s)){
+ toRemove.add(s);
+ }
+ }
+ for(String remove : toRemove){
+ attachedControllers.get(remove).destroyAll();
+ attachedControllers.remove(remove);
+ }
+
+ //Add attached devices!
+ for(Entry entry : connectedDevices.entrySet()){
+ String deviceIdentifier = entry.getKey();
+ if(!attachedControllers.containsKey(deviceIdentifier)){
+ Controller c = null;
+ switch(entry.getValue()){
+ case PureJAVAHid:
+ try {
+ c= PureJavaHidController.getInstance(deviceIdentifier);
+ } catch (ControllerInitializationFailedException e) {
+ //e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ break;
+ case LINUX:
+ try {
+ c = new LinuxDevInputController(deviceIdentifier);
+ } catch (ControllerInitializationFailedException e) {
+ //e.printStackTrace();
+ }
+ break;
+ /*
+ * TODO:
+ * Currently the XInput will be set active automatically.
+ * But this should move to something for the settings?
+ */
+ case XINPUT14:
+ try {
+ c = new XInput14Controller(deviceIdentifier);
+ c.setActive(true);
+ } catch (ControllerInitializationFailedException e) {
+ //e.printStackTrace();
+ }
+ break;
+ case XINPUT13:
+ try {
+ c = new XInput13Controller(deviceIdentifier);
+ c.setActive(true);
+ } catch (ControllerInitializationFailedException e) {
+ //e.printStackTrace();
+ }
+ break;
+ default:
+ break;
+ }
+ if(c != null){ //I don't like that starting the Thread happens here =/
+ new Thread(c).start();
+ attachedControllers.put(deviceIdentifier,c);
+ }
+ }
+ }
+ }
+
+ @Synchronized("attachedControllers")
+ public static List getAttachedControllers() {
+ return new ArrayList<>(attachedControllers.values());
+ }
+
+ private static Map detectHIDDevices() {
+ Map connectedDevices = new HashMap<>();
+
+ for (HidDeviceInfo info : PureJavaHidApi.enumerateDevices()) {
+ if(info.getUsagePage() == 0x05 || info.getUsagePage() == 0x04 || (info.getVendorId() == 0x57e)){
+ connectedDevices.put(info.getPath(),ControllerType.PureJAVAHid);
+ }
+ }
+
+ return connectedDevices;
+ }
+
+ private static Map detectWindowsControllers() {
+ Map result = new HashMap<>();
+ ControllerType type = ControllerType.XINPUT13;
+ if(XInputDevice.isAvailable() || XInputDevice14.isAvailable()) {
+ if(XInputDevice14.isAvailable()){
+ type = ControllerType.XINPUT14;
+ }
+ for(int i =0;i<4;i++){
+ XInputDevice device;
+ try {
+ device = XInputDevice.getDeviceFor(i);
+ if(device.poll() && device.isConnected()){ //Check if it is this controller is connected
+ result.put(XInputController.XINPUT_INDENTIFER + i, type);
+ }
+ } catch (XInputNotLoadedException e) {
+ //This shouln't happen?
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private static Map detectLinuxControllers() {
+ Map result = new HashMap<>();
+ File devInput = new File("/dev/input");
+ if (!devInput.exists()) return result;
+
+ File[] linuxControllers = devInput.listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.startsWith("js"); //js0, js1, etc...
+ }
+ });
+
+ for (File controller : linuxControllers) {
+ result.put(controller.getAbsolutePath(),ControllerType.LINUX);
+ }
+
+ return result;
+ }
+
+ @Synchronized("attachedControllers")
+ public static List getActiveControllers() {
+ List active = new ArrayList<>();
+ for(Controller c : attachedControllers.values()){
+ if(c.isActive()){
+ active.add(c);
+ }
+ }
+ return active;
+ }
+
+ @Synchronized("attachedControllers")
+ public static void deactivateAllAttachedControllers() {
+ for(Controller c : attachedControllers.values()){
+ c.setActive(false);
+ }
+ }
+
+}
diff --git a/src/net/ash/HIDToVPADNetworkClient/network/NetworkHIDDevice.java b/src/net/ash/HIDToVPADNetworkClient/network/NetworkHIDDevice.java
new file mode 100644
index 0000000..c9a3af4
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/network/NetworkHIDDevice.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+
+package net.ash.HIDToVPADNetworkClient.network;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+import net.ash.HIDToVPADNetworkClient.network.commands.AttachCommand;
+import net.ash.HIDToVPADNetworkClient.network.commands.DetachCommand;
+import net.ash.HIDToVPADNetworkClient.network.commands.DeviceCommand;
+import net.ash.HIDToVPADNetworkClient.network.commands.ReadCommand;
+import net.ash.HIDToVPADNetworkClient.util.HandleFoundry;
+
+public class NetworkHIDDevice {
+ @Getter private final short vid;
+ @Getter private final short pid;
+
+ @Getter @Setter private short deviceslot;
+ @Getter @Setter private byte padslot;
+
+ @Getter private int hidHandle = HandleFoundry.next();
+ @Getter(AccessLevel.PRIVATE) private List commands = new ArrayList<>();
+
+ @Getter(AccessLevel.PRIVATE) @Setter(AccessLevel.PRIVATE) private ReadCommand latestRead;
+
+ private Object readCommandLock = new Object();
+
+ public NetworkHIDDevice(short vid, short pid){
+ this.vid = vid;
+ this.pid = pid;
+ }
+
+ private void addCommand(DeviceCommand command){
+ this.commands.add(command);
+ }
+
+ private void clearCommands(){
+ this.commands.clear();
+ }
+
+ public void sendAttach(){
+ addCommand(new AttachCommand(getHidHandle(), getVid(), getPid(),this));
+ }
+
+ public void sendDetach(){
+ addCommand(new DetachCommand(getHidHandle(),this));
+ }
+
+ private byte[] lastdata = null;
+ public void sendRead(byte[] data){
+ if(!Arrays.equals(lastdata, data)){
+ synchronized (readCommandLock) {
+ setLatestRead(new ReadCommand(getHidHandle(),data, this)); //Only get the latest Value.
+ }
+ lastdata = data.clone();
+ }
+ }
+
+ public Collection extends DeviceCommand> getCommandList() {
+ List commands = new ArrayList<>();
+ commands.addAll(getCommands());
+ DeviceCommand lastRead;
+
+ synchronized (readCommandLock) {
+ if((lastRead = getLatestRead()) != null){
+ commands.add(lastRead);
+ setLatestRead(null);
+ }
+ }
+
+ clearCommands();
+ return commands;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + hidHandle;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ NetworkHIDDevice other = (NetworkHIDDevice) obj;
+ if (hidHandle != other.hidHandle)
+ return false;
+ return true;
+ }
+}
diff --git a/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java b/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java
index f4e1b3c..46c6ea6 100644
--- a/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java
+++ b/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java
@@ -19,240 +19,329 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*******************************************************************************/
+
package net.ash.HIDToVPADNetworkClient.network;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
-import java.util.HashMap;
-import java.util.concurrent.ThreadLocalRandom;
+import lombok.Getter;
+import lombok.Synchronized;
+import lombok.extern.java.Log;
+import net.ash.HIDToVPADNetworkClient.network.commands.AttachCommand;
+import net.ash.HIDToVPADNetworkClient.network.commands.DetachCommand;
+import net.ash.HIDToVPADNetworkClient.network.commands.DeviceCommand;
+import net.ash.HIDToVPADNetworkClient.network.commands.PingCommand;
+import net.ash.HIDToVPADNetworkClient.network.commands.ReadCommand;
+import net.ash.HIDToVPADNetworkClient.util.Settings;
+import net.ash.HIDToVPADNetworkClient.util.Utilities;
-import net.ash.HIDToVPADNetworkClient.controller.Controller;
-import net.ash.HIDToVPADNetworkClient.gui.GuiInteractionManager;
+@Log
+public class NetworkManager implements Runnable{
+ private final TCPClient tcpClient = new TCPClient();
+ private UDPClient udpClient = null;
+
+ private static NetworkManager instance = null;
+
+ private List ownCommands = new ArrayList<>();
+
+ private NetworkManager() {
+
+ }
+
+ public static NetworkManager getInstance(){
+ if(instance == null){
+ instance = new NetworkManager();
+ }
+ return instance;
+ }
+
+ @Getter private List devices = new ArrayList<>();
+
+ public void addHIDDevice(NetworkHIDDevice device){
+ if(!getDevices().contains(device)){
+ synchronized (devices) {
+ getDevices().add(device);
+ }
+ }
+ }
+
+ /*
+ * We want to remove them at the end of a cycle. To make sure the detach was send before removing.
+ */
+ @Getter private List toRemove = new ArrayList<>();
+ @Synchronized("toRemove")
+ public void removeHIDDevice(NetworkHIDDevice device) {
+ device.sendDetach();
+ toRemove.add(device);
+ }
-public class NetworkManager implements Runnable {
- private static final int PING_INTERVAL = 1000;
-
- public static int clientID;
-
- private static NetworkManager instance = null;
-
- private Object controllersLock = new Object();
- private HashMap controllers = new HashMap();
-
- private Thread networkThread;
-
- private TCPClient tcp;
- private UDPClient udp;
-
- private int packetInterval = 100;
-
- private enum NetworkState {
- DISCONNECTED,
- FRESH, //Connected, no handshake
- CONNECTED
- }
- private NetworkState networkState = NetworkState.DISCONNECTED;
-
- public NetworkManager() throws Exception {
- if (instance != null) {
- throw new Exception("NetworkManager already has an instance!");
- }
- instance = this;
-
- networkThread = new Thread(this);
- tcp = new TCPClient();
- udp = new UDPClient();
-
- pingThread.start();
-
- clientID = ThreadLocalRandom.current().nextInt();
- System.out.println("[NetworkManager] clientID: " + clientID);
- }
-
- private int runLoopCounter = 0;
- @Override
- public void run() {
- for (;;) {
- for (;;) {
- /*
- * Socket is connected, handshake needed
- */
- if (networkState == NetworkState.FRESH) {
- try {
- switch (tcp.doHandshake()) {
-
- case BAD_HANDSHAKE:
- tcp.abort();
- networkState = NetworkState.DISCONNECTED;
- continue;
-
- case NEW_CLIENT:
- synchronized (controllersLock) {
- for (Controller c : controllers.values()) {
- tcp.sendAttach(c);
- }
- }
- networkState = NetworkState.CONNECTED;
- break;
-
- case SAME_CLIENT:
- networkState = NetworkState.CONNECTED;
- break;
-
- default:
- tcp.abort();
- networkState = NetworkState.DISCONNECTED;
- continue;
-
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- } else if (networkState == NetworkState.CONNECTED) {
- synchronized (controllersLock) {
- try {
- udp.send(controllers);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- if (runLoopCounter++ == PING_INTERVAL / packetInterval) {
- synchronized(pingThread) {
- pingThread.notify();
- }
- runLoopCounter = 0;
- }
-
- sleep(packetInterval);
- } else if (networkState == NetworkState.DISCONNECTED) break;
- } //for (;;)
-
- try {
- synchronized (networkThread) {
- networkThread.wait();
- }
- } catch (InterruptedException e) {}
- }
- }
-
- public void connect(String ip) {
- System.out.println("[NetworkManager] Connecting to " + ip + "..."); //XXX debug text
- try {
- udp.connect(ip);
- tcp.connect(ip);
- } catch (Exception e) {
- System.err.println("[NetworkManager] Couldn't connect to Wii U!");
- e.printStackTrace();
- return;
- }
- networkState = NetworkState.FRESH;
- if (networkThread.getState() == Thread.State.NEW) {
- networkThread.start();
- } else if (networkThread.getState() == Thread.State.WAITING) {
- synchronized (networkThread) {
- networkThread.notify();
- }
- }
- }
-
- public void disconnect() {
- networkState = NetworkState.DISCONNECTED;
-
- if (!Thread.currentThread().equals(networkThread) && networkThread.getState() != Thread.State.NEW) {
- while (networkThread.getState() != Thread.State.WAITING) {sleep(1);}
- }
-
- try {
- tcp.abort();
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- System.out.println("[NetworkManager] Disconnected.");
- }
-
- /*
- * Is there an active connection?
- */
- public boolean isConnected() {
- return networkState == NetworkState.FRESH || networkState == NetworkState.CONNECTED;
- }
-
- /*
- * Is the active connection good for data?
- */
- public boolean isRunning() {
- return networkState == NetworkState.CONNECTED;
- }
-
- public HashMap getControllers() {
- return controllers;
- }
-
- public void setPacketInterval(int packetInterval) {
- this.packetInterval = packetInterval;
- }
-
- public void addController(Controller controller) {
- synchronized (controllersLock) {
- if (isRunning()) {
- try {
- tcp.sendAttach(controller);
- } catch (Exception e) {return;};
- }
- controllers.put(controller.getID().hashCode(), controller);
- }
- }
-
- public void removeController(Controller controller) {
- synchronized (controllersLock) {
- if (isRunning()) {
- try {
- tcp.sendDetach(controller);
- } catch (Exception e) {return;};
- }
- controller.destroy();
- controllers.remove(controller.getID().hashCode());
- }
- }
-
- public void removeAllControllers() {
- synchronized (controllersLock) {
- for (Controller c : controllers.values()) {
- if (isRunning()) {
- try {
- tcp.sendDetach(c);
- } catch (Exception e) {continue;};
- }
- c.destroy();
- controllers.remove(c.getID().hashCode());
- }
- }
- }
-
- private Thread pingThread = new Thread(new Runnable() {
- public void run() {
- for (;;) {
- synchronized (pingThread) {
- try {
- pingThread.wait();
- } catch (InterruptedException e) {}
- }
-
- if (!tcp.ping()) {
- System.out.println("[NetworkManager] Ping failed, disconnecting...");
- GuiInteractionManager.instance().disconnect();
- }
- }
- }
- }, "Ping Thread");
-
- private void sleep(long ticks) {
- try {
- Thread.sleep(ticks);
- } catch (InterruptedException e) {}
- }
-
- public static NetworkManager instance() {
- return instance;
- }
+ @Override
+ public void run() {
+ int i = 0;
+ while(true){
+ proccessCommands();
+ Utilities.sleep(Settings.PROCESS_CMD_INTERVAL);
+ if(i++ > Settings.PING_INTERVAL/Settings.PROCESS_CMD_INTERVAL){
+ ping();
+ i = 0;
+ }
+ }
+ }
+
+ private void ping() {
+ if(isConnected() || tcpClient.isShouldRetry())sendingCommand(new PingCommand());
+ }
+
+ public void proccessCommands(){
+ List commands = new ArrayList<>();
+ commands.addAll(ownCommands); //TODO: Does this need a synchronized block? It _should_ be only access from this thread. Need to think about it
+ ownCommands.clear();
+ synchronized (toRemove) {
+ synchronized (devices) {
+ for(NetworkHIDDevice device : getDevices()){
+ commands.addAll(device.getCommandList());
+ }
+ }
+ }
+
+ if(commands.isEmpty())return;
+
+ //Split up into "read commands" and other commands.
+ List readCommands = new ArrayList<>();
+ {
+ for(DeviceCommand command : commands){
+ if(command instanceof ReadCommand){
+ readCommands.add((ReadCommand) command);
+ }
+ }
+ commands.removeAll(readCommands);
+ }
+
+ if(!readCommands.isEmpty()){
+ sendingRead(readCommands);
+ }
+
+ if(!commands.isEmpty()){
+ for(DeviceCommand command : commands){
+ sendingCommand(command);
+ }
+ }
+
+ synchronized (toRemove) {
+ synchronized (devices) {
+ for(NetworkHIDDevice d: toRemove){
+ commands.remove(d);
+ }
+ }
+ }
+ }
+
+ private void sendingCommand(DeviceCommand command) {
+ boolean result = false;
+ if(isConnected() || tcpClient.isShouldRetry()){
+ if(command instanceof AttachCommand){
+ result = sendAttach((AttachCommand) command);
+ }else if(command instanceof DetachCommand){
+ result = sendDetach((DetachCommand) command);
+ }else if(command instanceof PingCommand){
+ sendPing((PingCommand) command);
+ result = true;
+ }else{
+ log.info("UNKNOWN COMMAND!");
+ result = true;
+ }
+ }else{
+ Utilities.sleep(Settings.SENDING_CMD_SLEEP_IF_NOT_CONNECTED); //TODO: move magic value to Settings
+ }
+
+ //Add the command again on errors
+ if(!result){
+ addCommand(command);
+ }
+ }
+
+ //TODO: PONG from WiiU? Hey Quark ;)
+ private void sendPing(PingCommand command) {
+ if(sendTCP(Protocol.getRawPingDataToSend(command))){
+ log.info("PING");
+ }else{
+ log.info("Sending the PING failed");
+ }
+ }
+
+ private boolean sendDetach(DetachCommand command) {
+ byte[] sendData;
+ try {
+ sendData = Protocol.getRawDetachDataToSend(command);
+ if(sendTCP(sendData)){
+ log.info("Success detach command for device (" + command.getSender() + ") sent!");
+ }else{
+ log.info("Sending the detach command for device (" + command.getSender() + ") failed. sendTCP failed");
+ return false;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+ //TODO: Maybe move it into the Protocol class?
+ private boolean sendAttach(AttachCommand command) {
+ //Send the TCP command
+ byte[] sendData = null;
+ try {
+ sendData = Protocol.getRawAttachDataToSend(command);
+ }catch (IOException e) {
+ log.info("Building the attach command for device (" + command.getSender() + ") failed." + e.getMessage());
+ return false;
+ }
+ if(sendTCP(sendData)){
+ byte configFound = 0;
+ try {
+ configFound = recvTCPByte();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ if(configFound == Protocol.TCP_CMD_ATTACH_CONFIG_FOUND){
+ //log.info("Config on the console found!");
+ }else if(configFound == Protocol.TCP_CMD_ATTACH_CONFIG_NOT_FOUND){
+ log.info("NO CONFIG FOUND.");
+ return false;
+ }else if (configFound == 0){
+ log.info("Failed to get byte.");
+ disconnect();
+ return false;
+ }
+
+ byte userDataOkay = 0;
+ try {
+ userDataOkay = recvTCPByte();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ if(userDataOkay == Protocol.TCP_CMD_ATTACH_USERDATA_OKAY){
+ //log.info("userdata okay!");
+ }else if(userDataOkay == Protocol.TCP_CMD_ATTACH_USERDATA_BAD){
+ log.info("USERDATA BAD.");
+ return false;
+ }else if (userDataOkay == 0){
+ log.info("Failed to get byte.");
+ disconnect();
+ return false;
+ }
+
+ //We receive our device slot and pad slot
+ short deviceslot = -1;
+ byte padslot = -1;
+ try {
+ deviceslot = recvTCPShort();
+ padslot = recvTCPByte();
+ } catch (IOException e) {
+ log.info("Recieving data after sending a attach failed for device (" + command.getSender() + ") failed." + e.getMessage());
+ return false;
+ }
+
+ if(deviceslot < 0 || padslot < 0){
+ log.info("Recieving data after sending a attach failed for device (" + command.getSender() + ") failed. We need to disconnect =(");
+ disconnect();
+ return false;
+ }
+
+ //Let's save them for later.
+ NetworkHIDDevice sender = command.getSender();
+ if(sender != null){
+ sender.setDeviceslot(deviceslot);
+ sender.setPadslot(padslot);
+ }else{
+ log.info("Something really went wrong. Got an attach event with out an " + NetworkHIDDevice.class.getSimpleName());
+ return false;
+ }
+ log.info("Attaching done!");
+ return true;
+ }else{
+ log.info("Sending the attach command for device (" + command.getSender() + ") failed. sendTCP failed");
+ return false;
+ }
+ }
+
+ private void sendingRead(List readCommands) {
+ byte[] rawCommand;
+ try {
+ rawCommand = Protocol.getRawReadDataToSend(readCommands);
+ System.out.println("UDP Packet: "+ Utilities.ByteArrayToString(rawCommand));
+ sendUDP(rawCommand);
+ } catch (IOException e) {
+ System.out.println("Sending read data failed.");
+ }
+ }
+
+ private boolean sendUDP(byte[] rawCommand) {
+ boolean result = false;
+ if(udpClient != null){
+ try {
+ udpClient.send(rawCommand);
+ result = true;
+ } catch (Exception e) {
+ //
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ private boolean sendTCP(byte[] rawCommand) {
+ boolean result = false;
+ if(tcpClient != null){
+ try {
+ tcpClient.send(rawCommand);
+ result = true;
+ } catch (Exception e) {
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ public void disconnect() {
+ //ControllerManager.deactivateAllAttachedControllers();
+ tcpClient.abort();
+ }
+
+ private short recvTCPShort() throws IOException {
+ return tcpClient.recvShort();
+ }
+
+ private byte recvTCPByte() throws IOException {
+ return tcpClient.recvByte();
+ }
+
+ public boolean isConnected() {
+ return (tcpClient != null && tcpClient.isConnected());
+ }
+
+ public boolean connect(String ip) {
+ boolean result = false;
+ log.info("Trying to connect to: " + ip);
+ try {
+ tcpClient.connect(ip);
+ System.out.println("TCP Connected!");
+ udpClient = UDPClient.createUDPClient(ip);
+ if(udpClient != null){
+ result = true;
+ }
+ } catch (Exception e) {
+ System.out.println("Error while connecting: " + e.getMessage());
+ }
+ return result;
+ }
+
+ public void addCommand(DeviceCommand command) {
+ this.ownCommands.add(command);
+ }
+
+ public boolean isReconnecting() {
+ return !tcpClient.isConnected() && tcpClient.isShouldRetry();
+ }
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/network/Protocol.java b/src/net/ash/HIDToVPADNetworkClient/network/Protocol.java
index 00bc96a..7695a66 100644
--- a/src/net/ash/HIDToVPADNetworkClient/network/Protocol.java
+++ b/src/net/ash/HIDToVPADNetworkClient/network/Protocol.java
@@ -21,6 +21,19 @@
*******************************************************************************/
package net.ash.HIDToVPADNetworkClient.network;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import lombok.extern.java.Log;
+import net.ash.HIDToVPADNetworkClient.network.commands.AttachCommand;
+import net.ash.HIDToVPADNetworkClient.network.commands.DetachCommand;
+import net.ash.HIDToVPADNetworkClient.network.commands.PingCommand;
+import net.ash.HIDToVPADNetworkClient.network.commands.ReadCommand;
+
+@Log
public class Protocol {
public static final int TCP_PORT = 8112;
public static final int UDP_PORT = 8113;
@@ -34,4 +47,65 @@ public class Protocol {
public static final byte TCP_CMD_PING = (byte)0xF0;
public static final byte UDP_CMD_DATA = 0x03;
+
+
+ public static final byte TCP_CMD_ATTACH_CONFIG_FOUND = (byte) 0xE0;
+ public static final byte TCP_CMD_ATTACH_CONFIG_NOT_FOUND = (byte) 0xE1;
+ public static final byte TCP_CMD_ATTACH_USERDATA_OKAY = (byte) 0xE8;
+ public static final byte TCP_CMD_ATTACH_USERDATA_BAD = (byte) 0xE9;
+
+ private Protocol(){}
+
+ public enum HandshakeReturnCode {
+ BAD_HANDSHAKE,
+ SAME_CLIENT,
+ NEW_CLIENT
+ }
+
+ public static byte[] getRawAttachDataToSend(AttachCommand command) throws IOException {
+ return ByteBuffer.allocate(9)
+ .put(Protocol.TCP_CMD_ATTACH)
+ .putInt(command.getHandle())
+ .putShort(command.getVid())
+ .putShort(command.getPid())
+ .array();
+ }
+
+ public static byte[] getRawDetachDataToSend(DetachCommand command) throws IOException {
+ return ByteBuffer.allocate(5)
+ .put(Protocol.TCP_CMD_DETACH)
+ .putInt(command.getHandle())
+ .array();
+ }
+
+ public static byte[] getRawPingDataToSend(PingCommand command){
+ return new byte[]{Protocol.TCP_CMD_PING};
+ }
+
+ public static byte[] getRawReadDataToSend(List readCommands) throws IOException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ dos.writeByte(Protocol.UDP_CMD_DATA);
+ dos.writeByte(readCommands.size());
+
+ for(ReadCommand command : readCommands){
+ NetworkHIDDevice sender = command.getSender();
+ byte[] data = command.getData();
+ if(data.length > 0xFF){
+ log.info("Tried to send too much data. Maximum is 0xFF bytes read command.");
+ continue;
+ }
+
+ byte newLength = (byte)(data.length & 0xFF);
+
+ dos.writeInt(command.getHandle());
+ dos.writeShort(sender.getDeviceslot());
+ dos.writeByte(sender.getPadslot());
+
+ dos.write(newLength);
+ dos.write(data, 0, newLength);
+ }
+
+ return bos.toByteArray();
+ }
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java b/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java
index 5ebb771..d17a3f4 100644
--- a/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java
+++ b/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java
@@ -26,79 +26,122 @@ import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
+import java.nio.ByteBuffer;
-import net.ash.HIDToVPADNetworkClient.controller.Controller;
-import net.ash.HIDToVPADNetworkClient.controller.ControllerData;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.java.Log;
+import net.ash.HIDToVPADNetworkClient.manager.ActiveControllerManager;
+import net.ash.HIDToVPADNetworkClient.network.Protocol.HandshakeReturnCode;
+import net.ash.HIDToVPADNetworkClient.util.HandleFoundry;
+import net.ash.HIDToVPADNetworkClient.util.Settings;
+@Log
public class TCPClient {
private Socket sock;
private DataInputStream in;
private DataOutputStream out;
+ @Getter private int clientID = HandleFoundry.next();
+
+ @Getter @Setter(AccessLevel.PRIVATE)
+ private int shouldRetry = Settings.MAXIMUM_TRIES_FOR_RECONNECTING;
+
+ private String ip;
public TCPClient() {
}
public synchronized void connect(String ip) throws Exception {
+
sock = new Socket();
sock.connect(new InetSocketAddress(ip, Protocol.TCP_PORT), 2000);
in = new DataInputStream(sock.getInputStream());
out = new DataOutputStream(sock.getOutputStream());
+
+ HandshakeReturnCode resultHandshake = doHandshake();
+ if(resultHandshake == HandshakeReturnCode.BAD_HANDSHAKE){
+ log.info("[TCP] Handshaking failed");
+ throw new Exception();
+ }else{
+ if(resultHandshake == HandshakeReturnCode.NEW_CLIENT && this.ip != null){
+ //We check the IP to be sure it's the first time we connect to a WiiU. //TODO: Sending a ID from the WiiU which will be compared?
+ //we are new to the client.
+ ActiveControllerManager.getInstance().attachAllActiveControllers();
+ }else if(resultHandshake == HandshakeReturnCode.SAME_CLIENT){
+
+ }
+ this.ip = ip;
+ shouldRetry = 0;
+ }
}
- public enum HandshakeReturnCode {
- BAD_HANDSHAKE,
- SAME_CLIENT,
- NEW_CLIENT
+ private synchronized HandshakeReturnCode doHandshake() throws Exception {
+ if (recvByte() != Protocol.TCP_HANDSHAKE) return HandshakeReturnCode.BAD_HANDSHAKE;
+ send(clientID);
+ log.info("[TCP] Handshaking...");
+ HandshakeReturnCode test = (recvByte() == Protocol.TCP_NEW_CLIENT) ? HandshakeReturnCode.NEW_CLIENT : HandshakeReturnCode.SAME_CLIENT;
+ return test;
}
- public synchronized HandshakeReturnCode doHandshake() throws Exception {
- if (in.readByte() != Protocol.TCP_HANDSHAKE) return HandshakeReturnCode.BAD_HANDSHAKE;
-
- out.writeInt(NetworkManager.clientID);
- out.flush();
- System.out.println("[TCP] Handshaking...");
- return (in.readByte() == Protocol.TCP_NEW_CLIENT) ? HandshakeReturnCode.NEW_CLIENT : HandshakeReturnCode.SAME_CLIENT;
- }
-
- public synchronized void sendAttach(Controller c) throws Exception {
- System.out.println("[TCPClient] Attach " + c); //XXX debug text
- out.writeByte(Protocol.TCP_CMD_ATTACH);
-
-
- out.writeInt(c.getHandle());
- ControllerData d = c.getLatestData(); //GetLatestData allocates a new ControllerData
- out.writeShort(d.getVID());
- out.writeShort(d.getPID());
- out.flush();
-
- short deviceSlot = in.readShort();
- byte padSlot = in.readByte();
- c.setSlotData(deviceSlot, padSlot);
-
- System.out.println("Attached! deviceSlot: " + Integer.toHexString((int)deviceSlot & 0xFFFF) + " padSlot: " + Integer.toHexString((int)padSlot & 0xFF));
- }
-
- public synchronized void sendDetach(Controller c) throws Exception {
- System.out.println("[TCPClient] Detach " + c);
- out.write(Protocol.TCP_CMD_DETACH);
-
- out.writeInt(c.getHandle());
- out.flush();
- }
-
- public synchronized boolean ping() {
- //System.out.println("Ping!");
+ public synchronized boolean abort(){
try {
- out.writeByte(Protocol.TCP_CMD_PING);
- out.flush();
- } catch (IOException e) {
- return false;
- }
- //TODO convince Maschell to make the client actually respond to pings
+ shouldRetry = Settings.MAXIMUM_TRIES_FOR_RECONNECTING;
+ sock.close();
+ clientID = HandleFoundry.next();
+ } catch (IOException e) {
+ System.out.println(e.getMessage()); //TODO: handle
+ return false;
+ }
return true;
}
-
- public synchronized void abort() throws Exception {
- sock.close();
- }
+
+ public synchronized void send(byte[] rawCommand) throws IOException {
+ try{
+ out.write(rawCommand);
+ out.flush();
+ }catch(IOException e){
+ try {
+ if(shouldRetry++ < Settings.MAXIMUM_TRIES_FOR_RECONNECTING){
+ System.out.println("Trying again to connect! Attempt number " + shouldRetry);
+ connect(ip); //TODO: this is for reconnecting when the WiiU switches the application. But this breaks disconnecting, woops.
+ }else{
+ abort();
+ }
+ } catch (Exception e1) {
+ //e1.printStackTrace();
+ }
+ throw e;
+ }
+ }
+
+ public synchronized void send(int value) throws IOException {
+ send(ByteBuffer.allocate(4).putInt(value).array());
+ }
+
+ public synchronized byte recvByte() throws IOException {
+ try{
+ return in.readByte();
+ }catch(IOException e){
+ System.out.println(e.getMessage());
+ throw e;
+ }
+ }
+
+ public synchronized short recvShort() throws IOException {
+ try{
+ return in.readShort();
+ }catch(IOException e){
+ System.out.println(e.getMessage());
+ throw e;
+ }
+ }
+
+ public synchronized boolean isConnected(){
+ return (sock != null && sock.isConnected() && !sock.isClosed());
+ }
+
+ public boolean isShouldRetry() {
+ return this.shouldRetry < Settings.MAXIMUM_TRIES_FOR_RECONNECTING;
+ }
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/network/UDPClient.java b/src/net/ash/HIDToVPADNetworkClient/network/UDPClient.java
index f41ea5f..41951a4 100644
--- a/src/net/ash/HIDToVPADNetworkClient/network/UDPClient.java
+++ b/src/net/ash/HIDToVPADNetworkClient/network/UDPClient.java
@@ -21,61 +21,33 @@
*******************************************************************************/
package net.ash.HIDToVPADNetworkClient.network;
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
+import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
-import java.util.HashMap;
-
-import net.ash.HIDToVPADNetworkClient.controller.Controller;
-import net.ash.HIDToVPADNetworkClient.controller.ControllerData;
+import java.net.SocketException;
+import java.net.UnknownHostException;
public class UDPClient {
- private DatagramSocket sock;
- private InetAddress host;
+ private final DatagramSocket sock;
+ private final InetAddress host;
- private ByteArrayOutputStream outBytes;
- private DataOutputStream out;
-
- public UDPClient() {
- outBytes = new ByteArrayOutputStream();
- out = new DataOutputStream(outBytes);
+ private UDPClient(String ip) throws SocketException, UnknownHostException{
+ sock = new DatagramSocket();
+ host = InetAddress.getByName(ip);
+ }
+ public static UDPClient createUDPClient(String ip){
+ UDPClient result = null;
+ try {
+ result = new UDPClient(ip);
+ } catch (Exception e) {
+ //handle?
+ }
+ return result;
}
- public void connect(String ip) throws Exception {
- sock = new DatagramSocket();
- host = InetAddress.getByName(ip);
- }
-
- public void send(HashMap controllers) throws Exception {
- out.writeByte(Protocol.UDP_CMD_DATA);
-
- out.writeByte((byte)(controllers.size() & 0xFF));
-
- for (Controller c : controllers.values()) {
- out.writeInt(c.getHandle());
- out.writeShort(c.getDeviceSlot());
- out.writeByte(c.getPadSlot());
-
- ControllerData d = c.getLatestData();
- try {
- out.writeInt(d.getData().length);
- out.write(d.getData(), 0, d.getData().length);
- } catch (NullPointerException e) {
- out.writeInt(1);
- out.writeByte(0x00);
- }
- }
-
- out.flush();
- byte[] payload = outBytes.toByteArray();
- DatagramPacket packet = new DatagramPacket(payload, payload.length, host, Protocol.UDP_PORT);
-
+ public void send(byte[] data) throws IOException {
+ DatagramPacket packet = new DatagramPacket(data, data.length, host, Protocol.UDP_PORT);
sock.send(packet);
-
- //System.out.println(Arrays.toString(payload)); //XXX debug text
-
- outBytes.reset();
}
-}
+}
\ No newline at end of file
diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/GuiController.java b/src/net/ash/HIDToVPADNetworkClient/network/commands/AttachCommand.java
similarity index 63%
rename from src/net/ash/HIDToVPADNetworkClient/gui/GuiController.java
rename to src/net/ash/HIDToVPADNetworkClient/network/commands/AttachCommand.java
index 34b8399..b0fe1b5 100644
--- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiController.java
+++ b/src/net/ash/HIDToVPADNetworkClient/network/commands/AttachCommand.java
@@ -19,41 +19,22 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*******************************************************************************/
-package net.ash.HIDToVPADNetworkClient.gui;
+package net.ash.HIDToVPADNetworkClient.network.commands;
+import lombok.Getter;
+import net.ash.HIDToVPADNetworkClient.network.NetworkHIDDevice;
-public class GuiController {
- private boolean activeState = false;
- private GuiControllerType type;
- private Object id;
-
- public GuiController(GuiControllerType type, Object id) {
- this.type = type;
- this.id = id;
- }
-
- public Object getId() {
- return id;
- }
-
- public GuiControllerType getType() {
- return type;
- }
-
- public boolean getActiveState() {
- return activeState;
- }
-
- public void setActiveState(boolean activeState) {
- this.activeState = activeState;
- }
-
- @Override
- public String toString() {
- return "GuiController, active: " + activeState + ", type: " + type.toString() + ", id: " + id.toString();
- }
-
- @Override
- public int hashCode() {
- return id.hashCode() + type.hashCode() /*+ (activeState ? 1 : 0)*/;
- }
+public class AttachCommand extends DeviceCommand {
+ @Getter private final short vid;
+ @Getter private final short pid;
+
+ public AttachCommand(int hidHandle, short vid, short pid, NetworkHIDDevice sender) {
+ super(hidHandle,sender);
+ this.vid = vid;
+ this.pid = pid;
+ }
+
+ @Override
+ public String toString() {
+ return "AttachCommand [vid=" + vid + ", pid=" + pid+ ", handle=" + getHandle() + ", sender=" + getSender() + "]";
+ }
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/network/commands/DetachCommand.java b/src/net/ash/HIDToVPADNetworkClient/network/commands/DetachCommand.java
new file mode 100644
index 0000000..54aa0ca
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/network/commands/DetachCommand.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+package net.ash.HIDToVPADNetworkClient.network.commands;
+
+import net.ash.HIDToVPADNetworkClient.network.NetworkHIDDevice;
+
+public class DetachCommand extends DeviceCommand{
+ public DetachCommand(int hidHandle, NetworkHIDDevice sender){
+ super(hidHandle,sender);
+ }
+
+ @Override
+ public String toString() {
+ return "DetachCommand [handle=" + getHandle() + ", sender=" + getSender() + "]";
+ }
+}
diff --git a/src/net/ash/HIDToVPADNetworkClient/network/commands/DeviceCommand.java b/src/net/ash/HIDToVPADNetworkClient/network/commands/DeviceCommand.java
new file mode 100644
index 0000000..02395c9
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/network/commands/DeviceCommand.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+package net.ash.HIDToVPADNetworkClient.network.commands;
+import lombok.Data;
+import net.ash.HIDToVPADNetworkClient.network.NetworkHIDDevice;
+
+@Data
+public abstract class DeviceCommand {
+ private final int handle;
+ private final NetworkHIDDevice sender;
+ private final Class extends DeviceCommand> type;
+
+ protected DeviceCommand(int hidHandle,NetworkHIDDevice sender){
+ this.handle = hidHandle;
+ this.sender = sender;
+ this.type = this.getClass();
+ }
+}
diff --git a/src/net/ash/HIDToVPADNetworkClient/network/commands/PingCommand.java b/src/net/ash/HIDToVPADNetworkClient/network/commands/PingCommand.java
new file mode 100644
index 0000000..35ff8e5
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/network/commands/PingCommand.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+package net.ash.HIDToVPADNetworkClient.network.commands;
+
+import net.ash.HIDToVPADNetworkClient.network.NetworkHIDDevice;
+
+public class PingCommand extends DeviceCommand {
+ public PingCommand() {
+ this((short) 0,null);
+ }
+ private PingCommand(int hidHandle, NetworkHIDDevice sender) {
+ super(hidHandle, sender);
+ }
+
+ @Override
+ public String toString() {
+ return "PingCommand []";
+ }
+}
\ No newline at end of file
diff --git a/src/net/ash/HIDToVPADNetworkClient/network/commands/ReadCommand.java b/src/net/ash/HIDToVPADNetworkClient/network/commands/ReadCommand.java
new file mode 100644
index 0000000..eb18cd5
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/network/commands/ReadCommand.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+package net.ash.HIDToVPADNetworkClient.network.commands;
+
+import lombok.Getter;
+import net.ash.HIDToVPADNetworkClient.network.NetworkHIDDevice;
+
+public class ReadCommand extends DeviceCommand{
+ @Getter private final byte[] data;
+ public ReadCommand(int hidHandle,byte[] data, NetworkHIDDevice sender) {
+ super(hidHandle, sender);
+ this.data = data;
+ }
+
+ @Override
+ public String toString() {
+ return "ReadCommand [handle=" + getHandle() + ", sender=" + getSender() + "]";
+ }
+}
diff --git a/src/net/ash/HIDToVPADNetworkClient/util/BlockingIOStabbifier.java b/src/net/ash/HIDToVPADNetworkClient/util/BlockingIOStabbifier.java
deleted file mode 100644
index c54227a..0000000
--- a/src/net/ash/HIDToVPADNetworkClient/util/BlockingIOStabbifier.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *******************************************************************************/
-package net.ash.HIDToVPADNetworkClient.util;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-
-public class BlockingIOStabbifier extends InputStream implements Runnable {
- private byte[] buffer;
- private BufferedInputStream file;
- private ByteArrayOutputStream baos;
- private boolean running;
-
- int baosSize = 0;
- int bufferRemaining = 0;
- int bufferOffset = -1;
-
- public void run() {
- while (running) {
- try {
- synchronized (baos) {
- baos.write(file.read());
- baosSize++;
- }
- Thread.sleep(1);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- private void allocate() {
- synchronized (baos) {
- buffer = baos.toByteArray();
- bufferRemaining = baos.size();
- bufferOffset = -1;
- baos.reset();
- baosSize = 0;
- }
- }
-
- @Override
- public int read() throws IOException {
- if (bufferRemaining == 0) {
- allocate();
- if (bufferRemaining == 0) {
- return -1;
- }
- }
- bufferRemaining--;
- bufferOffset++;
- return buffer[bufferOffset] & 0xFF;
- }
-
- @Override
- public boolean markSupported() {
- return false;
- }
-
- @Override
- public void close() throws IOException {
- running = false;
- file.close();
- baos.close();
- super.close();
- }
-
- @Override
- public int available() {
- return bufferRemaining + baosSize;
- }
-
- public BlockingIOStabbifier(String path) throws FileNotFoundException {
- file = new BufferedInputStream(new FileInputStream(path));
-
- baos = new ByteArrayOutputStream();
- buffer = new byte[0];
-
- running = true;
- new Thread(this).start();
- }
-}
diff --git a/src/net/ash/HIDToVPADNetworkClient/util/HandleFoundry.java b/src/net/ash/HIDToVPADNetworkClient/util/HandleFoundry.java
index 6abcc63..a68121c 100644
--- a/src/net/ash/HIDToVPADNetworkClient/util/HandleFoundry.java
+++ b/src/net/ash/HIDToVPADNetworkClient/util/HandleFoundry.java
@@ -25,10 +25,15 @@
package net.ash.HIDToVPADNetworkClient.util;
+import java.util.concurrent.ThreadLocalRandom;
+
public class HandleFoundry {
- private static int h = 1;
+ //We start with a random value, so we have at each startup a different clientID!
+ private static int h = ThreadLocalRandom.current().nextInt(1, 10000);
+
+ private HandleFoundry(){}
public static int next() {
return h++;
}
-}
+}
\ No newline at end of file
diff --git a/src/net/ash/HIDToVPADNetworkClient/util/PureJavaHidApiManager.java b/src/net/ash/HIDToVPADNetworkClient/util/PureJavaHidApiManager.java
new file mode 100644
index 0000000..ab60196
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/util/PureJavaHidApiManager.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+package net.ash.HIDToVPADNetworkClient.util;
+
+import java.io.IOException;
+import java.util.List;
+
+import purejavahidapi.HidDevice;
+import purejavahidapi.HidDeviceInfo;
+import purejavahidapi.PureJavaHidApi;
+
+public class PureJavaHidApiManager {
+
+ private PureJavaHidApiManager(){}
+
+ /**
+ * Searches the corresponding HIDDevice for the given path
+ * @param path Path of the HIDDevice
+ * @return It the device is found, it will be returned. Otherwise null is returned.
+ * @throws IOException
+ */
+ public static HidDevice getDeviceByPath(String path) throws IOException{
+ List devList = PureJavaHidApi.enumerateDevices();
+ for (HidDeviceInfo info : devList) {
+ if(info.getPath().equals(path)){
+ return PureJavaHidApi.openDevice(info);
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/net/ash/HIDToVPADNetworkClient/util/WakeupThread.java b/src/net/ash/HIDToVPADNetworkClient/util/Settings.java
similarity index 71%
rename from src/net/ash/HIDToVPADNetworkClient/util/WakeupThread.java
rename to src/net/ash/HIDToVPADNetworkClient/util/Settings.java
index 0aee430..919fc1c 100644
--- a/src/net/ash/HIDToVPADNetworkClient/util/WakeupThread.java
+++ b/src/net/ash/HIDToVPADNetworkClient/util/Settings.java
@@ -21,32 +21,15 @@
*******************************************************************************/
package net.ash.HIDToVPADNetworkClient.util;
-public class WakeupThread extends Thread {
- private Object wakeup;
- private int timeout;
- private boolean run;
- public WakeupThread(Object wakeup) {
- super();
- this.wakeup = wakeup;
- run = true;
- }
- public void setTimeout(int timeout) {
- this.timeout = timeout;
- }
- public void tryStop() {
- run = false;
- }
- @Override
- public void run() {
- while (run) {
- synchronized (wakeup) {
- wakeup.notify();
- }
- try {
- Thread.sleep(timeout);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
+public class Settings {
+ public static final int DETECT_CONTROLLER_INTERVAL = 1000;
+ public static final int HANDLE_INPUTS_INTERVAL = 15;
+ public static final int MAXIMUM_TRIES_FOR_RECONNECTING = 10;
+ public static final int SLEEP_AFER_POLLING = 10;
+ public static final int SENDING_CMD_SLEEP_IF_NOT_CONNECTED = 500;
+ public static final int PING_INTERVAL = 1000;
+ public static final int PROCESS_CMD_INTERVAL = 10;
+
+ private Settings(){}
+
}
diff --git a/src/net/ash/HIDToVPADNetworkClient/util/Utilities.java b/src/net/ash/HIDToVPADNetworkClient/util/Utilities.java
new file mode 100644
index 0000000..c66b1d3
--- /dev/null
+++ b/src/net/ash/HIDToVPADNetworkClient/util/Utilities.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *******************************************************************************/
+package net.ash.HIDToVPADNetworkClient.util;
+
+public class Utilities{
+
+ private Utilities(){}
+
+ /**
+ * Let me just sleep!
+ * @param ms sleep duration in ms
+ */
+ public static void sleep(int ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ /**
+ * Convert a byte array to a formated String
+ * @param ba byte array
+ * @return String representing the binary data
+ */
+ public static String ByteArrayToString(byte[] ba){
+ if(ba == null) return null;
+ StringBuilder hex = new StringBuilder(ba.length * 2);
+ for(byte b : ba){
+ hex.append(String.format("%02X", b));
+ }
+ return hex.toString();
+ }
+
+ /**
+ * Converts a signed short value to a unsigned byte
+ * @param value short value
+ * @return converted value
+ */
+ public static short signedShortToByte(int value){
+ return (short) (((((short)value) + Short.MAX_VALUE + 1) >> 8) & 0xFF);
+ }
+
+ /**
+ * Converts a signed short value to a unsigned byte
+ * @param value short value
+ * @return converted value
+ */
+ public static short signedShortToByte(short value){
+ return signedShortToByte((int) value);
+ }
+}