|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // プレーン VRAM モニターウィンドウ
9: //
10:
11: #include "wxplanemonitor.h"
12: #include "wxcolor.h"
13: #include "wxuimessage.h"
14: #include "bt454.h"
15: #include "planevram.h"
16: #include "videoctlr.h"
17:
18: enum {
19: ID_PLANE0 = IDGROUP_PLANEVRAM,
20: ID_PLANE1,
21: ID_PLANE2,
22: ID_PLANE3,
23: ID_PLANE4,
24: ID_PLANE5,
25: ID_PLANE6,
26: ID_PLANE7,
27: ID_PALETTE,
28: ID_SCALE,
29:
30: ID_local_end, // 最後に置く (チェック用)
31: };
32: static_assert(ID_local_end - 1 <= (int)IDGROUP_PLANEVRAM_END, "ID exceeded");
33:
34: #define ID_PLANE(id) (ID_PLANE0 + (id))
35:
36: //
37: // プレーン VRAM ビットマップ
38: //
39:
40: // コンストラクタ
41: WXPlaneVRAMBitmapPanel::WXPlaneVRAMBitmapPanel(wxWindow *parent)
42: : inherited(parent)
43: {
44: plane_width = gPlaneVRAM->GetComposite().GetWidth();
45: plane_height = gPlaneVRAM->GetComposite().GetHeight();
46:
47: // まず等倍でセット
48: scale = 1;
49: SetScale(1);
50:
51: // VM からの通知を受け取る
52: WXUIMessage::Connect(UIMessage::PALETTE, this,
53: wxCommandEventHandler(WXPlaneVRAMBitmapPanel::OnPaletteChanged));
54: }
55:
56: // デストラクタ
57: WXPlaneVRAMBitmapPanel::~WXPlaneVRAMBitmapPanel()
58: {
59: WXUIMessage::Disconnect(UIMessage::PALETTE,
60: wxCommandEventHandler(WXPlaneVRAMBitmapPanel::OnPaletteChanged));
61: }
62:
63: // XXX 実際にはこの辺かどこかで更新チェックをしないといけない。
64: // 今は表示領域を毎回描画しているので無駄。
65:
66: // ビットマッププレーンの内容を描画する
67: void
68: WXPlaneVRAMBitmapPanel::Draw()
69: {
70: assert(view.GetRight() < virtual_width);
71: assert(view.GetBottom() < virtual_height);
72:
73: const BitmapI8& src = gPlaneVRAM->GetComposite();
74:
75: if (scale > 1) {
76: Rect dr(0, 0, bitmap.GetWidth(), bitmap.GetHeight());
77: bitmap.DrawScale(dr, src, &pal[0], view.x, scale, 1, view.y, scale, 1);
78: } else {
79: bitmap.DrawBitmap(0, 0, src, &pal[0], view);
80: }
81: }
82:
83: // プレーン選択変更
84: void
85: WXPlaneVRAMBitmapPanel::EnablePlane(int plane, bool value)
86: {
87: if (value) {
88: planemask |= 1U << plane;
89: } else {
90: planemask &= ~(1U << plane);
91: }
92:
93: // パレット生成
94: GenPalette();
95: }
96:
97: // パレット適用変更
98: void
99: WXPlaneVRAMBitmapPanel::EnablePalette(bool value)
100: {
101: apply_palette = value;
102:
103: // パレット生成
104: GenPalette();
105: }
106:
107: // VM からのパレット変更通知
108: void
109: WXPlaneVRAMBitmapPanel::OnPaletteChanged(wxCommandEvent& event)
110: {
111: // パレット生成
112: GenPalette();
113: }
114:
115: // パレット生成
116: void
117: WXPlaneVRAMBitmapPanel::GenPalette()
118: {
119: if (apply_palette) {
120: // パレットを適用する場合
121: const ColorXBGR *vmpal;
122: // 雑に X680x0/LUNA 対応
123: if (gVideoCtlr) {
124: vmpal = gVideoCtlr->GetHostPalette();
125: } else {
126: vmpal = gBT454->GetHostPalette();
127: }
128: const Color bg = vmpal[0].ToColor();
129: for (int i = 0; i < pal.size(); i++) {
130: if ((i & planemask)) {
131: int n = i % 16;
132: pal[i] = vmpal[n].ToColor();
133: } else {
134: pal[i] = bg;
135: }
136: }
137: } else {
138: // パレットを適用しない場合
139: for (int i = 0; i < pal.size(); i++) {
140: if ((i & planemask)) {
141: pal[i] = Color(255, 255, 255);
142: } else {
143: pal[i] = Color(0, 0, 0);
144: }
145: }
146: }
147: }
148:
149: // 倍率を設定
150: void
151: WXPlaneVRAMBitmapPanel::SetScale(int scale_)
152: {
153: int before = scale;
154:
155: scale = scale_;
156:
157: // 仮想画面の大きさを計算
158: virtual_width = plane_width * scale;
159: virtual_height = plane_height * scale;
160:
161: // ビューのスケール変換は前後のスケールがいるのでここでやる
162: view.x = view.x * scale / before;
163: view.y = view.y * scale / before;
164: view.w = view.w * scale / before;
165: view.h = view.h * scale / before;
166:
167: SetMaxClientSize(wxSize(virtual_width, virtual_height));
168: }
169:
170: // スクロール位置変更通知
171: void
172: WXPlaneVRAMBitmapPanel::OnScroll(wxScrollEvent& event)
173: {
174: int orient = event.GetOrientation();
175: int pos = event.GetPosition();
176:
177: if (orient == wxHORIZONTAL) {
178: view.x = pos;
179: } else {
180: view.y = pos;
181: }
182: }
183:
184:
185: //
186: // プレーン VRAM パネル
187: //
188:
189: // イベントテーブル
190: wxBEGIN_EVENT_TABLE(WXPlaneVRAMPanel, inherited)
191: EVT_SIZE(WXPlaneVRAMPanel::OnSize)
192: EVT_TIMER(wxID_ANY, WXPlaneVRAMPanel::OnTimer)
193: wxEND_EVENT_TABLE()
194:
195: // コンストラクタ
196: WXPlaneVRAMPanel::WXPlaneVRAMPanel(wxWindow *parent)
197: : inherited(parent)
198: {
199: // 表示パネル
200: viewctl = new WXPlaneVRAMBitmapPanel(this);
201:
202: // スクロールバーを自分で持つ (配置は Layout() で行う)
203: vscroll = new WXScrollBar(this, wxID_ANY, wxSB_VERTICAL);
204: hscroll = new WXScrollBar(this, wxID_ANY, wxSB_HORIZONTAL);
205:
206: // 右下の空き地
207: corner = new WXBitmapPanel(this);
208:
209: // Sizer 使わず自前でレイアウトする。
210: SetAutoLayout(true);
211:
212: // 最小値は適当
213: int vscr_width = vscroll->GetClientSize().x;
214: int hscr_height = hscroll->GetClientSize().y;
215: SetMinClientSize(wxSize(16 + vscr_width, 16 + hscr_height));
216: SetMaxClientSize(wxSize(viewctl->virtual_width + vscr_width,
217: viewctl->virtual_height + hscr_height));
218:
219: // スクロールバーからの通知イベントは表示パネルへ回す
220: vscroll->Connect(NONO_EVT_SCROLL,
221: wxScrollEventHandler(WXPlaneVRAMBitmapPanel::OnScroll), NULL, viewctl);
222: hscroll->Connect(NONO_EVT_SCROLL,
223: wxScrollEventHandler(WXPlaneVRAMBitmapPanel::OnScroll), NULL, viewctl);
224:
225: // 更新タイマーはモニターパネル同様の 30Hz にしておく。
226: timer.SetOwner(this);
227: timer.Start(33);
228: }
229:
230: // デストラクタ
231: WXPlaneVRAMPanel::~WXPlaneVRAMPanel()
232: {
233: }
234:
235: bool
236: WXPlaneVRAMPanel::Layout()
237: {
238: if (inherited::Layout() == false) {
239: return false;
240: }
241:
242: // コントロールを配置
243: const wxSize size = GetClientSize();
244: int vscr_width = vscroll->GetClientSize().x;
245: int hscr_height = hscroll->GetClientSize().y;
246: int view_width = size.x - vscr_width;
247: int view_height = size.y - hscr_height;
248: viewctl->SetSize(0, 0, view_width, view_height);
249: vscroll->SetSize(view_width, 0, vscr_width, view_height);
250: hscroll->SetSize(0, view_height, view_width, hscr_height);
251: corner->SetSize(view_width, view_height, vscr_width, hscr_height);
252:
253: // XXX 内部のコントロールのサイズが確定してからでないと DoSize() できない
254: DoSize(size);
255:
256: return true;
257: }
258:
259: // サイズ変更イベント
260: void
261: WXPlaneVRAMPanel::OnSize(wxSizeEvent& event)
262: {
263: // Mac ではウィンドウ作成時に 0 以下の値が来ることがある。
264: // GTK では 1 で来ることがある。
265: wxSize size = event.GetSize();
266: if (size.x <= 1 || size.y <= 1) {
267: return;
268: }
269:
270: // Layout() を呼ぶために必要
271: event.Skip();
272:
273: // XXX レイアウトが終わった後でないと DoSize() できない
274: // DoSize(size);
275: }
276:
277: // サイズ変更処理。パネルサイズと倍率が変わったら呼ぶこと。
278: // 倍率が変わった場合もスクロールバーの再設定とかがあるため。
279: void
280: WXPlaneVRAMPanel::DoSize(wxSize size)
281: {
282: wxSize viewsize = viewctl->GetClientSize();
283: int virtual_width = viewctl->virtual_width;
284: int virtual_height = viewctl->virtual_height;
285: Rect& view = viewctl->view;
286:
287: // スクロールバーが右端まで行っている場合は右を固定したように見せる。
288: // ただし全体が見えている場合は除く。
289:
290: if (view.x > 0 &&
291: (view.x + view.w == virtual_width ||
292: view.x + viewsize.x >= virtual_width))
293: {
294: view.x = virtual_width - viewsize.x;
295: if (view.x < 0) {
296: view.x = 0;
297: }
298: }
299: view.w = viewsize.x;
300:
301: // 下端も同様
302: if (view.y > 0 &&
303: (view.y + view.h >= virtual_height ||
304: view.y + size.y >= virtual_height))
305: {
306: view.y = virtual_height - viewsize.y;
307: if (view.y < 0) {
308: view.y = 0;
309: }
310: }
311: view.h = viewsize.y;
312:
313: // スクロールバー(横)を再設定
314: assert(hscroll);
315: int hpos = view.x;
316: int hthumbsize = view.w;
317: int hrange = virtual_width;
318: int hpagesize = view.w;
319: hscroll->SetScrollParam(hpos, hthumbsize, hrange, hpagesize);
320:
321: // スクロールバー(縦)を再設定
322: assert(vscroll);
323: int vpos = view.y;
324: int vthumbsize = view.h;
325: int vrange = virtual_height;
326: int vpagesize = view.h;
327: vscroll->SetScrollParam(vpos, vthumbsize, vrange, vpagesize);
328: }
329:
330: // タイマーイベント
331: void
332: WXPlaneVRAMPanel::OnTimer(wxTimerEvent& event)
333: {
334: Refresh();
335: }
336:
337: // プレーン選択変更
338: void
339: WXPlaneVRAMPanel::EnablePlane(int plane, bool value)
340: {
341: viewctl->EnablePlane(plane, value);
342: }
343:
344: // パレット適用変更
345: void
346: WXPlaneVRAMPanel::EnablePalette(bool value)
347: {
348: viewctl->EnablePalette(value);
349: }
350:
351: // 倍率を設定
352: void
353: WXPlaneVRAMPanel::SetScale(int scale_)
354: {
355: viewctl->SetScale(scale_);
356:
357: int vscr_width = vscroll->GetClientSize().x;
358: int hscr_height = hscroll->GetClientSize().y;
359: SetMaxClientSize(wxSize(viewctl->virtual_width + vscr_width,
360: viewctl->virtual_height + hscr_height));
361:
362: DoSize(GetSize());
363: }
364:
365:
366: //
367: // プレーン VRAM ウィンドウ
368: //
369:
370: // イベントテーブル
371: wxBEGIN_EVENT_TABLE(WXPlaneVRAMWindow, inherited)
372: EVT_COMMAND_RANGE(ID_PLANE0, ID_PLANE7, wxEVT_CHECKBOX,
373: WXPlaneVRAMWindow::OnPlane)
374: EVT_CHECKBOX(ID_PALETTE, WXPlaneVRAMWindow::OnApplyPalette)
375: EVT_CHOICE(ID_SCALE, WXPlaneVRAMWindow::OnScale)
376: wxEND_EVENT_TABLE()
377:
378: // コンストラクタ
379: WXPlaneVRAMWindow::WXPlaneVRAMWindow(wxWindow *parent, const wxString& name)
380: : inherited(parent, wxID_ANY, name,
381: inherited::DEFAULT_STYLE | wxRESIZE_BORDER)
382: {
383: auto *topsizer = new wxBoxSizer(wxVERTICAL);
384:
385: // 上にコントロールパネル(のまず下敷き)
386: ctrlpanel = new WXBitmapPanel(this);
387: topsizer->Add(ctrlpanel, 0, wxEXPAND);
388:
389: // コントロールパネル用の横 Sizer
390: auto *ctrlbox = new wxBoxSizer(wxHORIZONTAL);
391:
392: // プレーン選択チェックボックス (#3, #2, #1, #0 の順に並べる)
393: for (int i = planesw.size() - 1; i >= 0; i--) {
394: planesw[i] = new wxCheckBox(ctrlpanel, ID_PLANE(i),
395: string_format("#%d", i));
396: ctrlbox->Add(planesw[i], 0, wxALIGN_CENTER);
397: }
398:
399: // パレット合成チェックボックス
400: applysw = new wxCheckBox(ctrlpanel, ID_PALETTE, _("Apply palette"));
401: ctrlbox->Add(applysw, 0, wxALIGN_CENTER);
402:
403: // セパレータを出したいのだが…
404: ctrlbox->Add(new wxStaticText(ctrlpanel, wxID_ANY, "|"), 0, wxALIGN_CENTER);
405:
406: // 倍率
407: wxString scale_choice[] = {
408: "x1",
409: "x2",
410: "x4",
411: };
412: scalesw = new wxChoice(ctrlpanel, ID_SCALE,
413: wxDefaultPosition, wxDefaultSize,
414: countof(scale_choice), scale_choice);
415: ctrlbox->Add(scalesw, 0, wxALIGN_CENTER);
416:
417: // sizer と下敷きパネルを紐付ける
418: ctrlpanel->SetSizer(ctrlbox);
419: ctrlbox->SetSizeHints(ctrlpanel);
420:
421: viewpanel = new WXPlaneVRAMPanel(this);
422: topsizer->Add(viewpanel, 1, wxEXPAND);
423:
424: SetSizer(topsizer);
425:
426: // 最小サイズは上コントロール部の横幅が最小基準
427: wxSize minsize = ctrlpanel->GetMinClientSize();
428: minsize.y += viewpanel->GetMinClientSize().y;
429: SetMinClientSize(minsize);
430: // 最大サイズは DoScale() 内で設定している
431:
432: // 1bpp ならプレーン選択はすべて無効、4bpp ならすべて有効。
433: if (gPlaneVRAM->GetPlaneCount() == 1) {
434: for (int i = 0; i < planesw.size(); i++) {
435: planesw[i]->Enable(false);
436: }
437: }
438:
439: // 初期値をコントロールにセットする
440: if (sticky_used) {
441: // 前回の値があればそれを使う。
442: for (int i = 0; i < planesw.size(); i++) {
443: planesw[i]->SetValue(sticky_plane[i]);
444: }
445: applysw->SetValue(sticky_apply);
446: scalesw->SetSelection(sticky_scale);
447: } else {
448: if (gPlaneVRAM->GetPlaneCount() == 1) {
449: // 初回 1bpp なら #0 プレーンのみオン。
450: for (int i = 0; i < planesw.size(); i++) {
451: planesw[i]->SetValue((i == 0));
452: }
453: applysw->SetValue(true);
454: } else {
455: // 初回 4bpp なら全プレーンオン。
456: for (int i = 0; i < planesw.size(); i++) {
457: planesw[i]->SetValue(true);
458: }
459: applysw->SetValue(true);
460: }
461: scalesw->SetSelection(0);
462: }
463:
464: // SetValue() ではイベントが飛ばないので、
465: // 直接呼び出してイベントが来たことにする。うーん…。
466: for (int i = 0; i < planesw.size(); i++) {
467: DoPlane(i, planesw[i]->IsChecked());
468: }
469: DoApplyPalette(applysw->IsChecked());
470: DoScale(scalesw->GetSelection());
471: }
472:
473: // デストラクタ
474: WXPlaneVRAMWindow::~WXPlaneVRAMWindow()
475: {
476: }
477:
478: void
479: WXPlaneVRAMWindow::FontChanged()
480: {
481: // 親 (wxSubWindow) の FontChanged() を実行しないようにするための
482: // 空のオーバーライド。親の FontChanged() が呼ぶ DoSize() が
483: // ウィンドウサイズを変更しようとするが、ここはテキストフォントを
484: // 持っていないのでその操作は不要。うーん。
485: }
486:
487: // プレーン選択のチェックボックスイベント
488: void
489: WXPlaneVRAMWindow::OnPlane(wxCommandEvent& event)
490: {
491: int plane = event.GetId() - ID_PLANE0;
492: bool value = event.IsChecked();
493:
494: DoPlane(plane, value);
495: }
496:
497: // プレーン選択 ON/OFF 処理
498: void
499: WXPlaneVRAMWindow::DoPlane(int plane, bool value)
500: {
501: // コントロールに指示
502: viewpanel->EnablePlane(plane, value);
503:
504: // 値を保存
505: sticky_plane[plane] = value;
506: sticky_used = true;
507: }
508:
509: // パレット合成のチェックボックスイベント
510: void
511: WXPlaneVRAMWindow::OnApplyPalette(wxCommandEvent& event)
512: {
513: bool value = event.IsChecked();
514:
515: DoApplyPalette(value);
516: }
517:
518: // パレット合成 ON/OFF 処理
519: void
520: WXPlaneVRAMWindow::DoApplyPalette(bool value)
521: {
522: // コントロールに指示
523: viewpanel->EnablePalette(value);
524:
525: // 値を保存
526: sticky_apply = value;
527: sticky_used = true;
528: }
529:
530: // 倍率変更イベント
531: void
532: WXPlaneVRAMWindow::OnScale(wxCommandEvent& event)
533: {
534: int scale_index = event.GetSelection();
535:
536: DoScale(scale_index);
537: }
538:
539: // 倍率変更処理
540: void
541: WXPlaneVRAMWindow::DoScale(int scale_index)
542: {
543: viewpanel->SetScale(1 << scale_index);
544:
545: // 最大サイズは表示部の最大サイズから
546: wxSize maxsize = viewpanel->GetMaxClientSize();
547: maxsize.y += ctrlpanel->GetMinClientSize().y;
548: SetMaxClientSize(maxsize);
549:
550: // 保存しておくのはインデックスのほう
551: sticky_scale = scale_index;
552: }
553:
554: // static 変数
555: std::array<bool, WXPlaneVRAMWindow::NPLANES> WXPlaneVRAMWindow::sticky_plane;
556: bool WXPlaneVRAMWindow::sticky_apply;
557: int WXPlaneVRAMWindow::sticky_scale;
558: bool WXPlaneVRAMWindow::sticky_used;
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.