ZKeyLEDPort.java
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13: package xeij;
14:
15: import java.io.*;
16: import java.lang.foreign.*;
17: import java.lang.invoke.*;
18: import java.nio.charset.*;
19: import java.util.*;
20:
21: public final class ZKeyLEDPort implements AutoCloseable {
22:
23: private boolean debugFlag;
24: public void setDebugFlag (boolean debugFlag) {
25: this.debugFlag = debugFlag;
26: }
27:
28:
29:
30:
31:
32: private static final MemoryLayout GUID = MemoryLayout.structLayout (
33: ValueLayout.JAVA_INT.withName ("Data1"),
34: ValueLayout.JAVA_SHORT.withName ("Data2"),
35: ValueLayout.JAVA_SHORT.withName ("Data3"),
36: MemoryLayout.sequenceLayout (
37: 8,
38: ValueLayout.JAVA_BYTE).withName ("Data4")
39:
40: );
41:
42:
43:
44: private static final MemoryLayout HIDD_ATTRIBUTES = MemoryLayout.structLayout (
45: ValueLayout.JAVA_INT.withName ("Size"),
46: ValueLayout.JAVA_SHORT.withName ("VendorID"),
47: ValueLayout.JAVA_SHORT.withName ("ProductID"),
48: ValueLayout.JAVA_SHORT.withName ("VersionNumber"),
49: MemoryLayout.paddingLayout (2)
50:
51: );
52:
53:
54:
55: private static final MemoryLayout HIDP_CAPS = MemoryLayout.structLayout (
56: ValueLayout.JAVA_SHORT.withName ("Usage"),
57: ValueLayout.JAVA_SHORT.withName ("UsagePage"),
58: ValueLayout.JAVA_SHORT.withName ("InputReportByteLength"),
59: ValueLayout.JAVA_SHORT.withName ("OutputReportByteLength"),
60: ValueLayout.JAVA_SHORT.withName ("FeatureReportByteLength"),
61: MemoryLayout.sequenceLayout (
62: 17,
63: ValueLayout.JAVA_SHORT).withName ("Reserved"),
64: ValueLayout.JAVA_SHORT.withName ("NumberLinkCollectionNodes"),
65: ValueLayout.JAVA_SHORT.withName ("NumberInputButtonCaps"),
66: ValueLayout.JAVA_SHORT.withName ("NumberInputValueCaps"),
67: ValueLayout.JAVA_SHORT.withName ("NumberInputDataIndices"),
68: ValueLayout.JAVA_SHORT.withName ("NumberOutputButtonCaps"),
69: ValueLayout.JAVA_SHORT.withName ("NumberOutputValueCaps"),
70: ValueLayout.JAVA_SHORT.withName ("NumberOutputDataIndices"),
71: ValueLayout.JAVA_SHORT.withName ("NumberFeatureButtonCaps"),
72: ValueLayout.JAVA_SHORT.withName ("NumberFeatureValueCaps"),
73: ValueLayout.JAVA_SHORT.withName ("NumberFeatureDataIndices")
74:
75: );
76:
77:
78:
79: private static final int INPUT_KEYBOARD = 1;
80: private static final int KEYEVENTF_KEYUP = 0x0002;
81: private static final MemoryLayout INPUT = MemoryLayout.structLayout (
82: ValueLayout.JAVA_INT.withName ("type"),
83: MemoryLayout.paddingLayout (4),
84: MemoryLayout.unionLayout (
85:
86:
87: MemoryLayout.structLayout (
88: ValueLayout.JAVA_INT.withName ("dx"),
89: ValueLayout.JAVA_INT.withName ("dy"),
90: ValueLayout.JAVA_INT.withName ("mouseData"),
91: ValueLayout.JAVA_INT.withName ("dwFlags"),
92: ValueLayout.JAVA_INT.withName ("time"),
93: MemoryLayout.paddingLayout (4),
94: ValueLayout.ADDRESS.withName ("dwExtraInfo")
95:
96: ).withName ("mi"),
97:
98:
99: MemoryLayout.structLayout (
100: ValueLayout.JAVA_SHORT.withName ("wVk"),
101: ValueLayout.JAVA_SHORT.withName ("wScan"),
102: ValueLayout.JAVA_INT.withName ("dwFlags"),
103: ValueLayout.JAVA_INT.withName ("time"),
104: MemoryLayout.paddingLayout (4),
105: ValueLayout.ADDRESS.withName ("dwExtraInfo")
106:
107: ).withName ("ki"),
108:
109:
110: MemoryLayout.structLayout (
111: ValueLayout.JAVA_INT.withName ("uMsg"),
112: ValueLayout.JAVA_SHORT.withName ("wParamL"),
113: ValueLayout.JAVA_SHORT.withName ("wParamH")
114:
115: ).withName ("hi")
116: ).withName ("DUMMYUNIONNAME")
117:
118: );
119:
120:
121:
122: private static final MemoryLayout SP_DEVICE_INTERFACE_DATA = MemoryLayout.structLayout (
123: ValueLayout.JAVA_INT.withName ("cbSize"),
124: GUID.withName ("InterfaceClassGuid"),
125: ValueLayout.JAVA_INT.withName ("Flags"),
126: ValueLayout.ADDRESS.withName ("Reserved")
127:
128: );
129:
130:
131:
132: private static final int ANYSIZE_ARRAY = 1;
133: private static final MemoryLayout SP_DEVICE_INTERFACE_DETAIL_DATA_W = MemoryLayout.structLayout (
134: ValueLayout.JAVA_INT.withName ("cbSize"),
135: MemoryLayout.sequenceLayout (
136: ANYSIZE_ARRAY,
137: ValueLayout.JAVA_SHORT).withName ("DevicePath"),
138: MemoryLayout.paddingLayout (2)
139:
140: );
141:
142:
143:
144: private final int ERROR_INSUFFICIENT_BUFFER = 122;
145: private final int ERROR_NO_MORE_ITEMS = 259;
146:
147:
148: private Linker linker;
149: private MethodHandle downcallHandle (MemorySegment address, FunctionDescriptor function) {
150: return linker.downcallHandle (address, function);
151: }
152:
153:
154: private Arena arena;
155:
156:
157: private MethodHandle CloseHandle;
158: private static final int FILE_SHARE_READ = 0x00000001;
159: private static final int FILE_SHARE_WRITE = 0x00000002;
160: private static final int OPEN_EXISTING = 3;
161: private static final long INVALID_HANDLE_VALUE = -1L;
162: private MethodHandle CreateFileW;
163: private MethodHandle GetKeyState;
164: private MethodHandle GetLastError;
165: private MethodHandle HidD_FreePreparsedData;
166: private MethodHandle HidD_GetAttributes;
167: private MethodHandle HidD_GetHidGuid;
168: private MethodHandle HidD_GetPreparsedData;
169: private MethodHandle HidD_SetFeature;
170: private static final int HIDP_STATUS_SUCCESS = 0x0 << 28 | 0x11 << 16 | 0;
171: private MethodHandle HidP_GetCaps;
172: private MethodHandle SendInput;
173: private MethodHandle SetupDiDestroyDeviceInfoList;
174: private MethodHandle SetupDiEnumDeviceInterfaces;
175: private static final int DIGCF_PRESENT = 0x00000002;
176: private static final int DIGCF_DEVICEINTERFACE = 0x00000010;
177: private MethodHandle SetupDiGetClassDevsA;
178: private MethodHandle SetupDiGetDeviceInterfaceDetailW;
179:
180:
181: private MemorySegment handle;
182:
183:
184:
185: public ZKeyLEDPort (boolean debugFlag) throws IOException {
186: this.debugFlag = debugFlag;
187: if (debugFlag) {
188: System.out.println ("ZKeyLEDPort(\"" + debugFlag + "\")");
189: }
190:
191:
192: linker = Linker.nativeLinker ();
193:
194:
195: arena = Arena.ofAuto ();
196:
197:
198: SymbolLookup hid = SymbolLookup.libraryLookup ("hid", arena);
199: SymbolLookup kernel32 = SymbolLookup.libraryLookup ("kernel32", arena);
200: SymbolLookup setupapi = SymbolLookup.libraryLookup ("setupapi", arena);
201: SymbolLookup user32 = SymbolLookup.libraryLookup ("user32", arena);
202:
203:
204: try {
205:
206:
207:
208: CloseHandle = downcallHandle (
209: kernel32.findOrThrow ("CloseHandle"),
210: FunctionDescriptor.of (
211: ValueLayout.JAVA_INT,
212: ValueLayout.ADDRESS));
213:
214:
215:
216: CreateFileW = downcallHandle (
217: kernel32.findOrThrow ("CreateFileW"),
218: FunctionDescriptor.of (
219: ValueLayout.ADDRESS,
220: ValueLayout.ADDRESS,
221: ValueLayout.JAVA_INT,
222: ValueLayout.JAVA_INT,
223: ValueLayout.ADDRESS,
224: ValueLayout.JAVA_INT,
225: ValueLayout.JAVA_INT,
226: ValueLayout.ADDRESS));
227:
228:
229:
230: GetKeyState = downcallHandle (
231: user32.findOrThrow ("GetKeyState"),
232: FunctionDescriptor.of (
233: ValueLayout.JAVA_SHORT,
234: ValueLayout.JAVA_INT));
235:
236:
237:
238: GetLastError = downcallHandle (
239: kernel32.findOrThrow ("GetLastError"),
240: FunctionDescriptor.of (
241: ValueLayout.JAVA_INT));
242:
243:
244:
245: HidD_FreePreparsedData = downcallHandle (
246: hid.findOrThrow ("HidD_FreePreparsedData"),
247: FunctionDescriptor.of (
248: ValueLayout.JAVA_INT,
249: ValueLayout.ADDRESS));
250:
251:
252:
253: HidD_GetAttributes = downcallHandle (
254: hid.findOrThrow ("HidD_GetAttributes"),
255: FunctionDescriptor.of (
256: ValueLayout.JAVA_INT,
257: ValueLayout.ADDRESS,
258: ValueLayout.ADDRESS));
259:
260:
261:
262: HidD_GetHidGuid = downcallHandle (
263: hid.findOrThrow ("HidD_GetHidGuid"),
264: FunctionDescriptor.ofVoid (
265: ValueLayout.ADDRESS));
266:
267:
268:
269: HidD_GetPreparsedData = downcallHandle (
270: hid.findOrThrow ("HidD_GetPreparsedData"),
271: FunctionDescriptor.of (
272: ValueLayout.JAVA_INT,
273: ValueLayout.ADDRESS,
274: ValueLayout.ADDRESS));
275:
276:
277:
278: HidD_SetFeature = downcallHandle (
279: hid.findOrThrow ("HidD_SetFeature"),
280: FunctionDescriptor.of (
281: ValueLayout.JAVA_INT,
282: ValueLayout.ADDRESS,
283: ValueLayout.ADDRESS,
284: ValueLayout.JAVA_INT));
285:
286:
287:
288: HidP_GetCaps = downcallHandle (
289: hid.findOrThrow ("HidP_GetCaps"),
290: FunctionDescriptor.of (
291: ValueLayout.JAVA_INT,
292: ValueLayout.ADDRESS,
293: ValueLayout.ADDRESS));
294:
295:
296:
297: SendInput = downcallHandle (
298: user32.findOrThrow ("SendInput"),
299: FunctionDescriptor.of (
300: ValueLayout.JAVA_INT,
301: ValueLayout.JAVA_INT,
302: ValueLayout.ADDRESS,
303: ValueLayout.JAVA_INT));
304:
305:
306:
307: SetupDiDestroyDeviceInfoList = downcallHandle (
308: setupapi.findOrThrow ("SetupDiDestroyDeviceInfoList"),
309: FunctionDescriptor.of (
310: ValueLayout.JAVA_INT,
311: ValueLayout.ADDRESS));
312:
313:
314:
315: SetupDiEnumDeviceInterfaces = downcallHandle (
316: setupapi.findOrThrow ("SetupDiEnumDeviceInterfaces"),
317: FunctionDescriptor.of (
318: ValueLayout.JAVA_INT,
319: ValueLayout.ADDRESS,
320: ValueLayout.ADDRESS,
321: ValueLayout.ADDRESS,
322: ValueLayout.JAVA_INT,
323: ValueLayout.ADDRESS));
324:
325:
326:
327: SetupDiGetClassDevsA = downcallHandle (
328: setupapi.findOrThrow ("SetupDiGetClassDevsA"),
329: FunctionDescriptor.of (
330: ValueLayout.ADDRESS,
331: ValueLayout.ADDRESS,
332: ValueLayout.ADDRESS,
333: ValueLayout.ADDRESS,
334: ValueLayout.JAVA_INT));
335:
336:
337:
338: SetupDiGetDeviceInterfaceDetailW = downcallHandle (
339: setupapi.findOrThrow ("SetupDiGetDeviceInterfaceDetailW"),
340: FunctionDescriptor.of (
341: ValueLayout.JAVA_INT,
342: ValueLayout.ADDRESS,
343: ValueLayout.ADDRESS,
344: ValueLayout.ADDRESS,
345: ValueLayout.JAVA_INT,
346: ValueLayout.ADDRESS,
347: ValueLayout.ADDRESS));
348:
349: } catch (NoSuchElementException nsee) {
350: nsee.printStackTrace ();
351: }
352:
353: handle = null;
354: open ();
355: }
356:
357:
358:
359:
360:
361: @Override public void close () {
362:
363: if (handle == null || handle.address () == INVALID_HANDLE_VALUE) {
364: return;
365: }
366:
367: try {
368: int error;
369:
370: if ((int) CloseHandle.invoke (
371: handle) == 0 &&
372: (error = (int) GetLastError.invoke ()) != -1) {
373: if (debugFlag) {
374: System.out.printf ("close: CloseHandle failed (%d)\n", error);
375: }
376:
377: }
378: } catch (Throwable e) {
379: e.printStackTrace ();
380: if (debugFlag) {
381: System.out.printf ("close: CloseHandle invocation failed\n");
382: }
383:
384: }
385:
386: handle = null;
387: }
388:
389:
390:
391:
392:
393:
394: public void hitKey (int vk) {
395: MemorySegment pInputs = arena.allocate (INPUT.byteSize () * 2,
396: INPUT.byteAlignment ());
397: pInputs.fill ((byte) 0);
398: pInputs.set (ValueLayout.JAVA_INT,
399: INPUT.byteOffset (MemoryLayout.PathElement.groupElement ("type")),
400: INPUT_KEYBOARD);
401: pInputs.set (ValueLayout.JAVA_SHORT,
402: INPUT.byteOffset (MemoryLayout.PathElement.groupElement ("DUMMYUNIONNAME"),
403: MemoryLayout.PathElement.groupElement ("ki"),
404: MemoryLayout.PathElement.groupElement ("wVk")),
405: (short) vk);
406: pInputs.set (ValueLayout.JAVA_INT,
407: INPUT.byteSize () +
408: INPUT.byteOffset (MemoryLayout.PathElement.groupElement ("type")),
409: INPUT_KEYBOARD);
410: pInputs.set (ValueLayout.JAVA_SHORT,
411: INPUT.byteSize () +
412: INPUT.byteOffset (MemoryLayout.PathElement.groupElement ("DUMMYUNIONNAME"),
413: MemoryLayout.PathElement.groupElement ("ki"),
414: MemoryLayout.PathElement.groupElement ("wVk")),
415: (short) vk);
416: pInputs.set (ValueLayout.JAVA_INT,
417: INPUT.byteSize () +
418: INPUT.byteOffset (MemoryLayout.PathElement.groupElement ("DUMMYUNIONNAME"),
419: MemoryLayout.PathElement.groupElement ("ki"),
420: MemoryLayout.PathElement.groupElement ("dwFlags")),
421: KEYEVENTF_KEYUP);
422: try {
423: int error;
424:
425: if ((int) SendInput.invoke (
426: 2,
427: pInputs,
428: (int) INPUT.byteSize ()) == 0 &&
429: (error = (int) GetLastError.invoke ()) != -1) {
430: if (debugFlag) {
431: System.out.printf ("hitKey: SendInput failed (%d)\n", error);
432: }
433: }
434: } catch (Throwable e) {
435: e.printStackTrace ();
436: if (debugFlag) {
437: System.out.printf ("hitKey: SendInput invocation failed\n");
438: }
439: }
440: }
441:
442:
443:
444:
445:
446:
447:
448:
449: public boolean isKeyPressed (int vk) {
450: try {
451:
452: return ((short) GetKeyState.invoke (
453: vk
454: ) & 128) != 0;
455: } catch (Throwable e) {
456: e.printStackTrace ();
457: if (debugFlag) {
458: System.out.printf ("isKeyPressed: GetKeyState invocation failed\n");
459: }
460: }
461: return false;
462: }
463:
464:
465:
466:
467:
468:
469:
470:
471: public boolean isKeyToggled (int vk) {
472: try {
473:
474: return ((short) GetKeyState.invoke (
475: vk
476: ) & 1) != 0;
477: } catch (Throwable e) {
478: e.printStackTrace ();
479: if (debugFlag) {
480: System.out.printf ("isKeyToggled: GetKeyState invocation failed\n");
481: }
482: }
483: return false;
484: }
485:
486: private void printCaps (String devicePath) {
487: System.out.printf ("--------------------------------------\n");
488: System.out.printf ("%s\n", devicePath);
489: System.out.printf ("--------------------------------------\n");
490: MemorySegment handle;
491: try {
492: int error;
493:
494: if ((handle = (MemorySegment) CreateFileW.invoke (
495: arena.allocateFrom (devicePath, StandardCharsets.UTF_16LE),
496: 0,
497: FILE_SHARE_READ | FILE_SHARE_WRITE,
498: MemorySegment.NULL,
499: OPEN_EXISTING,
500: 0,
501: MemorySegment.NULL)
502: ).address () == INVALID_HANDLE_VALUE &&
503: (error = (int) GetLastError.invoke ()) != -1) {
504: System.out.printf ("printCaps: CreateFileW %s failed (%d)\n", devicePath, error);
505: return;
506: }
507: } catch (Throwable e) {
508: e.printStackTrace ();
509: if (debugFlag) {
510: System.out.printf ("printCaps: CreateFileW invocation failed\n");
511: }
512: return;
513: }
514: MemorySegment attributes = arena.allocate (HIDD_ATTRIBUTES);
515: attributes.fill ((byte) 0);
516: try {
517: int error;
518:
519: if ((int) HidD_GetAttributes.invoke (
520: handle,
521: attributes) == 0 &&
522: (error = (int) GetLastError.invoke ()) != -1) {
523: System.out.printf ("printCaps: HidD_GetAttributes failed (%d)\n", error);
524: } else {
525: System.out.printf ("VendorID\t\t\t0x%04x\n",
526: 0xffff & attributes.get (ValueLayout.JAVA_SHORT,
527: HIDD_ATTRIBUTES.byteOffset (MemoryLayout.PathElement.groupElement ("VendorID"))));
528: System.out.printf ("ProductID\t\t\t0x%04x\n",
529: 0xffff & attributes.get (ValueLayout.JAVA_SHORT,
530: HIDD_ATTRIBUTES.byteOffset (MemoryLayout.PathElement.groupElement ("ProductID"))));
531: System.out.printf ("VersionNumber\t\t\t0x%04x\n",
532: 0xffff & attributes.get (ValueLayout.JAVA_SHORT,
533: HIDD_ATTRIBUTES.byteOffset (MemoryLayout.PathElement.groupElement ("VersionNumber"))));
534: System.out.printf ("--------------------------------------\n");
535: }
536: } catch (Throwable e) {
537: e.printStackTrace ();
538: if (debugFlag) {
539: System.out.printf ("printCaps: HidD_GetAttributes invocation failed\n");
540: }
541: }
542: MemorySegment preparsedDataHandle = arena.allocate (ValueLayout.ADDRESS);
543: preparsedDataHandle.fill ((byte) 0);
544: try {
545: int error;
546:
547: if ((int) HidD_GetPreparsedData.invoke (
548: handle,
549: preparsedDataHandle) == 0 &&
550: (error = (int) GetLastError.invoke ()) != -1) {
551: System.out.printf ("printCaps: HidD_GetPreparsedData failed (%d)\n", error);
552: return;
553: }
554: } catch (Throwable e) {
555: e.printStackTrace ();
556: if (debugFlag) {
557: System.out.printf ("printCaps: HidD_GetPreparsedData invocation failed\n");
558: }
559: return;
560: }
561: MemorySegment preparsedData = preparsedDataHandle.get (ValueLayout.ADDRESS, 0L);
562: MemorySegment caps = arena.allocate (HIDP_CAPS);
563: caps.fill ((byte) 0);
564: try {
565: int ntstatus;
566:
567: if ((ntstatus = (int) HidP_GetCaps.invoke (
568: preparsedData,
569: caps)
570: ) != HIDP_STATUS_SUCCESS) {
571: System.out.printf ("printCaps: HidP_GetCaps failed (0x%08x)\n", ntstatus);
572: } else {
573: System.out.printf ("Usage\t\t\t\t0x%04x\n",
574: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
575: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("Usage"))));
576: System.out.printf ("UsagePage\t\t\t0x%04x\n",
577: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
578: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("UsagePage"))));
579: System.out.printf ("InputReportByteLength\t\t0x%04x\n",
580: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
581: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("InputReportByteLength"))));
582: System.out.printf ("OutputReportByteLength\t\t0x%04x\n",
583: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
584: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("OutputReportByteLength"))));
585: System.out.printf ("FeatureReportByteLength\t\t0x%04x\n",
586: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
587: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("FeatureReportByteLength"))));
588: System.out.printf ("NumberLinkCollectionNodes\t0x%04x\n",
589: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
590: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("NumberLinkCollectionNodes"))));
591: System.out.printf ("NumberInputButtonCaps\t\t0x%04x\n",
592: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
593: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("NumberInputButtonCaps"))));
594: System.out.printf ("NumberInputValueCaps\t\t0x%04x\n",
595: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
596: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("NumberInputValueCaps"))));
597: System.out.printf ("NumberInputDataIndices\t\t0x%04x\n",
598: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
599: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("NumberInputDataIndices"))));
600: System.out.printf ("NumberOutputButtonCaps\t\t0x%04x\n",
601: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
602: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("NumberOutputButtonCaps"))));
603: System.out.printf ("NumberOutputValueCaps\t\t0x%04x\n",
604: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
605: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("NumberOutputValueCaps"))));
606: System.out.printf ("NumberOutputDataIndices\t\t0x%04x\n",
607: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
608: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("NumberOutputDataIndices"))));
609: System.out.printf ("NumberFeatureButtonCaps\t\t0x%04x\n",
610: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
611: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("NumberFeatureButtonCaps"))));
612: System.out.printf ("NumberFeatureValueCaps\t\t0x%04x\n",
613: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
614: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("NumberFeatureValueCaps"))));
615: System.out.printf ("NumberFeatureDataIndices\t0x%04x\n",
616: 0xffff & caps.get (ValueLayout.JAVA_SHORT,
617: HIDP_CAPS.byteOffset (MemoryLayout.PathElement.groupElement ("NumberFeatureDataIndices"))));
618: System.out.printf ("--------------------------------------\n");
619: }
620: } catch (Throwable e) {
621: e.printStackTrace ();
622: if (debugFlag) {
623: System.out.printf ("printCaps: HidP_GetCaps invocation failed\n");
624: }
625: }
626: try {
627: int error;
628:
629: if ((int) HidD_FreePreparsedData.invoke (
630: preparsedData) == 0 &&
631: (error = (int) GetLastError.invoke ()) != -1) {
632: System.out.printf ("printCaps: HidD_FreePreparsedData failed (%d)\n", error);
633: }
634: } catch (Throwable e) {
635: e.printStackTrace ();
636: if (debugFlag) {
637: System.out.printf ("printCaps: HidD_FreePreparsedData invocation failed\n");
638: }
639: }
640: try {
641: int error;
642:
643: if ((int) CloseHandle.invoke (
644: handle) == 0 &&
645: (error = (int) GetLastError.invoke ()) != -1) {
646: System.out.printf ("printCaps: CloseHandle failed (%d)\n", error);
647: }
648: } catch (Throwable e) {
649: e.printStackTrace ();
650: if (debugFlag) {
651: System.out.printf ("printCaps: CloseHandle invocation failed\n");
652: }
653: }
654: }
655:
656:
657:
658:
659:
660: public void open () throws IOException {
661:
662:
663: if (handle != null && handle.address () != INVALID_HANDLE_VALUE) {
664: if (debugFlag) {
665: System.out.printf ("open: already open\n");
666: }
667: throw new IOException ("already open");
668: }
669:
670: MemorySegment hidGuid = arena.allocate (GUID);
671: hidGuid.fill ((byte) 0);
672: try {
673:
674: HidD_GetHidGuid.invoke (
675: hidGuid);
676: } catch (Throwable e) {
677: e.printStackTrace ();
678: if (debugFlag) {
679: System.out.printf ("open: HidD_GetHidGuid invocation failed\n");
680: }
681: }
682: MemorySegment infoSet = arena.allocate (ValueLayout.ADDRESS);
683: try {
684: int error;
685:
686: if ((infoSet = (MemorySegment) SetupDiGetClassDevsA.invoke (
687: hidGuid,
688: MemorySegment.NULL,
689: MemorySegment.NULL,
690: DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)
691: ).address () == INVALID_HANDLE_VALUE &&
692: (error = (int) GetLastError.invoke ()) != -1) {
693: if (debugFlag) {
694: System.out.printf ("open: SetupDiGetClassDevsA failed (%d)\n", error);
695: }
696: throw new IOException ("SetupDiGetClassDevsA failed");
697: } else {
698: if (debugFlag) {
699: System.out.printf ("open: SetupDiGetClassDevsA success\n");
700: }
701: }
702: } catch (IOException ioe) {
703: throw ioe;
704: } catch (Throwable e) {
705: e.printStackTrace ();
706: if (debugFlag) {
707: System.out.printf ("open: SetupDiGetClassDevsA invocation failed\n");
708: }
709: return;
710: }
711: String targetPath = "";
712:
713: MemorySegment interfaceData = arena.allocate (SP_DEVICE_INTERFACE_DATA);
714: for (int index = 0; ; index++) {
715:
716: interfaceData.fill ((byte) 0);
717: interfaceData.set (ValueLayout.JAVA_INT,
718: SP_DEVICE_INTERFACE_DATA.byteOffset (MemoryLayout.PathElement.groupElement ("cbSize")),
719: (int) SP_DEVICE_INTERFACE_DATA.byteSize ());
720: try {
721: int error;
722:
723: if ((int) SetupDiEnumDeviceInterfaces.invoke (
724: infoSet,
725: MemorySegment.NULL,
726: hidGuid,
727: index,
728: interfaceData) == 0 &&
729: (error = (int) GetLastError.invoke ()) != -1) {
730: if (error != 0 &&
731: error != ERROR_NO_MORE_ITEMS) {
732: if (debugFlag) {
733: System.out.printf ("open: index %d, SetupDiEnumDeviceInterfaces failed (%d)\n", index, error);
734: }
735: } else {
736: if (debugFlag) {
737: System.out.printf ("open: index %d, no more items\n", index);
738: }
739: }
740: break;
741: }
742: } catch (Throwable e) {
743: e.printStackTrace ();
744: if (debugFlag) {
745: System.out.printf ("open: index %d, SetupDiEnumDeviceInterfaces invocation failed\n", index);
746: }
747: break;
748: }
749: if (debugFlag) {
750: System.out.printf ("open: index %d, SetupDiEnumDeviceInterfaces success\n", index);
751: }
752:
753: MemorySegment detailSizeSegment = arena.allocate (ValueLayout.JAVA_INT);
754: int detailSize;
755: try {
756: int error;
757:
758: if ((int) SetupDiGetDeviceInterfaceDetailW.invoke (
759: infoSet,
760: interfaceData,
761: MemorySegment.NULL,
762: 0,
763: detailSizeSegment,
764: MemorySegment.NULL) == 0 &&
765: (error = (int) GetLastError.invoke ()) != -1) {
766: if (error != 0 &&
767: error != ERROR_INSUFFICIENT_BUFFER) {
768: if (debugFlag) {
769: System.out.printf ("open: index %d, SetupDiGetDeviceInterfaceDetail failed (%d)\n", index, error);
770: }
771: continue;
772: } else {
773: detailSize = detailSizeSegment.get (ValueLayout.JAVA_INT, 0L);
774: if (debugFlag) {
775: System.out.printf ("open: index %d, SetupDiGetDeviceInterfaceDetail insufficient buffer, detailSize %d\n", index, detailSize);
776: }
777: }
778: } else {
779: if (debugFlag) {
780: System.out.printf ("open: index %d, SetupDiGetDeviceInterfaceDetail unexpected success\n", index);
781: }
782: continue;
783: }
784: } catch (Throwable e) {
785: e.printStackTrace ();
786: if (debugFlag) {
787: System.out.printf ("open: index %d, SetupDiGetDeviceInterfaceDetailW invocation failed\n", index);
788: }
789: continue;
790: }
791:
792: MemorySegment detailData = arena.allocate ((long) detailSize, 4L);
793: detailData.fill ((byte) 0);
794: detailData.set (ValueLayout.JAVA_INT,
795: SP_DEVICE_INTERFACE_DETAIL_DATA_W.byteOffset (MemoryLayout.PathElement.groupElement ("cbSize")),
796: (int) SP_DEVICE_INTERFACE_DETAIL_DATA_W.byteSize ());
797: try {
798: int error;
799:
800: if ((int) SetupDiGetDeviceInterfaceDetailW.invoke (
801: infoSet,
802: interfaceData,
803: detailData,
804: detailSize,
805: MemorySegment.NULL,
806: MemorySegment.NULL) == 0 &&
807: (error = (int) GetLastError.invoke ()) != -1) {
808: if (debugFlag) {
809: System.out.printf ("open: index %d, SetupDiGetDeviceInterfaceDetail failed (%d)\n", index, error);
810: }
811: continue;
812: } else {
813: if (debugFlag) {
814: System.out.printf ("open: index %d, SetupDiGetDeviceInterfaceDetail success\n", index);
815: }
816: }
817: } catch (Throwable e) {
818: e.printStackTrace ();
819: if (debugFlag) {
820: System.out.printf ("open: index %d, SetupDiGetDeviceInterfaceDetailW invocation failed\n", index);
821: }
822: continue;
823: }
824:
825: String devicePath = detailData.getString (SP_DEVICE_INTERFACE_DETAIL_DATA_W.byteOffset (MemoryLayout.PathElement.groupElement ("DevicePath")),
826: StandardCharsets.UTF_16LE);
827: String lowerPath = devicePath.toLowerCase ();
828:
829: if (debugFlag) {
830: printCaps (devicePath);
831: }
832:
833:
834: if (lowerPath.indexOf ("vid_33dd&pid_0011&mi_01&col05") == -1) {
835: if (debugFlag) {
836: System.out.printf ("open: index %d, mismatch\n", index);
837: }
838: continue;
839: } else {
840: if (debugFlag) {
841: System.out.printf ("open: index %d, match\n", index);
842: }
843: }
844: targetPath = devicePath;
845: }
846:
847: try {
848: int error;
849:
850: if ((int) SetupDiDestroyDeviceInfoList.invoke (
851: infoSet) == 0 &&
852: (error = (int) GetLastError.invoke ()) != -1) {
853: if (debugFlag) {
854: System.out.printf ("open: SetupDiDestroyDeviceInfoList failed (%d)\n", error);
855: }
856: throw new IOException ("SetupDiDestroyDeviceInfoList failed");
857: } else {
858: if (debugFlag) {
859: System.out.printf ("open: SetupDiDestroyDeviceInfoList success\n");
860: }
861: }
862: } catch (IOException ioe) {
863: throw ioe;
864: } catch (Throwable e) {
865: e.printStackTrace ();
866: if (debugFlag) {
867: System.out.printf ("open: SetupDiDestroyDeviceInfoList invocation failed\n");
868: }
869: return;
870: }
871:
872: if (targetPath.equals ("")) {
873: if (debugFlag) {
874: System.out.printf ("open: device not found\n");
875: }
876: throw new IOException ("device not found");
877: } else {
878: if (debugFlag) {
879: System.out.printf ("open: device found\n");
880: }
881: }
882:
883: try {
884: int error;
885:
886: if ((handle = (MemorySegment) CreateFileW.invoke (
887: arena.allocateFrom (targetPath, StandardCharsets.UTF_16LE),
888: 0,
889: FILE_SHARE_READ | FILE_SHARE_WRITE,
890: MemorySegment.NULL,
891: OPEN_EXISTING,
892: 0,
893: MemorySegment.NULL)
894: ).address () == INVALID_HANDLE_VALUE &&
895: (error = (int) GetLastError.invoke ()) != -1) {
896: if (debugFlag) {
897: System.out.printf ("open: CreateFile %s failed (%d)\n", targetPath, error);
898: System.out.printf ("open: device not available\n");
899: }
900: throw new IOException ("device not available");
901: } else {
902: if (debugFlag) {
903: System.out.printf ("open: CreateFile %s success\n", targetPath);
904: }
905: }
906: } catch (IOException ioe) {
907: throw ioe;
908: } catch (Throwable e) {
909: e.printStackTrace ();
910: if (debugFlag) {
911: System.out.printf ("open: CreateFileW invocation failed\n");
912: }
913: return;
914: }
915:
916: }
917:
918:
919:
920:
921:
922:
923:
924:
925:
926:
927:
928:
929: private static final int[] indexes = new int[] { 7, 8, 9, 10, 11, 13, 14 };
930: public boolean send (long data) {
931:
932: if (handle == null || handle.address () == INVALID_HANDLE_VALUE) {
933: return false;
934: }
935:
936: final int length = 65;
937: MemorySegment report = arena.allocate (length);
938: report.fill ((byte) 0);
939: report.set (ValueLayout.JAVA_BYTE, 0L, (byte) 10);
940: report.set (ValueLayout.JAVA_BYTE, 1L, (byte) 248);
941: for (int i = 0; i < 7; i++) {
942: report.set (ValueLayout.JAVA_BYTE, (long) indexes[i], (byte) (data >> (8 * i)));
943: }
944:
945: try {
946: int error;
947:
948: if ((int) HidD_SetFeature.invoke (
949: handle,
950: report,
951: length) == 0 &&
952: (error = (int) GetLastError.invoke ()) != -1) {
953: if (debugFlag) {
954: System.out.printf ("send: HidD_SetFeature failed (%d)\n", error);
955: }
956: return false;
957: }
958: } catch (Throwable e) {
959: e.printStackTrace ();
960: if (debugFlag) {
961: System.out.printf ("send: HidD_SetFeature invocation failed\n");
962: }
963: return false;
964: }
965: return true;
966: }
967:
968: }