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