The future is now. Read Hardware inputs from devices plugged into a computer, cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
jinput2.10/src/plugins/linux/net/java/games/input/LinuxEnvironmentPlugin.java

519 lines
22 KiB

/*
* Copyright (C) 2003 Jeremy Booth (jeremy@newdawnsoftware.com)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. Redistributions in binary
* form must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials provided
* with the distribution.
* The name of the author may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
*/
package net.java.games.input;
import net.java.games.util.plugins.Plugin;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.ArrayList;
import java.io.IOException;
import java.io.File;
import java.io.FilenameFilter;
import java.security.AccessController;
import java.security.PrivilegedAction;
/** Environment plugin for linux
* @author elias
* @author Jeremy Booth (jeremy@newdawnsoftware.com)
*/
public final class LinuxEnvironmentPlugin extends ControllerEnvironment implements Plugin {
private final static String LIBNAME = "jinput-linux";
private final static String POSTFIX64BIT = "64";
private static boolean supported = false;
private final Controller[] controllers;
private final List<LinuxJoystickDevice> joystickDevices = new ArrayList<>();
private final List<LinuxEventDevice> eventDevices = new ArrayList<>();
private final static LinuxDeviceThread device_thread = new LinuxDeviceThread();
/**
* Static utility method for loading native libraries.
* It will try to load from either the path given by
* the net.java.games.input.librarypath property
* or through System.loadLibrary().
*
*/
static void loadLibrary(final String lib_name) {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
String lib_path = System.getProperty("net.java.games.input.librarypath");
try {
if(lib_path != null)
System.load(lib_path + File.separator + System.mapLibraryName(lib_name));
else
System.loadLibrary(lib_name);
} catch(UnsatisfiedLinkError e) {
log("Failed to load library: " + e.getMessage());
e.printStackTrace();
supported = false;
}
return null;
});
}
static String getPrivilegedProperty(final String property) {
return AccessController.doPrivileged((PrivilegedAction<String>)() -> System.getProperty(property));
}
static String getPrivilegedProperty(final String property, final String default_value) {
return AccessController.doPrivileged((PrivilegedAction<String>)() -> System.getProperty(property, default_value));
}
static {
String osName = getPrivilegedProperty("os.name", "").trim();
if(osName.equals("Linux")) {
supported = true;
if("i386".equals(getPrivilegedProperty("os.arch"))) {
loadLibrary(LIBNAME);
} else {
loadLibrary(LIBNAME + POSTFIX64BIT);
}
}
}
public final static Object execute(LinuxDeviceTask task) throws IOException {
return device_thread.execute(task);
}
public LinuxEnvironmentPlugin() {
if(isSupported()) {
this.controllers = enumerateControllers();
log("Linux plugin claims to have found " + controllers.length + " controllers");
AccessController.doPrivileged((PrivilegedAction<Void>)() -> {
Runtime.getRuntime().addShutdownHook(new ShutdownHook());
return null;
});
} else {
controllers = new Controller[0];
}
}
/** Returns a list of all controllers available to this environment,
* or an empty array if there are no controllers in this environment.
* @return Returns a list of all controllers available to this environment,
* or an empty array if there are no controllers in this environment.
*/
public final Controller[] getControllers() {
return controllers;
}
public final Controller[] rescanControllers() {
Controller[] newControllers = enumerateControllers();
log("Linux plugin claims to have found " + newControllers.length + " controllers");
return newControllers;
}
private final static Component[] createComponents(List<LinuxEventComponent> event_components, LinuxEventDevice device) {
LinuxEventComponent[][] povs = new LinuxEventComponent[4][2];
List<LinuxComponent> components = new ArrayList<>();
for(int i = 0; i < event_components.size(); i++) {
LinuxEventComponent event_component = event_components.get(i);
Component.Identifier identifier = event_component.getIdentifier();
if(identifier == Component.Identifier.Axis.POV) {
int native_code = event_component.getDescriptor().getCode();
switch(native_code) {
case NativeDefinitions.ABS_HAT0X:
povs[0][0] = event_component;
break;
case NativeDefinitions.ABS_HAT0Y:
povs[0][1] = event_component;
break;
case NativeDefinitions.ABS_HAT1X:
povs[1][0] = event_component;
break;
case NativeDefinitions.ABS_HAT1Y:
povs[1][1] = event_component;
break;
case NativeDefinitions.ABS_HAT2X:
povs[2][0] = event_component;
break;
case NativeDefinitions.ABS_HAT2Y:
povs[2][1] = event_component;
break;
case NativeDefinitions.ABS_HAT3X:
povs[3][0] = event_component;
break;
case NativeDefinitions.ABS_HAT3Y:
povs[3][1] = event_component;
break;
default:
log("Unknown POV instance: " + native_code);
break;
}
} else if(identifier != null) {
LinuxComponent component = new LinuxComponent(event_component);
components.add(component);
device.registerComponent(event_component.getDescriptor(), component);
}
}
for(int i = 0; i < povs.length; i++) {
LinuxEventComponent x = povs[i][0];
LinuxEventComponent y = povs[i][1];
if(x != null && y != null) {
LinuxComponent controller_component = new LinuxPOV(x, y);
components.add(controller_component);
device.registerComponent(x.getDescriptor(), controller_component);
device.registerComponent(y.getDescriptor(), controller_component);
}
}
Component[] components_array = new Component[components.size()];
components.toArray(components_array);
return components_array;
}
private final static Mouse createMouseFromDevice(LinuxEventDevice device, Component[] components) throws IOException {
Mouse mouse = new LinuxMouse(device, components, new Controller[]{}, device.getRumblers());
if(mouse.getX() != null && mouse.getY() != null && mouse.getPrimaryButton() != null)
return mouse;
else
return null;
}
private final static Keyboard createKeyboardFromDevice(LinuxEventDevice device, Component[] components) throws IOException {
Keyboard keyboard = new LinuxKeyboard(device, components, new Controller[]{}, device.getRumblers());
return keyboard;
}
private final static Controller createJoystickFromDevice(LinuxEventDevice device, Component[] components, Controller.Type type) throws IOException {
Controller joystick = new LinuxAbstractController(device, components, new Controller[]{}, device.getRumblers(), type);
return joystick;
}
private final static Controller createControllerFromDevice(LinuxEventDevice device) throws IOException {
List<LinuxEventComponent> event_components = device.getComponents();
Component[] components = createComponents(event_components, device);
Controller.Type type = device.getType();
if(type == Controller.Type.MOUSE) {
return createMouseFromDevice(device, components);
} else if(type == Controller.Type.KEYBOARD) {
return createKeyboardFromDevice(device, components);
} else if(type == Controller.Type.STICK || type == Controller.Type.GAMEPAD) {
return createJoystickFromDevice(device, components, type);
} else
return null;
}
private final Controller[] enumerateControllers() {
List<Controller> controllers = new ArrayList<>();
List<Controller> eventControllers = new ArrayList<>();
List<Controller> jsControllers = new ArrayList<>();
enumerateEventControllers(eventControllers);
enumerateJoystickControllers(jsControllers);
for(int i = 0; i < eventControllers.size(); i++) {
for(int j = 0; j < jsControllers.size(); j++) {
Controller evController = eventControllers.get(i);
Controller jsController = jsControllers.get(j);
// compare
// Check if the nodes have the same name
if(evController.getName().equals(jsController.getName())) {
System.out.println("Same names. "+jsController.getName());
// Check they have the same component count
Component[] evComponents = evController.getComponents();
Component[] jsComponents = jsController.getComponents();
if(evComponents.length == jsComponents.length) {
System.out.println("Same component count."+evComponents.length);
boolean foundADifference = false;
// check the component pairs are of the same type
for(int k = 0; k < evComponents.length; k++) {
// Check the type of the component is the same
if(!(evComponents[k].getIdentifier() == jsComponents[k].getIdentifier())) {
System.out.println("Differing components: "+evComponents[k].getIdentifier()+"//"+jsComponents[k].getIdentifier());
foundADifference = true;
}
}
if(!foundADifference) {
controllers.add(new LinuxCombinedController((LinuxAbstractController) eventControllers.remove(i), (LinuxJoystickAbstractController) jsControllers.remove(j)));
i--;
j--;
break;
}
}
}
}
}
controllers.addAll(eventControllers);
controllers.addAll(jsControllers);
Controller[] controllers_array = new Controller[controllers.size()];
controllers.toArray(controllers_array);
return controllers_array;
}
private final static Component.Identifier.Button getButtonIdentifier(int index) {
switch(index) {
case 0:
return Component.Identifier.Button._0;
case 1:
return Component.Identifier.Button._1;
case 2:
return Component.Identifier.Button._2;
case 3:
return Component.Identifier.Button._3;
case 4:
return Component.Identifier.Button._4;
case 5:
return Component.Identifier.Button._5;
case 6:
return Component.Identifier.Button._6;
case 7:
return Component.Identifier.Button._7;
case 8:
return Component.Identifier.Button._8;
case 9:
return Component.Identifier.Button._9;
case 10:
return Component.Identifier.Button._10;
case 11:
return Component.Identifier.Button._11;
case 12:
return Component.Identifier.Button._12;
case 13:
return Component.Identifier.Button._13;
case 14:
return Component.Identifier.Button._14;
case 15:
return Component.Identifier.Button._15;
case 16:
return Component.Identifier.Button._16;
case 17:
return Component.Identifier.Button._17;
case 18:
return Component.Identifier.Button._18;
case 19:
return Component.Identifier.Button._19;
case 20:
return Component.Identifier.Button._20;
case 21:
return Component.Identifier.Button._21;
case 22:
return Component.Identifier.Button._22;
case 23:
return Component.Identifier.Button._23;
case 24:
return Component.Identifier.Button._24;
case 25:
return Component.Identifier.Button._25;
case 26:
return Component.Identifier.Button._26;
case 27:
return Component.Identifier.Button._27;
case 28:
return Component.Identifier.Button._28;
case 29:
return Component.Identifier.Button._29;
case 30:
return Component.Identifier.Button._30;
case 31:
return Component.Identifier.Button._31;
default:
return null;
}
}
private final static Controller createJoystickFromJoystickDevice(LinuxJoystickDevice device) {
List<AbstractComponent> components = new ArrayList<>();
byte[] axisMap = device.getAxisMap();
char[] buttonMap = device.getButtonMap();
LinuxJoystickAxis[] hatBits = new LinuxJoystickAxis[6];
for(int i = 0; i < device.getNumButtons(); i++) {
Component.Identifier button_id = LinuxNativeTypesMap.getButtonID(buttonMap[i]);
if(button_id != null) {
LinuxJoystickButton button = new LinuxJoystickButton(button_id);
device.registerButton(i, button);
components.add(button);
}
}
for(int i = 0; i < device.getNumAxes(); i++) {
Component.Identifier.Axis axis_id;
axis_id = (Component.Identifier.Axis) LinuxNativeTypesMap.getAbsAxisID(axisMap[i]);
LinuxJoystickAxis axis = new LinuxJoystickAxis(axis_id);
device.registerAxis(i, axis);
if(axisMap[i] == NativeDefinitions.ABS_HAT0X) {
hatBits[0] = axis;
} else if(axisMap[i] == NativeDefinitions.ABS_HAT0Y) {
hatBits[1] = axis;
axis = new LinuxJoystickPOV(Component.Identifier.Axis.POV, hatBits[0], hatBits[1]);
device.registerPOV((LinuxJoystickPOV) axis);
components.add(axis);
} else if(axisMap[i] == NativeDefinitions.ABS_HAT1X) {
hatBits[2] = axis;
} else if(axisMap[i] == NativeDefinitions.ABS_HAT1Y) {
hatBits[3] = axis;
axis = new LinuxJoystickPOV(Component.Identifier.Axis.POV, hatBits[2], hatBits[3]);
device.registerPOV((LinuxJoystickPOV) axis);
components.add(axis);
} else if(axisMap[i] == NativeDefinitions.ABS_HAT2X) {
hatBits[4] = axis;
} else if(axisMap[i] == NativeDefinitions.ABS_HAT2Y) {
hatBits[5] = axis;
axis = new LinuxJoystickPOV(Component.Identifier.Axis.POV, hatBits[4], hatBits[5]);
device.registerPOV((LinuxJoystickPOV) axis);
components.add(axis);
} else {
components.add(axis);
}
}
return new LinuxJoystickAbstractController(device, components.toArray(new Component[]{}), new Controller[]{}, new Rumbler[]{});
}
private final void enumerateJoystickControllers(List<Controller> controllers) {
File[] joystick_device_files = enumerateJoystickDeviceFiles("/dev/input");
if(joystick_device_files == null || joystick_device_files.length == 0) {
joystick_device_files = enumerateJoystickDeviceFiles("/dev");
if(joystick_device_files == null)
return;
}
for(int i = 0; i < joystick_device_files.length; i++) {
File event_file = joystick_device_files[i];
try {
String path = getAbsolutePathPrivileged(event_file);
LinuxJoystickDevice device;
if (joystickDevices.size()>i) {
device=joystickDevices.get(i);
if (device==null) {
device = new LinuxJoystickDevice(path);
joystickDevices.set(i,device);
}
} else {
device = new LinuxJoystickDevice(path);
joystickDevices.add(device);
}
Controller controller = createJoystickFromJoystickDevice(device);
if(controller != null) {
controllers.add(controller);
} else {
device.close();
joystickDevices.set(i,null);
}
} catch(IOException e) {
log("Failed to open device (" + event_file + "): " + e.getMessage());
}
}
}
private final static File[] enumerateJoystickDeviceFiles(final String dev_path) {
final File dev = new File(dev_path);
return listFilesPrivileged(dev, new FilenameFilter() {
public final boolean accept(File dir, String name) {
return name.startsWith("js");
}
});
}
private static String getAbsolutePathPrivileged(final File file) {
return AccessController.doPrivileged((PrivilegedAction<String>) () -> file.getAbsolutePath());
}
private static File[] listFilesPrivileged(final File dir, final FilenameFilter filter) {
return AccessController.doPrivileged((PrivilegedAction<File[]>) () -> {
File[] files = dir.listFiles(filter);
if(files == null) {
log("dir " + dir.getName() + " exists: " + dir.exists() + ", is writable: " + dir.isDirectory());
files = new File[]{};
} else {
Arrays.sort(files, Comparator.comparing(File::getName));
}
return files;
});
}
private final void enumerateEventControllers(List<Controller> controllers) {
final File dev = new File("/dev/input");
File[] event_device_files = listFilesPrivileged(dev, (File dir, String name) -> name.startsWith("event"));
if(event_device_files == null)
return;
for(int i = 0; i < event_device_files.length; i++) {
File event_file = event_device_files[i];
try {
String path = getAbsolutePathPrivileged(event_file);
LinuxEventDevice device;
//System.out.println(event_file+":"+i);
if (eventDevices.size()>i) {
device=eventDevices.get(i);
if (device==null) {
device = new LinuxEventDevice(path);
eventDevices.set(i,device);
}
} else {
device = new LinuxEventDevice(path);
eventDevices.add(device);
}
try {
Controller controller = createControllerFromDevice(device);
if(controller != null) {
controllers.add(controller);
} else {
device.close();
eventDevices.set(i,null);
}
} catch(IOException e) {
log("Failed to create Controller: " + e.getMessage());
device.close();
}
} catch(IOException e) {
log("Failed to open device (" + event_file + "): " + e.getMessage());
}
}
}
private final class ShutdownHook extends Thread {
public final void run() {
for(int i = 0; i < eventDevices.size(); i++) {
try {
LinuxEventDevice device = eventDevices.get(i);
device.close();
} catch(IOException e) {
log("Failed to close device: " + e.getMessage());
}
}
for(int i = 0; i < joystickDevices.size(); i++) {
try {
LinuxJoystickDevice device = joystickDevices.get(i);
device.close();
} catch(IOException e) {
log("Failed to close device: " + e.getMessage());
}
}
}
}
public boolean isSupported() {
return supported;
}
}