--- nono/vm/lunakbd.cpp 2026/04/29 17:04:28 1.1 +++ nono/vm/lunakbd.cpp 2026/04/29 17:04:42 1.1.1.6 @@ -1,21 +1,49 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // +// キーボードとマウス +// +// キーボード/マウスからシリアルポートへの入力は、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_size = nnSize(30, 1); + memset(reptable, 0, sizeof(reptable)); + led.resize(2); - // 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 = 2_msec * i; + mouse_event[i].SetName(string_format("Mouse Slot%d", i)); + } } // デストラクタ @@ -23,6 +51,57 @@ LunaKeyboard::~LunaKeyboard() { } +// リセット +void +LunaKeyboard::ResetHard() +{ + // 動作中のタイマーは一旦とめる + key_event.Stop(); + for (int i = 0; i < countof(mouse_event); i++) { + mouse_event[i].Stop(); + } + + // で、マウスサンプリングは電源オン時から有効らしい + mouse_event[0].Start(); +} + +// モニター +void +LunaKeyboard::MonitorUpdate(TextScreen& monitor) +{ + 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' : '-')); +} + +// LUNA キーコードから LED 番号を返す。LED キーでなければ -1 を返す。 +int +LunaKeyboard::LunaKey2LED(uint lunakey) const +{ + // 2つしかないのでこのくらいでいいか + if (lunakey == LunaKey_kana) + return 0; + if (lunakey == LunaKey_CAPS) + return 1; + + return -1; +} + // キーを押した void LunaKeyboard::MakeKey(uint keycode) @@ -42,13 +121,19 @@ LunaKeyboard::MakeKey(uint keycode) return; } + // LED はトグル? + int n = LunaKey2LED(lunakey); + if (n >= 0) { + led[n] = !led[n]; + } + // キーが押された putlog(1, "MakeKey %d(%s)", lunakey, keyname[lunakey]); reptable[lunakey] = 1; - // SIO に通知 - event.code = lunakey; - gScheduler->AddEvent(&event); + // イベントを登録 + // (LED キーの自動 Break はイベントコールバック側で処理する) + SetKeyEvent(lunakey); } // キーを離した @@ -65,13 +150,173 @@ LunaKeyboard::BreakKey(uint keycode) return; } + // LED キーはここでは処理しない (MakeKey() で処理してある) + if (LunaKey2LED(lunakey) >= 0) { + return; + } + + BreakKeyCommon(lunakey); +} + +// キーを離した (通常キーと LED キーの場合の両方から呼ばれる) +void +LunaKeyboard::BreakKeyCommon(uint lunakey) +{ // キーが離された 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; + key_event.Start(); + } +} + +// マウス入力 +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(Event& ev) +{ + int& code = ev.code; + + gSIO->Rx(1, code); + key_sending = false; + + // かなと CAP キーの押下なら、続けてキーの復旧も送る。 + if (LunaKey2LED(code) >= 0) { + BreakKeyCommon(code); + } +} + +// マウスサンプリングコールバック +// 20msec ごとに呼ばれる。 +void +LunaKeyboard::MouseSamplingCallback(Event& ev) +{ + 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) + { + 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 | + (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].code = (uint8)mouse_x; + mouse_event[1].Start(); + + // 3バイト目は 4msec 秒後に送信 + mouse_event[2].code = (uint8)mouse_y; + mouse_event[2].Start(); + } else { + putlog(3, "Mouse Sampling inactive"); + } + + // 積算リセット + mouse_x = reset_x; + mouse_y = reset_y; + prev_r = mouse_r; + prev_l = mouse_l; + prev_m = mouse_m; + + // 20msec 後に再び自分を呼び出す + ev.Start(); +} + +// マウスの後続バイトを送出するイベントコールバック +void +LunaKeyboard::MouseCallback(Event& ev) +{ + gSIO->Rx(1, ev.code); +} + +// ホストからの制御 +void +LunaKeyboard::Command(uint32 data) +{ } // 共通キーコードを LUNA キーコードに変換して返す。