--- nono/vm/keyboard.cpp 2026/04/29 17:05:06 1.1.1.9 +++ nono/vm/keyboard.cpp 2026/04/29 17:05:51 1.1.1.17 @@ -4,6 +4,10 @@ // Licensed under nono-license.txt // +// +// キーボード (基本クラス) +// + // 物理キーボードとソフトウェアキーボードについて // // 物理キーボード入力 ---------->+ @@ -83,9 +87,8 @@ // v // MakeKey()/BreakKey() : // | -// | inputqueue + : -// | keyinput_event -// | - - - - - - - - - - - -> KeyInputCallback() +// | KEY_INPUT Message : +// | - - - - - - - - - - - -> KeyInputMessage() // o | // : MD::KeyInput() // | @@ -96,20 +99,38 @@ // 機種ごとに接続先に送信 #include "keyboard.h" +#include "config.h" +#include "mainapp.h" #include "scheduler.h" +#include "syncer.h" #include "uimessage.h" +#include + +// +// 文字入力しか持たないキーボードの下位クラス +// + +// コンストラクタ +DumbKeyboard::DumbKeyboard() + : inherited(OBJ_KEYBOARD) +{ +} + +// デストラクタ +DumbKeyboard::~DumbKeyboard() +{ +} + -std::unique_ptr gKeyboard; +// +// 通常のキーボード +// // コンストラクタ 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"); } // デストラクタ @@ -117,42 +138,88 @@ Keyboard::~Keyboard() { } -// リセット +// 初期化 +bool +Keyboard::Init() +{ + // LUNA ならスケジューラとの連携が必要。 + // キーリピートをソフトウェアで処理しないといけないため。 + if (gMainApp.Has(VMCap::LUNA)) { + syncer = GetSyncer(); + } + + uimessage = gMainApp.GetUIMessage(); + + 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(); + } + + // キー通知 + SyncKeyPressed(Any()); + } - inputqueue.Clear(); + // 内部状態をリセット sendqueue.Clear(); +} + +// 電源オフ +void +Keyboard::PowerOff() +{ + // LED は全部消灯 + std::fill(led.begin(), led.end(), false); + + // キーボードから手を離したことにする std::fill(pressed.begin(), pressed.end(), false); - active = 0; + // キーが全部解放されたので通知 - gScheduler->RequestKeyPressed(false); + SyncKeyPressed(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); } // 文字による入力 @@ -190,26 +257,95 @@ 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; + } +} + +// キーボードの接続処理の実体。 +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); +} + +// キーの押し下げ状態を設定する。 +// 初期化時以外で pressed を変更するときはこの関数を使用すること。 +void +Keyboard::SetPressed(uint keycode, bool ismake) +{ + // キーがどれか1つでも押されてる間は VM の高速実行を抑制する。 + // すべてのキーが離されたら高速モードを許可する。 + if (ismake) { + bool prev = Any(); + pressed[keycode] = true; + if (prev == false) { + SyncKeyPressed(true); + } + } else { + pressed[keycode] = false; + if (Any() == false) { + SyncKeyPressed(false); + } + } +} + +// LUNA なら、キーの押し下げ状態 (いずれかでも押し下げなら true) を +// syncer にも通知する。 +void +Keyboard::SyncKeyPressed(bool any_pressed) +{ + if (syncer) { + syncer->RequestKeyPressed(any_pressed); } } // キー入力を1つ処理する。 // keystat が押下・開放されたキーの物理的な状態。 -// キーを破棄したら false を返す。 +// キー入力を受け取らずに破棄した場合は false を返す。 bool Keyboard::KeyInputCommon(uint keystat) { @@ -233,7 +369,7 @@ Keyboard::KeyInputCommon(uint keystat) if (loglevel >= 1) { std::string msg; - msg += string_format("%d(", keycode); + msg += string_format("%u(", keycode); msg += GetKeyName(keycode); msg += ")"; @@ -258,14 +394,7 @@ Keyboard::KeyInputCommon(uint keystat) } } - // キーが押されてる間は VM の高速実行を抑制する - // すべてのキーが離されたら高速モードを許可する - pressed[keycode] = ismake; - if (ismake) { - Acquire(); - } else { - Release(); - } + SetPressed(keycode, ismake); // キーの状態が変わったので UI に通知。 // KC_FLAG_POST が立っていればキーコードも送信 (char 入力モード用)。 @@ -278,13 +407,18 @@ Keyboard::KeyInputCommon(uint keystat) } else { arg = 0; } - UIMessage::Post(UIMessage::KEYBOARD, arg); + uimessage->Post(UIMessage::KEYBOARD, arg); // 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) { // 送信キューに登録 @@ -299,24 +433,12 @@ Keyboard::KeyInputCommon(uint keystat) return true; } -// アクティブカウンタを 1 増やす。 -// 0 -> 1 の遷移でスケジューラに通知する。 -void -Keyboard::Acquire() -{ - if (active++ == 0) { - gScheduler->RequestKeyPressed(true); - } -} - -// アクティブカウンタを 1 減らす。 -// 1 -> 0 の遷移でスケジューラに通知する。 -void -Keyboard::Release() +// キーが1つでも押されていれば true を返す。 +bool +Keyboard::Any() const { - if (--active == 0) { - gScheduler->RequestKeyPressed(false); - } + auto it = std::find(pressed.begin(), pressed.end(), true); + return (it != pressed.end()); } // キー名を返す (主にデバッグ用) @@ -336,7 +458,7 @@ Keyboard::GetKeyName(uint keycode) Keyboard::keyname { "(none)", // 00 "BREAK", // 01 - "COPY", // 02 + NULL, // 02 "romaji", // 03 "code", // 04 "kigou", // 05 @@ -354,7 +476,7 @@ Keyboard::keyname { "ESC", // 10 "BS", // 11 "enter", // 12 - "INS", // 13 + NULL, // 13 "space", // 14 "DEL", // 15 "XFER", // 16 @@ -368,8 +490,8 @@ Keyboard::keyname { "right", // 1e "down", // 1f - "ROLLUP", // 20 - "ROLLDOWN", // 21 + "INS", // 20 + "COPY", // 21 "1", // 22 "2", // 23 "3", // 24 @@ -383,10 +505,10 @@ Keyboard::keyname { "minus", // 2c "circum", // 2d "backslash", // 2e - NULL, // 2f + "", // 2f - "OPT1", // 30 - "OPT2", // 31 + "CUT", // 30 + "PASTE", // 31 "Q", // 32 "W", // 33 "E", // 34 @@ -402,7 +524,7 @@ Keyboard::keyname { "hira", // 3e "zenkaku", // 3f - "HOME", // 40 + NULL, // 40 "UNDO", // 41 "A", // 42 "S", // 43 @@ -416,8 +538,8 @@ Keyboard::keyname { "semicolon", // 4b "colon", // 4c "bracketright", // 4d - NULL, // 4e - NULL, // 4f + "OPT1", // 4e + "OPT2", // 4f "XF1", // 50 "XF2", // 51 @@ -436,7 +558,7 @@ Keyboard::keyname { "XF4", // 5e "XF5", // 5f - NULL, // 60 + "HOME", // 60 "PAD_plus", // 61 "PAD_minus", // 62 "PAD_7", // 63 @@ -453,8 +575,8 @@ Keyboard::keyname { "PAD_enter", // 6e "PAD_CLR", // 6f - NULL, // 70 - NULL, // 71 + "ROLLUP", // 70 + "ROLLDOWN", // 71 "F1", // 72 "F2", // 73 "F3", // 74