CyberStickAnalog.java
     1: //========================================================================================
     2: //  CyberStickAnalog.java
     3: //    en:CYBER STICK (analog mode)
     4: //    ja:サイバースティック(アナログモード)
     5: //  Copyright (C) 2003-2024 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: //サイバースティック(アナログモード)
    14: //
    15: //  データ
    16: //    +-------+---------------------------------------------------------------+
    17: //    | 出力  |                             入力                              |
    18: //    +-------+-------+-------+-------+-------+-------+-------+-------+-------+
    19: //    | pin8  |       | pin7  | pin6  |       | pin4  | pin3  | pin2  | pin1  |
    20: //    |       +-------+-------+-------+-------+-------+-------+-------+-------+
    21: //    |       |   7   |   6   |   5   |   4   |   3   |   2   |   1   |   0   |
    22: //    +-------+-------+-------+-------+-------+-------+-------+-------+-------+
    23: //    |  REQ  |       |  ACK  |  L/H  |       |  D3   |  D2   |  D1   |  D0   |
    24: //    +-------+-------+-------+-------+-------+-------+-------+-------+-------+
    25: //    | 1→0  |   1   |   0   |   0   |   1   | A+A'  | B+B'  |   C   |   D   |
    26: //    +-------+-------+-------+-------+-------+-------+-------+-------+-------+
    27: //    |       |   1   |   0   |   1   |   1   |  E1   |  E2   | START |SELECT |
    28: //    +-------+-------+-------+-------+-------+-------+-------+-------+-------+
    29: //    |       |   1   |   0   |   0   |   1   |      STICK UP/DOWN UPPER      |
    30: //    +-------+-------+-------+-------+-------+-------------------------------+
    31: //    |       |   1   |   0   |   1   |   1   |    STICK LEFT/RIGHT UPPER     |
    32: //    +-------+-------+-------+-------+-------+-------------------------------+
    33: //    |       |   1   |   0   |   0   |   1   |        THROTTLE UPPER         |
    34: //    +-------+-------+-------+-------+-------+-------------------------------+
    35: //    |       |   1   |   0   |   1   |   1   |         OPTION UPPER          |
    36: //    +-------+-------+-------+-------+-------+-------------------------------+
    37: //    |       |   1   |   0   |   0   |   1   |      STICK UP/DOWN LOWER      |
    38: //    +-------+-------+-------+-------+-------+-------------------------------+
    39: //    |       |   1   |   0   |   1   |   1   |    STICK LEFT/RIGHT LOWER     |
    40: //    +-------+-------+-------+-------+-------+-------------------------------+
    41: //    |       |   1   |   0   |   0   |   1   |        THROTTLE LOWER         |
    42: //    +-------+-------+-------+-------+-------+-------------------------------+
    43: //    |       |   1   |   0   |   1   |   1   |         OPTION LOWER          |
    44: //    +-------+-------+-------+-------+-------+-------+-------+-------+-------+
    45: //    |       |   1   |   0   |   0   |   1   |   A   |   B   |  A'   |  B'   |
    46: //    +-------+-------+-------+-------+-------+-------+-------+-------+-------+
    47: //    |       |   1   |   0   |   1   |   1   |   1   |   1   |   1   |   1   |
    48: //    +-------+-------+-------+-------+-------+-------+-------+-------+-------+
    49: //    REQの立ち下がりから50us毎に12個のデータを出力する
    50: //    スティックとスロットルの上下は0=上,…,127=128=中央,…,255=下
    51: //    左右は0=左,…,127=128=中央,…,255=右
    52: //    ボタンは0=押されている,1=押されていない
    53: //
    54: //  参考
    55: //    http://retropc.net/x68000/software/hardware/analog/ajoy/
    56: //    https://ktjdragon.com/nb/misc/atari_cyberstick
    57: 
    58: package xeij;
    59: 
    60: import java.awt.*;
    61: import java.awt.event.*;
    62: import java.util.*;
    63: import javax.swing.*;
    64: import javax.swing.event.*;
    65: 
    66: //class CyberStickAnalog
    67: //  サイバースティック(アナログモード)
    68: public class CyberStickAnalog extends Joystick implements ActionListener, ChangeListener, FocusListener, XInput.GamepadListener, KeyListener {
    69: 
    70:   protected static final int SUP_BIT           =  0;  //STICK ↑
    71:   protected static final int SLEFT_BIT         =  1;  //STICK ←
    72:   protected static final int TUP_BIT           =  2;  //THROTTLE ↑
    73:   protected static final int OLEFT_BIT         =  3;  //OPTION ←
    74:   protected static final int STICKS            =  4;  //スティックの数
    75:   protected static final int A_BIT             =  4;  //A
    76:   protected static final int APRIME_BIT        =  5;  //A'
    77:   protected static final int B_BIT             =  6;  //B
    78:   protected static final int BPRIME_BIT        =  7;  //B'
    79:   protected static final int C_BIT             =  8;  //C
    80:   protected static final int D_BIT             =  9;  //D
    81:   protected static final int E1_BIT            = 10;  //E1
    82:   protected static final int E2_BIT            = 11;  //E2
    83:   protected static final int SELECT_BIT        = 12;  //SELECT
    84:   protected static final int START_BIT         = 13;  //START
    85:   protected static final int STICK_AND_BUTTONS = 14;  //スティックとボタンの数
    86: 
    87:   protected static final int SUP_MASK          = (1 << SUP_BIT);
    88:   protected static final int SLEFT_MASK        = (1 << SLEFT_BIT);
    89:   protected static final int TUP_MASK          = (1 << TUP_BIT);
    90:   protected static final int OLEFT_MASK        = (1 << OLEFT_BIT);
    91:   protected static final int A_MASK            = (1 << A_BIT);
    92:   protected static final int APRIME_MASK       = (1 << APRIME_BIT);
    93:   protected static final int B_MASK            = (1 << B_BIT);
    94:   protected static final int BPRIME_MASK       = (1 << BPRIME_BIT);
    95:   protected static final int C_MASK            = (1 << C_BIT);
    96:   protected static final int D_MASK            = (1 << D_BIT);
    97:   protected static final int E1_MASK           = (1 << E1_BIT);
    98:   protected static final int E2_MASK           = (1 << E2_BIT);
    99:   protected static final int SELECT_MASK       = (1 << SELECT_BIT);
   100:   protected static final int START_MASK        = (1 << START_BIT);
   101: 
   102:   protected static final String[] BIT_TO_TEXT = {
   103:     "STICK ↑",
   104:     "STICK ←",
   105:     "THROTTLE ↑",
   106:     "OPTION ←",
   107:     "A",
   108:     "A'",
   109:     "B",
   110:     "B'",
   111:     "C",
   112:     "D",
   113:     "E1",
   114:     "E2",
   115:     "SELECT",
   116:     "START",
   117:   };
   118: 
   119:   protected static final boolean[] BIT_TO_REPEATABLE = {
   120:     false,  //STICK ↑
   121:     false,  //STICK ←
   122:     false,  //THROTTLE ↑
   123:     false,  //OPTION ←
   124:     true,   //A
   125:     true,   //A'
   126:     true,   //B
   127:     true,   //B'
   128:     true,   //C
   129:     true,   //D
   130:     true,   //E1
   131:     true,   //E2
   132:     false,  //SELECT
   133:     false,  //START
   134:   };
   135: 
   136:   protected static final int MAP_CODE     = STICK_AND_BUTTONS * 0;  //XInputコードとキーコード
   137:   protected static final int MAP_REPEAT   = STICK_AND_BUTTONS * 1;  //連射有効
   138:   protected static final int MAP_DELAY    = STICK_AND_BUTTONS * 2;  //連射開始
   139:   protected static final int MAP_INTERVAL = STICK_AND_BUTTONS * 3;  //連射間隔
   140:   protected static final int MAP_LENGTH   = STICK_AND_BUTTONS * 4;
   141:   protected int[] map;
   142: 
   143:   protected int xinputFocusedButton;  //フォーカスされているXInputテキストフィールドの番号。-1=なし
   144:   protected long[] startTimeOf;  //連射開始時刻。ボタンが押されたとき初期化して押されている間だけ使う
   145:   protected int lastButtons;  //前回押されていたボタン。XInputを含む
   146: 
   147:   protected int req;  //REQ
   148:   protected long transferStartTime;  //転送開始時刻。REQの立ち下がり
   149:   protected static final int TRANSFER_STEPS = 12;  //転送データの数。必要なデータは11個だがAJOY.Xが12個読んでいる
   150:   protected int[] transferData;  //転送データ
   151: 
   152:   protected JTextField[] xinputTextFieldOf = new JTextField[STICK_AND_BUTTONS];
   153:   protected JCheckBox[] repeatCheckBoxOf = new JCheckBox[STICK_AND_BUTTONS];
   154:   protected SpinnerNumberModel[] delayModelOf = new SpinnerNumberModel[STICK_AND_BUTTONS];
   155:   protected JSpinner[] delaySpinnerOf = new JSpinner[STICK_AND_BUTTONS];
   156:   protected SpinnerNumberModel[] intervalModelOf = new SpinnerNumberModel[STICK_AND_BUTTONS];
   157:   protected JSpinner[] intervalSpinnerOf = new JSpinner[STICK_AND_BUTTONS];
   158: 
   159:   //  map[i]=xCode<<16|keyCode
   160:   //  xCode=map[i]>>>16
   161:   //  keyCode=map[i]&65535
   162:   //  xCode=128|xIndex<<5|xBit
   163:   //  xIndex=(xCode>>5)&3
   164:   //  xBit=xCode&31
   165:   protected final int[][] defaultMaps = new int[][] {
   166:     {
   167:       (128 | 0 << 5 | XInput.RSUP_BIT   ) << 16 | KeyEvent.VK_UNDEFINED,  //STICK ↑
   168:       (128 | 0 << 5 | XInput.RSLEFT_BIT ) << 16 | KeyEvent.VK_UNDEFINED,  //STICK ←
   169:       (128 | 0 << 5 | XInput.LSUP_BIT   ) << 16 | KeyEvent.VK_UNDEFINED,  //THROTTLE ↑
   170:       (128 | 0 << 5 | XInput.LSLEFT_BIT ) << 16 | KeyEvent.VK_UNDEFINED,  //OPTION ←
   171:       (128 | 0 << 5 | XInput.RSTICK_BIT ) << 16 | KeyEvent.VK_UNDEFINED,  //A。右スティック
   172:       (128 | 0 << 5 | XInput.A_BIT      ) << 16 | KeyEvent.VK_UNDEFINED,  //A'。台座
   173:       (128 | 0 << 5 | XInput.X_BIT      ) << 16 | KeyEvent.VK_UNDEFINED,  //B。右スティック
   174:       (128 | 0 << 5 | XInput.B_BIT      ) << 16 | KeyEvent.VK_UNDEFINED,  //B'。台座
   175:       (128 | 0 << 5 | XInput.Y_BIT      ) << 16 | KeyEvent.VK_UNDEFINED,  //C。台座
   176:       (128 | 0 << 5 | XInput.LSTICK_BIT ) << 16 | KeyEvent.VK_UNDEFINED,  //D。左スティック
   177:       (128 | 0 << 5 | XInput.LB_BIT     ) << 16 | KeyEvent.VK_UNDEFINED,  //E1。左スティック
   178:       (128 | 0 << 5 | XInput.RB_BIT     ) << 16 | KeyEvent.VK_UNDEFINED,  //E2。左スティック
   179:       (128 | 0 << 5 | XInput.BACK_BIT   ) << 16 | KeyEvent.VK_UNDEFINED,  //SELECT
   180:       (128 | 0 << 5 | XInput.START_BIT  ) << 16 | KeyEvent.VK_UNDEFINED,  //START
   181:       0, 0, 0, 0,   0,   0,   0,   0,   0,   0,   0,   0, 0, 0,  //連射有効
   182:       0, 0, 0, 0,  50,  50,  50,  50,  50,  50,  50,  50, 0, 0,  //連射開始
   183:       0, 0, 0, 0, 100, 100, 100, 100, 100, 100, 100, 100, 0, 0,  //連射間隔
   184:     },
   185:     {
   186:       (128 | 1 << 5 | XInput.RSUP_BIT   ) << 16 | KeyEvent.VK_UNDEFINED,  //STICK ↑
   187:       (128 | 1 << 5 | XInput.RSLEFT_BIT ) << 16 | KeyEvent.VK_UNDEFINED,  //STICK ←
   188:       (128 | 1 << 5 | XInput.LSUP_BIT   ) << 16 | KeyEvent.VK_UNDEFINED,  //THROTTLE ↑
   189:       (128 | 1 << 5 | XInput.LSLEFT_BIT ) << 16 | KeyEvent.VK_UNDEFINED,  //OPTION ←
   190:       (128 | 1 << 5 | XInput.RSTICK_BIT ) << 16 | KeyEvent.VK_UNDEFINED,  //A。右スティック
   191:       (128 | 1 << 5 | XInput.A_BIT      ) << 16 | KeyEvent.VK_UNDEFINED,  //A'。台座
   192:       (128 | 1 << 5 | XInput.X_BIT      ) << 16 | KeyEvent.VK_UNDEFINED,  //B。右スティック
   193:       (128 | 1 << 5 | XInput.B_BIT      ) << 16 | KeyEvent.VK_UNDEFINED,  //B'。台座
   194:       (128 | 1 << 5 | XInput.Y_BIT      ) << 16 | KeyEvent.VK_UNDEFINED,  //C。台座
   195:       (128 | 1 << 5 | XInput.LSTICK_BIT ) << 16 | KeyEvent.VK_UNDEFINED,  //D。左スティック
   196:       (128 | 1 << 5 | XInput.LB_BIT     ) << 16 | KeyEvent.VK_UNDEFINED,  //E1。左スティック
   197:       (128 | 1 << 5 | XInput.RB_BIT     ) << 16 | KeyEvent.VK_UNDEFINED,  //E2。左スティック
   198:       (128 | 1 << 5 | XInput.BACK_BIT   ) << 16 | KeyEvent.VK_UNDEFINED,  //SELECT
   199:       (128 | 1 << 5 | XInput.START_BIT  ) << 16 | KeyEvent.VK_UNDEFINED,  //START
   200:       0, 0, 0, 0,   0,   0,   0,   0,   0,   0,   0,   0, 0, 0,  //連射有効
   201:       0, 0, 0, 0,  50,  50,  50,  50,  50,  50,  50,  50, 0, 0,  //連射開始
   202:       0, 0, 0, 0, 100, 100, 100, 100, 100, 100, 100, 100, 0, 0,  //連射間隔
   203:     },
   204:   };
   205: 
   206:   //new CyberStickAnalog (number)
   207:   //  コンストラクタ
   208:   //  number  枝番号。1~2
   209:   public CyberStickAnalog (int number) {
   210:     this.number = number;
   211:     id = "cyberstickanalog" + number;
   212:     nameEn = "CYBER STICK (analog) #" + number;
   213:     nameJa = "サイバースティック (アナログ) #" + number;
   214:     map = new int[MAP_LENGTH];
   215:     int[] tempMap = Settings.sgsGetIntArray (id);
   216:     if (0 < tempMap.length && tempMap[0] == -2) {  //新フォーマット
   217:       for (int i = 0; i < MAP_LENGTH; i++) {
   218:         map[i] = i + 1 < tempMap.length ? tempMap[i + 1] : 0;
   219:       }
   220:     } else {  //初期値
   221:       System.arraycopy (defaultMaps[number - 1], 0,  //from
   222:                         map, 0,  //to
   223:                         MAP_LENGTH);  //length
   224:     }
   225:     if (PPI.PPI_XINPUT_ON) {
   226:       xinputFocusedButton = -1;
   227:     }
   228:     startTimeOf = new long[STICK_AND_BUTTONS];
   229:     transferData = new int[TRANSFER_STEPS + 1];
   230:     Arrays.fill (transferData, 0b11111111);
   231:     reset ();
   232:     configurationPanel = null;
   233:   }
   234: 
   235:   //tini ()
   236:   //  後始末
   237:   @Override public void tini () {
   238:     if (true &&  //false=初期値と同じでも出力して値を確認する
   239:         Arrays.equals (map, defaultMaps[number - 1])) {  //初期値と同じ
   240:       Settings.sgsPutIntArray (id, new int[0]);  //"none"ではなく""にする
   241:     } else {  //初期値と違う
   242:       int[] tempMap = new int[1 + MAP_LENGTH];
   243:       tempMap[0] = -2;  //新フォーマット
   244:       for (int i = 0; i < MAP_LENGTH; i++) {
   245:         tempMap[1 + i] = map[i];
   246:       }
   247:       Settings.sgsPutIntArray (id, tempMap);
   248:     }
   249:   }
   250: 
   251:   //reset ()
   252:   //  リセット。設定パネルが表示されるとき呼び出される
   253:   @Override public final void reset () {
   254:     lastButtons = 0;
   255:     req = 0;
   256:     transferStartTime = 0L;
   257:   }
   258: 
   259:   private void updateText () {
   260:     for (int i = 0; i < STICK_AND_BUTTONS; i++) {
   261:       String text;
   262:       int xCode = map[MAP_CODE + i] >>> 16;
   263:       if (xCode == 0) {
   264:         text = Multilingual.mlnJapanese ? "なし" : "none";
   265:       } else {
   266:         int xIndex = (xCode >> 5) & 3;
   267:         int xBit = xCode & 31;
   268:         text = "#" + xIndex + " " + XInput.BIT_TO_TEXT[xBit];
   269:       }
   270:       xinputTextFieldOf[i].setText (text);
   271:     }
   272:   }
   273: 
   274: 
   275:   //ボタンのアクションリスナー
   276:   @Override public void actionPerformed (ActionEvent ae) {
   277:     Object source = ae.getSource ();
   278:     String command = ae.getActionCommand ();
   279:     if (command.equals ("Reset to default values")) {  //初期値に戻す
   280:       if (Arrays.equals (map, defaultMaps[number - 1])) {  //初期値と同じ
   281:         JOptionPane.showMessageDialog (
   282:           null,
   283:           Multilingual.mlnJapanese ? nameJa + " の設定は初期値と同じです" : nameEn + " settings are equals to default values",
   284:           Multilingual.mlnJapanese ? "確認" : "Confirmation",
   285:           JOptionPane.PLAIN_MESSAGE);
   286:         return;
   287:       }
   288:       if (JOptionPane.showConfirmDialog (
   289:         null,
   290:         Multilingual.mlnJapanese ? nameJa + " の設定を初期値に戻しますか?" : "Do you want to reset " + nameEn + " settings to default values?",
   291:         Multilingual.mlnJapanese ? "確認" : "Confirmation",
   292:         JOptionPane.YES_NO_OPTION,
   293:         JOptionPane.PLAIN_MESSAGE) != JOptionPane.YES_OPTION) {
   294:         return;
   295:       }
   296:       System.arraycopy (defaultMaps[number - 1], 0,  //from
   297:                         map, 0,  //to
   298:                         MAP_LENGTH);  //length
   299:       updateText ();
   300:       for (int i = 0; i < STICK_AND_BUTTONS; i++) {
   301:         if (BIT_TO_REPEATABLE[i]) {
   302:           repeatCheckBoxOf[i].setSelected (map[MAP_REPEAT + i] != 0);
   303:           delayModelOf[i].setValue (Math.max (DELAY_MIN, Math.min (DELAY_MAX, map[MAP_DELAY + i])));
   304:           intervalModelOf[i].setValue (Math.max (INTERVAL_MIN, Math.min (INTERVAL_MAX, map[MAP_INTERVAL + i])));
   305:         }
   306:       }
   307:     } else if (command.startsWith ("Repeat ")) {
   308:       int i = Integer.parseInt (command.substring (7));
   309:       map[MAP_REPEAT + i] = repeatCheckBoxOf[i].isSelected () ? 1 : 0;
   310:     } else {
   311:       System.out.println ("unknown action command " + command);
   312:     }
   313:   }
   314: 
   315: 
   316:   //スピナーのチェンジリスナー
   317:   @Override public void stateChanged (ChangeEvent ce) {
   318:     JSpinner spinner = (JSpinner) ce.getSource ();
   319:     String name = spinner.getName ();
   320:     if (name.startsWith ("Delay ")) {
   321:       int i = Integer.parseInt (name.substring (6));
   322:       map[MAP_DELAY + i] = delayModelOf[i].getNumber ().intValue ();
   323:     } else if (name.startsWith ("Interval ")) {
   324:       int i = Integer.parseInt (name.substring (9));
   325:       map[MAP_INTERVAL + i] = intervalModelOf[i].getNumber ().intValue ();
   326:     } else {
   327:       System.out.println ("unknown spinner name " + name);
   328:     }
   329:   }
   330: 
   331: 
   332:   //テキストフィールドのフォーカスリスナー
   333:   @Override public void focusGained (FocusEvent fe) {
   334:     Component component = fe.getComponent ();
   335:     String componentName = component.getName ();
   336:     int type = componentName.charAt (0);  //'x'または'k'
   337:     int i = Integer.parseInt (componentName.substring (1));
   338:     //背景色を変えて目立たさせる
   339:     component.setBackground (new Color (LnF.lnfRGB[6]));
   340:     if (PPI.PPI_XINPUT_ON && type == 'x') {  //XInput
   341:       //Gamepadリスナーを追加する
   342:       xinputFocusedButton = i;
   343:       if (PPI.ppiXInput != null) {
   344:         PPI.ppiXInput.addGamepadListener (this);
   345:       }
   346:     } else if (type == 'k') {  //キー
   347:     } else {
   348:       System.out.println ("unknown component name " + componentName);
   349:     }
   350:   }
   351:   @Override public void focusLost (FocusEvent fe) {
   352:     Component component = fe.getComponent ();
   353:     String componentName = component.getName ();
   354:     int type = componentName.charAt (0);  //'x'または'k'
   355:     int i = Integer.parseInt (componentName.substring (1));
   356:     //背景色を元に戻す
   357:     component.setBackground (null);
   358:     if (PPI.PPI_XINPUT_ON && type == 'x') {  //XInput
   359:       //Gamepadリスナーを削除する
   360:       if (PPI.ppiXInput != null) {
   361:         PPI.ppiXInput.removeGamepadListeners ();
   362:       }
   363:       xinputFocusedButton = -1;
   364:     } else if (type == 'k') {  //キー
   365:     } else {
   366:       System.out.println ("unknown component name " + componentName);
   367:     }
   368:   }
   369: 
   370: 
   371:   //Gamepadリスナー
   372:   @Override public void connected (XInput.Gamepad gamepad) {
   373:   }
   374:   @Override public void disconnected (XInput.Gamepad gamepad) {
   375:   }
   376:   @Override public void buttonPressed (XInput.Gamepad gamepad, int buttonMasks) {
   377:     if (buttonMasks == 0) {  //ないはず
   378:       return;
   379:     }
   380:     if (PPI.PPI_XINPUT_ON && 0 <= xinputFocusedButton) {  //フォーカスされているxinputのテキストフィールドがある
   381:       int xIndex = gamepad.getIndex ();
   382:       int xBit = Integer.numberOfTrailingZeros (buttonMasks);
   383:       int xCode = 128 | xIndex << 5 | xBit;
   384:       if (STICKS <= xinputFocusedButton ||  //ボタンはすべて割り当てられる
   385:           ((1 << xBit) & (XInput.LSUP_MASK |
   386:                           XInput.LSDOWN_MASK |
   387:                           XInput.LSLEFT_MASK |
   388:                           XInput.LSRIGHT_MASK |
   389:                           XInput.RSUP_MASK |
   390:                           XInput.RSDOWN_MASK |
   391:                           XInput.RSLEFT_MASK |
   392:                           XInput.RSRIGHT_MASK)) != 0) {  //スティックはスティックのみ割り当てられる
   393:         map[MAP_CODE + xinputFocusedButton] = xCode << 16;
   394:         //すべてのボタンのxIndexを更新する
   395:         //  インデックスを変更したいときボタンを1個割り当て直せば済む
   396:         for (int i = 0; i < STICK_AND_BUTTONS; i++) {
   397:           int xCode2 = map[MAP_CODE + i] >>> 16;
   398:           if (xCode2 != 0) {
   399:             map[MAP_CODE + i] = ((xCode2 & ~(3 << 5)) | xIndex << 5) << 16;
   400:           }
   401:         }
   402:         updateText ();
   403:       }
   404:     }
   405:   }
   406:   @Override public void buttonReleased (XInput.Gamepad gamepad, int buttonMasks) {
   407:   }
   408:   @Override public void leftStickMovedX (XInput.Gamepad gamepad) {
   409:   }
   410:   @Override public void leftStickMovedY (XInput.Gamepad gamepad) {
   411:   }
   412:   @Override public void leftTriggerMoved (XInput.Gamepad gamepad) {
   413:   }
   414:   @Override public void rightStickMovedX (XInput.Gamepad gamepad) {
   415:   }
   416:   @Override public void rightStickMovedY (XInput.Gamepad gamepad) {
   417:   }
   418:   @Override public void rightTriggerMoved (XInput.Gamepad gamepad) {
   419:   }
   420: 
   421: 
   422:   //テキストフィールドのキーリスナー
   423:   @Override public void keyPressed (KeyEvent ke) {
   424:     Component component = ke.getComponent ();
   425:     String componentName = component.getName ();
   426:     int type = componentName.charAt (0);  //'x'または'k'
   427:     int i = Integer.parseInt (componentName.substring (1));
   428:     int xCode = map[MAP_CODE + i] >>> 16;
   429:     int keyCode = map[MAP_CODE + i] & 65535;
   430:     if (PPI.PPI_XINPUT_ON && type == 'x') {  //XInput
   431:       if (ke.getKeyCode () == KeyEvent.VK_ESCAPE) {  //Escキーは解除とみなす
   432:         xCode = 0;
   433:       }
   434:     } else if (type == 'k') {  //キー
   435:     } else {
   436:       System.out.println ("unknown component name " + componentName);
   437:     }
   438:     map[MAP_CODE + i] = xCode << 16 | keyCode;
   439:     updateText ();
   440:     ke.consume ();
   441:   }
   442:   @Override public void keyReleased (KeyEvent ke) {
   443:     ke.consume ();
   444:   }
   445:   @Override public void keyTyped (KeyEvent ke) {
   446:     ke.consume ();
   447:   }
   448: 
   449: 
   450:   //configurationPanel = getConfigurationPanel ()
   451:   //  設定パネルを返す。初回は作る
   452:   @Override public JComponent getConfigurationPanel () {
   453: 
   454:     if (configurationPanel != null) {
   455:       return configurationPanel;
   456:     }
   457: 
   458:     for (int i = 0; i < STICK_AND_BUTTONS; i++) {
   459:       xinputTextFieldOf[i] =
   460:         ComponentFactory.setEnabled (
   461:           ComponentFactory.addListener (
   462:             ComponentFactory.addListener (
   463:               ComponentFactory.setHorizontalAlignment (
   464:                 ComponentFactory.setName (
   465:                   ComponentFactory.createTextField ("", 8),
   466:                   "x" + i),
   467:                 JTextField.CENTER),
   468:               (KeyListener) this),
   469:             (FocusListener) this),
   470:           PPI.PPI_XINPUT_ON && XEiJ.prgWindllLoaded);
   471:       if (BIT_TO_REPEATABLE[i]) {
   472:         repeatCheckBoxOf[i] =
   473:           ComponentFactory.setText (
   474:             ComponentFactory.createCheckBox (map[MAP_REPEAT + i] != 0, "Repeat " + i, (ActionListener) this),
   475:             "");
   476:         delayModelOf[i] =
   477:           new SpinnerNumberModel (Math.max (DELAY_MIN, Math.min (DELAY_MAX, map[MAP_DELAY + i])),
   478:                                   DELAY_MIN, DELAY_MAX, DELAY_STEP);
   479:         delaySpinnerOf[i] =
   480:           ComponentFactory.setName (
   481:             ComponentFactory.createNumberSpinner (delayModelOf[i], 4, (ChangeListener) this),
   482:             "Delay " + i);
   483:         intervalModelOf[i] =
   484:           new SpinnerNumberModel (Math.max (INTERVAL_MIN, Math.min (INTERVAL_MAX, map[MAP_INTERVAL + i])),
   485:                                   INTERVAL_MIN, INTERVAL_MAX, INTERVAL_STEP);
   486:         intervalSpinnerOf[i] =
   487:           ComponentFactory.setName (
   488:             ComponentFactory.createNumberSpinner (intervalModelOf[i], 4, (ChangeListener) this),
   489:             "Interval " + i);
   490:       }
   491:     }
   492:     updateText ();
   493: 
   494:     //    0       1           2      3      4       5
   495:     //       Stick/Button  XInput  Burst  Delay  Interval
   496:     //    -----------------------------------------------
   497:     //    #       *           *      *      *       *
   498:     ArrayList<Object> cellList = new ArrayList<Object> ();
   499:     //
   500:     cellList.add (null);
   501:     cellList.add (Multilingual.mlnText (ComponentFactory.createLabel ("Stick/Button"), "ja", "スティック/ボタン"));
   502:     cellList.add (ComponentFactory.createLabel ("XInput"));
   503:     cellList.add (Multilingual.mlnText (ComponentFactory.createLabel ("Burst"), "ja", "連射"));
   504:     cellList.add (Multilingual.mlnText (ComponentFactory.createLabel ("Delay (ms)"), "ja", "開始 (ms)"));
   505:     cellList.add (Multilingual.mlnText (ComponentFactory.createLabel ("Interval (ms)"), "ja", "間隔 (ms)"));
   506:     //
   507:     cellList.add (ComponentFactory.createHorizontalSeparator ());
   508:     //
   509:     for (int i = 0; i < STICK_AND_BUTTONS; i++) {
   510:       cellList.add (String.valueOf (1 + i));  //#
   511:       cellList.add (BIT_TO_TEXT[i]);  //Button
   512:       cellList.add (xinputTextFieldOf[i]);  //XInput
   513:       cellList.add (BIT_TO_REPEATABLE[i] ? repeatCheckBoxOf[i] : null);  //Burst
   514:       cellList.add (BIT_TO_REPEATABLE[i] ? delaySpinnerOf[i] : null);  //Delay
   515:       cellList.add (BIT_TO_REPEATABLE[i] ? intervalSpinnerOf[i] : null);  //Interval
   516:     }
   517: 
   518:     configurationPanel =
   519:       ComponentFactory.createVerticalBox (
   520:         Box.createVerticalStrut (5),
   521:         ComponentFactory.createHorizontalBox (
   522:           Box.createHorizontalGlue (),
   523:           Multilingual.mlnText (ComponentFactory.createLabel (getNameEn ()), "ja", getNameJa ()),
   524:           Box.createHorizontalGlue ()),
   525:         Box.createVerticalStrut (5),
   526:         ComponentFactory.createHorizontalBox (
   527:           ComponentFactory.createGridPanel (
   528:             6,  //colCount
   529:             2 + STICK_AND_BUTTONS,  //rowCount
   530:             "paddingLeft=3,paddingRight=3,center",   //gridStyles
   531:             "",  //colStyles
   532:             "italic;colSpan=6,widen",  //rowStyles
   533:             "",  //cellStyles
   534:             cellList.toArray (new Object[0]))),
   535:         Box.createVerticalStrut (5),
   536:         ComponentFactory.createHorizontalBox (
   537:           Box.createHorizontalGlue (),
   538:           Multilingual.mlnText (ComponentFactory.createButton ("Reset to default values", (ActionListener) this), "ja", "初期値に戻す"),
   539:           Box.createHorizontalGlue ()),
   540:         Box.createVerticalStrut (5));
   541: 
   542:     return configurationPanel;
   543:   }
   544: 
   545: 
   546:   //input (ke, pressed)
   547:   //  キー入力イベントを処理する
   548:   //  ke  キーイベント
   549:   //  pressed  false=離された,true=押された
   550:   @Override public boolean input (KeyEvent ke, boolean pressed) {
   551:     return false;
   552:   }
   553: 
   554: 
   555:   //setPin8 (pin8)
   556:   //  ピン8を変更する
   557:   //  pin8  ピン8
   558:   @Override public void setPin8 (int pin8) {
   559:     if (!(PPI.PPI_XINPUT_ON && XEiJ.prgWindllLoaded)) {
   560:       req = pin8;
   561:       return;
   562:     }
   563:     if ((req & ~pin8) == 0) {  //REQの立ち下がりではない
   564:       req = pin8;
   565:       return;
   566:     }
   567:     //REQの立ち下がり
   568:     req = 0;
   569:     //転送開始時刻を記録する
   570:     transferStartTime = XEiJ.mpuClockTime;
   571:     //スティックの位置を確認する
   572:     int outputSticks = 0;  //出力するスティックの位置
   573:     if (PPI.ppiXInput != null) {
   574:       for (int i = 0; i < STICKS; i++) {
   575:         int xCode = map[MAP_CODE + i] >>> 16;
   576:         if (xCode != 0) {
   577:           int xIndex = (xCode >> 5) & 3;
   578:           int xBit = xCode & 31;
   579:           //スティックの位置を-32768~32768で取得する
   580:           int t = (xBit == XInput.LSUP_BIT ? -PPI.ppiXInput.getLeftStickY (xIndex) :  //左スティック。上が0、下が255
   581:                    xBit == XInput.LSDOWN_BIT ? PPI.ppiXInput.getLeftStickY (xIndex) :  //左スティック。下が0、上が255
   582:                    xBit == XInput.LSLEFT_BIT ? PPI.ppiXInput.getLeftStickX (xIndex) :  //左スティック。左が0、右が255
   583:                    xBit == XInput.LSRIGHT_BIT ? -PPI.ppiXInput.getLeftStickX (xIndex) :  //左スティック。右が0、左が255
   584:                    xBit == XInput.RSUP_BIT ? -PPI.ppiXInput.getRightStickY (xIndex) :  //右スティック。上が0、下が255
   585:                    xBit == XInput.RSDOWN_BIT ? PPI.ppiXInput.getRightStickY (xIndex) :  //右スティック。下が0、上が255
   586:                    xBit == XInput.RSLEFT_BIT ? PPI.ppiXInput.getRightStickX (xIndex) :  //右スティック。左が0、右が255
   587:                    xBit == XInput.RSRIGHT_BIT ? -PPI.ppiXInput.getRightStickX (xIndex) :  //右スティック。右が0、左が255
   588:                    0);
   589:           //符号を付けたまま右に8ビットシフトする。-256~-1は-1、0~255は0になる
   590:           t >>= 8;
   591:           //128に加えてクリッピングする。127と128(の境目)が中央になる
   592:           t = Math.max (0, Math.min (255, 128 + t));
   593:           outputSticks |= t << (8 * i);
   594:         }
   595:       }
   596:     }
   597:     //ボタンの状態を確認する
   598:     int currentButtons = 0;  //現在押されているボタン
   599:     int lastIndex = -1;
   600:     int lastMasks = 0;
   601:     for (int i = STICKS; i < STICK_AND_BUTTONS; i++) {
   602:       int xCode = map[MAP_CODE + i] >>> 16;
   603:       if (xCode != 0) {  //割り当てられている
   604:         int xIndex = (xCode >> 5) & 3;
   605:         int xBit = xCode & 31;
   606:         if (lastIndex != xIndex) {
   607:           lastIndex = xIndex;
   608:           lastMasks = PPI.ppiXInput == null ? 0 : PPI.ppiXInput.getButtonMasks (lastIndex);
   609:         }
   610:         if ((lastMasks & (1 << xBit)) != 0) {  //押されている
   611:           currentButtons |= 1 << i;
   612:         }
   613:       }
   614:     }
   615:     int pressedButtons = ~lastButtons & currentButtons;  //今回押されたボタン
   616:     lastButtons = currentButtons;
   617:     int outputButtons = 0;  //出力するボタン
   618:     for (int i = STICKS; i < STICK_AND_BUTTONS; i++) {
   619:       if ((pressedButtons & (1 << i)) != 0 &&  //今回押された
   620:           map[MAP_REPEAT + i] != 0) {  //連射有効
   621:         startTimeOf[i] = XEiJ.mpuClockTime + map[MAP_DELAY + i] * (XEiJ.TMR_FREQ / 1000);  //連射開始時刻
   622:       }
   623:       if ((currentButtons & (1 << i)) != 0 &&  //現在押されている
   624:           (map[MAP_REPEAT + i] == 0 ||  //連射が無効または
   625:            XEiJ.mpuClockTime < startTimeOf[i] ||  //連射開始時刻になっていないまたは
   626:            ((int) ((XEiJ.mpuClockTime - startTimeOf[i]) /  //連射開始時刻を過ぎた時間を
   627:                    ((map[MAP_INTERVAL + i] >> 1) * (XEiJ.TMR_FREQ / 1000)))  //連射間隔の半分で割った商が
   628:             & 1) != 0)) {  //奇数
   629:         outputButtons |= 1 << i;
   630:       }
   631:     }
   632:     //転送データを作る
   633:     transferData[0] = (0b10010000 |
   634:                        ((outputButtons & (A_MASK | APRIME_MASK)) != 0 ? 0 : 0b00001000) |
   635:                        ((outputButtons & (B_MASK | BPRIME_MASK)) != 0 ? 0 : 0b00000100) |
   636:                        ((outputButtons &  C_MASK               ) != 0 ? 0 : 0b00000010) |
   637:                        ((outputButtons &  D_MASK               ) != 0 ? 0 : 0b00000001));
   638:     transferData[1] = (0b10110000 |
   639:                        ((outputButtons & E1_MASK    ) != 0 ? 0 : 0b00001000) |
   640:                        ((outputButtons & E2_MASK    ) != 0 ? 0 : 0b00000100) |
   641:                        ((outputButtons & START_MASK ) != 0 ? 0 : 0b00000010) |
   642:                        ((outputButtons & SELECT_MASK) != 0 ? 0 : 0b00000001));
   643:     transferData[2] = 0b10010000 | ((outputSticks >>> (8 * SUP_BIT   + 4)) & 0b00001111);
   644:     transferData[3] = 0b10110000 | ((outputSticks >>> (8 * SLEFT_BIT + 4)) & 0b00001111);
   645:     transferData[4] = 0b10010000 | ((outputSticks >>> (8 * TUP_BIT   + 4)) & 0b00001111);
   646:     transferData[5] = 0b10110000 | ((outputSticks >>> (8 * OLEFT_BIT + 4)) & 0b00001111);
   647:     transferData[6] = 0b10010000 | ((outputSticks >>> (8 * SUP_BIT      )) & 0b00001111);
   648:     transferData[7] = 0b10110000 | ((outputSticks >>> (8 * SLEFT_BIT    )) & 0b00001111);
   649:     transferData[8] = 0b10010000 | ((outputSticks >>> (8 * TUP_BIT      )) & 0b00001111);
   650:     transferData[9] = 0b10110000 | ((outputSticks >>> (8 * OLEFT_BIT    )) & 0b00001111);
   651:     transferData[10] = (0b10010000 |
   652:                         ((outputButtons & A_MASK     ) != 0 ? 0 : 0b00001000) |
   653:                         ((outputButtons & B_MASK     ) != 0 ? 0 : 0b00000100) |
   654:                         ((outputButtons & APRIME_MASK) != 0 ? 0 : 0b00000010) |
   655:                         ((outputButtons & BPRIME_MASK) != 0 ? 0 : 0b00000001));
   656:     transferData[11] = 0b10111111;
   657:   }  //setPin8
   658: 
   659:   //d = readByte ()
   660:   //  ポートから読み出す
   661:   //  d  値。0~255
   662:   @Override public int readByte () {
   663:     long t = XEiJ.mpuClockTime - transferStartTime;  //転送開始時刻からの経過時間を
   664:     t /= XEiJ.TMR_FREQ / 20000L;  //50us=1/20000Hzで割る
   665:     int step = (int) Math.max (0L, Math.min ((long) TRANSFER_STEPS, t));
   666:     return transferData[step];
   667:   }  //readByte
   668: 
   669: }  //class CyberStickAnalog