--- nono/wx/wxsoftkey.cpp 2026/04/29 17:04:30 1.1.1.2 +++ nono/wx/wxsoftkey.cpp 2026/04/29 17:05:44 1.1.1.15 @@ -1,222 +1,335 @@ // // nono -// Copyright (C) 2019 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt +// + +// +// ソフトウェアキーボード // -#include "wxheader.h" -#include "wxcolor.h" #include "wxsoftkey.h" -#include "wxtextscreen.h" +#include "fontmanager.h" +#include "wxcolor.h" +#include "wxmainframe.h" +#include "wxmainview.h" +#include "wxuimessage.h" +#include "sram.h" // -// LUNA ソフトウェアキーボード (パネル) +// ソフトウェアキーボード (パネル) 共通部分 // +// エンター記号。 +// 描画の際にキー刻印文字列の半角文字数から描画幅を決定しており、 +// エンター記号は半角2文字幅で描いているので実際の文字列も2文字にしておく。 +// そうすると "\\n" が意味的にもちょうどいい。 +// これは幅を算出するのに使うだけで表示はされない。 +#define EnterMark "\\n" + // イベントテーブル -wxBEGIN_EVENT_TABLE(WXLunaSoftKeyPanel, inherited) - EVT_LEFT_DOWN(WXLunaSoftKeyPanel::OnLeftDown) - EVT_LEFT_UP(WXLunaSoftKeyPanel::OnLeftUp) - EVT_LEFT_DCLICK(WXLunaSoftKeyPanel::OnLeftDClick) - EVT_RIGHT_DOWN(WXLunaSoftKeyPanel::OnRightDown) - EVT_PAINT(WXLunaSoftKeyPanel::OnPaint) - EVT_SIZE(WXLunaSoftKeyPanel::OnSize) +wxBEGIN_EVENT_TABLE(WXSoftKeyPanel, inherited) + EVT_LEFT_DOWN(WXSoftKeyPanel::OnLeftDown) + EVT_LEFT_UP(WXSoftKeyPanel::OnLeftUp) + EVT_LEFT_DCLICK(WXSoftKeyPanel::OnLeftDClick) + EVT_RIGHT_DOWN(WXSoftKeyPanel::OnRightDown) + EVT_TIMER(wxID_ANY, WXSoftKeyPanel::OnTimer) wxEND_EVENT_TABLE() // コンストラクタ -WXLunaSoftKeyPanel::WXLunaSoftKeyPanel(wxWindow *parent) +WXSoftKeyPanel::WXSoftKeyPanel(wxWindow *parent, + const std::vector& keydata_, int u_width_, int u_height_) : inherited(parent) + , keydata(keydata_) { - // LUNA では - // BS キーの左隣 (0x5c) は円記号ではなくバックスラッシュ。 - // それを SHIFT したやつ(0x7c) は破線でないパイプ。 - // そのもう一つ左隣 (0x7e) はオーバーラインではなくチルダ。 - SetGlyph5C(true); - SetGlyph7E(true); + // デバイスを取得 + keyboard = GetKeyboard(); - DoSize(); + timer.SetOwner(this); + + u_width = u_width_; + u_height = u_height_; + + FontChanged(); + + // トグル動作にするキー + toggle.emplace(KC_SHIFT, 0); + toggle.emplace(KC_SHIFT_L, 0); + toggle.emplace(KC_SHIFT_R, 0); + toggle.emplace(KC_CTRL, 0); + + // 残光の初期化。 + // persistence は keydata 分の配列ではなく共通キーコード分の配列。 + persistence.resize(KC_max); + std::fill(persistence.begin(), persistence.end(), 0); + + // このパネルへのキー入力はメインビューへのキー入力と同じ。 + auto mainframe = dynamic_cast(GetParent()->GetParent()); + auto mainview = mainframe->GetMainView(); + Connect(wxEVT_CHAR, wxCharEventHandler(WXMainView::OnChar), NULL, + mainview); + Connect(wxEVT_KEY_UP, wxKeyEventHandler(WXMainView::OnKeyUp), NULL, + mainview); + Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(WXMainView::OnKeyDown), NULL, + mainview); + + // VM からの通知を受け取る + WXUIMessage::Connect(UIMessage::KEYBOARD, this, + wxCommandEventHandler(WXSoftKeyPanel::OnKeyChanged)); + + // 初期タイトルを保存しといて.. + title = dynamic_cast(parent)->GetTitle(); + // キーボードの接続状況でタイトルを更新する + UpdateTitle(); } // デストラクタ -WXLunaSoftKeyPanel::~WXLunaSoftKeyPanel() +WXSoftKeyPanel::~WXSoftKeyPanel() { + // ウィンドウクローズでトグルキーのワンショットは開放する + // (長押しは開放しない) + for (const auto& k : toggle) { + auto code = k.second; + if (code) { + KeyUp(code); + } + } + + timer.Stop(); + + WXUIMessage::Disconnect(UIMessage::KEYBOARD, this, + wxCommandEventHandler(WXSoftKeyPanel::OnKeyChanged)); } -// フォントサイズ変更 (外部インタフェース) +// キーボードの接続状況によって親フレームのタイトルを更新する。 +// ついでにメンバ変数を更新する。 void -WXLunaSoftKeyPanel::SetFontSize(fontsize_t new_fontsize) +WXSoftKeyPanel::UpdateTitle() { - // 同じなら何もしない - if (fontsize == new_fontsize) { - return; + auto parent = dynamic_cast(GetParent()); + assert(parent); + + // 次回比較用にメンバ変数に覚えておく + connected = keyboard->IsConnected(); + + if (connected) { + parent->SetTitle(title); + } else { + parent->SetTitle(title + _(" (disconnected)")); } +} + +// color.Keytop の初期化(補間)。 +// num はキートップの種類の数。1 か 2 +void +WXSoftKeyPanel::InitColorKeytop(int num) +{ + Color pressed = UD_BLUE; - // フォントを更新 - UpdateFont(new_fontsize); + auto f = [](int a, int b, int x) { + return a * x / 100 + b * (100 - x) / 100; + }; - // サイズ変更処理 - DoSize(); + // Keytop[i][9] は押されたときの色 + // Keytop[i][0] は押されていないときの色 + for (int i = 0; i < num; i++) { + for (int j = 1; j < 10; j++) { + int n = j * 100 / 9; + color.Keytop[i][j] = Color( + f(pressed.r, color.Keytop[i][0].r, n), + f(pressed.g, color.Keytop[i][0].g, n), + f(pressed.b, color.Keytop[i][0].b, n)); + } + } } -// サイズ変更処理 -// サイズを変更したい時に呼ぶ (サイズが変更されたらではない)。 -// ここではサイズは fontsize にのみ依存しているので、フォントサイズが -// 変化/確定した時に呼び出すこと。 +// フォントサイズ変更 void -WXLunaSoftKeyPanel::DoSize() +WXSoftKeyPanel::FontChanged() { - assert(fontsize != FONT_NONE); + inherited::FontChanged(); // このパネル内の描画の基本単位 unit = font_height / 4; // エンターキーだけ矩形でないのでここでリージョンを計算。 // unit が確定したところで一回行う。 + // ただし SHIFT_R のキーコードはちょっと使いづらいので、ここでは SHIFT_R + // を使わず [_] キーで代用する。 // - // | ^ | \ | BS | - // | | | | - // +---+---+--(A)--+------(B) - // | | | - // | [ | | - // | | Enter | - // +---+---+--(F)-(E) | - // | | | | - // | : | ] | | - // | | | | - // +---+---+------(D)-----(C) - // | | - // | SHIFT | + // | ^ | \ | BS | + // | | | | + // --+---+---+--(A)--+------(B) + // | | | + // | [ | Enter | + // | | | + // --+---+---+--(F)-(E) - - (G) + // | | | | + // | : | ] | | + // | | | | + // --+---+---+------(D)-----(C) + // | | + // _ | SHIFT | + // + // Enter キーの上半分 A-B-G-F を enter_top。F-G の線分を含む。 + // Enter キーの下半分 E-G-C-D を enter_btm。E-G の線分を含む。 const keydata_t& keybs = FindKeydata(KC_BS); const keydata_t& keyleft = FindKeydata(KC_bracketleft); const keydata_t& keyright = FindKeydata(KC_bracketright); - const keydata_t& keyshift = FindKeydata(KC_SHIFT_R); - // 各点のピクセル座標 - enter_points = { - { keyleft.right() * unit, keyleft.y * unit }, // A - { keybs.right() * unit, keyleft.y * unit }, // B - { keyshift.right() * unit, keyshift.y * unit }, // C - { keyright.right() * unit, keyshift.y * unit }, // D - { keyright.right() * unit, keyright.y * unit }, // E - { keyleft.right() * unit, keyright.y * unit }, // F - }; - // 当たり判定用のリージョン - enter_region = wxRegion(enter_points.size(), enter_points.data()); + const keydata_t& keyscore = FindKeydata(KC_underscore); + enter_top.SetLTRB( + keyleft.right() * unit, keyleft.y * unit, + keybs.right() * unit, keyright.y * unit); + enter_btm.SetLTRB( + keyright.right() * unit, keyright.y * unit, + keybs.right() * unit, keyscore.y * unit); + + // アイコンを変更。参照順序の関係で下のほうにおいてある…。 + InitIcon(); + + // バックバッファの下限を固定する + wxSize size(u_width * unit, u_height * unit); + SetMinBitmapSize(size); // コントロールの大きさを変更 - wxSize size(228 * unit, 54 * unit); - SetClientSize(size); + SetSize(size); SetMinSize(size); } -// サイズ変更イベント void -WXLunaSoftKeyPanel::OnSize(wxSizeEvent& event) +WXSoftKeyPanel::OnTimer(wxTimerEvent& event) { - // Mac ではウィンドウ作成時に 0 以下の値が来ることがある - // GTK では 1 で来ることがある - wxSize size = event.GetSize(); - if (size.x <= 1 || size.y <= 1) { - return; - } - - view_width = size.x; - view_height = size.y; - - // ビットマップを持っていれば一旦解放 - if (bitmap) { - delete bitmap; - bitmap = NULL; - } - if (image) { - delete image; - image = NULL; - } - if (bmpbuf) { - delete bmpbuf; - bmpbuf = NULL; - } - - // ビットマップ作成 - // 正しく描かれなかった時に分かりやすいよう初期値は濃いグレーにしておく - bmpbuf = new uint8 [view_width * view_height * 3]; - memset(bmpbuf, 0x70, view_width * view_height * 3); - image = new wxImage(view_width, view_height, bmpbuf, true); - bitmap = new wxBitmap(*image); - - // 再描画を指示 Refresh(); } -// 描画イベント (UI スレッドから呼ばれる) +// キー状態変更イベント (UIMessage) +// +// キー状態変更イベントは、キーの状態が変わるたびに都度通知される。 +// ソフトウェアキーボード入力とパススルーモードの入力では、どのキーに変化 +// (押下 or 開放)があったかの情報は付属しない。各キーの押下状態は全キーを +// 逐一調査することで判定する。人間時間で起きる事象なのでこの計算量は許容。 +// Char 入力モードの場合は、イベントのパラメータ引数 (GetInt() で取得可) に +// 入力のあったキーの共通キーコードが入っている (複数のキーが同時に押される +// 場合はキーの回数分のイベントが飛んでくる)。Char 入力モードにはキー開放 +// という概念はない。 void -WXLunaSoftKeyPanel::OnPaint(wxPaintEvent& event) +WXSoftKeyPanel::OnKeyChanged(wxCommandEvent& event) { - // メモリ DC に描画 - wxMemoryDC memDC; - memDC.SelectObject(*bitmap); - Draw(memDC); - - // 実画面 DC にコピー - wxPaintDC dstDC(this); - dstDC.Blit(0, 0, view_width, view_height, &memDC, 0, 0); + // Char 入力モードでキーが押された場合は、共通キーコードが送られてくるので、 + // persistence[] をここでセットする。 + // Char 入力モードでは、キーの Make/Break が一瞬で終わるため、このイベント + // を受け取って Refresh() を発行 -> OnPaint() -> Draw() 内で IsPressed() + // を調べた頃にはタイミング問題でもうキーが離されている可能性があるので、 + // 描画時に全キーを調べる方法は使えない。 + uint keycode = event.GetInt(); + if (keycode != 0) { + assert(keycode < persistence.size()); + // 9 が減光時の初期値 (10 は減光しない点灯) + persistence[keycode] = 9; + } + + // トグルキーが開放されていたらトグル情報をクリアする。 + // ハードウェアキーボードを含む VM 側で開放された場合と、 + // ソフトウェアキーボードの KeyUp() で開放された場合のどちらも + // ここでクリア。 + for (auto& k : toggle) { + if (keyboard->IsPressed(k.first) == false) { + k.second = 0; + } + } + + // 接続状況が変わっていたらタイトルを更新。 + if (keyboard->IsConnected() != connected) { + UpdateTitle(); + } + + Refresh(); } // 描画本体 void -WXLunaSoftKeyPanel::Draw(wxDC& dc) +WXSoftKeyPanel::Draw() { - wxColour fgcolor; // 前景色 - wxColour bgcolor; // 背景というかガワの色 - wxColour keydown_color = UD_BLUE; // キーが押された時の色 - - if (black_keyboard) { - fgcolor = *wxWHITE; - bgcolor = *wxBLACK; + bool timer_start = false; + + // キー刻印の表示更新用に修飾キーの状態を更新。 + // 本来は IsShift() だけで判定すべきだが、Char 入力モードでいずれかの + // SHIFT キーが減光中は SHIFT 刻印を継続してみるのはどうか。 + // 0 まで待つとうちの環境だと消灯した少し後に刻印が戻るように見えるため、 + // ある程度暗くなったところでもう切り替えてみる。閾値は適当。 + if ((2 < persistence[KC_SHIFT_L] && persistence[KC_SHIFT_L] < 10) || + (2 < persistence[KC_SHIFT_R] && persistence[KC_SHIFT_R] < 10) || + (2 < persistence[KC_SHIFT] && persistence[KC_SHIFT] < 10) || + keyboard->IsShift()) + { + modifier = ModShift; } else { - fgcolor = *wxBLACK; - bgcolor = GetTextBackColor(); + modifier = ModNormal; } - // 新品現物のガワは何色だったんだろう - dc.SetPen(wxPen(bgcolor)); - dc.SetBrush(wxBrush(bgcolor)); - dc.DrawRectangle(wxPoint(0, 0), GetSize()); + auto text_color = color.Text; + + // キーボードが接続されていないときは刻印をグレーにしておく + if (keyboard->IsConnected() == false) { + text_color = UD_GREY; + } + SetTextForeColor(text_color); - dc.SetPen(fgcolor); - SetTextForeColor(fgcolor); for (const auto& key : keydata) { - if (hit[key.keycode]) { - // 押されている - SetTextBackColor(keydown_color); - dc.SetBrush(keydown_color); + int c = persistence[key.keycode]; + if (0 < c && c < 10) { + // Char 入力モードで減光中 + c--; + timer_start = true; } else { - // 押されていない - const wxColour& keytop = GetTopColor(key); - SetTextBackColor(keytop); - dc.SetBrush(wxBrush(keytop)); + // そうでない場合に限り、現在の状態を問い合わせる + bool is_pressed = keyboard->IsPressed(key.keycode); + if (is_pressed) { + // 押されている + c = 10; + } else { + // 押されていない + c = 0; + } + } + // persistence[] は 10 が(減光しない)点灯、9..1 が減光中を示す。 + persistence[key.keycode] = c; + // ただし色配列は 0..9 で 9 が最大点灯色 + if (c > 9) { + c = 9; } - // エンターキーだけ枠の描き方が違うので別処理 - if (__predict_false(key.x < 0)) { - DrawEnterKey(dc); + SetTextBackColor(color.Keytop[key.coltype][c]); + + // エンターキーとブザー表示は描き方が違うので別処理 + if (__predict_false(key.keycode == KC_enter)) { + DrawEnterKey(); + } else if (__predict_false(key.keycode == KC_buzzer)) { + DrawBuzzer(key); } else { - DrawKey(dc, key); + DrawKey(key); } } - SetTextBackColor(bgcolor); - wxString utfstr(wxT("右クリックで長押し")); - const char *sjis = (const char *)utfstr.mb_str(conv); - DrawStringSJIS(dc, 190 * unit, 2 * unit, sjis, TA::Disable); + if (timer_start) { + timer.Start(25); + } else { + timer.Stop(); + } } // 指定のキーを一つ (枠から刻印まで) 描画する。 void -WXLunaSoftKeyPanel::DrawKey(wxDC& dc, const keydata_t& key) +WXSoftKeyPanel::DrawKey(const keydata_t& key) { + uint attr = TA::Normal; + // 枠 - wxPoint pt(key.x * unit, key.y * unit); - wxSize sz(key.w * unit + 1, key.h * unit + 1); - dc.DrawRectangle(pt, sz); + Rect rect( + key.x * unit, key.y * unit, + key.w * unit + 1, key.h * unit + 1); + bitmap.FillRect(palette[0], rect); + bitmap.DrawRect(color.Text, rect); // 刻印を選択 const char *disp = key.disp[modifier]; @@ -224,125 +337,232 @@ WXLunaSoftKeyPanel::DrawKey(wxDC& dc, co disp = key.disp[0]; } + // トグルキーのワンショット押しは太字で表示を区別したい + const auto it = toggle.find(key.keycode); + if (it != toggle.end()) { + if (it->second) { + attr = TA::Em; + } + } + // Shift_JIS に変換 wxString utfstr(disp, wxConvUTF8); - const char *sjis = (const char *)utfstr.mb_str(conv); - // Shift_JIS での文字数なので半角換算の文字数と一致する。 - int sjislen = strlen(sjis); + // mb_str() をキャストせずに一度保持しておく必要がある (scan-build 対策) + auto mbstr = utfstr.mb_str(conv); + const char *sjis = (const char *)mbstr; + // 改行があれば2行に分割 + char sjis1[8] {}; + char sjis2[8] {}; + int sjislen; + int row; + const char *lf = strchr(sjis, '\n'); + if (lf == NULL) { + // 1行なら、Shift_JIS での文字数は半角換算の文字数と一致する。 + sjislen = strlen(sjis); + row = 1; + } else { + // 2行に分割。センタリングは1行目の長さだけで調べる。 + strncpy(sjis1, sjis, lf - sjis); + strlcpy(sjis2, lf + 1, sizeof(sjis2)); + sjislen = strlen(sjis1); + row = 2; + } // センタリング int x = key.x + (key.w - sjislen * 2) / 2; - int y = key.y + (key.h - 4) / 2; - // 「CAP」と「KANA」は LED 付き - if (IsLEDKey(key.keycode)) { - bool led = (key.keycode == KC_CAPS) - ? led_cap - : led_kana; - - const wxPen oldpen = dc.GetPen(); - const wxBrush oldbrush = dc.GetBrush(); - if (led) { - // 点灯時は赤、枠は黒 - dc.SetPen(*wxBLACK); - dc.SetBrush(UD_RED); - } else { - // 消灯時は元のキートップの色で、枠は薄めにしたい - dc.SetPen(GetTextDisableColor()); - dc.SetBrush(GetTopColor(key)); - } - dc.DrawRectangle( - wxPoint(key.x * unit + 2, key.y * unit + 2), - wxSize(4 * unit - 2, 3 * unit - 2)); - dc.SetPen(oldpen); - dc.SetBrush(oldbrush); + int y = key.y + (key.h - row * 4) / 2; - // LED を描くため文字を少し下げる - y += 1; + // LED を描画 (機種によって色々違うので個別クラス側で処理) + if (key.led >= 0) { + y += DrawLED(key); } // 文字を描画 - pt = wxPoint(x * unit + 1, y * unit + 1); - if (key.keycode == KC_PAD_enter) { - DrawEnterMark(dc, pt); + x = x * unit + 1; + y = y * unit + 1; + if (strcmp(disp, EnterMark) == 0) { + bitmap.DrawBitmapI1(x, y, *enter_icon, palette); } else { - DrawStringSJIS(dc, pt.x, pt.y, sjis); + if (lf == NULL) { + // 1行の場合 + DrawStringSJIS(x, y, sjis, attr); + } else { + // 2行の場合 + DrawStringSJIS(x, y, sjis1); + y += 4 * unit; + DrawStringSJIS(x, y, sjis2); + } } } // エンターキー void -WXLunaSoftKeyPanel::DrawEnterKey(wxDC& dc) +WXSoftKeyPanel::DrawEnterKey() { - // エンターキーを描画 - dc.DrawPolygon(enter_points.size(), enter_points.data()); + // 矩形の内側を背景色で塗りつぶす。 + bitmap.FillRect(palette[0], enter_top); + bitmap.FillRect(palette[0], enter_btm); + + // 周囲の線分 + // (A)-(B) + bitmap.DrawLineH(color.Text, + enter_top.x, enter_top.y, enter_top.GetRight()); + // (B)-(C) + bitmap.DrawLineV(color.Text, + enter_top.GetRight(), enter_top.y, enter_btm.GetBottom()); + // (C)-(D) + bitmap.DrawLineH(color.Text, + enter_btm.GetRight(), enter_btm.GetBottom(), enter_btm.x); + // (D)-(E) + bitmap.DrawLineV(color.Text, + enter_btm.x, enter_btm.GetBottom(), enter_btm.y); + // (E)-(F) + bitmap.DrawLineH(color.Text, + enter_btm.x, enter_top.GetBottom(), enter_top.x); + // (F)-(A) + bitmap.DrawLineV(color.Text, + enter_top.x, enter_top.GetBottom(), enter_top.y); // エンター記号を描画 - DrawEnterMark(dc, wxPoint( - (enter_points[0].x + enter_points[1].x) / 2 - (4 / 2), - (enter_points[1].y + enter_points[2].y) / 2 - (4 / 2))); -} - -// 指定の位置にエンター記号を描画する (エンターキー全体の描画ではない)。 -// メインキーとテンキーの両方で同じ図柄を使う。 -// 大きさは (3unit, 4unit) なので実質全角1文字分くらい。 -void -WXLunaSoftKeyPanel::DrawEnterMark(wxDC& dc, const wxPoint& basep) -{ - // エンター記号は幅 3 unit、高さ 4 unit。 - // +0 +1 +2 +3 - // +0 .. .. .. 1. - // .. .. .. 1. - // +1 .. .. .. 1. - // .. .. .. 1. - // +2 .. 1. .. 1. - // .1 .. .. 1. - // +3 11 11 11 1. - // .1 .. .. .. - // +4 .. 1. .. .. - // .. .. .. .. - static const std::vector> entermark { - { 3, 0 }, - { 3, 3 }, // │ - { 0, 3 }, // ─ - { 1, 2 }, // / - { 0, 3 }, // (一筆書きなので戻る) - { 1, 4 }, // \ - }; - for (int i = 1; i < entermark.size(); i++) { - auto start = entermark[i - 1]; - auto end = entermark[i]; - wxPoint startp(start.first * unit, start.second * unit); - wxPoint endp(end.first * unit, end.second * unit); - dc.DrawLine(basep + startp, basep + endp); - } + bitmap.DrawBitmapI1( + (enter_top.x + enter_top.GetRight()) / 2 - (4 / 2), + (enter_top.y + enter_btm.GetBottom()) / 2 - (4 / 2), + *enter_icon, palette); } -// キートップの色を返す。 -const wxColour -WXLunaSoftKeyPanel::GetTopColor(const keydata_t& key) const -{ - // 濃いキートップに UD_GREY は濃すぎるので薄めた色を使う。 - // ここは濃淡で見分けを付けたいわけではなくてただのデザインなので。 - if (key.coltype == 0) { - // 薄いキートップ - if (black_keyboard) { - return wxColour(50, 50, 53); - } else { - return *wxWHITE; - } - } else { - // 濃いキートップ - if (black_keyboard) { - return *wxBLACK; - } else { - return wxColour(200, 200, 203); - } +// ブザー表示(キーに相当)を描画 +void +WXSoftKeyPanel::DrawBuzzer(const keydata_t& key) +{ + Color fg; + + // 枠。キーではないので薄くしておく。 + Rect rect( + key.x * unit, key.y * unit, + key.w * unit + 1, key.h * unit + 1); + bitmap.FillRect(palette[0], rect); + bitmap.DrawRect(UD_GREY, rect); + + // テキスト色は本来呼び出し元の DrawKey() で設定してあるが、 + // ここはキーではないので非発声時の文字色だけ薄くしたい。 + // 発声時はキー押下と同じにする。 + fg = GetTextForeColor(); + if (persistence[key.keycode] == 0) { + SetTextForeColor(UD_GREY); + } + + // 刻印 (モディファイヤの影響を受けないと分かっているので常に [0]) + const char *disp = key.disp[0]; + assert(disp); + + // センタリング + int x = key.x + (key.w - strlen(disp) * 2) / 2; + int y = key.y + (key.h - 4) / 2; + + // 文字を描画 + x = x * unit + 1; + y = y * unit + 1; + DrawStringSJIS(x, y, disp, TA::Normal); + + // 前景色を元に戻す + SetTextForeColor(fg); +} + +#define B(x) (0b##x) +#define B2(x) (B(x) >> 8), (B(x) & 0xff) +#define B3(x) (B(x) >> 16), ((B(x) >> 8) & 0xff), (B(x) & 0xff) + +static const uint8 EnterIcon12[] = { // 12x12 + B2(000000000000'0000), // 0 + B2(000000001110'0000), // 1 + B2(000000001010'0000), // 2 + B2(000000001010'0000), // 3 + B2(000000001010'0000), // 4 + B2(001100001010'0000), // 5 + B2(010111110010'0000), // 6 + B2(100000000010'0000), // 7 + B2(010111111100'0000), // 8 + B2(001100000000'0000), // 9 + B2(000000000000'0000), // a + B2(000000000000'0000), // b +}; + +static const uint8 EnterIcon16[] = { // 16x16 + B2(0000000000111110), // 0 + B2(0000000000100010), // 1 + B2(0000000000100010), // 2 + B2(0000000000100010), // 3 + B2(0000000000100010), // 4 + B2(0000000000100010), // 5 + B2(0000000000100010), // 6 + B2(0000110000100010), // 7 + B2(0001010000100010), // 8 + B2(0010011111000010), // 9 + B2(0100000000000010), // a + B2(1000000000000010), // b + B2(0100000000000010), // c + B2(0010011111111100), // d + B2(0001010000000000), // e + B2(0000110000000000), // f +}; + +static const uint8 EnterIcon24[] = { // 24x24 + B3(00000000'00000001'11111100), // 0 + B3(00000000'00000001'11111100), // 1 + B3(00000000'00000001'10001100), // 2 + B3(00000000'00000001'10001100), // 3 + B3(00000000'00000001'10001100), // 4 + B3(00000000'00000001'10001100), // 5 + B3(00000000'00000001'10001100), // 6 + B3(00000000'00000001'10001100), // 7 + B3(00000000'00000001'10001100), // 8 + B3(00000000'11000001'10001100), // 9 + B3(00000001'11000001'10001100), // 10 + B3(00000011'11000001'10001100), // 11 + B3(00000110'11000001'10001100), // 12 + B3(00001100'11111111'10001100), // 13 + B3(00011000'11111111'10001100), // 14 + B3(00110000'00000000'00001100), // 15 + B3(01100000'00000000'00001100), // 16 + B3(00110000'00000000'00001100), // 17 + B3(00011000'11111111'11111100), // 18 + B3(00001100'11111111'11111000), // 19 + B3(00000110'11000000'00000000), // 20 + B3(00000011'11000000'00000000), // 21 + B3(00000001'11000000'00000000), // 22 + B3(00000000'11000000'00000000), // 23 +}; + +// アイコンを初期化する。FontChanged() から呼ばれる。 +// ソース先頭のほうにデータの羅列を置きたくないが、 +// 容量不詳の配列の解決が面倒なのでこれだけ下のほうのここに置いてある。 +void +WXSoftKeyPanel::InitIcon() +{ + const int width = font_width; + const int height = font_height; + const uint8 *icon; + + switch (height) { + case 12: + icon = EnterIcon12; + break; + case 16: + icon = EnterIcon16; + break; + case 24: + icon = EnterIcon24; + break; + default: + PANIC("Unexpected font_height=%d", height); } + + enter_icon.reset(new BitmapI1(icon, width * 2, height)); } // keycode に対応する keydata を返す const keydata_t& -WXLunaSoftKeyPanel::FindKeydata(uint keycode) const +WXSoftKeyPanel::FindKeydata(uint keycode) const { for (const auto& key : keydata) { if (key.keycode == keycode) { @@ -350,59 +570,65 @@ WXLunaSoftKeyPanel::FindKeydata(uint key } } // 見付からないはずはない - warnx("keycode %d not found", keycode); - assert(true); - __unreachable(); + PANIC("keycode %d not found", keycode); } // マウス左クリック (Down) イベント void -WXLunaSoftKeyPanel::OnLeftDown(wxMouseEvent& event) +WXSoftKeyPanel::OnLeftDown(wxMouseEvent& event) { - wxPoint pt = event.GetPosition(); - uint code = GetKeycodeFromPoint(pt); + const wxPoint& pt = event.GetPosition(); + const keydata_t *key = GetKeydataFromPoint(pt); - DoLeftDown(code); + if (key != NULL) { + DoLeftDown(key->keycode); + } } // マウス左クリック (Down) 処理 void -WXLunaSoftKeyPanel::DoLeftDown(uint code) +WXSoftKeyPanel::DoLeftDown(uint code) { - if (code != 0) { - if (hit[code]) { - // 押下(長押し)中のキーならここで開放 + auto it = toggle.find(code); + if (it != toggle.end()) { + // トグルキーはここで完結。LeftUp では何もしない。 + // second のクリアは KeyUp() からコールバックされる OnKeyChanged() + // で行っている。 + if (keyboard->IsPressed(code)) { KeyUp(code); } else { - // 押下して、LeftUp のために覚えておく KeyDown(code); - leftdown_keycode = code; + it->second = code; } + leftdown_key = KC_none; + } else { + // トグルでない通常キーはここで押下して LeftUp のために覚えておく + KeyDown(code); + leftdown_key = code; } } // マウス左クリック (Up) イベント void -WXLunaSoftKeyPanel::OnLeftUp(wxMouseEvent& event) +WXSoftKeyPanel::OnLeftUp(wxMouseEvent& event) { // Up 時は、これが起きた地点のキーコードではなく // 直近に LeftDown したキーコードをターゲットにする。 - uint code = leftdown_keycode; + uint code = leftdown_key; - DoLeftUp(code); -} + if (code != KC_none) { + KeyUp(code); -// マウス左クリック (Up) 処理 -void -WXLunaSoftKeyPanel::DoLeftUp(uint code) -{ - if (code != 0) { - // 押されていたら開放 - if (hit[code]) { - KeyUp(code); + // 通常キーが離されたら、トグルキーはすべて開放 + for (const auto& k : toggle) { + auto tcode = k.second; + if (tcode) { + KeyUp(tcode); + } } } - leftdown_keycode = 0; + + leftdown_key = KC_none; } // マウス左ダブルクリックイベント @@ -416,25 +642,28 @@ WXLunaSoftKeyPanel::DoLeftUp(uint code) // 直接 EVT_LEFT_DCLICK => OnLeftDown に割り付けることも出来そうだが // デバッグが紛らわしくなりそうなので独立したイベントとしておく。 void -WXLunaSoftKeyPanel::OnLeftDClick(wxMouseEvent& event) +WXSoftKeyPanel::OnLeftDClick(wxMouseEvent& event) { - wxPoint pt = event.GetPosition(); - uint code = GetKeycodeFromPoint(pt); + const wxPoint& pt = event.GetPosition(); + const keydata_t *key = GetKeydataFromPoint(pt); - DoLeftDown(code); + if (key != NULL) { + DoLeftDown(key->keycode); + } } // マウス右クリック (Down) イベント // 長押し開始/解除。 // 右クリックのほうは Down だけで Up は使わない。 void -WXLunaSoftKeyPanel::OnRightDown(wxMouseEvent& event) +WXSoftKeyPanel::OnRightDown(wxMouseEvent& event) { - wxPoint pt = event.GetPosition(); - uint code = GetKeycodeFromPoint(pt); + const wxPoint& pt = event.GetPosition(); + const keydata_t *key = GetKeydataFromPoint(pt); - if (code != 0) { - if (hit[code]) { + if (key != NULL) { + uint code = key->keycode; + if (keyboard->IsPressed(code)) { // 押されていたら開放 KeyUp(code); } else { @@ -444,87 +673,121 @@ WXLunaSoftKeyPanel::OnRightDown(wxMouseE } } -// このキーが押された処理 -void -WXLunaSoftKeyPanel::KeyDown(uint code) +// 座標からキーデータを返す。 +// キーに当たっていなければ NULL を返す。 +const keydata_t * +WXSoftKeyPanel::GetKeydataFromPoint(const wxPoint& pt) const { - // 押された - hit[code] = true; + // エンターキーだけ先に別判定 + if (enter_top.Contains(pt.x, pt.y) || enter_btm.Contains(pt.x, pt.y)) { + return &FindKeydata(KC_enter); + } - // LED キーは押下時に変化(反転)させて、その結果 - // 点灯なら Make、消灯なら Break を送信。 - if (code == KC_CAPS) { - led_cap = !led_cap; - if (led_cap) { - gKeyboard->MakeKey(code); - } else { - gKeyboard->BreakKey(code); + for (const auto& key : keydata) { + if (key.x < 0) { + continue; } - } else if (code == KC_kana) { - led_kana = !led_kana; - if (led_kana) { - gKeyboard->MakeKey(code); - } else { - gKeyboard->BreakKey(code); + // 始点と幅の +1/-1 は罫線の分 + wxRect rect( + (key.x * unit) + 1, + (key.y * unit) + 1, + (key.w * unit) - 1, + (key.h * unit) - 1); + if (rect.Contains(pt)) { + return &key; } - } else { - // 通常キーは押下で Make 送信。 - gKeyboard->MakeKey(code); - } - - // 修飾キー - if (code == KC_SHIFT_L || code == KC_SHIFT_R) { - modifier = ModShift; } + return NULL; +} - // キー状態が変わった(かも知れない)ので再描画 - Refresh(); +// このキーが押された処理 +void +WXSoftKeyPanel::KeyDown(uint code) +{ + // VM に通知 (Keyboard がホストに通知する) + keyboard->MakeKey(code); } // このキーが放された処理 void -WXLunaSoftKeyPanel::KeyUp(uint code) +WXSoftKeyPanel::KeyUp(uint code) { - hit[code] = false; - // 通常キーなら Break を送る。(LED キーは KeyDown で送っているので不要) - if (!IsLEDKey(code)) { - gKeyboard->BreakKey(code); - } + // VM に通知 (Keyboard がホストに通知する) + keyboard->BreakKey(code); +} - // 修飾キー - if (code == KC_SHIFT_L || code == KC_SHIFT_R) { - modifier = ModNormal; + +// +// LUNA ソフトウェアキーボード (パネル) +// + +// コンストラクタ +WXLunaSoftKeyPanel::WXLunaSoftKeyPanel(wxWindow *parent) + : inherited(parent, keydata, 226, 54) +{ + // LUNA では + // BS キーの左隣 (0x5c) は円記号ではなくバックスラッシュ。 + // それを SHIFT したやつ(0x7c) は破線でないパイプ。 + // そのもう一つ左隣 (0x7e) はオーバーラインではなくチルダ。 + gFontManager->SetGlyph5C(true); + gFontManager->SetGlyph7E(true); + + // 白黒モデル共通 + color.Border = UD_BLACK; + color.LED[0] = UD_RED; + + // キートップの濃淡はユニバーサルデザインの推奨色ではないけど + // ここは見分けを付けるためではなくただのデザインなので。 + if (1) { + // 白モデル + color.Background = UD_LIGHT_GREY; // 現物新品は何色だったんだろう + color.Text = UD_BLACK; + color.Keytop[0][0] = UD_WHITE; + color.Keytop[1][0] = Color(200, 200, 203); + } else { + // 黒モデル + color.Background = UD_BLACK; + color.Text = UD_WHITE; + color.Keytop[0][0] = Color(50, 50, 53); + color.Keytop[1][0] = UD_BLACK; } + InitColorKeytop(2); - // キー状態が変わった(かも知れない)ので再描画 - Refresh(); + SetBitmapBGColor(color.Background); + Fill(); } -// 座標からキーコード(KC_*)を返す。 -// キーに当たっていなければ KC_none を返す。 -uint -WXLunaSoftKeyPanel::GetKeycodeFromPoint(const wxPoint& pt) const +// デストラクタ +WXLunaSoftKeyPanel::~WXLunaSoftKeyPanel() { - // エンターキーだけ先に別判定 - if (enter_region.Contains(pt) == wxInRegion) { - return KC_enter; - } +} - for (const auto& key : keydata) { - if (key.x == -1) { - continue; - } - // 始点と幅の +1/-1 は罫線の分 - wxRect rect( - (key.x * unit) + 1, - (key.y * unit) + 1, - (key.w * unit) - 1, - (key.h * unit) - 1); - if (rect.Contains(pt)) { - return key.keycode; - } +// LED を描画して刻印描画のための y offset を返す。 +int +WXLunaSoftKeyPanel::DrawLED(const keydata_t& key) +{ + const std::vector& led = keyboard->GetLED(); + Color fg; + Color bg; + + if (led[key.led]) { + // 点灯時は赤、枠は黒 + fg = color.Border; + bg = color.LED[0]; + } else { + // 消灯時は元のキートップの色で、枠は薄めにしたい + // (枠の色は今の所全モデル共通なので ColorPalette に含めていない) + fg = UD_GREY; + bg = color.Keytop[key.coltype][0]; } - return KC_none; + + // LED を左肩に描く + Rect rect(key.x * unit + 2, key.y * unit + 2, 4 * unit - 2, 3 * unit - 2); + bitmap.FillRect(bg, rect); + bitmap.DrawRect(fg, rect); + + // そのため文字は少し下げる + return +1; } // キーデータ @@ -535,157 +798,352 @@ WXLunaSoftKeyPanel::GetKeycodeFromPoint( #define L3 (28) // CTRL の行 #define L4 (36) // SHIFT の行 #define L5 (44) // space の行 -// エンター記号は半角2文字幅なので空白2個で表現しておく。 -// 文字数から文字幅をカウントするのに使っているだけで、描画には使わない。 -#define EnterMark " " /*static*/ std::vector WXLunaSoftKeyPanel::keydata { - // +--- キートップの色種別(0:薄い 1:濃い) - // | - // X Y W H C KeyCode 通常 SHIFT - { 2, L0, 15, H, 1, KC_F1, { "PF1" } }, - { 17, L0, 15, H, 1, KC_F2, { "PF2" } }, - { 32, L0, 15, H, 1, KC_F3, { "PF3" } }, - { 47, L0, 15, H, 1, KC_F4, { "PF4" } }, - { 62, L0, 15, H, 1, KC_F5, { "PF5" } }, - { 81, L0, 15, H, 1, KC_F6, { "PF6" } }, - { 96, L0, 15, H, 1, KC_F7, { "PF7" } }, - { 111, L0, 15, H, 1, KC_F8, { "PF8" } }, - { 126, L0, 15, H, 1, KC_F9, { "PF9" } }, - { 141, L0, 15, H, 1, KC_F10, { "PF10" } }, - - { 2, L1, 12, H, 1, KC_ESC, { "ESC" } }, - { 14, L1, 10, H, 0, KC_1, { "1", "!" } }, - { 24, L1, 10, H, 0, KC_2, { "2", "\"" } }, - { 34, L1, 10, H, 0, KC_3, { "3", "#" } }, - { 44, L1, 10, H, 0, KC_4, { "4", "$" } }, - { 54, L1, 10, H, 0, KC_5, { "5", "%" } }, - { 64, L1, 10, H, 0, KC_6, { "6", "&" } }, - { 74, L1, 10, H, 0, KC_7, { "7", "'" } }, - { 84, L1, 10, H, 0, KC_8, { "8", "(" } }, - { 94, L1, 10, H, 0, KC_9, { "9", ")" } }, - { 104, L1, 10, H, 0, KC_0, { "0", " " } }, - { 114, L1, 10, H, 0, KC_minus, { "-", "=" } }, - { 124, L1, 10, H, 0, KC_circum, { "^", "~" } }, - { 134, L1, 10, H, 0, KC_backslash, { "\\", "|" } }, - { 144, L1, 12, H, 1, KC_BS, { "BS" } }, - - { 2, L2, 17, H, 1, KC_TAB, { "TAB" } }, - { 19, L2, 10, H, 0, KC_Q, { "q", "Q" } }, - { 29, L2, 10, H, 0, KC_W, { "w", "W" } }, - { 39, L2, 10, H, 0, KC_E, { "e", "E" } }, - { 49, L2, 10, H, 0, KC_R, { "r", "R" } }, - { 59, L2, 10, H, 0, KC_T, { "t", "T" } }, - { 69, L2, 10, H, 0, KC_Y, { "y", "Y" } }, - { 79, L2, 10, H, 0, KC_U, { "u", "U" } }, - { 89, L2, 10, H, 0, KC_I, { "i", "I" } }, - { 99, L2, 10, H, 0, KC_O, { "o", "O" } }, - { 109, L2, 10, H, 0, KC_P, { "p", "P" } }, - { 119, L2, 10, H, 0, KC_at, { "@", "`" } }, - { 129, L2, 10, H, 0, KC_bracketleft, { "[", "{" } }, - - { 2, L3, 20, H, 1, KC_CTRL, { "CTRL" } }, - { 22, L3, 10, H, 0, KC_A, { "a", "A" } }, - { 32, L3, 10, H, 0, KC_S, { "s", "S" } }, - { 42, L3, 10, H, 0, KC_D, { "d", "D" } }, - { 52, L3, 10, H, 0, KC_F, { "f", "F" } }, - { 62, L3, 10, H, 0, KC_G, { "g", "G" } }, - { 72, L3, 10, H, 0, KC_H, { "h", "H" } }, - { 82, L3, 10, H, 0, KC_J, { "j", "J" } }, - { 92, L3, 10, H, 0, KC_K, { "k", "K" } }, - { 102, L3, 10, H, 0, KC_L, { "l", "L" } }, - { 112, L3, 10, H, 0, KC_semicolon, { ";", "+" } }, - { 122, L3, 10, H, 0, KC_colon, { ":", "*" } }, - { 132, L3, 10, H, 0, KC_bracketright, { "]", "}" } }, - - { 2, L4, 25, H, 1, KC_SHIFT_L, { "SHIFT" } }, - { 27, L4, 10, H, 0, KC_Z, { "z", "Z" } }, - { 37, L4, 10, H, 0, KC_X, { "x", "X" } }, - { 47, L4, 10, H, 0, KC_C, { "c", "C" } }, - { 57, L4, 10, H, 0, KC_V, { "v", "V" } }, - { 67, L4, 10, H, 0, KC_B, { "b", "B" } }, - { 77, L4, 10, H, 0, KC_N, { "n", "N" } }, - { 87, L4, 10, H, 0, KC_M, { "m", "M" } }, - { 97, L4, 10, H, 0, KC_comma, { ",", "<" } }, - { 107, L4, 10, H, 0, KC_period, { ".", ">" } }, - { 117, L4, 10, H, 0, KC_slash, { "/", "?" } }, - { 127, L4, 10, H, 0, KC_underscore, { " ", "_" } }, - { 137, L4, 19, H, 1, KC_SHIFT_R, { "SHIFT" } }, - - { 2, L5, 20, H, 1, KC_VALID, { "確定" } }, - { 22, L5, 10, H, 1, KC_SF, { "前面" } }, - { 32, L5, 10, H, 1, KC_CAPS, { "CAP" } }, - { 42, L5, 80, H, 0, KC_space, { " " } }, - { 122, L5, 10, H, 1, KC_kana, { "かな" } }, - { 132, L5, 24, H, 1, KC_XFER, { "変換" } }, + // +---- キートップの色(1:濃) + // | +- 0 以上なら LED 番号 + // X Y W H C L KeyCode 通常 SHIFT + { 2, L0, 15, H, 1,-1, KC_F1, { "PF1" } }, + { 17, L0, 15, H, 1,-1, KC_F2, { "PF2" } }, + { 32, L0, 15, H, 1,-1, KC_F3, { "PF3" } }, + { 47, L0, 15, H, 1,-1, KC_F4, { "PF4" } }, + { 62, L0, 15, H, 1,-1, KC_F5, { "PF5" } }, + { 81, L0, 15, H, 1,-1, KC_F6, { "PF6" } }, + { 96, L0, 15, H, 1,-1, KC_F7, { "PF7" } }, + { 111, L0, 15, H, 1,-1, KC_F8, { "PF8" } }, + { 126, L0, 15, H, 1,-1, KC_F9, { "PF9" } }, + { 141, L0, 15, H, 1,-1, KC_F10, { "PF10" } }, + { 210, L0+1, 14, H-2, 1,-1, KC_buzzer, { "Buzzer" } }, + + { 2, L1, 12, H, 1,-1, KC_ESC, { "ESC" } }, + { 14, L1, 10, H, 0,-1, KC_1, { "1", "!" } }, + { 24, L1, 10, H, 0,-1, KC_2, { "2", "\"" } }, + { 34, L1, 10, H, 0,-1, KC_3, { "3", "#" } }, + { 44, L1, 10, H, 0,-1, KC_4, { "4", "$" } }, + { 54, L1, 10, H, 0,-1, KC_5, { "5", "%" } }, + { 64, L1, 10, H, 0,-1, KC_6, { "6", "&" } }, + { 74, L1, 10, H, 0,-1, KC_7, { "7", "'" } }, + { 84, L1, 10, H, 0,-1, KC_8, { "8", "(" } }, + { 94, L1, 10, H, 0,-1, KC_9, { "9", ")" } }, + { 104, L1, 10, H, 0,-1, KC_0, { "0", " " } }, + { 114, L1, 10, H, 0,-1, KC_minus, { "-", "=" } }, + { 124, L1, 10, H, 0,-1, KC_circum, { "^", "~" } }, + { 134, L1, 10, H, 0,-1, KC_backslash, { "\\", "|" } }, + { 144, L1, 12, H, 1,-1, KC_BS, { "BS" } }, + + { 2, L2, 17, H, 1,-1, KC_TAB, { "TAB" } }, + { 19, L2, 10, H, 0,-1, KC_Q, { "q", "Q" } }, + { 29, L2, 10, H, 0,-1, KC_W, { "w", "W" } }, + { 39, L2, 10, H, 0,-1, KC_E, { "e", "E" } }, + { 49, L2, 10, H, 0,-1, KC_R, { "r", "R" } }, + { 59, L2, 10, H, 0,-1, KC_T, { "t", "T" } }, + { 69, L2, 10, H, 0,-1, KC_Y, { "y", "Y" } }, + { 79, L2, 10, H, 0,-1, KC_U, { "u", "U" } }, + { 89, L2, 10, H, 0,-1, KC_I, { "i", "I" } }, + { 99, L2, 10, H, 0,-1, KC_O, { "o", "O" } }, + { 109, L2, 10, H, 0,-1, KC_P, { "p", "P" } }, + { 119, L2, 10, H, 0,-1, KC_at, { "@", "`" } }, + { 129, L2, 10, H, 0,-1, KC_bracketleft, { "[", "{" } }, + + { 2, L3, 20, H, 1,-1, KC_CTRL, { "CTRL" } }, + { 22, L3, 10, H, 0,-1, KC_A, { "a", "A" } }, + { 32, L3, 10, H, 0,-1, KC_S, { "s", "S" } }, + { 42, L3, 10, H, 0,-1, KC_D, { "d", "D" } }, + { 52, L3, 10, H, 0,-1, KC_F, { "f", "F" } }, + { 62, L3, 10, H, 0,-1, KC_G, { "g", "G" } }, + { 72, L3, 10, H, 0,-1, KC_H, { "h", "H" } }, + { 82, L3, 10, H, 0,-1, KC_J, { "j", "J" } }, + { 92, L3, 10, H, 0,-1, KC_K, { "k", "K" } }, + { 102, L3, 10, H, 0,-1, KC_L, { "l", "L" } }, + { 112, L3, 10, H, 0,-1, KC_semicolon, { ";", "+" } }, + { 122, L3, 10, H, 0,-1, KC_colon, { ":", "*" } }, + { 132, L3, 10, H, 0,-1, KC_bracketright, { "]", "}" } }, + + { 2, L4, 25, H, 1,-1, KC_SHIFT_L, { "SHIFT" } }, + { 27, L4, 10, H, 0,-1, KC_Z, { "z", "Z" } }, + { 37, L4, 10, H, 0,-1, KC_X, { "x", "X" } }, + { 47, L4, 10, H, 0,-1, KC_C, { "c", "C" } }, + { 57, L4, 10, H, 0,-1, KC_V, { "v", "V" } }, + { 67, L4, 10, H, 0,-1, KC_B, { "b", "B" } }, + { 77, L4, 10, H, 0,-1, KC_N, { "n", "N" } }, + { 87, L4, 10, H, 0,-1, KC_M, { "m", "M" } }, + { 97, L4, 10, H, 0,-1, KC_comma, { ",", "<" } }, + { 107, L4, 10, H, 0,-1, KC_period, { ".", ">" } }, + { 117, L4, 10, H, 0,-1, KC_slash, { "/", "?" } }, + { 127, L4, 10, H, 0,-1, KC_underscore, { " ", "_" } }, + { 137, L4, 19, H, 1,-1, KC_SHIFT_R, { "SHIFT" } }, + + { 2, L5, 20, H, 1,-1, KC_VALID, { "確定" } }, + { 22, L5, 10, H, 1,-1, KC_SF, { "前面" } }, + { 32, L5, 10, H, 1, 1, KC_CAPS, { "CAP" } }, + { 42, L5, 80, H, 0,-1, KC_space, { " " } }, + { 122, L5, 10, H, 1, 0, KC_kana, { "かな" } }, + { 132, L5, 24, H, 1,-1, KC_XFER, { "変換" } }, // カーソルキー島 - { 158, L1, 13, H, 1, KC_PF11, { "消去" } }, - { 171, L1, 13, H, 1, KC_PF12, { "呼出" } }, - { 158, L2, 13, H, 1, KC_PF13, { "文節←" } }, - { 171, L2, 13, H, 1, KC_PF14, { "文節→" } }, - { 158, L3, 26, H, 1, KC_up, { "↑" } }, - { 158, L4, 13, H, 1, KC_left, { "←" } }, - { 171, L4, 13, H, 1, KC_right, { "→" } }, - { 158, L5, 26, H, 1, KC_down, { "↓" } }, + { 160, L1, 10, H, 1,-1, KC_PF11, { "消去" } }, + { 170, L1, 10, H, 1,-1, KC_PF12, { "呼出" } }, + { 160, L2, 10, H, 1,-1, KC_PF13, { "文節\n←" } }, + { 170, L2, 10, H, 1,-1, KC_PF14, { "文節\n→" } }, + { 160, L3, 20, H, 1,-1, KC_up, { "↑" } }, + { 160, L4, 10, H, 1,-1, KC_left, { "←" } }, + { 170, L4, 10, H, 1,-1, KC_right, { "→" } }, + { 160, L5, 20, H, 1,-1, KC_down, { "↓" } }, // テンキー島 - { 186, L1, 10, H, 1, KC_DEL, { "DEL" } }, - { 196, L1, 10, H, 1, KC_PAD_plus, { "+" } }, - { 206, L1, 10, H, 1, KC_PAD_minus, { "-" } }, - { 216, L1, 10, H, 1, KC_PAD_multiply, { "*" } }, - - { 186, L2, 10, H, 0, KC_PAD_7, { "7" } }, - { 196, L2, 10, H, 0, KC_PAD_8, { "8" } }, - { 206, L2, 10, H, 0, KC_PAD_9, { "9" } }, - { 216, L2, 10, H, 1, KC_PAD_divide, { "/" } }, - - { 186, L3, 10, H, 0, KC_PAD_4, { "4" } }, - { 196, L3, 10, H, 0, KC_PAD_5, { "5" } }, - { 206, L3, 10, H, 0, KC_PAD_6, { "6" } }, - { 216, L3, 10, H, 1, KC_PAD_equal, { "=" } }, - - { 186, L4, 10, H, 0, KC_PAD_1, { "1" } }, - { 196, L4, 10, H, 0, KC_PAD_2, { "2" } }, - { 206, L4, 10, H, 0, KC_PAD_3, { "3" } }, - { 216, L4, 10, H, 1, KC_PAD_comma, { "," } }, - - { 186, L5, 10, H, 0, KC_PAD_0, { "0" } }, - { 196, L5, 10, H, 0, KC_PAD_decimal, { "." } }, - { 206, L5, 20, H, 1, KC_PAD_enter, { EnterMark } }, + { 184, L1, 10, H, 1,-1, KC_DEL, { "DEL" } }, + { 194, L1, 10, H, 1,-1, KC_PAD_plus, { "+" } }, + { 204, L1, 10, H, 1,-1, KC_PAD_minus, { "-" } }, + { 214, L1, 10, H, 1,-1, KC_PAD_multiply, { "*" } }, + + { 184, L2, 10, H, 0,-1, KC_PAD_7, { "7" } }, + { 194, L2, 10, H, 0,-1, KC_PAD_8, { "8" } }, + { 204, L2, 10, H, 0,-1, KC_PAD_9, { "9" } }, + { 214, L2, 10, H, 1,-1, KC_PAD_divide, { "/" } }, + + { 184, L3, 10, H, 0,-1, KC_PAD_4, { "4" } }, + { 194, L3, 10, H, 0,-1, KC_PAD_5, { "5" } }, + { 204, L3, 10, H, 0,-1, KC_PAD_6, { "6" } }, + { 214, L3, 10, H, 1,-1, KC_PAD_equal, { "=" } }, + + { 184, L4, 10, H, 0,-1, KC_PAD_1, { "1" } }, + { 194, L4, 10, H, 0,-1, KC_PAD_2, { "2" } }, + { 204, L4, 10, H, 0,-1, KC_PAD_3, { "3" } }, + { 214, L4, 10, H, 1,-1, KC_PAD_comma, { "," } }, + + { 184, L5, 10, H, 0,-1, KC_PAD_0, { "0" } }, + { 194, L5, 10, H, 0,-1, KC_PAD_decimal, { "." } }, + { 204, L5, 20, H, 1,-1, KC_PAD_enter, { EnterMark } }, // Enter だけ矩形でないので別処理するが // この構造体には全部のキー情報が統一的に並んでいるほうがよかろう。 - { -1, -1, -1, -1, 1, KC_enter, { EnterMark } }, + { -1, -1, -1, -1, 1,-1, KC_enter, { EnterMark } }, }; // -// LUNA ソフトウェアキーボード (ウィンドウ) +// X68k ソフトウェアキーボード (パネル) // // コンストラクタ -WXLunaSoftKeyWindow::WXLunaSoftKeyWindow(wxWindow *parent, wxWindowID id) - : inherited(parent, id, wxT("ソフトウェアキーボード")) +WXX68kSoftKeyPanel::WXX68kSoftKeyPanel(wxWindow *parent) + : inherited(parent, keydata, 235, 54) { - panel = new WXLunaSoftKeyPanel(this); + // X68k では SRAM 設定に同期させる。 + const auto sram = GetSRAMDevice(); + uint8 sw = sram->Peek1(0xed0059); + gFontManager->SetGlyph5C((sw & 0x01)); + gFontManager->SetGlyph7E((sw & 0x02)); + gFontManager->SetGlyph7C((sw & 0x04)); + + // とりあえず黒のみ + color.Background = Color(50, 50, 53); + color.Text = UD_WHITE; + color.Border = UD_BLACK; + color.LED[0] = UD_RED; + color.LED[1] = UD_YELLOW_GREEN; + color.Keytop[0][0] = UD_BLACK; + InitColorKeytop(1); - // 大きさをそれに固定 - Layout(); - Fit(); + SetBitmapBGColor(color.Background); + Fill(); } // デストラクタ -WXLunaSoftKeyWindow::~WXLunaSoftKeyWindow() +WXX68kSoftKeyPanel::~WXX68kSoftKeyPanel() { } -// フォントサイズ変更 -void -WXLunaSoftKeyWindow::SetFontSize(fontsize_t fontsize) +// LED を描画して刻印描画のための y offset を返す。 +int +WXX68kSoftKeyPanel::DrawLED(const keydata_t& key) +{ + const std::vector& led = keyboard->GetLED(); + Color fg; + Color bg; + + // 色を選択 + // idx 刻印 色 + // ---------------- + // 0 かな 赤 + // 1 ローマ 赤 + // 2 コード 赤 + // 3 CAPS 赤 + // 4 INS 赤 + // 5 ひら 緑 + // 6 全角 緑 + if (led[key.led]) { + int n = (key.led < 5) ? 0 : 1; + fg = color.LED[n]; + bg = color.LED[n]; + } else { + // 消灯時は元のキートップの色で、枠は薄めにしたい + // (枠の色は今の所全モデル共通なので ColorPalette に含めていない) + fg = UD_GREY; + bg = color.Keytop[key.coltype][0]; + } + + // LED は中央前方(画面でいうと下) + Rect rect((key.x + 3) * unit, (key.y + key.h - 2) * unit, + 4 * unit, 2 * unit); + bitmap.FillRect(bg, rect); + bitmap.DrawRect(fg, rect); + + // そのため文字は少し上げる + return -1; +} + +#define LF (L0 + 2) // ファンクションキーは少し低い(横長い) +#define HT (H * 2) // 縦2倍角のキー +/*static*/ std::vector +WXX68kSoftKeyPanel::keydata { + // +- 0以上なら LED 番号 + // X Y W H C L KeyCode 通常 SHIFT + { 2, L0, 10, H, 0,-1, KC_BREAK, { "\x4\x5\x6\x7" } }, + { 15, L0, 10, H, 0,-1, KC_COPY, { "COPY" } }, + { 29, LF, 12, 6, 0,-1, KC_F1, { "F1" } }, + { 41, LF, 12, 6, 0,-1, KC_F2, { "F2" } }, + { 53, LF, 12, 6, 0,-1, KC_F3, { "F3" } }, + { 65, LF, 12, 6, 0,-1, KC_F4, { "F4" } }, + { 77, LF, 13, 6, 0,-1, KC_F5, { "F5" } }, + { 94, LF, 13, 6, 0,-1, KC_F6, { "F6" } }, + { 106, LF, 12, 6, 0,-1, KC_F7, { "F7" } }, + { 118, LF, 12, 6, 0,-1, KC_F8, { "F8" } }, + { 130, LF, 12, 6, 0,-1, KC_F9, { "F9" } }, + { 142, LF, 13, 6, 0,-1, KC_F10, { "F10" } }, + + { 2, L1, 10, H, 0,-1, KC_ESC, { "ESC" } }, + { 12, L1, 10, H, 0,-1, KC_1, { "1", "!" } }, + { 22, L1, 10, H, 0,-1, KC_2, { "2", "\"" } }, + { 32, L1, 10, H, 0,-1, KC_3, { "3", "#" } }, + { 42, L1, 10, H, 0,-1, KC_4, { "4", "$" } }, + { 52, L1, 10, H, 0,-1, KC_5, { "5", "%" } }, + { 62, L1, 10, H, 0,-1, KC_6, { "6", "&" } }, + { 72, L1, 10, H, 0,-1, KC_7, { "7", "'" } }, + { 82, L1, 10, H, 0,-1, KC_8, { "8", "(" } }, + { 92, L1, 10, H, 0,-1, KC_9, { "9", ")" } }, + { 102, L1, 10, H, 0,-1, KC_0, { "0", " " } }, + { 112, L1, 10, H, 0,-1, KC_minus, { "-", "=" } }, + { 122, L1, 10, H, 0,-1, KC_circum, { "^", "~" } }, + { 132, L1, 10, H, 0,-1, KC_backslash, { "\\", "|" } }, + { 142, L1, 13, H, 0,-1, KC_BS, { "BS" } }, + + { 2, L2, 16, H, 0,-1, KC_TAB, { "TAB" } }, + { 18, L2, 10, H, 0,-1, KC_Q, { "q", "Q" } }, + { 28, L2, 10, H, 0,-1, KC_W, { "w", "W" } }, + { 38, L2, 10, H, 0,-1, KC_E, { "e", "E" } }, + { 48, L2, 10, H, 0,-1, KC_R, { "r", "R" } }, + { 58, L2, 10, H, 0,-1, KC_T, { "t", "T" } }, + { 68, L2, 10, H, 0,-1, KC_Y, { "y", "Y" } }, + { 78, L2, 10, H, 0,-1, KC_U, { "u", "U" } }, + { 88, L2, 10, H, 0,-1, KC_I, { "i", "I" } }, + { 98, L2, 10, H, 0,-1, KC_O, { "o", "O" } }, + { 108, L2, 10, H, 0,-1, KC_P, { "p", "P" } }, + { 118, L2, 10, H, 0,-1, KC_at, { "@", "`" } }, + { 128, L2, 10, H, 0,-1, KC_bracketleft, { "[", "{" } }, + + { 2, L3, 19, H, 0,-1, KC_CTRL, { "CTRL" } }, + { 21, L3, 10, H, 0,-1, KC_A, { "a", "A" } }, + { 31, L3, 10, H, 0,-1, KC_S, { "s", "S" } }, + { 41, L3, 10, H, 0,-1, KC_D, { "d", "D" } }, + { 51, L3, 10, H, 0,-1, KC_F, { "f", "F" } }, + { 61, L3, 10, H, 0,-1, KC_G, { "g", "G" } }, + { 71, L3, 10, H, 0,-1, KC_H, { "h", "H" } }, + { 81, L3, 10, H, 0,-1, KC_J, { "j", "J" } }, + { 91, L3, 10, H, 0,-1, KC_K, { "k", "K" } }, + { 101, L3, 10, H, 0,-1, KC_L, { "l", "L" } }, + { 111, L3, 10, H, 0,-1, KC_semicolon, { ";", "+" } }, + { 121, L3, 10, H, 0,-1, KC_colon, { ":", "*" } }, + { 131, L3, 10, H, 0,-1, KC_bracketright, { "]", "}" } }, + + { 2, L4, 24, H, 0,-1, KC_SHIFT, { "SHIFT" } }, + { 26, L4, 10, H, 0,-1, KC_Z, { "z", "Z" } }, + { 36, L4, 10, H, 0,-1, KC_X, { "x", "X" } }, + { 46, L4, 10, H, 0,-1, KC_C, { "c", "C" } }, + { 56, L4, 10, H, 0,-1, KC_V, { "v", "V" } }, + { 66, L4, 10, H, 0,-1, KC_B, { "b", "B" } }, + { 76, L4, 10, H, 0,-1, KC_N, { "n", "N" } }, + { 86, L4, 10, H, 0,-1, KC_M, { "m", "M" } }, + { 96, L4, 10, H, 0,-1, KC_comma, { ",", "<" } }, + { 106, L4, 10, H, 0,-1, KC_period, { ".", ">" } }, + { 116, L4, 10, H, 0,-1, KC_slash, { "/", "?" } }, + { 126, L4, 10, H, 0,-1, KC_underscore, { " ", "_" } }, + { 136, L4, 19, H, 0,-1, KC_SHIFT, { "SHIFT" } }, + + { 21, L5, 10, H, 0, 5, KC_hira, { "ひら" } }, + { 31, L5, 12, H, 0,-1, KC_XF1, { "XF1" } }, + { 43, L5, 13, H, 0,-1, KC_XF2, { "XF2" } }, + { 56, L5, 36, H, 0,-1, KC_space, { " " } }, + { 92, L5, 12, H, 0,-1, KC_XF3, { "XF3" } }, + { 104, L5, 12, H, 0,-1, KC_XF4, { "XF4" } }, + { 116, L5, 12, H, 0,-1, KC_XF5, { "XF5" } }, + { 128, L5, 10, H, 0, 6, KC_zenkaku, { "全角" } }, + + // カーソルキー島 + { 159, L0, 10, H, 0, 0, KC_kana, { "かな" } }, + { 169, L0, 10, H, 0, 1, KC_romaji, { "ローマ" } }, + { 179, L0, 10, H, 0, 2, KC_code, { "コード" } }, + { 159, L1, 10, H, 0,-1, KC_HOME, { "HOME" } }, + { 169, L1, 10, H, 0, 4, KC_INS, { "INS" } }, + { 179, L1, 10, H, 0,-1, KC_DEL, { "DEL" } }, + { 159, L2, 10, H, 0,-1, KC_ROLLUP, { "ROLL\n UP" } }, + { 169, L2, 10, H, 0,-1, KC_ROLLDOWN, { "ROLL\nDOWN" } }, + { 179, L2, 10, H, 0,-1, KC_UNDO, { "UNDO" } }, + { 169, L3, 10, H, 0,-1, KC_up, { "↑" } }, + { 159, L3, 10, HT,0,-1, KC_left, { "←" } }, + { 179, L3, 10, HT,0,-1, KC_right, { "→" } }, + { 169, L4, 10, H, 0,-1, KC_down, { "↓" } }, + { 159, L5, 15, H, 0,-1, KC_OPT1, { "OPT.1" } }, + { 174, L5, 15, H, 0,-1, KC_OPT2, { "OPT.2" } }, + + // テンキー島 + { 193, L0, 10, H, 0, 3, KC_CAPS, { "CAPS" } }, + { 203, L0, 10, H, 0,-1, KC_kigou, { "記号" } }, + { 213, L0, 10, H, 0,-1, KC_touroku, { "登録" } }, + { 223, L0, 10, H, 0,-1, KC_HELP, { "HELP" } }, + { 193, L1, 10, H, 0,-1, KC_PAD_CLR, { "CLR" } }, + { 203, L1, 10, H, 0,-1, KC_PAD_divide, { "/" } }, + { 213, L1, 10, H, 0,-1, KC_PAD_multiply, { "*" } }, + { 223, L1, 10, H, 0,-1, KC_PAD_minus, { "-" } }, + { 193, L2, 10, H, 0,-1, KC_PAD_7, { "7" } }, + { 203, L2, 10, H, 0,-1, KC_PAD_8, { "8" } }, + { 213, L2, 10, H, 0,-1, KC_PAD_9, { "9" } }, + { 223, L2, 10, H, 0,-1, KC_PAD_plus, { "+" } }, + { 193, L3, 10, H, 0,-1, KC_PAD_4, { "4" } }, + { 203, L3, 10, H, 0,-1, KC_PAD_5, { "5" } }, + { 213, L3, 10, H, 0,-1, KC_PAD_6, { "6" } }, + { 223, L3, 10, H, 0,-1, KC_PAD_equal, { "=" } }, + { 193, L4, 10, H, 0,-1, KC_PAD_1, { "1" } }, + { 203, L4, 10, H, 0,-1, KC_PAD_2, { "2" } }, + { 213, L4, 10, H, 0,-1, KC_PAD_3, { "3" } }, + { 223, L4, 10, HT,0,-1, KC_PAD_enter, { "\xb\xc\xe\xf" } }, + { 193, L5, 10, H, 0,-1, KC_PAD_0, { "0" } }, + { 203, L5, 10, H, 0,-1, KC_PAD_comma, { "," } }, + { 213, L5, 10, H, 0,-1, KC_PAD_decimal, { "." } }, + + // Enter だけ矩形でないので別処理するが + // この構造体には全部のキー情報が統一的に並んでいるほうがよかろう。 + { -1, -1, -1, -1, 0,-1, KC_enter, { EnterMark } }, +}; + +// +// ソフトウェアキーボード (ウィンドウ) +// + +// コンストラクタ +WXSoftKeyWindow::WXSoftKeyWindow(wxWindow *parent, VMType vmtype) + : inherited(parent, wxID_ANY, _("Software Keyboard")) { - panel->SetFontSize(fontsize); - // 大きさ変更 - Layout(); + if (vmtype == VMType::X68030) { + new WXX68kSoftKeyPanel(this); + } else { + new WXLunaSoftKeyPanel(this); + } + Fit(); } + +// デストラクタ +WXSoftKeyWindow::~WXSoftKeyWindow() +{ +}