CONDevice.java
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13: package xeij;
14:
15: import java.awt.datatransfer.*;
16: import java.awt.event.*;
17: import java.io.*;
18: import java.util.*;
19: import java.util.concurrent.*;
20: import javax.swing.*;
21:
22: public class CONDevice {
23:
24:
25: public static final long CON_PASTE_DELAY = 10L;
26:
27:
28:
29: public static final long CON_PASTE_INTERVAL = 10L;
30:
31:
32: public static final String CON_PASTE_PIPE_NAME = "XEiJPaste";
33:
34: public static final String CON_CONTROL_PIPE_NAME = "XEiJControl";
35:
36:
37: public static final int CON_PIPE_SJIS = 0;
38: public static final int CON_PIPE_UTF8 = 1;
39: public static int conPipeEncoding;
40:
41:
42:
43:
44:
45:
46: public static final int CON_PIPE_INSTANCES_WIN = 4;
47: public static final int CON_PIPE_INSTANCES_GEN = 2;
48: public static int conPipeInstances;
49:
50:
51: public static final String[] CON_KEY_BASE = (
52: "esc,1," +
53: "1,2," +
54: "2,3," +
55: "3,4," +
56: "4,5," +
57: "5,6," +
58: "6,7," +
59: "7,8," +
60: "8,9," +
61: "9,10," +
62: "0,11," +
63: "minus,12," +
64: "caret,13," +
65: "yen,14," +
66: "bs,15," +
67: "tab,16," +
68: "q,17," +
69: "w,18," +
70: "e,19," +
71: "r,20," +
72: "t,21," +
73: "y,22," +
74: "u,23," +
75: "i,24," +
76: "o,25," +
77: "p,26," +
78: "at,27," +
79: "leftbracket,28," +
80: "return,29," +
81: "a,30," +
82: "s,31," +
83: "d,32," +
84: "f,33," +
85: "g,34," +
86: "h,35," +
87: "j,36," +
88: "k,37," +
89: "l,38," +
90: "semicolon,39," +
91: "colon,40," +
92: "rightbracket,41," +
93: "z,42," +
94: "x,43," +
95: "c,44," +
96: "v,45," +
97: "b,46," +
98: "n,47," +
99: "m,48," +
100: "comma,49," +
101: "period,50," +
102: "slash,51," +
103: "underline,52," +
104: "space,53," +
105: "home,54," +
106: "del,55," +
107: "rollup,56," +
108: "rolldown,57," +
109: "undo,58," +
110: "left,59," +
111: "up,60," +
112: "right,61," +
113: "down,62," +
114: "clr,63," +
115: "tenkeyslash,64," +
116: "tenkeyasterisk,65," +
117: "tenkeyminus,66," +
118: "tenkey7,67," +
119: "tenkey8,68," +
120: "tenkey9,69," +
121: "tenkeyplus,70," +
122: "tenkey4,71," +
123: "tenkey5,72," +
124: "tenkey6,73," +
125: "tenkeyequal,74," +
126: "tenkey1,75," +
127: "tenkey2,76," +
128: "tenkey3,77," +
129: "enter,78," +
130: "tenkey0,79," +
131: "tenkeycomma,80," +
132: "tenkeyperiod,81," +
133: "kigou,82," +
134: "touroku,83," +
135: "help,84," +
136: "xf1,85," +
137: "xf2,86," +
138: "xf3,87," +
139: "xf4,88," +
140: "xf5,89," +
141: "kana,90," +
142: "roma,91," +
143: "code,92," +
144: "caps,93," +
145: "ins,94," +
146: "hiragana,95," +
147: "zenkaku,96," +
148: "break,97," +
149: "copy,98," +
150: "f1,99," +
151: "f2,100," +
152: "f3,101," +
153: "f4,102," +
154: "f5,103," +
155: "f6,104," +
156: "f7,105," +
157: "f8,106," +
158: "f9,107," +
159: "f10,108," +
160:
161: "shift,112," +
162: "ctrl,113," +
163: "opt1,114," +
164: "opt2,115," +
165: "num,116," +
166: "").split (",");
167:
168:
169: public static boolean conPipeOn;
170:
171: public static JMenu conSettingsMenu;
172:
173: public static JCheckBoxMenuItem conPipeCheckBox;
174:
175:
176: public static boolean conCleanupFlag;
177:
178: public static int conCON;
179:
180:
181: public static CONPasteTask conPasteTask;
182:
183: public static LinkedBlockingQueue<String> conPasteQueue;
184:
185: public static CONPasteQueueThread conPasteQueueThread;
186:
187:
188: public static HashMap<String,Integer> conKeyMap;
189:
190: public static LinkedBlockingQueue<String> conControlQueue;
191:
192: public static CONControlQueueThread conControlQueueThread;
193:
194:
195: public static CONPipeThread[] conPipeThreadArray;
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215: public static void conInit () {
216:
217: conPipeOn = Settings.sgsGetOnOff ("pastepipe");
218: {
219: String s = Settings.sgsGetString ("pipeencoding").toLowerCase ();
220: conPipeEncoding = (s.equals ("utf8") ? CON_PIPE_UTF8 :
221: CON_PIPE_SJIS);
222: }
223: conPipeInstances = XEiJ.prgIsWindows ? CON_PIPE_INSTANCES_WIN : CON_PIPE_INSTANCES_GEN;
224:
225: ActionListener listener = new ActionListener () {
226: @Override public void actionPerformed (ActionEvent ae) {
227: Object source = ae.getSource ();
228: String command = ae.getActionCommand ();
229: switch (command) {
230: case "Stop paste":
231: conStopPaste ();
232: break;
233: case "Paste and control pipe":
234: conSetPipeOn (((JCheckBoxMenuItem) source).isSelected ());
235: break;
236: case "SJIS":
237: conPipeEncoding = CON_PIPE_SJIS;
238: break;
239: case "UTF-8":
240: conPipeEncoding = CON_PIPE_UTF8;
241: break;
242: }
243: }
244: };
245: ButtonGroup encodingGroup = new ButtonGroup ();
246: conSettingsMenu = Multilingual.mlnText (
247: ComponentFactory.createMenu (
248: "Pipe settings",
249: Multilingual.mlnText (
250: ComponentFactory.createMenuItem ("Stop paste", listener),
251: "ja", "貼り付け中止"),
252: ComponentFactory.createHorizontalSeparator (),
253: conPipeCheckBox = Multilingual.mlnText (
254: ComponentFactory.createCheckBoxMenuItem (conPipeOn, "Paste and control pipe", listener),
255: "ja", "貼り付けおよび制御パイプ"),
256: ComponentFactory.createRadioButtonMenuItem (encodingGroup, conPipeEncoding == CON_PIPE_SJIS, "SJIS", listener),
257: ComponentFactory.createRadioButtonMenuItem (encodingGroup, conPipeEncoding == CON_PIPE_UTF8, "UTF-8", listener)
258: ),
259: "ja", "パイプ設定");
260:
261:
262: conCleanupFlag = false;
263:
264: conCON = 0;
265:
266:
267: conPasteTask = null;
268:
269: conPasteQueue = new LinkedBlockingQueue<String> ();
270:
271: conPasteQueueThread = new CONPasteQueueThread ();
272:
273: conPasteQueueThread.start ();
274:
275:
276: conKeyMap = new HashMap<String,Integer> ();
277: for (int i = 0; i + 1 < CON_KEY_BASE.length; i += 2) {
278: conKeyMap.put (CON_KEY_BASE[i], Integer.parseInt (CON_KEY_BASE[i + 1], 10));
279: }
280:
281: conControlQueue = new LinkedBlockingQueue<String> ();
282:
283: conControlQueueThread = new CONControlQueueThread ();
284:
285: conControlQueueThread.start ();
286:
287:
288: conPipeThreadArray = new CONPipeThread[conPipeInstances];
289:
290: for (int i = 0; i < conPipeInstances; i++) {
291: if (conPipeOn) {
292:
293: conPipeThreadArray[i] = new CONPipeThread ((i & 1) != 0);
294:
295: if (conPipeThreadArray[i].getPipeStream () != null) {
296:
297: conPipeThreadArray[i].start ();
298: }
299: } else {
300:
301: conPipeThreadArray[i] = null;
302: }
303: }
304: }
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326: public static void conTini () {
327:
328: Settings.sgsPutOnOff ("pastepipe", conPipeOn);
329: Settings.sgsPutString ("pipeencoding",
330: conPipeEncoding == CON_PIPE_UTF8 ? "utf8" :
331: "sjis");
332:
333: conCleanupFlag = true;
334:
335: if (conPasteQueueThread != null) {
336:
337: conPasteQueueThread.interrupt ();
338:
339: try {
340: conPasteQueueThread.join (100);
341: } catch (InterruptedException ie) {
342: }
343:
344: conPasteQueueThread = null;
345: }
346:
347: CONPasteTask t = conPasteTask;
348: if (t != null) {
349:
350: t.cancel ();
351:
352: conPasteTask = null;
353: }
354:
355: if (conControlQueueThread != null) {
356:
357: conControlQueueThread.interrupt ();
358:
359: try {
360: conControlQueueThread.join (100);
361: } catch (InterruptedException ie) {
362: }
363:
364: conControlQueueThread = null;
365: }
366:
367: for (int i = 0; i < conPipeInstances; i++) {
368:
369: if (conPipeThreadArray[i] != null) {
370:
371: conPipeThreadArray[i].closePipe ();
372:
373: try {
374: conPipeThreadArray[i].join (100);
375: } catch (InterruptedException ie) {
376: }
377:
378: conPipeThreadArray[i] = null;
379: }
380: }
381: }
382:
383:
384:
385:
386: public static void conReset () {
387:
388: conCON = 0;
389: }
390:
391:
392:
393:
394:
395: public static void conDoPaste () {
396:
397: if (XEiJ.clpClipboard == null) {
398: return;
399: }
400: String string = null;
401: try {
402: string = (String) XEiJ.clpClipboard.getData (DataFlavor.stringFlavor);
403: } catch (Exception e) {
404: return;
405: }
406: if (string == null || string.equals ("")) {
407: return;
408: }
409:
410: conPasteQueue.add (string);
411: }
412:
413:
414:
415:
416:
417:
418: public static void conStopPaste () {
419:
420: conPasteQueue.clear ();
421:
422: CONPasteTask t = conPasteTask;
423: if (t != null) {
424:
425: t.stopPaste ();
426: }
427: }
428:
429:
430:
431:
432:
433:
434:
435:
436:
437:
438:
439:
440:
441:
442:
443: public static void conSetPipeOn (boolean on) {
444: if (conPipeOn != on) {
445: conPipeOn = on;
446: conPipeCheckBox.setSelected (on);
447: if (on) {
448:
449: for (int i = 0; i < conPipeInstances; i++) {
450:
451: if (conPipeThreadArray[i] == null) {
452:
453: conPipeThreadArray[i] = new CONPipeThread ((i & 1) != 0);
454:
455: if (conPipeThreadArray[i].getPipeStream () != null) {
456:
457: conPipeThreadArray[i].start ();
458: }
459: }
460: }
461: } else {
462:
463: for (int i = 0; i < conPipeInstances; i++) {
464:
465: if (conPipeThreadArray[i] != null) {
466:
467: conPipeThreadArray[i].closePipe ();
468:
469: try {
470: conPipeThreadArray[i].join (100);
471: } catch (InterruptedException ie) {
472: }
473:
474: conPipeThreadArray[i] = null;
475: }
476: }
477: }
478: }
479: }
480:
481:
482:
483:
484:
485:
486:
487:
488:
489:
490:
491:
492:
493:
494:
495: public static class CONPasteQueueThread extends Thread {
496: @Override public void run () {
497:
498: for (;;) {
499:
500: if (conCleanupFlag) {
501:
502: return;
503: }
504:
505: if (conPasteTask == null) {
506:
507:
508: String string = null;
509: try {
510: string = conPasteQueue.take ();
511: } catch (InterruptedException ie) {
512: }
513:
514: if (conCleanupFlag) {
515:
516: return;
517: }
518:
519: if (string != null && !string.equals ("")) {
520:
521: conPasteTask = new CONPasteTask (string);
522:
523: conPasteTask.start ();
524: }
525: }
526:
527: try {
528: Thread.sleep (200);
529: } catch (InterruptedException ie) {
530: }
531: }
532: }
533: }
534:
535:
536:
537:
538:
539:
540:
541:
542:
543:
544:
545:
546:
547: public static class CONControlQueueThread extends Thread {
548: @Override public void run () {
549: StringBuilder controlBuilder = new StringBuilder ();
550:
551: for (;;) {
552:
553: if (conCleanupFlag) {
554:
555: return;
556: }
557:
558:
559: String string = null;
560: try {
561: string = conControlQueue.take ();
562: } catch (InterruptedException ie) {
563: }
564:
565: if (conCleanupFlag) {
566:
567: return;
568: }
569:
570: if (string != null && !string.equals ("")) {
571:
572: controlBuilder.append (string);
573: for (;;) {
574: int l = controlBuilder.length ();
575:
576: int i = 0;
577: for (; i < l; i++) {
578: int c = controlBuilder.charAt (i);
579: if (!(c == '\n' || c == '\r' || c == ':')) {
580: break;
581: }
582: }
583: if (l <= i) {
584: break;
585: }
586:
587: int j = i;
588: for (; j < l; j++) {
589: int c = controlBuilder.charAt (j);
590: if (c == '\n' || c == '\r' || c == ':') {
591: break;
592: }
593: }
594: if (l <= j) {
595: break;
596: }
597:
598: String s = controlBuilder.substring (i, j);
599: controlBuilder.delete (0, j);
600:
601: conCommand (s);
602: }
603: }
604:
605: try {
606: Thread.sleep (200);
607: } catch (InterruptedException ie) {
608: }
609: }
610: }
611: }
612:
613:
614:
615:
616:
617:
618:
619:
620: public static void conCommand (String command) {
621:
622: command = command.trim ().toLowerCase (Locale.ROOT);
623:
624: if (command.length () == 0) {
625: return;
626: }
627:
628: String[] args = command.split ("[\\x00-\\x20]+");
629:
630: switch (args[0]) {
631: case "interrupt":
632: XEiJ.sysInterrupt ();
633: return;
634: case "presskey":
635: conPressKey (args);
636: return;
637: case "releasekey":
638: conReleaseKey (args);
639: return;
640: case "typekey":
641: conPressKey (args);
642: conReleaseKey (args);
643: return;
644: case "opt1reset":
645: XEiJ.mpuReset (0, -1);
646: return;
647: case "reset":
648: XEiJ.mpuReset (-1, -1);
649: return;
650: default:
651: System.out.println ("unknown command " + args[0]);
652: return;
653: }
654: }
655:
656:
657:
658: public static void conPressKey (String[] args) {
659: for (int k = 1; k < args.length; k++) {
660: String key = args[k];
661: if (conKeyMap.containsKey (key)) {
662: Keyboard.kbdCommandPress (conKeyMap.get (key).intValue ());
663: } else {
664: System.out.println ("unknown key " + key);
665: return;
666: }
667: }
668: }
669:
670:
671:
672: public static void conReleaseKey (String[] args) {
673: for (int k = args.length - 1; 1 <= k; k--) {
674: String key = args[k];
675: if (conKeyMap.containsKey (key)) {
676: Keyboard.kbdCommandRelease (conKeyMap.get (key).intValue ());
677: } else {
678: System.out.println ("unknown key " + key);
679: return;
680: }
681: }
682: }
683:
684:
685:
686: public static class CONPipeThread extends Thread {
687:
688:
689: public boolean isControlPipe;
690:
691: public NamedPipeInputStream pipeStream;
692:
693: public boolean closeFlag;
694:
695:
696:
697:
698:
699:
700:
701: public CONPipeThread (boolean isControlPipe) {
702:
703: this.isControlPipe = isControlPipe;
704:
705: pipeStream = null;
706:
707: closeFlag = false;
708:
709: try {
710: pipeStream = NamedPipeInputStream.create (isControlPipe ? CON_CONTROL_PIPE_NAME : CON_PASTE_PIPE_NAME);
711: } catch (IOException ioe) {
712: ioe.printStackTrace ();
713: }
714: }
715:
716:
717:
718:
719:
720: public NamedPipeInputStream getPipeStream () {
721:
722: return pipeStream;
723: }
724:
725:
726:
727:
728:
729: public void closePipe () {
730:
731: closeFlag = true;
732:
733: if (pipeStream != null) {
734: pipeStream.close ();
735: }
736: }
737:
738:
739:
740:
741:
742:
743:
744:
745: @Override public void run () {
746: byte[] b = new byte[1024];
747: int pool = 0;
748: try {
749: while (!conCleanupFlag &&
750: !closeFlag &&
751: pipeStream != null) {
752:
753: int l = pipeStream.read (b);
754:
755: if (l == -1) {
756: break;
757: }
758:
759: StringBuilder sb = new StringBuilder ();
760: if (conPipeEncoding == CON_PIPE_UTF8) {
761: for (int i = 0; i < l; i++) {
762: int u = (pool << 8) | (0xff & b[i]);
763: if ((u & 0xffffffe0) == 0x000000c0 ||
764: (u & 0xfffffff0) == 0x000000e0 ||
765: (u & 0xfffff0c0) == 0x0000e080 ||
766: (u & 0xfffffff8) == 0x000000f0 ||
767: (u & 0xfffff8c0) == 0x0000f080 ||
768: (u & 0xfff8c0c0) == 0x00f08080) {
769: pool = u;
770: continue;
771: }
772: pool = 0;
773: if ((u & 0xffffff80) == 0x00000000) {
774: } else if ((u & 0xffffe0c0) == 0x0000c080) {
775: u = ((u & 0x00001f00) >> 2) | (u & 0x0000003f);
776: } else if ((u & 0xfff0c0c0) == 0x00e08080) {
777: u = ((u & 0x000f0000) >> 4) | ((u & 0x00003f00) >> 2) | (u & 0x0000003f);
778: } else if ((u & 0xf8c0c0c0) == 0xf0808080) {
779: u = ((u & 0x07000000) >> 6) | ((u & 0x003f0000) >> 4) | ((u & 0x00003f00) >> 2) | (u & 0x0000003f);
780: if (0x10ffff < u) {
781: u = '※';
782: } else if (0x00ffff < u) {
783:
784:
785:
786:
787:
788: u = '※';
789: }
790: } else {
791: u = '※';
792: }
793: sb.append ((char) u);
794: }
795: } else {
796: for (int i = 0; i < l; i++) {
797: int s = (pool << 8) | (0xff & b[i]);
798: if ((0x0080 <= s && s <= 0x009f) || (0x00e0 <= s && s <= 0x00ff)) {
799: pool = s;
800: continue;
801: }
802: pool = 0;
803: int u = CharacterCode.chrSJISToChar[s];
804: if (u == 0 && s != 0) {
805: u = '\ufffd';
806: }
807: sb.append ((char) u);
808: }
809: }
810:
811: (isControlPipe ? conControlQueue : conPasteQueue).add (sb.toString ());
812: }
813: } catch (IOException ioe) {
814: ioe.printStackTrace ();
815: }
816: }
817:
818: }
819:
820:
821: public static class CONPasteTask extends TimerTask {
822:
823:
824: public String string;
825:
826: public int length;
827:
828: public int index;
829:
830: public boolean stopFlag;
831:
832:
833:
834:
835: public CONPasteTask (String string) {
836: this.string = string;
837: length = string.length ();
838: index = 0;
839:
840: stopFlag = false;
841: }
842:
843:
844:
845:
846: public void start () {
847:
848: XEiJ.tmrTimer.schedule (conPasteTask, CON_PASTE_DELAY, CON_PASTE_INTERVAL);
849: }
850:
851:
852:
853:
854: public void stopPaste () {
855:
856: stopFlag = true;
857: }
858:
859:
860:
861:
862:
863:
864:
865:
866:
867:
868:
869:
870:
871:
872:
873:
874:
875:
876:
877:
878:
879:
880:
881: @Override public void run () {
882:
883: int con = conCON;
884: if (con == 0) {
885:
886: con = MainMemory.mmrHumanDev ('C' << 24 | 'O' << 16 | 'N' << 8 | ' ',
887: ' ' << 24 | ' ' << 16 | ' ' << 8 | ' ');
888: if (0 <= con &&
889: (
890:
891:
892:
893:
894:
895: MC68060.mmuPeekLongData (con + 0x000180, 1) == 0x20826082 &&
896: MC68060.mmuPeekLongData (con + 0x000184, 1) == 0x72826a82 &&
897: MC68060.mmuPeekLongData (con + 0x000188, 1) == 0x55825782 &&
898: MC68060.mmuPeekLongData (con + 0x00018c, 1) == 0x6a20666f &&
899: MC68060.mmuPeekLongData (con + 0x000190, 1) == 0x72205836 &&
900: MC68060.mmuPeekLongData (con + 0x000194, 1) == 0x38303030 &&
901: MC68060.mmuPeekLongData (con + 0x000198, 1) == 0x20766572 &&
902: MC68060.mmuPeekLongData (con + 0x00019c, 1) == 0x73696f6e &&
903: MC68060.mmuPeekLongData (con + 0x0001a0, 1) == 0x20332e30 &&
904: MC68060.mmuPeekLongData (con + 0x0001a4, 1) == 0x320d0a43
905:
906:
907:
908:
909:
910:
911:
912:
913:
914:
915:
916:
917: )) {
918: conCON = con;
919: }
920: }
921:
922: if (con == 0 || stopFlag) {
923:
924: conPasteQueue.clear ();
925:
926: cancel ();
927:
928: conPasteTask = null;
929:
930: return;
931: }
932:
933: int read = MC68060.mmuPeekLongData (con + 0x00e460, 1);
934: int write = MC68060.mmuPeekLongData (con + 0x00e464, 1);
935: if (write != read) {
936:
937: return;
938: }
939:
940: int head = con + 0x010504;
941: int tail = head + 200;
942: for (; index < length; index++) {
943: int c = CharacterCode.chrCharToSJIS[string.charAt (index)];
944: if (c == 0) {
945: continue;
946: }
947: if (c == '\r' && index + 1 < length && string.charAt (index + 1) == '\n') {
948: index++;
949: } else if (c == '\n') {
950: c = '\r';
951: }
952: if (!(c >= ' ' || c == '\t' || c == '\r' || c == 0x1b)) {
953: continue;
954: }
955: int write1 = write + 1 == tail ? head : write + 1;
956: int write2 = write1 + 1 == tail ? head : write1 + 1;
957: int write3 = write2 + 1 == tail ? head : write2 + 1;
958: if (write1 == read || write2 == read || write3 == read || write3 == read) {
959: break;
960: }
961: if (c < 0x0100) {
962: MC68060.mmuPokeByteData (write1, c, 1);
963: write = write1;
964: } else {
965: MC68060.mmuPokeByteData (write1, c >> 8, 1);
966: MC68060.mmuPokeByteData (write2, c, 1);
967: write = write2;
968: }
969: }
970: MC68060.mmuPokeLongData (con + 0x00e464, write, 1);
971:
972: if (index == length) {
973:
974: cancel ();
975:
976: conPasteTask = null;
977:
978: return;
979: }
980:
981: }
982:
983: }
984:
985: }