|
|
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: //
1.1.1.13 root 8: // LUNA のキーボードとマウス
9: //
10:
1.1.1.2 root 11: // キーボード/マウスからシリアルポートへの入力は、1バイトの送信自体に約1msec
12: // (9600bps、スタートビット1、ストップビット1) で送信後に 1msec 空けるそう
13: // なので、ここでは単純に 2msec を1スロットとする。
1.1.1.14 root 14: //
15: // ブザー発声はキー入力とは本来あまり関係ないが、ブザー発声中もキー入力中と
16: // 同様に高速走行を抑制したいため、キー扱いとしていることに留意。
1.1.1.2 root 17:
1.1 root 18: #include "lunakbd.h"
1.1.1.17 root 19: #include "event.h"
1.1.1.16 root 20: #include "monitor.h"
1.1.1.13 root 21: #include "scheduler.h"
1.1 root 22: #include "sio.h"
1.1.1.12 root 23: #include "uimessage.h"
1.1 root 24:
25: // コンストラクタ
26: LunaKeyboard::LunaKeyboard()
27: {
1.1.1.4 root 28: led.resize(2);
1.1 root 29:
1.1.1.16 root 30: monitor = gMonitorManager->Regist(ID_MONITOR_KEYBOARD, this);
1.1.1.18! root 31: monitor->SetCallback(&LunaKeyboard::MonitorScreen);
1.1.1.16 root 32: monitor->SetSize(40, 4);
1.1 root 33: }
34:
35: // デストラクタ
36: LunaKeyboard::~LunaKeyboard()
37: {
38: }
39:
1.1.1.14 root 40: // 初期化
41: bool
42: LunaKeyboard::Init()
43: {
44: if (inherited::Init() == false) {
45: return false;
46: }
47:
1.1.1.17 root 48: // LUNA-88K は左 [SHIFT] が 0x0d、右 [SHIFT] が 0x0c。
49: if (gMainApp.IsLUNA88K()) {
50: keycode2lunakey_table[0x0c] = LunaKey_SHIFT_R;
51: keycode2lunakey_table[0x0d] = LunaKey_SHIFT_L;
52: }
53:
1.1.1.14 root 54: sio = GetSIODevice();
55:
1.1.1.17 root 56: auto evman = GetEventManager();
57: event = evman->Regist(this,
58: ToEventCallback(&LunaKeyboard::Callback),
59: "Keyboard/Mouse");
1.1.1.15 root 60:
1.1.1.14 root 61: return true;
62: }
63:
1.1.1.13 root 64: // キーボード接続時の MD 固有処理
65: void
66: LunaKeyboard::MDConnect()
1.1.1.9 root 67: {
68: // XXX リセットはマウスには届かないような気がするので
1.1.1.13 root 69: // 接続時(電源オン)で初期化。
1.1.1.9 root 70: mouse_x = 0;
71: mouse_y = 0;
72: mouse_r = false;
73: mouse_l = false;
74: mouse_m = false;
75: prev_r = false;
76: prev_l = false;
77: prev_m = false;
78:
1.1.1.13 root 79: // ブザー停止状態で初期化すると定義されている
1.1.1.14 root 80: BreakKey(KC_buzzer);
1.1.1.12 root 81:
1.1.1.13 root 82: // デフォルトでマウス有効
83: MouseOn();
1.1.1.9 root 84: }
85:
1.1.1.13 root 86: // キーボード取り外し時の MD 固有処理
1.1.1.2 root 87: void
1.1.1.13 root 88: LunaKeyboard::MDDisconnect()
1.1.1.2 root 89: {
1.1.1.13 root 90: // 動作中のタイマーは停止
1.1.1.14 root 91: scheduler->StopEvent(event);
1.1.1.9 root 92:
1.1.1.13 root 93: // 電源が切れるのでブザー停止
1.1.1.14 root 94: BreakKey(KC_buzzer);
1.1.1.6 root 95:
1.1.1.13 root 96: // マウスサンプリングは停止
97: MouseOff();
1.1.1.2 root 98: }
99:
100: // モニター
1.1.1.5 root 101: void
1.1.1.18! root 102: LunaKeyboard::MonitorScreen(Monitor *, TextScreen& screen)
1.1.1.2 root 103: {
1.1.1.12 root 104: int x;
105: int y;
106: int mx;
107: int my;
108:
1.1.1.10 root 109: screen.Clear();
1.1.1.12 root 110: y = 0;
1.1.1.2 root 111:
1.1.1.12 root 112: // 0123456789012345678901234567890123456789
113: // LED: CAP Kana
114: // Buzzer: 150msec 6000Hz
115: // Mouse: sampling=off x=-999 y=-999 L M R
116: x = 8;
117: screen.Puts(0, y, "LED:");
118: screen.Puts(x, y, TA::OnOff(led[1]), "CAP");
119: screen.Puts(x + 4, y, TA::OnOff(led[0]), "Kana");
120: y++;
121: screen.Puts(0, y, "Buzzer:");
1.1.1.14 root 122: screen.Print(x, y, IsPressed(KC_buzzer) ? TA::Normal : TA::Disable,
1.1.1.12 root 123: "%umsec %uHz", buzzer_msec, buzzer_freq);
124: y++;
125:
126: // マウスとの間を1行開ける
127: y++;
128:
129: mx = mouse_x;
130: my = mouse_y;
131: if (mx < -999)
132: mx = -999;
133: if (mx > 999)
134: mx = 999;
135: if (my < -999)
136: my = -999;
137: if (my > 999)
138: my = 999;
139:
140: screen.Puts(0, y, "Mouse:");
141: screen.Print(x, y, "sampling=%s", mouse_on ? "on" : "off");
142: screen.Print(x + 13, y, (mouse_on ? TA::Normal : TA::Disable),
143: "x=%-4d y=%-4d", mx, my);
144: if (mouse_on) {
145: screen.Puts(x + 27, y, TA::OnOff(mouse_l), "L");
146: screen.Puts(x + 29, y, TA::OnOff(mouse_m), "M");
147: screen.Puts(x + 31, y, TA::OnOff(mouse_r), "R");
148: } else {
149: screen.Puts(x + 27, y, TA::Disable, "L M R");
150: }
1.1.1.2 root 151: }
152:
1.1.1.12 root 153: // 共通 keystat を LUNA キーコードに変換して返す。
1.1.1.9 root 154: // 対応する LUNA のキーがなければ 0 を返す。
155: uint
1.1.1.12 root 156: LunaKeyboard::KeyToMDKey(uint keystat) const
1.1.1.4 root 157: {
1.1.1.12 root 158: uint keycode = KC_CODE(keystat);
1.1.1.9 root 159: if (keycode >= countof(keycode2lunakey_table)) {
1.1.1.4 root 160: return 0;
161: }
162:
1.1.1.12 root 163: uint mdkey = keycode2lunakey_table[keycode];
164: if (mdkey && KC_IS_BREAK(keystat)) {
165: mdkey |= 0x80;
166: }
167: return mdkey;
1.1 root 168: }
169:
1.1.1.9 root 170: // シフト状態なら true を返す
171: bool
172: LunaKeyboard::IsShift() const
1.1 root 173: {
1.1.1.9 root 174: return pressed[KC_SHIFT_L] || pressed[KC_SHIFT_R];
1.1.1.4 root 175: }
176:
1.1.1.10 root 177: // キー入力を1つ処理する
178: void
179: LunaKeyboard::KeyInput(uint keystat)
1.1.1.4 root 180: {
1.1.1.10 root 181: uint keycode = KC_CODE(keystat);
182: bool ismake = KC_IS_MAKE(keystat);
183:
184: assert(keycode < KC_max);
185:
1.1.1.12 root 186: // LUNA キーボードの LED キーは、
187: // LED 消灯中の押下で Make、点灯中の押下で Break を送り、
188: // LED キーの開放は VM に対しては何もしない。Issue#83
189: //
190: // LED ismake keystat 送信コード
191: // ------------ -------------
192: // off MAKE : MAKE MAKE キー押下、Make を入力 (LED を on)
193: // on MAKE : MAKE BREAK キー押下、Break を入力 (LED を off)
194: // off BREAK : BREAK none キー開放
195: // on BREAK : BREAK none キー開放
196: int n = Keycode2LED(keycode);
197: if (n >= 0) {
198: if (ismake) {
199: keystat |= (led[n] ? KC_FLAG_LED_BREAK : KC_FLAG_LED_MAKE);
200: } else {
201: // Break 時はキー入力はしない (キーの開放処理自体は必要)
202: keystat |= KC_FLAG_LED_DROP;
203: }
204: }
205:
1.1.1.10 root 206: // 共通処理へ
207: if (KeyInputCommon(keystat) == false) {
208: return;
209: }
210:
1.1.1.12 root 211: // 処理できたらここで LED の状態を更新
212: if (n >= 0 && ismake) {
213: led[n] = !led[n];
214:
215: // LED 状態が変更になったので全走査指示。
216: // KeyInputCommon() 内で一度ポストしてるので無駄気味だが仕方ない…
1.1.1.17 root 217: uimessage->Post(UIMessage::KEYBOARD);
1.1.1.9 root 218: }
1.1 root 219:
1.1.1.10 root 220: // LUNA のキーボードはキーボード側でキーリピートしないので
221: // ここでは何もしなくてよい
1.1.1.2 root 222: }
223:
1.1.1.9 root 224: // 共通キーコードから LED 番号を返す。LED キーでなければ -1 を返す。
225: int
226: LunaKeyboard::Keycode2LED(uint keycode) const
1.1.1.2 root 227: {
1.1.1.9 root 228: // 2つしかないのでこのくらいでいいか
229: if (keycode == KC_kana)
230: return 0;
231: if (keycode == KC_CAPS)
232: return 1;
1.1.1.2 root 233:
1.1.1.9 root 234: return -1;
1.1.1.2 root 235: }
236:
1.1.1.12 root 237: // LED 番号から共通キーコードを返す。LED 番号が不正なら KC_none を返す。
238: uint
239: LunaKeyboard::LED2Keycode(int ledcode) const
240: {
241: // 2つしかないのでこのくらいでいいか
242: if (ledcode == 0)
243: return KC_kana;
244: if (ledcode == 1)
245: return KC_CAPS;
246:
247: return KC_none;
248: }
249:
1.1.1.2 root 250: // マウス入力
251: void
252: LunaKeyboard::MouseInput(int x, int y, bool rb, bool lb, bool mb)
253: {
254: // ホストからマウスイベントのたびに呼ばれるので、ここで積算。
255: // 20msec ごとのサンプリングのタイミングで積算結果を処理する。
256: // LUNA のマウスの Y 方向は上に動く方向がプラスなので、ホスト入力とは逆。
1.1.1.12 root 257: if (mouse_on) {
258: mouse_x += x;
259: mouse_y -= y;
260: mouse_r = rb;
261: mouse_l = lb;
262: mouse_m = mb;
263: }
1.1.1.2 root 264: }
265:
1.1.1.12 root 266: // キー送信を開始 (Keyboard から呼ばれる)
1.1.1.9 root 267: void
268: LunaKeyboard::SendStart()
269: {
1.1.1.12 root 270: // ここでは次の 2msec 単位にイベントコールバックを起動するだけ。
271: Start(2_msec);
272: }
1.1.1.9 root 273:
1.1.1.12 root 274: // イベントを次の period nsec 単位後に開始。
275: void
276: LunaKeyboard::Start(uint64 period)
277: {
1.1.1.14 root 278: uint64 now = scheduler->GetVirtTime();
1.1.1.12 root 279: uint64 target = ((now / period) + 1) * period;
280: // XXX メンバの event.vtime 参照してるけどどうするかね
1.1.1.17 root 281: if (event->IsRunning() == false || target < event->vtime) {
282: event->time = target - now;
1.1.1.14 root 283: scheduler->RestartEvent(event);
1.1.1.9 root 284: }
285: }
286:
1.1.1.12 root 287: // イベントコールバック。
288: // キー入力、マウス入力、ブザー出力のいずれかが発生している間 2msec ごとに
289: // 呼ばれる。
1.1.1.2 root 290: void
1.1.1.17 root 291: LunaKeyboard::Callback(Event *ev)
1.1.1.2 root 292: {
1.1.1.14 root 293: uint64 now = scheduler->GetVirtTime();
1.1.1.12 root 294: uint mdkey;
1.1.1.6 root 295:
1.1.1.12 root 296: // マウスサンプリングオンなら 20msec ごとにサンプリングを行う。
297: if (mouse_on && (now / 2_msec) % 10 == 0) {
298: MouseSampling();
1.1.1.9 root 299: }
300:
1.1.1.13 root 301: // キューから取り出す。ここで取り出せるのは MDKey またはマウスデータ
1.1.1.12 root 302: if (sendqueue.Dequeue(&mdkey)) {
303: // 送信
1.1.1.14 root 304: sio->Rx(1, mdkey);
1.1.1.12 root 305: }
1.1.1.4 root 306:
1.1.1.14 root 307: if (IsPressed(KC_buzzer) && now > buzzer_end) {
308: // VM スレッド内なので BreakKey() ではなくこっちを直接呼ぶ
309: KeyInput(KC_buzzer | KC_FLAG_BREAK);
1.1.1.9 root 310: }
311:
1.1.1.14 root 312: if (sendqueue.Empty() == false || IsPressed(KC_buzzer)) {
1.1.1.12 root 313: // 送信データが残っていれば次は 2msec 後
314: Start(2_msec);
315: } else if (mouse_on) {
316: // そうでなくて、マウスサンプリングがオンなら次の 20msec 単位後
317: Start(20_msec);
1.1.1.4 root 318: }
1.1.1.2 root 319: }
320:
1.1.1.12 root 321: // マウスサンプリング
1.1.1.2 root 322: void
1.1.1.12 root 323: LunaKeyboard::MouseSampling()
1.1.1.2 root 324: {
325: // 移動量が±1 を超えるかボタンがどれかでも変化すれば送信。
1.1.1.11 root 326: if (mouse_x < -1 || mouse_x > 1 ||
327: mouse_y < -1 || mouse_y > 1 ||
1.1.1.2 root 328: mouse_r != prev_r ||
329: mouse_l != prev_l ||
330: mouse_m != prev_m)
331: {
1.1.1.12 root 332: putlog(2, "Mouse Sampling x=%d y=%d %c%c%c",
333: mouse_x, mouse_y,
334: (mouse_l ? 'L' : '-'),
335: (mouse_m ? 'M' : '-'),
336: (mouse_r ? 'R' : '-'));
337:
338: // 1バイト目、ボタン状態。
339: // mouse_[lmr] 変数は押し下げが true、LUNA のマウスは押し下げが %0
340: uint8 b = 0x80 |
341: (mouse_l ? 0 : 0x04) |
342: (mouse_m ? 0 : 0x02) |
343: (mouse_r ? 0 : 0x01);
344:
345: // 2バイト目が X、3バイト目が Y。
346: // 移動量は絶対値 0x7f でサチる (signed char ではない)。
347: if (mouse_x < -127)
348: mouse_x = -127;
349: if (mouse_x > 127)
350: mouse_x = 127;
351:
352: if (mouse_y < -127)
353: mouse_y = -127;
354: if (mouse_y > 127)
355: mouse_y = 127;
356:
357: // キューに入れておく
358: sendqueue.Enqueue(b);
359: sendqueue.Enqueue((uint8)mouse_x);
360: sendqueue.Enqueue((uint8)mouse_y);
361:
362: // 積算リセット
363: mouse_x = 0;
364: mouse_y = 0;
365: prev_r = mouse_r;
366: prev_l = mouse_l;
367: prev_m = mouse_m;
1.1.1.2 root 368: }
1.1.1.11 root 369: }
370:
1.1.1.12 root 371: // マウスサンプリングを ON にする
1.1.1.11 root 372: void
1.1.1.12 root 373: LunaKeyboard::MouseOn()
1.1.1.11 root 374: {
1.1.1.12 root 375: // 厳密なタイミングは分からないので、
376: // 実装都合で次のマウススロットからサンプリングを開始(再開)する。
377: mouse_on = true;
1.1.1.2 root 378:
1.1.1.12 root 379: // 次の 20msec 単位後から開始
380: Start(20_msec);
381: }
382:
383: // マウスサンプリングを OFF にする
384: void
385: LunaKeyboard::MouseOff()
386: {
387: mouse_on = false;
1.1.1.11 root 388: mouse_x = 0;
389: mouse_y = 0;
1.1.1.12 root 390: mouse_r = false;
391: mouse_l = false;
392: mouse_m = false;
393: prev_r = false;
394: prev_l = false;
395: prev_m = false;
1.1.1.2 root 396: }
397:
1.1.1.12 root 398: // ?
1.1.1.2 root 399: void
1.1.1.12 root 400: LunaKeyboard::MouseSendStart()
1.1.1.2 root 401: {
1.1 root 402: }
403:
1.1.1.12 root 404: static const uint buzzer_msec_table[] = { 40, 150, 400, 700 };
405: static const uint buzzer_freq_table[] = {
406: 6000, 3000, 1500, 1000, 600, 300, 150, 100
407: };
408:
1.1.1.4 root 409: // ホストからの制御
410: void
411: LunaKeyboard::Command(uint32 data)
412: {
1.1.1.12 root 413: switch (data & 0xe0) {
414: case 0x00: // LED 点灯コマンド
415: {
416: // 7 6 5 4 3 2 1 0
417: // +---+---+---+---+---+---+---+---+
418: // | 0 0 0 |LED| x x x |COD|
419: // +---+---+---+---+---+---+---+---+
420: // | +--- 0:かな 1:CAP
421: // +------------------- 0:消灯 1:点灯
422: uint code = data & 0x01;
423: uint on = data & 0x10;
424: led[code] = (bool)on;
1.1.1.17 root 425: uimessage->Post(UIMessage::KEYBOARD);
1.1.1.12 root 426: if (loglevel >= 1) {
427: const auto name = GetKeyName(LED2Keycode(code));
1.1.1.13 root 428: putlogn("Command $%02x LED $%x(%s) %s",
429: data, code, name.c_str(), (on ? "ON" : "OFF"));
1.1.1.12 root 430: }
431: break;
432: }
1.1.1.4 root 433:
1.1.1.12 root 434: case 0x40: // ブザーコマンド
435: {
436: // 7 6 5 4 3 2 1 0
437: // +---+---+---+---+---+---+---+---+
438: // | 0 1 0 |T2 T1 |F3 F2 F1 |
439: // +---+---+---+---+---+---+---+---+
440: // | +------- 周波数
441: // +----------------- 時間
442: uint t = (data >> 3) & 3;
443: uint f = data & 7;
444: buzzer_msec = buzzer_msec_table[t];
445: buzzer_freq = buzzer_freq_table[f];
1.1.1.15 root 446: putlog(1, "Command $%02x Buzzer %umsec, %uHz",
1.1.1.13 root 447: data, buzzer_msec, buzzer_freq);
1.1.1.12 root 448:
1.1.1.14 root 449: uint64 now = scheduler->GetVirtTime();
1.1.1.18! root 450: buzzer_end = now + msec_to_nsec(buzzer_msec);
1.1.1.14 root 451: // VM スレッド内なので MakeKey() ではなく KeyInput() を直接呼ぶ
452: KeyInput(KC_buzzer | KC_FLAG_MAKE);
1.1.1.12 root 453: Start(2_msec);
454: break;
455: }
456:
457: case 0x20: // マウス OFF コマンド
458: // 7 6 5 4 3 2 1 0
459: // +---+---+---+---+---+---+---+---+
460: // | 0 0 1 | x x x x x |
461: // +---+---+---+---+---+---+---+---+
1.1.1.13 root 462: putlog(1, "Command $%02x Mouse Sampling Off", data);
1.1.1.12 root 463: MouseOff();
464: break;
465:
466: case 0x60: // マウス ON コマンド
467: // 7 6 5 4 3 2 1 0
468: // +---+---+---+---+---+---+---+---+
469: // | 0 1 1 | x x x x x |
470: // +---+---+---+---+---+---+---+---+
1.1.1.13 root 471: putlog(1, "Command $%02x Mouse Sampling On", data);
1.1.1.12 root 472: MouseOn();
473: break;
474:
475: default:
1.1.1.13 root 476: putlog(1, "Command $%02x Undefined", data);
1.1.1.12 root 477: break;
478: }
479: }
1.1 root 480:
481: // キーコード変換表
482: // 共通キーコードから LUNA キーコードに変換する。
483: // ぶっちゃけた話共通キーコードは LUNA キーコードをベースにしているので、
484: // 穴がある以外は全部透過。
1.1.1.17 root 485: /*static*/ uint
1.1.1.12 root 486: LunaKeyboard::keycode2lunakey_table[KC_max] = {
1.1 root 487: NoKey, // [00]
488: NoKey, // [01]
489: NoKey, // [02]
490: NoKey, // [03]
491: NoKey, // [04]
492: NoKey, // [05]
493: NoKey, // [06]
494: NoKey, // [07]
495: NoKey, // [08]
496: LunaKey_TAB, // [09]
497: LunaKey_CTRL, // [0a]
498: LunaKey_kana, // [0b]
499: LunaKey_SHIFT_L, // [0c]
500: LunaKey_SHIFT_R, // [0d]
501: LunaKey_CAPS, // [0e]
502: LunaKey_SF, // [0f]
503:
504: LunaKey_ESC, // [10]
505: LunaKey_BS, // [11]
506: LunaKey_enter, // [12]
507: NoKey, // [13]
508: LunaKey_space, // [14]
509: LunaKey_DEL, // [15]
510: LunaKey_XFER, // [16]
511: LunaKey_VALID, // [17]
512: LunaKey_PF11, // [18]
513: LunaKey_PF12, // [19]
514: LunaKey_PF13, // [1a]
515: LunaKey_PF14, // [1b]
516: LunaKey_up, // [1c]
517: LunaKey_left, // [1d]
518: LunaKey_right, // [1e]
519: LunaKey_down, // [1f]
520:
1.1.1.17 root 521: LunaKey_INSERT, // [20]
522: LunaKey_COPY, // [21]
1.1 root 523: LunaKey_1, // [22]
524: LunaKey_2, // [23]
525: LunaKey_3, // [24]
526: LunaKey_4, // [25]
527: LunaKey_5, // [26]
528: LunaKey_6, // [27]
529: LunaKey_7, // [28]
530: LunaKey_8, // [29]
531: LunaKey_9, // [2a]
532: LunaKey_0, // [2b]
533: LunaKey_minus, // [2c]
534: LunaKey_circum, // [2d]
535: LunaKey_backslash, // [2e]
1.1.1.14 root 536: LunaKey_buzzer, // [2f]
1.1 root 537:
1.1.1.17 root 538: LunaKey_CUT, // [30]
539: LunaKey_PASTE, // [31]
1.1 root 540: LunaKey_Q, // [32]
541: LunaKey_W, // [33]
542: LunaKey_E, // [34]
543: LunaKey_R, // [35]
544: LunaKey_T, // [36]
545: LunaKey_Y, // [37]
546: LunaKey_U, // [38]
547: LunaKey_I, // [39]
548: LunaKey_O, // [3a]
549: LunaKey_P, // [3b]
550: LunaKey_at, // [3c]
551: LunaKey_bracketleft, // [3d]
552: NoKey, // [3e]
553: NoKey, // [3f]
554:
555: NoKey, // [40]
556: NoKey, // [41]
557: LunaKey_A, // [42]
558: LunaKey_S, // [43]
559: LunaKey_D, // [44]
560: LunaKey_F, // [45]
561: LunaKey_G, // [46]
562: LunaKey_H, // [47]
563: LunaKey_J, // [48]
564: LunaKey_K, // [49]
565: LunaKey_L, // [4a]
566: LunaKey_semicolon, // [4b]
567: LunaKey_colon, // [4c]
568: LunaKey_bracketright, // [4d]
569: NoKey, // [4e]
570: NoKey, // [4f]
571:
572: NoKey, // [50]
573: NoKey, // [51]
574: LunaKey_Z, // [52]
575: LunaKey_X, // [53]
576: LunaKey_C, // [54]
577: LunaKey_V, // [55]
578: LunaKey_B, // [56]
579: LunaKey_N, // [57]
580: LunaKey_M, // [58]
581: LunaKey_comma, // [59]
582: LunaKey_period, // [5a]
583: LunaKey_slash, // [5b]
584: LunaKey_underscore, // [5c]
585: NoKey, // [5d]
586: NoKey, // [5e]
587: NoKey, // [5f]
588:
1.1.1.17 root 589: LunaKey_PAD_HOME, // [60]
1.1 root 590: LunaKey_PAD_plus, // [61]
591: LunaKey_PAD_minus, // [62]
592: LunaKey_PAD_7, // [63]
593: LunaKey_PAD_8, // [64]
594: LunaKey_PAD_9, // [65]
595: LunaKey_PAD_4, // [66]
596: LunaKey_PAD_5, // [67]
597: LunaKey_PAD_6, // [68]
598: LunaKey_PAD_1, // [69]
599: LunaKey_PAD_2, // [6a]
600: LunaKey_PAD_3, // [6b]
601: LunaKey_PAD_0, // [6c]
602: LunaKey_PAD_decimal, // [6d]
603: LunaKey_PAD_enter, // [6e]
604: NoKey, // [6f]
605:
606: NoKey, // [70]
607: NoKey, // [71]
608: LunaKey_F1, // [72]
609: LunaKey_F2, // [73]
610: LunaKey_F3, // [74]
611: LunaKey_F4, // [75]
612: LunaKey_F5, // [76]
613: LunaKey_F6, // [77]
614: LunaKey_F7, // [78]
615: LunaKey_F8, // [79]
616: LunaKey_F9, // [7a]
617: LunaKey_F10, // [7b]
618: LunaKey_PAD_multiply, // [7c]
619: LunaKey_PAD_divide, // [7d]
620: LunaKey_PAD_equal, // [7e]
621: LunaKey_PAD_comma, // [7f]
622: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.