1 /* 2 * %W% %E% 3 * 4 * Copyright 2002 Sun Microsystems, Inc. All rights reserved. 5 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 6 */ 7 /***************************************************************************** 8 * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions are met: 11 * 12 * - Redistribution of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * - Redistribution in binary form must reproduce the above copyright notice, 16 * this list of conditions and the following disclaimer in the documentation 17 * and/or other materails provided with the distribution. 18 * 19 * Neither the name Sun Microsystems, Inc. or the names of the contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * This software is provided "AS IS," without a warranty of any kind. 24 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING 25 * ANY IMPLIED WARRANT OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR 26 * NON-INFRINGEMEN, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND 27 * ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS 28 * A RESULT OF USING, MODIFYING OR DESTRIBUTING THIS SOFTWARE OR ITS 29 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST 30 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, 31 * INCIDENTAL OR PUNITIVE DAMAGES. HOWEVER CAUSED AND REGARDLESS OF THE THEORY 32 * OF LIABILITY, ARISING OUT OF THE USE OF OUR INABILITY TO USE THIS SOFTWARE, 33 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 34 * 35 * You acknowledge that this software is not designed or intended for us in 36 * the design, construction, operation or maintenance of any nuclear facility 37 * 38 *****************************************************************************/ 39 package net.java.games.input; 40 41 import java.security.AccessController; 42 import java.security.PrivilegedAction; 43 import java.util.List; 44 import java.util.ArrayList; 45 import java.io.File; 46 import java.io.IOException; 47 48 import net.java.games.util.plugins.Plugin; 49 50 /** DirectInput implementation of controller environment 51 * @author martak 52 * @author elias 53 * @version 1.0 54 */ 55 public final class RawInputEnvironmentPlugin extends ControllerEnvironment implements Plugin { 56 57 private static boolean supported = false; 58 59 /** 60 * Static utility method for loading native libraries. 61 * It will try to load from either the path given by 62 * the net.java.games.input.librarypath property 63 * or through System.loadLibrary(). 64 * 65 */ loadLibrary(final String lib_name)66 static void loadLibrary(final String lib_name) { 67 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 68 try { 69 String lib_path = System.getProperty("net.java.games.input.librarypath"); 70 if (lib_path != null) 71 System.load(lib_path + File.separator + System.mapLibraryName(lib_name)); 72 else 73 System.loadLibrary(lib_name); 74 } catch (UnsatisfiedLinkError e) { 75 e.printStackTrace(); 76 supported = false; 77 } 78 return null; 79 }); 80 } 81 getPrivilegedProperty(final String property)82 static String getPrivilegedProperty(final String property) { 83 return AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getProperty(property)); 84 } 85 86 getPrivilegedProperty(final String property, final String default_value)87 static String getPrivilegedProperty(final String property, final String default_value) { 88 return AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getProperty(property, default_value)); 89 } 90 91 static { 92 String osName = getPrivilegedProperty("os.name", "").trim(); 93 if(osName.startsWith("Windows")) { 94 supported = true; 95 if("x86".equals(getPrivilegedProperty("os.arch"))) { 96 loadLibrary("jinput-raw"); 97 } else { 98 loadLibrary("jinput-raw_64"); 99 } 100 } 101 } 102 103 private final Controller[] controllers; 104 105 /** Creates new DirectInputEnvironment */ RawInputEnvironmentPlugin()106 public RawInputEnvironmentPlugin() { 107 RawInputEventQueue queue; 108 Controller[] controllers = new Controller[]{}; 109 if(isSupported()) { 110 try { 111 queue = new RawInputEventQueue(); 112 controllers = enumControllers(queue); 113 } catch (IOException e) { 114 log("Failed to enumerate devices: " + e.getMessage()); 115 } 116 } 117 this.controllers = controllers; 118 } 119 getControllers()120 public final Controller[] getControllers() { 121 return controllers; 122 } 123 lookupSetupAPIDevice(String device_name, List<SetupAPIDevice> setupapi_devices)124 private final static SetupAPIDevice lookupSetupAPIDevice(String device_name, List<SetupAPIDevice> setupapi_devices) { 125 /* First, replace # with / in the device name, since that 126 * seems to be the format in raw input device name 127 */ 128 device_name = device_name.replaceAll("#", "\\\\").toUpperCase(); 129 for (int i = 0; i < setupapi_devices.size(); i++) { 130 SetupAPIDevice device = setupapi_devices.get(i); 131 if (device_name.contains(device.getInstanceId().toUpperCase())) 132 return device; 133 } 134 return null; 135 } 136 createControllersFromDevices(RawInputEventQueue queue, List<Controller> controllers, List<RawDevice> devices, List<SetupAPIDevice> setupapi_devices)137 private final static void createControllersFromDevices(RawInputEventQueue queue, List<Controller> controllers, List<RawDevice> devices, List<SetupAPIDevice> setupapi_devices) throws IOException { 138 List<RawDevice> active_devices = new ArrayList<>(); 139 for (int i = 0; i < devices.size(); i++) { 140 RawDevice device = devices.get(i); 141 SetupAPIDevice setupapi_device = lookupSetupAPIDevice(device.getName(), setupapi_devices); 142 if (setupapi_device == null) { 143 /* Either the device is an RDP or we failed to locate the 144 * SetupAPI device that matches 145 */ 146 continue; 147 } 148 RawDeviceInfo info = device.getInfo(); 149 Controller controller = info.createControllerFromDevice(device, setupapi_device); 150 if (controller != null) { 151 controllers.add(controller); 152 active_devices.add(device); 153 } 154 } 155 queue.start(active_devices); 156 } 157 enumerateDevices(RawInputEventQueue queue, List<RawDevice> devices)158 private final static native void enumerateDevices(RawInputEventQueue queue, List<RawDevice> devices) throws IOException; 159 enumControllers(RawInputEventQueue queue)160 private final Controller[] enumControllers(RawInputEventQueue queue) throws IOException { 161 List<Controller> controllers = new ArrayList<>(); 162 List<RawDevice> devices = new ArrayList<>(); 163 enumerateDevices(queue, devices); 164 List<SetupAPIDevice> setupapi_devices = enumSetupAPIDevices(); 165 createControllersFromDevices(queue, controllers, devices, setupapi_devices); 166 Controller[] controllers_array = new Controller[controllers.size()]; 167 controllers.toArray(controllers_array); 168 return controllers_array; 169 } 170 isSupported()171 public boolean isSupported() { 172 return supported; 173 } 174 175 /* 176 * The raw input API, while being able to access 177 * multiple mice and keyboards, is a bit raw (hah) 178 * since it lacks some important features: 179 * 180 * 1. The list of keyboards and the list of mice 181 * both include useless Terminal Server 182 * devices (RDP_MOU and RDP_KEY) that we'd 183 * like to skip. 184 * 2. The device names returned by GetRawInputDeviceInfo() 185 * are not for display, but instead synthesized 186 * from a combination of a device instance id 187 * and a GUID. 188 * 189 * A solution to both problems is the SetupAPI that allows 190 * us to enumerate all keyboard and mouse devices and fetch their 191 * descriptive names and at the same time filter out the unwanted 192 * RDP devices. 193 */ enumSetupAPIDevices()194 private final static List<SetupAPIDevice> enumSetupAPIDevices() throws IOException { 195 List<SetupAPIDevice> devices = new ArrayList<>(); 196 nEnumSetupAPIDevices(getKeyboardClassGUID(), devices); 197 nEnumSetupAPIDevices(getMouseClassGUID(), devices); 198 return devices; 199 } nEnumSetupAPIDevices(byte[] guid, List<SetupAPIDevice> devices)200 private final static native void nEnumSetupAPIDevices(byte[] guid, List<SetupAPIDevice> devices) throws IOException; 201 getKeyboardClassGUID()202 private final static native byte[] getKeyboardClassGUID(); getMouseClassGUID()203 private final static native byte[] getMouseClassGUID(); 204 205 } // class DirectInputEnvironment 206