ZKeyLEDPort.java
     1: //========================================================================================
     2: //  ZKeyLEDPort.java
     3: //    en:Z keyboard LED port
     4: //    ja:ZキーボードLEDポート
     5: //  Copyright (C) 2003-2025 Makoto Kamada
     6: //
     7: //  This file is part of the XEiJ (X68000 Emulator in Java).
     8: //  You can use, modify and redistribute the XEiJ if the conditions are met.
     9: //  Read the XEiJ License for more details.
    10: //  https://stdkmd.net/xeij/
    11: //========================================================================================
    12: 
    13: package xeij;
    14: 
    15: import java.io.*;
    16: import java.lang.foreign.*;  //Arena,FunctionDescriptor,Linker,MemorySegment,SymbolLookup,ValueLayout
    17: import java.lang.invoke.*;  //MethodHandle
    18: import java.nio.charset.*;  //StandardCharsets
    19: import java.util.*;  //NoSuchElementException
    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:   //GUID構造体
    31:   //  https://learn.microsoft.com/ja-jp/windows/win32/api/guiddef/ns-guiddef-guid
    32:   private static final MemoryLayout GUID = MemoryLayout.structLayout (
    33:     ValueLayout.JAVA_INT.withName ("Data1"),  //0 unsigned long Data1
    34:     ValueLayout.JAVA_SHORT.withName ("Data2"),  //4 unsigned short Data2
    35:     ValueLayout.JAVA_SHORT.withName ("Data3"),  //6 unsigned short Data3
    36:     MemoryLayout.sequenceLayout (
    37:       8,
    38:       ValueLayout.JAVA_BYTE).withName ("Data4")  //8 unsigned char Data4[8]
    39:     //16
    40:     );
    41: 
    42:   //HIDD_ATTRIBUTES構造体
    43:   //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidsdi/ns-hidsdi-_hidd_attributes
    44:   private static final MemoryLayout HIDD_ATTRIBUTES = MemoryLayout.structLayout (
    45:     ValueLayout.JAVA_INT.withName ("Size"),  //0 ULONG Size
    46:     ValueLayout.JAVA_SHORT.withName ("VendorID"),  //4 USHORT VendorID
    47:     ValueLayout.JAVA_SHORT.withName ("ProductID"),  //6 USHORT ProductID
    48:     ValueLayout.JAVA_SHORT.withName ("VersionNumber"),  //8 USHORT VersionNumber
    49:     MemoryLayout.paddingLayout (2)  //10
    50:     //12
    51:     );
    52: 
    53:   //HIDP_CAPS構造体
    54:   //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidpi/ns-hidpi-_hidp_caps
    55:   private static final MemoryLayout HIDP_CAPS = MemoryLayout.structLayout (
    56:     ValueLayout.JAVA_SHORT.withName ("Usage"),  //0 USAGE Usage  USAGEはUSHORT。hidusage.hで定義されている
    57:     ValueLayout.JAVA_SHORT.withName ("UsagePage"),  //2 USAGE UsagePage
    58:     ValueLayout.JAVA_SHORT.withName ("InputReportByteLength"),  //4 USHORT InputReportByteLength
    59:     ValueLayout.JAVA_SHORT.withName ("OutputReportByteLength"),  //6 USHORT OutputReportByteLength
    60:     ValueLayout.JAVA_SHORT.withName ("FeatureReportByteLength"),  //8 USHORT FeatureReportByteLength
    61:     MemoryLayout.sequenceLayout (
    62:       17,
    63:       ValueLayout.JAVA_SHORT).withName ("Reserved"),  //10 USHORT Reserved[17]
    64:     ValueLayout.JAVA_SHORT.withName ("NumberLinkCollectionNodes"),  //44 USHORT NumberLinkCollectionNodes
    65:     ValueLayout.JAVA_SHORT.withName ("NumberInputButtonCaps"),  //46 USHORT NumberInputButtonCaps
    66:     ValueLayout.JAVA_SHORT.withName ("NumberInputValueCaps"),  //48 USHORT NumberInputValueCaps
    67:     ValueLayout.JAVA_SHORT.withName ("NumberInputDataIndices"),  //50 USHORT NumberInputDataIndices
    68:     ValueLayout.JAVA_SHORT.withName ("NumberOutputButtonCaps"),  //52 USHORT NumberOutputButtonCaps
    69:     ValueLayout.JAVA_SHORT.withName ("NumberOutputValueCaps"),  //54 USHORT NumberOutputValueCaps
    70:     ValueLayout.JAVA_SHORT.withName ("NumberOutputDataIndices"),  //56 USHORT NumberOutputDataIndices
    71:     ValueLayout.JAVA_SHORT.withName ("NumberFeatureButtonCaps"),  //58 USHORT NumberFeatureButtonCaps
    72:     ValueLayout.JAVA_SHORT.withName ("NumberFeatureValueCaps"),  //60 USHORT NumberFeatureValueCaps
    73:     ValueLayout.JAVA_SHORT.withName ("NumberFeatureDataIndices")  //62 USHORT NumberFeatureDataIndices
    74:     //64
    75:     );
    76: 
    77:   //INPUT構造体
    78:   //  https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/ns-winuser-input
    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"),  //0 DWORD type
    83:     MemoryLayout.paddingLayout (4),  //4
    84:     MemoryLayout.unionLayout (
    85:       //MOUSEINPUT構造体
    86:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/ns-winuser-mouseinput
    87:       MemoryLayout.structLayout (
    88:         ValueLayout.JAVA_INT.withName ("dx"),  //8 LONG dx
    89:         ValueLayout.JAVA_INT.withName ("dy"),  //12 LONG dy
    90:         ValueLayout.JAVA_INT.withName ("mouseData"),  //16 DWORD mouseData
    91:         ValueLayout.JAVA_INT.withName ("dwFlags"),  //20 DWORD dwFlags
    92:         ValueLayout.JAVA_INT.withName ("time"),  //24 DWORD time
    93:         MemoryLayout.paddingLayout (4),  //28
    94:         ValueLayout.ADDRESS.withName ("dwExtraInfo")  //32 ULONG_PTR dwExtraInfo
    95:         //40
    96:         ).withName ("mi"),  //8
    97:       //KEYBDINPUT構造体
    98:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/ns-winuser-keybdinput
    99:       MemoryLayout.structLayout (
   100:         ValueLayout.JAVA_SHORT.withName ("wVk"),  //8 WORD wVk
   101:         ValueLayout.JAVA_SHORT.withName ("wScan"),  //10 WORD wScan
   102:         ValueLayout.JAVA_INT.withName ("dwFlags"),  //12 DWORD dwFlags
   103:         ValueLayout.JAVA_INT.withName ("time"),  //16 DWORD time
   104:         MemoryLayout.paddingLayout (4),  //20
   105:         ValueLayout.ADDRESS.withName ("dwExtraInfo")  //24 ULONG_PTR dwExtraInfo
   106:         //32
   107:         ).withName ("ki"),  //8
   108:       //HARDWAREINPUT構造体
   109:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/ns-winuser-hardwareinput
   110:       MemoryLayout.structLayout (
   111:         ValueLayout.JAVA_INT.withName ("uMsg"),  //8 DWORD uMsg
   112:         ValueLayout.JAVA_SHORT.withName ("wParamL"),  //12 WORD wParamL
   113:         ValueLayout.JAVA_SHORT.withName ("wParamH")  //14 WORD wParamH
   114:         //16
   115:         ).withName ("hi")  //8
   116:       ).withName ("DUMMYUNIONNAME")
   117:     //40
   118:     );
   119: 
   120:   //SP_DEVICE_INTERFACE_DATA構造体
   121:   //  https://learn.microsoft.com/ja-jp/windows/win32/api/setupapi/ns-setupapi-sp_device_interface_data
   122:   private static final MemoryLayout SP_DEVICE_INTERFACE_DATA = MemoryLayout.structLayout (
   123:     ValueLayout.JAVA_INT.withName ("cbSize"),  //0 DWORD cbSize
   124:     GUID.withName ("InterfaceClassGuid"),  //4 GUID InterfaceClassGuid
   125:     ValueLayout.JAVA_INT.withName ("Flags"),  //20 DWORD Flags
   126:     ValueLayout.ADDRESS.withName ("Reserved")  //24 ULONG_PTR Reserved
   127:     //32
   128:     );
   129: 
   130:   //SP_DEVICE_INTERFACE_DETAIL_DATA_W構造体
   131:   //  https://learn.microsoft.com/ja-jp/windows/win32/api/setupapi/ns-setupapi-sp_device_interface_detail_data_w
   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"),  //0 DWORD cbSize
   135:     MemoryLayout.sequenceLayout (
   136:       ANYSIZE_ARRAY,
   137:       ValueLayout.JAVA_SHORT).withName ("DevicePath"),  //4 WCHAR DevicePath[ANYSIZE_ARRAY]
   138:     MemoryLayout.paddingLayout (2)  //6
   139:     //8
   140:     );
   141: 
   142:   //エラーコード
   143:   //  https://learn.microsoft.com/ja-jp/windows/win32/debug/system-error-codes--0-499-
   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:   //port = new ZKeyLEDPort (debugFlag)
   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:       //CloseHandle関数
   207:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/handleapi/nf-handleapi-closehandle
   208:       CloseHandle = downcallHandle (
   209:         kernel32.findOrThrow ("CloseHandle"),
   210:         FunctionDescriptor.of (
   211:           ValueLayout.JAVA_INT,  //BOOL
   212:           ValueLayout.ADDRESS));  //HANDLE hObject
   213: 
   214:       //CreateFileW関数
   215:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-createfilew
   216:       CreateFileW = downcallHandle (
   217:         kernel32.findOrThrow ("CreateFileW"),
   218:         FunctionDescriptor.of (
   219:           ValueLayout.ADDRESS,  //HANDLE
   220:           ValueLayout.ADDRESS,  //LPCWSTR lpFileName
   221:           ValueLayout.JAVA_INT,  //DWORD dwDesiredAccess
   222:           ValueLayout.JAVA_INT,  //DWORD dwShareMode
   223:           ValueLayout.ADDRESS,  //LPSECURITY_ATTRIBUTES lpSecurityAttributes
   224:           ValueLayout.JAVA_INT,  //DWORD dwCreationDisposition
   225:           ValueLayout.JAVA_INT,  //DWORD dwFlagsAndAttributes
   226:           ValueLayout.ADDRESS));  //HANDLE hTemplateFile
   227: 
   228:       //GetKeyState関数
   229:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getkeystate
   230:       GetKeyState = downcallHandle (
   231:         user32.findOrThrow ("GetKeyState"),
   232:         FunctionDescriptor.of (
   233:           ValueLayout.JAVA_SHORT,  //SHORT
   234:           ValueLayout.JAVA_INT));  //int nVirtKey
   235: 
   236:       //GetLastError関数
   237:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
   238:       GetLastError = downcallHandle (
   239:         kernel32.findOrThrow ("GetLastError"),
   240:         FunctionDescriptor.of (
   241:           ValueLayout.JAVA_INT));  //DWORD
   242: 
   243:       //HidD_FreePreparsedData関数
   244:       //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidsdi/nf-hidsdi-hidd_freepreparseddata
   245:       HidD_FreePreparsedData = downcallHandle (
   246:         hid.findOrThrow ("HidD_FreePreparsedData"),
   247:         FunctionDescriptor.of (
   248:           ValueLayout.JAVA_INT,  //BOOLEAN
   249:           ValueLayout.ADDRESS));  //PHIDP_PREPARSED_DATA PreparsedData
   250: 
   251:       //HidD_GetAttributes関数
   252:       //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidsdi/nf-hidsdi-hidd_getattributes
   253:       HidD_GetAttributes = downcallHandle (
   254:         hid.findOrThrow ("HidD_GetAttributes"),
   255:         FunctionDescriptor.of (
   256:           ValueLayout.JAVA_INT,  //BOOLEAN
   257:           ValueLayout.ADDRESS,  //HANDLE HidDeviceObject
   258:           ValueLayout.ADDRESS));  //PHIDD_ATTRIBUTES Attributes
   259: 
   260:       //HidD_GetHidGuid関数
   261:       //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidsdi/nf-hidsdi-hidd_gethidguid
   262:       HidD_GetHidGuid = downcallHandle (
   263:         hid.findOrThrow ("HidD_GetHidGuid"),
   264:         FunctionDescriptor.ofVoid (
   265:           ValueLayout.ADDRESS));  //LPGUID HidGuid
   266: 
   267:       //HidD_GetPreparsedData関数
   268:       //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidsdi/nf-hidsdi-hidd_getpreparseddata
   269:       HidD_GetPreparsedData = downcallHandle (
   270:         hid.findOrThrow ("HidD_GetPreparsedData"),
   271:         FunctionDescriptor.of (
   272:           ValueLayout.JAVA_INT,  //BOOLEAN
   273:           ValueLayout.ADDRESS,  //HANDLE HidDeviceObject
   274:           ValueLayout.ADDRESS));  //PHIDP_PREPARSED_DATA *PreparsedData
   275: 
   276:       //HidD_SetFeature関数
   277:       //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidsdi/nf-hidsdi-hidd_setfeature
   278:       HidD_SetFeature = downcallHandle (
   279:         hid.findOrThrow ("HidD_SetFeature"),
   280:         FunctionDescriptor.of (
   281:           ValueLayout.JAVA_INT,  //BOOLEAN
   282:           ValueLayout.ADDRESS,  //HANDLE HidDeviceObject
   283:           ValueLayout.ADDRESS,  //PVOID ReportBuffer
   284:           ValueLayout.JAVA_INT));  //ULONG ReportBufferLength
   285: 
   286:       //HidP_GetCaps関数
   287:       //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidpi/nf-hidpi-hidp_getcaps
   288:       HidP_GetCaps = downcallHandle (
   289:         hid.findOrThrow ("HidP_GetCaps"),
   290:         FunctionDescriptor.of (
   291:           ValueLayout.JAVA_INT,  //NTSTATUS
   292:           ValueLayout.ADDRESS,  //PHIDP_PREPARSED_DATA PreparsedData
   293:           ValueLayout.ADDRESS));  //PHIDP_CAPS Capabilities
   294: 
   295:       //SendInput関数
   296:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-sendinput
   297:       SendInput = downcallHandle (
   298:         user32.findOrThrow ("SendInput"),
   299:         FunctionDescriptor.of (
   300:           ValueLayout.JAVA_INT,  //UINT
   301:           ValueLayout.JAVA_INT,  //UINT cInputs
   302:           ValueLayout.ADDRESS,  //LPINPUT pInputs
   303:           ValueLayout.JAVA_INT));  //int cbSize
   304: 
   305:       //SetupDiDestroyDeviceInfoList関数
   306:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/setupapi/nf-setupapi-setupdidestroydeviceinfolist
   307:       SetupDiDestroyDeviceInfoList = downcallHandle (
   308:         setupapi.findOrThrow ("SetupDiDestroyDeviceInfoList"),
   309:         FunctionDescriptor.of (
   310:           ValueLayout.JAVA_INT,  //BOOL
   311:           ValueLayout.ADDRESS));  //HDEVINFO DeviceInfoSet
   312: 
   313:       //SetupDiEnumDeviceInterfaces関数
   314:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/setupapi/nf-setupapi-setupdienumdeviceinterfaces
   315:       SetupDiEnumDeviceInterfaces = downcallHandle (
   316:         setupapi.findOrThrow ("SetupDiEnumDeviceInterfaces"),
   317:         FunctionDescriptor.of (
   318:           ValueLayout.JAVA_INT,  //BOOL
   319:           ValueLayout.ADDRESS,  //HDEVINFO DeviceInfoSet
   320:           ValueLayout.ADDRESS,  //PSP_DEVINFO_DATA DeviceInfoData
   321:           ValueLayout.ADDRESS,  //GUID *InterfaceClassGuid
   322:           ValueLayout.JAVA_INT,  //DWORD MemberIndex
   323:           ValueLayout.ADDRESS));  //PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
   324: 
   325:       //SetupDiGetClassDevsA関数
   326:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/setupapi/nf-setupapi-setupdigetclassdevsa
   327:       SetupDiGetClassDevsA = downcallHandle (
   328:         setupapi.findOrThrow ("SetupDiGetClassDevsA"),  //SetupDiGetClassDevsWは見つからない
   329:         FunctionDescriptor.of (
   330:           ValueLayout.ADDRESS,  //HDEVINFO
   331:           ValueLayout.ADDRESS,  //GUID *ClassGuid
   332:           ValueLayout.ADDRESS,  //PCSTR Enumerator
   333:           ValueLayout.ADDRESS,  //HWND hwndParent
   334:           ValueLayout.JAVA_INT));  //DWORD Flags
   335: 
   336:       //SetupDiGetDeviceInterfaceDetailW関数
   337:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/setupapi/nf-setupapi-setupdigetdeviceinterfacedetailw
   338:       SetupDiGetDeviceInterfaceDetailW = downcallHandle (
   339:         setupapi.findOrThrow ("SetupDiGetDeviceInterfaceDetailW"),
   340:         FunctionDescriptor.of (
   341:           ValueLayout.JAVA_INT,  //BOOL
   342:           ValueLayout.ADDRESS,  //HDEVINFO DeviceInfoSet
   343:           ValueLayout.ADDRESS,  //PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
   344:           ValueLayout.ADDRESS,  //PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData
   345:           ValueLayout.JAVA_INT,  //DWORD DeviceInterfaceDetailDataSize
   346:           ValueLayout.ADDRESS,  //PDWORD RequiredSize
   347:           ValueLayout.ADDRESS));  //PSP_DEVINFO_DATA DeviceInfoData
   348: 
   349:     } catch (NoSuchElementException nsee) {
   350:       nsee.printStackTrace ();
   351:     }
   352: 
   353:     handle = null;
   354:     open ();
   355:   }
   356: 
   357:   //------------------------------------------------------------------------
   358:   //ZKeyLEDPort
   359:   //  close ()
   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:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/handleapi/nf-handleapi-closehandle
   370:       if ((int) CloseHandle.invoke (
   371:         handle) == 0 &&  //HANDLE hObject
   372:           (error = (int) GetLastError.invoke ()) != -1) {
   373:         if (debugFlag) {
   374:           System.out.printf ("close: CloseHandle failed (%d)\n", error);
   375:         }
   376:         //handle = null;
   377:       }
   378:     } catch (Throwable e) {
   379:       e.printStackTrace ();
   380:       if (debugFlag) {
   381:         System.out.printf ("close: CloseHandle invocation failed\n");
   382:       }
   383:       //handle = null;
   384:     }
   385:     //ハンドルを消す
   386:     handle = null;
   387:   }  //close
   388: 
   389:   //------------------------------------------------------------------------
   390:   //ZKeyLEDPort
   391:   //  hitKey (vk)
   392:   //  キーを叩く
   393:   //  vk       キー
   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:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-sendinput
   425:       if ((int) SendInput.invoke (
   426:         2,  //UINT cInputs
   427:         pInputs,  //LPINPUT pInputs
   428:         (int) INPUT.byteSize ()) == 0 &&  //int cbSize
   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:   }  //hitKey
   441: 
   442:   //------------------------------------------------------------------------
   443:   //ZKeyLEDPort
   444:   //  pressed = isKeyPressed (vk)
   445:   //  キーは押されているか
   446:   //  vk       キー
   447:   //  pressed  true   キーは押されている
   448:   //           false  キーは離されている
   449:   public boolean isKeyPressed (int vk) {
   450:     try {
   451:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getkeystate
   452:       return ((short) GetKeyState.invoke (
   453:         vk  //int nVirtKey
   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:   }  //isKeyPressed
   463: 
   464:   //------------------------------------------------------------------------
   465:   //ZKeyLEDPort
   466:   //  toggled = isKeyToggled (vk)
   467:   //  キーは点灯しているか
   468:   //  vk       キー
   469:   //  toggled  true   キーは点灯している
   470:   //           false  キーは消灯している
   471:   public boolean isKeyToggled (int vk) {
   472:     try {
   473:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getkeystate
   474:       return ((short) GetKeyState.invoke (
   475:         vk  //int nVirtKey
   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:   }  //isKeyToggled
   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;  //HANDLE handle
   491:     try {
   492:       int error;
   493:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-createfilew
   494:       if ((handle = (MemorySegment) CreateFileW.invoke (
   495:         arena.allocateFrom (devicePath, StandardCharsets.UTF_16LE),  //LPCWSTR lpFileName
   496:         0,  //DWORD dwDesiredAccess
   497:         FILE_SHARE_READ | FILE_SHARE_WRITE,  //DWORD dwShareMode
   498:         MemorySegment.NULL,  //LPSECURITY_ATTRIBUTES lpSecurityAttributes
   499:         OPEN_EXISTING,  //DWORD dwCreationDisposition
   500:         0,  //DWORD dwFlagsAndAttributes
   501:         MemorySegment.NULL)  //HANDLE hTemplateFile
   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:       //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidsdi/nf-hidsdi-hidd_getattributes
   519:       if ((int) HidD_GetAttributes.invoke (
   520:         handle,  //HANDLE HidDeviceObject
   521:         attributes) == 0 &&  //PHIDD_ATTRIBUTES Attributes
   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);  //PHIDP_PREPARSED_DATA
   543:     preparsedDataHandle.fill ((byte) 0);
   544:     try {
   545:       int error;
   546:       //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidsdi/nf-hidsdi-hidd_getpreparseddata
   547:       if ((int) HidD_GetPreparsedData.invoke (
   548:         handle,  //HANDLE HidDeviceObject
   549:         preparsedDataHandle) == 0 &&  //PHIDP_PREPARSED_DATA *PreparsedData
   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:       //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidpi/nf-hidpi-hidp_getcaps
   567:       if ((ntstatus = (int) HidP_GetCaps.invoke (
   568:         preparsedData,  //PHIDP_PREPARSED_DATA PreparsedData
   569:         caps)  //PHIDP_CAPS Capabilities
   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:       //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidsdi/nf-hidsdi-hidd_freepreparseddata
   629:       if ((int) HidD_FreePreparsedData.invoke (
   630:         preparsedData) == 0 &&  //PHIDP_PREPARSED_DATA PreparsedData
   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:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/handleapi/nf-handleapi-closehandle
   643:       if ((int) CloseHandle.invoke (
   644:         handle) == 0 &&  //HANDLE hObject
   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:   //ZKeyLEDPort
   658:   //  open ()
   659:   //  ポートを開く
   660:   public void open () throws IOException {
   661:     //開いていたら失敗
   662:     //  Javaは変数にゴミが入っていることはない
   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:       //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidsdi/nf-hidsdi-hidd_gethidguid
   674:       HidD_GetHidGuid.invoke (
   675:         hidGuid);  //LPGUID 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);  //HDEVINFO
   683:     try {
   684:       int error;
   685:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/setupapi/nf-setupapi-setupdigetclassdevsw
   686:       if ((infoSet = (MemorySegment) SetupDiGetClassDevsA.invoke (
   687:         hidGuid,  //GUID *ClassGuid
   688:         MemorySegment.NULL,  //PCSTR Enumerator
   689:         MemorySegment.NULL,  //HWND hwndParent
   690:         DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)  //DWORD Flags
   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:         //  https://learn.microsoft.com/ja-jp/windows/win32/api/setupapi/nf-setupapi-setupdienumdeviceinterfaces
   723:         if ((int) SetupDiEnumDeviceInterfaces.invoke (
   724:           infoSet,  //HDEVINFO DeviceInfoSet
   725:           MemorySegment.NULL,  //PSP_DEVINFO_DATA DeviceInfoData
   726:           hidGuid,  //GUID *InterfaceClassGuid
   727:           index,  //DWORD MemberIndex
   728:           interfaceData) == 0 &&  //PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
   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);  //DWORD detailSize
   754:       int detailSize;
   755:       try {
   756:         int error;
   757:         //  https://learn.microsoft.com/ja-jp/windows/win32/api/setupapi/nf-setupapi-setupdigetdeviceinterfacedetailw
   758:         if ((int) SetupDiGetDeviceInterfaceDetailW.invoke (
   759:           infoSet,  //HDEVINFO DeviceInfoSet
   760:           interfaceData,  //PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
   761:           MemorySegment.NULL,  //PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData
   762:           0,  //DWORD DeviceInterfaceDetailDataSize
   763:           detailSizeSegment,  //PDWORD RequiredSize
   764:           MemorySegment.NULL) == 0 &&  //PSP_DEVINFO_DATA DeviceInfoData
   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);  //SP_DEVICE_INTERFACE_DETAIL_DATA_W
   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 ());  //detailSizeではない。可変部分のサイズを含まない
   797:       try {
   798:         int error;
   799:         //  https://learn.microsoft.com/ja-jp/windows/win32/api/setupapi/nf-setupapi-setupdigetdeviceinterfacedetailw
   800:         if ((int) SetupDiGetDeviceInterfaceDetailW.invoke (
   801:           infoSet,  //HDEVINFO DeviceInfoSet
   802:           interfaceData,  //PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
   803:           detailData,  //PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData
   804:           detailSize,  //DWORD DeviceInterfaceDetailDataSize
   805:           MemorySegment.NULL,  //PDWORD RequiredSize
   806:           MemorySegment.NULL) == 0 &&  //PSP_DEVINFO_DATA DeviceInfoData
   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:       //  DevicePathはcase-insensitive
   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:     }  //for index
   846:     //デバイス情報セットを捨てる
   847:     try {
   848:       int error;
   849:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/setupapi/nf-setupapi-setupdidestroydeviceinfolist
   850:       if ((int) SetupDiDestroyDeviceInfoList.invoke (
   851:         infoSet) == 0 &&  //HDEVINFO DeviceInfoSet
   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:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-createfilew
   886:       if ((handle = (MemorySegment) CreateFileW.invoke (
   887:         arena.allocateFrom (targetPath, StandardCharsets.UTF_16LE),  //LPCWSTR lpFileName
   888:         0,  //DWORD dwDesiredAccess
   889:         FILE_SHARE_READ | FILE_SHARE_WRITE,  //DWORD dwShareMode
   890:         MemorySegment.NULL,  //LPSECURITY_ATTRIBUTES lpSecurityAttributes
   891:         OPEN_EXISTING,  //DWORD dwCreationDisposition
   892:         0,  //DWORD dwFlagsAndAttributes
   893:         MemorySegment.NULL)  //HANDLE hTemplateFile
   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:   }  //open
   917: 
   918:   //------------------------------------------------------------------------
   919:   //ZKeyLEDPort
   920:   //  success = send (data)
   921:   //  LEDのデータを送る
   922:   //  success  true   成功
   923:   //           false  失敗
   924:   //  data    LEDのデータ。0=消灯,…,32=暗い,…,64=やや暗い,…,128=やや明るい,…,255=明るい
   925:   //          +----------+----------+----------+----------+----------+----------+----------+----------+
   926:   //          |63      56|55      48|47      40|39      32|31      24|23      16|15       8|7        0|
   927:   //          |          |   全角   | ひらがな |    INS   |   CAPS   |コード入力| ローマ字 |   かな   |
   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:       //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/hidsdi/nf-hidsdi-hidd_setfeature
   948:       if ((int) HidD_SetFeature.invoke (
   949:         handle,  //HANDLE HidDeviceObject
   950:         report,  //PVOID ReportBuffer
   951:         length) == 0 &&  //ULONG ReportBufferLength
   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:   }  //send
   967: 
   968: }  //class ZKeyLEDPort