Compare commits

...

9 Commits

Author SHA1 Message Date
Maschell
2ae8142f61 Fix for the Xbox Controller 2017-05-20 19:19:19 +02:00
Maschell
da80e946d0 Version bump to 0.4 2017-04-27 19:30:56 +02:00
Maschell
e70045ef1c Fix for HID-Test. 2017-04-23 13:39:11 +02:00
Maschell
70b5da2206 Added Rumble support (currently XInput only), changed protocol version, code cleanup
Updated the code conventions.xml!
2017-04-13 15:32:55 +02:00
Maschell
f5f1d42d1b Minor code clean up 2017-04-13 11:23:02 +02:00
Ash
b94ed1cc9a [Linux] Add warning for users without required udev rule 2017-04-13 13:47:56 +10:00
Ash
29fe7917a0 [OSX] Change filtering behavior, GUI fix
Opening a mouse turns it off for whatever reason. This can be hard to
deal with on a mouse-based OS like OSX.
2017-04-13 13:33:09 +10:00
Ash
61c7cb31a2 Add filtering for XInput devices 2017-04-13 13:08:27 +10:00
Ash
39e8459840 Show VID/PIDs to user 2017-04-13 13:00:35 +10:00
23 changed files with 380 additions and 229 deletions

View File

@ -15,4 +15,4 @@ deployment:
branch: master
commands:
- go get github.com/tcnksm/ghr
- ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME -prerelease -b 'Nightly release - Use caution! We recommend downloading a stable release from the README.' `echo "v0.3-nightly-$(git rev-parse --short=7 HEAD)"` ci/
- ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME -prerelease -b 'Nightly release - Use caution! We recommend downloading a stable release from the README.' `echo "v0.4-nightly-$(git rev-parse --short=7 HEAD)"` ci/

View File

@ -26,7 +26,7 @@
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>

View File

@ -4,13 +4,13 @@
<modelVersion>4.0.0</modelVersion>
<groupId>net.ash</groupId>
<artifactId>HIDToVPADNetworkClient</artifactId>
<version>0.3</version>
<version>0.4</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<exec.mainClass>net.ash.HIDToVPADNetworkClient.Main</exec.mainClass>
<hidtest_version>0.1</hidtest_version>
<hidtest_version>0.2</hidtest_version>
</properties>
<name>HID To VPAD Network Client</name>

View File

