|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// LUNA のキーボードとマウス
//
// キーボード/マウスからシリアルポートへの入力は、1バイトの送信自体に約1msec
// (9600bps、スタートビット1、ストップビット1) で送信後に 1msec 空けるそう
// なので、ここでは単純に 2msec を1スロットとする。
//
// ブザー発声はキー入力とは本来あまり関係ないが、ブザー発声中もキー入力中と
// 同様に高速走行を抑制したいため、キー扱いとしていることに留意。
#include "lunakbd.h"
#include "event.h"
#include "monitor.h"
#include "scheduler.h"
#include "sio.h"
#include "uimessage.h"
// コンストラクタ
LunaKeyboard::LunaKeyboard()
{
led.resize(2);
monitor = gMonitorManager->Regist(ID_MONITOR_KEYBOARD, this);
monitor->SetCallback(&LunaKeyboard::MonitorScreen);
monitor->SetSize(40, 4);
}
// デストラクタ
LunaKeyboard::~LunaKeyboard()
{
}
// 初期化
bool
LunaKeyboard::Init()
{
if (inherited::Init() == false) {
return false;
}
// LUNA-88K は左 [SHIFT] が 0x0d、右 [SHIFT] が 0x0c。
if (gMainApp.IsLUNA88K()) {
keycode2lunakey_table[0x0c] = LunaKey_SHIFT_R;
keycode2lunakey_table[0x0d] = LunaKey_SHIFT_L;
}
sio = GetSIODevice();
auto evman = GetEventManager();
event = evman->Regist(this,
ToEventCallback(&LunaKeyboard::Callback),
"Keyboard/Mouse");
return true;
}
// キーボード接続時の MD 固有処理
void
LunaKeyboard::MDConnect()
{
// 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;
// ブザー停止状態で初期化すると定義されている
BreakKey(KC_buzzer);
// デフォルトでマウス有効
MouseOn();
}
// キーボード取り外し時の MD 固有処理
void
LunaKeyboard::MDDisconnect()
{
// 動作中のタイマーは停止
scheduler->StopEvent(event);
// 電源が切れるのでブザー停止
BreakKey(KC_buzzer);
// マウスサンプリングは停止
MouseOff();
}
// モニター
void
LunaKeyboard::MonitorScreen(Monitor *, TextScreen& screen)
{
int x;
int y;
int mx;
int my;
screen.Clear();
y = 0;
// 0123456789012345678901234567890123456789
// LED: CAP Kana
// Buzzer: 150msec 6000Hz
// Mouse: sampling=off x=-999 y=-999 L M R
x = 8;
screen.Puts(0, y, "LED:");
screen.Puts(x, y, TA::OnOff(led[1]), "CAP");
screen.Puts(x + 4, y, TA::OnOff(led[0]), "Kana");
y++;
screen.Puts(0, y, "Buzzer:");
screen.Print(x, y, IsPressed(KC_buzzer) ? TA::Normal : TA::Disable,
"%umsec %uHz", buzzer_msec, buzzer_freq);
y++;
// マウスとの間を1行開ける
y++;
mx = mouse_x;
my = mouse_y;
if (mx < -999)
mx = -999;
if (mx > 999)
mx = 999;
if (my < -999)
my = -999;
if (my > 999)
my = 999;
screen.Puts(0, y, "Mouse:");
screen.Print(x, y, "sampling=%s", mouse_on ? "on" : "off");
screen.Print(x + 13, y, (mouse_on ? TA::Normal : TA::Disable),
"x=%-4d y=%-4d", mx, my);
if (mouse_on) {
screen.Puts(x + 27, y, TA::OnOff(mouse_l), "L");
screen.Puts(x + 29, y, TA::OnOff(mouse_m), "M");
screen.Puts(x + 31, y, TA::OnOff(mouse_r), "R");
} else {
screen.Puts(x + 27, y, TA::Disable, "L M R");
}
}
// 共通 keystat を LUNA キーコードに変換して返す。
// 対応する LUNA のキーがなければ 0 を返す。
uint
LunaKeyboard::KeyToMDKey(uint keystat) const
{
uint keycode = KC_CODE(keystat);
if (keycode >= countof(keycode2lunakey_table)) {
return 0;
}
uint mdkey = keycode2lunakey_table[keycode];
if (mdkey && KC_IS_BREAK(keystat)) {
mdkey |= 0x80;
}
return mdkey;
}
// シフト状態なら true を返す
bool
LunaKeyboard::IsShift() const
{
return pressed[KC_SHIFT_L] || pressed[KC_SHIFT_R];
}
// キー入力を1つ処理する
void
LunaKeyboard::KeyInput(uint keystat)
{
uint keycode = KC_CODE(keystat);
bool ismake = KC_IS_MAKE(keystat);
assert(keycode < KC_max);
// LUNA キーボードの LED キーは、
// LED 消灯中の押下で Make、点灯中の押下で Break を送り、
// LED キーの開放は VM に対しては何もしない。Issue#83
//
// LED ismake keystat 送信コード
// ------------ -------------
// off MAKE : MAKE MAKE キー押下、Make を入力 (LED を on)
// on MAKE : MAKE BREAK キー押下、Break を入力 (LED を off)
// off BREAK : BREAK none キー開放
// on BREAK : BREAK none キー開放
int n = Keycode2LED(keycode);
if (n >= 0) {
if (ismake) {
keystat |= (led[n] ? KC_FLAG_LED_BREAK : KC_FLAG_LED_MAKE);
} else {
// Break 時はキー入力はしない (キーの開放処理自体は必要)
keystat |= KC_FLAG_LED_DROP;
}
}
// 共通処理へ
if (KeyInputCommon(keystat) == false) {
return;
}
// 処理できたらここで LED の状態を更新
if (n >= 0 && ismake) {
led[n] = !led[n];
// LED 状態が変更になったので全走査指示。
// KeyInputCommon() 内で一度ポストしてるので無駄気味だが仕方ない…
uimessage->Post(UIMessage::KEYBOARD);
}
// LUNA のキーボードはキーボード側でキーリピートしないので
// ここでは何もしなくてよい
}
// 共通キーコードから LED 番号を返す。LED キーでなければ -1 を返す。
int
LunaKeyboard::Keycode2LED(uint keycode) const
{
// 2つしかないのでこのくらいでいいか
if (keycode == KC_kana)
return 0;
if (keycode == KC_CAPS)
return 1;
return -1;
}
// LED 番号から共通キーコードを返す。LED 番号が不正なら KC_none を返す。
uint
LunaKeyboard::LED2Keycode(int ledcode) const
{
// 2つしかないのでこのくらいでいいか
if (ledcode == 0)
return KC_kana;
if (ledcode == 1)
return KC_CAPS;
return KC_none;
}
// マウス入力
void
LunaKeyboard::MouseInput(int x, int y, bool rb, bool lb, bool mb)
{
// ホストからマウスイベントのたびに呼ばれるので、ここで積算。
// 20msec ごとのサンプリングのタイミングで積算結果を処理する。
// LUNA のマウスの Y 方向は上に動く方向がプラスなので、ホスト入力とは逆。
if (mouse_on) {
mouse_x += x;
mouse_y -= y;
mouse_r = rb;
mouse_l = lb;
mouse_m = mb;
}
}
// キー送信を開始 (Keyboard から呼ばれる)
void
LunaKeyboard::SendStart()
{
// ここでは次の 2msec 単位にイベントコールバックを起動するだけ。
Start(2_msec);
}
// イベントを次の period nsec 単位後に開始。
void
LunaKeyboard::Start(uint64 period)
{
uint64 now = scheduler->GetVirtTime();
uint64 target = ((now / period) + 1) * period;
// XXX メンバの event.vtime 参照してるけどどうするかね
if (event->IsRunning() == false || target < event->vtime) {
event->time = target - now;
scheduler->RestartEvent(event);
}
}
// イベントコールバック。
// キー入力、マウス入力、ブザー出力のいずれかが発生している間 2msec ごとに
// 呼ばれる。
void
LunaKeyboard::Callback(Event *ev)
{
uint64 now = scheduler->GetVirtTime();
uint mdkey;
// マウスサンプリングオンなら 20msec ごとにサンプリングを行う。
if (mouse_on && (now / 2_msec) % 10 == 0) {
MouseSampling();
}
// キューから取り出す。ここで取り出せるのは MDKey またはマウスデータ
if (sendqueue.Dequeue(&mdkey)) {
// 送信
sio->Rx(1, mdkey);
}
if (IsPressed(KC_buzzer) && now > buzzer_end) {
// VM スレッド内なので BreakKey() ではなくこっちを直接呼ぶ
KeyInput(KC_buzzer | KC_FLAG_BREAK);
}
if (sendqueue.Empty() == false || IsPressed(KC_buzzer)) {
// 送信データが残っていれば次は 2msec 後
Start(2_msec);
} else if (mouse_on) {
// そうでなくて、マウスサンプリングがオンなら次の 20msec 単位後
Start(20_msec);
}
}
// マウスサンプリング
void
LunaKeyboard::MouseSampling()
{
// 移動量が±1 を超えるかボタンがどれかでも変化すれば送信。
if (mouse_x < -1 || mouse_x > 1 ||
mouse_y < -1 || mouse_y > 1 ||
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_l ? 'L' : '-'),
(mouse_m ? 'M' : '-'),
(mouse_r ? 'R' : '-'));
// 1バイト目、ボタン状態。
// mouse_[lmr] 変数は押し下げが 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;
// キューに入れておく
sendqueue.Enqueue(b);
sendqueue.Enqueue((uint8)mouse_x);
sendqueue.Enqueue((uint8)mouse_y);
// 積算リセット
mouse_x = 0;
mouse_y = 0;
prev_r = mouse_r;
prev_l = mouse_l;
prev_m = mouse_m;
}
}
// マウスサンプリングを ON にする
void
LunaKeyboard::MouseOn()
{
// 厳密なタイミングは分からないので、
// 実装都合で次のマウススロットからサンプリングを開始(再開)する。
mouse_on = true;
// 次の 20msec 単位後から開始
Start(20_msec);
}
// マウスサンプリングを OFF にする
void
LunaKeyboard::MouseOff()
{
mouse_on = false;
mouse_x = 0;
mouse_y = 0;
mouse_r = false;
mouse_l = false;
mouse_m = false;
prev_r = false;
prev_l = false;
prev_m = false;
}
// ?
void
LunaKeyboard::MouseSendStart()
{
}
static const uint buzzer_msec_table[] = { 40, 150, 400, 700 };
static const uint buzzer_freq_table[] = {
6000, 3000, 1500, 1000, 600, 300, 150, 100
};
// ホストからの制御
void
LunaKeyboard::Command(uint32 data)
{
switch (data & 0xe0) {
case 0x00: // LED 点灯コマンド
{
// 7 6 5 4 3 2 1 0
// +---+---+---+---+---+---+---+---+
// | 0 0 0 |LED| x x x |COD|
// +---+---+---+---+---+---+---+---+
// | +--- 0:かな 1:CAP
// +------------------- 0:消灯 1:点灯
uint code = data & 0x01;
uint on = data & 0x10;
led[code] = (bool)on;
uimessage->Post(UIMessage::KEYBOARD);
if (loglevel >= 1) {
const auto name = GetKeyName(LED2Keycode(code));
putlogn("Command $%02x LED $%x(%s) %s",
data, code, name.c_str(), (on ? "ON" : "OFF"));
}
break;
}
case 0x40: // ブザーコマンド
{
// 7 6 5 4 3 2 1 0
// +---+---+---+---+---+---+---+---+
// | 0 1 0 |T2 T1 |F3 F2 F1 |
// +---+---+---+---+---+---+---+---+
// | +------- 周波数
// +----------------- 時間
uint t = (data >> 3) & 3;
uint f = data & 7;
buzzer_msec = buzzer_msec_table[t];
buzzer_freq = buzzer_freq_table[f];
putlog(1, "Command $%02x Buzzer %umsec, %uHz",
data, buzzer_msec, buzzer_freq);
uint64 now = scheduler->GetVirtTime();
buzzer_end = now + msec_to_nsec(buzzer_msec);
// VM スレッド内なので MakeKey() ではなく KeyInput() を直接呼ぶ
KeyInput(KC_buzzer | KC_FLAG_MAKE);
Start(2_msec);
break;
}
case 0x20: // マウス OFF コマンド
// 7 6 5 4 3 2 1 0
// +---+---+---+---+---+---+---+---+
// | 0 0 1 | x x x x x |
// +---+---+---+---+---+---+---+---+
putlog(1, "Command $%02x Mouse Sampling Off", data);
MouseOff();
break;
case 0x60: // マウス ON コマンド
// 7 6 5 4 3 2 1 0
// +---+---+---+---+---+---+---+---+
// | 0 1 1 | x x x x x |
// +---+---+---+---+---+---+---+---+
putlog(1, "Command $%02x Mouse Sampling On", data);
MouseOn();
break;
default:
putlog(1, "Command $%02x Undefined", data);
break;
}
}
// キーコード変換表
// 共通キーコードから LUNA キーコードに変換する。
// ぶっちゃけた話共通キーコードは LUNA キーコードをベースにしているので、
// 穴がある以外は全部透過。
/*static*/ uint
LunaKeyboard::keycode2lunakey_table[KC_max] = {
NoKey, // [00]
NoKey, // [01]
NoKey, // [02]
NoKey, // [03]
NoKey, // [04]
NoKey, // [05]
NoKey, // [06]
NoKey, // [07]
NoKey, // [08]
LunaKey_TAB, // [09]
LunaKey_CTRL, // [0a]
LunaKey_kana, // [0b]
LunaKey_SHIFT_L, // [0c]
LunaKey_SHIFT_R, // [0d]
LunaKey_CAPS, // [0e]
LunaKey_SF, // [0f]
LunaKey_ESC, // [10]
LunaKey_BS, // [11]
LunaKey_enter, // [12]
NoKey, // [13]
LunaKey_space, // [14]
LunaKey_DEL, // [15]
LunaKey_XFER, // [16]
LunaKey_VALID, // [17]
LunaKey_PF11, // [18]
LunaKey_PF12, // [19]
LunaKey_PF13, // [1a]
LunaKey_PF14, // [1b]
LunaKey_up, // [1c]
LunaKey_left, // [1d]
LunaKey_right, // [1e]
LunaKey_down, // [1f]
LunaKey_INSERT, // [20]
LunaKey_COPY, // [21]
LunaKey_1, // [22]
LunaKey_2, // [23]
LunaKey_3, // [24]
LunaKey_4, // [25]
LunaKey_5, // [26]
LunaKey_6, // [27]
LunaKey_7, // [28]
LunaKey_8, // [29]
LunaKey_9, // [2a]
LunaKey_0, // [2b]
LunaKey_minus, // [2c]
LunaKey_circum, // [2d]
LunaKey_backslash, // [2e]
LunaKey_buzzer, // [2f]
LunaKey_CUT, // [30]
LunaKey_PASTE, // [31]
LunaKey_Q, // [32]
LunaKey_W, // [33]
LunaKey_E, // [34]
LunaKey_R, // [35]
LunaKey_T, // [36]
LunaKey_Y, // [37]
LunaKey_U, // [38]
LunaKey_I, // [39]
LunaKey_O, // [3a]
LunaKey_P, // [3b]
LunaKey_at, // [3c]
LunaKey_bracketleft, // [3d]
NoKey, // [3e]
NoKey, // [3f]
NoKey, // [40]
NoKey, // [41]
LunaKey_A, // [42]
LunaKey_S, // [43]
LunaKey_D, // [44]
LunaKey_F, // [45]
LunaKey_G, // [46]
LunaKey_H, // [47]
LunaKey_J, // [48]
LunaKey_K, // [49]
LunaKey_L, // [4a]
LunaKey_semicolon, // [4b]
LunaKey_colon, // [4c]
LunaKey_bracketright, // [4d]
NoKey, // [4e]
NoKey, // [4f]
NoKey, // [50]
NoKey, // [51]
LunaKey_Z, // [52]
LunaKey_X, // [53]
LunaKey_C, // [54]
LunaKey_V, // [55]
LunaKey_B, // [56]
LunaKey_N, // [57]
LunaKey_M, // [58]
LunaKey_comma, // [59]
LunaKey_period, // [5a]
LunaKey_slash, // [5b]
LunaKey_underscore, // [5c]
NoKey, // [5d]
NoKey, // [5e]
NoKey, // [5f]
LunaKey_PAD_HOME, // [60]
LunaKey_PAD_plus, // [61]
LunaKey_PAD_minus, // [62]
LunaKey_PAD_7, // [63]
LunaKey_PAD_8, // [64]
LunaKey_PAD_9, // [65]
LunaKey_PAD_4, // [66]
LunaKey_PAD_5, // [67]
LunaKey_PAD_6, // [68]
LunaKey_PAD_1, // [69]
LunaKey_PAD_2, // [6a]
LunaKey_PAD_3, // [6b]
LunaKey_PAD_0, // [6c]
LunaKey_PAD_decimal, // [6d]
LunaKey_PAD_enter, // [6e]
NoKey, // [6f]
NoKey, // [70]
NoKey, // [71]
LunaKey_F1, // [72]
LunaKey_F2, // [73]
LunaKey_F3, // [74]
LunaKey_F4, // [75]
LunaKey_F5, // [76]
LunaKey_F6, // [77]
LunaKey_F7, // [78]
LunaKey_F8, // [79]
LunaKey_F9, // [7a]
LunaKey_F10, // [7b]
LunaKey_PAD_multiply, // [7c]
LunaKey_PAD_divide, // [7d]
LunaKey_PAD_equal, // [7e]
LunaKey_PAD_comma, // [7f]
};
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.