|
|
1.1 root 1: //
2: // nono
1.1.1.4 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.2 root 7: // キーボードとマウス
8: //
9: // キーボード/マウスからシリアルポートへの入力は、1バイトの送信自体に約1msec
10: // (9600bps、スタートビット1、ストップビット1) で送信後に 1msec 空けるそう
11: // なので、ここでは単純に 2msec を1スロットとする。
12: // マウスは 20msec ごとにサンプリングして連続する3バイトを送り、
13: // キーボードは任意のスロットで 1バイト送ってくるので、概念的には
14: // 10スロットのうち 0, 1, 2 をマウスが、3..9 をキーボードが使用する
15: // ということにする。
16:
1.1 root 17: #include "lunakbd.h"
18: #include "sio.h"
19:
20: // コンストラクタ
21: LunaKeyboard::LunaKeyboard()
22: {
1.1.1.4 root 23: led.resize(2);
1.1 root 24:
1.1.1.2 root 25: key_event.func = (DeviceCallback_t)&LunaKeyboard::KeyboardCallback;
26: key_event.time = 0;
1.1.1.9 root 27: key_event.SetName("Keyboard Slot");
1.1.1.2 root 28:
29: // マウスイベント#0 はサンプリング用も兼ねているので常にオン
30: mouse_event[0].dev = this;
31: mouse_event[0].func =
32: (DeviceCallback_t)&LunaKeyboard::MouseSamplingCallback;
33: mouse_event[0].time = 20_msec;
34: mouse_event[0].SetName("Mouse Sampling");
35:
36: for (int i = 1; i < 3; i++) {
37: mouse_event[i].dev = this;
38: mouse_event[i].func = (DeviceCallback_t)&LunaKeyboard::MouseCallback;
1.1.1.6 root 39: mouse_event[i].time = 2_msec * i;
1.1.1.2 root 40: mouse_event[i].SetName(string_format("Mouse Slot%d", i));
41: }
1.1.1.10! root 42:
! 43: monitor.func = (MonitorCallback_t)&LunaKeyboard::MonitorUpdate;
! 44: monitor.SetSize(30, 1);
! 45: monitor.Regist(ID_MONITOR_KEYBOARD);
1.1 root 46: }
47:
48: // デストラクタ
49: LunaKeyboard::~LunaKeyboard()
50: {
51: }
52:
1.1.1.9 root 53: // 電源オン
54: bool
55: LunaKeyboard::PowerOn()
56: {
57: // XXX リセットはマウスには届かないような気がするので
58: // 電源オンで初期化。
59: mouse_x = 0;
60: mouse_y = 0;
61: mouse_r = false;
62: mouse_l = false;
63: mouse_m = false;
64: prev_r = false;
65: prev_l = false;
66: prev_m = false;
67:
68: return true;
69: }
70:
1.1.1.2 root 71: // リセット
72: void
73: LunaKeyboard::ResetHard()
74: {
1.1.1.9 root 75: inherited::ResetHard();
76:
1.1.1.6 root 77: // 動作中のタイマーは一旦とめる
78: key_event.Stop();
79: for (int i = 0; i < countof(mouse_event); i++) {
80: mouse_event[i].Stop();
81: }
82:
83: // で、マウスサンプリングは電源オン時から有効らしい
1.1.1.3 root 84: mouse_event[0].Start();
1.1.1.2 root 85: }
86:
87: // モニター
1.1.1.5 root 88: void
1.1.1.10! root 89: LunaKeyboard::MonitorUpdate(Monitor *, TextScreen& screen)
1.1.1.2 root 90: {
91: int x, y;
1.1.1.10! root 92: screen.Clear();
1.1.1.2 root 93:
94: x = mouse_x;
95: y = mouse_y;
96: if (x < -999)
97: x = -999;
98: if (x > 999)
99: x = 999;
100: if (y < -999)
101: y = -999;
102: if (y > 999)
103: y = 999;
1.1.1.10! root 104: screen.Print(0, 0, "Mouse: x=%-4d y=%-4d %c %c %c",
1.1.1.2 root 105: x, y,
106: (mouse_l ? 'L' : '-'),
107: (mouse_m ? 'M' : '-'),
108: (mouse_r ? 'R' : '-'));
109: }
110:
1.1.1.9 root 111: // 共通キーコードを LUNA キーコードに変換して返す。
112: // 対応する LUNA のキーがなければ 0 を返す。
113: uint
114: LunaKeyboard::KeyToMDKey(uint keycode) const
1.1.1.4 root 115: {
1.1.1.9 root 116: if (keycode >= countof(keycode2lunakey_table)) {
1.1.1.4 root 117: return 0;
118: }
119:
1.1.1.9 root 120: return keycode2lunakey_table[keycode];
1.1 root 121: }
122:
1.1.1.9 root 123: // シフト状態なら true を返す
124: bool
125: LunaKeyboard::IsShift() const
1.1 root 126: {
1.1.1.9 root 127: return pressed[KC_SHIFT_L] || pressed[KC_SHIFT_R];
1.1.1.4 root 128: }
129:
1.1.1.10! root 130: // キー入力を1つ処理する
! 131: void
! 132: LunaKeyboard::KeyInput(uint keystat)
1.1.1.4 root 133: {
1.1.1.10! root 134: uint keycode = KC_CODE(keystat);
! 135: bool ismake = KC_IS_MAKE(keystat);
! 136:
! 137: assert(keycode < KC_max);
! 138:
! 139: // 共通処理へ
! 140: if (KeyInputCommon(keystat) == false) {
! 141: return;
! 142: }
! 143:
! 144: // LED キーの処理
! 145: if (ismake) {
1.1.1.9 root 146: // LED はトグル?
147: int n = Keycode2LED(keycode);
148: if (n >= 0) {
149: led[n] = !led[n];
150: }
151: }
1.1 root 152:
1.1.1.10! root 153: // LUNA のキーボードはキーボード側でキーリピートしないので
! 154: // ここでは何もしなくてよい
1.1.1.2 root 155: }
156:
1.1.1.9 root 157: // 共通キーコードから LED 番号を返す。LED キーでなければ -1 を返す。
158: int
159: LunaKeyboard::Keycode2LED(uint keycode) const
1.1.1.2 root 160: {
1.1.1.9 root 161: // 2つしかないのでこのくらいでいいか
162: if (keycode == KC_kana)
163: return 0;
164: if (keycode == KC_CAPS)
165: return 1;
1.1.1.2 root 166:
1.1.1.9 root 167: return -1;
1.1.1.2 root 168: }
169:
170: // マウス入力
171: void
172: LunaKeyboard::MouseInput(int x, int y, bool rb, bool lb, bool mb)
173: {
174: // ホストからマウスイベントのたびに呼ばれるので、ここで積算。
175: // 20msec ごとのサンプリングのタイミングで積算結果を処理する。
176: // LUNA のマウスの Y 方向は上に動く方向がプラスなので、ホスト入力とは逆。
177: mouse_x += x;
178: mouse_y -= y;
179: mouse_r = rb;
180: mouse_l = lb;
181: mouse_m = mb;
182: }
183:
1.1.1.9 root 184: // キーイベントを起こせる空きスロットを計算して、イベントを開始
185: void
186: LunaKeyboard::SendStart()
187: {
188: // 例えば、スロット 4 期間内のどこかでキー入力(K)が起きると
189: // スロット 5 の先頭時刻にイベント(E)を起こす。
190: //
191: // 通算スロット 44 45 46
192: // スロット番号 4 5 6
193: // +-----+-----+-----+..
194: // | K--->E
195: // +-----+-----+-----+..
196: //
197: // スロット 9 期間内のどこかでキー入力(K)が起きると、(0, 1, 2 はマウス用
198: // のスロットにしているので、これを飛ばして) スロット 3 の先頭でイベント
199: // (E)を起こす。
200: //
201: // 通算スロット 209 210 211 212 213
202: // スロット番号 9 0 1 2 3
203: // +-----+-----+-----+-----+-----+..
204: // | K------------------>E
205: // +-----+-----+-----+-----+-----+..
206:
207: // イベントが進行中ならなにもせず帰る
208: if (key_event.IsRunning()) {
209: return;
210: }
211:
212: uint64 now = gMPU->GetVirtTime();
213:
214: // t は現在の仮想時刻に対応する通算スロット
215: uint64 t = now / 2_msec;
216: // n はその次の通算スロット
217: uint64 n = t + 1;
218:
219: // マウススロットなら避ける
220: while (n % 10 < 3) {
221: n++;
222: }
223:
224: // n の時刻がイベントを起こす時刻
225: uint64 target = n * 2_msec;
226:
227: // イベントを登録。
228: key_event.time = target - now;
229: key_event.Start();
230: }
231:
1.1.1.2 root 232: // キー入力コールバック
233: // SIO に送信するタイミングで呼ばれる。
234: void
1.1.1.6 root 235: LunaKeyboard::KeyboardCallback(Event& ev)
1.1.1.2 root 236: {
1.1.1.9 root 237: // 呼ばれたということはキューは空でないはず
238: assert(sendqueue.Empty() == false);
239:
240: // キューから取り出す。ここで取り出せるのは keystat
241: uint keystat = sendqueue.Dequeue();
242: uint keycode = KC_CODE(keystat);
243: // ここで LUNA キーコードに変換 (変換できないはずはない)
244: uint mdkey = KeyToMDKey(keycode);
245: assert(mdkey != 0);
1.1.1.6 root 246:
1.1.1.9 root 247: // 送信
248: if (KC_IS_BREAK(keystat)) {
249: mdkey |= 0x80;
250: }
251: gSIO->Rx(1, mdkey);
252:
253: if (Keycode2LED(keystat) >= 0) {
254: // かなと CAP キーの押下なら、続けてキーの復旧も送る。
255: // 押下の時だけここに来ればいいので keycode じゃなく keystat。
1.1.1.4 root 256:
1.1.1.9 root 257: // (BreakKey() が内部でもう一度イベントをキックしている)
258: BreakKey(keycode);
259: }
260:
261: // キューにまだあるならここで次のイベントをキックする必要がある
262: if (sendqueue.Empty() == false) {
263: SendStart();
1.1.1.4 root 264: }
1.1.1.2 root 265: }
266:
267: // マウスサンプリングコールバック
268: // 20msec ごとに呼ばれる。
269: void
1.1.1.6 root 270: LunaKeyboard::MouseSamplingCallback(Event& ev)
1.1.1.2 root 271: {
272: int reset_x = 0;
273: int reset_y = 0;
274:
275: // 移動量±1は送信しないが次回に向けた積算は続けるそうだ。
276: if (mouse_x == -1 || mouse_x == 1) {
277: reset_x = mouse_x;
278: mouse_x = 0;
279: }
280: if (mouse_y == -1 || mouse_y == 1) {
281: reset_y = mouse_y;
282: mouse_y = 0;
283: }
284:
285: // 移動量が±1 を超えるかボタンがどれかでも変化すれば送信。
286: if (mouse_x != 0 ||
287: mouse_y != 0 ||
288: mouse_r != prev_r ||
289: mouse_l != prev_l ||
290: mouse_m != prev_m)
291: {
1.1.1.3 root 292: putlog(2, "Mouse Sampling x=%d y=%d %c%c%c",
293: mouse_x, mouse_y,
294: (mouse_r ? 'L' : '-'),
295: (mouse_m ? 'M' : '-'),
296: (mouse_l ? 'R' : '-'));
297:
1.1.1.2 root 298: // 1バイト目、ボタン状態。
299: // mouse_[rlm] 変数は押し下げが true、LUNA のマウスは押し下げが %0
300: uint8 b = 0x80 |
301: (mouse_l ? 0 : 0x04) |
302: (mouse_m ? 0 : 0x02) |
303: (mouse_r ? 0 : 0x01);
304:
305: // 2バイト目が X、3バイト目が Y。
306: // 移動量は絶対値 0x7f でサチる (signed char ではない)。
307: if (mouse_x < -127)
308: mouse_x = -127;
309: if (mouse_x > 127)
310: mouse_x = 127;
311:
312: if (mouse_y < -127)
313: mouse_y = -127;
314: if (mouse_y > 127)
315: mouse_y = 127;
316:
317: // 1バイト目はここで送信
318: gSIO->Rx(1, b);
319:
1.1.1.6 root 320: // 2バイト目は 2msec 秒後に送信
1.1.1.2 root 321: mouse_event[1].code = (uint8)mouse_x;
1.1.1.3 root 322: mouse_event[1].Start();
1.1.1.2 root 323:
1.1.1.6 root 324: // 3バイト目は 4msec 秒後に送信
1.1.1.2 root 325: mouse_event[2].code = (uint8)mouse_y;
1.1.1.3 root 326: mouse_event[2].Start();
327: } else {
328: putlog(3, "Mouse Sampling inactive");
1.1.1.2 root 329: }
330:
331: // 積算リセット
332: mouse_x = reset_x;
333: mouse_y = reset_y;
334: prev_r = mouse_r;
335: prev_l = mouse_l;
336: prev_m = mouse_m;
337:
338: // 20msec 後に再び自分を呼び出す
1.1.1.6 root 339: ev.Start();
1.1.1.2 root 340: }
341:
342: // マウスの後続バイトを送出するイベントコールバック
343: void
1.1.1.6 root 344: LunaKeyboard::MouseCallback(Event& ev)
1.1.1.2 root 345: {
1.1.1.6 root 346: gSIO->Rx(1, ev.code);
1.1 root 347: }
348:
1.1.1.4 root 349: // ホストからの制御
350: void
351: LunaKeyboard::Command(uint32 data)
352: {
353: }
354:
1.1 root 355:
356: // キーコード変換表
357: // 共通キーコードから LUNA キーコードに変換する。
358: // ぶっちゃけた話共通キーコードは LUNA キーコードをベースにしているので、
359: // 穴がある以外は全部透過。
360: /*static*/ const uint
1.1.1.9 root 361: LunaKeyboard::keycode2lunakey_table[0x80] = {
1.1 root 362: NoKey, // [00]
363: NoKey, // [01]
364: NoKey, // [02]
365: NoKey, // [03]
366: NoKey, // [04]
367: NoKey, // [05]
368: NoKey, // [06]
369: NoKey, // [07]
370: NoKey, // [08]
371: LunaKey_TAB, // [09]
372: LunaKey_CTRL, // [0a]
373: LunaKey_kana, // [0b]
374: LunaKey_SHIFT_L, // [0c]
375: LunaKey_SHIFT_R, // [0d]
376: LunaKey_CAPS, // [0e]
377: LunaKey_SF, // [0f]
378:
379: LunaKey_ESC, // [10]
380: LunaKey_BS, // [11]
381: LunaKey_enter, // [12]
382: NoKey, // [13]
383: LunaKey_space, // [14]
384: LunaKey_DEL, // [15]
385: LunaKey_XFER, // [16]
386: LunaKey_VALID, // [17]
387: LunaKey_PF11, // [18]
388: LunaKey_PF12, // [19]
389: LunaKey_PF13, // [1a]
390: LunaKey_PF14, // [1b]
391: LunaKey_up, // [1c]
392: LunaKey_left, // [1d]
393: LunaKey_right, // [1e]
394: LunaKey_down, // [1f]
395:
396: NoKey, // [20]
397: NoKey, // [21]
398: LunaKey_1, // [22]
399: LunaKey_2, // [23]
400: LunaKey_3, // [24]
401: LunaKey_4, // [25]
402: LunaKey_5, // [26]
403: LunaKey_6, // [27]
404: LunaKey_7, // [28]
405: LunaKey_8, // [29]
406: LunaKey_9, // [2a]
407: LunaKey_0, // [2b]
408: LunaKey_minus, // [2c]
409: LunaKey_circum, // [2d]
410: LunaKey_backslash, // [2e]
411: NoKey, // [2f]
412:
413: NoKey, // [30]
414: NoKey, // [31]
415: LunaKey_Q, // [32]
416: LunaKey_W, // [33]
417: LunaKey_E, // [34]
418: LunaKey_R, // [35]
419: LunaKey_T, // [36]
420: LunaKey_Y, // [37]
421: LunaKey_U, // [38]
422: LunaKey_I, // [39]
423: LunaKey_O, // [3a]
424: LunaKey_P, // [3b]
425: LunaKey_at, // [3c]
426: LunaKey_bracketleft, // [3d]
427: NoKey, // [3e]
428: NoKey, // [3f]
429:
430: NoKey, // [40]
431: NoKey, // [41]
432: LunaKey_A, // [42]
433: LunaKey_S, // [43]
434: LunaKey_D, // [44]
435: LunaKey_F, // [45]
436: LunaKey_G, // [46]
437: LunaKey_H, // [47]
438: LunaKey_J, // [48]
439: LunaKey_K, // [49]
440: LunaKey_L, // [4a]
441: LunaKey_semicolon, // [4b]
442: LunaKey_colon, // [4c]
443: LunaKey_bracketright, // [4d]
444: NoKey, // [4e]
445: NoKey, // [4f]
446:
447: NoKey, // [50]
448: NoKey, // [51]
449: LunaKey_Z, // [52]
450: LunaKey_X, // [53]
451: LunaKey_C, // [54]
452: LunaKey_V, // [55]
453: LunaKey_B, // [56]
454: LunaKey_N, // [57]
455: LunaKey_M, // [58]
456: LunaKey_comma, // [59]
457: LunaKey_period, // [5a]
458: LunaKey_slash, // [5b]
459: LunaKey_underscore, // [5c]
460: NoKey, // [5d]
461: NoKey, // [5e]
462: NoKey, // [5f]
463:
464: NoKey, // [60]
465: LunaKey_PAD_plus, // [61]
466: LunaKey_PAD_minus, // [62]
467: LunaKey_PAD_7, // [63]
468: LunaKey_PAD_8, // [64]
469: LunaKey_PAD_9, // [65]
470: LunaKey_PAD_4, // [66]
471: LunaKey_PAD_5, // [67]
472: LunaKey_PAD_6, // [68]
473: LunaKey_PAD_1, // [69]
474: LunaKey_PAD_2, // [6a]
475: LunaKey_PAD_3, // [6b]
476: LunaKey_PAD_0, // [6c]
477: LunaKey_PAD_decimal, // [6d]
478: LunaKey_PAD_enter, // [6e]
479: NoKey, // [6f]
480:
481: NoKey, // [70]
482: NoKey, // [71]
483: LunaKey_F1, // [72]
484: LunaKey_F2, // [73]
485: LunaKey_F3, // [74]
486: LunaKey_F4, // [75]
487: LunaKey_F5, // [76]
488: LunaKey_F6, // [77]
489: LunaKey_F7, // [78]
490: LunaKey_F8, // [79]
491: LunaKey_F9, // [7a]
492: LunaKey_F10, // [7b]
493: LunaKey_PAD_multiply, // [7c]
494: LunaKey_PAD_divide, // [7d]
495: LunaKey_PAD_equal, // [7e]
496: LunaKey_PAD_comma, // [7f]
497: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.