--- nono/vm/keyboard.cpp 2026/04/29 17:05:03 1.1.1.8 +++ nono/vm/keyboard.cpp 2026/04/29 17:05:34 1.1.1.14 @@ -4,6 +4,10 @@ // Licensed under nono-license.txt // +// +// キーボード (基本クラス) +// + // 物理キーボードとソフトウェアキーボードについて // // 物理キーボード入力 ---------->+ @@ -62,26 +66,70 @@ // 物理 [SHIFT] キーを押し続けたまま、ソフトウェアキーボードから左クリック // (押下後即開放) すると、物理キーを押しているにも関わらず [SHIFT] キーは // 開放される。これは上述の通り仕様である。 +// +// LUNA のキーボードは左右の SHIFT キーの区別がある。このため、 LUNA は +// 特別処理をする必要はない。 +// ただしゲスト上の入力ルーチンがこの区別をしているかは別問題である。 -// LUNA のキーボードは左右の SHIFT キーの区別がある。このため、 LUNA は -// 特別処理をする必要はない。 -// ただしゲスト上の入力ルーチンがこの区別をしているかは別問題である。 +// 処理の流れ (SendStart まで) +// +// MakeKey(), BreakKey() までは基本的に VM スレッドに処理を渡すだけ。 +// +// UI スレッド : VM スレッド +// +// キャラクタ入力 : +// | +// 直接キー入力 | : +// | v +// | CharInput() : +// | | +// +------------+ : +// v +// MakeKey()/BreakKey() : +// | +// | KEY_INPUT Message : +// | - - - - - - - - - - - -> KeyInputMessage() +// o | +// : MD::KeyInput() +// | +// : KeyInputCommon() +// | +// : MD::SendStart() +// v +// 機種ごとに接続先に送信 #include "keyboard.h" +#include "config.h" #include "scheduler.h" +#include "syncer.h" #include "uimessage.h" +#include -std::unique_ptr gKeyboard; +// +// 文字入力しか持たないキーボードの下位クラス +// + +// コンストラクタ +DumbKeyboard::DumbKeyboard() + : inherited(OBJ_KEYBOARD) +{ +} + +// デストラクタ +DumbKeyboard::~DumbKeyboard() +{ +} + + +// +// 通常のキーボード +// // コンストラクタ Keyboard::Keyboard() - : inherited("Keyboard") + : inherited() { pressed.resize(KC_max); - - keyinput_event.func = (DeviceCallback_t)&Keyboard::KeyInputCallback; - keyinput_event.time = 0; - keyinput_event.SetName("Keyboard Input"); } // デストラクタ @@ -89,42 +137,86 @@ Keyboard::~Keyboard() { } -// リセット +// 初期化 +bool +Keyboard::Init() +{ + if (inherited::Init() == false) { + return false; + } + + syncer = GetSyncer(); + + scheduler->ConnectMessage(MessageID::KEY_INPUT, this, + ToMessageCallback(&Keyboard::KeyInputMessage)); + return true; +} + +// 電源オン/リセット void -Keyboard::ResetHard() +Keyboard::ResetHard(bool poweron) { - // 内部状態を全部リセット + if (poweron) { + is_connected = gConfig->Find("keyboard-connect").AsInt(); + + if (is_connected) { + DoConnect(); + } else { + DoDisconnect(); + } + + // キー通知 + syncer->RequestKeyPressed(Any()); + } - inputqueue.Clear(); + // 内部状態をリセット sendqueue.Clear(); +} + +// 電源オフ +void +Keyboard::PowerOff() +{ + // LED は全部消灯 + std::fill(led.begin(), led.end(), false); + + // キーボードから手を離したことにする std::fill(pressed.begin(), pressed.end(), false); - num_of_pressed = 0; + // キーが全部解放されたので通知 - gScheduler->RequestKeyPressed(false); + syncer->RequestKeyPressed(false); + + DoDisconnect(); +} + +// キーボードを接続する (外部から呼ばれる) +void +Keyboard::Connect(bool connect) +{ + uint code = connect ? CC_connect : CC_disconnect; + + // ここは UI スレッドからも呼ばれるのでメッセージを送る + scheduler->SendMessage(MessageID::KEY_INPUT, code); } // キーを押した (外部から、共通キーコードで呼ばれる) void Keyboard::MakeKey(uint keycode) { - // ここは UI スレッドからも呼ばれるのでキューに突っ込むだけ - { - std::unique_lock lock(mtx); - inputqueue.Enqueue(keycode | KC_FLAG_MAKE); - } - keyinput_event.Start(); + keycode |= KC_FLAG_MAKE; + + // ここは UI スレッドからも呼ばれるのでメッセージを送る + scheduler->SendMessage(MessageID::KEY_INPUT, keycode); } // キーを離した (外部から、共通キーコードで呼ばれる) void Keyboard::BreakKey(uint keycode) { - // ここは UI スレッドからも呼ばれるのでキューに突っ込むだけ - { - std::unique_lock lock(mtx); - inputqueue.Enqueue(keycode | KC_FLAG_BREAK); - } - keyinput_event.Start(); + keycode |= KC_FLAG_BREAK; + + // ここは UI スレッドからも呼ばれるのでメッセージを送る + scheduler->SendMessage(MessageID::KEY_INPUT, keycode); } // 文字による入力 @@ -162,34 +254,74 @@ Keyboard::CharInput(uint charcode) } } -// キー入力コールバック +// キー入力メッセージコールバック void -Keyboard::KeyInputCallback(Event& ev) +Keyboard::KeyInputMessage(MessageID msgid, uint32 arg) { - for (;;) { - uint keystat; - { - std::unique_lock lock(mtx); - if (inputqueue.Empty()) { - return; - } - keystat = inputqueue.Dequeue(); - } + uint keystat = arg; + switch (keystat) { + case CC_connect: + DoConnect(); + break; + case CC_disconnect: + DoDisconnect(); + break; + default: KeyInput(keystat); + break; } } -// キー入力を1つ処理する -// キーを送信したら true を返す +// キーボードの接続処理の実体。 +void +Keyboard::DoConnect() +{ + is_connected = true; + + MDConnect(); + + // LED は全部消灯で初期化 + std::fill(led.begin(), led.end(), false); + + // 接続されたので押しっぱなしになっているキーをスキャンして送る + for (int i = 0; i < pressed.size(); i++) { + if (pressed[i]) { + KeyInput(i | KC_FLAG_BREAK); + KeyInput(i); + } + } + + // UI に通知する + UIMessage::Post(UIMessage::KEYBOARD); +} + +void +Keyboard::DoDisconnect() +{ + is_connected = false; + + // 電源が切れたので LED は全部消す + std::fill(led.begin(), led.end(), false); + + MDDisconnect(); + + // UI に通知する + UIMessage::Post(UIMessage::KEYBOARD); +} + +// キー入力を1つ処理する。 +// keystat が押下・開放されたキーの物理的な状態。 +// キー入力を受け取らずに破棄した場合は false を返す。 bool Keyboard::KeyInputCommon(uint keystat) { uint keycode = KC_CODE(keystat); bool ismake = KC_IS_MAKE(keystat); + uint ledflag = KC_FLAG_LED(keystat); // 対応する機種別キーコードがなければ無視。 // (ここでは調べるだけでまだ変換はしない) - if (KeyToMDKey(keycode) == 0) { + if (KeyToMDKey(keystat) == 0) { return false; } @@ -200,29 +332,54 @@ Keyboard::KeyInputCommon(uint keystat) // 処理することが確定したのでログ // (離すほうは押すほうよりログレベルを1つ上げてある) - if (ismake) { - putlog(1, "MakeKey %d(%s)", keycode, GetKeyName(keycode).c_str()); - } else { - putlog(2, "BreakKey %d(%s)", keycode, GetKeyName(keycode).c_str()); + if (loglevel >= 1) { + std::string msg; + + msg += string_format("%u(", keycode); + msg += GetKeyName(keycode); + msg += ")"; + + if (ledflag) { + // LUNA の LED キーの場合、sendstat も明示 + if (ledflag == KC_FLAG_LED_DROP) { + msg += ", drop"; + } else { + msg += ", send "; + if (ledflag == KC_FLAG_LED_MAKE) { + msg += "Make"; + } else { + msg += "Break"; + } + } + } + + if (ismake) { + putlogn( "MakeKey %s", msg.c_str()); + } else { + putlog(2, "BreakKey %s", msg.c_str()); + } } - // キーが押されてる間は VM の高速実行を抑制する - // すべてのキーが離されたら高速モードを許可する - pressed[keycode] = ismake; + // キーがどれか1つでも押されてる間は VM の高速実行を抑制する。 + // すべてのキーが離されたら高速モードを許可する。 if (ismake) { - if (num_of_pressed++ == 0) { - gScheduler->RequestKeyPressed(true); + bool prev = Any(); + pressed[keycode] = true; + if (prev == false) { + syncer->RequestKeyPressed(true); } } else { - if (--num_of_pressed == 0) { - gScheduler->RequestKeyPressed(false); + pressed[keycode] = false; + if (Any() == false) { + syncer->RequestKeyPressed(false); } } - // UI に状態変更を通知。 + // キーの状態が変わったので UI に通知。 // KC_FLAG_POST が立っていればキーコードも送信 (char 入力モード用)。 // 通知を受け取った UI 側が IsPressed() などでキーの現状を調べることが // あるため pressed[] 更新後に通知する。 + // XXX LED 変更が間に合わない (see lunakbd.cpp) uint arg; if (KC_IS_POST(keystat)) { arg = keycode; @@ -231,14 +388,38 @@ Keyboard::KeyInputCommon(uint keystat) } UIMessage::Post(UIMessage::KEYBOARD, arg); - // 送信キューに登録 - sendqueue.Enqueue(keystat); + // LUNA の LED キー消灯はキー開放を送信 + if (ledflag == KC_FLAG_LED_BREAK) { + keystat |= KC_FLAG_BREAK; + } + + // キーボードが接続されていなければここまで処理して false を返す + if (is_connected == false) { + return false; + } + + if (ledflag != KC_FLAG_LED_DROP) { + // 送信キューに登録 + + // ここでは変換できるはず + uint mdkey = KeyToMDKey(keystat); + sendqueue.Enqueue(mdkey); + + // 機種依存のキー送信処理 + SendStart(); + } - // 機種依存のキー送信処理 - SendStart(); return true; } +// キーが1つでも押されていれば true を返す。 +bool +Keyboard::Any() const +{ + auto it = std::find(pressed.begin(), pressed.end(), true); + return (it != pressed.end()); +} + // キー名を返す (主にデバッグ用) const std::string Keyboard::GetKeyName(uint keycode) @@ -262,7 +443,7 @@ Keyboard::keyname { "kigou", // 05 "touroku", // 06 "HELP", // 07 - "HOME", // 08 + "SHIFT", // 08 "TAB", // 09 "CTRL", // 0a "kana", // 0b @@ -303,7 +484,7 @@ Keyboard::keyname { "minus", // 2c "circum", // 2d "backslash", // 2e - "UNDO", // 2f + "", // 2f "OPT1", // 30 "OPT2", // 31 @@ -322,8 +503,8 @@ Keyboard::keyname { "hira", // 3e "zenkaku", // 3f - NULL, // 40 - NULL, // 41 + "HOME", // 40 + "UNDO", // 41 "A", // 42 "S", // 43 "D", // 44 @@ -339,8 +520,8 @@ Keyboard::keyname { NULL, // 4e NULL, // 4f - NULL, // 50 - NULL, // 51 + "XF1", // 50 + "XF2", // 51 "Z", // 52 "X", // 53 "C", // 54 @@ -352,9 +533,9 @@ Keyboard::keyname { "period", // 5a "slash", // 5b "underscore", // 5c - NULL, // 5d - NULL, // 5e - NULL, // 5f + "XF3", // 5d + "XF4", // 5e + "XF5", // 5f NULL, // 60 "PAD_plus", // 61 @@ -389,13 +570,6 @@ Keyboard::keyname { "PAD_divide", // 7d "PAD_equal", // 7e "PAD_comma", // 7f - - "XF1", // 80 - "XF2", // 81 - "XF3", // 82 - "XF4", // 83 - "XF5", // 84 - "SHIFT", // 85 }; // 文字対キーストローク表 @@ -548,7 +722,7 @@ Keyboard::char_key = { KC_F7, // 0x87 CC_F7 KC_F8, // 0x88 CC_F8 KC_F9, // 0x89 CC_F9 - KC_F10, // 0x8a CC_Fa + KC_F10, // 0x8a CC_F10 0, // 0x8b KC_up, // 0x8c CC_up KC_left, // 0x8d CC_left