Merge pull request #9 from QuarkTheAwesome/untested_both_hid_libs

Adding an abstract HID-Layer to be able to use different HID-Implementations + various other fixes.

"With this extra abstraction we can use two different HID-APIs. Currently we use purejavahidapi for Windows (supports more devices) and hid4java for OSX and Unix (hopefully better support).
This will also enable the auto scanning for OSX and change some Debug messages.
HIDTest should now also run (a bit better)."
This commit is contained in:
Maschell 2017-04-05 20:14:03 +02:00 committed by GitHub
commit 2eaab5a211
20 changed files with 474 additions and 129 deletions

View File

@ -138,9 +138,14 @@
<version>1eb4087</version> <!-- JXInput 0.7 -->
</dependency>
<dependency>
<groupId>com.github.Maschell</groupId>
<groupId>com.github.QuarkTheAwesome</groupId>
<artifactId>purejavahidapi</artifactId>
<version>cbf0588</version>
<version>3591b7e</version>
</dependency>
<dependency>
<groupId>org.hid4java</groupId>
<artifactId>hid4java</artifactId>
<version>0.4.0</version>
</dependency>
</dependencies>
</project>

View File

@ -37,8 +37,8 @@ public final class Main {
public static void main(String[] args) {
Settings.loadSettings();
try {
new Thread(ActiveControllerManager.getInstance()).start();
new Thread(NetworkManager.getInstance()).start();
new Thread(ActiveControllerManager.getInstance(), "ActiveControllerManager").start();
new Thread(NetworkManager.getInstance(), "NetworkManager").start();
} catch (Exception e) {
e.printStackTrace();
fatal();

View File

@ -21,6 +21,8 @@
*******************************************************************************/
package net.ash.HIDToVPADNetworkClient.controller;
import java.util.Arrays;
import lombok.Getter;
import lombok.Synchronized;
import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
@ -41,6 +43,8 @@ public abstract class Controller implements Runnable {
@Getter private final String identifier;
private byte[] latestData = null;
protected int MAX_PACKET_LENGTH = 64;
boolean shutdown = false;
boolean shutdownDone = false;
private final Object dataLock = new Object();
@ -54,7 +58,7 @@ public abstract class Controller implements Runnable {
this.type = type;
this.identifier = identifier;
if (!initController(identifier)) {
throw new ControllerInitializationFailedException();
throw new ControllerInitializationFailedException("Initialization failed");
}
}
@ -62,14 +66,18 @@ public abstract class Controller implements Runnable {
public void run() {
boolean shutdownState = shutdown;
while (!shutdownState) {
Utilities.sleep(Settings.DETECT_CONTROLLER_INTERVAL);
while (isActive()) {
byte[] newData = pollLatestData();
if (newData != null && newData.length != 0) {
if (newData.length > MAX_PACKET_LENGTH) {
newData = Arrays.copyOfRange(newData, 0, MAX_PACKET_LENGTH);
}
// System.out.println("data:" + Utilities.ByteArrayToString(newData));
setLatestData(newData);
}
doSleepAfterPollingData();
}
Utilities.sleep(Settings.DETECT_CONTROLLER_ACTIVE_INTERVAL);
synchronized (shutdownLock) {
shutdownState = shutdown;
}
@ -202,7 +210,7 @@ public abstract class Controller implements Runnable {
}
public enum ControllerType {
PureJAVAHid, LINUX, XINPUT13, XINPUT14
HIDController, LINUX, XINPUT13, XINPUT14
}
public abstract String getInfoText();

View File

@ -5,26 +5,27 @@ import java.util.Arrays;
import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
import net.ash.HIDToVPADNetworkClient.util.Settings;
public class DS4NewController extends PureJavaHidController {
public class DS4NewController extends HidController {
public static final short DS4_NEW_CONTROLLER_VID = 0x54C;
public static final short DS4_NEW_CONTROLLER_PID = 0x09CC;
public DS4NewController(String identifier) throws ControllerInitializationFailedException {
super(identifier);
if (Settings.isMacOSX()) {
this.PACKET_LENGTH = 7;
this.MAX_PACKET_LENGTH = 7;
} else {
this.PACKET_LENGTH = 6;
this.MAX_PACKET_LENGTH = 6;
}
}
@Override
public byte[] pollLatestData() {
if (Settings.isMacOSX()) { // for some reason the controller has one extra byte at the beginning under OSX
return Arrays.copyOfRange(currentData, 1, 7);
byte[] currentData = super.pollLatestData();
if (Settings.isMacOSX() && currentData != null && currentData.length > 6) { // for some reason the controller has one extra byte at the beginning under
// OSX
currentData = Arrays.copyOfRange(currentData, 1, 7);
}
return currentData.clone();
return currentData;
}
@Override

View File

@ -22,63 +22,54 @@
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 lombok.extern.java.Log;
import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
import net.ash.HIDToVPADNetworkClient.util.PureJavaHidApiManager;
import purejavahidapi.HidDevice;
import purejavahidapi.InputReportListener;
import net.ash.HIDToVPADNetworkClient.hid.HidDevice;
import net.ash.HIDToVPADNetworkClient.hid.HidManager;
@Log
public class PureJavaHidController extends Controller implements InputReportListener {
private final Object dataLock = new Object();
protected byte[] currentData = new byte[1];
protected int PACKET_LENGTH = 64;
public class HidController extends Controller {
@Getter @Setter(AccessLevel.PRIVATE) private HidDevice hidDevice;
public static Controller getInstance(String deviceIdentifier) throws IOException, ControllerInitializationFailedException {
HidDevice device = PureJavaHidApiManager.getDeviceByPath(deviceIdentifier);
HidDevice device = HidManager.getDeviceByPath(deviceIdentifier);
short vid = 0;
short pid = 0;
if (device != null) {
vid = device.getHidDeviceInfo().getVendorId();
pid = device.getHidDeviceInfo().getProductId();
device.close();
vid = device.getVendorId();
pid = device.getProductId();
}
// We use a special version to optimize the data for the switch pro controller
if (vid == SwitchProController.SWITCH_PRO_CONTROLLER_VID && pid == SwitchProController.SWITCH_PRO_CONTROLLER_PID) {
return new SwitchProController(deviceIdentifier);
} else if (vid == DS4NewController.DS4_NEW_CONTROLLER_VID && pid == DS4NewController.DS4_NEW_CONTROLLER_PID) {
return new DS4NewController(deviceIdentifier);
} else {
return new PureJavaHidController(deviceIdentifier);
return new HidController(deviceIdentifier);
}
}
public PureJavaHidController(String identifier) throws ControllerInitializationFailedException {
super(ControllerType.PureJAVAHid, identifier);
public HidController(String identifier) throws ControllerInitializationFailedException {
super(ControllerType.HIDController, identifier);
}
@Override
public boolean initController(String identifier) {
HidDevice device;
try {
device = PureJavaHidApiManager.getDeviceByPath(identifier);
if (device == null) {
HidDevice device = HidManager.getDeviceByPath(identifier);
if (device == null || !device.open()) {
return false;
}
log.info("HidDevice opened!");
device.setInputReportListener(this);
setHidDevice(device);
return true;
@ -89,9 +80,9 @@ public class PureJavaHidController extends Controller implements InputReportList
}
@Override
@Synchronized("dataLock")
public byte[] pollLatestData() {
return currentData.clone();
byte[] result = hidDevice.getLatestData();
return result;
}
@Override
@ -105,34 +96,21 @@ public class PureJavaHidController extends Controller implements InputReportList
throw e;
}
}
}
@Override
public short getVID() {
return getHidDevice().getHidDeviceInfo().getVendorId();
return getHidDevice().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);
}
return getHidDevice().getProductId();
}
@Override
public String getInfoText() {
// TODO:
// TODO: own class for joycons
if (getVID() == 0x57e) {
if (getPID() == 0x2006) {
return "Joy-Con (L) on " + getIdentifier();

View File

@ -23,18 +23,19 @@ package net.ash.HIDToVPADNetworkClient.controller;
import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
public class SwitchProController extends PureJavaHidController {
public class SwitchProController extends HidController {
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;
this.MAX_PACKET_LENGTH = 11;
}
@Override
public byte[] pollLatestData() {
byte[] currentData = super.pollLatestData();
if (currentData == null || currentData.length < 10) {
return new byte[0];
}
@ -43,7 +44,7 @@ public class SwitchProController extends PureJavaHidController {
currentData[5] = 0;
currentData[7] = 0;
currentData[9] = 0;
return currentData.clone();
return currentData;
}
@Override

View File

@ -23,6 +23,10 @@ package net.ash.HIDToVPADNetworkClient.exeption;
public class ControllerInitializationFailedException extends Exception {
public ControllerInitializationFailedException(String string) {
super(string);
}
/**
*
*/

View File

@ -61,11 +61,7 @@ public final class GuiInputControls extends JPanel {
connectButton.setAlignmentX(Component.CENTER_ALIGNMENT);
final JCheckBox cbautoScanForController = new JCheckBox();
if (Settings.isMacOSX()) {
cbautoScanForController.setEnabled(false);
} else {
cbautoScanForController.setSelected(Settings.SCAN_AUTOMATICALLY_FOR_CONTROLLERS);
}
cbautoScanForController.setSelected(Settings.SCAN_AUTOMATICALLY_FOR_CONTROLLERS);
cbautoScanForController.addActionListener(new ActionListener() {

View File

@ -0,0 +1,40 @@
/*******************************************************************************
* 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.hid;
public interface HidDevice {
short getVendorId();
short getProductId();
void close();
byte[] getLatestData();
short getUsage();
String getPath();
boolean open();
}

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.hid;
import java.io.IOException;
import java.util.List;
import net.ash.HIDToVPADNetworkClient.hid.hid4java.Hid4JavaHidManagerBackend;
import net.ash.HIDToVPADNetworkClient.hid.purejavahid.PureJavaHidManagerBackend;
import net.ash.HIDToVPADNetworkClient.util.Settings;
public class HidManager {
private final static HidManagerBackend backend;
public static List<HidDevice> getAttachedController() {
return backend.getAttachedController();
}
public static HidDevice getDeviceByPath(String path) throws IOException {
return backend.getDeviceByPath(path);
}
static {
if (Settings.isMacOSX()) {
backend = new Hid4JavaHidManagerBackend();
} else if (Settings.isWindows()) {
backend = new PureJavaHidManagerBackend();
} else if (Settings.isLinux()) {
backend = new Hid4JavaHidManagerBackend();
} else
backend = null;
}
}

View File

@ -19,22 +19,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*******************************************************************************/
package net.ash.HIDToVPADNetworkClient.util;
package net.ash.HIDToVPADNetworkClient.hid;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.ash.HIDToVPADNetworkClient.manager.ControllerManager;
import purejavahidapi.HidDevice;
import purejavahidapi.HidDeviceInfo;
import purejavahidapi.PureJavaHidApi;
public final class PureJavaHidApiManager {
private PureJavaHidApiManager() {
}
import net.ash.HIDToVPADNetworkClient.util.Settings;
public abstract class HidManagerBackend {
/**
* Searches the corresponding HIDDevice for the given path
*
@ -43,25 +36,12 @@ public final class PureJavaHidApiManager {
* @return It the device is found, it will be returned. Otherwise null is returned.
* @throws IOException
*/
public static HidDevice getDeviceByPath(String path) throws IOException {
HidDeviceInfo deviceinfo = ControllerManager.getDeviceInfoByPath(path);
if (deviceinfo != null) {
HidDevice result = PureJavaHidApi.openDevice(deviceinfo);
if (result != null) {
return result;
}
}
/*
* List<HidDeviceInfo> devList = PureJavaHidApi.enumerateDevices(); HidDevice result = null; for (HidDeviceInfo info : devList) { String real_path =
* info.getPath(); if (real_path.equals(path)) { return PureJavaHidApi.openDevice(info); } }
*/
return null;
}
public abstract HidDevice getDeviceByPath(String path) throws IOException;
public static List<HidDeviceInfo> getAttachedController() {
List<HidDeviceInfo> connectedGamepads = new ArrayList<HidDeviceInfo>();
public List<HidDevice> getAttachedController() {
List<HidDevice> connectedGamepads = new ArrayList<HidDevice>();
for (HidDeviceInfo info : PureJavaHidApi.enumerateDevices()) {
for (HidDevice info : enumerateDevices()) {
if (isGamepad(info)) {
// Skip Xbox controller under windows. We should use XInput instead.
if (isXboxController(info) && Settings.isWindows()) {
@ -73,24 +53,26 @@ public final class PureJavaHidApiManager {
return connectedGamepads;
}
public static boolean isGamepad(HidDeviceInfo info) {
public static boolean isGamepad(HidDevice info) {
if (info == null) return false;
short usagePage = info.getUsagePage();
return (usagePage == 0x05 || usagePage == 0x01 || usagePage == 0x04 || isNintendoController(info) || isPlaystationController(info));
short usage = info.getUsage();
return (usage == 0x05 || usage == 0x04 || isNintendoController(info) || isPlaystationController(info));
}
private static boolean isPlaystationController(HidDeviceInfo info) {
private static boolean isPlaystationController(HidDevice info) {
if (info == null) return false;
return (info.getVendorId() == 0x054c);
}
private static boolean isNintendoController(HidDeviceInfo info) {
private static boolean isNintendoController(HidDevice info) {
if (info == null) return false;
return (info.getVendorId() == 0x57e);
}
private static boolean isXboxController(HidDeviceInfo info) {
private static boolean isXboxController(HidDevice info) {
if (info == null) return false;
return (info.getVendorId() == 0x045e) && ((info.getProductId() == 0x02ff) || (info.getProductId() == 0x02a1));
}
public abstract List<HidDevice> enumerateDevices();
}

View File

@ -0,0 +1,78 @@
/*******************************************************************************
* 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.hid.hid4java;
import java.util.Arrays;
import net.ash.HIDToVPADNetworkClient.hid.HidDevice;
class Hid4JavaHidDevice implements HidDevice {
private org.hid4java.HidDevice myDevice;
private final byte[] data = new byte[64];
public Hid4JavaHidDevice(org.hid4java.HidDevice device) {
this.myDevice = device;
}
@Override
public boolean open() {
return myDevice.open();
}
@Override
public void close() {
myDevice.close();
}
@Override
public short getVendorId() {
return myDevice.getVendorId();
}
@Override
public short getProductId() {
return myDevice.getProductId();
}
@Override
public byte[] getLatestData() {
int length = myDevice.read(data);
if (length <= 0) return new byte[0];
return Arrays.copyOf(data, length);
}
@Override
public String getPath() {
return myDevice.getPath();
}
@Override
public String toString() {
return "Hid4JavaHidDevice [myDevice=" + myDevice + ", data=" + Arrays.toString(data) + "]";
}
@Override
public short getUsage() {
return (short) myDevice.getUsage();
}
}

View File

@ -0,0 +1,60 @@
/*******************************************************************************
* 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.hid.hid4java;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.hid4java.HidManager;
import org.hid4java.HidServices;
import net.ash.HIDToVPADNetworkClient.hid.HidDevice;
import net.ash.HIDToVPADNetworkClient.hid.HidManagerBackend;
public class Hid4JavaHidManagerBackend extends HidManagerBackend {
@Override
public HidDevice getDeviceByPath(String path) throws IOException {
HidDevice result = null;
HidServices services = HidManager.getHidServices();
if (services == null) return result;
for (org.hid4java.HidDevice device : services.getAttachedHidDevices()) {
if (device.getPath().equals(path)) {
result = new Hid4JavaHidDevice(device);
break;
}
}
return result;
}
@Override
public List<HidDevice> enumerateDevices() {
List<HidDevice> result = new ArrayList<HidDevice>();
for (org.hid4java.HidDevice info : HidManager.getHidServices().getAttachedHidDevices()) {
result.add(new Hid4JavaHidDevice(info));
}
return result;
}
}

View File

@ -0,0 +1,92 @@
/*******************************************************************************
* 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.hid.purejavahid;
import java.io.IOException;
import java.util.Arrays;
import lombok.Synchronized;
import net.ash.HIDToVPADNetworkClient.hid.HidDevice;
import purejavahidapi.HidDeviceInfo;
import purejavahidapi.InputReportListener;
class PureJavaHidDevice implements HidDevice, InputReportListener {
private purejavahidapi.HidDevice myDevice = null;
private final purejavahidapi.HidDeviceInfo myDeviceInfo;
private final Object dataLock = new Object();
protected byte[] currentData = new byte[1];
public PureJavaHidDevice(HidDeviceInfo info) {
this.myDeviceInfo = info;
}
@Override
@Synchronized("dataLock")
public void onInputReport(purejavahidapi.HidDevice source, byte reportID, byte[] reportData, int reportLength) {
currentData = Arrays.copyOfRange(reportData, 0, reportLength);
}
@Override
public short getVendorId() {
return myDeviceInfo.getVendorId();
}
@Override
public short getProductId() {
return myDeviceInfo.getProductId();
}
@Override
public boolean open() {
boolean result = true;
try {
myDevice = purejavahidapi.PureJavaHidApi.openDevice(myDeviceInfo);
myDevice.setInputReportListener(this);
} catch (IOException e) {
result = false;
e.printStackTrace();
}
return result;
}
@Override
public void close() {
myDevice.close();
}
@Override
@Synchronized("dataLock")
public byte[] getLatestData() {
return currentData.clone();
}
@Override
public short getUsage() {
return myDeviceInfo.getUsagePage();
}
@Override
public String getPath() {
return myDeviceInfo.getPath();
}
}

View File

@ -0,0 +1,56 @@
/*******************************************************************************
* 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.hid.purejavahid;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.ash.HIDToVPADNetworkClient.hid.HidDevice;
import net.ash.HIDToVPADNetworkClient.hid.HidManagerBackend;
import purejavahidapi.PureJavaHidApi;
public class PureJavaHidManagerBackend extends HidManagerBackend {
@Override
public List<HidDevice> enumerateDevices() {
List<HidDevice> result = new ArrayList<HidDevice>();
for (purejavahidapi.HidDeviceInfo info : PureJavaHidApi.enumerateDevices()) {
result.add(new PureJavaHidDevice(info));
}
return result;
}
@Override
public HidDevice getDeviceByPath(String path) throws IOException {
List<purejavahidapi.HidDeviceInfo> devList = PureJavaHidApi.enumerateDevices();
HidDevice result = null;
for (purejavahidapi.HidDeviceInfo info : devList) {
String real_path = info.getPath();
if (real_path.equals(path)) {
return new PureJavaHidDevice(info);
}
}
return result;
}
}

View File

@ -61,7 +61,7 @@ public final class ActiveControllerManager implements Runnable {
Utilities.sleep(Settings.DETECT_CONTROLLER_INTERVAL);
}
}
}).start();
}, "DetectControllerThread").start();
new Thread(new Runnable() {
@Override
@ -71,7 +71,7 @@ public final class ActiveControllerManager implements Runnable {
Utilities.sleep(Settings.HANDLE_INPUTS_INTERVAL);
}
}
}).start();
}, "HandleControllerInputThread").start();
}
public void updateControllerStates() {

View File

@ -38,22 +38,21 @@ import lombok.Synchronized;
import lombok.extern.java.Log;
import net.ash.HIDToVPADNetworkClient.controller.Controller;
import net.ash.HIDToVPADNetworkClient.controller.Controller.ControllerType;
import net.ash.HIDToVPADNetworkClient.controller.HidController;
import net.ash.HIDToVPADNetworkClient.controller.LinuxDevInputController;
import net.ash.HIDToVPADNetworkClient.controller.PureJavaHidController;
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 net.ash.HIDToVPADNetworkClient.hid.HidDevice;
import net.ash.HIDToVPADNetworkClient.hid.HidManager;
import net.ash.HIDToVPADNetworkClient.util.MessageBox;
import net.ash.HIDToVPADNetworkClient.util.MessageBoxManager;
import net.ash.HIDToVPADNetworkClient.util.PureJavaHidApiManager;
import net.ash.HIDToVPADNetworkClient.util.Settings;
import purejavahidapi.HidDeviceInfo;
@Log
public final class ControllerManager {
private static final Map<String, Controller> attachedControllers = new HashMap<String, Controller>();
private static final Map<String, HidDeviceInfo> connectedDevicesInfo = new HashMap<String, HidDeviceInfo>();
private static boolean threwUnsatisfiedLinkError = false;
@ -71,7 +70,7 @@ public final class ControllerManager {
if (Settings.isLinux()) {
connectedDevices.putAll(detectLinuxControllers());
} else if (Settings.isWindows()) {
connectedDevices.putAll(detectWindowsControllers());
connectedDevices.putAll(detectXInputControllers());
}
connectedDevices.putAll(detectHIDDevices());
@ -80,7 +79,6 @@ public final class ControllerManager {
List<String> toRemove = new ArrayList<String>();
synchronized (attachedControllers) {
for (String s : attachedControllers.keySet()) {
System.out.println(s);
if (!connectedDevices.containsKey(s)) {
toRemove.add(s);
}
@ -91,6 +89,7 @@ public final class ControllerManager {
synchronized (attachedControllers) {
attachedControllers.get(remove).destroyAll();
attachedControllers.remove(remove);
log.info("Device removed: " + toRemove);
}
}
@ -104,11 +103,11 @@ public final class ControllerManager {
if (!contains) {
Controller c = null;
switch (entry.getValue()) {
case PureJAVAHid:
case HIDController:
try {
c = PureJavaHidController.getInstance(deviceIdentifier);
c = HidController.getInstance(deviceIdentifier);
} catch (ControllerInitializationFailedException e) {
// e.printStackTrace();
log.info(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
}
@ -117,14 +116,14 @@ public final class ControllerManager {
try {
c = new LinuxDevInputController(deviceIdentifier);
} catch (ControllerInitializationFailedException e) {
// e.printStackTrace();
log.info(e.getMessage());
}
break;
case XINPUT14:
try {
c = new XInput14Controller(deviceIdentifier);
} catch (ControllerInitializationFailedException e) {
// e.printStackTrace();
log.info(e.getMessage());
}
break;
case XINPUT13:
@ -141,10 +140,11 @@ public final class ControllerManager {
if (Settings.AUTO_ACTIVATE_CONTROLLER) {
c.setActive(true);
}
new Thread(c).start();
new Thread(c, "Controller Thread " + deviceIdentifier).start();
synchronized (attachedControllers) {
attachedControllers.put(deviceIdentifier, c);
}
log.info("Device added: " + deviceIdentifier);
}
}
}
@ -157,19 +157,15 @@ public final class ControllerManager {
private static Map<String, ControllerType> detectHIDDevices() {
Map<String, ControllerType> connectedDevices = new HashMap<String, ControllerType>();
System.out.println("detectHIDDevices");
for (HidDeviceInfo info : PureJavaHidApiManager.getAttachedController()) {
for (HidDevice info : HidManager.getAttachedController()) {
String path = info.getPath();
connectedDevices.put(path, ControllerType.PureJAVAHid);
synchronized (connectedDevicesInfo) {
connectedDevicesInfo.put(path, info);
}
connectedDevices.put(path, ControllerType.HIDController);
}
return connectedDevices;
}
private static Map<String, ControllerType> detectWindowsControllers() {
private static Map<String, ControllerType> detectXInputControllers() {
Map<String, ControllerType> result = new HashMap<String, ControllerType>();
ControllerType type = ControllerType.XINPUT13;
@ -245,10 +241,4 @@ public final class ControllerManager {
c.setActive(false);
}
}
@Synchronized("connectedDevicesInfo")
public static HidDeviceInfo getDeviceInfoByPath(String path) {
return connectedDevicesInfo.get(path);
}
}

View File

@ -157,17 +157,17 @@ public final class NetworkManager implements Runnable {
}
}
// TODO: PONG from WiiU? Hey Quark ;)
private void sendPing(PingCommand command) {
if (sendTCP(Protocol.getRawPingDataToSend(command))) {
log.info("PING");
byte pong;
try {
pong = tcpClient.recvByte();
if (pong != Protocol.TCP_CMD_PONG) {
if (pong == Protocol.TCP_CMD_PONG) {
log.info("Ping...Pong!");
} else {
log.info("Got no valid response to a Ping. Disconnecting.");
disconnect();
}
log.info("got PONG!");
} catch (IOException e) {
log.info("Failed to get PONG. Disconnecting.");
tcpClient.checkShouldRetry();

View File

@ -73,7 +73,7 @@ public final class MessageBoxManager implements Runnable {
public static void addMessageBoxListener(MessageBoxListener msglistener) {
if (!threadStarted) {
new Thread(instance).start();
new Thread(instance, "MessageBoxManager").start();
threadStarted = true;
}
newList.add(msglistener);

View File

@ -44,7 +44,9 @@ public final class Settings {
public static final int PING_INTERVAL = 1000;
public static final int PROCESS_CMD_INTERVAL = 10;
public static boolean SCAN_AUTOMATICALLY_FOR_CONTROLLERS = !isMacOSX(); // It doesn't work on OSX
public static final int DETECT_CONTROLLER_ACTIVE_INTERVAL = 100;
public static boolean SCAN_AUTOMATICALLY_FOR_CONTROLLERS = true;
public static boolean DEBUG_UDP_OUTPUT = false;
public static boolean SEND_DATA_ONLY_ON_CHANGE = false;