|
|
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:
130: // キー入力コールバック
131: void
132: Keyboard::KeyInputCallback(Event& ev)
133: {
134:
135: for (;;) {
136: uint keystat;
137: {
138: std::unique_lock<std::mutex> lock(mtx);
139: if (inputqueue.Empty()) {
140: return;
141: }
142: keystat = inputqueue.Dequeue();
143: }
144: KeyInput(keystat);
145: }
146: }
147:
148: // キー入力を1つ処理する
1.1.1.6 root 149: // キーを送信したら true を返す
150: bool
151: Keyboard::KeyInputCommon(uint keystat)
1.1.1.5 root 152: {
153: uint keycode = KC_CODE(keystat);
154: bool ismake = KC_IS_MAKE(keystat);
155:
156: // 対応する機種別キーコードがなければ無視。
157: // (ここでは調べるだけでまだ変換はしない)
158: if (KeyToMDKey(keycode) == 0) {
1.1.1.6 root 159: return false;
1.1.1.5 root 160: }
161:
162: // このキーの状態が変化しないのなら何もしない。
163: if (IsPressed(keycode) == ismake) {
1.1.1.6 root 164: return false;
1.1.1.5 root 165: }
166:
167: // 処理することが確定したのでログ
168: // (離すほうは押すほうよりログレベルを1つ上げてある)
169: if (ismake) {
1.1.1.6 root 170: putlog(1, "MakeKey %d(%s)", keycode, GetKeyName(keycode).c_str());
1.1.1.5 root 171: } else {
1.1.1.6 root 172: putlog(2, "BreakKey %d(%s)", keycode, GetKeyName(keycode).c_str());
1.1.1.5 root 173: }
174:
1.1.1.7 ! root 175: // UI に状態変更を通知
! 176: UIMessage::Post(UIMessage::KEYBOARD);
! 177:
1.1.1.5 root 178: // キーが押されてる間は VM の高速実行を抑制する
179: // すべてのキーが離されたら高速モードを許可する
180: pressed[keycode] = ismake;
181: if (ismake) {
182: if (num_of_pressed++ == 0) {
183: gScheduler->RequestKeyPressed(true);
184: }
185: } else {
186: if (--num_of_pressed == 0) {
187: gScheduler->RequestKeyPressed(false);
188: }
189: }
190:
191: // 送信キューに登録
192: sendqueue.Enqueue(keystat);
193:
194: // 機種依存のキー送信処理
195: SendStart();
1.1.1.6 root 196: return true;
1.1.1.5 root 197: }
198:
1.1 root 199: // キー名を返す (主にデバッグ用)
200: const std::string
1.1.1.6 root 201: Keyboard::GetKeyName(uint keycode)
1.1 root 202: {
1.1.1.5 root 203: if (keycode >= keyname.size()) {
204: return string_format("(keycode=0x%x, out of range)", keycode);
1.1 root 205: }
1.1.1.5 root 206: if (keyname[keycode] == NULL) {
207: return string_format("(keycode=0x%x)", keycode);
1.1 root 208: }
1.1.1.5 root 209: return std::string(keyname[keycode]);
1.1 root 210: }
211:
212: /*static*/ std::vector<const char *>
213: Keyboard::keyname {
214: "(none)", // 00
215: "BREAK", // 01
216: "COPY", // 02
217: "romaji", // 03
218: "code", // 04
219: "kigou", // 05
220: "touroku", // 06
221: "HELP", // 07
222: "HOME", // 08
223: "TAB", // 09
224: "CTRL", // 0a
225: "kana", // 0b
226: "SHIFT_L", // 0c
227: "SHIFT_R", // 0d
228: "CAPS", // 0e
229: "SF", // 0f
230:
231: "ESC", // 10
232: "BS", // 11
233: "enter", // 12
234: "INS", // 13
235: "space", // 14
236: "DEL", // 15
237: "XFER", // 16
238: "VALID", // 17
239: "PF11", // 18
240: "PF12", // 19
241: "PF13", // 1a
242: "PF14", // 1b
243: "up", // 1c
244: "left", // 1d
245: "right", // 1e
246: "down", // 1f
247:
248: "ROLLUP", // 20
249: "ROLLDOWN", // 21
250: "1", // 22
251: "2", // 23
252: "3", // 24
253: "4", // 25
254: "5", // 26
255: "6", // 27
256: "7", // 28
257: "8", // 29
258: "9", // 2a
259: "0", // 2b
260: "minus", // 2c
261: "circum", // 2d
262: "backslash", // 2e
263: "UNDO", // 2f
264:
265: "OPT1", // 30
266: "OPT2", // 31
267: "Q", // 32
268: "W", // 33
269: "E", // 34
270: "R", // 35
271: "T", // 36
272: "Y", // 37
273: "U", // 38
274: "I", // 39
275: "O", // 3a
276: "P", // 3b
277: "at", // 3c
278: "bracketleft", // 3d
279: "hira", // 3e
280: "zenkaku", // 3f
281:
282: NULL, // 40
283: NULL, // 41
284: "A", // 42
285: "S", // 43
286: "D", // 44
287: "F", // 45
288: "G", // 46
289: "H", // 47
290: "J", // 48
291: "K", // 49
292: "L", // 4a
293: "semicolon", // 4b
294: "colon", // 4c
295: "bracketright", // 4d
296: NULL, // 4e
297: NULL, // 4f
298:
299: NULL, // 50
300: NULL, // 51
301: "Z", // 52
302: "X", // 53
303: "C", // 54
304: "V", // 55
305: "B", // 56
306: "N", // 57
307: "M", // 58
308: "comma", // 59
309: "period", // 5a
310: "slash", // 5b
311: "underscore", // 5c
312: NULL, // 5d
313: NULL, // 5e
314: NULL, // 5f
315:
316: NULL, // 60
317: "PAD_plus", // 61
318: "PAD_minus", // 62
319: "PAD_7", // 63
320: "PAD_8", // 64
321: "PAD_9", // 65
322: "PAD_4", // 66
323: "PAD_5", // 67
324: "PAD_6", // 68
325: "PAD_1", // 69
326: "PAD_2", // 6a
327: "PAD_3", // 6b
328: "PAD_0", // 6c
329: "PAD_decimal", // 6d
330: "PAD_enter", // 6e
331: "PAD_CLR", // 6f
332:
333: NULL, // 70
334: NULL, // 71
335: "F1", // 72
336: "F2", // 73
337: "F3", // 74
338: "F4", // 75
339: "F5", // 76
340: "F6", // 77
341: "F7", // 78
342: "F8", // 79
343: "F9", // 7a
344: "F10", // 7b
345: "PAD_multiply", // 7c
346: "PAD_divide", // 7d
347: "PAD_equal", // 7e
348: "PAD_comma", // 7f
349:
350: "XF1", // 80
351: "XF2", // 81
352: "XF3", // 82
353: "XF4", // 83
354: "XF5", // 84
1.1.1.5 root 355: "SHIFT", // 85
1.1 root 356: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.