From 82a25e0cf4671347b3c78d9b76a3f1821b64794b Mon Sep 17 00:00:00 2001 From: Maschell Date: Sun, 5 Feb 2017 22:04:09 +0100 Subject: [PATCH 1/5] My approach on improving it, currently not really working NOTE: I changed the protoctol a bit. The actual network code is not finished and still under developement. BUT: - I simplified the GUI Classes - Added XInput support - And changed too much thing, can't list all here. - Comments etc missing. Just want to have a commited version until I break stuff. --- pom.xml | 17 + src/net/ash/HIDToVPADNetworkClient/Main.java | 12 +- .../controller/Controller.java | 204 +++++-- .../controller/ControllerManager.java | 126 ----- .../controller/Hid4JavaController.java | 113 ++-- .../controller/LinuxDevInputController.java | 181 ++---- .../LinuxDevInputControllerLegacyDriver.java | 187 ------- ...ollerData.java => XInput13Controller.java} | 30 +- .../controller/XInput14Controller.java | 30 + .../controller/XInputController.java | 128 +++++ ...trollerInitializationFailedException.java} | 11 +- .../gui/ControllerDetector.java | 89 --- .../gui/GuiCloseListener.java | 36 +- .../gui/GuiControllerList.java | 100 ++-- .../gui/GuiControllerListItem.java | 89 ++- .../gui/GuiInputControls.java | 58 +- .../gui/GuiInteractionManager.java | 105 ---- .../HIDToVPADNetworkClient/gui/GuiMain.java | 22 - .../manager/ActiveControllerManager.java | 132 +++++ .../manager/ControllerManager.java | 203 +++++++ .../network/NetworkHIDDevice.java | 99 ++++ .../network/NetworkManager.java | 517 ++++++++++-------- .../network/Protocol.java | 74 +++ .../network/TCPClient.java | 131 +++-- .../network/UDPClient.java | 68 +-- .../commands/AttachCommand.java} | 53 +- .../network/commands/DetachCommand.java | 35 ++ .../network/commands/DeviceCommand.java | 37 ++ .../network/commands/PingCommand.java | 38 ++ .../network/commands/ReadCommand.java | 38 ++ .../util/BlockingIOStabbifier.java | 105 ---- .../util/HID4JavaManager.java | 51 ++ .../util/HandleFoundry.java | 9 +- .../util/{WakeupThread.java => Settings.java} | 34 +- .../util/Utilities.java | 70 +++ 35 files changed, 1789 insertions(+), 1443 deletions(-) delete mode 100644 src/net/ash/HIDToVPADNetworkClient/controller/ControllerManager.java delete mode 100644 src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputControllerLegacyDriver.java rename src/net/ash/HIDToVPADNetworkClient/controller/{ControllerData.java => XInput13Controller.java} (77%) create mode 100644 src/net/ash/HIDToVPADNetworkClient/controller/XInput14Controller.java create mode 100644 src/net/ash/HIDToVPADNetworkClient/controller/XInputController.java rename src/net/ash/HIDToVPADNetworkClient/{gui/GuiControllerType.java => exeption/ControllerInitializationFailedException.java} (86%) delete mode 100644 src/net/ash/HIDToVPADNetworkClient/gui/ControllerDetector.java delete mode 100644 src/net/ash/HIDToVPADNetworkClient/gui/GuiInteractionManager.java create mode 100644 src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java create mode 100644 src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java create mode 100644 src/net/ash/HIDToVPADNetworkClient/network/NetworkHIDDevice.java rename src/net/ash/HIDToVPADNetworkClient/{gui/GuiController.java => network/commands/AttachCommand.java} (63%) create mode 100644 src/net/ash/HIDToVPADNetworkClient/network/commands/DetachCommand.java create mode 100644 src/net/ash/HIDToVPADNetworkClient/network/commands/DeviceCommand.java create mode 100644 src/net/ash/HIDToVPADNetworkClient/network/commands/PingCommand.java create mode 100644 src/net/ash/HIDToVPADNetworkClient/network/commands/ReadCommand.java delete mode 100644 src/net/ash/HIDToVPADNetworkClient/util/BlockingIOStabbifier.java create mode 100644 src/net/ash/HIDToVPADNetworkClient/util/HID4JavaManager.java rename src/net/ash/HIDToVPADNetworkClient/util/{WakeupThread.java => Settings.java} (71%) create mode 100644 src/net/ash/HIDToVPADNetworkClient/util/Utilities.java diff --git a/pom.xml b/pom.xml index e8f7344..b470556 100644 --- a/pom.xml +++ b/pom.xml @@ -16,11 +16,28 @@ + + + jitpack.io + https://jitpack.io + + org.hid4java hid4java 0.4.0 + + org.projectlombok + lombok + 1.16.12 + provided + + + com.github.strikerx3 + jxinput + 1eb4087 + \ No newline at end of file diff --git a/src/net/ash/HIDToVPADNetworkClient/Main.java b/src/net/ash/HIDToVPADNetworkClient/Main.java index bfb8759..d1b68ff 100644 --- a/src/net/ash/HIDToVPADNetworkClient/Main.java +++ b/src/net/ash/HIDToVPADNetworkClient/Main.java @@ -25,9 +25,8 @@ 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 @@ -38,10 +37,9 @@ import net.ash.HIDToVPADNetworkClient.network.NetworkManager; public class Main { 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(); @@ -52,11 +50,9 @@ public class Main { 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); diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java b/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java index b95ab9a..0467f08 100644 --- a/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java +++ b/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java @@ -21,13 +21,76 @@ *******************************************************************************/ 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.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(); + + 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(1000); + while(isActive()) { + byte[] newData = pollLatestData(); + if(newData != null){ + setLatestData(newData); + } + Utilities.sleep(10); + } + synchronized (shutdownLock) { + shutdownState = shutdown; + } + } + synchronized (shutdownLock) { + shutdownDone = true; + } + } + + @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 +99,93 @@ 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; + } + + public enum ControllerType { + HID4JAVA, LINUX, XINPUT13,XINPUT14 + } +} \ 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 index 8b9948c..33ecdb3 100644 --- a/src/net/ash/HIDToVPADNetworkClient/controller/Hid4JavaController.java +++ b/src/net/ash/HIDToVPADNetworkClient/controller/Hid4JavaController.java @@ -21,91 +21,60 @@ *******************************************************************************/ package net.ash.HIDToVPADNetworkClient.controller; +import java.util.Arrays; + import org.hid4java.HidDevice; -import org.hid4java.HidManager; -import net.ash.HIDToVPADNetworkClient.util.HandleFoundry; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException; +import net.ash.HIDToVPADNetworkClient.util.HID4JavaManager; +import net.ash.HIDToVPADNetworkClient.util.Utilities; -public class Hid4JavaController implements Controller { - static final int PACKET_LENGTH = 64; +public class Hid4JavaController extends Controller { + public Hid4JavaController(String identifier) throws ControllerInitializationFailedException { + super(ControllerType.HID4JAVA, identifier); + } + + private static final int PACKET_LENGTH = 128; - 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; + @Getter @Setter(AccessLevel.PRIVATE) + private HidDevice hidDevice; + + @Override + public boolean initController(String identifier) { + HidDevice device = HID4JavaManager.getDeviceByPath(identifier); + //device.setNonBlocking(true); //TODO: What does this do? This is done no in a extra Thread so it shouldn't matter. + device.open(); + Utilities.sleep(20); //What a bit until is opened. + if (device == null | !device.isOpen()) return false; + setHidDevice(device); + //System.out.println("ctrl: " + device.isOpen() + " " + device.getLastErrorMessage()); return true; } @Override - public void setPollingState(boolean poll) { - - } + public byte[] pollLatestData() { + byte[] data = new byte[PACKET_LENGTH]; + int length = getHidDevice().read(data); + if(length <= 0) return null; + //if(length > data.length) System.out.println("WTF?"); + return Arrays.copyOf(data, length); + } @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(); + public void destroyDriver() { + getHidDevice().close(); } - private short getVID() { - return controller.getVendorId(); + @Override + public short getVID() { + return getHidDevice().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; + public short getPID() { + return getHidDevice().getProductId(); } - - @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; - } -} +} \ No newline at end of file diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java b/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java index d0ff86b..6eef7a7 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,20 @@ 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()); - } - } - - @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]"); } + } 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/ControllerData.java b/src/net/ash/HIDToVPADNetworkClient/controller/XInput13Controller.java similarity index 77% rename from src/net/ash/HIDToVPADNetworkClient/controller/ControllerData.java rename to src/net/ash/HIDToVPADNetworkClient/controller/XInput13Controller.java index cb00c16..037f85c 100644 --- a/src/net/ash/HIDToVPADNetworkClient/controller/ControllerData.java +++ b/src/net/ash/HIDToVPADNetworkClient/controller/XInput13Controller.java @@ -21,28 +21,10 @@ *******************************************************************************/ 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); + } } diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/XInput14Controller.java b/src/net/ash/HIDToVPADNetworkClient/controller/XInput14Controller.java new file mode 100644 index 0000000..cab0a9f --- /dev/null +++ b/src/net/ash/HIDToVPADNetworkClient/controller/XInput14Controller.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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); + } +} diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/XInputController.java b/src/net/ash/HIDToVPADNetworkClient/controller/XInputController.java new file mode 100644 index 0000000..2558598 --- /dev/null +++ b/src/net/ash/HIDToVPADNetworkClient/controller/XInputController.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * 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) { + //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; + } +} 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..1fe0a43 100644 --- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerListItem.java +++ b/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerListItem.java @@ -23,52 +23,91 @@ 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(); + switch (controller.getType()) { //TODO: String.format with value from Settings. + case XINPUT13: + return "XInput 1.3 on " + controller.getIdentifier(); + case XINPUT14: + return "XInput 1.4 on " + controller.getIdentifier(); + case HID4JAVA: + return "USB HID on " + controller.getIdentifier(); + case LINUX: + return "Linux controller on " + controller.getIdentifier(); + default: + return controller.toString(); } } - - public void addActionListener(ActionListener l) { - checkbox.addActionListener(l); - } - - public GuiController getData() { - return data; - } - - @Override - public int hashCode() { - return data.hashCode(); - } + + @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..2f54231 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,24 @@ 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 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 +61,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 +88,19 @@ 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().isConnected()){ + connectButton.setText(DISCONNECT); + }else{ + connectButton.setText(CONNECT); + } + } + }; + new Timer(delay, taskPerformer).start(); } public static GuiInputControls instance() { @@ -112,4 +122,18 @@ 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().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..217f549 --- /dev/null +++ b/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * 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 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; + +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)){ + System.out.println("Added " + c); //TODO: real logging + toAdd.add(c); + } + } + + //removing all old + for(Controller c : activeControllers.keySet()){ + if(!currentControllers.contains(c)){ + System.out.println("Removed " + c); //TODO: real logging + 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); + } + } + } + } +} diff --git a/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java b/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java new file mode 100644 index 0000000..8f5606b --- /dev/null +++ b/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java @@ -0,0 +1,203 @@ +/******************************************************************************* + * 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.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.hid4java.HidDevice; +import org.hid4java.HidManager; + +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.Hid4JavaController; +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; + +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()); + } + + //TODO: Enable HID4Java again + //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 HID4JAVA: + try { + c= new Hid4JavaController(deviceIdentifier); + } catch (ControllerInitializationFailedException e) { + //e.printStackTrace(); + } + break; + case LINUX: + try { + c = new LinuxDevInputController(deviceIdentifier); + } catch (ControllerInitializationFailedException e) { + //e.printStackTrace(); + } + break; + case XINPUT14: + try { + c = new XInput14Controller(deviceIdentifier); + } catch (ControllerInitializationFailedException e) { + //e.printStackTrace(); + } + break; + case XINPUT13: + try { + c = new XInput13Controller(deviceIdentifier); + } 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 (HidDevice device : HidManager.getHidServices().getAttachedHidDevices()) { + if(device.getUsage() == 0x05 || device.getUsage() == 0x04){ + connectedDevices.put(device.getPath(),ControllerType.HID4JAVA); + } + } + 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..0236ac8 --- /dev/null +++ b/src/net/ash/HIDToVPADNetworkClient/network/NetworkHIDDevice.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * 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 getCommandList() { + List commands = new ArrayList<>(); + commands.addAll(getCommands()); + DeviceCommand lastRead; + + synchronized (readCommandLock) { + if((lastRead = getLatestRead()) != null){ + commands.add(lastRead); + setLatestRead(null); + } + } + + clearCommands(); + return commands; + } +} diff --git a/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java b/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java index f4e1b3c..0091574 100644 --- a/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java @@ -19,240 +19,293 @@ * 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 org.omg.Messaging.SyncScopeHelper; -import net.ash.HIDToVPADNetworkClient.controller.Controller; -import net.ash.HIDToVPADNetworkClient.gui.GuiInteractionManager; +import lombok.Getter; +import lombok.Synchronized; +import lombok.extern.java.Log; +import net.ash.HIDToVPADNetworkClient.manager.ControllerManager; +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.Utilities; -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; - } +@Log +public class NetworkManager implements Runnable{ + private final TCPClient tcpClient = new TCPClient(); + private UDPClient udpClient = null; + + private static NetworkManager instance = null; + 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); + } + + @Override + public void run() { + int i = 0; + while(true){ + proccessCommands(); + Utilities.sleep(10); + if(i++ > 1000/10){ + ping(); + i = 0; + } + } + } + + private void ping() { + if(isConnected())sendingCommand(new PingCommand()); + } + + public void proccessCommands(){ + List commands = new ArrayList<>(); + 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) { + if(command instanceof AttachCommand){ + sendAttach((AttachCommand) command); + }else if(command instanceof DetachCommand){ + sendDetach((DetachCommand) command); + }else if(command instanceof PingCommand){ + sendPing((PingCommand) command); + } + } + + private void sendPing(PingCommand command) { + if(sendTCP(Protocol.getRawPingDataToSend(command))){ + log.info("PING"); + }else{ + log.info("Sending the PING failed"); + } + } + + private void sendDetach(DetachCommand command) { + byte[] sendData; + try { + sendData = Protocol.getRawDetachDataToSend(command); + if(!sendTCP(sendData)){ + log.info("Sending the detach command for device (" + command.getSender() + ") failed. sendTCP failed"); + } + } catch (IOException e) { + e.printStackTrace(); + } + + } + + private void 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; + } + 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; + }else if (configFound == 0){ + log.info("Failed to get byte."); + disconnect(); + } + + 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; + }else if (userDataOkay == 0){ + log.info("Failed to get byte."); + disconnect(); + } + + //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()); + } + + if(deviceslot < 0 || padslot < 0){ + log.info("Recieving data after sending a attach failed for device (" + command.getSender() + ") failed. We need to disconnect =("); + disconnect(); + } + + //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()); + } + log.info("Attaching done!"); + }else{ + log.info("Sending the attach command for device (" + command.getSender() + ") failed. sendTCP failed"); + } + } + + 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; + } } 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..39c1026 100644 --- a/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java +++ b/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java @@ -26,79 +26,96 @@ import java.io.DataOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; +import java.nio.ByteBuffer; +import java.util.concurrent.SynchronousQueue; -import net.ash.HIDToVPADNetworkClient.controller.Controller; -import net.ash.HIDToVPADNetworkClient.controller.ControllerData; +import lombok.Getter; +import net.ash.HIDToVPADNetworkClient.network.Protocol.HandshakeReturnCode; +import net.ash.HIDToVPADNetworkClient.util.HandleFoundry; public class TCPClient { private Socket sock; private DataInputStream in; private DataOutputStream out; + @Getter private int clientID = HandleFoundry.next(); + + private String ip; public TCPClient() { } public synchronized void connect(String ip) throws Exception { + this.ip = ip; sock = new Socket(); - sock.connect(new InetSocketAddress(ip, Protocol.TCP_PORT), 2000); + sock.connect(new InetSocketAddress(ip, Protocol.TCP_PORT), 30000); in = new DataInputStream(sock.getInputStream()); out = new DataOutputStream(sock.getOutputStream()); - } - - public enum HandshakeReturnCode { - BAD_HANDSHAKE, - SAME_CLIENT, - NEW_CLIENT - } - - 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!"); - try { - out.writeByte(Protocol.TCP_CMD_PING); - out.flush(); - } catch (IOException e) { - return false; + if(doHandshake() == HandshakeReturnCode.BAD_HANDSHAKE){ + throw new Exception(); } - //TODO convince Maschell to make the client actually respond to pings + } + + private synchronized HandshakeReturnCode doHandshake() throws Exception { + if (recvByte() != Protocol.TCP_HANDSHAKE) return HandshakeReturnCode.BAD_HANDSHAKE; + + send(clientID); + System.out.println("clientID:" + clientID); + + System.out.println("[TCP] Handshaking..."); + HandshakeReturnCode test = (recvByte() == Protocol.TCP_NEW_CLIENT) ? HandshakeReturnCode.NEW_CLIENT : HandshakeReturnCode.SAME_CLIENT; + System.out.println(test); + return test; + } + + public synchronized boolean abort(){ + try { + sock.close(); + clientID = HandleFoundry.next(); + } catch (IOException e) { + System.out.println(e.getMessage()); + 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 { + connect(ip); //TODO: this is for reconnecting when the WiiU switches the application. But this breaks disconnecting, woops. + } 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()); + } } 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 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/HID4JavaManager.java b/src/net/ash/HIDToVPADNetworkClient/util/HID4JavaManager.java new file mode 100644 index 0000000..75cff97 --- /dev/null +++ b/src/net/ash/HIDToVPADNetworkClient/util/HID4JavaManager.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.util; + +import org.hid4java.HidDevice; +import org.hid4java.HidManager; +import org.hid4java.HidServices; + +public class HID4JavaManager { + + private HID4JavaManager(){} + + /** + * 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. + */ + public static HidDevice getDeviceByPath(String path){ + HidDevice result = null; + + HidServices services = HidManager.getHidServices(); + if(services == null) return result; + + for (HidDevice device : services.getAttachedHidDevices()) { + if (device.getPath().equals(path)) { + result = device; + break; + } + } + return result; + } +} \ No newline at end of file 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/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..569aa02 100644 --- a/src/net/ash/HIDToVPADNetworkClient/util/WakeupThread.java +++ b/src/net/ash/HIDToVPADNetworkClient/util/Settings.java @@ -21,32 +21,10 @@ *******************************************************************************/ 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; + + 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); + } +} From 3918a71627fd36062e8f63c27b89aef01043653e Mon Sep 17 00:00:00 2001 From: Maschell Date: Mon, 6 Feb 2017 09:17:22 +0100 Subject: [PATCH 2/5] Fixed possible problem with the LinuxDevInputController --- .../ash/HIDToVPADNetworkClient/controller/Controller.java | 6 +++++- .../controller/LinuxDevInputController.java | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java b/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java index 0467f08..44b0df7 100644 --- a/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java +++ b/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java @@ -62,7 +62,7 @@ public abstract class Controller implements Runnable{ if(newData != null){ setLatestData(newData); } - Utilities.sleep(10); + doSleepAfterPollingData(); } synchronized (shutdownLock) { shutdownState = shutdown; @@ -73,6 +73,10 @@ public abstract class Controller implements Runnable{ } } + protected void doSleepAfterPollingData() { + Utilities.sleep(10); + } + @Synchronized("dataLock") private void setLatestData(byte[] newData) { this.latestData = newData; diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java b/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java index 6eef7a7..82bde01 100644 --- a/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java +++ b/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java @@ -125,6 +125,11 @@ public class LinuxDevInputController extends Controller implements Runnable{ } return newData; } + + @Override + 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 void destroyDriver() { From a479bb14eeb4d6d9c8800179f88166dc1b8d9f58 Mon Sep 17 00:00:00 2001 From: Maschell Date: Mon, 6 Feb 2017 20:40:48 +0100 Subject: [PATCH 3/5] Improved the reconnecting and fixes some issues about the connection. This should be pretty stable now =) Code is not really beautiful atm, but better commit it before I break it again! --- .../gui/GuiInputControls.java | 20 +++-- .../manager/ActiveControllerManager.java | 9 ++ .../manager/ControllerManager.java | 12 ++- .../network/NetworkManager.java | 82 ++++++++++++++----- .../network/TCPClient.java | 57 +++++++++---- .../HIDToVPADNetworkClient/util/Settings.java | 1 + 6 files changed, 138 insertions(+), 43 deletions(-) diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java b/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java index 2f54231..47a0da1 100644 --- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java +++ b/src/net/ash/HIDToVPADNetworkClient/gui/GuiInputControls.java @@ -44,6 +44,7 @@ public class GuiInputControls extends JPanel implements ActionListener { private static final String CONNECT = "Connect"; private static final String DISCONNECT = "Disconnect"; + private static final String RECONNECTING = "Reconnecting"; private JButton connectButton; private JTextField ipTextBox; @@ -93,10 +94,15 @@ public class GuiInputControls extends JPanel implements ActionListener { int delay = 100; //milliseconds ActionListener taskPerformer = new ActionListener() { public void actionPerformed(ActionEvent evt) { - if(NetworkManager.getInstance().isConnected()){ + 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); } } }; @@ -127,10 +133,14 @@ public class GuiInputControls extends JPanel implements ActionListener { public void actionPerformed(ActionEvent e) { SwingUtilities.invokeLater(new Runnable() { public void run() { - if(NetworkManager.getInstance().isConnected()){ - NetworkManager.getInstance().disconnect(); - }else{ - NetworkManager.getInstance().connect(ipTextBox.getText()); + 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/manager/ActiveControllerManager.java b/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java index 217f549..bea48ed 100644 --- a/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java @@ -129,4 +129,13 @@ public class ActiveControllerManager implements Runnable{ } } } + + public void attachAllActiveControllers() { + synchronized (activeControllers) { + for(Entry entry : activeControllers.entrySet()){ + NetworkHIDDevice device = entry.getValue(); + device.sendAttach(); + } + } + } } diff --git a/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java b/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java index 8f5606b..832cc51 100644 --- a/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java @@ -65,7 +65,10 @@ public class ControllerManager{ connectedDevices.putAll(detectWindowsControllers()); } - //TODO: Enable HID4Java again + /*TODO: Enable HID4Java again. Currently it's disabled because + * We can either use the WiiU directly OR have XInput anyway. + * Adding an option menu for enabling it? + */ //connectedDevices.putAll(detectHIDDevices()); //Remove detached devices @@ -100,9 +103,15 @@ public class ControllerManager{ //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(); } @@ -110,6 +119,7 @@ public class ControllerManager{ case XINPUT13: try { c = new XInput13Controller(deviceIdentifier); + c.setActive(true); } catch (ControllerInitializationFailedException e) { //e.printStackTrace(); } diff --git a/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java b/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java index 0091574..5152b9a 100644 --- a/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java @@ -44,6 +44,9 @@ public class NetworkManager implements Runnable{ private UDPClient udpClient = null; private static NetworkManager instance = null; + + private List ownCommands = new ArrayList<>(); + private NetworkManager() { } @@ -80,8 +83,8 @@ public class NetworkManager implements Runnable{ int i = 0; while(true){ proccessCommands(); - Utilities.sleep(10); - if(i++ > 1000/10){ + Utilities.sleep(10);//TODO: move magic value to Settings + if(i++ > 1000/10){//TODO: move magic value to Settings ping(); i = 0; } @@ -89,11 +92,13 @@ public class NetworkManager implements Runnable{ } private void ping() { - if(isConnected())sendingCommand(new PingCommand()); + 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()){ @@ -135,15 +140,30 @@ public class NetworkManager implements Runnable{ } private void sendingCommand(DeviceCommand command) { - if(command instanceof AttachCommand){ - sendAttach((AttachCommand) command); - }else if(command instanceof DetachCommand){ - sendDetach((DetachCommand) command); - }else if(command instanceof PingCommand){ - sendPing((PingCommand) 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(500); //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"); @@ -152,27 +172,32 @@ public class NetworkManager implements Runnable{ } } - private void sendDetach(DetachCommand command) { + private boolean sendDetach(DetachCommand command) { byte[] sendData; try { sendData = Protocol.getRawDetachDataToSend(command); - if(!sendTCP(sendData)){ + 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; } - private void sendAttach(AttachCommand command) { + //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; + return false; } if(sendTCP(sendData)){ byte configFound = 0; @@ -182,13 +207,14 @@ public class NetworkManager implements Runnable{ e1.printStackTrace(); } if(configFound == Protocol.TCP_CMD_ATTACH_CONFIG_FOUND){ - log.info("Config on the console found!"); + //log.info("Config on the console found!"); }else if(configFound == Protocol.TCP_CMD_ATTACH_CONFIG_NOT_FOUND){ log.info("NO CONFIG FOUND."); - return; + return false; }else if (configFound == 0){ log.info("Failed to get byte."); disconnect(); + return false; } byte userDataOkay = 0; @@ -198,13 +224,14 @@ public class NetworkManager implements Runnable{ e1.printStackTrace(); } if(userDataOkay == Protocol.TCP_CMD_ATTACH_USERDATA_OKAY){ - log.info("userdata okay!"); + //log.info("userdata okay!"); }else if(userDataOkay == Protocol.TCP_CMD_ATTACH_USERDATA_BAD){ log.info("USERDATA BAD."); - return; + return false; }else if (userDataOkay == 0){ log.info("Failed to get byte."); disconnect(); + return false; } //We receive our device slot and pad slot @@ -215,11 +242,13 @@ public class NetworkManager implements Runnable{ 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. @@ -229,10 +258,13 @@ public class NetworkManager implements Runnable{ 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; } } @@ -268,7 +300,6 @@ public class NetworkManager implements Runnable{ tcpClient.send(rawCommand); result = true; } catch (Exception e) { - // result = false; } } @@ -276,7 +307,7 @@ public class NetworkManager implements Runnable{ } public void disconnect() { - ControllerManager.deactivateAllAttachedControllers(); + //ControllerManager.deactivateAllAttachedControllers(); tcpClient.abort(); } @@ -287,7 +318,6 @@ public class NetworkManager implements Runnable{ private byte recvTCPByte() throws IOException { return tcpClient.recvByte(); } - public boolean isConnected() { return (tcpClient != null && tcpClient.isConnected()); @@ -308,4 +338,12 @@ public class NetworkManager implements Runnable{ } 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/TCPClient.java b/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java index 39c1026..1e70239 100644 --- a/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java +++ b/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java @@ -29,51 +29,69 @@ import java.net.Socket; import java.nio.ByteBuffer; import java.util.concurrent.SynchronousQueue; +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 { - this.ip = ip; + sock = new Socket(); - sock.connect(new InetSocketAddress(ip, Protocol.TCP_PORT), 30000); + sock.connect(new InetSocketAddress(ip, Protocol.TCP_PORT), 2000); in = new DataInputStream(sock.getInputStream()); out = new DataOutputStream(sock.getOutputStream()); - if(doHandshake() == HandshakeReturnCode.BAD_HANDSHAKE){ - throw new Exception(); - } + 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; + } } private synchronized HandshakeReturnCode doHandshake() throws Exception { - if (recvByte() != Protocol.TCP_HANDSHAKE) return HandshakeReturnCode.BAD_HANDSHAKE; - + if (recvByte() != Protocol.TCP_HANDSHAKE) return HandshakeReturnCode.BAD_HANDSHAKE; send(clientID); - System.out.println("clientID:" + clientID); - - System.out.println("[TCP] Handshaking..."); + log.info("[TCP] Handshaking..."); HandshakeReturnCode test = (recvByte() == Protocol.TCP_NEW_CLIENT) ? HandshakeReturnCode.NEW_CLIENT : HandshakeReturnCode.SAME_CLIENT; - System.out.println(test); return test; } public synchronized boolean abort(){ try { + shouldRetry = Settings.MAXIMUM_TRIES_FOR_RECONNECTING; sock.close(); clientID = HandleFoundry.next(); } catch (IOException e) { - System.out.println(e.getMessage()); + System.out.println(e.getMessage()); //TODO: handle return false; } return true; @@ -85,10 +103,15 @@ public class TCPClient { out.flush(); }catch(IOException e){ try { - connect(ip); //TODO: this is for reconnecting when the WiiU switches the application. But this breaks disconnecting, woops. + 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(); - } + //e1.printStackTrace(); + } throw e; } } @@ -118,4 +141,8 @@ public class TCPClient { 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/util/Settings.java b/src/net/ash/HIDToVPADNetworkClient/util/Settings.java index 569aa02..41ce03c 100644 --- a/src/net/ash/HIDToVPADNetworkClient/util/Settings.java +++ b/src/net/ash/HIDToVPADNetworkClient/util/Settings.java @@ -24,6 +24,7 @@ package net.ash.HIDToVPADNetworkClient.util; 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; private Settings(){} From e0a392442deff683ffe4a4fa226647669866a7e3 Mon Sep 17 00:00:00 2001 From: Maschell Date: Thu, 16 Mar 2017 19:33:50 +0100 Subject: [PATCH 4/5] Changed HID backend to purejavahid moved some magic values to the settings class --- .gitmodules | 3 + pom.xml | 12 +- src/net/ash/HIDToVPADNetworkClient/Main.java | 12 +- .../controller/Controller.java | 29 ++++- .../controller/Hid4JavaController.java | 80 ------------ .../controller/LinuxDevInputController.java | 5 + .../controller/PureJavaHidController.java | 122 ++++++++++++++++++ .../controller/SwitchProController.java | 30 +++++ .../controller/XInput13Controller.java | 5 + .../controller/XInput14Controller.java | 5 + .../controller/XInputController.java | 11 +- .../gui/GuiControllerListItem.java | 13 +- .../manager/ActiveControllerManager.java | 23 +++- .../manager/ControllerManager.java | 30 ++--- .../network/NetworkHIDDevice.java | 22 ++++ .../network/NetworkManager.java | 10 +- .../network/TCPClient.java | 1 - ...anager.java => PureJavaHidApiManager.java} | 31 +++-- .../HIDToVPADNetworkClient/util/Settings.java | 4 + 19 files changed, 294 insertions(+), 154 deletions(-) create mode 100644 .gitmodules delete mode 100644 src/net/ash/HIDToVPADNetworkClient/controller/Hid4JavaController.java create mode 100644 src/net/ash/HIDToVPADNetworkClient/controller/PureJavaHidController.java create mode 100644 src/net/ash/HIDToVPADNetworkClient/controller/SwitchProController.java rename src/net/ash/HIDToVPADNetworkClient/util/{HID4JavaManager.java => PureJavaHidApiManager.java} (73%) 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 b470556..7fff789 100644 --- a/pom.xml +++ b/pom.xml @@ -21,13 +21,9 @@ jitpack.io https://jitpack.io + - - org.hid4java - hid4java - 0.4.0 - org.projectlombok lombok @@ -39,5 +35,11 @@ jxinput 1eb4087 + + + purejavahidapi + purejavahidapi + 0.0.1 + \ No newline at end of file diff --git a/src/net/ash/HIDToVPADNetworkClient/Main.java b/src/net/ash/HIDToVPADNetworkClient/Main.java index d1b68ff..bdffeea 100644 --- a/src/net/ash/HIDToVPADNetworkClient/Main.java +++ b/src/net/ash/HIDToVPADNetworkClient/Main.java @@ -23,19 +23,17 @@ package net.ash.HIDToVPADNetworkClient; import javax.swing.SwingUtilities; -import org.hid4java.HidManager; - 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 Thread(ActiveControllerManager.getInstance()).start(); @@ -43,15 +41,14 @@ public class Main { } catch (Exception e) { e.printStackTrace(); fatal(); - } - + } SwingUtilities.invokeLater(new Runnable() { public void run() { GuiMain.createGUI(); } }); } - + public static void fatal() { System.err.println("HID To VPAD Network Client encountered an irrecoverable error."); System.err.println("Exiting..."); @@ -59,7 +56,6 @@ public class Main { } 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 44b0df7..3f007c4 100644 --- a/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java +++ b/src/net/ash/HIDToVPADNetworkClient/controller/Controller.java @@ -25,6 +25,7 @@ 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; /** @@ -43,6 +44,9 @@ public abstract class Controller implements Runnable{ 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; @@ -56,7 +60,7 @@ public abstract class Controller implements Runnable{ public void run() { boolean shutdownState = shutdown; while(!shutdownState){ - Utilities.sleep(1000); + Utilities.sleep(Settings.DETECT_CONTROLLER_INTERVAL); while(isActive()) { byte[] newData = pollLatestData(); if(newData != null){ @@ -74,7 +78,7 @@ public abstract class Controller implements Runnable{ } protected void doSleepAfterPollingData() { - Utilities.sleep(10); + Utilities.sleep(Settings.SLEEP_AFER_POLLING); } @Synchronized("dataLock") @@ -189,7 +193,24 @@ public abstract class Controller implements Runnable{ return true; } - public enum ControllerType { - HID4JAVA, LINUX, XINPUT13,XINPUT14 + @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/Hid4JavaController.java b/src/net/ash/HIDToVPADNetworkClient/controller/Hid4JavaController.java deleted file mode 100644 index 33ecdb3..0000000 --- a/src/net/ash/HIDToVPADNetworkClient/controller/Hid4JavaController.java +++ /dev/null @@ -1,80 +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.Arrays; - -import org.hid4java.HidDevice; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; -import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException; -import net.ash.HIDToVPADNetworkClient.util.HID4JavaManager; -import net.ash.HIDToVPADNetworkClient.util.Utilities; - -public class Hid4JavaController extends Controller { - public Hid4JavaController(String identifier) throws ControllerInitializationFailedException { - super(ControllerType.HID4JAVA, identifier); - } - - private static final int PACKET_LENGTH = 128; - - @Getter @Setter(AccessLevel.PRIVATE) - private HidDevice hidDevice; - - @Override - public boolean initController(String identifier) { - HidDevice device = HID4JavaManager.getDeviceByPath(identifier); - //device.setNonBlocking(true); //TODO: What does this do? This is done no in a extra Thread so it shouldn't matter. - device.open(); - Utilities.sleep(20); //What a bit until is opened. - if (device == null | !device.isOpen()) return false; - setHidDevice(device); - //System.out.println("ctrl: " + device.isOpen() + " " + device.getLastErrorMessage()); - return true; - } - - @Override - public byte[] pollLatestData() { - byte[] data = new byte[PACKET_LENGTH]; - int length = getHidDevice().read(data); - if(length <= 0) return null; - //if(length > data.length) System.out.println("WTF?"); - return Arrays.copyOf(data, length); - } - - @Override - public void destroyDriver() { - getHidDevice().close(); - } - - @Override - public short getVID() { - return getHidDevice().getVendorId(); - } - - @Override - public short getPID() { - return getHidDevice().getProductId(); - } -} \ No newline at end of file diff --git a/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java b/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java index 82bde01..3092337 100644 --- a/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java +++ b/src/net/ash/HIDToVPADNetworkClient/controller/LinuxDevInputController.java @@ -144,4 +144,9 @@ public class LinuxDevInputController extends Controller implements Runnable{ 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/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..4281b5c --- /dev/null +++ b/src/net/ash/HIDToVPADNetworkClient/controller/SwitchProController.java @@ -0,0 +1,30 @@ +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/XInput13Controller.java b/src/net/ash/HIDToVPADNetworkClient/controller/XInput13Controller.java index 037f85c..454740e 100644 --- a/src/net/ash/HIDToVPADNetworkClient/controller/XInput13Controller.java +++ b/src/net/ash/HIDToVPADNetworkClient/controller/XInput13Controller.java @@ -27,4 +27,9 @@ 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 index cab0a9f..1fe49a8 100644 --- a/src/net/ash/HIDToVPADNetworkClient/controller/XInput14Controller.java +++ b/src/net/ash/HIDToVPADNetworkClient/controller/XInput14Controller.java @@ -27,4 +27,9 @@ 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 index 2558598..8b090fd 100644 --- a/src/net/ash/HIDToVPADNetworkClient/controller/XInputController.java +++ b/src/net/ash/HIDToVPADNetworkClient/controller/XInputController.java @@ -52,7 +52,7 @@ public class XInputController extends Controller { try { device = XInputDevice.getDeviceFor(pad); } catch (XInputNotLoadedException e) { - //Log? + //TODO: Log? } if(device == null) return false; setDevice(device); @@ -103,8 +103,8 @@ public class XInputController extends Controller { axesDataShoulderButtons |= axes.rtRaw << 0; buttonState |= axesDataShoulderButtons << 16; - data.putInt(axesData).putInt(buttonState); - + data.putInt(axesData).putInt(buttonState); + return(data.array()); } return null; @@ -125,4 +125,9 @@ public class XInputController extends Controller { public short getPID() { return 0x1337; } + + @Override + public String getInfoText(){ + return "XInput on " + getIdentifier(); + } } diff --git a/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerListItem.java b/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerListItem.java index 1fe0a43..7b254d0 100644 --- a/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerListItem.java +++ b/src/net/ash/HIDToVPADNetworkClient/gui/GuiControllerListItem.java @@ -65,18 +65,7 @@ public class GuiControllerListItem extends JPanel implements ActionListener { } private String getFlavorText() { - switch (controller.getType()) { //TODO: String.format with value from Settings. - case XINPUT13: - return "XInput 1.3 on " + controller.getIdentifier(); - case XINPUT14: - return "XInput 1.4 on " + controller.getIdentifier(); - case HID4JAVA: - return "USB HID on " + controller.getIdentifier(); - case LINUX: - return "Linux controller on " + controller.getIdentifier(); - default: - return controller.toString(); - } + return controller.getInfoText(); } @Override diff --git a/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java b/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java index bea48ed..65d3118 100644 --- a/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/manager/ActiveControllerManager.java @@ -27,12 +27,14 @@ 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(); @@ -66,8 +68,7 @@ public class ActiveControllerManager implements Runnable{ } }).start(); } - - + private Map activeControllers = new HashMap<>(); public void updateControllerStates() { List currentControllers = ControllerManager.getActiveControllers(); @@ -79,7 +80,7 @@ public class ActiveControllerManager implements Runnable{ for(Controller c: currentControllers){ if(!activeControllers.containsKey(c)){ - System.out.println("Added " + c); //TODO: real logging + log.info("Added " + c); toAdd.add(c); } } @@ -87,7 +88,7 @@ public class ActiveControllerManager implements Runnable{ //removing all old for(Controller c : activeControllers.keySet()){ if(!currentControllers.contains(c)){ - System.out.println("Removed " + c); //TODO: real logging + log.info("Removed " + c); toRemove.add(c); } } @@ -138,4 +139,18 @@ public class ActiveControllerManager implements Runnable{ } } } + + /** + * + * @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 index 832cc51..931b529 100644 --- a/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/manager/ControllerManager.java @@ -23,15 +23,13 @@ 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 org.hid4java.HidDevice; -import org.hid4java.HidManager; - import com.ivan.xinput.XInputDevice; import com.ivan.xinput.XInputDevice14; import com.ivan.xinput.exceptions.XInputNotLoadedException; @@ -39,12 +37,14 @@ 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.Hid4JavaController; +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<>(); @@ -64,12 +64,8 @@ public class ControllerManager{ } else if (os.contains("Windows")) { connectedDevices.putAll(detectWindowsControllers()); } - - /*TODO: Enable HID4Java again. Currently it's disabled because - * We can either use the WiiU directly OR have XInput anyway. - * Adding an option menu for enabling it? - */ - //connectedDevices.putAll(detectHIDDevices()); + + connectedDevices.putAll(detectHIDDevices()); //Remove detached devices List toRemove = new ArrayList<>(); @@ -89,11 +85,13 @@ public class ControllerManager{ if(!attachedControllers.containsKey(deviceIdentifier)){ Controller c = null; switch(entry.getValue()){ - case HID4JAVA: + case PureJAVAHid: try { - c= new Hid4JavaController(deviceIdentifier); + c= PureJavaHidController.getInstance(deviceIdentifier); } catch (ControllerInitializationFailedException e) { //e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); } break; case LINUX: @@ -142,11 +140,13 @@ public class ControllerManager{ private static Map detectHIDDevices() { Map connectedDevices = new HashMap<>(); - for (HidDevice device : HidManager.getHidServices().getAttachedHidDevices()) { - if(device.getUsage() == 0x05 || device.getUsage() == 0x04){ - connectedDevices.put(device.getPath(),ControllerType.HID4JAVA); + + for (HidDeviceInfo info : PureJavaHidApi.enumerateDevices()) { + if(info.getUsagePage() == 0x05 || info.getUsagePage() == 0x04 || (info.getVendorId() == 0x57e)){ + connectedDevices.put(info.getPath(),ControllerType.PureJAVAHid); } } + return connectedDevices; } diff --git a/src/net/ash/HIDToVPADNetworkClient/network/NetworkHIDDevice.java b/src/net/ash/HIDToVPADNetworkClient/network/NetworkHIDDevice.java index 0236ac8..c9a3af4 100644 --- a/src/net/ash/HIDToVPADNetworkClient/network/NetworkHIDDevice.java +++ b/src/net/ash/HIDToVPADNetworkClient/network/NetworkHIDDevice.java @@ -96,4 +96,26 @@ public class NetworkHIDDevice { 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 5152b9a..46c6ea6 100644 --- a/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/network/NetworkManager.java @@ -25,17 +25,15 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.omg.Messaging.SyncScopeHelper; - import lombok.Getter; import lombok.Synchronized; import lombok.extern.java.Log; -import net.ash.HIDToVPADNetworkClient.manager.ControllerManager; 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; @Log @@ -83,8 +81,8 @@ public class NetworkManager implements Runnable{ int i = 0; while(true){ proccessCommands(); - Utilities.sleep(10);//TODO: move magic value to Settings - if(i++ > 1000/10){//TODO: move magic value to Settings + Utilities.sleep(Settings.PROCESS_CMD_INTERVAL); + if(i++ > Settings.PING_INTERVAL/Settings.PROCESS_CMD_INTERVAL){ ping(); i = 0; } @@ -154,7 +152,7 @@ public class NetworkManager implements Runnable{ result = true; } }else{ - Utilities.sleep(500); //TODO: move magic value to Settings + Utilities.sleep(Settings.SENDING_CMD_SLEEP_IF_NOT_CONNECTED); //TODO: move magic value to Settings } //Add the command again on errors diff --git a/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java b/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java index 1e70239..d17a3f4 100644 --- a/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java +++ b/src/net/ash/HIDToVPADNetworkClient/network/TCPClient.java @@ -27,7 +27,6 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; -import java.util.concurrent.SynchronousQueue; import lombok.AccessLevel; import lombok.Getter; diff --git a/src/net/ash/HIDToVPADNetworkClient/util/HID4JavaManager.java b/src/net/ash/HIDToVPADNetworkClient/util/PureJavaHidApiManager.java similarity index 73% rename from src/net/ash/HIDToVPADNetworkClient/util/HID4JavaManager.java rename to src/net/ash/HIDToVPADNetworkClient/util/PureJavaHidApiManager.java index 75cff97..ab60196 100644 --- a/src/net/ash/HIDToVPADNetworkClient/util/HID4JavaManager.java +++ b/src/net/ash/HIDToVPADNetworkClient/util/PureJavaHidApiManager.java @@ -21,31 +21,30 @@ *******************************************************************************/ package net.ash.HIDToVPADNetworkClient.util; -import org.hid4java.HidDevice; -import org.hid4java.HidManager; -import org.hid4java.HidServices; +import java.io.IOException; +import java.util.List; -public class HID4JavaManager { +import purejavahidapi.HidDevice; +import purejavahidapi.HidDeviceInfo; +import purejavahidapi.PureJavaHidApi; + +public class PureJavaHidApiManager { - private HID4JavaManager(){} + 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){ - HidDevice result = null; - - HidServices services = HidManager.getHidServices(); - if(services == null) return result; - - for (HidDevice device : services.getAttachedHidDevices()) { - if (device.getPath().equals(path)) { - result = device; - break; + 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 result; + return null; } } \ No newline at end of file diff --git a/src/net/ash/HIDToVPADNetworkClient/util/Settings.java b/src/net/ash/HIDToVPADNetworkClient/util/Settings.java index 41ce03c..919fc1c 100644 --- a/src/net/ash/HIDToVPADNetworkClient/util/Settings.java +++ b/src/net/ash/HIDToVPADNetworkClient/util/Settings.java @@ -25,6 +25,10 @@ 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(){} From 845cdcb8f2429b16a52c20a265295eb367bc47f3 Mon Sep 17 00:00:00 2001 From: Maschell Date: Thu, 16 Mar 2017 19:44:36 +0100 Subject: [PATCH 5/5] Added missing submodules, added missing lincense --- purejavahidapi | 1 + .../controller/SwitchProController.java | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 160000 purejavahidapi 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/controller/SwitchProController.java b/src/net/ash/HIDToVPADNetworkClient/controller/SwitchProController.java index 4281b5c..f8fa377 100644 --- a/src/net/ash/HIDToVPADNetworkClient/controller/SwitchProController.java +++ b/src/net/ash/HIDToVPADNetworkClient/controller/SwitchProController.java @@ -1,3 +1,24 @@ +/******************************************************************************* + * 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;