Initial commit

This commit is contained in:
Ash 2017-01-31 19:12:28 +11:00
commit fff503652f
27 changed files with 2345 additions and 0 deletions

20
.classpath Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
bin/
target/
.settings/

23
.project Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>HIDToVPADNetworkClient</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

26
pom.xml Normal file
View File

@ -0,0 +1,26 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.ash</groupId>
<artifactId>HIDToVPADNetworkClient</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.hid4java</groupId>
<artifactId>hid4java</artifactId>
<version>0.4.0</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,69 @@
/*******************************************************************************
* 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;
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.network.NetworkManager;
/* Ash's todo list
* TODO finish Hid4JavaController
* TODO locale
*/
public class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
try {
new ControllerManager();
new NetworkManager();
} catch (Exception e) {
e.printStackTrace();
fatal();
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GuiMain.createGUI();
}
});
new ControllerDetector().start();
}
public static void fatal() {
System.err.println("HID To VPAD Network Client encountered an irrecoverable error.");
System.err.println("Exiting...");
System.exit(1);
}
public static void initiateShutdown() {
HidManager.getHidServices().shutdown();
System.exit(0);
}
}

View File

@ -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.controller;
/**
* Main controller interface, extended by controller drivers.
* <br><br>
* See {@link LinuxDevInputController} for a full implementation.
* @author ash
*/
public interface Controller {
/**
* Sets up the driver.
* <br>
* During this method call, a connection will be made with the controller hardware (if required).
*
* @param arg Driver-specific init argument, see {@link ControllerManager} and {@link ControllerDetector}.
* @return Whether initialization was successful.
*/
public boolean initController(Object arg);
/**
* Allocates and returns a copy of the latest data available from the controller.
*
* @return A ControllerData instance containing the latest controller data.
*/
public ControllerData getLatestData();
/**
* Used to tell a driver whether or not to poll the controller.
* <br>
* Is currently only ever used to initialize a driver (poll=true).
* destroy() is called for deinitialization.
* <br><br>
* <i>Candidate to be removed during refactoring.</i>
*
* @param poll Whether or not the driver should poll the controller.
*/
public void setPollingState(boolean poll);
/**
* Destroys the controller driver.
* <br>
* 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.
* <br>
* Please note that this is unique to the <i>driver</i>, 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();
}

View File

@ -0,0 +1,48 @@
/*******************************************************************************
* 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;
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;
}
}

View File

@ -0,0 +1,126 @@
/*******************************************************************************
* 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<GuiController> controllersIn) {
List<Controller> 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<GuiController> controllersIn) {
HashSet<Integer> m = new HashSet<Integer>();
for (GuiController g : controllersIn) {
if (!g.getActiveState()) continue;
m.add(g.getId().hashCode());
}
HashMap<Integer, Controller> d = NetworkManager.instance().getControllers();
for (Controller c : d.values()) {
if (!m.contains(c.getID().hashCode())) {
NetworkManager.instance().removeController(c);
}
}
List<GuiController> list = new ArrayList<GuiController>();
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<Controller> setupControllers(List<GuiController> controllers) {
List<Controller> out = new ArrayList<Controller>();
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<Integer, Controller> m = NetworkManager.instance().getControllers();
if (m.containsKey(c.getID().hashCode())) {
NetworkManager.instance().removeController(c);
}
}
public static ControllerManager instance() {
return instance;
}
}

View File

@ -0,0 +1,111 @@
/*******************************************************************************
* Copyright (c) 2017 Ash (QuarkTheAwesome) & Maschell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*******************************************************************************/
package net.ash.HIDToVPADNetworkClient.controller;
import org.hid4java.HidDevice;
import org.hid4java.HidManager;
import net.ash.HIDToVPADNetworkClient.util.HandleFoundry;
public class Hid4JavaController implements Controller {
static final int PACKET_LENGTH = 64;
private String path = null;
private HidDevice controller = null;
@Override
public boolean initController(Object arg) {
for (HidDevice device : HidManager.getHidServices().getAttachedHidDevices()) {
if (device.getPath().equals(arg.toString())) {
controller = device;
controller.setNonBlocking(true);
path = arg.toString();
break;
}
}
System.out.println("ctrl: " + controller.open() + " " + controller.getLastErrorMessage());
if (controller == null | !controller.isOpen()) return false;
return true;
}
@Override
public void setPollingState(boolean poll) {
}
@Override
public ControllerData getLatestData() {
ControllerData data = new ControllerData(getVID(), getPID(), getHandle(), new byte[1200]);
System.out.println("Data size: " + controller.read(data.data, 500));
System.out.println("hrm: " + controller.getLastErrorMessage());
return data;
}
@Override
public void destroy() {
controller.close();
}
private short getVID() {
return controller.getVendorId();
}
private short getPID() {
return controller.getProductId();
}
private short deviceSlot = 0;
private byte padSlot = 0;
@Override
public void setSlotData(short deviceSlot, byte padSlot) {
this.deviceSlot = deviceSlot;
this.padSlot = padSlot;
}
@Override
public short getDeviceSlot() {
return deviceSlot;
}
@Override
public byte getPadSlot() {
return padSlot;
}
private int handle = 0;
@Override
public int getHandle() {
if (handle == 0) {
handle = HandleFoundry.next();
}
return handle;
}
@Override
public String getID() {
return path;
}
}

View File

@ -0,0 +1,227 @@
/*******************************************************************************
* 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.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import net.ash.HIDToVPADNetworkClient.util.HandleFoundry;
public class LinuxDevInputController extends Thread implements Controller {
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;
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]);
}
@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 (;;)
}
private long buttonState = 0;
private byte[] axisState = new byte[NUM_SUPPORTED_AXIS];
private void processNextControllerEvent() {
//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();
} catch (IOException e) {
System.err.println("[LinuxDevInputController] Couldn't read from controller!");
e.printStackTrace();
System.out.println("[LinuxDevInputController] Detaching...");
ControllerManager.instance().detachController(this);
return;
}
//Treat init events as normal (clear init bit)
type &= ~JS_EVENT_INIT;
if (type == JS_EVENT_BUTTON) {
if (number >= Long.SIZE) {
System.out.println("[LinuxDevInputController] Button number " + number + " out of range; ignoring");
return;
}
if (value != 0) {
//Set bit with button number
buttonState |= (1 << number);
} else {
//Clear bit with button number
buttonState &= ~(1 << number);
}
} else if (type == JS_EVENT_AXIS) {
if (number >= NUM_SUPPORTED_AXIS) {
System.out.println("[LinuxDevInputController] Axis number " + number + " out of range; ignoring");
return;
}
//Do byteswap
value = (short)(((value & 0xFF) << Byte.SIZE) | ((value & 0xFF00) >> Byte.SIZE));
//Convert to unsigned byte and store
axisState[number] = (byte)(((value + Short.MAX_VALUE + 1) >> 8) & 0xFF);
}
byte[] newData = new byte[CONTROLLER_DATA_SIZE];
//Copy in button states
for (int i = 0; i < Long.BYTES; i++) {
newData[i] = (byte)((buttonState >> (i * Byte.SIZE)) & 0xFF);
}
//Copy in axis data
for (int i = Long.BYTES; i < CONTROLLER_DATA_SIZE; i++) {
newData[i] = axisState[i - Long.BYTES];
}
synchronized (controllerData) {
controllerData.data = newData;
}
}
@Override
public ControllerData getLatestData() {
synchronized (controllerData) {
return new ControllerData(getVID(), getPID(), getHandle(), controllerData.getData());
}
}
@Override
public boolean initController(Object arg) {
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;
}
@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]");
}
}

View File

@ -0,0 +1,187 @@
/*******************************************************************************
* 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
}
}

View File

@ -0,0 +1,89 @@
/*******************************************************************************
* 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<GuiController> 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<GuiController> detectControllers() {
List<GuiController> controllers = new ArrayList<GuiController>();
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<GuiController> 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()));
}
}
}

View File

@ -0,0 +1,53 @@
/*******************************************************************************
* 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.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 windowClosed(WindowEvent arg0) {}
@Override
public void windowDeactivated(WindowEvent arg0) {}
@Override
public void windowDeiconified(WindowEvent arg0) {}
@Override
public void windowIconified(WindowEvent arg0) {}
@Override
public void windowOpened(WindowEvent arg0) {}
}

View File

@ -0,0 +1,59 @@
/*******************************************************************************
* 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;
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)*/;
}
}

