--- nono/vm/lunakbd.cpp 2026/04/29 17:04:28 1.1 +++ nono/vm/lunakbd.cpp 2026/04/29 17:04:29 1.1.1.2 @@ -3,19 +3,45 @@ // Copyright (C) 2018 isaki@NetBSD.org // +// キーボードとマウス +// +// キーボード/マウスからシリアルポートへの入力は、1バイトの送信自体に約1msec +// (9600bps、スタートビット1、ストップビット1) で送信後に 1msec 空けるそう +// なので、ここでは単純に 2msec を1スロットとする。 +// マウスは 20msec ごとにサンプリングして連続する3バイトを送り、 +// キーボードは任意のスロットで 1バイト送ってくるので、概念的には +// 10スロットのうち 0, 1, 2 をマウスが、3..9 をキーボードが使用する +// ということにする。 + #include "lunakbd.h" +#include "mystring.h" #include "sio.h" // コンストラクタ LunaKeyboard::LunaKeyboard() { + monitor.Init(30, 1); + memset(reptable, 0, sizeof(reptable)); - // SIO へキー入力を通知するイベント - event.dev = gSIO; - event.func = (DeviceCallback_t)&SIODevice::RxCh1; - event.time = 0; - event.name = "KeyInput"; + key_event.dev = this; + key_event.func = (DeviceCallback_t)&LunaKeyboard::KeyboardCallback; + key_event.time = 0; + key_event.SetName("KeyInput"); + + // マウスイベント#0 はサンプリング用も兼ねているので常にオン + mouse_event[0].dev = this; + mouse_event[0].func = + (DeviceCallback_t)&LunaKeyboard::MouseSamplingCallback; + mouse_event[0].time = 20_msec; + mouse_event[0].SetName("Mouse Sampling"); + + 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].SetName(string_format("Mouse Slot%d", i)); + } } // デストラクタ @@ -23,6 +49,40 @@ LunaKeyboard::~LunaKeyboard() { } +// リセット +void +LunaKeyboard::ResetHard() +{ + // マウスサンプリングは電源オン時は有効らしい + gScheduler->AddEvent(&mouse_event[0]); +} + +// モニター +bool +LunaKeyboard::MonitorUpdate() +{ + int x, y; + monitor.Clear(); + + x = mouse_x; + y = mouse_y; + if (x < -999) + x = -999; + if (x > 999) + x = 999; + if (y < -999) + y = -999; + if (y > 999) + y = 999; + monitor.Print(0, 0, "Mouse: x=%-4d y=%-4d %c %c %c", + x, y, + (mouse_l ? 'L' : '-'), + (mouse_m ? 'M' : '-'), + (mouse_r ? 'R' : '-')); + + return true; +} + // キーを押した void LunaKeyboard::MakeKey(uint keycode) @@ -46,9 +106,8 @@ LunaKeyboard::MakeKey(uint keycode) putlog(1, "MakeKey %d(%s)", lunakey, keyname[lunakey]); reptable[lunakey] = 1; - // SIO に通知 - event.code = lunakey; - gScheduler->AddEvent(&event); + // イベントを登録 + SetKeyEvent(lunakey); } // キーを離した @@ -69,9 +128,139 @@ LunaKeyboard::BreakKey(uint keycode) putlog(2, "BreakKey %d(%s)", lunakey, keyname[lunakey]); reptable[lunakey] = 0; - // SIO に通知 - event.code = lunakey | 0x80; - gScheduler->AddEvent(&event); + // イベントを登録 + SetKeyEvent(lunakey | 0x80); +} + +// キー入力用の空きスロットにイベントをセット +void +LunaKeyboard::SetKeyEvent(uint code) +{ + uint64 now = gScheduler->GetVirtTime(); + + // t は現在の仮想時刻に対応するスロット + uint64 t = (now / 2_msec) % 10; + // n はその次のスロット + uint64 n = (t + 1) % 10; + + // マウススロットなら避ける + if (n < 3) + n = 3; + + // このイベントが起きる時間を再計算 + uint64 target = (now / 20_msec) * 20_msec + n * 2_msec; + if (n < t) { + // n が折り返している場合は n は次の20msec 枠内 + target += 20_msec; + } + + // イベントを登録。 + if (key_sending == false) { + key_sending = true; + key_event.time = target - now; + key_event.code = code; + gScheduler->AddEvent(&key_event); + } +} + +// マウス入力 +void +LunaKeyboard::MouseInput(int x, int y, bool rb, bool lb, bool mb) +{ + // ホストからマウスイベントのたびに呼ばれるので、ここで積算。 + // 20msec ごとのサンプリングのタイミングで積算結果を処理する。 + // LUNA のマウスの Y 方向は上に動く方向がプラスなので、ホスト入力とは逆。 + mouse_x += x; + mouse_y -= y; + mouse_r = rb; + mouse_l = lb; + mouse_m = mb; +} + +// キー入力コールバック +// SIO に送信するタイミングで呼ばれる。 +void +LunaKeyboard::KeyboardCallback(int code) +{ + gSIO->Rx(1, code); + key_sending = false; +} + +// マウスサンプリングコールバック +// 20msec ごとに呼ばれる。 +void +LunaKeyboard::MouseSamplingCallback(int arg) +{ + int reset_x = 0; + int reset_y = 0; + + // 移動量±1は送信しないが次回に向けた積算は続けるそうだ。 + if (mouse_x == -1 || mouse_x == 1) { + reset_x = mouse_x; + mouse_x = 0; + } + if (mouse_y == -1 || mouse_y == 1) { + reset_y = mouse_y; + mouse_y = 0; + } + + // 移動量が±1 を超えるかボタンがどれかでも変化すれば送信。 + if (mouse_x != 0 || + mouse_y != 0 || + mouse_r != prev_r || + mouse_l != prev_l || + mouse_m != prev_m) + { + // 1バイト目、ボタン状態。 + // mouse_[rlm] 変数は押し下げが true、LUNA のマウスは押し下げが %0 + uint8 b = 0x80 | + (mouse_l ? 0 : 0x04) | + (mouse_m ? 0 : 0x02) | + (mouse_r ? 0 : 0x01); + + // 2バイト目が X、3バイト目が Y。 + // 移動量は絶対値 0x7f でサチる (signed char ではない)。 + if (mouse_x < -127) + mouse_x = -127; + if (mouse_x > 127) + mouse_x = 127; + + if (mouse_y < -127) + mouse_y = -127; + if (mouse_y > 127) + mouse_y = 127; + + // 1バイト目はここで送信 + gSIO->Rx(1, b); + + // 2バイト目は 2msec 秒後に予約 + mouse_event[1].time = 2_msec; + mouse_event[1].code = (uint8)mouse_x; + gScheduler->AddEvent(&mouse_event[1]); + + // 3バイト目は 4msec 秒後に予約 + mouse_event[2].time = 4_msec; + mouse_event[2].code = (uint8)mouse_y; + gScheduler->AddEvent(&mouse_event[2]); + } + + // 積算リセット + mouse_x = reset_x; + mouse_y = reset_y; + prev_r = mouse_r; + prev_l = mouse_l; + prev_m = mouse_m; + + // 20msec 後に再び自分を呼び出す + mouse_event[0].time = 20_msec; + gScheduler->AddEvent(&mouse_event[0]); +} + +// マウスの後続バイトを送出するイベントコールバック +void +LunaKeyboard::MouseCallback(int code) +{ + gSIO->Rx(1, code); } // 共通キーコードを LUNA キーコードに変換して返す。