|
|
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: // 開放される。これは上述の通り仕様である。
1.1.1.9 ! root 65: //
! 66: // LUNA のキーボードは左右の SHIFT キーの区別がある。このため、 LUNA は
! 67: // 特別処理をする必要はない。
! 68: // ただしゲスト上の入力ルーチンがこの区別をしているかは別問題である。
! 69:
! 70: // 処理の流れ (SendStart まで)
! 71: //
! 72: // MakeKey(), BreakKey() までは基本的に VM スレッドに処理を渡すだけ。
! 73: //
! 74: // UI スレッド : VM スレッド
! 75: //
! 76: // キャラクタ入力 :
! 77: // |
! 78: // 直接キー入力 | :
! 79: // | v
! 80: // | CharInput() :
! 81: // | |
! 82: // +------------+ :
! 83: // v
! 84: // MakeKey()/BreakKey() :
! 85: // |
! 86: // | inputqueue + :
! 87: // | keyinput_event
! 88: // | - - - - - - - - - - - -> KeyInputCallback()
! 89: // o |
! 90: // : MD::KeyInput()
! 91: // |
! 92: // : KeyInputCommon()
! 93: // |
! 94: // : MD::SendStart()
! 95: // v
! 96: // 機種ごとに接続先に送信
1.1.1.5 root 97:
1.1 root 98: #include "keyboard.h"
1.1.1.5 root 99: #include "scheduler.h"
1.1.1.7 root 100: #include "uimessage.h"
1.1 root 101:
1.1.1.2 root 102: std::unique_ptr<Keyboard> gKeyboard;
1.1 root 103:
104: // コンストラクタ
105: Keyboard::Keyboard()
1.1.1.6 root 106: : inherited("Keyboard")
1.1 root 107: {
1.1.1.5 root 108: pressed.resize(KC_max);
109:
110: keyinput_event.func = (DeviceCallback_t)&Keyboard::KeyInputCallback;
111: keyinput_event.time = 0;
112: keyinput_event.SetName("Keyboard Input");
1.1 root 113: }
114:
115: // デストラクタ
116: Keyboard::~Keyboard()
117: {
118: }
119:
1.1.1.5 root 120: // リセット
121: void
122: Keyboard::ResetHard()
123: {
124: // 内部状態を全部リセット
125:
126: inputqueue.Clear();
127: sendqueue.Clear();
128: std::fill(pressed.begin(), pressed.end(), false);
1.1.1.9 ! root 129: active = 0;
1.1.1.5 root 130: // キーが全部解放されたので通知
131: gScheduler->RequestKeyPressed(false);
132: }
133:
134: // キーを押した (外部から、共通キーコードで呼ばれる)
135: void
136: Keyboard::MakeKey(uint keycode)
137: {
138: // ここは UI スレッドからも呼ばれるのでキューに突っ込むだけ
139: {
140: std::unique_lock<std::mutex> lock(mtx);
141: inputqueue.Enqueue(keycode | KC_FLAG_MAKE);
142: }
143: keyinput_event.Start();
144: }
145:
146: // キーを離した (外部から、共通キーコードで呼ばれる)
147: void
148: Keyboard::BreakKey(uint keycode)
149: {
150: // ここは UI スレッドからも呼ばれるのでキューに突っ込むだけ
151: {
152: std::unique_lock<std::mutex> lock(mtx);
153: inputqueue.Enqueue(keycode | KC_FLAG_BREAK);
154: }
155: keyinput_event.Start();
156: }
157:
1.1.1.8 root 158: // 文字による入力
159: // WX での文字入力モード、CLI からの入力、ターミナルエミュレーションでの
160: // 入力によりキーボードの押し下げをエミュレートする。
161: void
162: Keyboard::CharInput(uint charcode)
163: {
164: if (charcode >= char_key.size()) {
165: return;
166: }
167:
168: uint key = char_key[charcode];
169: uint code = KC_CODE(key);
170: if ((key & KM_SHIFT)) {
171: MakeKey(KC_SHIFT_L | KC_FLAG_POST);
172: MakeKey(KC_SHIFT_R | KC_FLAG_POST);
173: } else {
174: BreakKey(KC_SHIFT_L);
175: BreakKey(KC_SHIFT_R);
176: }
177: if ((key & KM_CTRL)) {
178: MakeKey(KC_CTRL | KC_FLAG_POST);
179: } else {
180: BreakKey(KC_CTRL);
181: }
182: MakeKey(code | KC_FLAG_POST);
183: BreakKey(code);
184: if ((key & KM_CTRL)) {
185: BreakKey(KC_CTRL);
186: }
187: if ((key & KM_SHIFT)) {
188: BreakKey(KC_SHIFT_L);
189: BreakKey(KC_SHIFT_R);
190: }
191: }
192:
1.1.1.5 root 193: // キー入力コールバック
194: void
195: Keyboard::KeyInputCallback(Event& ev)
196: {
197: for (;;) {
198: uint keystat;
199: {
200: std::unique_lock<std::mutex> lock(mtx);
201: if (inputqueue.Empty()) {
202: return;
203: }
204: keystat = inputqueue.Dequeue();
205: }
206: KeyInput(keystat);
207: }
208: }
209:
1.1.1.9 ! root 210: // キー入力を1つ処理する。
! 211: // keystat が押下・開放されたキーの物理的な状態。
! 212: // キーを破棄したら false を返す。
1.1.1.6 root 213: bool
214: Keyboard::KeyInputCommon(uint keystat)
1.1.1.5 root 215: {
216: uint keycode = KC_CODE(keystat);
217: bool ismake = KC_IS_MAKE(keystat);
1.1.1.9 ! root 218: uint ledflag = KC_FLAG_LED(keystat);
1.1.1.5 root 219:
220: // 対応する機種別キーコードがなければ無視。
221: // (ここでは調べるだけでまだ変換はしない)
1.1.1.9 ! root 222: if (KeyToMDKey(keystat) == 0) {
1.1.1.6 root 223: return false;
1.1.1.5 root 224: }
225:
226: // このキーの状態が変化しないのなら何もしない。
227: if (IsPressed(keycode) == ismake) {
1.1.1.6 root 228: return false;
1.1.1.5 root 229: }
230:
231: // 処理することが確定したのでログ
232: // (離すほうは押すほうよりログレベルを1つ上げてある)
1.1.1.9 ! root 233: if (loglevel >= 1) {
! 234: std::string msg;
! 235:
! 236: msg += string_format("%d(", keycode);
! 237: msg += GetKeyName(keycode);
! 238: msg += ")";
! 239:
! 240: if (ledflag) {
! 241: // LUNA の LED キーの場合、sendstat も明示
! 242: if (ledflag == KC_FLAG_LED_DROP) {
! 243: msg += ", drop";
! 244: } else {
! 245: msg += ", send ";
! 246: if (ledflag == KC_FLAG_LED_MAKE) {
! 247: msg += "Make";
! 248: } else {
! 249: msg += "Break";
! 250: }
! 251: }
! 252: }
! 253:
! 254: if (ismake) {
! 255: putlogn( "MakeKey %s", msg.c_str());
! 256: } else {
! 257: putlog(2, "BreakKey %s", msg.c_str());
! 258: }
1.1.1.5 root 259: }
260:
261: // キーが押されてる間は VM の高速実行を抑制する
262: // すべてのキーが離されたら高速モードを許可する
263: pressed[keycode] = ismake;
264: if (ismake) {
1.1.1.9 ! root 265: Acquire();
1.1.1.5 root 266: } else {
1.1.1.9 ! root 267: Release();
1.1.1.5 root 268: }
269:
1.1.1.9 ! root 270: // キーの状態が変わったので UI に通知。
1.1.1.8 root 271: // KC_FLAG_POST が立っていればキーコードも送信 (char 入力モード用)。
272: // 通知を受け取った UI 側が IsPressed() などでキーの現状を調べることが
273: // あるため pressed[] 更新後に通知する。
1.1.1.9 ! root 274: // XXX LED 変更が間に合わない (see lunakbd.cpp)
1.1.1.8 root 275: uint arg;
276: if (KC_IS_POST(keystat)) {
277: arg = keycode;
278: } else {
279: arg = 0;
280: }
281: UIMessage::Post(UIMessage::KEYBOARD, arg);
282:
1.1.1.9 ! root 283: // LUNA の LED キー消灯はキー開放を送信
! 284: if (ledflag == KC_FLAG_LED_BREAK) {
! 285: keystat |= KC_FLAG_BREAK;
! 286: }
! 287:
! 288: if (ledflag != KC_FLAG_LED_DROP) {
! 289: // 送信キューに登録
! 290:
! 291: // ここでは変換できるはず
! 292: uint mdkey = KeyToMDKey(keystat);
! 293: sendqueue.Enqueue(mdkey);
! 294:
! 295: // 機種依存のキー送信処理
! 296: SendStart();
! 297: }
1.1.1.5 root 298:
1.1.1.6 root 299: return true;
1.1.1.5 root 300: }
301:
1.1.1.9 ! root 302: // アクティブカウンタを 1 増やす。
! 303: // 0 -> 1 の遷移でスケジューラに通知する。
! 304: void
! 305: Keyboard::Acquire()
! 306: {
! 307: if (active++ == 0) {
! 308: gScheduler->RequestKeyPressed(true);
! 309: }
! 310: }
! 311:
! 312: // アクティブカウンタを 1 減らす。
! 313: // 1 -> 0 の遷移でスケジューラに通知する。
! 314: void
! 315: Keyboard::Release()
! 316: {
! 317: if (--active == 0) {
! 318: gScheduler->RequestKeyPressed(false);
! 319: }
! 320: }
! 321:
1.1 root 322: // キー名を返す (主にデバッグ用)
323: const std::string
1.1.1.6 root 324: Keyboard::GetKeyName(uint keycode)
1.1 root 325: {
1.1.1.5 root 326: if (keycode >= keyname.size()) {
327: return string_format("(keycode=0x%x, out of range)", keycode);
1.1 root 328: }
1.1.1.5 root 329: if (keyname[keycode] == NULL) {
330: return string_format("(keycode=0x%x)", keycode);
1.1 root 331: }
1.1.1.5 root 332: return std::string(keyname[keycode]);
1.1 root 333: }
334:
335: /*static*/ std::vector<const char *>
336: Keyboard::keyname {
337: "(none)", // 00
338: "BREAK", // 01
339: "COPY", // 02
340: "romaji", // 03
341: "code", // 04
342: "kigou", // 05
343: "touroku", // 06
344: "HELP", // 07
1.1.1.9 ! root 345: "SHIFT", // 08
1.1 root 346: "TAB", // 09
347: "CTRL", // 0a
348: "kana", // 0b
349: "SHIFT_L", // 0c
350: "SHIFT_R", // 0d
351: "CAPS", // 0e
352: "SF", // 0f
353:
354: "ESC", // 10
355: "BS", // 11
356: "enter", // 12
357: "INS", // 13
358: "space", // 14
359: "DEL", // 15
360: "XFER", // 16
361: "VALID", // 17
362: "PF11", // 18
363: "PF12", // 19
364: "PF13", // 1a
365: "PF14", // 1b
366: "up", // 1c
367: "left", // 1d
368: "right", // 1e
369: "down", // 1f
370:
371: "ROLLUP", // 20
372: "ROLLDOWN", // 21
373: "1", // 22
374: "2", // 23
375: "3", // 24
376: "4", // 25
377: "5", // 26
378: "6", // 27
379: "7", // 28
380: "8", // 29
381: "9", // 2a
382: "0", // 2b
383: "minus", // 2c
384: "circum", // 2d
385: "backslash", // 2e
1.1.1.9 ! root 386: NULL, // 2f
1.1 root 387:
388: "OPT1", // 30
389: "OPT2", // 31
390: "Q", // 32
391: "W", // 33
392: "E", // 34
393: "R", // 35
394: "T", // 36
395: "Y", // 37
396: "U", // 38
397: "I", // 39
398: "O", // 3a
399: "P", // 3b
400: "at", // 3c
401: "bracketleft", // 3d
402: "hira", // 3e
403: "zenkaku", // 3f
404:
1.1.1.9 ! root 405: "HOME", // 40
! 406: "UNDO", // 41
1.1 root 407: "A", // 42
408: "S", // 43
409: "D", // 44
410: "F", // 45
411: "G", // 46
412: "H", // 47
413: "J", // 48
414: "K", // 49
415: "L", // 4a
416: "semicolon", // 4b
417: "colon", // 4c
418: "bracketright", // 4d
419: NULL, // 4e
420: NULL, // 4f
421:
1.1.1.9 ! root 422: "XF1", // 50
! 423: "XF2", // 51
1.1 root 424: "Z", // 52
425: "X", // 53
426: "C", // 54
427: "V", // 55
428: "B", // 56
429: "N", // 57
430: "M", // 58
431: "comma", // 59
432: "period", // 5a
433: "slash", // 5b
434: "underscore", // 5c
1.1.1.9 ! root 435: "XF3", // 5d
! 436: "XF4", // 5e
! 437: "XF5", // 5f
1.1 root 438:
439: NULL, // 60
440: "PAD_plus", // 61
441: "PAD_minus", // 62
442: "PAD_7", // 63
443: "PAD_8", // 64
444: "PAD_9", // 65
445: "PAD_4", // 66
446: "PAD_5", // 67
447: "PAD_6", // 68
448: "PAD_1", // 69
449: "PAD_2", // 6a
450: "PAD_3", // 6b
451: "PAD_0", // 6c
452: "PAD_decimal", // 6d
453: "PAD_enter", // 6e
454: "PAD_CLR", // 6f
455:
456: NULL, // 70
457: NULL, // 71
458: "F1", // 72
459: "F2", // 73
460: "F3", // 74
461: "F4", // 75
462: "F5", // 76
463: "F6", // 77
464: "F7", // 78
465: "F8", // 79
466: "F9", // 7a
467: "F10", // 7b
468: "PAD_multiply", // 7c
469: "PAD_divide", // 7d
470: "PAD_equal", // 7e
471: "PAD_comma", // 7f
472: };
1.1.1.8 root 473:
474: // 文字対キーストローク表
475: // 文字による入力エミュレーションで使用。
476: /*static*/ std::vector<uint>
477: Keyboard::char_key = {
478: 0, // NONE
479: KC_A + KM_CTRL, // 0x01 ^A
480: KC_B + KM_CTRL, // 0x02 ^B
481: KC_C + KM_CTRL, // 0x03 ^C
482: KC_D + KM_CTRL, // 0x04 ^D
483: KC_E + KM_CTRL, // 0x05 ^E
484: KC_F + KM_CTRL, // 0x06 ^F
485: KC_G + KM_CTRL, // 0x07 ^G
486: KC_BS, // 0x08 ^H
487: KC_TAB, // 0x09 ^I
488: KC_J + KM_CTRL, // 0x0a ^J
489: KC_K + KM_CTRL, // 0x0b ^K
490: KC_L + KM_CTRL, // 0x0c ^L
491: KC_enter, // 0x0d ^M
492: KC_N + KM_CTRL, // 0x0e ^N
493: KC_O + KM_CTRL, // 0x0f ^O
494: KC_P + KM_CTRL, // 0x10 ^P
495: KC_Q + KM_CTRL, // 0x11 ^Q
496: KC_R + KM_CTRL, // 0x12 ^R
497: KC_S + KM_CTRL, // 0x13 ^S
498: KC_T + KM_CTRL, // 0x14 ^T
499: KC_U + KM_CTRL, // 0x15 ^U
500: KC_V + KM_CTRL, // 0x16 ^V
501: KC_W + KM_CTRL, // 0x17 ^W
502: KC_X + KM_CTRL, // 0x18 ^X
503: KC_Y + KM_CTRL, // 0x19 ^Y
504: KC_Z + KM_CTRL, // 0x1a ^Z
505:
506: KC_ESC, // 0x1b ^[
507: KC_backslash + KM_CTRL, // 0x1c ^'\'
508: KC_bracketright + KM_CTRL, // 0x1d ^]
509: KC_circum + KM_CTRL, // 0x1e ^^
510: KC_underscore + KM_CTRL, // 0x1f ^_
511:
512: KC_space, // 0x20 ' '
513: KC_1 + KM_SHIFT, // 0x21 !
514: KC_2 + KM_SHIFT, // 0x22 "
515: KC_3 + KM_SHIFT, // 0x23 #
516: KC_4 + KM_SHIFT, // 0x24 $
517: KC_5 + KM_SHIFT, // 0x25 %
518: KC_6 + KM_SHIFT, // 0x26 &
519: KC_7 + KM_SHIFT, // 0x27 '
520: KC_8 + KM_SHIFT, // 0x28 (
521: KC_9 + KM_SHIFT, // 0x29 )
522: KC_colon + KM_SHIFT, // 0x2a *
523: KC_semicolon + KM_SHIFT, // 0x2b +
524: KC_comma, // 0x2c ,
525: KC_minus, // 0x2d -
526: KC_period, // 0x2e .
527: KC_slash, // 0x2f /
528:
529: KC_0, // 0x30 0
530: KC_1, // 0x31 1
531: KC_2, // 0x32 2
532: KC_3, // 0x33 3
533: KC_4, // 0x34 4
534: KC_5, // 0x35 5
535: KC_6, // 0x36 6
536: KC_7, // 0x37 7
537: KC_8, // 0x38 8
538: KC_9, // 0x39 9
539: KC_colon, // 0x3a :
540: KC_semicolon, // 0x3b ;
541: KC_comma + KM_SHIFT, // 0x3c <
542: KC_minus + KM_SHIFT, // 0x3d =
543: KC_period + KM_SHIFT, // 0x3e >
544: KC_slash + KM_SHIFT, // 0x3f ?
545:
546: KC_at, // 0x40 @
547: KC_A + KM_SHIFT, // 0x41 A
548: KC_B + KM_SHIFT, // 0x42 B
549: KC_C + KM_SHIFT, // 0x43 C
550: KC_D + KM_SHIFT, // 0x44 D
551: KC_E + KM_SHIFT, // 0x45 E
552: KC_F + KM_SHIFT, // 0x46 F
553: KC_G + KM_SHIFT, // 0x47 G
554: KC_H + KM_SHIFT, // 0x48 H
555: KC_I + KM_SHIFT, // 0x49 I
556: KC_J + KM_SHIFT, // 0x4a J
557: KC_K + KM_SHIFT, // 0x4b K
558: KC_L + KM_SHIFT, // 0x4c L
559: KC_M + KM_SHIFT, // 0x4d M
560: KC_N + KM_SHIFT, // 0x4e N
561: KC_O + KM_SHIFT, // 0x4f O
562: KC_P + KM_SHIFT, // 0x50 P
563: KC_Q + KM_SHIFT, // 0x51 Q
564: KC_R + KM_SHIFT, // 0x52 R
565: KC_S + KM_SHIFT, // 0x53 S
566: KC_T + KM_SHIFT, // 0x54 T
567: KC_U + KM_SHIFT, // 0x55 U
568: KC_V + KM_SHIFT, // 0x56 V
569: KC_W + KM_SHIFT, // 0x57 W
570: KC_X + KM_SHIFT, // 0x58 X
571: KC_Y + KM_SHIFT, // 0x59 Y
572: KC_Z + KM_SHIFT, // 0x5a Z
573:
574: KC_bracketleft, // 0x5b [
575: KC_backslash, // 0x5c '\'
576: KC_bracketright, // 0x5d ]
577: KC_circum, // 0x5e ^
578: KC_underscore + KM_SHIFT, // 0x5f _
579:
580: KC_at + KM_SHIFT, // 0x60 `
581: KC_A, // 0x61 a
582: KC_B, // 0x62 b
583: KC_C, // 0x63 c
584: KC_D, // 0x64 d
585: KC_E, // 0x65 e
586: KC_F, // 0x66 f
587: KC_G, // 0x67 g
588: KC_H, // 0x68 h
589: KC_I, // 0x69 i
590: KC_J, // 0x6a j
591: KC_K, // 0x6b k
592: KC_L, // 0x6c l
593: KC_M, // 0x6d m
594: KC_N, // 0x6e n
595: KC_O, // 0x6f o
596: KC_P, // 0x70 p
597: KC_Q, // 0x71 q
598: KC_R, // 0x72 r
599: KC_S, // 0x73 s
600: KC_T, // 0x74 t
601: KC_U, // 0x75 u
602: KC_V, // 0x76 v
603: KC_W, // 0x77 w
604: KC_X, // 0x78 x
605: KC_Y, // 0x79 y
606: KC_Z, // 0x7a z
607:
608: KC_bracketleft + KM_SHIFT, // 0x7b {
609: KC_backslash + KM_SHIFT, // 0x7c |
610: KC_bracketright + KM_SHIFT, // 0x7d }
611: KC_circum + KM_SHIFT, // 0x7e ~
612: KC_none, // 0x7f DEL
613:
614: KC_at + KM_CTRL, // 0x80 CC_NUL
615: KC_F1, // 0x81 CC_F1
616: KC_F2, // 0x82 CC_F2
617: KC_F3, // 0x83 CC_F3
618: KC_F4, // 0x84 CC_F4
619: KC_F5, // 0x85 CC_F5
620: KC_F6, // 0x86 CC_F6
621: KC_F7, // 0x87 CC_F7
622: KC_F8, // 0x88 CC_F8
623: KC_F9, // 0x89 CC_F9
1.1.1.9 ! root 624: KC_F10, // 0x8a CC_F10
1.1.1.8 root 625: 0, // 0x8b
626: KC_up, // 0x8c CC_up
627: KC_left, // 0x8d CC_left
628: KC_right, // 0x8e CC_right
629: KC_down, // 0x8f CC_down
630: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.