--- nono/vm/lunakbd.cpp 2026/04/29 17:04:29 1.1.1.2 +++ nono/vm/lunakbd.cpp 2026/04/29 17:04:52 1.1.1.9 @@ -1,6 +1,7 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // // キーボードとマウス @@ -14,20 +15,19 @@ // ということにする。 #include "lunakbd.h" -#include "mystring.h" #include "sio.h" // コンストラクタ LunaKeyboard::LunaKeyboard() { - monitor.Init(30, 1); + monitor_size = nnSize(30, 1); - memset(reptable, 0, sizeof(reptable)); + led.resize(2); key_event.dev = this; key_event.func = (DeviceCallback_t)&LunaKeyboard::KeyboardCallback; key_event.time = 0; - key_event.SetName("KeyInput"); + key_event.SetName("Keyboard Slot"); // マウスイベント#0 はサンプリング用も兼ねているので常にオン mouse_event[0].dev = this; @@ -39,7 +39,7 @@ LunaKeyboard::LunaKeyboard() for (int i = 1; i < 3; i++) { mouse_event[i].dev = this; mouse_event[i].func = (DeviceCallback_t)&LunaKeyboard::MouseCallback; - mouse_event[i].time = 0; + mouse_event[i].time = 2_msec * i; mouse_event[i].SetName(string_format("Mouse Slot%d", i)); } } @@ -49,17 +49,43 @@ LunaKeyboard::~LunaKeyboard() { } +// 電源オン +bool +LunaKeyboard::PowerOn() +{ + // XXX リセットはマウスには届かないような気がするので + // 電源オンで初期化。 + mouse_x = 0; + mouse_y = 0; + mouse_r = false; + mouse_l = false; + mouse_m = false; + prev_r = false; + prev_l = false; + prev_m = false; + + return true; +} + // リセット void LunaKeyboard::ResetHard() { - // マウスサンプリングは電源オン時は有効らしい - gScheduler->AddEvent(&mouse_event[0]); + inherited::ResetHard(); + + // 動作中のタイマーは一旦とめる + key_event.Stop(); + for (int i = 0; i < countof(mouse_event); i++) { + mouse_event[i].Stop(); + } + + // で、マウスサンプリングは電源オン時から有効らしい + mouse_event[0].Start(); } // モニター -bool -LunaKeyboard::MonitorUpdate() +void +LunaKeyboard::MonitorUpdate(TextScreen& monitor) { int x, y; monitor.Clear(); @@ -79,88 +105,54 @@ LunaKeyboard::MonitorUpdate() (mouse_l ? 'L' : '-'), (mouse_m ? 'M' : '-'), (mouse_r ? 'R' : '-')); - - return true; } -// キーを押した -void -LunaKeyboard::MakeKey(uint keycode) +// 共通キーコードを LUNA キーコードに変換して返す。 +// 対応する LUNA のキーがなければ 0 を返す。 +uint +LunaKeyboard::KeyToMDKey(uint keycode) const { - uint lunakey; - - // 共通キーコードから LUNA キーコードに - lunakey = keycode2lunakey(keycode); - - // LUNA にないキーは無視 - if (lunakey == NoKey) { - return; - } - - // すでにこのキーが押されていれば送らない - if (reptable[lunakey]) { - return; + if (keycode >= countof(keycode2lunakey_table)) { + return 0; } - // キーが押された - putlog(1, "MakeKey %d(%s)", lunakey, keyname[lunakey]); - reptable[lunakey] = 1; - - // イベントを登録 - SetKeyEvent(lunakey); + return keycode2lunakey_table[keycode]; } -// キーを離した -void -LunaKeyboard::BreakKey(uint keycode) +// シフト状態なら true を返す +bool +LunaKeyboard::IsShift() const { - uint lunakey; - - // 共通キーコードから LUNA キーコードに - lunakey = keycode2lunakey(keycode); - - // LUNA にないキーは無視 - if (lunakey == NoKey) { - return; - } - - // キーが離された - putlog(2, "BreakKey %d(%s)", lunakey, keyname[lunakey]); - reptable[lunakey] = 0; - - // イベントを登録 - SetKeyEvent(lunakey | 0x80); + return pressed[KC_SHIFT_L] || pressed[KC_SHIFT_R]; } -// キー入力用の空きスロットにイベントをセット -void -LunaKeyboard::SetKeyEvent(uint code) +// キー処理 (MD固有部分) +bool +LunaKeyboard::ProcessKey(uint keystat) { - uint64 now = gScheduler->GetVirtTime(); - - // t は現在の仮想時刻に対応するスロット - uint64 t = (now / 2_msec) % 10; - // n はその次のスロット - uint64 n = (t + 1) % 10; + if (KC_IS_MAKE(keystat)) { + // LED はトグル? + uint keycode = KC_CODE(keystat); + int n = Keycode2LED(keycode); + if (n >= 0) { + led[n] = !led[n]; + } + } - // マウススロットなら避ける - if (n < 3) - n = 3; + return true; +} - // このイベントが起きる時間を再計算 - uint64 target = (now / 20_msec) * 20_msec + n * 2_msec; - if (n < t) { - // n が折り返している場合は n は次の20msec 枠内 - target += 20_msec; - } +// 共通キーコードから LED 番号を返す。LED キーでなければ -1 を返す。 +int +LunaKeyboard::Keycode2LED(uint keycode) const +{ + // 2つしかないのでこのくらいでいいか + if (keycode == KC_kana) + return 0; + if (keycode == KC_CAPS) + return 1; - // イベントを登録。 - if (key_sending == false) { - key_sending = true; - key_event.time = target - now; - key_event.code = code; - gScheduler->AddEvent(&key_event); - } + return -1; } // マウス入力 @@ -177,19 +169,93 @@ LunaKeyboard::MouseInput(int x, int y, b mouse_m = mb; } +// キーイベントを起こせる空きスロットを計算して、イベントを開始 +void +LunaKeyboard::SendStart() +{ + // 例えば、スロット 4 期間内のどこかでキー入力(K)が起きると + // スロット 5 の先頭時刻にイベント(E)を起こす。 + // + // 通算スロット 44 45 46 + // スロット番号 4 5 6 + // +-----+-----+-----+.. + // | K--->E + // +-----+-----+-----+.. + // + // スロット 9 期間内のどこかでキー入力(K)が起きると、(0, 1, 2 はマウス用 + // のスロットにしているので、これを飛ばして) スロット 3 の先頭でイベント + // (E)を起こす。 + // + // 通算スロット 209 210 211 212 213 + // スロット番号 9 0 1 2 3 + // +-----+-----+-----+-----+-----+.. + // | K------------------>E + // +-----+-----+-----+-----+-----+.. + + // イベントが進行中ならなにもせず帰る + if (key_event.IsRunning()) { + return; + } + + uint64 now = gMPU->GetVirtTime(); + + // t は現在の仮想時刻に対応する通算スロット + uint64 t = now / 2_msec; + // n はその次の通算スロット + uint64 n = t + 1; + + // マウススロットなら避ける + while (n % 10 < 3) { + n++; + } + + // n の時刻がイベントを起こす時刻 + uint64 target = n * 2_msec; + + // イベントを登録。 + key_event.time = target - now; + key_event.Start(); +} + // キー入力コールバック // SIO に送信するタイミングで呼ばれる。 void -LunaKeyboard::KeyboardCallback(int code) +LunaKeyboard::KeyboardCallback(Event& ev) { - gSIO->Rx(1, code); - key_sending = false; + // 呼ばれたということはキューは空でないはず + assert(sendqueue.Empty() == false); + + // キューから取り出す。ここで取り出せるのは keystat + uint keystat = sendqueue.Dequeue(); + uint keycode = KC_CODE(keystat); + // ここで LUNA キーコードに変換 (変換できないはずはない) + uint mdkey = KeyToMDKey(keycode); + assert(mdkey != 0); + + // 送信 + if (KC_IS_BREAK(keystat)) { + mdkey |= 0x80; + } + gSIO->Rx(1, mdkey); + + if (Keycode2LED(keystat) >= 0) { + // かなと CAP キーの押下なら、続けてキーの復旧も送る。 + // 押下の時だけここに来ればいいので keycode じゃなく keystat。 + + // (BreakKey() が内部でもう一度イベントをキックしている) + BreakKey(keycode); + } + + // キューにまだあるならここで次のイベントをキックする必要がある + if (sendqueue.Empty() == false) { + SendStart(); + } } // マウスサンプリングコールバック // 20msec ごとに呼ばれる。 void -LunaKeyboard::MouseSamplingCallback(int arg) +LunaKeyboard::MouseSamplingCallback(Event& ev) { int reset_x = 0; int reset_y = 0; @@ -211,6 +277,12 @@ LunaKeyboard::MouseSamplingCallback(int mouse_l != prev_l || mouse_m != prev_m) { + putlog(2, "Mouse Sampling x=%d y=%d %c%c%c", + mouse_x, mouse_y, + (mouse_r ? 'L' : '-'), + (mouse_m ? 'M' : '-'), + (mouse_l ? 'R' : '-')); + // 1バイト目、ボタン状態。 // mouse_[rlm] 変数は押し下げが true、LUNA のマウスは押し下げが %0 uint8 b = 0x80 | @@ -233,15 +305,15 @@ LunaKeyboard::MouseSamplingCallback(int // 1バイト目はここで送信 gSIO->Rx(1, b); - // 2バイト目は 2msec 秒後に予約 - mouse_event[1].time = 2_msec; + // 2バイト目は 2msec 秒後に送信 mouse_event[1].code = (uint8)mouse_x; - gScheduler->AddEvent(&mouse_event[1]); + mouse_event[1].Start(); - // 3バイト目は 4msec 秒後に予約 - mouse_event[2].time = 4_msec; + // 3バイト目は 4msec 秒後に送信 mouse_event[2].code = (uint8)mouse_y; - gScheduler->AddEvent(&mouse_event[2]); + mouse_event[2].Start(); + } else { + putlog(3, "Mouse Sampling inactive"); } // 積算リセット @@ -252,35 +324,29 @@ LunaKeyboard::MouseSamplingCallback(int prev_m = mouse_m; // 20msec 後に再び自分を呼び出す - mouse_event[0].time = 20_msec; - gScheduler->AddEvent(&mouse_event[0]); + ev.Start(); } // マウスの後続バイトを送出するイベントコールバック void -LunaKeyboard::MouseCallback(int code) +LunaKeyboard::MouseCallback(Event& ev) { - gSIO->Rx(1, code); + gSIO->Rx(1, ev.code); } -// 共通キーコードを LUNA キーコードに変換して返す。 -// LUNA キーがなければ NoKey を返す。 -uint -LunaKeyboard::keycode2lunakey(uint keycode) +// ホストからの制御 +void +LunaKeyboard::Command(uint32 data) { - if (keycode >= countof(keycode2lunakey_table)) { - return NoKey; - } - - return keycode2lunakey_table[keycode]; } + // キーコード変換表 // 共通キーコードから LUNA キーコードに変換する。 // ぶっちゃけた話共通キーコードは LUNA キーコードをベースにしているので、 // 穴がある以外は全部透過。 /*static*/ const uint -LunaKeyboard::keycode2lunakey_table[128] = { +LunaKeyboard::keycode2lunakey_table[0x80] = { NoKey, // [00] NoKey, // [01] NoKey, // [02] @@ -417,143 +483,3 @@ LunaKeyboard::keycode2lunakey_table[128] LunaKey_PAD_equal, // [7e] LunaKey_PAD_comma, // [7f] }; - -/*static*/ const char * const -LunaKeyboard::keyname[] = -{ - "", // 00 - "", // 01 - "", // 02 - "", // 03 - "", // 04 - "", // 05 - "", // 06 - "", // 07 - "", // 08 - "TAB", // 09 - "CTRL", // 0a - "かな", // 0b - "SHIFT", // 0c - "SHIFT", // 0d - "CAPS", // 0e - "S.F", // 0f - - "ESC", // 10 - "BS", // 11 - "Enter", // 12 - "", // 13 - "Space", // 14 - "DEL", // 15 - "XFER", // 16 - "VALID", // 17 - "PF11", // 18 - "PF12", // 19 - "PF13", // 1a - "PF14", // 1b - "↑", // 1c - "←", // 1d - "→", // 1e - "↓", // 1f - - "", // 20 - "", // 21 - "1!", // 22 - "2\"", // 23 - "3#", // 24 - "4$", // 25 - "5%", // 26 - "6&", // 27 - "7'", // 28 - "8(", // 29 - "9)", // 2a - "0", // 2b - "-=", // 2c - "^~", // 2d - "\\|", // 2e - "", // 2f - - "", // 30 - "", // 31 - "Q", // 32 - "W", // 33 - "E", // 34 - "R", // 35 - "T", // 36 - "Y", // 37 - "U", // 38 - "I", // 39 - "O", // 3a - "P", // 3b - "@`", // 3c - "[{", // 3d - "", // 3e - "", // 3f - - "", // 40 - "", // 41 - "A", // 42 - "S", // 43 - "D", // 44 - "F", // 45 - "G", // 46 - "H", // 47 - "J", // 48 - "K", // 49 - "L", // 4a - ";+", // 4b - ":*", // 4c - "]}", // 4d - "", // 4e - "", // 4f - - "", // 50 - "", // 51 - "Z", // 52 - "X", // 53 - "C", // 54 - "V", // 55 - "B", // 56 - "N", // 57 - "M", // 58 - ",<", // 59 - ".>", // 5a - "/?", // 5b - " _", // 5c - "", // 5d - "", // 5e - "", // 5f - - "", // 60 - "PAD+", // 61 - "PAD-", // 62 - "PAD7", // 63 - "PAD8", // 64 - "PAD9", // 65 - "PAD4", // 66 - "PAD5", // 67 - "PAD6", // 68 - "PAD1", // 69 - "PAD2", // 6a - "PAD3", // 6b - "PAD0", // 6c - "PAD.", // 6d - "PADEn", // 6e - "", // 6f - - "", // 70 - "", // 71 - "F1", // 72 - "F2", // 73 - "F3", // 74 - "F4", // 75 - "F5", // 76 - "F6", // 77 - "F7", // 78 - "F8", // 79 - "F9", // 7a - "F10", // 7b - "PAD*", // 7c - "PAD/", // 7d - "PAD=", // 7e - "PAD,", // 7f -};