From a775cc409f52bfe3a2b9d5fe7aacf9d5010897c6 Mon Sep 17 00:00:00 2001 From: Rafael Kitover Date: Wed, 2 Nov 2016 18:14:58 -0700 Subject: [PATCH] fix kbd controls on Mac + misc. After creating the drawing panel, call SetFocus() on it and use Connect() to bind keyboard events from it. Add the wxWANTS_CHARS flag to all DrawingArea subclasses so that wxEVT_CHAR_HOOK can be used instead of wxEVT_KEY_DOWN, because it is more general and catches more keys. Change the process_key_press function to return a bool indicating whether a game control is currently pressed or not, this is used in the key events to determine whether the event should be propagated or not. If in a game key, do not propagate the event, otherwise it hits one of the other controls and generates a beep sound. The menu open/closed/highlighted events had to be turned off for Mac, because the menubar is catching all keyboard events for some reason even before they reach the drawing panel event handler. So on Mac the game will not be paused when the menu is being used, this is not really a big deal and can be fixed later. Other improvements: * do not bundle and link dylibs when CMAKE_BUILD_TYPE is not "Release", this makes for quicker debug builds * finally make a generic PaintEv for the DrawingPanel abstract base class using dynamic_cast<> and Bind(), unfortunately this is not wx 2.8 compatible * set the default audio_buffers to 10 instead of 5, this completely or almost completely fixes sound stuttering during normal game play on Mac with OpenAL * spew path info on startup only once --- src/wx/CMakeLists.txt | 8 ++++-- src/wx/drawing.h | 20 ------------- src/wx/opts.cpp | 5 +++- src/wx/panel.cpp | 66 +++++++++++++++++++++++-------------------- src/wx/wxvbam.cpp | 26 ++++++++++++----- src/wx/wxvbam.h | 9 +----- 6 files changed, 65 insertions(+), 69 deletions(-) diff --git a/src/wx/CMakeLists.txt b/src/wx/CMakeLists.txt index 9821028a..b700729d 100644 --- a/src/wx/CMakeLists.txt +++ b/src/wx/CMakeLists.txt @@ -247,8 +247,12 @@ if(APPLE) SET(MACOSX_BUNDLE_ICON_FILE ${VBAM_ICON}) SET_SOURCE_FILES_PROPERTIES(${VBAM_ICON_PATH} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - ADD_CUSTOM_COMMAND(TARGET visualboyadvance-m POST_BUILD - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tools/osx/third_party_libs_tool "$/../..") + # budle dylibs and relink them for releasing .app + # but only in Release mode + IF(CMAKE_BUILD_TYPE STREQUAL "Release") + ADD_CUSTOM_COMMAND(TARGET visualboyadvance-m POST_BUILD + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tools/osx/third_party_libs_tool "$/../..") + ENDIF() endif(APPLE) SET(WX_EXE_NAME visualboyadvance-m-wx${CMAKE_EXECUTABLE_SUFFIX}) diff --git a/src/wx/drawing.h b/src/wx/drawing.h index 7c63ab01..f595b5de 100644 --- a/src/wx/drawing.h +++ b/src/wx/drawing.h @@ -8,14 +8,9 @@ public: BasicDrawingPanel(wxWindow* parent, int _width, int _height); protected: - void PaintEv2(wxPaintEvent& ev) - { - PaintEv(ev); - } void DrawArea(wxWindowDC& dc); DECLARE_CLASS() - DECLARE_EVENT_TABLE() }; #ifndef NO_OGL @@ -27,10 +22,6 @@ public: virtual ~GLDrawingPanel(); protected: - void PaintEv2(wxPaintEvent& ev) - { - PaintEv(ev); - } void DrawArea(wxWindowDC& dc); #if wxCHECK_VERSION(2, 9, 0) wxGLContext* ctx; @@ -40,7 +31,6 @@ protected: int texsize; DECLARE_CLASS() - DECLARE_EVENT_TABLE() }; #endif @@ -50,15 +40,10 @@ public: DXDrawingPanel(wxWindow* parent, int _width, int _height); protected: - void PaintEv2(wxPaintEvent& ev) - { - PaintEv(ev); - } void DrawArea(wxWindowDC&); void DrawingPanelInit(); DECLARE_CLASS() - DECLARE_EVENT_TABLE() }; #endif @@ -71,15 +56,10 @@ public: ~CairoDrawingPanel(); protected: - void PaintEv2(wxPaintEvent& ev) - { - PaintEv(ev); - } void DrawArea(wxWindowDC&); cairo_surface_t* conv_surf; DECLARE_CLASS() - DECLARE_EVENT_TABLE() }; #endif diff --git a/src/wx/opts.cpp b/src/wx/opts.cpp index e47becf9..3dad7a73 100644 --- a/src/wx/opts.cpp +++ b/src/wx/opts.cpp @@ -293,7 +293,10 @@ opts_t::opts_t() if (max_threads < 0) max_threads = 2; - audio_buffers = 5; + // 10 fixes stuttering on mac with openal, as opposed to 5 + // also should be better for modern hardware in general + audio_buffers = 10; + sound_en = 0x30f; sound_vol = 100; sound_qual = 1; diff --git a/src/wx/panel.cpp b/src/wx/panel.cpp index f70c7d18..4726ee17 100644 --- a/src/wx/panel.cpp +++ b/src/wx/panel.cpp @@ -1029,7 +1029,6 @@ void GameArea::OnIdle(wxIdleEvent& event) wxWindow* w = panel->GetWindow(); w->SetBackgroundStyle(wxBG_STYLE_CUSTOM); - w->Enable(false); // never give it the keyboard focus w->SetSize(wxSize(basic_width, basic_height)); if (maxScale) @@ -1045,6 +1044,13 @@ void GameArea::OnIdle(wxIdleEvent& event) // if user changed Display/Scale config, this needs to run AdjustMinSize(); AdjustSize(false); + + // set focus to panel + w->SetFocus(); + + // capture keyboard events + w->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(GameArea::OnKeyDown), NULL, this); + w->Connect(wxEVT_KEY_UP, wxKeyEventHandler(GameArea::OnKeyUp), NULL, this); } if (!paused && (!pauseWhenInactive || wxGetApp().frame->HasFocus())) { @@ -1125,8 +1131,10 @@ static uint32_t bmask[NUM_KEYS] = { static wxJoyKeyBinding_v keys_pressed; -static void process_key_press(bool down, int key, int mod, int joy = 0) +static bool process_key_press(bool down, int key, int mod, int joy = 0) { + static bool in_game_key = false; + // check if key is already pressed int kpno; @@ -1137,14 +1145,14 @@ static void process_key_press(bool down, int key, int mod, int joy = 0) if (kpno < keys_pressed.size()) { // double press is noop if (down) - return; + return in_game_key; // otherwise forget it keys_pressed.erase(keys_pressed.begin() + kpno); } else { // double release is noop if (!down) - return; + return in_game_key; // otherwise remember it // c++0x @@ -1153,6 +1161,8 @@ static void process_key_press(bool down, int key, int mod, int joy = 0) keys_pressed.push_back(jb); } + bool matched_game_key = false; + // find all game keys this is bound to for (int i = 0; i < 4; i++) for (int j = 0; j < NUM_KEYS; j++) { @@ -1160,8 +1170,10 @@ static void process_key_press(bool down, int key, int mod, int joy = 0) for (int k = 0; k < b.size(); k++) if (b[k].key == key && b[k].mod == mod && b[k].joy == joy) { - if (down) + if (down) { joypress[i] |= bmask[j]; + matched_game_key = true; + } else { // only release if no others pressed int k2; @@ -1178,25 +1190,29 @@ static void process_key_press(bool down, int key, int mod, int joy = 0) break; } - if (k2 == b.size()) + if (k2 == b.size()) { joypress[i] &= ~bmask[j]; + matched_game_key = true; + } } break; } } + + in_game_key = matched_game_key; + + return in_game_key; } void GameArea::OnKeyDown(wxKeyEvent& ev) { - process_key_press(true, ev.GetKeyCode(), 0 /* ev.GetModifiers() */); - ev.Skip(); // process accelerators + ev.Skip(!process_key_press(true, ev.GetKeyCode(), 0 /* ev.GetModifiers() */)); } void GameArea::OnKeyUp(wxKeyEvent& ev) { - process_key_press(false, ev.GetKeyCode(), 0 /* ev.GetModifiers() */); - ev.Skip(); // process accelerators + ev.Skip(!process_key_press(false, ev.GetKeyCode(), 0 /* ev.GetModifiers() */)); } void GameArea::OnSDLJoy(wxSDLJoyEvent& ev) @@ -1378,6 +1394,11 @@ DrawingPanel::DrawingPanel(int _width, int _height) void DrawingPanel::DrawingPanelInit() { + wxWindow* w = dynamic_cast(this); + + // this is not 2.8 compatible, sorry + w->Bind(wxEVT_PAINT, &DrawingPanel::PaintEv, this); + did_init = true; } @@ -1894,13 +1915,9 @@ DrawingPanel::~DrawingPanel() IMPLEMENT_CLASS2(BasicDrawingPanel, DrawingPanel, wxPanel) -BEGIN_EVENT_TABLE(BasicDrawingPanel, wxPanel) -EVT_PAINT(BasicDrawingPanel::PaintEv2) -END_EVENT_TABLE() - BasicDrawingPanel::BasicDrawingPanel(wxWindow* parent, int _width, int _height) : wxPanel(parent, wxID_ANY, wxPoint(0, 0), parent->GetSize(), - wxFULL_REPAINT_ON_RESIZE) + wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) , DrawingPanel(_width, _height) { // wxImage is 24-bit RGB, so 24-bit is preferred. Filters require @@ -1980,11 +1997,6 @@ void BasicDrawingPanel::DrawArea(wxWindowDC& dc) IMPLEMENT_CLASS2(GLDrawingPanel, DrawingPanel, wxGLCanvas) -// this would be easier in 2.9 -BEGIN_EVENT_TABLE(GLDrawingPanel, wxGLCanvas) -EVT_PAINT(GLDrawingPanel::PaintEv2) -END_EVENT_TABLE() - // This is supposed to be the default, but DOUBLEBUFFER doesn't seem to be // turned on by default for wxGTK. static int glopts[] = { @@ -2001,7 +2013,7 @@ static int glopts[] = { GLDrawingPanel::GLDrawingPanel(wxWindow* parent, int _width, int _height) : glc(parent, wxID_ANY, glopts, wxPoint(0, 0), parent->GetSize(), - wxFULL_REPAINT_ON_RESIZE) + wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) , DrawingPanel(_width, _height) { #ifdef __WXMAC__ @@ -2166,13 +2178,9 @@ void GLDrawingPanel::DrawArea(wxWindowDC& dc) IMPLEMENT_CLASS(CairoDrawingPanel, DrawingPanel) -BEGIN_EVENT_TABLE(CairoDrawingPanel, wxPanel) -EVT_PAINT(CairoDrawingPanel::PaintEv2) -END_EVENT_TABLE() - CairoDrawingPanel::CairoDrawingPanel(wxWindow* parent, int _width, int _height) : wxPanel(parent, wxID_ANY, wxPoint(0, 0), parent->GetSize(), - wxFULL_REPAINT_ON_RESIZE) + wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) , DrawingPanel(_width, _height) { conv_surf = NULL; @@ -2295,13 +2303,9 @@ void CairoDrawingPanel::DrawArea(wxWindowDC& dc) IMPLEMENT_CLASS(DXDrawingPanel, DrawingPanel) -BEGIN_EVENT_TABLE(DXDrawingPanel, wxPanel) -EVT_PAINT(DXDrawingPanel::PaintEv2) -END_EVENT_TABLE() - DXDrawingPanel::DXDrawingPanel(wxWindow* parent, int _width, int _height) : wxPanel(parent, wxID_ANY, wxPoint(0, 0), parent->GetSize(), - wxFULL_REPAINT_ON_RESIZE) + wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) , DrawingPanel(_width, _height) { // FIXME: implement diff --git a/src/wx/wxvbam.cpp b/src/wx/wxvbam.cpp index 738d2aca..6443b879 100644 --- a/src/wx/wxvbam.cpp +++ b/src/wx/wxvbam.cpp @@ -71,13 +71,19 @@ static void get_config_path(wxPathList& path, bool exists = true) path.Add(s); \ } while (0) - vbamDebug("GetUserLocalDataDir(): %s", static_cast(stdp.GetUserLocalDataDir().utf8_str())); - vbamDebug("GetUserDataDir(): %s", static_cast(stdp.GetUserDataDir().utf8_str())); - vbamDebug("GetLocalizedResourcesDir(wxGetApp().locale.GetCanonicalName()): %s", static_cast(stdp.GetLocalizedResourcesDir(wxGetApp().locale.GetCanonicalName()).utf8_str())); - vbamDebug("GetResourcesDir(): %s", static_cast(stdp.GetResourcesDir().utf8_str())); - vbamDebug("GetDataDir(): %s", static_cast(stdp.GetDataDir().utf8_str())); - vbamDebug("GetLocalDataDir(): %s", static_cast(stdp.GetLocalDataDir().utf8_str())); - vbamDebug("GetPluginsDir(): %s", static_cast(stdp.GetPluginsDir().utf8_str())); + static bool debug_dumped = false; + + if (!debug_dumped) { + vbamDebug("GetUserLocalDataDir(): %s", static_cast(stdp.GetUserLocalDataDir().utf8_str())); + vbamDebug("GetUserDataDir(): %s", static_cast(stdp.GetUserDataDir().utf8_str())); + vbamDebug("GetLocalizedResourcesDir(wxGetApp().locale.GetCanonicalName()): %s", static_cast(stdp.GetLocalizedResourcesDir(wxGetApp().locale.GetCanonicalName()).utf8_str())); + vbamDebug("GetResourcesDir(): %s", static_cast(stdp.GetResourcesDir().utf8_str())); + vbamDebug("GetDataDir(): %s", static_cast(stdp.GetDataDir().utf8_str())); + vbamDebug("GetLocalDataDir(): %s", static_cast(stdp.GetLocalDataDir().utf8_str())); + vbamDebug("GetPluginsDir(): %s", static_cast(stdp.GetPluginsDir().utf8_str())); + + debug_dumped = true; + } // NOTE: this does not support XDG (freedesktop.org) paths add_path(GetUserLocalDataDir()); @@ -591,10 +597,16 @@ EVT_CONTEXT_MENU(MainFrame::OnMenu) EVT_ACTIVATE(MainFrame::OnActivate) // requires DragAcceptFiles(true); even then may not do anything EVT_DROP_FILES(MainFrame::OnDropFile) + // pause game if menu pops up +// +// this causes problems with keyboard game keys on mac, disable for now +#ifndef __WXMAC__ EVT_MENU_OPEN(MainFrame::MenuPopped) EVT_MENU_CLOSE(MainFrame::MenuPopped) EVT_MENU_HIGHLIGHT_ALL(MainFrame::MenuPopped) +#endif + END_EVENT_TABLE() void MainFrame::OnActivate(wxActivateEvent& event) diff --git a/src/wx/wxvbam.h b/src/wx/wxvbam.h index 40c2d1d9..50f96669 100644 --- a/src/wx/wxvbam.h +++ b/src/wx/wxvbam.h @@ -630,10 +630,10 @@ public: void DrawArea(uint8_t** pixels); // using dynamic_cast<> to not force trivial reimplementation in concrete classes - // TODO: figure something out for PaintEv as well virtual wxWindow* GetWindow() { return dynamic_cast(this); } virtual void Delete() { (dynamic_cast(this))->Destroy(); } + void PaintEv(wxPaintEvent& ev); protected: virtual void DrawArea(wxWindowDC&) = 0; virtual void DrawOSD(wxWindowDC&); @@ -651,13 +651,6 @@ protected: // largest buffer required is 32-bit * (max width + 1) * (max height + 2) uint8_t delta[257 * 4 * 226]; - // following can't work in 2.8 as intended - // inheriting from wxEvtHandler is required, but also breaks subclasses - // due to lack of virtual inheritance (2.9 drops wxEvtHandler req) - // so each child must have a paint event handler (not override of this, - // but it's not virtual anyway, so that won't happen) that calls this - void PaintEv(wxPaintEvent& ev); - DECLARE_ABSTRACT_CLASS() };