|
|
1.1 root 1: //
2: // nono
1.1.1.3 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.5 root 7: // 物理キーボードとソフトウェアキーボードについて
8: //
9: // 物理キーボード入力 ---------->+
10: // OR --> VM Keyboard ---> VM (MFP/SIO)
11: // ソフトウェアキーボード 入力 ->+ |
12: // |
13: // ソフトウェアキーボード 表示 <-------------+
14: //
15: // 物理キーボードとソフトウェアキーボードからの入力は並列で対等とする。
16: // つまり優劣を付けず後から成立したイベントで現在の状態を上書きする。
17: // これは特に物理キーボードから [ALT]+[TAB] でウィンドウを切り替えた際に
18: // [TAB] の Make は発生するが Break が VM に届かない (そのため高速モード
19: // が抑制されたままになる) という事態をソフトウェアキーボードから復旧する
20: // ために必要。
21: //
22: // 例えば、ソフトウェアキーボードであるキーを(右クリックで)長押ししたまま
23: // (A)、物理キーボードで同じキーを押して(B)離す(C)と、まず A で Make が
24: // 成立、B は無視して (この時点ですでに Make 状態なので)、C で Break が
25: // 成立、となる。
26: // Break が成立したのでソフトウェアキーボードの描画上もキー開放になる。
27: // 逆に物理キーボードであるキーを押したまま(A)、ソフトウェアキーボードで
28: // 同じキーをクリックする、つまり押して(B)離す(C) と、まず A で Make が
29: // 成立、B は無視され、C で Break が成立までは同じだが、引き続き物理キー
30: // が押されたままならおそらくホストの動作によってもう一度 Make が入ること
31: // になる。この動作は(望ましいかどうかはともかく)仕様として許容する。
32: //
33: // また、ソフトウェアキーボードの表示は上図の通り、ソフトウェアキーボード
34: // 自身(のみ)の状態ではなく VM Keyboard 全体の現状を表示する表示器とする。
35:
36: // SHIFT キーについて
37: // 物理キーボード層、ソフトウェアキーボード層、VM の Keyboard クラスは
38: // いずれも左右の [SHIFT] キーは別個のキーとして管理しているが、X680x0 の
39: // キーボードは左右の [SHIFT] キーが独立しておらず一つのキーのように
40: // 振る舞っている(はず)。
41: //
42: // ヒューマンインタフェース的には例えば、
43: // 物理キーボードで 左[SHIFT]押下(A) -> 右[SHIFT]押下(B) -> 右[SHIFT]開放
44: // (C) -> 左[SHIFT]開放(D) の順で操作すると、A で Make が成立、B は無視
45: // ([SHIFT] はすでに押されているので)、C は無視([SHIFT] はまだ押されてる
46: // ので)、D で Break が成立、となってほしい。これは明らかに人間がそのように
47: // キーを操作しているため。
48: // 一方、ソフトウェアキーボードでは、右[SHIFT]を右クリック(押下維持)する
49: // と両方の [SHIFT] キーが点灯する。この状態で左[SHIFT] を左クリック
50: // (押下後即開放)すると、両方の [SHIFT] が開放されてほしい。これは、
51: // ユーザからはどちらのキーが押下維持で点灯していて、どちらが押されてない
52: // のに付随点灯しているかは区別できないため。
53: //
54: // このことから、
55: // o 物理キー入力は左右 [SHIFT] を区別する
56: // o ソフトウェアキーボードは左右どちらのキーに対する操作でも、内部では
57: // 左右両方の [SHIFT] を押下開放する
58: // o この結果、右 [SHIFT] と左 [SHIFT] の OR を代表値とし、
59: // MFP への通知や表示(左右の [SHIFT] は常に連動) を行う。
60: // でどうか。
61: //
62: // 物理 [SHIFT] キーを押し続けたまま、ソフトウェアキーボードから左クリック
63: // (押下後即開放) すると、物理キーを押しているにも関わらず [SHIFT] キーは
64: // 開放される。これは上述の通り仕様である。
65:
66: // LUNA のキーボードは左右の SHIFT キーの区別がある。このため、 LUNA は
67: // 特別処理をする必要はない。
68: // ただしゲスト上の入力ルーチンがこの区別をしているかは別問題である。
69:
1.1 root 70: #include "keyboard.h"
1.1.1.5 root 71: #include "scheduler.h"
1.1.1.7 root 72: #include "uimessage.h"
1.1 root 73:
1.1.1.2 root 74: std::unique_ptr<Keyboard> gKeyboard;
1.1 root 75:
76: // コンストラクタ
77: Keyboard::Keyboard()
1.1.1.6 root 78: : inherited("Keyboard")
1.1 root 79: {
1.1.1.5 root 80: pressed.resize(KC_max);
81:
82: keyinput_event.func = (DeviceCallback_t)&Keyboard::KeyInputCallback;
83: keyinput_event.time = 0;
84: keyinput_event.SetName("Keyboard Input");
1.1 root 85: }
86:
87: // デストラクタ
88: Keyboard::~Keyboard()
89: {
90: }
91:
1.1.1.5 root 92: // リセット
93: void
94: Keyboard::ResetHard()
95: {
96: // 内部状態を全部リセット
97:
98: inputqueue.Clear();
99: sendqueue.Clear();
100: std::fill(pressed.begin(), pressed.end(), false);
101: num_of_pressed = 0;
102: // キーが全部解放されたので通知
103: gScheduler->RequestKeyPressed(false);
104: }
105:
106: // キーを押した (外部から、共通キーコードで呼ばれる)
107: void
108: Keyboard::MakeKey(uint keycode)
109: {
110: // ここは UI スレッドからも呼ばれるのでキューに突っ込むだけ
111: {
112: std::unique_lock<std::mutex> lock(mtx);
113: inputqueue.Enqueue(keycode | KC_FLAG_MAKE);
114: }
115: keyinput_event.Start();
116: }
117:
118: // キーを離した (外部から、共通キーコードで呼ばれる)
119: void
120: Keyboard::BreakKey(uint keycode)
121: {
122: // ここは UI スレッドからも呼ばれるのでキューに突っ込むだけ
123: {
124: std::unique_lock<std::mutex> lock(mtx);
125: inputqueue.Enqueue(keycode | KC_FLAG_BREAK);
126: }
127: keyinput_event.Start();
128: }
129:
1.1.1.8 ! root 130: // 文字による入力
! 131: // WX での文字入力モード、CLI からの入力、ターミナルエミュレーションでの
! 132: // 入力によりキーボードの押し下げをエミュレートする。
! 133: void
! 134: Keyboard::CharInput(uint charcode)
! 135: {
! 136: if (charcode >= char_key.size()) {
! 137: return;
! 138: }
! 139:
! 140: uint key = char_key[charcode];
! 141: uint code = KC_CODE(key);
! 142: if ((key & KM_SHIFT)) {
! 143: MakeKey(KC_SHIFT_L | KC_FLAG_POST);
! 144: MakeKey(KC_SHIFT_R | KC_FLAG_POST);
! 145: } else {
! 146: BreakKey(KC_SHIFT_L);
! 147: BreakKey(KC_SHIFT_R);
! 148: }
! 149: if ((key & KM_CTRL)) {
! 150: MakeKey(KC_CTRL | KC_FLAG_POST);
! 151: } else {
! 152: BreakKey(KC_CTRL);
! 153: }
! 154: MakeKey(code | KC_FLAG_POST);
! 155: BreakKey(code);
! 156: if ((key & KM_CTRL)) {
! 157: BreakKey(KC_CTRL);
! 158: }
! 159: if ((key & KM_SHIFT)) {
! 160: BreakKey(KC_SHIFT_L);
! 161: BreakKey(KC_SHIFT_R);
! 162: }
! 163: }
! 164:
1.1.1.5 root 165: // キー入力コールバック
166: void
167: Keyboard::KeyInputCallback(Event& ev)
168: {
169: for (;;) {
170: uint keystat;
171: {
172: std::unique_lock<std::mutex> lock(mtx);
173: if (inputqueue.Empty()) {
174: return;
175: }
176: keystat = inputqueue.Dequeue();
177: }
178: KeyInput(keystat);
179: }
180: }
181:
182: // キー入力を1つ処理する
1.1.1.6 root 183: // キーを送信したら true を返す
184: bool
185: Keyboard::KeyInputCommon(uint keystat)
1.1.1.5 root 186: {
187: uint keycode = KC_CODE(keystat);
188: bool ismake = KC_IS_MAKE(keystat);
189:
190: // 対応する機種別キーコードがなければ無視。
191: // (ここでは調べるだけでまだ変換はしない)
192: if (KeyToMDKey(keycode) == 0) {
1.1.1.6 root 193: return false;
1.1.1.5 root 194: }
195:
196: // このキーの状態が変化しないのなら何もしない。
197: if (IsPressed(keycode) == ismake) {
1.1.1.6 root 198: return false;
1.1.1.5 root 199: }
200:
201: // 処理することが確定したのでログ
202: // (離すほうは押すほうよりログレベルを1つ上げてある)
203: if (ismake) {
1.1.1.6 root 204: putlog(1, "MakeKey %d(%s)", keycode, GetKeyName(keycode).c_str());
1.1.1.5 root 205: } else {
1.1.1.6 root 206: putlog(2, "BreakKey %d(%s)", keycode, GetKeyName(keycode).c_str());
1.1.1.5 root 207: }
208:
209: // キーが押されてる間は VM の高速実行を抑制する
210: // すべてのキーが離されたら高速モードを許可する
211: pressed[keycode] = ismake;
212: if (ismake) {
213: if (num_of_pressed++ == 0) {
214: gScheduler->RequestKeyPressed(true);
215: }
216: } else {
217: if (--num_of_pressed == 0) {
218: gScheduler->RequestKeyPressed(false);
219: }
220: }
221:
1.1.1.8 ! root 222: // UI に状態変更を通知。
! 223: // KC_FLAG_POST が立っていればキーコードも送信 (char 入力モード用)。
! 224: // 通知を受け取った UI 側が IsPressed() などでキーの現状を調べることが
! 225: // あるため pressed[] 更新後に通知する。
! 226: uint arg;
! 227: if (KC_IS_POST(keystat)) {
! 228: arg = keycode;
! 229: } else {
! 230: arg = 0;
! 231: }
! 232: UIMessage::Post(UIMessage::KEYBOARD, arg);
! 233:
1.1.1.5 root 234: // 送信キューに登録
235: sendqueue.Enqueue(keystat);
236:
237: // 機種依存のキー送信処理
238: SendStart();
1.1.1.6 root 239: return true;
1.1.1.5 root 240: }
241:
1.1 root 242: // キー名を返す (主にデバッグ用)
243: const std::string
1.1.1.6 root 244: Keyboard::GetKeyName(uint keycode)
1.1 root 245: {
1.1.1.5 root 246: if (keycode >= keyname.size()) {
247: return string_format("(keycode=0x%x, out of range)", keycode);
1.1 root 248: }
1.1.1.5 root 249: if (keyname[keycode] == NULL) {
250: return string_format("(keycode=0x%x)", keycode);
1.1 root 251: }
1.1.1.5 root 252: return std::string(keyname[keycode]);
1.1 root 253: }
254:
255: /*static*/ std::vector<const char *>
256: Keyboard::keyname {
257: "(none)", // 00
258: "BREAK", // 01
259: "COPY", // 02
260: "romaji", // 03
261: "code", // 04
262: "kigou", // 05
263: "touroku", // 06
264: "HELP", // 07
265: "HOME", // 08
266: "TAB", // 09
267: "CTRL", // 0a
268: "kana", // 0b
269: "SHIFT_L", // 0c
270: "SHIFT_R", // 0d
271: "CAPS", // 0e
272: "SF", // 0f
273:
274: "ESC", // 10
275: "BS", // 11
276: "enter", // 12
277: "INS", // 13
278: "space", // 14
279: "DEL", // 15
280: "XFER", // 16
281: "VALID", // 17
282: "PF11", // 18
283: "PF12", // 19
284: "PF13", // 1a
285: "PF14", // 1b
286: "up", // 1c
287: "left", // 1d
288: "right", // 1e
289: "down", // 1f
290:
291: "ROLLUP", // 20
292: "ROLLDOWN", // 21
293: "1", // 22
294: "2", // 23
295: "3", // 24
296: "4", // 25
297: "5", // 26
298: "6", // 27
299: "7", // 28
300: "8", // 29
301: "9", // 2a
302: "0", // 2b
303: "minus", // 2c
304: "circum", // 2d
305: "backslash", // 2e
306: "UNDO", // 2f
307:
308: "OPT1", // 30
309: "OPT2", // 31
310: "Q", // 32
311: "W", // 33
312: "E", // 34
313: "R", // 35
314: "T", // 36
315: "Y", // 37
316: "U", // 38
317: "I", // 39
318: "O", // 3a
319: "P", // 3b
320: "at", // 3c
321: "bracketleft", // 3d
322: "hira", // 3e
323: "zenkaku", // 3f
324:
325: NULL, // 40
326: NULL, // 41
327: "A", // 42
328: "S", // 43
329: "D", // 44
330: "F", // 45
331: "G", // 46
332: "H", // 47
333: "J", // 48
334: "K", // 49
335: "L", // 4a
336: "semicolon", // 4b
337: "colon", // 4c
338: "bracketright", // 4d
339: NULL, // 4e
340: NULL, // 4f
341:
342: NULL, // 50
343: NULL, // 51
344: "Z", // 52
345: "X", // 53
346: "C", // 54
347: "V", // 55
348: "B", // 56
349: "N", // 57
350: "M", // 58
351: "comma", // 59
352: "period", // 5a
353: "slash", // 5b
354: "underscore", // 5c
355: NULL, // 5d
356: NULL, // 5e
357: NULL, // 5f
358:
359: NULL, // 60
360: "PAD_plus", // 61
361: "PAD_minus", // 62
362: "PAD_7", // 63
363: "PAD_8", // 64
364: "PAD_9", // 65
365: "PAD_4", // 66
366: "PAD_5", // 67
367: "PAD_6", // 68
368: "PAD_1", // 69
369: "PAD_2", // 6a
370: "PAD_3", // 6b
371: "PAD_0", // 6c
372: "PAD_decimal", // 6d
373: "PAD_enter", // 6e
374: "PAD_CLR", // 6f
375:
376: NULL, // 70
377: NULL, // 71
378: "F1", // 72
379: "F2", // 73
380: "F3", // 74
381: "F4", // 75
382: "F5", // 76
383: "F6", // 77
384: "F7", // 78
385: "F8", // 79
386: "F9", // 7a
387: "F10", // 7b
388: "PAD_multiply", // 7c
389: "PAD_divide", // 7d
390: "PAD_equal", // 7e
391: "PAD_comma", // 7f
392:
393: "XF1", // 80
394: "XF2", // 81
395: "XF3", // 82
396: "XF4", // 83
397: "XF5", // 84
1.1.1.5 root 398: "SHIFT", // 85
1.1 root 399: };
1.1.1.8 ! root 400:
! 401: // 文字対キーストローク表
! 402: // 文字による入力エミュレーションで使用。
! 403: /*static*/ std::vector<uint>
! 404: Keyboard::char_key = {
! 405: 0, // NONE
! 406: KC_A + KM_CTRL, // 0x01 ^A
! 407: KC_B + KM_CTRL, // 0x02 ^B
! 408: KC_C + KM_CTRL, // 0x03 ^C
! 409: KC_D + KM_CTRL, // 0x04 ^D
! 410: KC_E + KM_CTRL, // 0x05 ^E
! 411: KC_F + KM_CTRL, // 0x06 ^F
! 412: KC_G + KM_CTRL, // 0x07 ^G
! 413: KC_BS, // 0x08 ^H
! 414: KC_TAB, // 0x09 ^I
! 415: KC_J + KM_CTRL, // 0x0a ^J
! 416: KC_K + KM_CTRL, // 0x0b ^K
! 417: KC_L + KM_CTRL, // 0x0c ^L
! 418: KC_enter, // 0x0d ^M
! 419: KC_N + KM_CTRL, // 0x0e ^N
! 420: KC_O + KM_CTRL, // 0x0f ^O
! 421: KC_P + KM_CTRL, // 0x10 ^P
! 422: KC_Q + KM_CTRL, // 0x11 ^Q
! 423: KC_R + KM_CTRL, // 0x12 ^R
! 424: KC_S + KM_CTRL, // 0x13 ^S
! 425: KC_T + KM_CTRL, // 0x14 ^T
! 426: KC_U + KM_CTRL, // 0x15 ^U
! 427: KC_V + KM_CTRL, // 0x16 ^V
! 428: KC_W + KM_CTRL, // 0x17 ^W
! 429: KC_X + KM_CTRL, // 0x18 ^X
! 430: KC_Y + KM_CTRL, // 0x19 ^Y
! 431: KC_Z + KM_CTRL, // 0x1a ^Z
! 432:
! 433: KC_ESC, // 0x1b ^[
! 434: KC_backslash + KM_CTRL, // 0x1c ^'\'
! 435: KC_bracketright + KM_CTRL, // 0x1d ^]
! 436: KC_circum + KM_CTRL, // 0x1e ^^
! 437: KC_underscore + KM_CTRL, // 0x1f ^_
! 438:
! 439: KC_space, // 0x20 ' '
! 440: KC_1 + KM_SHIFT, // 0x21 !
! 441: KC_2 + KM_SHIFT, // 0x22 "
! 442: KC_3 + KM_SHIFT, // 0x23 #
! 443: KC_4 + KM_SHIFT, // 0x24 $
! 444: KC_5 + KM_SHIFT, // 0x25 %
! 445: KC_6 + KM_SHIFT, // 0x26 &
! 446: KC_7 + KM_SHIFT, // 0x27 '
! 447: KC_8 + KM_SHIFT, // 0x28 (
! 448: KC_9 + KM_SHIFT, // 0x29 )
! 449: KC_colon + KM_SHIFT, // 0x2a *
! 450: KC_semicolon + KM_SHIFT, // 0x2b +
! 451: KC_comma, // 0x2c ,
! 452: KC_minus, // 0x2d -
! 453: KC_period, // 0x2e .
! 454: KC_slash, // 0x2f /
! 455:
! 456: KC_0, // 0x30 0
! 457: KC_1, // 0x31 1
! 458: KC_2, // 0x32 2
! 459: KC_3, // 0x33 3
! 460: KC_4, // 0x34 4
! 461: KC_5, // 0x35 5
! 462: KC_6, // 0x36 6
! 463: KC_7, // 0x37 7
! 464: KC_8, // 0x38 8
! 465: KC_9, // 0x39 9
! 466: KC_colon, // 0x3a :
! 467: KC_semicolon, // 0x3b ;
! 468: KC_comma + KM_SHIFT, // 0x3c <
! 469: KC_minus + KM_SHIFT, // 0x3d =
! 470: KC_period + KM_SHIFT, // 0x3e >
! 471: KC_slash + KM_SHIFT, // 0x3f ?
! 472:
! 473: KC_at, // 0x40 @
! 474: KC_A + KM_SHIFT, // 0x41 A
! 475: KC_B + KM_SHIFT, // 0x42 B
! 476: KC_C + KM_SHIFT, // 0x43 C
! 477: KC_D + KM_SHIFT, // 0x44 D
! 478: KC_E + KM_SHIFT, // 0x45 E
! 479: KC_F + KM_SHIFT, // 0x46 F
! 480: KC_G + KM_SHIFT, // 0x47 G
! 481: KC_H + KM_SHIFT, // 0x48 H
! 482: KC_I + KM_SHIFT, // 0x49 I
! 483: KC_J + KM_SHIFT, // 0x4a J
! 484: KC_K + KM_SHIFT, // 0x4b K
! 485: KC_L + KM_SHIFT, // 0x4c L
! 486: KC_M + KM_SHIFT, // 0x4d M
! 487: KC_N + KM_SHIFT, // 0x4e N
! 488: KC_O + KM_SHIFT, // 0x4f O
! 489: KC_P + KM_SHIFT, // 0x50 P
! 490: KC_Q + KM_SHIFT, // 0x51 Q
! 491: KC_R + KM_SHIFT, // 0x52 R
! 492: KC_S + KM_SHIFT, // 0x53 S
! 493: KC_T + KM_SHIFT, // 0x54 T
! 494: KC_U + KM_SHIFT, // 0x55 U
! 495: KC_V + KM_SHIFT, // 0x56 V
! 496: KC_W + KM_SHIFT, // 0x57 W
! 497: KC_X + KM_SHIFT, // 0x58 X
! 498: KC_Y + KM_SHIFT, // 0x59 Y
! 499: KC_Z + KM_SHIFT, // 0x5a Z
! 500:
! 501: KC_bracketleft, // 0x5b [
! 502: KC_backslash, // 0x5c '\'
! 503: KC_bracketright, // 0x5d ]
! 504: KC_circum, // 0x5e ^
! 505: KC_underscore + KM_SHIFT, // 0x5f _
! 506:
! 507: KC_at + KM_SHIFT, // 0x60 `
! 508: KC_A, // 0x61 a
! 509: KC_B, // 0x62 b
! 510: KC_C, // 0x63 c
! 511: KC_D, // 0x64 d
! 512: KC_E, // 0x65 e
! 513: KC_F, // 0x66 f
! 514: KC_G, // 0x67 g
! 515: KC_H, // 0x68 h
! 516: KC_I, // 0x69 i
! 517: KC_J, // 0x6a j
! 518: KC_K, // 0x6b k
! 519: KC_L, // 0x6c l
! 520: KC_M, // 0x6d m
! 521: KC_N, // 0x6e n
! 522: KC_O, // 0x6f o
! 523: KC_P, // 0x70 p
! 524: KC_Q, // 0x71 q
! 525: KC_R, // 0x72 r
! 526: KC_S, // 0x73 s
! 527: KC_T, // 0x74 t
! 528: KC_U, // 0x75 u
! 529: KC_V, // 0x76 v
! 530: KC_W, // 0x77 w
! 531: KC_X, // 0x78 x
! 532: KC_Y, // 0x79 y
! 533: KC_Z, // 0x7a z
! 534:
! 535: KC_bracketleft + KM_SHIFT, // 0x7b {
! 536: KC_backslash + KM_SHIFT, // 0x7c |
! 537: KC_bracketright + KM_SHIFT, // 0x7d }
! 538: KC_circum + KM_SHIFT, // 0x7e ~
! 539: KC_none, // 0x7f DEL
! 540:
! 541: KC_at + KM_CTRL, // 0x80 CC_NUL
! 542: KC_F1, // 0x81 CC_F1
! 543: KC_F2, // 0x82 CC_F2
! 544: KC_F3, // 0x83 CC_F3
! 545: KC_F4, // 0x84 CC_F4
! 546: KC_F5, // 0x85 CC_F5
! 547: KC_F6, // 0x86 CC_F6
! 548: KC_F7, // 0x87 CC_F7
! 549: KC_F8, // 0x88 CC_F8
! 550: KC_F9, // 0x89 CC_F9
! 551: KC_F10, // 0x8a CC_Fa
! 552: 0, // 0x8b
! 553: KC_up, // 0x8c CC_up
! 554: KC_left, // 0x8d CC_left
! 555: KC_right, // 0x8e CC_right
! 556: KC_down, // 0x8f CC_down
! 557: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.