1 /* 2 * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions are met: 5 * 6 * - Redistribution of source code must retain the above copyright notice, 7 * this list of conditions and the following disclaimer. 8 * 9 * - Redistribution in binary form must reproduce the above copyright notice, 10 * this list of conditions and the following disclaimer in the documentation 11 * and/or other materails provided with the distribution. 12 * 13 * Neither the name Sun Microsystems, Inc. or the names of the contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * This software is provided "AS IS," without a warranty of any kind. 18 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING 19 * ANY IMPLIED WARRANT OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR 20 * NON-INFRINGEMEN, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND 21 * ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS 22 * A RESULT OF USING, MODIFYING OR DESTRIBUTING THIS SOFTWARE OR ITS 23 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST 24 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, 25 * INCIDENTAL OR PUNITIVE DAMAGES. HOWEVER CAUSED AND REGARDLESS OF THE THEORY 26 * OF LIABILITY, ARISING OUT OF THE USE OF OUR INABILITY TO USE THIS SOFTWARE, 27 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 28 * 29 * You acknowledge that this software is not designed or intended for us in 30 * the design, construction, operation or maintenance of any nuclear facility 31 * 32 */ 33 package net.java.games.input; 34 35 import java.io.IOException; 36 import java.util.List; 37 import java.util.ArrayList; 38 import java.util.Map; 39 import java.util.Iterator; 40 import java.util.logging.Logger; 41 42 /** OSX HIDManager implementation 43 * @author elias 44 * @author gregorypierce 45 * @version 1.0 46 */ 47 final class OSXHIDDevice { 48 private final static Logger log = Logger.getLogger(OSXHIDDevice.class.getName()); 49 private final static int AXIS_DEFAULT_MIN_VALUE = 0; 50 private final static int AXIS_DEFAULT_MAX_VALUE = 64*1024; 51 52 private final static String kIOHIDTransportKey = "Transport"; 53 private final static String kIOHIDVendorIDKey = "VendorID"; 54 private final static String kIOHIDVendorIDSourceKey = "VendorIDSource"; 55 private final static String kIOHIDProductIDKey = "ProductID"; 56 private final static String kIOHIDVersionNumberKey = "VersionNumber"; 57 private final static String kIOHIDManufacturerKey = "Manufacturer"; 58 private final static String kIOHIDProductKey = "Product"; 59 private final static String kIOHIDSerialNumberKey = "SerialNumber"; 60 private final static String kIOHIDCountryCodeKey = "CountryCode"; 61 private final static String kIOHIDLocationIDKey = "LocationID"; 62 private final static String kIOHIDDeviceUsageKey = "DeviceUsage"; 63 private final static String kIOHIDDeviceUsagePageKey = "DeviceUsagePage"; 64 private final static String kIOHIDDeviceUsagePairsKey = "DeviceUsagePairs"; 65 private final static String kIOHIDPrimaryUsageKey = "PrimaryUsage"; 66 private final static String kIOHIDPrimaryUsagePageKey = "PrimaryUsagePage"; 67 private final static String kIOHIDMaxInputReportSizeKey = "MaxInputReportSize"; 68 private final static String kIOHIDMaxOutputReportSizeKey = "MaxOutputReportSize"; 69 private final static String kIOHIDMaxFeatureReportSizeKey = "MaxFeatureReportSize"; 70 71 private final static String kIOHIDElementKey = "Elements"; 72 73 private final static String kIOHIDElementCookieKey = "ElementCookie"; 74 private final static String kIOHIDElementTypeKey = "Type"; 75 private final static String kIOHIDElementCollectionTypeKey = "CollectionType"; 76 private final static String kIOHIDElementUsageKey = "Usage"; 77 private final static String kIOHIDElementUsagePageKey = "UsagePage"; 78 private final static String kIOHIDElementMinKey = "Min"; 79 private final static String kIOHIDElementMaxKey = "Max"; 80 private final static String kIOHIDElementScaledMinKey = "ScaledMin"; 81 private final static String kIOHIDElementScaledMaxKey = "ScaledMax"; 82 private final static String kIOHIDElementSizeKey = "Size"; 83 private final static String kIOHIDElementReportSizeKey = "ReportSize"; 84 private final static String kIOHIDElementReportCountKey = "ReportCount"; 85 private final static String kIOHIDElementReportIDKey = "ReportID"; 86 private final static String kIOHIDElementIsArrayKey = "IsArray"; 87 private final static String kIOHIDElementIsRelativeKey = "IsRelative"; 88 private final static String kIOHIDElementIsWrappingKey = "IsWrapping"; 89 private final static String kIOHIDElementIsNonLinearKey = "IsNonLinear"; 90 private final static String kIOHIDElementHasPreferredStateKey = "HasPreferredState"; 91 private final static String kIOHIDElementHasNullStateKey = "HasNullState"; 92 private final static String kIOHIDElementUnitKey = "Unit"; 93 private final static String kIOHIDElementUnitExponentKey = "UnitExponent"; 94 private final static String kIOHIDElementNameKey = "Name"; 95 private final static String kIOHIDElementValueLocationKey = "ValueLocation"; 96 private final static String kIOHIDElementDuplicateIndexKey = "DuplicateIndex"; 97 private final static String kIOHIDElementParentCollectionKey = "ParentCollection"; 98 99 private final long device_address; 100 private final long device_interface_address; 101 private final Map<String,?> properties; 102 103 private boolean released; 104 OSXHIDDevice(long device_address, long device_interface_address)105 public OSXHIDDevice(long device_address, long device_interface_address) throws IOException { 106 this.device_address = device_address; 107 this.device_interface_address = device_interface_address; 108 this.properties = getDeviceProperties(); 109 open(); 110 } 111 getPortType()112 public final Controller.PortType getPortType() { 113 String transport = (String)properties.get(kIOHIDTransportKey); 114 if (transport == null) 115 return Controller.PortType.UNKNOWN; 116 if (transport.equals("USB")) { 117 return Controller.PortType.USB; 118 } else { 119 return Controller.PortType.UNKNOWN; 120 } 121 } 122 getProductName()123 public final String getProductName() { 124 return (String)properties.get(kIOHIDProductKey); 125 } 126 createElementFromElementProperties(Map<String,?> element_properties)127 private final OSXHIDElement createElementFromElementProperties(Map<String,?> element_properties) { 128 /* long size = getLongFromProperties(element_properties, kIOHIDElementSizeKey); 129 // ignore elements that can't fit into the 32 bit value field of a hid event 130 if (size > 32) 131 return null;*/ 132 long element_cookie = getLongFromProperties(element_properties, kIOHIDElementCookieKey); 133 int element_type_id = getIntFromProperties(element_properties, kIOHIDElementTypeKey); 134 ElementType element_type = ElementType.map(element_type_id); 135 int min = (int)getLongFromProperties(element_properties, kIOHIDElementMinKey, AXIS_DEFAULT_MIN_VALUE); 136 int max = (int)getLongFromProperties(element_properties, kIOHIDElementMaxKey, AXIS_DEFAULT_MAX_VALUE); 137 /* long scaled_min = getLongFromProperties(element_properties, kIOHIDElementScaledMinKey, Long.MIN_VALUE); 138 long scaled_max = getLongFromProperties(element_properties, kIOHIDElementScaledMaxKey, Long.MAX_VALUE);*/ 139 UsagePair device_usage_pair = getUsagePair(); 140 boolean default_relative = device_usage_pair != null && (device_usage_pair.getUsage() == GenericDesktopUsage.POINTER || device_usage_pair.getUsage() == GenericDesktopUsage.MOUSE); 141 142 boolean is_relative = getBooleanFromProperties(element_properties, kIOHIDElementIsRelativeKey, default_relative); 143 /* boolean is_wrapping = getBooleanFromProperties(element_properties, kIOHIDElementIsWrappingKey); 144 boolean is_non_linear = getBooleanFromProperties(element_properties, kIOHIDElementIsNonLinearKey); 145 boolean has_preferred_state = getBooleanFromProperties(element_properties, kIOHIDElementHasPreferredStateKey); 146 boolean has_null_state = getBooleanFromProperties(element_properties, kIOHIDElementHasNullStateKey);*/ 147 int usage = getIntFromProperties(element_properties, kIOHIDElementUsageKey); 148 int usage_page = getIntFromProperties(element_properties, kIOHIDElementUsagePageKey); 149 UsagePair usage_pair = createUsagePair(usage_page, usage); 150 if (usage_pair == null || (element_type != ElementType.INPUT_MISC && element_type != ElementType.INPUT_BUTTON && element_type != ElementType.INPUT_AXIS)) { 151 //log.info("element_type = 0x" + element_type + " | usage = " + usage + " | usage_page = " + usage_page); 152 return null; 153 } else { 154 return new OSXHIDElement(this, usage_pair, element_cookie, element_type, min, max, is_relative); 155 } 156 } 157 158 @SuppressWarnings("unchecked") addElements(List<OSXHIDElement> elements, Map<String,?> properties)159 private final void addElements(List<OSXHIDElement> elements, Map<String,?> properties) { 160 Object[] elements_properties = (Object[])properties.get(kIOHIDElementKey); 161 if (elements_properties == null) 162 return; 163 for (int i = 0; i < elements_properties.length; i++) { 164 Map<String,?> element_properties = (Map<String,?>)elements_properties[i]; 165 OSXHIDElement element = createElementFromElementProperties(element_properties); 166 if (element != null) { 167 elements.add(element); 168 } 169 addElements(elements, element_properties); 170 } 171 } 172 getElements()173 public final List<OSXHIDElement> getElements() { 174 List<OSXHIDElement> elements = new ArrayList<>(); 175 addElements(elements, properties); 176 return elements; 177 } 178 getLongFromProperties(Map<String,?> properties, String key, long default_value)179 private final static long getLongFromProperties(Map<String,?> properties, String key, long default_value) { 180 Long long_obj = (Long)properties.get(key); 181 if (long_obj == null) 182 return default_value; 183 return long_obj.longValue(); 184 } 185 getBooleanFromProperties(Map<String,?> properties, String key, boolean default_value)186 private final static boolean getBooleanFromProperties(Map<String,?> properties, String key, boolean default_value) { 187 return getLongFromProperties(properties, key, default_value ? 1 : 0) != 0; 188 } 189 getIntFromProperties(Map<String,?> properties, String key)190 private final static int getIntFromProperties(Map<String,?> properties, String key) { 191 return (int)getLongFromProperties(properties, key); 192 } 193 getLongFromProperties(Map<String,?> properties, String key)194 private final static long getLongFromProperties(Map<String,?> properties, String key) { 195 Long long_obj = (Long)properties.get(key); 196 return long_obj.longValue(); 197 } 198 createUsagePair(int usage_page_id, int usage_id)199 private final static UsagePair createUsagePair(int usage_page_id, int usage_id) { 200 UsagePage usage_page = UsagePage.map(usage_page_id); 201 if (usage_page != null) { 202 Usage usage = usage_page.mapUsage(usage_id); 203 if (usage != null) 204 return new UsagePair(usage_page, usage); 205 } 206 return null; 207 } 208 getUsagePair()209 public final UsagePair getUsagePair() { 210 int usage_page_id = getIntFromProperties(properties, kIOHIDPrimaryUsagePageKey); 211 int usage_id = getIntFromProperties(properties, kIOHIDPrimaryUsageKey); 212 return createUsagePair(usage_page_id, usage_id); 213 } 214 dumpProperties()215 private final void dumpProperties() { 216 log.info(toString()); 217 dumpMap("", properties); 218 } 219 dumpArray(String prefix, Object[] array)220 private final static void dumpArray(String prefix, Object[] array) { 221 log.info(prefix + "{"); 222 for (int i = 0; i < array.length; i++) { 223 dumpObject(prefix + "\t", array[i]); 224 log.info(prefix + ","); 225 } 226 log.info(prefix + "}"); 227 } 228 dumpMap(String prefix, Map<String,?> map)229 private final static void dumpMap(String prefix, Map<String,?> map) { 230 Iterator<String> keys = map.keySet().iterator(); 231 while (keys.hasNext()) { 232 Object key = keys.next(); 233 Object value = map.get(key); 234 dumpObject(prefix, key); 235 dumpObject(prefix + "\t", value); 236 } 237 } 238 239 @SuppressWarnings("unchecked") dumpObject(String prefix, Object obj)240 private final static void dumpObject(String prefix, Object obj) { 241 if (obj instanceof Long) { 242 Long l = (Long)obj; 243 log.info(prefix + "0x" + Long.toHexString(l.longValue())); 244 } else if (obj instanceof Map) 245 dumpMap(prefix, (Map<String,?>)obj); 246 else if (obj.getClass().isArray()) 247 dumpArray(prefix, (Object[])obj); 248 else 249 log.info(prefix + obj); 250 } 251 getDeviceProperties()252 private final Map<String,?> getDeviceProperties() throws IOException { 253 return nGetDeviceProperties(device_address); 254 } nGetDeviceProperties(long device_address)255 private final static native Map<String,?> nGetDeviceProperties(long device_address) throws IOException; 256 release()257 public final synchronized void release() throws IOException { 258 try { 259 close(); 260 } finally { 261 released = true; 262 nReleaseDevice(device_address, device_interface_address); 263 } 264 } nReleaseDevice(long device_address, long device_interface_address)265 private final static native void nReleaseDevice(long device_address, long device_interface_address); 266 getElementValue(long element_cookie, OSXEvent event)267 public final synchronized void getElementValue(long element_cookie, OSXEvent event) throws IOException { 268 checkReleased(); 269 nGetElementValue(device_interface_address, element_cookie, event); 270 } nGetElementValue(long device_interface_address, long element_cookie, OSXEvent event)271 private final static native void nGetElementValue(long device_interface_address, long element_cookie, OSXEvent event) throws IOException; 272 createQueue(int queue_depth)273 public final synchronized OSXHIDQueue createQueue(int queue_depth) throws IOException { 274 checkReleased(); 275 long queue_address = nCreateQueue(device_interface_address); 276 return new OSXHIDQueue(queue_address, queue_depth); 277 } nCreateQueue(long device_interface_address)278 private final static native long nCreateQueue(long device_interface_address) throws IOException; 279 open()280 private final void open() throws IOException { 281 nOpen(device_interface_address); 282 } nOpen(long device_interface_address)283 private final static native void nOpen(long device_interface_address) throws IOException; 284 close()285 private final void close() throws IOException { 286 nClose(device_interface_address); 287 } nClose(long device_interface_address)288 private final static native void nClose(long device_interface_address) throws IOException; 289 checkReleased()290 private final void checkReleased() throws IOException { 291 if (released) 292 throw new IOException(); 293 } 294 } 295