@ -11,36 +11,32 @@ import javax.swing.JOptionPane;
* <p>
* v[(2), 2015-11-13 13:00 UTC]
* <p>
* One static method call will start a new instance of *THIS* application in the console and will EXIT the current
* instance. SO FAR ONLY WORKS ON WINDOWS! Users of other systems need to assist here. The methods are all in place.
* One static method call will start a new instance of *THIS* application in the console and will EXIT the current instance. SO FAR ONLY WORKS ON WINDOWS! Users
* of other systems need to assist here. The methods are all in place.
*/
final public class AutoRunFromConsole {
final private static String FAILMESSAGE_TITLE = "Please run in console.";
final private static String FAILMESSAGE_BODY = "This application must be run in the console (or \"command box\").\n\nIn there, you have to type:\n\njava -jar nameofprogram.jar";
static void showFailMessageAndExit() {
JOptionPane.showMessageDialog(null, FAILMESSAGE_BODY, FAILMESSAGE_TITLE, JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
private enum OSType {
UNDETERMINED, WINDOWS, LINUX, MACOS
}
private static OSType getOsType() {
// final String osName = System.getProperty("os.name");
// final String osVersion = System.getProperty("os.version");
// final String osArchitecture = System.getProperty("os.arch");
// System.out.println("\n\nOSNAME: " + osName);
// System.out.println("\n\nOSVERSION: " + osVersion);
// System.out.println("\n\nOSARCHITECTURE: " + osArchitecture);
// final String osName = System.getProperty("os.name");
// final String osVersion = System.getProperty("os.version");
// final String osArchitecture = System.getProperty("os.arch");
// System.out.println("\n\nOSNAME: " + osName);
// System.out.println("\n\nOSVERSION: " + osVersion);
// System.out.println("\n\nOSARCHITECTURE: " + osArchitecture);
final String osName = System.getProperty("os.name", "").toLowerCase();
if (osName.startsWith("windows")) {
@ -54,60 +50,55 @@ final public class AutoRunFromConsole {
return OSType.UNDETERMINED;
}
/**
* Checks if the program is currently running in console, and if not, starts the program from console and EXITS this
* instance of the program. Should be (one of) the first calls in your program.
* Checks if the program is currently running in console, and if not, starts the program from console and EXITS this instance of the program. Should be (one
* of) the first calls in your program.
* <p>
* This is the less safe variant of the method: To check if you're currently in the IDE, it just tries to find the
* executable name and if it exists in the current path. This should word perfectly at all times in IntelliJ - I
* don't know what values getExecutableName() returns inside Eclipse, but I suspect it will work just as well.
* This is the less safe variant of the method: To check if you're currently in the IDE, it just tries to find the executable name and if it exists in the
* current path. This should word perfectly at all times in IntelliJ - I don't know what values getExecutableName() returns inside Eclipse, but I suspect it
* will work just as well.
* <p>
* It's also less safe because you can't give a fallback executable name, but I believe it should do the trick in
* all situations.
* It's also less safe because you can't give a fallback executable name, but I believe it should do the trick in all situations.
* <p>
* If this is used on a system other than Windows, a message box is shown telling the user to start the program from
* the console. BECAUSE I DON'T KNOW HOW TO OPEN A CONSOLE ON OTHER SYSTEMS. SEE startExecutableInConsole();
* If this is used on a system other than Windows, a message box is shown telling the user to start the program from the console. BECAUSE I DON'T KNOW HOW
* TO OPEN A CONSOLE ON OTHER SYSTEMS. SEE startExecutableInConsole();
*/
public static void runYourselfInConsole(final boolean stayOpenAfterEnd) {
runYourselfInConsole(false, stayOpenAfterEnd, null, null);
}
/**
* Checks if the program is currently running in console, and if not, starts the program from console and EXITS this
* instance of the program. Should be (one of) the first calls in your program.
* Checks if the program is currently running in console, and if not, starts the program from console and EXITS this instance of the program. Should be (one
* of) the first calls in your program.
* <p>
* This is the safer variant of the method: The first command line argument GIVEN BY THE IDE'S RUN CONFIGURATION
* should be "ide" (Case is ignored.), which this method will use to determine if it's running from the IDE.
* This is the safer variant of the method: The first command line argument GIVEN BY THE IDE'S RUN CONFIGURATION should be "ide" (Case is ignored.), which
* this method will use to determine if it's running from the IDE.
* <p>
* It is also safer because you can give a fallback executable name in case getExecutableName() could not determine
* it.
* It is also safer because you can give a fallback executable name in case getExecutableName() could not determine it.
* <p>
* Ultimately, it is safer because if the executable could not be determined, it shows a message box telling the
* user to start the program from the console.
* Ultimately, it is safer because if the executable could not be determined, it shows a message box telling the user to start the program from the console.
* <p>
* You will probably never make use of this variant. It's meant to be a solution if all else seems to fail (e.g.
* customer calls and you need a quick fix).
* You will probably never make use of this variant. It's meant to be a solution if all else seems to fail (e.g. customer calls and you need a quick fix).
* <p>
* If this is used on a system other than Windows, a message box is shown telling the user to start the program from
* the console. BECAUSE I DON'T KNOW HOW TO OPEN A CONSOLE ON OTHER SYSTEMS. SEE startExecutableInConsole();
* If this is used on a system other than Windows, a message box is shown telling the user to start the program from the console. BECAUSE I DON'T KNOW HOW
* TO OPEN A CONSOLE ON OTHER SYSTEMS. SEE startExecutableInConsole();
*
* @param psvmArguments The arguments given to the main method.
* @param fallbackExecutableName Can be null. In case getExecutableName() can't determine the proper name, the
* fallback is used.
* @param psvmArguments
* The arguments given to the main method.
* @param fallbackExecutableName
* Can be null. In case getExecutableName() can't determine the proper name, the fallback is used.
*/
public static void runYourselfInConsole(final String[] psvmArguments, final String fallbackExecutableName, final boolean stayOpenAfterEnd) {
runYourselfInConsole(true, stayOpenAfterEnd, psvmArguments, fallbackExecutableName);
}
/**
* see the other two methods
*/
private static void runYourselfInConsole(final boolean useSaferApproach, final boolean stayOpenAfterEnd, final String[] psvmArguments, final String fallbackExecutableName) {
private static void runYourselfInConsole(final boolean useSaferApproach, final boolean stayOpenAfterEnd, final String[] psvmArguments,
final String fallbackExecutableName) {
String executableName = getExecutableName(fallbackExecutableName);
@ -135,18 +126,18 @@ final public class AutoRunFromConsole {
System.exit(0);
}
/**
* Opens a console window and starts the Java executable there.
* <p>
* If this is used on a system other than Windows, a message box is shown telling the user to start the program from
* the console. BECAUSE I DON'T KNOW HOW TO OPEN A CONSOLE ON OTHER SYSTEMS.
* If this is used on a system other than Windows, a message box is shown telling the user to start the program from the console. BECAUSE I DON'T KNOW HOW
* TO OPEN A CONSOLE ON OTHER SYSTEMS.
*
* @param executableName the full file name of the executable (without path)
* @param stayOpenAfterEnd If true (and if someone can figure out the necessary parameters for other systems than
* Windows), the console will not close once the executable has terminated. This is useful
* e.g. if you want to give some kind of bye bye message because you actually assumed that
* people start the program from console manually.
* @param executableName
* the full file name of the executable (without path)
* @param stayOpenAfterEnd
* If true (and if someone can figure out the necessary parameters for other systems than Windows), the console will not close once the
* executable has terminated. This is useful e.g. if you want to give some kind of bye bye message because you actually assumed that people start
* the program from console manually.
*/
private static void startExecutableInConsole(final String executableName, final boolean stayOpenAfterEnd) {
@ -157,9 +148,9 @@ final public class AutoRunFromConsole {
break;
case WINDOWS:
if (stayOpenAfterEnd) {
launchString = "cmd /c start cmd /k java -jar \"" + executableName+"\""; // No, using /k directly here DOES NOT do the trick.
launchString = "cmd /c start cmd /k java -jar \"" + executableName + "\""; // No, using /k directly here DOES NOT do the trick.
} else {
launchString = "cmd /c start java -jar \"" + executableName+"\"";
launchString = "cmd /c start java -jar \"" + executableName + "\"";
}
break;
case LINUX:
@ -180,35 +171,31 @@ final public class AutoRunFromConsole {
}
}
/**
* @param args the args as given to PSVM
* @return whether the first command line argument was "ide" (ignoring case). Don't forget to change your IDE's run
* configuration accordingly.
* @param args
* the args as given to PSVM
* @return whether the first command line argument was "ide" (ignoring case). Don't forget to change your IDE's run configuration accordingly.
*/
private static boolean isRunFromIDE(final String[] args) {
return args != null && args.length > 0 && args[0].equalsIgnoreCase("ide");
}
/**
* @return if System.console() is available. DOES NOT WORK properly from IDE, will return false then even though it
* should be true. Use isRunFromIDE or other means additionally.
* @return if System.console() is available. DOES NOT WORK properly from IDE, will return false then even though it should be true. Use isRunFromIDE or
* other means additionally.
*/
private static boolean isRunningInConsole() {
return System.console() != null;
}
/**
* @param fallbackExecutableName Can be null. In the very unlikely case this method can't determine the executable,
* the fallback will also be checked. But if the fallback also doesn't exist AS A FILE
* in the CURRENT path, null will be returned regardless, even if you're sure that
* your fallback should be correct.
* @return the name of the running jar file, OR NULL if it could not be determined (which should be a certainty
* while in IDE, hence can be abused for determining that).
* @param fallbackExecutableName
* Can be null. In the very unlikely case this method can't determine the executable, the fallback will also be checked. But if the fallback also
* doesn't exist AS A FILE in the CURRENT path, null will be returned regardless, even if you're sure that your fallback should be correct.
* @return the name of the running jar file, OR NULL if it could not be determined (which should be a certainty while in IDE, hence can be abused for
* determining that).
*/
public static String getExecutableName(final String fallbackExecutableName) {
@ -228,18 +215,15 @@ final public class AutoRunFromConsole {
}
}
// APPROACH 2 - QUERY SYSTEM PROPERTIES
final Properties properties = System.getProperties();
final String executableNameFromJavaClassPathProperty = properties.getProperty("java.class.path");
final String executableNameFromSunJavaCommandProperty = properties.getProperty("sun.java.command");
// System.out.println("\n\nexecutableNameFromClass:\n" + executableNameFromClass);
// System.out.println("\n\nexecutableNameFromJavaClassPathProperty:\n" + executableNameFromJavaClassPathProperty);
// System.out.println("\n\nexecutableNameFromSunJavaCommandProperty:\n" + executableNameFromSunJavaCommandProperty);
// System.out.println("\n\nfallbackExecutableName:\n" + fallbackExecutableName);
// System.out.println("\n\nexecutableNameFromClass:\n" + executableNameFromClass);
// System.out.println("\n\nexecutableNameFromJavaClassPathProperty:\n" + executableNameFromJavaClassPathProperty);
// System.out.println("\n\nexecutableNameFromSunJavaCommandProperty:\n" + executableNameFromSunJavaCommandProperty);
// System.out.println("\n\nfallbackExecutableName:\n" + fallbackExecutableName);
if (isThisProbablyTheExecutable(executableNameFromClass)) {
return executableNameFromClass;
@ -260,11 +244,10 @@ final public class AutoRunFromConsole {
return null;
}
/**
* @param candidateName suspected name of the running java executable
* @return if name is not null, ends with ".jar" (Case is ignored.), and points to a FILE existing in the CURRENT
* directory.
* @param candidateName
* suspected name of the running java executable
* @return if name is not null, ends with ".jar" (Case is ignored.), and points to a FILE existing in the CURRENT directory.
*/
private static boolean isThisProbablyTheExecutable(final String candidateName) {

View File

@ -3,15 +3,17 @@ package de.mas.HIDTest;
import net.ash.HIDToVPADNetworkClient.controller.Controller;
import net.ash.HIDToVPADNetworkClient.manager.ControllerManager;
import net.ash.HIDToVPADNetworkClient.util.Settings;
import net.ash.HIDToVPADNetworkClient.util.Settings.ControllerFiltering;
import net.ash.HIDToVPADNetworkClient.util.Settings.ControllerFiltering.Type;
import net.ash.HIDToVPADNetworkClient.util.Utilities;
public class Main {
public static void main(String[] args) {
AutoRunFromConsole.runYourselfInConsole(true);
Settings.AUTO_ACTIVATE_CONTROLLER = false;
new Thread(new Runnable() {
@Override
public void run() {
@ -21,31 +23,34 @@ public class Main {
}
}
}).start();
System.out.println("Scanning for controllers...");
Utilities.sleep(1000);
for(Controller c : ControllerManager.getAttachedControllers()){
for (Controller c : ControllerManager.getAttachedControllers()) {
c.setActive(true);
}
Utilities.sleep(1000);
Settings.AUTO_ACTIVATE_CONTROLLER = true;
ControllerFiltering.setFilterState(Type.HIDGAMEPAD, true);
ControllerFiltering.setFilterState(Type.XINPUT, true);
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
boolean attached = false;
for(Controller c : ControllerManager.getAttachedControllers()){
if(c.isActive()){
boolean attached = false;
for (Controller c : ControllerManager.getAttachedControllers()) {
if (c.isActive()) {
attached = true;
System.out.print(String.format("VID: %04X PID %04X", c.getVID(),c.getPID()) +" data: " + Utilities.ByteArrayToString(c.getLatestData()) + " | " );
System.out.print(String.format("VID: %04X PID %04X", c.getVID(), c.getPID()) + " data: "
+ Utilities.ByteArrayToString(c.getLatestData()) + " | ");
}
}
if(attached) System.out.print("\r");
if (attached) System.out.print("\r");
Utilities.sleep(15);
}
}

View File

@ -113,13 +113,13 @@ public class HidController extends Controller {
// TODO: own class for joycons
if (getVID() == 0x57e) {
if (getPID() == 0x2006) {
return "Joy-Con (L) on " + getIdentifier();
return "Joy-Con (L) (0x057e:0x2006) on " + getIdentifier();
} else if (getPID() == 0x2007) {
return "Joy-Con (R) on " + getIdentifier();
return "Joy-Con (R) (0x057e:0x2007) on " + getIdentifier();
}
}
String name = getHidDevice().getProductString();
return ((name != null) ? name : "USB HID") + " on " + getIdentifier();
return String.format("%s (0x%04X:0x%04X) on %s", (name != null) ? name : "USB HID", getVID(), getPID(), getIdentifier());
}
}

View File

@ -41,10 +41,10 @@ public class LinuxDevInputController extends Controller implements Runnable {
private static final byte JS_EVENT_AXIS = 0x02;
private DataInputStream controller;
@Getter private short VID;
@Getter private short PID;
private String name;
private long buttonState = 0;
@ -70,7 +70,7 @@ public class LinuxDevInputController extends Controller implements Runnable {
e.printStackTrace();
System.err.println("oops");
}
if (VID == 0 || PID == 0) {
VID = ((short) (identifier.hashCode() & 0xFFFF));
PID = ((short) ((identifier.hashCode() >> (Short.SIZE / Byte.SIZE)) & 0xFFFF));
@ -81,29 +81,29 @@ public class LinuxDevInputController extends Controller implements Runnable {
return true;
}
//This could probably do with some cleanup
// This could probably do with some cleanup
public void doSysFs(String identifier) throws Exception {
Process querySysFs = Runtime.getRuntime().exec("udevadm info -q path " + identifier);
querySysFs.waitFor();
String sysfs_path = "/sys" + Utilities.getStringFromInputStream(querySysFs.getInputStream()).trim() + "/device";
querySysFs.destroy();
File sysfs = new File(sysfs_path);
if (!sysfs.exists()) return;
char[] nameBuf = new char[1024];
FileReader nameGet = new FileReader(sysfs_path + "/name");
nameGet.read(nameBuf);
nameGet.close();
name = new String(nameBuf).trim();
char[] vidBuf = new char[6];
FileReader vidGet = new FileReader(sysfs_path + "/id/vendor");
vidGet.read(vidBuf);
vidGet.close();
short vid = Short.parseShort(new String(vidBuf).trim(), 16);
this.VID = vid;
char[] pidBuf = new char[6];
FileReader pidGet = new FileReader(sysfs_path + "/id/product");
pidGet.read(pidBuf);
@ -111,7 +111,7 @@ public class LinuxDevInputController extends Controller implements Runnable {
short pid = Short.parseShort(new String(pidBuf).trim(), 16);
this.PID = pid;
}
@Override
public byte[] pollLatestData() {
DataInputStream inputStream = this.controller;
@ -125,7 +125,7 @@ public class LinuxDevInputController extends Controller implements Runnable {
type = inputStream.readByte();
number = inputStream.readByte();
} catch (IOException e) {
if (!isActive()) return null; //"Stream closed" when removing
if (!isActive()) return null; // "Stream closed" when removing
System.err.println("[LinuxDevInputController] Couldn't read from controller!");
e.printStackTrace();
System.out.println("[LinuxDevInputController] Detaching...");
@ -169,14 +169,13 @@ public class LinuxDevInputController extends Controller implements Runnable {
for (int i = (Long.SIZE / Byte.SIZE); i < CONTROLLER_DATA_SIZE; i++) {
newData[i] = axisState[i - (Long.SIZE / Byte.SIZE)];
}
return newData;
}
@Override
protected void doSleepAfterPollingData() {
// This is event driven (aka pollLatestData() is blocking anyway until
// we have data), we don't need to sleep it all.
// This is event driven (aka pollLatestData() is blocking anyway until we have data), we don't need to sleep it all.
}
@Override
@ -195,6 +194,6 @@ public class LinuxDevInputController extends Controller implements Runnable {
@Override
public String getInfoText() {
return ((name != null) ? name : "Linux controller") + " on " + getIdentifier();
return String.format("%s (0x%04X:0x%04X) on %s", (name != null) ? name : "Linux Controller", getVID(), getPID(), getIdentifier());
}
}

View File

@ -30,6 +30,6 @@ public class XInput13Controller extends XInputController {
@Override
public String getInfoText() {
return "XInput 1.3 on " + getIdentifier();
return String.format("XInput 1.3 (0x%04X:0x%04X) on ", getVID(), getPID()) + getIdentifier();
}
}

View File

@ -30,6 +30,6 @@ public class XInput14Controller extends XInputController {
@Override
public String getInfoText() {
return "XInput 1.4 on " + getIdentifier();
return String.format("XInput 1.4 (0x%04X:0x%04X) on ", getVID(), getPID()) + getIdentifier();
}
}

View File

@ -35,6 +35,7 @@ import lombok.Getter;
import lombok.Setter;
import lombok.extern.java.Log;
import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException;
import net.ash.HIDToVPADNetworkClient.util.Settings;
import net.ash.HIDToVPADNetworkClient.util.Utilities;
@Log
@ -116,6 +117,15 @@ public class XInputController extends Controller {
buttonState |= axesDataShoulderButtons << 16;
data.putInt(axesData).putInt(buttonState);
if (isRumble()) {
int strength = Settings.RUMBLE_STRENGTH;
if (strength < 0) strength = 0;
if (strength > 100) strength = 100;
int value = (int) (65535 * (Settings.RUMBLE_STRENGTH / 100.0));
device.setVibration(value, value);
} else {
device.setVibration(0, 0);
}
return (data.array());
}
return new byte[0];
@ -139,6 +149,6 @@ public class XInputController extends Controller {
@Override
public String getInfoText() {
return "XInput on " + getIdentifier();
return String.format("XInput (0x%04X:%0x04X) on ", getVID(), getPID()) + getIdentifier();
}
}

View File

@ -87,7 +87,7 @@ public final class GuiInputControls extends JPanel {
});
ipTextBox = new JTextField();
ipTextBox.setColumns(15);
ipTextBox.setColumns(14);
ipTextBox.setText(Settings.getIpAddr());
JPanel ipTextBoxWrap = new JPanel(new FlowLayout());
ipTextBoxWrap.add(new JLabel("IP: "));

View File

@ -54,71 +54,71 @@ import net.ash.HIDToVPADNetworkClient.util.StatusReport;
public class GuiOptionsWindow extends JPanel {
private static final long serialVersionUID = 1L;
private static final GuiOptionsWindow instance = new GuiOptionsWindow();
private final List<Tab> tabs = new ArrayList<Tab>();
public static void showWindow() {
showWindow(null);
}
public static void showWindow(Component parent) {
instance.setOpaque(true);
for (Tab t : instance.tabs) {
t.updateTab();
}
JFrame window = new JFrame("Options");
window.setContentPane(instance);
window.pack();
window.setLocationRelativeTo(parent);
window.setVisible(true);
}
private GuiOptionsWindow() {
super(new GridLayout(1, 1));
log.info("Hello from the Options window!");
setPreferredSize(new Dimension(600, 200));
JTabbedPane tabPane = new JTabbedPane();
tabPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
Tab controllerTab = new ControllerTab();
tabs.add(controllerTab);
tabPane.addTab("Controllers", controllerTab);
Tab infoTab = new InfoTab();
tabs.add(infoTab);
tabPane.addTab("Info", infoTab);
add(tabPane);
}
private class ControllerTab extends Tab {
private static final long serialVersionUID = 1L;
private final ControllerFilteringList cFilterList;
private final JCheckBox cBoxScanForControllers;
private final JCheckBox cBoxAutoActivateControllers;
private ControllerTab() {
super(new GridLayout(1, 2));
cFilterList = new ControllerFilteringList();
for (Settings.ControllerFiltering.Type type : Settings.ControllerFiltering.Type.values()) {
if (!type.isSupportedOnPlatform()) continue;
ControllerFilteringListItem item = new ControllerFilteringListItem(type);
cFilterList.add(item);
}
add(cFilterList);
JPanel rightSideControls = new JPanel();
rightSideControls.setLayout(new BoxLayout(rightSideControls, BoxLayout.PAGE_AXIS));
rightSideControls.add(Box.createVerticalGlue());
cBoxScanForControllers = new JCheckBox("Automatically scan for controllers");
cBoxScanForControllers.setAlignmentX(Component.CENTER_ALIGNMENT);
cBoxScanForControllers.addActionListener(new ActionListener() {
@ -128,9 +128,9 @@ public class GuiOptionsWindow extends JPanel {
}
});
rightSideControls.add(cBoxScanForControllers);
rightSideControls.add(Box.createVerticalStrut(2));
cBoxAutoActivateControllers = new JCheckBox("Automatically activate controllers");
cBoxAutoActivateControllers.setAlignmentX(Component.CENTER_ALIGNMENT);
cBoxAutoActivateControllers.addActionListener(new ActionListener() {
@ -140,11 +140,11 @@ public class GuiOptionsWindow extends JPanel {
}
});
rightSideControls.add(cBoxAutoActivateControllers);
rightSideControls.add(Box.createVerticalGlue());
add(rightSideControls);
}
@Override
public void updateTab() {
for (ControllerFilteringListItem c : cFilterList.items) {
@ -153,45 +153,45 @@ public class GuiOptionsWindow extends JPanel {
cBoxScanForControllers.setSelected(Settings.SCAN_AUTOMATICALLY_FOR_CONTROLLERS);
cBoxAutoActivateControllers.setSelected(Settings.AUTO_ACTIVATE_CONTROLLER);
}
private class ControllerFilteringList extends JPanel {
private static final long serialVersionUID = 1L;
private List<ControllerFilteringListItem> items = new ArrayList<ControllerFilteringListItem>();
private JPanel innerPanel;
private ControllerFilteringList() {
super(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 0));
innerPanel = new JPanel();
innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.PAGE_AXIS));
JScrollPane innerPanelWrap = new JScrollPane(innerPanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
innerPanelWrap.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
add(innerPanelWrap, BorderLayout.CENTER);
JLabel controllerFilterText = new JLabel("Controllers to show:");
controllerFilterText.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
add(controllerFilterText, BorderLayout.PAGE_START);
}
public Component add(ControllerFilteringListItem c) {
items.add(c);
return innerPanel.add(c);
}
}
private class ControllerFilteringListItem extends JPanel {
private static final long serialVersionUID = 1L;
private final JCheckBox cBox;
private final Settings.ControllerFiltering.Type type;
private ControllerFilteringListItem(Settings.ControllerFiltering.Type typeIn) {
super(new GridLayout(1, 1));
this.type = typeIn;
cBox = new JCheckBox(type.getName());
cBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
cBox.addActionListener(new ActionListener() {
@ -202,38 +202,38 @@ public class GuiOptionsWindow extends JPanel {
});
add(cBox);
}
public void updateItem() {
cBox.setSelected(Settings.ControllerFiltering.getFilterState(type));
}
//I can't believe I didn't figure this out for GuiControllerList
// I can't believe I didn't figure this out for GuiControllerList
@Override
public Dimension getMaximumSize() {
return new Dimension(Integer.MAX_VALUE, getPreferredSize().height);
}
}
}
private class InfoTab extends Tab {
private static final long serialVersionUID = 1L;
private final JTextArea infoText;
private final JScrollPane infoTextWrap;
private InfoTab() {
super();
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
setBorder(BorderFactory.createEmptyBorder(5, 5, 10, 5));
infoText = new JTextArea();
infoText.setEditable(false);
infoTextWrap = new JScrollPane(infoText, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
infoTextWrap.setAlignmentX(Component.CENTER_ALIGNMENT);
add(infoTextWrap);
add(Box.createVerticalStrut(10));
JButton copyButton = new JButton("Copy");
copyButton.addActionListener(new ActionListener() {
@Override
@ -253,13 +253,16 @@ public class GuiOptionsWindow extends JPanel {
infoText.setCaretPosition(0);
}
}
private abstract class Tab extends JPanel {
private static final long serialVersionUID = 1L;
public abstract void updateTab();
public Tab(LayoutManager l) {
super(l);
}
public Tab() {
super();
}

View File

@ -62,7 +62,7 @@ public interface HidDevice {
* @return usage page
*/
short getUsagePage();
/**
* Retuns the Usage ID of this HID-Device
*
@ -76,7 +76,7 @@ public interface HidDevice {
* @return path
*/
String getPath();
/**
* Returns the name of the HID device
*

View File

@ -37,14 +37,14 @@ public class HidManager {
public static HidDevice getDeviceByPath(String path) throws IOException {
return backend.getDeviceByPath(path);
}
public static List<HidDevice> getAttachedControllers() {
List<HidDevice> connectedGamepads = new ArrayList<HidDevice>();
for (HidDevice info : backend.enumerateDevices()) {
if (isGamepad(info)) {
if (Settings.ControllerFiltering.getFilterState(Settings.ControllerFiltering.Type.HIDGAMEPAD)) {
// Skip Xbox controller under windows. We should use XInput instead.
// Skip Xbox controller under windows. We should use XInput instead.
if (isXboxController(info) && Settings.isWindows()) {
continue;
}
@ -64,7 +64,7 @@ public class HidManager {
}
return connectedGamepads;
}
public static List<HidDevice> getAllAttachedControllers() {
return backend.enumerateDevices();
}
@ -72,15 +72,16 @@ public class HidManager {
public static boolean isGamepad(HidDevice info) {
if (info == null) return false;
short usage = info.getUsageID();
return (usage == 0x05 || usage == 0x04 || isNintendoController(info) || isPlaystationController(info));
return (info.getProductString().toLowerCase().contains("gamepad") || usage == 0x05 || usage == 0x04 || isNintendoController(info)
|| isPlaystationController(info));
}
public static boolean isKeyboard(HidDevice info) {
if (info == null) return false;
short usage = info.getUsageID();
return (usage == 0x06);
}
public static boolean isMouse(HidDevice info) {
if (info == null) return false;
short usage = info.getUsageID();
@ -105,7 +106,7 @@ public class HidManager {
public static String getBackendType() {
return backend.getClass().getSimpleName();
}
static {
if (Settings.isMacOSX()) {
backend = new Hid4JavaHidManagerBackend();

View File

@ -70,17 +70,18 @@ class Hid4JavaHidDevice implements HidDevice {
public String getProductString() {
return myDevice.getProduct();
}
@Override
public String toString() {
return "Hid4JavaHidDevice [vid= " + getVendorId() + ", pid= " + getProductId() + ", usage= " + String.format("%04X:%04X", getUsagePage(), getUsageID()) + ", data=" + Arrays.toString(data) + "]";
return "Hid4JavaHidDevice [vid= " + getVendorId() + ", pid= " + getProductId() + ", usage= " + String.format("%04X:%04X", getUsagePage(), getUsageID())
+ ", data=" + Arrays.toString(data) + "]";
}
@Override
public short getUsageID() {
return (short) myDevice.getUsage();
}
@Override
public short getUsagePage() {
return (short) myDevice.getUsagePage();

View File

@ -25,10 +25,15 @@ import java.io.IOException;
import java.util.Arrays;
import lombok.Synchronized;
import lombok.extern.java.Log;
import net.ash.HIDToVPADNetworkClient.hid.HidDevice;
import net.ash.HIDToVPADNetworkClient.util.MessageBox;
import net.ash.HIDToVPADNetworkClient.util.MessageBoxManager;
import net.ash.HIDToVPADNetworkClient.util.Settings;
import purejavahidapi.HidDeviceInfo;
import purejavahidapi.InputReportListener;
@Log
class PureJavaHidDevice implements HidDevice, InputReportListener {
private purejavahidapi.HidDevice myDevice = null;
private final purejavahidapi.HidDeviceInfo myDeviceInfo;
@ -56,6 +61,8 @@ class PureJavaHidDevice implements HidDevice, InputReportListener {
return myDeviceInfo.getProductId();
}
private static boolean hasShownUdevErrorMessage = false;
@Override
public boolean open() {
boolean result = true;
@ -64,7 +71,17 @@ class PureJavaHidDevice implements HidDevice, InputReportListener {
myDevice.setInputReportListener(this);
} catch (IOException e) {
result = false;
e.printStackTrace();
if (e.getMessage().contains("errno 13") && Settings.isLinux()) {
if (!hasShownUdevErrorMessage) {
hasShownUdevErrorMessage = true;
String msg = "Could not access HID devices.\nTo fix this, please add the following udev rule:\n\nKERNEL==\"hidraw*\", SUBSYSTEM==\"hidraw\", MODE=\"0664\", GROUP=\"plugdev\"\n\nThis will allow everyone to read from HIDs, and users in the group \"plugdev\" to read and write.\nIt's reccomended to add yourself to this group.\n\nHID To VPAD Network Client will be unable to use HIDs until this issue is resolved.";
MessageBoxManager.addMessageBox(new MessageBox(msg, MessageBox.MESSAGE_ERROR));
log.severe(msg);
e.printStackTrace();
}
} else {
e.printStackTrace();
}
}
return result;
}
@ -84,7 +101,7 @@ class PureJavaHidDevice implements HidDevice, InputReportListener {
public short getUsagePage() {
return myDeviceInfo.getUsagePage();
}
@Override
public short getUsageID() {
return myDeviceInfo.getUsageID();
@ -97,12 +114,13 @@ class PureJavaHidDevice implements HidDevice, InputReportListener {
@Override
public String getProductString() {
return myDeviceInfo.getProductString();
if(myDevice != null || myDeviceInfo.getProductString() == null) return "";
return myDeviceInfo.getProductString().trim();
}
@Override
public String toString() {
return "PureJavaHidDevice [vid= " + String.format("%04X", getVendorId()) + ", pid= " + String.format("%04X", getProductId()) + ", path= " + getPath().trim()
+ ", usage= " + String.format("%04X:%04X", getUsagePage(), getUsageID()) + ", data=" + Arrays.toString(currentData) + "]";
return "PureJavaHidDevice [vid= " + String.format("%04X", getVendorId()) + ", pid= " + String.format("%04X", getProductId()) + ", path= "
+ getPath().trim() + ", usage= " + String.format("%04X:%04X", getUsagePage(), getUsageID()) + ", data=" + Arrays.toString(currentData) + "]";
}
}

View File

@ -24,6 +24,7 @@ package net.ash.HIDToVPADNetworkClient.manager;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -167,6 +168,8 @@ public final class ControllerManager {
private static Map<String, ControllerType> detectXInputControllers() {
Map<String, ControllerType> result = new HashMap<String, ControllerType>();
if (!Settings.ControllerFiltering.getFilterState(Settings.ControllerFiltering.Type.XINPUT)) return result;
ControllerType type = ControllerType.XINPUT13;
// Try and catch missing C++ redist
@ -193,8 +196,13 @@ public final class ControllerManager {
XInputDevice device;
try {
device = XInputDevice.getDeviceFor(i);
if (device.poll() && device.isConnected()) { // Check if it is this controller is connected
result.put(XInputController.XINPUT_INDENTIFER + i, type);
try {
if (device.poll() && device.isConnected()) { // Check if it is this controller is connected
result.put(XInputController.XINPUT_INDENTIFER + i, type);
}
} catch (BufferUnderflowException e) {
//
log.info("XInput error.");
}
} catch (XInputNotLoadedException e) {
// This shouln't happen?
@ -209,7 +217,7 @@ public final class ControllerManager {
private static Map<String, ControllerType> detectLinuxControllers() {
Map<String, ControllerType> result = new HashMap<String, ControllerType>();
if (!Settings.ControllerFiltering.getFilterState(Settings.ControllerFiltering.Type.LINUX)) return result;
File devInput = new File("/dev/input");
if (!devInput.exists()) return result;

View File

@ -74,6 +74,7 @@ public final class NetworkManager implements Runnable {
@Override
public void run() {
int i = 0;
new Thread(UDPServer.getInstance(), "UDP Server").start();
while (true) {
proccessCommands();
Utilities.sleep(Settings.PROCESS_CMD_INTERVAL);

View File

@ -25,24 +25,29 @@ import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import lombok.extern.java.Log;
import net.ash.HIDToVPADNetworkClient.controller.Controller;
import net.ash.HIDToVPADNetworkClient.manager.ActiveControllerManager;
import net.ash.HIDToVPADNetworkClient.util.MessageBox;
import net.ash.HIDToVPADNetworkClient.util.MessageBoxManager;
import net.ash.HIDToVPADNetworkClient.util.Utilities;
@Log
final class Protocol {
private static ProtocolVersion currentProtocol = ProtocolVersion.MY_VERSION;
public static ProtocolVersion currentProtocol = ProtocolVersion.MY_VERSION;
static final int TCP_PORT = 8112;
static final int UDP_PORT = 8113;
static final int UDP_CLIENT_PORT = 8114;
static final byte TCP_HANDSHAKE_VERSION_1 = 0x12;
static final byte TCP_HANDSHAKE_VERSION_2 = 0x13;
static final byte TCP_HANDSHAKE_VERSION_3 = 0x14;
static final byte TCP_HANDSHAKE_ABORT = 0x30;
static final byte TCP_HANDSHAKE = TCP_HANDSHAKE_VERSION_2; // default version.
static final byte TCP_HANDSHAKE = TCP_HANDSHAKE_VERSION_3; // default version.
static final byte TCP_SAME_CLIENT = 0x20;
static final byte TCP_NEW_CLIENT = 0x21;
@ -58,6 +63,7 @@ final class Protocol {
static final byte TCP_CMD_ATTACH_CONFIG_NOT_FOUND = (byte) 0xE1;
static final byte TCP_CMD_ATTACH_USERDATA_OKAY = (byte) 0xE8;
static final byte TCP_CMD_ATTACH_USERDATA_BAD = (byte) 0xE9;
private static boolean showVersionInfo = false;
private Protocol() {
}
@ -125,6 +131,9 @@ final class Protocol {
} else if (wiiuProtocolVersion == ProtocolVersion.Version2) {
// We want to do version 2!
tcpClient.send(ProtocolVersion.Version2.getVersionByte());
} else if (wiiuProtocolVersion == ProtocolVersion.Version3) {
// We want to do version 3!
tcpClient.send(ProtocolVersion.Version3.getVersionByte());
}
if (wiiuProtocolVersion == ProtocolVersion.Version1) {
@ -151,6 +160,14 @@ final class Protocol {
if (resultHandshake == HandshakeReturnCode.GOOD_HANDSHAKE) {
ActiveControllerManager.getInstance().attachAllActiveControllers();
log.info("Handshake was successful! Using protocol version " + (currentProtocol.getVersionByte() - TCP_HANDSHAKE_VERSION_1 + 1));
if (currentProtocol.getVersionByte() != ProtocolVersion.MY_VERSION.versionByte) {
String message = "Using an old network protocol. You may want to update this network client and HIDtoVPAD for the best experience.";
log.info(message);
if (!showVersionInfo) {
MessageBoxManager.addMessageBox(message, MessageBox.MESSAGE_INFO);
showVersionInfo = true;
}
}
return resultHandshake;
} else {
log.info("[TCP] Handshaking failed");
@ -160,8 +177,12 @@ final class Protocol {
}
public enum ProtocolVersion {
MY_VERSION((byte) TCP_HANDSHAKE), Version1((byte) TCP_HANDSHAKE_VERSION_1), Version2((byte) TCP_HANDSHAKE_VERSION_2), Abort(
(byte) TCP_HANDSHAKE_ABORT), UNKOWN((byte) 0x00);
Abort((byte) TCP_HANDSHAKE_ABORT),
UNKOWN((byte) 0x00),
Version1((byte) TCP_HANDSHAKE_VERSION_1),
Version2((byte) TCP_HANDSHAKE_VERSION_2),
Version3((byte) TCP_HANDSHAKE_VERSION_3),
MY_VERSION((byte) TCP_HANDSHAKE);
private final byte versionByte;
private ProtocolVersion(byte versionByte) {
@ -174,6 +195,8 @@ final class Protocol {
return ProtocolVersion.Version1;
case TCP_HANDSHAKE_VERSION_2:
return ProtocolVersion.Version2;
case TCP_HANDSHAKE_VERSION_3:
return ProtocolVersion.Version3;
case TCP_HANDSHAKE_ABORT:
return ProtocolVersion.Abort;
default:
@ -186,4 +209,24 @@ final class Protocol {
}
}
public static void parseUDPServerData(byte[] data) {
if (currentProtocol.versionByte >= TCP_HANDSHAKE_VERSION_3) {
ByteBuffer buffer = ByteBuffer.wrap(data);
buffer.order(ByteOrder.BIG_ENDIAN);
byte cmd = buffer.get();
if (cmd == 0x01) {
int handle = buffer.getInt();
byte rumble = buffer.get();
Controller c = ActiveControllerManager.getInstance().getControllerByHIDHandle(handle);
if (c != null) {
if (rumble == 0x01) {
c.startRumble();
} else {
c.stopRumble();
}
}
}
}
}
}

View File

@ -0,0 +1,66 @@
/*******************************************************************************
* 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.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.Arrays;
import lombok.extern.java.Log;
@Log
public final class UDPServer implements Runnable {
private final DatagramSocket sock;
private UDPServer() throws SocketException {
sock = new DatagramSocket(Protocol.UDP_CLIENT_PORT);
}
static UDPServer getInstance() {
UDPServer result = null;
try {
result = new UDPServer();
} catch (Exception e) {
// handle?
}
return result;
}
@Override
public void run() {
log.info("UDPServer running.");
byte[] receiveData = new byte[1400];
while (true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
try {
sock.receive(receivePacket);
} catch (IOException e) {
continue;
}
byte[] data = Arrays.copyOf(receivePacket.getData(), receivePacket.getLength());
Protocol.parseUDPServerData(data);
}
}
}

View File

@ -47,6 +47,8 @@ public final class Settings {
public static final int DETECT_CONTROLLER_ACTIVE_INTERVAL = 100;
public static final int RUMBLE_STRENGTH = 50; // in % TODO: Create setting for this.
public static boolean SCAN_AUTOMATICALLY_FOR_CONTROLLERS = true;
public static boolean DEBUG_UDP_OUTPUT = false;
@ -67,6 +69,7 @@ public final class Settings {
}
File configFile = getConfigFile();
if (!configFile.exists()) {
ControllerFiltering.setDefaultFilterStates();
log.info("Creating " + configFile.getAbsolutePath() + " with default values...");
try {
configFile.createNewFile();
@ -124,7 +127,7 @@ public final class Settings {
} else {
ControllerFiltering.setDefaultFilterStates();
}
log.info("Loaded config successfully!");
}
@ -148,7 +151,7 @@ public final class Settings {
prop.setProperty("sendDataOnlyOnChanges", Boolean.toString(Settings.SEND_DATA_ONLY_ON_CHANGE));
prop.setProperty("scanAutomaticallyForControllers", Boolean.toString(Settings.SCAN_AUTOMATICALLY_FOR_CONTROLLERS));
prop.setProperty("filterStates", ControllerFiltering.getFilterStates());
try {
FileOutputStream outStream = new FileOutputStream(configFile);
prop.store(outStream, "HIDToVPADNetworkClient");
@ -196,63 +199,72 @@ public final class Settings {
}
public enum Platform {
LINUX (0x1), WINDOWS (0x2), MAC_OS_X (0x4), UNKNOWN (0x8);
LINUX(0x1), WINDOWS(0x2), MAC_OS_X(0x4), UNKNOWN(0x8);
private int mask;
private Platform(int mask) {
this.mask = mask;
}
}
//TODO rename this to something less nonsensical
// TODO rename this to something less nonsensical
public static class ControllerFiltering {
public static enum Type {
HIDGAMEPAD (0, "HID Gamepads", Platform.LINUX.mask | Platform.WINDOWS.mask | Platform.MAC_OS_X.mask),
HIDKEYBOARD (1, "HID Keyboards", Platform.LINUX.mask | Platform.MAC_OS_X.mask),
HIDMOUSE (2, "HID Mice", Platform.LINUX.mask | Platform.MAC_OS_X.mask),
HIDOTHER (3, "Other HIDs", Platform.LINUX.mask | Platform.WINDOWS.mask | Platform.MAC_OS_X.mask),
LINUX (4, "Linux controllers", Platform.LINUX.mask);
HIDGAMEPAD(0, "HID Gamepads", Platform.LINUX.mask | Platform.WINDOWS.mask | Platform.MAC_OS_X.mask),
XINPUT(5, "XInput controllers", Platform.WINDOWS.mask),
HIDKEYBOARD(1, "HID Keyboards", Platform.LINUX.mask | Platform.MAC_OS_X.mask),
HIDMOUSE(2, "HID Mice", Platform.LINUX.mask),
HIDOTHER(3, "Other HIDs", Platform.LINUX.mask | Platform.WINDOWS.mask | Platform.MAC_OS_X.mask),
LINUX(4, "Linux controllers", Platform.LINUX.mask),;
private int index;
@Getter private String name;
private int platforms;
private Type(int index, String name, int platforms) {
this.index = index;
this.name = name;
this.platforms = platforms;
}
public boolean isSupportedOnPlatform() {
return (platforms & getPlattform().mask) != 0;
}
}
private static boolean[] filterStates = new boolean[Type.values().length];
public static String getFilterStates() {
return Arrays.toString(filterStates);
}
public static void loadFilterStates(String newFilterStates) {
boolean[] newFilterStatesParsed = Utilities.stringToBoolArray(newFilterStates);
if (newFilterStatesParsed.length != filterStates.length) {
//TODO handle changes in filtering more gracefully
// TODO handle changes in filtering more gracefully
log.warning("Number of controller filters in config does not match reality, using defaults...");
setDefaultFilterStates();
} else {
filterStates = newFilterStatesParsed;
}
}
public static void setFilterState(Type filter, boolean state) {
filterStates[filter.index] = state;
}
public static boolean getFilterState(Type filter) {
return filterStates[filter.index] || !filter.isSupportedOnPlatform();
return filterStates[filter.index];
}
public static void setDefaultFilterStates() {
filterStates[Type.HIDGAMEPAD.index] = true;
filterStates[Type.HIDKEYBOARD.index] = false;
filterStates[Type.HIDMOUSE.index] = false;
filterStates[Type.HIDOTHER.index] = false;
filterStates[Type.LINUX.index] = true;
filterStates[Type.XINPUT.index] = true;
}
}
}

View File

@ -8,32 +8,33 @@ import net.ash.HIDToVPADNetworkClient.network.NetworkManager;
public class StatusReport {
public static String generateStatusReport() {
String report = "HID to VPAD Network Client\n\nRunning on ";
report += Settings.getPlattform();
report += "\nHID Backend: ";
report += HidManager.getBackendType();
report += "\nCurrently ";
report += (NetworkManager.getInstance().isConnected()) ? "Connected.\n" : "Disconnected.\n";
report += (NetworkManager.getInstance().isReconnecting()) ? "" : "Not ";
report += "Reconnecting.";
report += "\n\nCurrently attached controllers:";
StringBuilder report = new StringBuilder();
report.append("HID to VPAD Network Client\n\nRunning on ");
report.append(Settings.getPlattform());
report.append(System.lineSeparator()).append("HID Backend: ");
report.append(HidManager.getBackendType());
report.append(System.lineSeparator()).append("Currently ");
report.append((NetworkManager.getInstance().isConnected()) ? "Connected.\n" : "Disconnected.").append(System.lineSeparator());
report.append((NetworkManager.getInstance().isReconnecting()) ? "" : "Not ");
report.append("Reconnecting.");
report.append(System.lineSeparator()).append(System.lineSeparator()).append("Currently attached controllers:");
for (Controller c : ControllerManager.getAttachedControllers()) {
report += "\n";
report += c.toString();
report.append(System.lineSeparator());
report.append(c.toString());
}
report += "\n\nFiltering settings:\n";
report += Settings.ControllerFiltering.getFilterStates();
report += "\n\nAll HIDs:";
report.append(System.lineSeparator()).append(System.lineSeparator()).append("Filtering settings:").append(System.lineSeparator());
report.append(Settings.ControllerFiltering.getFilterStates());
report.append(System.lineSeparator()).append(System.lineSeparator()).append("All HIDs:");
for (HidDevice d : HidManager.getAllAttachedControllers()) {
report += "\n";
report += d.toString();
report.append(System.lineSeparator());
report.append(d.toString());
}
return report;
return report.toString();
}
}

View File

@ -80,10 +80,10 @@ public final class Utilities {
public static short signedShortToByte(short value) {
return signedShortToByte((int) value);
}
/**
* Arrays.toString(boolean[]) in reverse.
* https://stackoverflow.com/questions/456367/
* Arrays.toString(boolean[]) in reverse. https://stackoverflow.com/questions/456367/
*
* @param string
* @return array
*/
@ -91,20 +91,20 @@ public final class Utilities {
String[] strings = string.replace("[", "").replace("]", "").split(", ");
boolean result[] = new boolean[strings.length];
for (int i = 0; i < result.length; i++) {
result[i] = Boolean.parseBoolean(strings[i]);
result[i] = Boolean.parseBoolean(strings[i]);
}
return result;
}
public static String getStringFromInputStream(InputStream is) throws IOException{
public static String getStringFromInputStream(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) != -1) {
baos.write(buffer, 0, length);
}
return baos.toString("UTF-8");
}
}