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