2017-02-05 22:04:09 +01:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright ( c ) 2017 Ash ( QuarkTheAwesome ) & Maschell
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
package net.ash.HIDToVPADNetworkClient.manager ;
import java.io.File ;
import java.io.FilenameFilter ;
2017-03-16 19:33:50 +01:00
import java.io.IOException ;
2017-04-13 15:32:55 +02:00
import java.nio.BufferUnderflowException ;
2017-02-05 22:04:09 +01:00
import java.util.ArrayList ;
import java.util.HashMap ;
import java.util.List ;
import java.util.Map ;
import java.util.Map.Entry ;
import com.ivan.xinput.XInputDevice ;
import com.ivan.xinput.XInputDevice14 ;
import com.ivan.xinput.exceptions.XInputNotLoadedException ;
import lombok.Synchronized ;
2017-03-31 07:41:50 +02:00
import lombok.extern.java.Log ;
2017-02-05 22:04:09 +01:00
import net.ash.HIDToVPADNetworkClient.controller.Controller ;
import net.ash.HIDToVPADNetworkClient.controller.Controller.ControllerType ;
2017-04-05 11:44:21 +02:00
import net.ash.HIDToVPADNetworkClient.controller.HidController ;
2017-02-05 22:04:09 +01:00
import net.ash.HIDToVPADNetworkClient.controller.LinuxDevInputController ;
import net.ash.HIDToVPADNetworkClient.controller.XInput13Controller ;
import net.ash.HIDToVPADNetworkClient.controller.XInput14Controller ;
import net.ash.HIDToVPADNetworkClient.controller.XInputController ;
import net.ash.HIDToVPADNetworkClient.exeption.ControllerInitializationFailedException ;
2017-04-05 18:14:58 +02:00
import net.ash.HIDToVPADNetworkClient.hid.HidDevice ;
2017-04-05 11:44:21 +02:00
import net.ash.HIDToVPADNetworkClient.hid.HidManager ;
2017-03-31 13:00:33 +02:00
import net.ash.HIDToVPADNetworkClient.util.MessageBox ;
2017-03-31 14:34:09 +02:00
import net.ash.HIDToVPADNetworkClient.util.MessageBoxManager ;
2017-03-23 22:56:32 +01:00
import net.ash.HIDToVPADNetworkClient.util.Settings ;
2017-02-05 22:04:09 +01:00
2017-03-31 07:41:50 +02:00
@Log
2017-03-31 15:31:41 +02:00
public final class ControllerManager {
2017-04-03 16:55:39 +02:00
private static final Map < String , Controller > attachedControllers = new HashMap < String , Controller > ( ) ;
2017-03-23 22:01:46 +01:00
2017-03-31 16:01:52 +02:00
private static boolean threwUnsatisfiedLinkError = false ;
2017-03-31 15:31:41 +02:00
private ControllerManager ( ) {
// Utility Class
}
2017-03-23 22:01:46 +01:00
/ * *
* Detects all attached controller .
* /
2017-04-03 16:55:39 +02:00
2017-02-05 22:04:09 +01:00
public static void detectControllers ( ) {
2017-03-23 22:01:46 +01:00
Map < String , ControllerType > connectedDevices = new HashMap < String , ControllerType > ( ) ;
2017-03-23 22:56:32 +01:00
if ( Settings . isLinux ( ) ) {
2017-02-05 22:04:09 +01:00
connectedDevices . putAll ( detectLinuxControllers ( ) ) ;
2017-03-23 22:56:32 +01:00
} else if ( Settings . isWindows ( ) ) {
2017-04-05 19:40:16 +02:00
connectedDevices . putAll ( detectXInputControllers ( ) ) ;
2017-02-05 22:04:09 +01:00
}
2017-03-23 22:01:46 +01:00
2017-03-23 22:56:32 +01:00
connectedDevices . putAll ( detectHIDDevices ( ) ) ;
2017-03-23 22:01:46 +01:00
// Remove detached devices
2017-03-21 10:42:26 +01:00
List < String > toRemove = new ArrayList < String > ( ) ;
2017-04-03 16:55:39 +02:00
synchronized ( attachedControllers ) {
for ( String s : attachedControllers . keySet ( ) ) {
if ( ! connectedDevices . containsKey ( s ) ) {
toRemove . add ( s ) ;
}
2017-02-05 22:04:09 +01:00
}
}
2017-04-03 16:55:39 +02:00
2017-03-23 22:01:46 +01:00
for ( String remove : toRemove ) {
2017-04-03 16:55:39 +02:00
synchronized ( attachedControllers ) {
attachedControllers . get ( remove ) . destroyAll ( ) ;
attachedControllers . remove ( remove ) ;
2017-04-05 20:07:02 +02:00
log . info ( " Device removed: " + toRemove ) ;
2017-04-03 16:55:39 +02:00
}
2017-02-05 22:04:09 +01:00
}
2017-03-23 22:01:46 +01:00
// Add attached devices!
for ( Entry < String , ControllerType > entry : connectedDevices . entrySet ( ) ) {
2017-02-05 22:04:09 +01:00
String deviceIdentifier = entry . getKey ( ) ;
2017-04-03 16:55:39 +02:00
boolean contains = false ;
synchronized ( attachedControllers ) {
contains = attachedControllers . containsKey ( deviceIdentifier ) ;
}
if ( ! contains ) {
2017-02-05 22:04:09 +01:00
Controller c = null ;
2017-03-23 22:01:46 +01:00
switch ( entry . getValue ( ) ) {
2017-04-05 11:44:21 +02:00
case HIDController :
2017-03-23 22:01:46 +01:00
try {
2017-04-05 11:44:21 +02:00
c = HidController . getInstance ( deviceIdentifier ) ;
2017-03-23 22:01:46 +01:00
} catch ( ControllerInitializationFailedException e ) {
2017-04-05 19:40:16 +02:00
log . info ( e . getMessage ( ) ) ;
2017-03-23 22:01:46 +01:00
} catch ( IOException e ) {
e . printStackTrace ( ) ;
}
break ;
case LINUX :
try {
c = new LinuxDevInputController ( deviceIdentifier ) ;
} catch ( ControllerInitializationFailedException e ) {
2017-04-05 19:40:16 +02:00
log . info ( e . getMessage ( ) ) ;
2017-03-23 22:01:46 +01:00
}
2017-03-27 20:40:44 +02:00
break ;
2017-03-23 22:01:46 +01:00
case XINPUT14 :
try {
c = new XInput14Controller ( deviceIdentifier ) ;
} catch ( ControllerInitializationFailedException e ) {
2017-04-05 19:40:16 +02:00
log . info ( e . getMessage ( ) ) ;
2017-03-23 22:01:46 +01:00
}
break ;
case XINPUT13 :
try {
2017-03-27 20:40:44 +02:00
c = new XInput13Controller ( deviceIdentifier ) ;
2017-03-23 22:01:46 +01:00
} catch ( ControllerInitializationFailedException e ) {
// e.printStackTrace();
}
break ;
2017-02-05 22:04:09 +01:00
default :
break ;
2017-03-23 22:01:46 +01:00
}
2017-03-26 22:35:23 +02:00
if ( c ! = null ) { // I don't like that starting the Thread happens here =/
2017-03-27 20:40:44 +02:00
if ( Settings . AUTO_ACTIVATE_CONTROLLER ) {
2017-03-27 20:37:22 +02:00
c . setActive ( true ) ;
}
2017-04-05 20:07:02 +02:00
new Thread ( c , " Controller Thread " + deviceIdentifier ) . start ( ) ;
2017-04-03 16:55:39 +02:00
synchronized ( attachedControllers ) {
attachedControllers . put ( deviceIdentifier , c ) ;
}
2017-04-05 20:07:02 +02:00
log . info ( " Device added: " + deviceIdentifier ) ;
2017-02-05 22:04:09 +01:00
}
}
}
}
2017-03-23 22:01:46 +01:00
2017-02-05 22:04:09 +01:00
@Synchronized ( " attachedControllers " )
public static List < Controller > getAttachedControllers ( ) {
2017-03-21 10:42:26 +01:00
return new ArrayList < Controller > ( attachedControllers . values ( ) ) ;
2017-02-05 22:04:09 +01:00
}
2017-03-26 23:00:23 +02:00
2017-02-05 22:04:09 +01:00
private static Map < String , ControllerType > detectHIDDevices ( ) {
2017-03-23 22:01:46 +01:00
Map < String , ControllerType > connectedDevices = new HashMap < String , ControllerType > ( ) ;
2017-04-05 20:38:03 +02:00
for ( HidDevice info : HidManager . getAttachedControllers ( ) ) {
2017-03-23 22:56:32 +01:00
String path = info . getPath ( ) ;
2017-04-05 11:44:21 +02:00
connectedDevices . put ( path , ControllerType . HIDController ) ;
2017-03-21 10:42:26 +01:00
}
2017-03-23 22:01:46 +01:00
2017-03-21 10:42:26 +01:00
return connectedDevices ;
}
2017-02-05 22:04:09 +01:00
2017-04-05 19:40:16 +02:00
private static Map < String , ControllerType > detectXInputControllers ( ) {
2017-03-23 22:01:46 +01:00
Map < String , ControllerType > result = new HashMap < String , ControllerType > ( ) ;
2017-04-13 05:08:27 +02:00
if ( ! Settings . ControllerFiltering . getFilterState ( Settings . ControllerFiltering . Type . XINPUT ) ) return result ;
2017-04-13 15:32:55 +02:00
2017-02-05 22:04:09 +01:00
ControllerType type = ControllerType . XINPUT13 ;
2017-03-31 14:34:09 +02:00
// Try and catch missing C++ redist
2017-03-31 07:41:50 +02:00
try {
2017-03-31 14:34:09 +02:00
XInputDevice . isAvailable ( ) ;
2017-03-31 07:41:50 +02:00
} catch ( UnsatisfiedLinkError e ) {
2017-03-31 14:34:09 +02:00
if ( ! threwUnsatisfiedLinkError ) {
e . printStackTrace ( ) ;
log . info ( " This error can be fixed! Please install the Visual C++ Redistributables: " ) ;
log . info ( " https://www.microsoft.com/en-us/download/details.aspx?id=48145 " ) ;
log . info ( " If that doesn't help, create an issue on GitHub. " ) ;
MessageBoxManager . addMessageBox (
" There was a problem setting up XInput. \ nTo fix this, try installing the Visual C++ \ nredistributables: https://tinyurl.com/vcredist2015. \ n \ nOther controller types should still work. " ,
MessageBox . MESSAGE_ERROR ) ;
threwUnsatisfiedLinkError = true ;
}
2017-03-31 07:41:50 +02:00
}
2017-03-31 14:34:09 +02:00
2017-03-23 22:01:46 +01:00
if ( XInputDevice . isAvailable ( ) | | XInputDevice14 . isAvailable ( ) ) {
if ( XInputDevice14 . isAvailable ( ) ) {
2017-02-05 22:04:09 +01:00
type = ControllerType . XINPUT14 ;
}
2017-03-23 22:01:46 +01:00
for ( int i = 0 ; i < 4 ; i + + ) {
2017-02-05 22:04:09 +01:00
XInputDevice device ;
try {
device = XInputDevice . getDeviceFor ( i ) ;
2017-04-13 15:32:55 +02:00
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. " ) ;
2017-02-05 22:04:09 +01:00
}
} catch ( XInputNotLoadedException e ) {
2017-03-23 22:01:46 +01:00
// This shouln't happen?
2017-02-05 22:04:09 +01:00
e . printStackTrace ( ) ;
}
}
}
return result ;
}
private static Map < String , ControllerType > detectLinuxControllers ( ) {
2017-03-23 22:01:46 +01:00
Map < String , ControllerType > result = new HashMap < String , ControllerType > ( ) ;
2017-04-13 04:03:35 +02:00
if ( ! Settings . ControllerFiltering . getFilterState ( Settings . ControllerFiltering . Type . LINUX ) ) return result ;
2017-04-13 15:32:55 +02:00
2017-02-05 22:04:09 +01:00
File devInput = new File ( " /dev/input " ) ;
2017-03-23 22:56:32 +01:00
if ( ! devInput . exists ( ) ) return result ;
2017-03-23 22:01:46 +01:00
2017-02-05 22:04:09 +01:00
File [ ] linuxControllers = devInput . listFiles ( new FilenameFilter ( ) {
public boolean accept ( File dir , String name ) {
2017-03-23 22:01:46 +01:00
return name . startsWith ( " js " ) ; // js0, js1, etc...
2017-02-05 22:04:09 +01:00
}
} ) ;
2017-03-23 22:01:46 +01:00
2017-02-05 22:04:09 +01:00
for ( File controller : linuxControllers ) {
2017-03-23 22:01:46 +01:00
result . put ( controller . getAbsolutePath ( ) , ControllerType . LINUX ) ;
2017-02-05 22:04:09 +01:00
}
2017-03-23 22:01:46 +01:00
2017-02-05 22:04:09 +01:00
return result ;
}
2017-03-23 22:01:46 +01:00
2017-02-05 22:04:09 +01:00
public static List < Controller > getActiveControllers ( ) {
2017-03-21 10:42:26 +01:00
List < Controller > active = new ArrayList < Controller > ( ) ;
2017-04-03 16:55:39 +02:00
List < Controller > attached = getAttachedControllers ( ) ;
for ( Controller c : attached ) {
2017-03-23 22:01:46 +01:00
if ( c . isActive ( ) ) {
2017-02-05 22:04:09 +01:00
active . add ( c ) ;
}
}
return active ;
}
public static void deactivateAllAttachedControllers ( ) {
2017-04-03 16:55:39 +02:00
List < Controller > attached = getAttachedControllers ( ) ;
for ( Controller c : attached ) {
2017-02-05 22:04:09 +01:00
c . setActive ( false ) ;
}
}
}