Mouse.java
     1: //========================================================================================
     2: //  Mouse.java
     3: //    en:Mouse
     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: package xeij;
    14: 
    15: import java.awt.*;  //Cursor
    16: import java.awt.event.*;  //MouseEvent
    17: import java.awt.image.*;  //BufferedImage
    18: import javax.swing.*;
    19: import javax.swing.event.*;  //ChangeListener
    20: 
    21: public class Mouse {
    22: 
    23:   //  パネルのマウスカーソルを変更する
    24:   //    X68000のマウスカーソルが表示されているときはホストのマウスカーソルを消す
    25:   //    X68000のマウスカーソルが表示されていないときはホストのマウスカーソルをX68000と同じサイズにする
    26:   //  SCCから要求があったときマウスデータを作る
    27:   //    シームレスモードのときは逆アクセラレーションを行う
    28:   //      X68000のマウスカーソルがホストのマウスカーソルの真下に来るようにマウスの移動データを作る
    29:   //  キーボードが操作されたときはキーボードにデータを渡す
    30: 
    31:   //逆アクセラレーション
    32:   //  ROMのアクセラレーション処理のコード
    33:   //    ;<d0.w:移動量
    34:   //    ;>d0.w:移動量
    35:   //    ;?d1
    36:   //    accelerate:
    37:   //        clr.w   -(sp)
    38:   //        tst.w   d0
    39:   //        bgt.s   1f
    40:   //        addq.w  #1,(sp)
    41:   //        neg.w   d0
    42:   //    1:  move.w  d0,d1   ;  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
    43:   //        lsr.w   #3,d1   ;  0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  1  2  2  2  2  2  2  2  2  3  3
    44:   //        bne.s   2f
    45:   //        move.w  #1,d1   ;  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  2  2  2  2  2  2  2  2  3  3
    46:   //    2:  mulu.w  d1,d0   ;  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 32 34 36 38 40 42 44 46 72 75
    47:   //        move.w  d0,d1
    48:   //        lsr.w   #2,d1   ;  0  0  0  0  1  1  1  1  2  2  2  2  3  3  3  3  8  8  9  9 10 10 11 11 18 18
    49:   //        add.w   d1,d0   ;  0  1  2  3  5  6  7  8 10 11 12 13 15 16 17 18 40 42 45 47 50 52 55 57 90 93
    50:   //        tst.w   (sp)+
    51:   //        beq.s   3f
    52:   //        neg.w   d0
    53:   //    3:  rts
    54:   //  アクセラレーションテーブル
    55:   //    変位:移動距離
    56:   //      0:   0   1:   1   2:   2   3:   3   4:   5   5:   6   6:   7   7:   8   8:  10   9:  11
    57:   //     10:  12  11:  13  12:  15  13:  16  14:  17  15:  18  16:  40  17:  42  18:  45  19:  47
    58:   //     20:  50  21:  52  22:  55  23:  57  24:  90  25:  93  26:  97  27: 101  28: 105  29: 108
    59:   //     30: 112  31: 116  32: 160  33: 165  34: 170  35: 175  36: 180  37: 185  38: 190  39: 195
    60:   //     40: 250  41: 256  42: 262  43: 268  44: 275  45: 281  46: 287  47: 293  48: 360  49: 367
    61:   //     50: 375  51: 382  52: 390  53: 397  54: 405  55: 412  56: 490  57: 498  58: 507  59: 516
    62:   //     60: 525  61: 533  62: 542  63: 551  64: 640  65: 650  66: 660  67: 670  68: 680  69: 690
    63:   //     70: 700  71: 710  72: 810  73: 821  74: 832  75: 843  76: 855  77: 866  78: 877  79: 888
    64:   //     80:1000  81:1012 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    65:   //    ~~~~~~~~~~~~~~~~~~ 82:1025  83:1037  84:1050  85:1062  86:1075  87:1087  88:1210  89:1223
    66:   //     90:1237  91:1251  92:1265  93:1278  94:1292  95:1306  96:1440  97:1455  98:1470  99:1485
    67:   //    100:1500 101:1515 102:1530 103:1545 104:1690 105:1706 106:1722 107:1738 108:1755 109:1771
    68:   //    110:1787 111:1803 112:1960 113:1977 114:1995 115:2012 116:2030 117:2047 118:2065 119:2082
    69:   //    120:2250 121:2268 122:2287 123:2306 124:2325 125:2343 126:2362 127:2381 128:2560
    70:   //  逆アクセラレーション
    71:   //    現在のマウスカーソルの位置とX68000のマウス座標の距離を超えない最大の移動距離をアクセラレーションテーブルから探してその変位に符号を付けて返す
    72:   //    例えば距離が639ピクセルのとき63(639-551=88),23(88-57=31),15(31-18=13),11(13-13=0)の4回で移動が完了する
    73:   public static final int[] MUS_DEACCELERATION_TABLE = new int[1025];  //逆アクセラレーションテーブル
    74: 
    75:   //マウスカーソル
    76:   public static final String[][] MUS_CURSOR_PATTERN = {
    77:     {
    78:     },
    79:     {
    80:       "00.........",
    81:       "010........",
    82:       "0110.......",
    83:       "01110......",
    84:       "011110.....",
    85:       "0111110....",
    86:       "01111110...",
    87:       "011111110..",
    88:       "0111111110.",
    89:       "01111100000",
    90:       "0110110....",
    91:       "010.0110...",
    92:       "00..0110...",
    93:       ".....0110..",
    94:       ".....0110..",
    95:       "......00...",
    96:     },
    97:   };
    98: 
    99:   //モード
   100:   //  シームレス
   101:   //    X68000のマウスカーソルが動作環境のマウスポインタを追いかける
   102:   //  エクスクルーシブ
   103:   //    動作環境のマウスポインタを独占して相対座標を利用できるようにする
   104:   public static boolean musSeamlessOn;  //true=シームレス,false=エクスクルーシブ
   105:   public static boolean musExclusiveStart;  //true=エクスクルーシブに切り替えた直後
   106:   public static boolean musEdgeAccelerationOn;  //true=シームレスのとき縁部加速を行う
   107:   public static boolean musHostsPixelUnitsOn;  //true=エクスクルーシブのときマウスはホストの画素単位で動く,false=X68000の画素単位で動く
   108: 
   109:   //マウスの状態
   110:   public static boolean musButtonLeft;  //マウスの左ボタンの状態。true=押されている。マウスカーソルがスクリーン上になくても有効
   111:   public static boolean musButtonRight;  //マウスの右ボタンの状態。true=押されている。マウスカーソルがスクリーン上になくても有効
   112:   public static int musData;  //マウスのボタンのデータ。0=ボタンが押されていない,1=左ボタンだけ押されている,2=右ボタンだけ押されている,3=左右のボタンが押されている。マウスカーソルがスクリーン上にあるときだけセットされる
   113:   public static int musExtraData;  //マウスのボタンの延長データ。0=ボタンが押されていない,1=左ボタンだけ押されている,2=右ボタンだけ押されている,3=左右のボタンが押されている。マウスカーソルがスクリーン上にあるときだけセットされる。ボタンが押されてからSCCのポートBが読み出されるまで押されたままになる
   114:   public static int musPanelX;  //パネル座標
   115:   public static int musPanelY;
   116:   public static int musScreenX;  //スクリーン座標。スクリーン上にあるとは限らない
   117:   public static int musScreenY;
   118:   public static boolean musOnScreen;  //true=マウスカーソルがスクリーン上にある
   119:   public static boolean musOnKeyboard;  //true=マウスカーソルがキーボード上にある
   120: 
   121:   //ホイール
   122:   public static final int MUS_WHEEL_TRACE      = 0;  //トレースとステップ
   123:   public static final int MUS_WHEEL_CLICK      = 1;  //左クリックと右クリック
   124:   public static final int MUS_WHEEL_DO_NOTHING = 2;  //何もしない
   125:   public static int musWheelButton;  //ホイールで押されたボタン。1=左ボタン,2=右ボタン
   126:   public static long musWheelReleaseTime;  //ホイールで押されたボタンが離される時刻
   127: 
   128:   //マウスカーソル
   129:   public static boolean musCursorAvailable;  //true=カスタムカーソルを利用できる
   130:   public static int musCursorNumber;  //表示されているマウスカーソルの番号
   131:   public static Cursor[] musCursorArray;  //マウスカーソルの配列
   132: 
   133:   //マウスのボタン
   134:   public static boolean musOutputButtonStatus;  //true=ボタンの状態を出力する
   135:   public static int musNumberOfButtons;  //ボタンの数
   136:   public static int musLastModifiersEx;  //前回のmodifiersEx
   137:   public static boolean musCtrlRightOn;  //Ctrlキー+左ボタンを右ボタンとみなす
   138: 
   139:   public static final int SHIFT_MASK          = 0x00000001;
   140:   public static final int CTRL_MASK           = 0x00000002;
   141:   public static final int META_MASK           = 0x00000004;
   142:   public static final int BUTTON3_MASK        = 0x00000004;
   143:   public static final int ALT_MASK            = 0x00000008;
   144:   public static final int BUTTON2_MASK        = 0x00000008;
   145:   public static final int BUTTON1_MASK        = 0x00000010;
   146:   public static final int ALT_GRAPH_MASK      = 0x00000020;
   147: 
   148:   public static final int SHIFT_DOWN_MASK     = 0x00000040;
   149:   public static final int CTRL_DOWN_MASK      = 0x00000080;
   150:   public static final int META_DOWN_MASK      = 0x00000100;
   151:   public static final int ALT_DOWN_MASK       = 0x00000200;
   152:   public static final int BUTTON1_DOWN_MASK   = 0x00000400;
   153:   public static final int BUTTON2_DOWN_MASK   = 0x00000800;
   154:   public static final int BUTTON3_DOWN_MASK   = 0x00001000;
   155:   public static final int ALT_GRAPH_DOWN_MASK = 0x00002000;
   156:   public static final int BUTTON4_DOWN_MASK   = 0x00004000;
   157:   public static final int BUTTON5_DOWN_MASK   = 0x00008000;
   158: 
   159:   //マウスリスナー、マウスモーションリスナー、マウスホイールリスナー
   160:   //
   161:   //  マウスの操作とイベント
   162:   //                                  modifiers   modifiersEx
   163:   //    button1  left        pressed  0x00000010  0x00000400
   164:   //                        released  0x00000010  0x00000000
   165:   //                         clicked  0x00000010  0x00000000
   166:   //    button2  wheel       pressed  0x00000008  0x00000800
   167:   //                        released  0x00000008  0x00000200  Altがセットされる。clickedは発生しない
   168:   //    button3  right       pressed  0x00000004  0x00001000
   169:   //                        released  0x00000004  0x00000100  Metaがセットされる
   170:   //                         clicked  0x00000004  0x00000100  Metaがセットされる
   171:   //    button4  back        pressed  0x00000000  0x00004000
   172:   //                        released  0x00000000  0x00000000  clickedは発生しない
   173:   //    button5  forward     pressed  0x00000000  0x00008000
   174:   //                        released  0x00000000  0x00000000  clickedは発生しない
   175:   //    wheel    pull     wheelmoved  preciseWheelRotation = 1.000000
   176:   //                                  scrollAmount = 3
   177:   //                                  scrollType = 0
   178:   //                                  unitsToScroll = 3
   179:   //                                  wheelRotation = 1
   180:   //             push     wheelmoved  preciseWheelRotation = -1.000000
   181:   //                                  scrollAmount = 3
   182:   //                                  scrollType = 0
   183:   //                                  unitsToScroll = -3
   184:   //                                  wheelRotation = -1
   185:   //             left     左スクロールではイベントが発生しない
   186:   //             right    右スクロールではイベントが発生しない
   187:   //    ダブルクリックはleftを2回クリックしているだけ
   188:   //    ズームインとズームアウトはkeyCode=0x11=VK_CONTROL,keyLocation=2=KEY_LOCATION_LEFTをpressしてホイールを回している
   189:   //
   190:   //  Macbookのマルチタッチトラックパッドでクリックする方法
   191:   //    左クリック
   192:   //      「リンゴ」→「システム環境設定」→「トラックパッド」の「タップでクリック」をONにする
   193:   //      1本指でタップする
   194:   //    右クリック
   195:   //      「リンゴ」→「システム環境設定」→「トラックパッド」の「副ボタンのクリック」をONにする
   196:   //      「2本指でクリックまたはタップ」を選択する
   197:   //      2本指でタップする
   198:   //    メモ
   199:   //      control+タップはCtrlがセットされた左クリックでしかない
   200:   //        個々のアプリケーションがこの操作に右クリックと同じ機能を割り当てなければ右クリックの代わりにならない
   201:   //      有効になっていない2本指タップやcontrol+タップでもmousePressedとmouseReleasedが発生する
   202:   //        右クリックとして機能しないのはmousePressedとmouseReleasedの間隔が短すぎるためかも知れない
   203:   //
   204:   //        Button1  左ボタン
   205:   //   Ctrl+Button1  右ボタン
   206:   //        Button2  マウスモード切り替え。mouseClickedは発生しない。modifiersExにAltがセットされる。Alt+Button2は無効
   207:   //        Button3  右ボタン。modifiersExにMetaがセットされるでMeta+Button3は無効
   208:   //        Button4  停止・再開
   209:   //        Button5  トレース1回
   210:   //  Shift+Button5  ステップ1回
   211:   //
   212: 
   213:   //移動速度
   214:   public static final int MUS_SPEED_SCALE_MIN = 0;
   215:   public static final int MUS_SPEED_SCALE_MAX = 40;
   216:   public static final int MUS_SPEED_SCALE_MID = (MUS_SPEED_SCALE_MAX - MUS_SPEED_SCALE_MIN) >> 1;
   217:   public static int musSpeedScaleIndex;  //マウスの移動速度のスケール。MUS_SPEED_SCALE_MIN~MUS_SPEED_SCALE_MAX
   218:   public static int musSpeedRatioX;  //マウスの移動速度の係数*65536
   219:   public static int musSpeedRatioY;
   220:   public static final String[] musSpeedTexts = new String[MUS_SPEED_SCALE_MAX - MUS_SPEED_SCALE_MIN + 1];  //スケールのテキストの配列
   221:   public static JLabel musSpeedLabel;  //スケールのラベル
   222:   public static JSlider musSpeedSlider;  //スケールを指定するスライダー
   223: 
   224:   //メニュー
   225:   public static JCheckBoxMenuItem musSeamlessMouseCheckBox;
   226:   public static JCheckBoxMenuItem musCtrlRightCheckBox;
   227:   public static JCheckBoxMenuItem musEdgeAccelerationCheckBox;
   228:   public static Box musMouseCursorSpeedBox;
   229:   public static JCheckBoxMenuItem musHostsPixelUnitsCheckBox;
   230: 
   231:   //musInit ()
   232:   //  初期化
   233:   public static void musInit () {
   234:     musSeamlessOn = Settings.sgsGetOnOff ("seamless");
   235:     musCtrlRightOn = Settings.sgsGetOnOff ("ctrlright");
   236:     musEdgeAccelerationOn = Settings.sgsGetOnOff ("edgeaccel");
   237:     musHostsPixelUnitsOn = Settings.sgsGetOnOff ("hostspixelunits");
   238:     musSpeedScaleIndex = Math.max (MUS_SPEED_SCALE_MIN, Math.min (MUS_SPEED_SCALE_MAX, Settings.sgsGetInt ("mousespeed", MUS_SPEED_SCALE_MID)));
   239:     //
   240:     musExclusiveStart = false;
   241:     musButtonLeft = false;
   242:     musButtonRight = false;
   243:     musData = 0;
   244:     musExtraData = 0;
   245:     musPanelX = 0;
   246:     musPanelY = 0;
   247:     musScreenX = 0;
   248:     musScreenY = 0;
   249:     musOnScreen = false;
   250:     musOnKeyboard = false;
   251:     musWheelButton = 0;
   252:     musWheelReleaseTime = 0L;
   253:     //逆アクセラレーション
   254:     {
   255:       int index = 0;
   256:       for (int delta = 0; delta <= 81; delta++) {
   257:         int next = delta + 1;
   258:         if (next >= 8) {
   259:           next *= next >> 3;
   260:         }
   261:         next += next >> 2;
   262:         while (index < next) {  //delta==81のときnext==1025
   263:           MUS_DEACCELERATION_TABLE[index++] = delta;
   264:         }
   265:       }
   266:     }
   267:     //マウスカーソル
   268:     musCursorAvailable = false;
   269:     try {
   270:       Toolkit toolkit = Toolkit.getDefaultToolkit ();
   271:       Dimension bestCursorSize = toolkit.getBestCursorSize (16, 16);
   272:       int width = bestCursorSize.width;
   273:       int height = bestCursorSize.height;
   274:       if (width >= 16 && height >= 16) {  //カスタムカーソルを利用できるとき
   275:         BufferedImage cursorImage = new BufferedImage (width, height, BufferedImage.TYPE_INT_ARGB);
   276:         int[] cursorBitmap = ((DataBufferInt) cursorImage.getRaster ().getDataBuffer ()).getData ();
   277:         Point point = new Point (0, 0);
   278:         musCursorArray = new Cursor[MUS_CURSOR_PATTERN.length];
   279:         for (int i = 0; i < MUS_CURSOR_PATTERN.length; i++) {
   280:           String[] ss = MUS_CURSOR_PATTERN[i];
   281:           int h = ss.length;
   282:           for (int y = 0; y < height; y++) {
   283:             String s = y < h ? ss[y] : "";
   284:             int w = s.length ();
   285:             for (int x = 0; x < width; x++) {
   286:               char c = x < w ? s.charAt (x) : '.';
   287:               cursorBitmap[x + width * y] = 0xff000000 & ('.' - c) | -(c & 1);
   288:             }
   289:           }
   290:           musCursorArray[i] = toolkit.createCustomCursor (cursorImage, point, "XEiJ_" + i);
   291:         }
   292:         musCursorAvailable = true;
   293:         musCursorNumber = 1;
   294:       }
   295:     } catch (Exception e) {
   296:     }
   297:     //マウスのボタン
   298:     musOutputButtonStatus = false;
   299:     musNumberOfButtons = -1;
   300:     try {
   301:       musNumberOfButtons = MouseInfo.getNumberOfButtons ();  //手元では5だった
   302:     } catch (Exception e) {
   303:     }
   304:     musLastModifiersEx = 0;
   305:     //musCtrlRightOn = false;
   306: 
   307:     //ラベル
   308:     //musSpeedTexts = new String[MUS_SPEED_SCALE_MAX - MUS_SPEED_SCALE_MIN + 1];
   309:     for (int i = MUS_SPEED_SCALE_MIN; i <= MUS_SPEED_SCALE_MAX; i++) {
   310:       musSpeedTexts[i - MUS_SPEED_SCALE_MIN] = String.format ("%4.2f", Math.pow (4.0, (double) (i - MUS_SPEED_SCALE_MID) / (double) MUS_SPEED_SCALE_MID));
   311:     }
   312:     musSpeedLabel = ComponentFactory.createLabel (musSpeedTexts[MUS_SPEED_SCALE_MID]);
   313:     //スライダー
   314:     musSpeedSlider = ComponentFactory.setEnabled (
   315:       ComponentFactory.setPreferredSize (
   316:         ComponentFactory.createHorizontalSlider (
   317:           MUS_SPEED_SCALE_MIN,
   318:           MUS_SPEED_SCALE_MAX,
   319:           musSpeedScaleIndex,
   320:           (MUS_SPEED_SCALE_MAX - MUS_SPEED_SCALE_MIN) / 4,
   321:           1,
   322:           musSpeedTexts,
   323:           new ChangeListener () {
   324:             @Override public void stateChanged (ChangeEvent ce) {
   325:               musSetSpeedScaleIndex (((JSlider) ce.getSource ()).getValue ());
   326:             }
   327:           }),
   328:         LnF.lnfFontSize * 18, LnF.lnfFontSize * 2 + 28),
   329:       XEiJ.rbtRobot != null);
   330:     musSetSpeedScaleIndex (musSpeedScaleIndex);
   331: 
   332:     //メニュー
   333:     ActionListener listener = new ActionListener () {
   334:       @Override public void actionPerformed (ActionEvent ae) {
   335:         Object source = ae.getSource ();
   336:         String command = ae.getActionCommand ();
   337:         switch (command) {
   338:         case "Seamless mouse":  //シームレスマウス
   339:           musSetSeamlessOn (((JCheckBoxMenuItem) source).isSelected ());
   340:           break;
   341:         case "Ctrl-key + left-button = right-button":  //Ctrl キー+左ボタン=右ボタン
   342:           musCtrlRightOn = ((JCheckBoxMenuItem) source).isSelected ();
   343:           break;
   344:         case "Edge acceleration":  //縁部加速
   345:           musSetEdgeAccelerationOn (((JCheckBoxMenuItem) source).isSelected ());
   346:           break;
   347:         case "Use host's pixel units":  //ホストの画素単位を使う
   348:           musSetHostsPixelUnitsOn (((JCheckBoxMenuItem) source).isSelected ());
   349:           break;
   350:         default:
   351:           System.out.println ("unknown action command " + command);
   352:         }
   353:       }
   354:     };  //ActionListener
   355:     //
   356:     musSeamlessMouseCheckBox =
   357:       ComponentFactory.setEnabled (
   358:         Multilingual.mlnText (
   359:           ComponentFactory.createCheckBoxMenuItem (musSeamlessOn, "Seamless mouse", KeyEvent.VK_F12, listener),
   360:           "ja", "シームレスマウス"),
   361:         XEiJ.rbtRobot != null);
   362:     //
   363:     musCtrlRightCheckBox =
   364:       Multilingual.mlnText (
   365:         ComponentFactory.createCheckBoxMenuItem (
   366:           musCtrlRightOn,
   367:           "Ctrl-key + left-button = right-button",
   368:           listener),
   369:         "ja", "Ctrl キー+左ボタン=右ボタン");
   370:     musEdgeAccelerationCheckBox =
   371:       Multilingual.mlnText (
   372:         ComponentFactory.createCheckBoxMenuItem (musEdgeAccelerationOn, "Edge acceleration", listener),
   373:         "ja", "縁部加速");
   374:     musMouseCursorSpeedBox =
   375:       ComponentFactory.createHorizontalBox (
   376:         Box.createHorizontalGlue (),
   377:         Multilingual.mlnText (
   378:           ComponentFactory.createLabel ("Mouse cursor speed "),
   379:           "ja", "マウスカーソルの速度 "),
   380:         musSpeedLabel,
   381:         Box.createHorizontalGlue ()
   382:         );
   383:     musHostsPixelUnitsCheckBox =
   384:       ComponentFactory.setEnabled (
   385:         Multilingual.mlnText (
   386:           ComponentFactory.createCheckBoxMenuItem (musHostsPixelUnitsOn, "Use host's pixel units", listener),
   387:           "ja", "ホストの画素単位を使う"),
   388:         XEiJ.rbtRobot != null);
   389: 
   390:   }  //musInit()
   391: 
   392:   //musTini ()
   393:   //  後始末
   394:   public static void musTini () {
   395:     Settings.sgsPutOnOff ("seamless", musSeamlessOn);
   396:     Settings.sgsPutOnOff ("ctrlright", musCtrlRightOn);
   397:     Settings.sgsPutOnOff ("edgeaccel", musEdgeAccelerationOn);
   398:     Settings.sgsPutOnOff ("hostspixelunits", musHostsPixelUnitsOn);
   399:     Settings.sgsPutInt ("mousespeed", musSpeedScaleIndex, MUS_SPEED_SCALE_MID);
   400:   }  //musTini
   401: 
   402:   public static void musSetSpeedScaleIndex (int i) {
   403:     musSpeedScaleIndex = i;
   404:     musSpeedLabel.setText (musSpeedTexts[i]);
   405:     musUpdateSpeedRatio ();
   406:   }  //musSetSpeedScaleIndex(int)
   407: 
   408:   public static void musUpdateSpeedRatio () {
   409:     double scale = Math.pow (4.0, (double) (musSpeedScaleIndex - MUS_SPEED_SCALE_MID) / (double) MUS_SPEED_SCALE_MID);
   410:     if (musHostsPixelUnitsOn) {
   411:       //musSpeedRatioX = (int) Math.round (65536.0 * scale * (double) XEiJ.pnlScreenWidth / (double) XEiJ.pnlZoomWidth);
   412:       //musSpeedRatioY = (int) Math.round (65536.0 * scale * (double) XEiJ.pnlScreenHeight / (double) XEiJ.pnlZoomHeight);
   413:       musSpeedRatioX = (int) (65536.0 * scale * (double) XEiJ.pnlScreenWidth / (double) XEiJ.pnlZoomWidth);
   414:       musSpeedRatioY = (int) (65536.0 * scale * (double) XEiJ.pnlScreenHeight / (double) XEiJ.pnlZoomHeight);
   415:     } else {
   416:       //musSpeedRatioX = (int) Math.round (65536.0 * scale);
   417:       //musSpeedRatioY = (int) Math.round (65536.0 * scale);
   418:       musSpeedRatioX = (int) (65536.0 * scale);
   419:       musSpeedRatioY = (int) (65536.0 * scale);
   420:     }
   421:   }  //musUpdateSpeedRatio()
   422: 
   423:   //musStart ()
   424:   //  マウスの動作を開始する
   425:   public static void musStart () {
   426: 
   427:     ComponentFactory.addListener (
   428:       XEiJ.pnlPanel,
   429:       new MouseAdapter () {
   430:         @Override public void mouseClicked (MouseEvent me) {
   431:           if (musOutputButtonStatus) {
   432:             int modifiersEx = me.getModifiersEx ();
   433:             if ((modifiersEx & BUTTON1_DOWN_MASK) != 0) {
   434:               System.out.println (String.format ("mouse button %d/%d was clicked. (0x%08x)",
   435:                                                  1, musNumberOfButtons, modifiersEx));
   436:             }
   437:             if ((modifiersEx & BUTTON2_DOWN_MASK) != 0) {
   438:               System.out.println (String.format ("mouse button %d/%d was clicked. (0x%08x)",
   439:                                                  2, musNumberOfButtons, modifiersEx));
   440:             }
   441:             if ((modifiersEx & BUTTON3_DOWN_MASK) != 0) {
   442:               System.out.println (String.format ("mouse button %d/%d was clicked. (0x%08x)",
   443:                                                  3, musNumberOfButtons, modifiersEx));
   444:             }
   445:             if ((modifiersEx & BUTTON4_DOWN_MASK) != 0) {
   446:               System.out.println (String.format ("mouse button %d/%d was clicked. (0x%08x)",
   447:                                                  4, musNumberOfButtons, modifiersEx));
   448:             }
   449:             if ((modifiersEx & BUTTON5_DOWN_MASK) != 0) {
   450:               System.out.println (String.format ("mouse button %d/%d was clicked. (0x%08x)",
   451:                                                  5, musNumberOfButtons, modifiersEx));
   452:             }
   453:             if ((modifiersEx & (BUTTON1_DOWN_MASK |
   454:                                 BUTTON2_DOWN_MASK |
   455:                                 BUTTON3_DOWN_MASK |
   456:                                 BUTTON4_DOWN_MASK |
   457:                                 BUTTON5_DOWN_MASK)) == 0) {
   458:               System.out.println (String.format ("mouse button ?/%d was clicked. (0x%08x)",
   459:                                                  musNumberOfButtons, modifiersEx));
   460:             }
   461:           }
   462:           if (!XEiJ.pnlPanel.isFocusOwner ()) {
   463:             XEiJ.pnlPanel.requestFocusInWindow ();
   464:           }
   465:         }
   466:         //@Override public void mouseEntered (MouseEvent me) {
   467:         //}
   468:         @Override public void mouseExited (MouseEvent me) {
   469:           if (musOnScreen) {  //スクリーンから出た
   470:             musOnScreen = false;
   471:           }
   472:           if (musOnKeyboard) {  //キーボードから出た
   473:             musOnKeyboard = false;
   474:             if (Keyboard.kbdPointedIndex >= 0) {  //ポイントされているキーがある
   475:               Keyboard.kbdHover (0, 0);  //ポイントを解除する
   476:             }
   477:           }
   478:         }
   479:         @Override public void mousePressed (MouseEvent me) {
   480:           musPressedOrReleased (me, true);  //マウスのボタンが操作された
   481:         }
   482:         @Override public void mouseReleased (MouseEvent me) {
   483:           musPressedOrReleased (me, false);  //マウスのボタンが操作された
   484:         }
   485:         @Override public void mouseDragged (MouseEvent me) {
   486:           musDraggedOrMoved (me);  //マウスが動いた
   487:         }
   488:         @Override public void mouseMoved (MouseEvent me) {
   489:           musDraggedOrMoved (me);  //マウスが動いた
   490:         }
   491:         @Override public void mouseWheelMoved (MouseWheelEvent mwe) {
   492:           int modifiersEx = mwe.getModifiersEx ();
   493:           if (musOutputButtonStatus) {
   494:             double preciseWheelRotation = mwe.getPreciseWheelRotation ();
   495:             int scrollAmount = mwe.getScrollAmount ();
   496:             int scrollType = mwe.getScrollType ();
   497:             int unitsToScroll = mwe.getUnitsToScroll ();
   498:             int wheelRotation = mwe.getWheelRotation ();
   499:             System.out.println (String.format ("mouse wheel moved (0x%08x)", modifiersEx));
   500:             System.out.println (String.format ("  preciseWheelRotation = %f", preciseWheelRotation));
   501:             System.out.println (String.format ("  scrollAmount = %d", scrollAmount));
   502:             System.out.println (String.format ("  scrollType = %d", scrollType));
   503:             System.out.println (String.format ("  unitsToScroll = %d", unitsToScroll));
   504:             System.out.println (String.format ("  wheelRotation = %d", wheelRotation));
   505:           }
   506:           int wheelRotation = mwe.getWheelRotation ();  //高解像度マウスは端数が蓄積するまで0が報告される
   507:           if (0 < wheelRotation) {  //スクロールアップ
   508:             ButtonFunction.bfnExecute (ButtonFunction.Button.WHEELUP, modifiersEx, true, null);
   509:           } else if (wheelRotation < 0) {  //スクロールダウン
   510:             ButtonFunction.bfnExecute (ButtonFunction.Button.WHEELDOWN, modifiersEx, true, null);
   511:           }
   512:           //マウスホイールイベントを消費する
   513:           mwe.consume ();
   514:         }
   515:       });
   516: 
   517:   }  //musStart()
   518: 
   519:   //musPressedOrReleased (me, pressed)
   520:   //  マウスのボタンが操作された
   521:   public static void musPressedOrReleased (MouseEvent me, boolean pressed) {
   522:     //  InputEvent.getModifiers()は変化したものだけ返す
   523:     //  InputEvent.getModifiersEx()は変化していないものも含めて現在の状態を返す
   524:     int modifiersEx = me.getModifiersEx ();
   525:     int pressedMask = ~musLastModifiersEx & modifiersEx;  //0→1
   526:     int releasedMask = musLastModifiersEx & ~modifiersEx;  //1→0
   527:     musLastModifiersEx = modifiersEx;
   528:     if (musOutputButtonStatus) {
   529:       if ((pressedMask & BUTTON1_DOWN_MASK) != 0) {
   530:         System.out.println (String.format ("mouse button %d/%d was pressed. (0x%08x)",
   531:                                            1, musNumberOfButtons, modifiersEx));
   532:       } else if ((releasedMask & BUTTON1_DOWN_MASK) != 0) {
   533:         System.out.println (String.format ("mouse button %d/%d was released. (0x%08x)",
   534:                                            1, musNumberOfButtons, modifiersEx));
   535:       }
   536:       if ((pressedMask & BUTTON2_DOWN_MASK) != 0) {
   537:         System.out.println (String.format ("mouse button %d/%d was pressed. (0x%08x)",
   538:                                            2, musNumberOfButtons, modifiersEx));
   539:       } else if ((releasedMask & BUTTON2_DOWN_MASK) != 0) {
   540:         System.out.println (String.format ("mouse button %d/%d was released. (0x%08x)",
   541:                                            2, musNumberOfButtons, modifiersEx));
   542:       }
   543:       if ((pressedMask & BUTTON3_DOWN_MASK) != 0) {
   544:         System.out.println (String.format ("mouse button %d/%d was pressed. (0x%08x)",
   545:                                            3, musNumberOfButtons, modifiersEx));
   546:       } else if ((releasedMask & BUTTON3_DOWN_MASK) != 0) {
   547:         System.out.println (String.format ("mouse button %d/%d was released. (0x%08x)",
   548:                                            3, musNumberOfButtons, modifiersEx));
   549:       }
   550:       if ((pressedMask & BUTTON4_DOWN_MASK) != 0) {
   551:         System.out.println (String.format ("mouse button %d/%d was pressed. (0x%08x)",
   552:                                            4, musNumberOfButtons, modifiersEx));
   553:       } else if ((releasedMask & BUTTON4_DOWN_MASK) != 0) {
   554:         System.out.println (String.format ("mouse button %d/%d was released. (0x%08x)",
   555:                                            4, musNumberOfButtons, modifiersEx));
   556:       }
   557:       if ((pressedMask & BUTTON5_DOWN_MASK) != 0) {
   558:         System.out.println (String.format ("mouse button %d/%d was pressed. (0x%08x)",
   559:                                            5, musNumberOfButtons, modifiersEx));
   560:       } else if ((releasedMask & BUTTON5_DOWN_MASK) != 0) {
   561:         System.out.println (String.format ("mouse button %d/%d was released. (0x%08x)",
   562:                                            5, musNumberOfButtons, modifiersEx));
   563:       }
   564:       if (((pressedMask | releasedMask) & (BUTTON1_DOWN_MASK |
   565:                                            BUTTON2_DOWN_MASK |
   566:                                            BUTTON3_DOWN_MASK |
   567:                                            BUTTON4_DOWN_MASK |
   568:                                            BUTTON5_DOWN_MASK)) == 0) {
   569:         System.out.println (String.format ("mouse button ?/%d was %s. (0x%08x)",
   570:                                            musNumberOfButtons, pressed ? "pressed" : "released", modifiersEx));
   571:       }
   572:     }
   573:     if (musCtrlRightOn &&  //Ctrlキー+左ボタンを右ボタンとみなす
   574:         (modifiersEx & MouseEvent.CTRL_DOWN_MASK) != 0) {  //Ctrlキーが押されている
   575:       if ((pressedMask & MouseEvent.BUTTON1_DOWN_MASK) != 0) {  //左ボタンが押された
   576:         pressedMask = ((pressedMask & ~MouseEvent.BUTTON1_DOWN_MASK) |  //左ボタンをクリアして
   577:                        MouseEvent.BUTTON3_DOWN_MASK);  //右ボタンをセットする
   578:       }
   579:       if ((releasedMask & MouseEvent.BUTTON1_DOWN_MASK) != 0) {  //左ボタンが離された
   580:         releasedMask = ((releasedMask & ~MouseEvent.BUTTON1_DOWN_MASK) |  //左ボタンをクリアして
   581:                         MouseEvent.BUTTON3_DOWN_MASK);  //右ボタンをセットする
   582:       }
   583:     }
   584:     if ((pressedMask & MouseEvent.BUTTON1_DOWN_MASK) != 0) {  //左ボタンが押された
   585:       musButtonLeft = true;
   586:       //if (musOnScreen) {  //マウスデータはスクリーン上で押されたときだけON
   587:       if (musOnScreen && (musSeamlessOn || XEiJ.frmIsActive)) {  //マウスデータはスクリーン上で押されたとき、エクスクルーシブのときは更にフォーカスがあるとき、だけON
   588:         musData |= 1;
   589:         musExtraData |= 1;
   590:       } else {
   591:         musData &= ~1;
   592:       }
   593:     } else if ((releasedMask & MouseEvent.BUTTON1_DOWN_MASK) != 0) {  //左ボタンが離された
   594:       musButtonLeft = false;
   595:       musData &= ~1;
   596:     }
   597:     if (musNumberOfButtons < 3) {  //2ボタンマウスのとき
   598:       if ((pressedMask & (MouseEvent.BUTTON2_DOWN_MASK |
   599:                           MouseEvent.BUTTON3_DOWN_MASK)) != 0) {  //右ボタンが押された
   600:         if ((modifiersEx & MouseEvent.ALT_DOWN_MASK) != 0) {  //Altキーが押されている
   601:           musSetSeamlessOn (!musSeamlessOn);  //シームレス/エクスクルーシブを切り替える
   602:         } else {  //Altキーが押されていない
   603:           musButtonRight = true;
   604:           //if (musOnScreen) {  //マウスデータはスクリーン上で押されたときだけON
   605:           if (musOnScreen && (musSeamlessOn || XEiJ.frmIsActive)) {  //マウスデータはスクリーン上で押されたとき、エクスクルーシブのときは更にフォーカスがあるとき、だけON
   606:             musData |= 2;
   607:             musExtraData |= 2;
   608:           } else {
   609:             musData &= ~2;
   610:           }
   611:         }
   612:       } else if ((releasedMask & (MouseEvent.BUTTON2_DOWN_MASK |
   613:                                   MouseEvent.BUTTON3_DOWN_MASK)) != 0) {  //右ボタンが離された
   614:         musButtonRight = false;
   615:         musData &= ~2;
   616:       }
   617:     } else {  //3ボタンマウスのとき
   618:       if ((pressedMask & MouseEvent.BUTTON2_DOWN_MASK) != 0) {  //ホイールが押された
   619:         ButtonFunction.bfnExecute (ButtonFunction.Button.WHEEL, modifiersEx, true, null);
   620:       } else if ((releasedMask & MouseEvent.BUTTON2_DOWN_MASK) != 0) {  //ホイールが離された
   621:         ButtonFunction.bfnExecute (ButtonFunction.Button.WHEEL, modifiersEx, false, null);
   622:       }
   623:       if ((pressedMask & MouseEvent.BUTTON3_DOWN_MASK) != 0) {  //右ボタンが押された
   624:         musButtonRight = true;
   625:         //if (musOnScreen) {  //マウスデータはスクリーン上で押されたときだけON
   626:         if (musOnScreen && (musSeamlessOn || XEiJ.frmIsActive)) {  //マウスデータはスクリーン上で押されたとき、エクスクルーシブのときは更にフォーカスがあるとき、だけON
   627:           musData |= 2;
   628:           musExtraData |= 2;
   629:         } else {
   630:           musData &= ~2;
   631:         }
   632:       } else if ((releasedMask & MouseEvent.BUTTON3_DOWN_MASK) != 0) {  //右ボタンが離された
   633:         musButtonRight = false;
   634:         musData &= ~2;
   635:       }
   636:       if (4 <= musNumberOfButtons) {  //4ボタンマウスのとき
   637:         if ((pressedMask & BUTTON4_DOWN_MASK) != 0) {  //ボタン4が押された
   638:           ButtonFunction.bfnExecute (ButtonFunction.Button.BUTTON4, modifiersEx, true, null);
   639:         } else if ((releasedMask & BUTTON4_DOWN_MASK) != 0) {  //ボタン4が離された
   640:           ButtonFunction.bfnExecute (ButtonFunction.Button.BUTTON4, modifiersEx, false, null);
   641:         }
   642:         if (5 <= musNumberOfButtons) {  //5ボタンマウスのとき
   643:           if ((pressedMask & BUTTON5_DOWN_MASK) != 0) {  //ボタン5が押された
   644:             ButtonFunction.bfnExecute (ButtonFunction.Button.BUTTON5, modifiersEx, true, null);
   645:           } else if ((releasedMask & BUTTON5_DOWN_MASK) != 0) {  //ボタン5が離された
   646:             ButtonFunction.bfnExecute (ButtonFunction.Button.BUTTON5, modifiersEx, false, null);
   647:           }
   648:         }
   649:       }
   650:     }
   651:     musDraggedOrMovedSub (me);
   652:     if (TextCopy.txcEncloseEachTime && musOnScreen) {
   653:       if ((pressedMask & MouseEvent.BUTTON1_DOWN_MASK) != 0) {  //左ボタンが押された
   654:         TextCopy.txcMousePressed (musScreenX, musScreenY);
   655:       } else if ((releasedMask & MouseEvent.BUTTON1_DOWN_MASK) != 0) {  //左ボタンが離された
   656:         TextCopy.txcMouseReleased (musScreenX, musScreenY);
   657:       }
   658:     }
   659:   }  //musPressedOrReleased(MouseEvent,boolean)
   660: 
   661:   //musDraggedOrMoved (me)
   662:   //  マウスが動いた
   663:   public static void musDraggedOrMoved (MouseEvent me) {
   664:     musDraggedOrMovedSub (me);
   665:     if (TextCopy.txcEncloseEachTime && musOnScreen) {
   666:       TextCopy.txcMouseMoved (musScreenX, musScreenY);
   667:     }
   668:   }
   669:   public static void musDraggedOrMovedSub (MouseEvent me) {
   670:     int x = musPanelX = me.getX ();
   671:     int y = musPanelY = me.getY ();
   672:     if (XEiJ.pnlScreenX1 <= x && x < XEiJ.pnlScreenX1 + XEiJ.pnlZoomWidth &&
   673:         XEiJ.pnlScreenY1 <= y && y < XEiJ.pnlScreenY1 + XEiJ.pnlZoomHeight) {  //スクリーン上にある
   674:       musOnScreen = true;  //スクリーンに入った
   675:       musScreenX = (x - XEiJ.pnlScreenX1) * XEiJ.pnlZoomRatioInX >> 16;  //端数は切り捨てる
   676:       musScreenY = (y - XEiJ.pnlScreenY1) * XEiJ.pnlZoomRatioInY >> 16;
   677:     } else {  //スクリーン上にない
   678:       musOnScreen = false;  //スクリーンから出た
   679:     }
   680:     if (XEiJ.pnlKeyboardX <= x && x < XEiJ.pnlKeyboardX + Keyboard.kbdWidth &&
   681:         XEiJ.pnlKeyboardY <= y && y < XEiJ.pnlKeyboardY + Keyboard.kbdHeight) {  //キーボード上にある
   682:       musOnKeyboard = true;  //キーボードに入った
   683:       Keyboard.kbdHover (x - XEiJ.pnlKeyboardX, y - XEiJ.pnlKeyboardY);
   684:     } else {  //キーボード上にない
   685:       if (musOnKeyboard) {  //キーボードから出た
   686:         musOnKeyboard = false;
   687:         if (Keyboard.kbdPointedIndex >= 0) {  //ポイントされているキーがあった
   688:           Keyboard.kbdHover (0, 0);  //ポイントを解除する
   689:         }
   690:       }
   691:     }
   692:   }  //musDraggedOrMoved(MouseEvent)
   693: 
   694:   //musSetSeamlessOn (on)
   695:   //  シームレス/エクスクルーシブを切り替える
   696:   public static void musSetSeamlessOn (boolean on) {
   697:     if (XEiJ.rbtRobot == null) {  //ロボットが使えないときは切り替えない(シームレスのみ)
   698:       return;
   699:     }
   700:     if (musSeamlessOn != on) {
   701:       musSeamlessOn = on;
   702:       if (on) {  //エクスクルーシブ→シームレス
   703:         musShow ();
   704:         //ホストのマウスカーソルをX68000のマウスカーソルの位置に移動させる
   705:         int x, y;
   706:         if (XEiJ.currentMPU < Model.MPU_MC68LC040) {  //MMUなし
   707:           if (Z8530.SCC_FSX_MOUSE &&
   708:               Z8530.sccFSXMouseHook != 0 &&  //FSX.Xが常駐している
   709:               MainMemory.mmrRls (0x0938) == Z8530.sccFSXMouseHook) {  //マウス受信データ処理ルーチンがFSX.Xを指している。SX-Windowが動作中
   710:             int xy = MainMemory.mmrRls (Z8530.sccFSXMouseWork + 0x0a);
   711:             x = (xy >> 16) - CRTC.crtR10TxXPort;  //SX-Windowのマウスカーソルの見かけのX座標
   712:             y = (short) xy - CRTC.crtR11TxYPort;  //SX-Windowのマウスカーソルの見かけのY座標
   713:           } else {  //SX-Windowが動作中ではない
   714:             int xy = MainMemory.mmrRls (0x0ace);
   715:             x = xy >> 16;  //IOCSのマウスカーソルのX座標
   716:             y = (short) xy;  //IOCSのマウスカーソルのY座標
   717:           }
   718:         } else {  //MMUあり
   719:           if (Z8530.SCC_FSX_MOUSE &&
   720:               Z8530.sccFSXMouseHook != 0 &&  //FSX.Xが常駐している
   721:               MC68060.mmuPeekLongData (0x0938, 1) == Z8530.sccFSXMouseHook) {  //マウス受信データ処理ルーチンがFSX.Xを指している。SX-Windowが動作中
   722:             int xy = MC68060.mmuPeekLongData (Z8530.sccFSXMouseWork + 0x0a, 1);
   723:             x = (xy >> 16) - CRTC.crtR10TxXPort;  //SX-Windowのマウスカーソルの見かけのX座標
   724:             y = (short) xy - CRTC.crtR11TxYPort;  //SX-Windowのマウスカーソルの見かけのY座標
   725:           } else {  //SX-Windowが動作中ではない
   726:             int xy = MC68060.mmuPeekLongData (0x0ace, 1);
   727:             x = xy >> 16;  //IOCSのマウスカーソルのX座標
   728:             y = (short) xy;  //IOCSのマウスカーソルのY座標
   729:           }
   730:         }
   731:         XEiJ.rbtRobot.mouseMove (x * XEiJ.pnlZoomWidth / XEiJ.pnlScreenWidth + XEiJ.pnlScreenX1 + XEiJ.pnlGlobalX,
   732:                                  y * XEiJ.pnlZoomHeight / XEiJ.pnlScreenHeight + XEiJ.pnlScreenY1 + XEiJ.pnlGlobalY);
   733:       } else {  //シームレス→エクスクルーシブ
   734:         musHide ();
   735:         Point point = XEiJ.pnlPanel.getLocationOnScreen ();
   736:         XEiJ.pnlGlobalX = point.x;
   737:         XEiJ.pnlGlobalY = point.y;
   738:         musExclusiveStart = true;  //エクスクルーシブに切り替えた直後
   739:       }
   740:     }
   741:     if (musSeamlessMouseCheckBox.isSelected () != on) {
   742:       musSeamlessMouseCheckBox.setSelected (on);
   743:     }
   744:   }  //musSetSeamlessOn(boolean)
   745: 
   746:   //musHide ()
   747:   //  マウスカーソルを消す
   748:   public static void musHide () {
   749:     if (musCursorNumber != 0 && musCursorAvailable) {
   750:       musCursorNumber = 0;
   751:       XEiJ.pnlPanel.setCursor (musCursorArray[0]);
   752:     }
   753:   }  //musHide()
   754: 
   755:   //musShow ()
   756:   //  マウスカーソルを表示する
   757:   public static void musShow () {
   758:     if (musCursorNumber != 1 && musCursorAvailable) {
   759:       musCursorNumber = 1;
   760:       XEiJ.pnlPanel.setCursor (musCursorArray[1]);
   761:     }
   762:   }  //musShow()
   763: 
   764:   //musSetEdgeAccelerationOn (on)
   765:   //  縁部加速
   766:   public static void musSetEdgeAccelerationOn (boolean on) {
   767:     musEdgeAccelerationOn = on;
   768:   }  //musSetEdgeAccelerationOn(boolean)
   769: 
   770:   //musSetHostsPixelUnitsOn (on)
   771:   //  true=エクスクルーシブのときマウスはホストの画素単位で動く,false=X68000の画素単位で動く
   772:   public static void musSetHostsPixelUnitsOn (boolean on) {
   773:     musHostsPixelUnitsOn = on;
   774:     musUpdateSpeedRatio ();
   775:   }  //musSetHostsPixelUnitsOn(boolean)
   776: 
   777: }  //class Mouse