View File

@ -0,0 +1,106 @@
/*******************************************************************************
* 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.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.swing.AbstractButton;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class GuiControllerList extends JPanel {
private static final long serialVersionUID = 1L;
private JPanel innerScrollPanel;
private ActionListener currentActionListener = null;
public GuiControllerList() {
super(new BorderLayout());
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);
}
public synchronized List<GuiController> getControllers() {
List<GuiController> controllers = new ArrayList<GuiController>();
for (Component component : innerScrollPanel.getComponents()) {
if (component instanceof GuiControllerListItem) {
controllers.add(((GuiControllerListItem)component).getData());
}
}
return controllers;
}
public synchronized void updateControllerList(List<GuiController> controllers) {
System.out.println("[GuiControllerList] Updating controller list..."); //XXX debug text
HashMap<Integer, GuiController> components = new HashMap<Integer, GuiController>();
for (Component component : innerScrollPanel.getComponents()) {
if (component instanceof GuiControllerListItem) {
components.put(component.hashCode(), ((GuiControllerListItem)component).getData());
}
}
List<GuiControllerListItem> newComponents = new ArrayList<GuiControllerListItem>();
for (GuiController controller : controllers) {
GuiControllerListItem i;
if (components.containsKey(controller.hashCode())) {
i = new GuiControllerListItem(components.get(controller.hashCode()));
} else {
i = new GuiControllerListItem(controller);
}
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!");
}
}
}
}

View File

@ -0,0 +1,74 @@
/*******************************************************************************
* 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.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
public class GuiControllerListItem extends JPanel {
private static final long serialVersionUID = 1L;
private GuiController data = null;
private JCheckBox checkbox;
public GuiControllerListItem(GuiController data) {
super(new BorderLayout());
setMinimumSize(new Dimension (300, 30));
setPreferredSize(new Dimension(300, 30));
setMaximumSize(new Dimension(2000, 30));
this.data = data;
checkbox = new JCheckBox(getFlavorText());
checkbox.setSelected(data.getActiveState());
add(checkbox);
}
private String getFlavorText() {
switch (data.getType()) {
case HID4JAVA:
return "USB HID on " + data.getId().toString();
case LINUX:
return "Linux controller on " + data.getId().toString();
default:
return data.toString();
}
}
public void addActionListener(ActionListener l) {
checkbox.addActionListener(l);
}
public GuiController getData() {
return data;
}
@Override
public int hashCode() {
return data.hashCode();
}
}

View File

@ -0,0 +1,27 @@
/*******************************************************************************
* 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;
public enum GuiControllerType {
HID4JAVA,
LINUX
}

View File

@ -0,0 +1,115 @@
/*******************************************************************************
* 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.Dimension;
import java.awt.FlowLayout;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class GuiInputControls extends JPanel {
private static final long serialVersionUID = 1L;
private static GuiInputControls instance = null;
private static final String DEFAULT_PACKET_INTERVAL = "1000";
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) {
throw new Exception("GuiInputControls already has an instance!");
}
instance = this;
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
setPreferredSize(new Dimension(220, 150));
connectButton = new JButton("Connect");
connectButton.setAlignmentX(Component.CENTER_ALIGNMENT);
ipTextBox = new JTextField();
ipTextBox.setColumns(15);
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(Box.createRigidArea(new Dimension(1, 4)));
add(connectButton);
add(Box.createRigidArea(new Dimension(1, 8)));
add(statusLabel);
add(Box.createVerticalGlue());
connectButton.addActionListener(GuiInteractionManager.instance());
}
public static GuiInputControls instance() {
return instance;
}
public JTextField getPacketIntervalTextBox() {
return packetIntervalTextBox;
}
public JTextField getIpTextBox() {
return ipTextBox;
}
public JButton getConnectButton() {
return connectButton;
}
public JLabel getStatusLabel() {
return statusLabel;
}
}

View File

@ -0,0 +1,105 @@
/*******************************************************************************
* 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;
}
}

View File

@ -0,0 +1,94 @@
/*******************************************************************************
* 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.BorderLayout;
import java.awt.Dimension;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import net.ash.HIDToVPADNetworkClient.Main;
public class GuiMain extends JPanel {
private static final long serialVersionUID = 1L;
private static GuiMain instance;
public static void createGUI() {
JFrame frame = new JFrame("HID To VPAD Network Client");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new GuiCloseListener());
instance = new GuiMain();
JComponent newContentPane = instance;
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
private GuiControllerList leftControllerList;
private GuiInputControls rightSideControls;
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();
} catch (Exception e) {
e.printStackTrace();
Main.fatal();
}
add(rightSideControls, BorderLayout.LINE_END);
}
public void updateControllerList(List<GuiController> controllers) {
leftControllerList.updateControllerList(controllers);
}
public List<GuiController> getControllers() {
return leftControllerList.getControllers();
}
public String getIPText() {
return rightSideControls.getIpTextBox().getText();
}
public static GuiMain instance() {
return instance;
}
}

View File

@ -0,0 +1,258 @@
/*******************************************************************************
* 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.HashMap;
import java.util.concurrent.ThreadLocalRandom;
import net.ash.HIDToVPADNetworkClient.controller.Controller;
import net.ash.HIDToVPADNetworkClient.gui.GuiInteractionManager;
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<Integer, Controller> controllers = new HashMap<Integer, Controller>();
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<Integer,Controller> 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;
}
}

View File

@ -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;
public class Protocol {
public static final int TCP_PORT = 8112;
public static final int UDP_PORT = 8113;
public static final byte TCP_HANDSHAKE = 0x12;
public static final byte TCP_SAME_CLIENT = 0x20;
public static final byte TCP_NEW_CLIENT = 0x21;
public static final byte TCP_CMD_ATTACH = 0x01;
public static final byte TCP_CMD_DETACH = 0x02;
public static final byte TCP_CMD_PING = (byte)0xF0;
public static final byte UDP_CMD_DATA = 0x03;
}

View File

@ -0,0 +1,104 @@
/*******************************************************************************
* 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.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import net.ash.HIDToVPADNetworkClient.controller.Controller;
import net.ash.HIDToVPADNetworkClient.controller.ControllerData;
public class TCPClient {
private Socket sock;
private DataInputStream in;
private DataOutputStream out;
public TCPClient() {
}
public synchronized void connect(String ip) throws Exception {
sock = new Socket();
sock.connect(new InetSocketAddress(ip, Protocol.TCP_PORT), 2000);
in = new DataInputStream(sock.getInputStream());
out = new DataOutputStream(sock.getOutputStream());
}
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;
}
//TODO convince Maschell to make the client actually respond to pings
return true;
}
public synchronized void abort() throws Exception {
sock.close();
}
}

View File

@ -0,0 +1,81 @@
/*******************************************************************************
* 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.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
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;
public class UDPClient {
private DatagramSocket sock;
private InetAddress host;
private ByteArrayOutputStream outBytes;
private DataOutputStream out;
public UDPClient() {
outBytes = new ByteArrayOutputStream();
out = new DataOutputStream(outBytes);
}
public void connect(String ip) throws Exception {
sock = new DatagramSocket();
host = InetAddress.getByName(ip);
}
public void send(HashMap<Integer, Controller> 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);
sock.send(packet);
//System.out.println(Arrays.toString(payload)); //XXX debug text
outBytes.reset();
}
}

View File

@ -0,0 +1,105 @@
/*******************************************************************************
* 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();
}
}

View File

@ -0,0 +1,34 @@
/*******************************************************************************
* 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.
*******************************************************************************/
/*
* WHY HAVE A FACTORY WHEN YOU CAN HAVE A FOUNDRY?
*/
package net.ash.HIDToVPADNetworkClient.util;
public class HandleFoundry {
private static int h = 1;
public static int next() {
return h++;
}
}

View File

@ -0,0 +1,52 @@
/*******************************************************************************
* 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 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();
}
}
}
}