XInput.java
     1: //========================================================================================
     2: //  XInput.java
     3: //    en:XInput Gamepad
     4: //    ja:XInput Gamepad
     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.lang.foreign.*;  //Arena,FunctionDescriptor,Linker,MemorySegment,SymbolLookup,ValueLayout
    16: import java.lang.invoke.*;  //MethodHandle
    17: import java.util.*;
    18: 
    19: //class XInput
    20: //  XInputクラス
    21: public final class XInput extends Thread {
    22: 
    23: 
    24: 
    25:   //公開クラス定数
    26: 
    27:   //ボタンのビット
    28:   public static final int UP_BIT        = 0;  //上ボタン
    29:   public static final int DOWN_BIT      = 1;  //下ボタン
    30:   public static final int LEFT_BIT      = 2;  //左ボタン
    31:   public static final int RIGHT_BIT     = 3;  //右ボタン
    32:   public static final int START_BIT     = 4;  //STARTボタン
    33:   public static final int BACK_BIT      = 5;  //BACKボタン
    34:   public static final int LSTICK_BIT    = 6;  //左スティックボタン
    35:   public static final int RSTICK_BIT    = 7;  //右スティックボタン
    36:   public static final int LB_BIT        = 8;  //LBボタン
    37:   public static final int RB_BIT        = 9;  //RBボタン
    38:   public static final int A_BIT         = 12;  //Aボタン
    39:   public static final int B_BIT         = 13;  //Bボタン
    40:   public static final int X_BIT         = 14;  //Xボタン
    41:   public static final int Y_BIT         = 15;  //Yボタン
    42:   public static final int LSUP_BIT      = 16;  //左スティック上
    43:   public static final int LSDOWN_BIT    = 17;  //左スティック下
    44:   public static final int LSLEFT_BIT    = 18;  //左スティック左
    45:   public static final int LSRIGHT_BIT   = 19;  //左スティック右
    46:   public static final int RSUP_BIT      = 20;  //右スティック上
    47:   public static final int RSDOWN_BIT    = 21;  //右スティック下
    48:   public static final int RSLEFT_BIT    = 22;  //右スティック左
    49:   public static final int RSRIGHT_BIT   = 23;  //右スティック右
    50:   public static final int LTRIGGER_BIT  = 24;  //左トリガー
    51:   public static final int RTRIGGER_BIT  = 25;  //右トリガー
    52:   public static final int BUTTONS       = 26;  //ボタンの数
    53: 
    54:   //ボタンのマスク
    55:   public static final int UP_MASK       = 1 << UP_BIT;
    56:   public static final int DOWN_MASK     = 1 << DOWN_BIT;
    57:   public static final int LEFT_MASK     = 1 << LEFT_BIT;
    58:   public static final int RIGHT_MASK    = 1 << RIGHT_BIT;
    59:   public static final int START_MASK    = 1 << START_BIT;
    60:   public static final int BACK_MASK     = 1 << BACK_BIT;
    61:   public static final int LSTICK_MASK   = 1 << LSTICK_BIT;
    62:   public static final int RSTICK_MASK   = 1 << RSTICK_BIT;
    63:   public static final int LB_MASK       = 1 << LB_BIT;
    64:   public static final int RB_MASK       = 1 << RB_BIT;
    65:   public static final int A_MASK        = 1 << A_BIT;
    66:   public static final int B_MASK        = 1 << B_BIT;
    67:   public static final int X_MASK        = 1 << X_BIT;
    68:   public static final int Y_MASK        = 1 << Y_BIT;
    69:   public static final int LSUP_MASK     = 1 << LSUP_BIT;
    70:   public static final int LSDOWN_MASK   = 1 << LSDOWN_BIT;
    71:   public static final int LSLEFT_MASK   = 1 << LSLEFT_BIT;
    72:   public static final int LSRIGHT_MASK  = 1 << LSRIGHT_BIT;
    73:   public static final int RSUP_MASK     = 1 << RSUP_BIT;
    74:   public static final int RSDOWN_MASK   = 1 << RSDOWN_BIT;
    75:   public static final int RSLEFT_MASK   = 1 << RSLEFT_BIT;
    76:   public static final int RSRIGHT_MASK  = 1 << RSRIGHT_BIT;
    77:   public static final int LTRIGGER_MASK = 1 << LTRIGGER_BIT;
    78:   public static final int RTRIGGER_MASK = 1 << RTRIGGER_BIT;
    79: 
    80:   //ボタンの名前
    81:   public static final String BIT_TO_TEXT[] = new String[] {
    82:     "UP", "DOWN", "LEFT", "RIGHT", "START", "BACK", "LSTICK", "RSTICK",
    83:     "LB", "RB", "(10)", "(11)", "A", "B", "X", "Y",
    84:     "LSUP", "LSDOWN", "LSLEFT", "LSRIGHT", "RSUP", "RSDOWN", "RSLEFT", "RSRIGHT",
    85:     "LTRIGGER", "RTRIGGER",
    86:   };
    87: 
    88: 
    89: 
    90:   //非公開クラス定数
    91: 
    92:   //型
    93:   protected static final ValueLayout.OfInt DWORD = ValueLayout.JAVA_INT;
    94:   protected static final ValueLayout.OfShort WORD = ValueLayout.JAVA_SHORT;
    95:   protected static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE;
    96:   protected static final ValueLayout.OfShort SHORT = ValueLayout.JAVA_SHORT;
    97: 
    98:   //エラーコード
    99:   protected static final int ERROR_SUCCESS = 0;
   100:   //protected static final int ERROR_DEVICE_NOT_CONNECTED = 1167;
   101: 
   102:   //構造体
   103:   //  XINPUT_GAMEPAD
   104:   //  https://learn.microsoft.com/ja-jp/windows/win32/api/xinput/ns-xinput-xinput_gamepad
   105:   protected static final MemoryLayout GAMEPAD_LAYOUT = MemoryLayout.structLayout (
   106:     WORD.withName ("wButtons"),
   107:     BYTE.withName ("bLeftTrigger"),
   108:     BYTE.withName ("bRightTrigger"),
   109:     SHORT.withName ("sThumbLX"),
   110:     SHORT.withName ("sThumbLY"),
   111:     SHORT.withName ("sThumbRX"),
   112:     SHORT.withName ("sThumbRY"));
   113:   protected static final long OFFSET_wButtons = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("wButtons"));
   114:   protected static final long OFFSET_bLeftTrigger = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("bLeftTrigger"));
   115:   protected static final long OFFSET_bRightTrigger = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("bRightTrigger"));
   116:   protected static final long OFFSET_sThumbLX = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("sThumbLX"));
   117:   protected static final long OFFSET_sThumbLY = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("sThumbLY"));
   118:   protected static final long OFFSET_sThumbRX = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("sThumbRX"));
   119:   protected static final long OFFSET_sThumbRY = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("sThumbRY"));
   120:   //  XINPUT_STATE
   121:   //  https://learn.microsoft.com/ja-jp/windows/win32/api/xinput/ns-xinput-xinput_state
   122:   protected static final MemoryLayout STATE_LAYOUT = MemoryLayout.structLayout (
   123:     DWORD.withName ("dwPacketNumber"),
   124:     GAMEPAD_LAYOUT.withName ("Gamepad"));
   125:   protected static final long OFFSET_dwPacketNumber = STATE_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("dwPacketNumber"));
   126: 
   127: 
   128: 
   129:   //非公開インスタンス変数
   130: 
   131:   //ゲームパッド
   132:   protected Gamepad[] gamepads;
   133: 
   134:   //リスナー
   135:   protected ArrayList<GamepadListener> gamepadListeners;
   136: 
   137:   //ポーリング
   138:   protected boolean polling;  //true=ポーリング動作中
   139: 
   140:   //関数
   141:   protected MethodHandle XInputGetState;
   142: 
   143:   //構造体
   144:   protected MemorySegment state;
   145:   protected MemorySegment gamepad;
   146: 
   147: 
   148: 
   149:   //公開コンストラクタ
   150: 
   151:   //new XInput ()
   152:   //  コンストラクタ
   153:   public XInput () {
   154:     //ゲームパッドを開く
   155:     gamepads = new Gamepad[4];
   156:     for (int index = 0; index < 4; index++) {
   157:       gamepads[index] = new Gamepad (index);
   158:     }
   159:     //リスナーを初期化する
   160:     gamepadListeners = new ArrayList<GamepadListener> ();
   161:     //ポーリングを開始する
   162:     polling = true;
   163:     this.start ();
   164:   }
   165: 
   166: 
   167: 
   168:   //スレッド
   169: 
   170:   //run ()
   171:   @Override public void run () {
   172:     //アリーナを開く
   173:     //  アリーナはスレッドを跨げない
   174:     try (Arena arena = Arena.ofConfined ()) {
   175:       //ライブラリを開く
   176:       SymbolLookup xinputLibrary = SymbolLookup.libraryLookup ("XInput1_4.dll", arena);
   177:       //関数をリンクする
   178:       Linker linker = Linker.nativeLinker ();
   179:       //  XInputGetState
   180:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/xinput/nf-xinput-xinputgetstate
   181:       XInputGetState = linker.downcallHandle (
   182:         xinputLibrary.findOrThrow ("XInputGetState"),
   183:         FunctionDescriptor.of (
   184:           DWORD,  //return
   185:           DWORD,  //dwUserIndex
   186:           ValueLayout.ADDRESS));  //XINPUT_STATE *
   187:       //構造体の領域を確保する
   188:       state = arena.allocate (STATE_LAYOUT);
   189:       gamepad = state.asSlice (STATE_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("Gamepad")));
   190:       //ポーリングを開始する
   191:       //  使用できるコントローラは1/100間隔、それ以外はおよそ1/3秒間隔でポーリングする
   192:       while (polling) {
   193:         boolean available = false;  //true=使用できるコントローラがある
   194:         int counter = 0;  //0~31
   195:         for (int index = 0; index < 4; index++) {
   196:           Gamepad gamepad = gamepads[index];
   197:           if (counter == 0 || gamepad.isAvailable ()) {
   198:             available = available || gamepad.isAvailable ();
   199:             if (gamepad.getState () &&  //状態を取得する。変化したかつ
   200:                 gamepadListeners.size () != 0) {  //リスナーがある
   201:               if (gamepad.isConnected ()) {  //接続された
   202:                 for (GamepadListener listener : gamepadListeners) {
   203:                   listener.connected (gamepad);
   204:                 }
   205:               } else if (gamepad.isDisconnected ()) {  //切断された
   206:                 for (GamepadListener listener : gamepadListeners) {
   207:                   listener.disconnected (gamepad);
   208:                 }
   209:               }
   210:               if (gamepad.isAvailable ()) {  //使用できる
   211:                 int buttonMasks = gamepad.getPressedButtonMasks ();
   212:                 if (buttonMasks != 0) {  //押されたボタンがある
   213:                   for (GamepadListener listener : gamepadListeners) {
   214:                     listener.buttonPressed (gamepad, buttonMasks);
   215:                   }
   216:                 }
   217:                 buttonMasks = gamepad.getReleasedButtonMasks ();
   218:                 if (buttonMasks != 0) {  //離されたボタンがある
   219:                   for (GamepadListener listener : gamepadListeners) {
   220:                     listener.buttonReleased (gamepad, buttonMasks);
   221:                   }
   222:                 }
   223:                 if (gamepad.isLeftStickMovedX ()) {  //左スティックがX方向に動いた
   224:                   for (GamepadListener listener : gamepadListeners) {
   225:                     listener.leftStickMovedX (gamepad);
   226:                   }
   227:                 }
   228:                 if (gamepad.isLeftStickMovedY ()) {  //左スティックがY方向に動いた
   229:                   for (GamepadListener listener : gamepadListeners) {
   230:                     listener.leftStickMovedY (gamepad);
   231:                   }
   232:                 }
   233:                 if (gamepad.isLeftTriggerMoved ()) {  //左トリガーが動いた
   234:                   for (GamepadListener listener : gamepadListeners) {
   235:                     listener.leftTriggerMoved (gamepad);
   236:                   }
   237:                 }
   238:                 if (gamepad.isRightStickMovedX ()) {  //右スティックがX方向に動いた
   239:                   for (GamepadListener listener : gamepadListeners) {
   240:                     listener.rightStickMovedX (gamepad);
   241:                   }
   242:                 }
   243:                 if (gamepad.isRightStickMovedY ()) {  //右スティックがY方向に動いた
   244:                   for (GamepadListener listener : gamepadListeners) {
   245:                     listener.rightStickMovedY (gamepad);
   246:                   }
   247:                 }
   248:                 if (gamepad.isRightTriggerMoved ()) {  //右トリガーが動いた
   249:                   for (GamepadListener listener : gamepadListeners) {
   250:                     listener.rightTriggerMoved (gamepad);
   251:                   }
   252:                 }
   253:               }  //if 使用できる
   254:             }  //if 変化したかつリスナーがある
   255:           }  //if counter==0||isAvailable
   256:         }  //for index
   257:         try {
   258:           if (available) {  //使用できるコントローラがある
   259:             counter = (counter + 1) & 31;
   260:             Thread.sleep (10L);
   261:           } else {  //使用できるコントローラがない
   262:             counter = 0;
   263:             Thread.sleep (320L);
   264:           }
   265:         } catch (InterruptedException ie) {  //割り込まれた
   266:           return;  //終了する
   267:         }
   268:       }  //while polling
   269:     }  //try
   270:   }  //run
   271: 
   272: 
   273: 
   274:   //公開インスタンスメソッド
   275: 
   276:   //end ()
   277:   //  終了する
   278:   public void end () {
   279:     //ポーリングを終了する
   280:     if (polling) {
   281:       polling = false;
   282:       if (this.isAlive ()) {  //スレッドがある
   283:         this.interrupt ();  //割り込む
   284:         try {
   285:           this.join ();  //止まるまで待つ
   286:         } catch (InterruptedException ie) {
   287:         }
   288:       }
   289:     }
   290:     //ゲームパッドを閉じる
   291:     for (int index = 0; index < 4; index++) {
   292:       gamepads[index].close ();
   293:     }
   294:   }
   295: 
   296:   //buttonMasks = getButtonMasks (index)
   297:   //  すべての押されているボタンのマスクを返す
   298:   //  index  コントローラのインデックス
   299:   //  buttonMasks  すべての押されているボタンのマスク
   300:   public int getButtonMasks (int index) {
   301:     return gamepads[index].getButtonMasks ();
   302:   }
   303: 
   304:   //leftStickX = getLeftStickX (index)
   305:   //  左スティックのX方向の位置を返す
   306:   //  index  コントローラのインデックス
   307:   //  leftStickX  左スティックのX方向の位置
   308:   public int getLeftStickX (int index) {
   309:     return gamepads[index].getLeftStickX ();
   310:   }
   311: 
   312:   //leftStickY = getLeftStickY (index)
   313:   //  左スティックのY方向の位置を返す
   314:   //  index  コントローラのインデックス
   315:   //  leftStickY  左スティックのY方向の位置
   316:   public int getLeftStickY (int index) {
   317:     return gamepads[index].getLeftStickY ();
   318:   }
   319: 
   320:   //leftTrigger = getLeftTrigger (index)
   321:   //  左トリガーの位置を返す
   322:   //  index  コントローラのインデックス
   323:   //  leftTrigger  左トリガーの位置
   324:   public int getLeftTrigger (int index) {
   325:     return gamepads[index].getLeftTrigger ();
   326:   }
   327: 
   328:   //rightStickX = getRightStickX (index)
   329:   //  右スティックのX方向の位置を返す
   330:   //  index  コントローラのインデックス
   331:   //  rightStickX  右スティックのX方向の位置
   332:   public int getRightStickX (int index) {
   333:     return gamepads[index].getRightStickX ();
   334:   }
   335: 
   336:   //rightStickY = getRightStickY (index)
   337:   //  右スティックのY方向の位置を返す
   338:   //  index  コントローラのインデックス
   339:   //  rightStickY  右スティックのY方向の位置
   340:   public int getRightStickY (int index) {
   341:     return gamepads[index].getRightStickY ();
   342:   }
   343: 
   344:   //rightTrigger = getRightTrigger (index)
   345:   //  右トリガーの位置を返す
   346:   //  index  コントローラのインデックス
   347:   //  rightTrigger  右トリガーの位置
   348:   public int getRightTrigger (int index) {
   349:     return gamepads[index].getRightTrigger ();
   350:   }
   351: 
   352:   //available = isAvailable (index)
   353:   //  使用できるかを返す
   354:   //  index  コントローラのインデックス
   355:   //  available  使用できるか
   356:   public boolean isAvailable (int index) {
   357:     return gamepads[index].isAvailable ();
   358:   }
   359: 
   360:   //setThresholdOfLeftStick (index, thresholdOfLeftStick)
   361:   //  左スティックの閾値を設定する
   362:   //  index  コントローラのインデックス
   363:   //  thresholdOfLeftStick  左スティックの閾値
   364:   public void setThresholdOfLeftStick (int index, int thresholdOfLeftStick) {
   365:     gamepads[index].setThresholdOfLeftStick (thresholdOfLeftStick);
   366:   }
   367: 
   368:   //setThresholdOfRightStick (index, thresholdOfRightStick)
   369:   //  右スティックの閾値を設定する
   370:   //  index  コントローラのインデックス
   371:   //  thresholdOfRightStick  右スティックの閾値
   372:   public void setThresholdOfRightStick (int index, int thresholdOfRightStick) {
   373:     gamepads[index].setThresholdOfRightStick (thresholdOfRightStick);
   374:   }
   375: 
   376:   //setThresholdOfLeftTrigger (index, thresholdOfLeftTrigger)
   377:   //  左トリガーの閾値を設定する
   378:   //  index  コントローラのインデックス
   379:   //  thresholdOfLeftTrigger  左トリガーの閾値
   380:   public void setThresholdOfLeftTrigger (int index, int thresholdOfLeftTrigger) {
   381:     gamepads[index].setThresholdOfLeftTrigger (thresholdOfLeftTrigger);
   382:   }
   383: 
   384:   //setThresholdOfRightTrigger (index, thresholdOfRightTrigger)
   385:   //  右トリガーの閾値を設定する
   386:   //  index  コントローラのインデックス
   387:   //  thresholdOfRightTrigger  右トリガーの閾値
   388:   public void setThresholdOfRightTrigger (int index, int thresholdOfRightTrigger) {
   389:     gamepads[index].setThresholdOfRightTrigger (thresholdOfRightTrigger);
   390:   }
   391: 
   392: 
   393: 
   394:   //ゲームパッドリスナー
   395: 
   396:   //interface XInput.GamepadListener
   397:   //  ゲームパッドリスナー
   398:   public interface GamepadListener {
   399:     public void connected (Gamepad gamepad);  //接続された
   400:     public void disconnected (Gamepad gamepad);  //切断された
   401:     public void buttonPressed (Gamepad gamepad, int buttonMasks);  //ボタンが押された
   402:     public void buttonReleased (Gamepad gamepad, int buttonMasks);  //ボタンが離された
   403:     public void leftStickMovedX (Gamepad gamepad);  //左スティックがX方向に動いた
   404:     public void leftStickMovedY (Gamepad gamepad);  //左スティックがY方向に動いた
   405:     public void leftTriggerMoved (Gamepad gamepad);  //左トリガーが動いた
   406:     public void rightStickMovedX (Gamepad gamepad);  //右スティックがX方向に動いた
   407:     public void rightStickMovedY (Gamepad gamepad);  //右スティックがY方向に動いた
   408:     public void rightTriggerMoved (Gamepad gamepad);  //右トリガーが動いた
   409:   }
   410: 
   411:   //addGamepadListener (listener)
   412:   //  ゲームパッドリスナーを追加する
   413:   //  listener  追加するGamepadリスナー
   414:   public void addGamepadListener (GamepadListener listener) {
   415:     if (listener != null && !gamepadListeners.contains (listener)) {
   416:       gamepadListeners.add (listener);
   417:     }
   418:   }
   419: 
   420:   //removeGamepadListener (listener)
   421:   //  ゲームパッドリスナーを削除する
   422:   //  listener  削除するGamepadリスナー
   423:   public void removeGamepadListener (GamepadListener listener) {
   424:     gamepadListeners.remove (listener);
   425:   }
   426: 
   427:   //removeGamepadListeners ()
   428:   //  すべてのゲームパッドリスナーを削除する
   429:   public void removeGamepadListeners () {
   430:     gamepadListeners.clear ();
   431:   }
   432: 
   433:   //getGamepadListeners ()
   434:   //  すべてのゲームパッドリスナーを取得する
   435:   public GamepadListener[] getGamepadListeners () {
   436:     return gamepadListeners.toArray (new GamepadListener[gamepadListeners.size ()]);
   437:   }
   438: 
   439: 
   440: 
   441:   //ゲームパッドアダプター
   442: 
   443:   //class XInput.GamepadAdapter
   444:   //  ゲームパッドアダプター
   445:   public static class GamepadAdapter implements GamepadListener {
   446:     @Override public void connected (Gamepad gamepad) {
   447:     }
   448:     @Override public void disconnected (Gamepad gamepad) {
   449:     }
   450:     @Override public void buttonPressed (Gamepad gamepad, int buttonMasks) {
   451:     }
   452:     @Override public void buttonReleased (Gamepad gamepad, int buttonMasks) {
   453:     }
   454:     @Override public void leftStickMovedX (Gamepad gamepad) {
   455:     }
   456:     @Override public void leftStickMovedY (Gamepad gamepad) {
   457:     }
   458:     @Override public void leftTriggerMoved (Gamepad gamepad) {
   459:     }
   460:     @Override public void rightStickMovedX (Gamepad gamepad) {
   461:     }
   462:     @Override public void rightStickMovedY (Gamepad gamepad) {
   463:     }
   464:     @Override public void rightTriggerMoved (Gamepad gamepad) {
   465:     }
   466:   }
   467: 
   468: 
   469: 
   470:   //ゲームパッドクラス
   471: 
   472:   //class XInput.Gamepad
   473:   //  ゲームパッドクラス
   474:   public class Gamepad implements AutoCloseable {
   475: 
   476:     //閾値
   477:     //  スティックの閾値を小さくしすぎると初動の方向がブレやすくなる
   478:     //  トリガーは小さくしても押し込まないと入らない印象。コントローラにもよるのだろう
   479:     static final int THRESHOLD_OF_STICK = (32768 / 2);
   480:     static final int THRESHOLD_OF_TRIGGER = (256 / 4);
   481: 
   482:     //ワークエリア
   483:     int index;  //コントローラのインデックス。0~3
   484:     //  今回の状態
   485:     boolean available;  //true=XInputGetStateがERROR_SUCCESSを返した
   486:     int dwPacketNumber;  //ここからXInputGetStateが出力したXINPUT_STATE構造体から取り出した値
   487:     int wButtons;
   488:     int bLeftTrigger;
   489:     int bRightTrigger;
   490:     int sThumbLX;
   491:     int sThumbLY;
   492:     int sThumbRX;
   493:     int sThumbRY;  //ここまで
   494:     int buttonMasks;  //スティックとトリガーの位置を加えたボタンのマスク
   495:     //  前回の状態
   496:     boolean previous_available;
   497:     int previous_dwPacketNumber;
   498:     int previous_wButtons;
   499:     int previous_bLeftTrigger;
   500:     int previous_bRightTrigger;
   501:     int previous_sThumbLX;
   502:     int previous_sThumbLY;
   503:     int previous_sThumbRX;
   504:     int previous_sThumbRY;
   505:     int previous_buttonMasks;
   506:     //  設定
   507:     long thresholdOfLeftStick2;  //左スティックの閾値の2乗
   508:     long thresholdOfRightStick2;  //右スティックの閾値の2乗
   509:     int thresholdOfLeftTrigger;  //左トリガーの閾値
   510:     int thresholdOfRightTrigger;  //右トリガーの閾値
   511: 
   512:     //new Gamepad (index)
   513:     //  コンストラクタ
   514:     //  index  コントローラのインデックス。0~3
   515:     public Gamepad (int index) {
   516:       open (index);
   517:     }
   518: 
   519:     //close ()
   520:     //  閉じる
   521:     @Override public void close () {
   522:       available = false;
   523:       dwPacketNumber = 0;
   524:       wButtons = 0;
   525:       bLeftTrigger = 0;
   526:       bRightTrigger = 0;
   527:       sThumbLX = 0;
   528:       sThumbLY = 0;
   529:       sThumbRX = 0;
   530:       sThumbRY = 0;
   531:       buttonMasks = 0;
   532:       previous_available = false;
   533:       previous_dwPacketNumber = 0;
   534:       previous_wButtons = 0;
   535:       previous_bLeftTrigger = 0;
   536:       previous_bRightTrigger = 0;
   537:       previous_sThumbLX = 0;
   538:       previous_sThumbLY = 0;
   539:       previous_sThumbRX = 0;
   540:       previous_sThumbRY = 0;
   541:       previous_buttonMasks = 0;
   542:     }
   543: 
   544:     //buttonMasks = getButtonMasks ()
   545:     //  getState()で取得したすべての押されているボタンのマスクを返す
   546:     //  buttonMasks  すべての押されているボタンのマスク
   547:     public int getButtonMasks () {
   548:       return buttonMasks;
   549:     }
   550: 
   551:     //index = getIndex ()
   552:     //  コントローラのインデックスを返す
   553:     //  index  コントローラのインデックス
   554:     public int getIndex () {
   555:       return index;
   556:     }
   557: 
   558:     //leftStickX = getLeftStickX ()
   559:     //  getState()で取得した左スティックのX方向の位置を返す
   560:     //  leftStickX  左スティックのX方向の位置
   561:     public int getLeftStickX () {
   562:       return sThumbLX;
   563:     }
   564: 
   565:     //leftStickY = getLeftStickY ()
   566:     //  getState()で取得した左スティックのY方向の位置を返す
   567:     //  leftStickY  左スティックのY方向の位置
   568:     public int getLeftStickY () {
   569:       return sThumbLY;
   570:     }
   571: 
   572:     //leftTrigger = getLeftTrigger ()
   573:     //  getState()で取得した左トリガーの位置を返す
   574:     //  leftTrigger  左トリガーの位置
   575:     public int getLeftTrigger () {
   576:       return bLeftTrigger;
   577:     }
   578: 
   579:     //pressedButtonMasks = getPressedButtonMasks ()
   580:     //  getState()で取得したすべての押されたボタンのマスクを返す
   581:     //  pressedButtonMasks  すべての押されたボタンのマスク
   582:     public int getPressedButtonMasks () {
   583:       return buttonMasks & ~previous_buttonMasks;
   584:     }
   585: 
   586:     //releasedButtonMasks = getReleasedButtonMasks ()
   587:     //  getState()で取得したすべての離されたボタンのマスクを返す
   588:     //  releasedButtonMasks  すべての離されたボタンのマスク
   589:     public int getReleasedButtonMasks () {
   590:       return ~buttonMasks & previous_buttonMasks;
   591:     }
   592: 
   593:     //rightStickX = getRightStickX ()
   594:     //  getState()で取得した右スティックのX方向の位置を返す
   595:     //  rightStickX  右スティックのX方向の位置
   596:     public int getRightStickX () {
   597:       return sThumbRX;
   598:     }
   599: 
   600:     //rightStickY = getRightStickY ()
   601:     //  getState()で取得した右スティックのY方向の位置を返す
   602:     //  rightStickY  右スティックのY方向の位置
   603:     public int getRightStickY () {
   604:       return sThumbRY;
   605:     }
   606: 
   607:     //rightTrigger = getRightTrigger ()
   608:     //  getState()で取得した右トリガーの位置を返す
   609:     //  rightTrigger  右トリガーの位置
   610:     public int getRightTrigger () {
   611:       return bRightTrigger;
   612:     }
   613: 
   614:     //changed = getState ()
   615:     //  コントローラの状態を取得する
   616:     //  changed  false=変化しなかった,true=変化した。使用可能とは限らない
   617:     public boolean getState () {
   618:       //前回の状態を保存する
   619:       previous_available = available;
   620:       previous_wButtons = wButtons;
   621:       previous_bLeftTrigger = bLeftTrigger;
   622:       previous_bRightTrigger = bRightTrigger;
   623:       previous_sThumbLX = sThumbLX;
   624:       previous_sThumbLY = sThumbLY;
   625:       previous_sThumbRX = sThumbRX;
   626:       previous_sThumbRY = sThumbRY;
   627:       previous_buttonMasks = buttonMasks;
   628:       //今回の状態を取得する
   629:       available = XInputGetState != null;
   630:       if (available) {
   631:         try {
   632:           available = (int) XInputGetState.invoke (index, state) == ERROR_SUCCESS;
   633:         } catch (Throwable e) {
   634:           //e.printStackTrace ();
   635:         }
   636:       }
   637:       if (available) {  //使用できる
   638:         dwPacketNumber = state.get (DWORD, OFFSET_dwPacketNumber);
   639:         wButtons = (char) gamepad.get (WORD, OFFSET_wButtons);
   640:         bLeftTrigger = 0xff & gamepad.get (BYTE, OFFSET_bLeftTrigger);
   641:         bRightTrigger = 0xff & gamepad.get (BYTE, OFFSET_bRightTrigger);
   642:         sThumbLX = gamepad.get (SHORT, OFFSET_sThumbLX);
   643:         sThumbLY = gamepad.get (SHORT, OFFSET_sThumbLY);
   644:         sThumbRX = gamepad.get (SHORT, OFFSET_sThumbRX);
   645:         sThumbRY = gamepad.get (SHORT, OFFSET_sThumbRY);
   646:       } else {  //使用できない
   647:         dwPacketNumber = 0;
   648:         wButtons = 0;
   649:         bLeftTrigger = 0;
   650:         bRightTrigger = 0;
   651:         sThumbLX = 0;
   652:         sThumbLY = 0;
   653:         sThumbRX = 0;
   654:         sThumbRY = 0;
   655:       }
   656:       //ボタンのマスクにスティックとトリガーの位置を加える
   657:       int masks = wButtons;
   658:       //左スティック
   659:       {
   660:         int x = sThumbLX;
   661:         int y = sThumbLY;
   662:         if (thresholdOfLeftStick2 < ((long) x * (long) x + (long) y * (long) y)) {  //閾値の円盤の外側。オーバーフローに注意
   663:           int px = 5741 * x - 2378 * y;  //+xから+yへpi/8回転させた方向。tan(pi/8)≒2378/5741
   664:           int py = 2378 * x + 5741 * y;
   665:           int qx = 5741 * x + 2378 * y;  //+xから+yへ-pi/8回転させた方向
   666:           int qy = -2378 * x + 5741 * y;
   667:           masks |= ((0 <= (-px | -qx) ? LSLEFT_MASK : 0 <= (px | qx) ? LSRIGHT_MASK : 0) |
   668:                     (0 <= (-py | -qy) ? LSDOWN_MASK : 0 <= (py | qy) ? LSUP_MASK : 0));  //8方向判別
   669:         }
   670:       }
   671:       //右スティック
   672:       {
   673:         int x = sThumbRX;
   674:         int y = sThumbRY;
   675:         if (thresholdOfRightStick2 < ((long) x * (long) x + (long) y * (long) y)) {  //閾値の円盤の外側。オーバーフローに注意
   676:           int px = 5741 * x - 2378 * y;  //+xから+yへpi/8回転させた方向。tan(pi/8)≒2378/5741
   677:           int py = 2378 * x + 5741 * y;
   678:           int qx = 5741 * x + 2378 * y;  //+xから+yへ-pi/8回転させた方向
   679:           int qy = -2378 * x + 5741 * y;
   680:           masks |= ((0 <= (-px | -qx) ? RSLEFT_MASK : 0 <= (px | qx) ? RSRIGHT_MASK : 0) |
   681:                     (0 <= (-py | -qy) ? RSDOWN_MASK : 0 <= (py | qy) ? RSUP_MASK : 0));  //8方向判別
   682:         }
   683:       }
   684:       //左トリガー
   685:       if (thresholdOfLeftTrigger < bLeftTrigger) {  //閾値より大きい
   686:         masks |= LTRIGGER_MASK;
   687:       }
   688:       //右トリガー
   689:       if (thresholdOfRightTrigger < bRightTrigger) {  //閾値より大きい
   690:         masks |= RTRIGGER_MASK;
   691:       }
   692:       buttonMasks = masks;
   693:       //変化したかどうかを返す
   694:       return ((available != previous_available) ||  //使用できるかが変化したか
   695:               (available &&  //使用できて
   696:                dwPacketNumber != previous_dwPacketNumber));  //変化した
   697:     }
   698: 
   699:     //available = isAvailable ()
   700:     //  使用できるかを返す
   701:     //  available  使用できるか
   702:     public boolean isAvailable () {
   703:       return available;
   704:     }
   705: 
   706:     //connected = isConnected ()
   707:     //  接続されたかを返す
   708:     //  connected  接続されたか
   709:     public boolean isConnected () {
   710:       return available && !previous_available;
   711:     }
   712: 
   713:     //disconnected = isDisconnected ()
   714:     //  切断されたかを返す
   715:     //  disconnected  切断されたか
   716:     public boolean isDisconnected () {
   717:       return !available && previous_available;
   718:     }
   719: 
   720:     //leftStickMovedX = isLeftStickMovedX ()
   721:     //  getState()で取得した左スティックがX方向に動いたかを返す
   722:     //  leftStickMovedX  左スティックがX方向に動いたか
   723:     public boolean isLeftStickMovedX () {
   724:       return sThumbLX != previous_sThumbLX;
   725:     }
   726: 
   727:     //leftStickMovedY = isLeftStickMovedY ()
   728:     //  getState()で取得した左スティックがY方向に動いたかを返す
   729:     //  leftStickMovedY  左スティックがY方向に動いたか
   730:     public boolean isLeftStickMovedY () {
   731:       return sThumbLY != previous_sThumbLY;
   732:     }
   733: 
   734:     //leftTriggerMoved = isLeftTriggerMoved ()
   735:     //  getState()で取得した左トリガーが動いたかを返す
   736:     //  leftTriggerMoved  左トリガーが動いたか
   737:     public boolean isLeftTriggerMoved () {
   738:       return bLeftTrigger != previous_bLeftTrigger;
   739:     }
   740: 
   741:     //rightStickMovedX = isRightStickMovedX ()
   742:     //  getState()で取得した右スティックがX方向に動いたかを返す
   743:     //  rightStickMovedX  右スティックがX方向に動いたか
   744:     public boolean isRightStickMovedX () {
   745:       return sThumbRX != previous_sThumbRX;
   746:     }
   747: 
   748:     //rightStickMovedY = isRightStickMovedY ()
   749:     //  getState()で取得した右スティックがY方向に動いたかを返す
   750:     //  rightStickMovedY  右スティックがY方向に動いたか
   751:     public boolean isRightStickMovedY () {
   752:       return sThumbRY != previous_sThumbRY;
   753:     }
   754: 
   755:     //rightTriggerMoved = isRightTriggerMoved ()
   756:     //  getState()で取得した右トリガーが動いたかを返す
   757:     //  rightTriggerMoved  右トリガーが動いたか
   758:     public boolean isRightTriggerMoved () {
   759:       return bRightTrigger != previous_bRightTrigger;
   760:     }
   761: 
   762:     //open (index)
   763:     //  開く
   764:     //  index  コントローラのインデックス。0~3
   765:     private void open (int index) {
   766:       //コントローラのインデックスを確認する
   767:       if (index < 0 || 3 < index) {
   768:         throw new IllegalArgumentException (String.format ("Controller %d cannot be specified", index));
   769:       }
   770:       //ワークエリアを初期化する
   771:       this.index = index;
   772:       available = false;
   773:       dwPacketNumber = 0;
   774:       wButtons = 0;
   775:       bLeftTrigger = 0;
   776:       bRightTrigger = 0;
   777:       sThumbLX = 0;
   778:       sThumbLY = 0;
   779:       sThumbRX = 0;
   780:       sThumbRY = 0;
   781:       buttonMasks = 0;
   782:       previous_available = false;
   783:       previous_dwPacketNumber = 0;
   784:       previous_wButtons = 0;
   785:       previous_bLeftTrigger = 0;
   786:       previous_bRightTrigger = 0;
   787:       previous_sThumbLX = 0;
   788:       previous_sThumbLY = 0;
   789:       previous_sThumbRX = 0;
   790:       previous_sThumbRY = 0;
   791:       previous_buttonMasks = 0;
   792:       thresholdOfLeftStick2 = (long) THRESHOLD_OF_STICK * (long) THRESHOLD_OF_STICK;
   793:       thresholdOfRightStick2 = (long) THRESHOLD_OF_STICK * (long) THRESHOLD_OF_STICK;
   794:       thresholdOfLeftTrigger = THRESHOLD_OF_TRIGGER;
   795:       thresholdOfRightTrigger = THRESHOLD_OF_TRIGGER;
   796:     }
   797: 
   798:     //setThresholdOfLeftStick (thresholdOfLeftStick)
   799:     //  左スティックの閾値を設定する
   800:     //  thresholdOfLeftStick  左スティックの閾値
   801:     public void setThresholdOfLeftStick (int thresholdOfLeftStick) {
   802:       thresholdOfLeftStick2 = (long) thresholdOfLeftStick * (long) thresholdOfLeftStick;
   803:     }
   804: 
   805:     //setThresholdOfRightStick (thresholdOfRightStick)
   806:     //  右スティックの閾値を設定する
   807:     //  thresholdOfRightStick  右スティックの閾値
   808:     public void setThresholdOfRightStick (int thresholdOfRightStick) {
   809:       thresholdOfRightStick2 = (long) thresholdOfRightStick * (long) thresholdOfRightStick;
   810:     }
   811: 
   812:     //setThresholdOfLeftTrigger (thresholdOfLeftTrigger)
   813:     //  左トリガーの閾値を設定する
   814:     //  thresholdOfLeftTrigger  左トリガーの閾値
   815:     public void setThresholdOfLeftTrigger (int thresholdOfLeftTrigger) {
   816:       this.thresholdOfLeftTrigger = thresholdOfLeftTrigger;
   817:     }
   818: 
   819:     //setThresholdOfRightTrigger (thresholdOfRightTrigger)
   820:     //  右トリガーの閾値を設定する
   821:     //  thresholdOfRightTrigger  右トリガーの閾値
   822:     public void setThresholdOfRightTrigger (int thresholdOfRightTrigger) {
   823:       this.thresholdOfRightTrigger = thresholdOfRightTrigger;
   824:     }
   825: 
   826:   }
   827: 
   828: 
   829: 
   830: }  //class XInput