--- nono/wx/wxmainview.cpp 2026/04/29 17:04:33 1.1.1.3 +++ nono/wx/wxmainview.cpp 2026/04/29 17:04:51 1.1.1.7 @@ -1,19 +1,15 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "wxheader.h" +#include "wxmainview.h" #include "keyboard.h" -#include "mystring.h" #include "renderer.h" #include "scheduler.h" -#include "wxmainview.h" #include #include -#if defined(__NetBSD__) -#include // __NetBSD_Version__ -#endif //#define KEYDEBUG 1 //#define MOUSEDEBUG 1 @@ -40,13 +36,12 @@ // Refresh{,Rect}() を呼ばず、再描画矩形を画面全体として運用する。 // ちょっと無駄なだけなので (動かないことに比べれば) 気にしない。 // -// 今のところ NetBSD 5.0 以上(と Linux (CentOS 6.3/x86_64)) が該当する。 -#if defined(__NetBSD__) && __NetBSD_Version__ >= 500000000 +// 今のところ NetBSD (5.0 以降) と OpenBSD(/amd64)(-6.7) +// (と Linux (CentOS 6.3/x86_64)) が該当する。 +#if defined(__NetBSD__) || defined(__OpenBSD__) #define DONT_USE_REFRESH 1 #endif -#define TIMERID_PAINTTIMER 1 - static void wxmainview_refresh(); WXMainView *gMainView; @@ -56,7 +51,7 @@ wxBEGIN_EVENT_TABLE(WXMainView, inherite EVT_KEY_DOWN(WXMainView::OnKeyDown) EVT_KEY_UP(WXMainView::OnKeyUp) EVT_MOUSE_EVENTS(WXMainView::OnMouse) - EVT_TIMER(TIMERID_PAINTTIMER, WXMainView::OnTimer) + EVT_TIMER(wxID_ANY, WXMainView::OnTimer) wxEND_EVENT_TABLE() WXMainView::WXMainView(wxWindow *parent) @@ -68,22 +63,24 @@ WXMainView::WXMainView(wxWindow *parent) // レンダラバッファは常に仮想画面サイズ int buflen = rendwidth * rendheight * 3; - ImageBuf = new uint8_t[buflen]; - memset(ImageBuf, 0x70, buflen); + ImageBuf.reset(new uint8_t[buflen]); + memset(&ImageBuf[0], 0x70, buflen); // レンダラに通知 - gRenderer->SetBuf(ImageBuf, wxmainview_refresh); + gRenderer->SetBuf(ImageBuf.get(), wxmainview_refresh); // 実画面 (ウィンドウの大きさ) DoResize(screen_scale); - paintTimer.SetOwner(this, TIMERID_PAINTTIMER); - paintTimer.Start(16); - paintRequested = true; // first paint request + // 描画を律速するためのタイマー + timer.SetOwner(this); + timer.Start(33); + + // 最初に一度描画 + Refresh(); } WXMainView::~WXMainView() { - delete[] ImageBuf; } // リフレッシュ要求コールバック。 @@ -106,19 +103,24 @@ WXMainView::DoRefresh() RefreshRect(wxRect(0, 0, viewwidth, viewheight), false); #endif - // PAINT イベントを投げる - // XXX んだけど mac だと投げすぎるとシステムイベントを拾えなくなってしまう - paintRequested = true; + // ここは VM Renderer が画面を更新した時に呼ばれるので、本来なら + // ここで単に Refresh() を呼んで再描画を指示すればいいのだが、 + // VM 高速モードになると、VM Renderer が高速で回ってここも高速で + // 呼ばれてしまい、その結果 Refresh() を発行しすぎて Mac では + // システムイベントが拾えなくなってしまう。 + // それを避けるため、ここではフラグを立てるだけにしておいて、 + // こちらで管理している実時間タイマーで Refresh() を律速する。 + paint_request = true; } +// タイマーイベント void WXMainView::OnTimer(wxTimerEvent& event) { - bool req = paintRequested.exchange(false); + bool req = paint_request.exchange(false); if (req) { - // PAINT イベントを投げる - wxPaintEvent *ev = new wxPaintEvent(); - QueueEvent(ev); + // 実際に再描画する + Refresh(); } } @@ -126,29 +128,27 @@ WXMainView::OnTimer(wxTimerEvent& event) void WXMainView::OnPaint(wxPaintEvent& event) { - wxClientDC dc(this); - - DoPaint(dc); -} - -// 描画実行 -void -WXMainView::DoPaint(wxDC& dc) -{ if (viewwidth == 0 || viewheight == 0) return; - wxImage image(rendwidth, rendheight, ImageBuf, true); - + wxImage image(rendwidth, rendheight, ImageBuf.get(), true); if (viewwidth != rendwidth || viewheight != rendheight) { - // wxIMAGE_QUALITY_NORMAL だと速いが、品質優先。 - image.Rescale(viewwidth, viewheight, wxIMAGE_QUALITY_HIGH); + // wxIMAGE_QUALITY_NORMAL だと速いが、ここは品質優先。 +#if _BYTE_ORDER == _BIG_ENDIAN + // XXX ..のはずだったが wxWidget 3.0.4 の QUALITY_HIGH は + // BigEndian だとバグってるようだ。QUALITY_NORMAL のほうは大丈夫。 + auto quality = wxIMAGE_QUALITY_NORMAL; +#else + auto quality = wxIMAGE_QUALITY_HIGH; +#endif + image.Rescale(viewwidth, viewheight, quality); } wxBitmap bitmap(image); wxMemoryDC memDC; memDC.SelectObject(bitmap); + wxPaintDC dc(this); wxRegion region(0, 0, viewwidth, viewheight); wxRegionIterator ri(region); for (; ri; ri++) { @@ -209,6 +209,8 @@ WXMainView::SetMouseMode(bool mode) // マウスカーソル OFF ::wxSetCursor(wxCursor(wxCURSOR_BLANK)); + CaptureMouse(); + // このウィンドウから外れないように移動を検出し続けるため、 // マウスカーソルをウィンドウ中央に移す。 // ウィンドウとメニューバーが分離してる wxOSX のために必要。 @@ -217,8 +219,11 @@ WXMainView::SetMouseMode(bool mode) // マウスモードを OFF にする DMPRINTF("SetMouseMode OFF\n"); + ReleaseMouse(); + // 分かりやすさのため、マウスカーソルを中央へ戻してから復帰 - MyWarpPointer(GetClientSize() / 2); + wxSize pt = GetClientSize() / 2; + WarpPointer(pt.x, pt.y); // マウスカーソル ON ::wxSetCursor(wxNullCursor); @@ -252,7 +257,7 @@ WXMainView::OnMouse(wxMouseEvent& event) bool rb = event.RightIsDown(); bool lb = event.LeftIsDown(); bool mb = event.MiddleIsDown(); - wxPoint pos = event.GetPosition(); + const wxPoint& pos = event.GetPosition(); int dx = 0; int dy = 0; @@ -267,7 +272,7 @@ WXMainView::OnMouse(wxMouseEvent& event) dstr += " Dragging"; if (motion_by_warp_pointer) dstr += " MotionByWarp"; - if (event.IsButton()) { + if (lb || mb || rb) { dstr += string_format(" Button(%c%c%c)", (lb ? 'L' : '-'), (mb ? 'M' : '-'), @@ -328,6 +333,26 @@ WXMainView::OnMouse(wxMouseEvent& event) event.Skip(); } +// アクティベートイベント。 +// この(トップレベル)ウィンドウにフォーカスが当たるか外れるかで飛んでくる。 +// 実際には WXMainFrame 宛てに飛んできたイベントをこちらに転送してある。 +// マウスモード中にこのウィンドウからフォーカスが外れたら (例えば ALT+TAB の +// ようなウィンドウを切り替える操作)、マウスモードを解除するため。 +void +WXMainView::OnActivate(wxActivateEvent& event) +{ + // フォーカスが外れる際にマウスモード中ならマウスモードを解除 + if (event.GetActive() == false) { + DMPRINTF("OnActivate FocusOut\n"); + if (mousemode) { + SetMouseMode(false); + } + } else { + DMPRINTF("OnActivate FocusIn\n"); + } +} + + // // キー入力 //