From fdc389c280aa304bbe494936d85bc1f40675bdab Mon Sep 17 00:00:00 2001 From: Rafael Kitover Date: Wed, 25 Jan 2017 10:53:05 -0800 Subject: [PATCH] fix wx 2.8 compat, debug logging works everywhere Fix backcompat with wx 2.8. This involved writing wxPositiveDoubleValidator and rewiring the DrawingPanel inheritance tree and event handling mechanisms (because 2.8 does not have ->Bind, only ->Connect which is less flexible.) As a result all the event handling has been gathered into GameArea and the affected code is somewhat cleaner. 2.8 support is untested on Mac because it requires 32 bit libs and Carbon. Add support for cross-compiling for windows using the Fedora MinGW packages in ./installdeps. Check for OpenGL support in the wx library being linked, this was necessary because the Fedora MinGW wx library does not have OpenGL support. Remove vbamDebug() in favor of wxLogDebug(), and add an override for it so that it works in non-debug builds of wx as well as on Windows. Turn off buffering on stdout and stderr on startup so that debug logging works in msys2/cygwin mintty as well. On Windows, build a console binary for debug builds. Update README.md to reflect Fedora MinGW support and debug logging support. Add -Wextra to cflags for debug builds. --- CMakeLists.txt | 14 ++- CMakeScripts/FindSDL2.cmake | 9 +- README.md | 27 +++++- installdeps | 56 +++++++++--- src/wx/CMakeLists.txt | 75 +++++++++++++++-- src/wx/cmdevents.cpp | 6 +- src/wx/drawing.h | 35 ++++---- src/wx/guiinit.cpp | 6 +- src/wx/macsupport.mm | 2 - src/wx/panel.cpp | 164 ++++++++++++++++++++---------------- src/wx/widgets/wx/keyedit.h | 10 ++- src/wx/widgets/wx/wxmisc.h | 13 +++ src/wx/widgets/wxmisc.cpp | 53 ++++++++++++ src/wx/wxvbam.cpp | 45 +++++----- src/wx/wxvbam.h | 51 ++++++++--- 15 files changed, 402 insertions(+), 164 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8839145d..d0f4423b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,13 +124,21 @@ ENDIF() FIND_PACKAGE(ZLIB REQUIRED) FIND_PACKAGE(OpenGL REQUIRED) -# TODO: make static on mac +# TODO: make static on mac and fedora cross-compiles FIND_PACKAGE(PNG REQUIRED) IF(APPLE AND NOT MACPORTS) SET(SDL2_STATIC ON) ENDIF() +IF(EXISTS /etc/redhat-release) + SET(FEDORA_HOST ON) +ENDIF() + +IF(WINDOWS AND FEDORA_HOST) + SET(SDL2_STATIC ON) +ENDIF() + FIND_PACKAGE(SDL2 REQUIRED) ADD_DEFINITIONS(${SDL2_DEFINITIONS}) @@ -299,8 +307,8 @@ IF(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") ENDIF(MINGW) IF(CMAKE_BUILD_TYPE STREQUAL Debug) - SET(MY_C_FLAGS ${MY_C_FLAGS} ${MY_C_AND_CXX_DBG_FLAGS} -Wall) - SET(MY_CXX_FLAGS ${MY_CXX_FLAGS} ${MY_C_AND_CXX_DBG_FLAGS} -Wall) + SET(MY_C_FLAGS ${MY_C_FLAGS} ${MY_C_AND_CXX_DBG_FLAGS} -Wall -Wextra) + SET(MY_CXX_FLAGS ${MY_CXX_FLAGS} ${MY_C_AND_CXX_DBG_FLAGS} -Wall -Wextra) ELSE() SET(MY_C_FLAGS ${MY_C_FLAGS} ${MY_C_AND_CXX_OPT_FLAGS} -Wno-error) SET(MY_CXX_FLAGS ${MY_CXX_FLAGS} ${MY_C_AND_CXX_OPT_FLAGS} -Wno-error) diff --git a/CMakeScripts/FindSDL2.cmake b/CMakeScripts/FindSDL2.cmake index 82f66b38..5a25af08 100644 --- a/CMakeScripts/FindSDL2.cmake +++ b/CMakeScripts/FindSDL2.cmake @@ -142,10 +142,15 @@ IF(NOT APPLE) FIND_PACKAGE(Threads) ENDIF(NOT APPLE) -# MinGW needs an additional link flag, -mwindows +# MinGW needs an additional link flag, -mwindows (to make a GUI app) +# but we only add it when not making a Debug build. # It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -mwindows IF(MINGW) - SET(MINGW32_LIBRARY -lmingw32 -mwindows CACHE STRING "mwindows for MinGW") + SET(MINGW32_LIBRARY -lmingw32 CACHE STRING "MinGW library") + + IF(NOT CMAKE_BUILD_TYPE STREQUAL Debug) + SET(MINGW32_LIBRARY ${MINGW32_LIBRARY} -mwindows) + ENDIF() ENDIF(MINGW) IF(SDL2_LIBRARY_TEMP) diff --git a/README.md b/README.md index e65ac4cf..3b66489b 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,9 @@ value of `$MSYSTEM`.) It will not run in the MSys shell. On Debian/Ubuntu this uses the MXE apt repository and works really well. +On Fedora it can build using the Fedora MinGW packages, albeit with wx 2.8, no +OpenGL support, and no Link support for lack of SFML. + On Arch it currently doesn't work at all because the AUR stuff is completely broken, I will at some point redo the arch stuff to use MXE as well. @@ -110,8 +113,8 @@ To run the resulting binary, you can simply type: in the shell where you built it. -**NOTE:** you will not see debug console messages because it is a GUI app, we -will try to fix this for debug builds. +If you built with `-DCMAKE_BUILD_TYPE=Debug`, you will get a console app and +will see debug messages, even in mintty. If you want to start the binary from e.g. a shortcut or Explorer, you will need to put `c:\msys64\mingw32\bin` for 32 bit builds and `c:\msys64\mingw64\bin` @@ -123,6 +126,26 @@ depends on, they can install to the same directory as the binary. For our own builds, we use MXE to make static builds. +## Debug Messages + +We have an override for `wxLogDebug()` to make it work even in non-debug builds +of wx and on windows, even in mintty. Using this function for console debug +messages is recommended. + +It works like `printf()`, but using `wxString`, so use `wxT()` e.g.: + +```cpp +int foo = 42; +wxLogDebug(wxT("the value of foo = %d"), foo); +``` + +`%s` does not work for `wxString`, so use concatenation instead, e.g.: + +```cpp +wxString world = "world"; +wxLogDebug(wxT("Hello, ") + world + wxT("!")); +``` + ## CONTRIBUTING Please keep in mind that this app needs to run on Windows, Linux and Mac OS X at diff --git a/installdeps b/installdeps index fd2d25da..df5ed763 100755 --- a/installdeps +++ b/installdeps @@ -92,11 +92,13 @@ error() { } warning() { + [ -z "$1" ] && return 0 printf '\nWARNING: %s.\n\n' "$1" >&2 } info_msg() { + [ -z "$1" ] && return 0 printf '\nINFO: %s.\n\n' "$1" >&2 } @@ -167,8 +169,8 @@ check_cross() { fi fi - if [ -z "$arch_linux" -a -z "$msys2" -a -z "$debian" ]; then - error 'cross compiling targets are only supported on Debian/Ubuntu, Arch Linux and MSYS2 at the moment' + if [ -z "$arch_linux" -a -z "$msys2" -a -z "$debian" -a -z "$fedora" ]; then + error 'cross compiling targets are only supported on Debian/Ubuntu, Fedora, Arch Linux and MSYS2 at the moment' fi target=$(echo "$target" | tr 'A-Z' 'a-z') @@ -240,23 +242,55 @@ fedora_installdeps() { check_cross installing + warning= + # make sure rpmfusion is installed for ffmpeg check sudo su -c 'dnf -y install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm' # non-multiarch packages first - check sudo dnf -y install gcc gcc-c++ make cmake git nasm + check sudo dnf -y install gcc gcc-c++ make cmake git nasm redhat-rpm-config - # install both 64bit and 32bit versions on 64 bit hosts set -- - for pkg in pkgconfig zlib-devel mesa-libGL-devel ffmpeg-devel gettext-devel libjpeg-turbo-devel libpng-devel libtiff-devel SDL2-devel SFML-devel openal-soft-devel wxGTK3-devel; do - if [ -n "$amd64" ]; then - set -- "$@" "${pkg}.x86_64" "${pkg}.i686" - else - set -- "$@" "$pkg" - fi - done + if [ -z "$target" ]; then + # install both 64bit and 32bit versions on 64 bit hosts + for pkg in pkgconfig zlib-devel mesa-libGL-devel ffmpeg-devel gettext-devel libjpeg-turbo-devel libpng-devel libtiff-devel SDL2-devel SFML-devel openal-soft-devel wxGTK3-devel; do + if [ -n "$amd64" ]; then + set -- "$@" "${pkg}.x86_64" "${pkg}.i686" + else + set -- "$@" "$pkg" + fi + done + else # mingw build + set -- "$@" pkgconfig + case "$target" in + mingw-w64-i686) + target=mingw32 + cmake_flags='-DCMAKE_TOOLCHAIN_FILE=../CMakeScripts/Toolchain-cross-MinGW-w64-i686.cmake -DENABLE_LINK=NO' + ;; + mingw-w64-x86_64) + target=mingw64 + cmake_flags='-DCMAKE_TOOLCHAIN_FILE=../CMakeScripts/Toolchain-cross-MinGW-w64-x86_64.cmake -DENABLE_LINK=NO' + ;; + *) + error 'unknown cross target (this should not happen)' + ;; + esac + # install static deps + for pkg in zlib gettext libjpeg-turbo libpng libtiff SDL2 wxWidgets; do + set -- "$@" "${target}-${pkg}-static" + done + # install deps that are not available as static + for pkg in openal-soft; do + set -- "$@" "${target}-${pkg}" + done + + warning='SFML is required for LINK support, Fedora does not currently have a MinGW SFML package, if you want LINK support you will need to install it manually' + fi + check sudo dnf -y install "$@" + warning "$warning" + build_instructions } diff --git a/src/wx/CMakeLists.txt b/src/wx/CMakeLists.txt index c0dbc1d9..852d676c 100644 --- a/src/wx/CMakeLists.txt +++ b/src/wx/CMakeLists.txt @@ -48,23 +48,74 @@ endif(ENABLE_OPENAL) IF(CMAKE_BUILD_TYPE STREQUAL "Debug") SET(wxWidgets_USE_DEBUG ON) # noop if wx is compiled with --disable-debug, like in Mac Homebrew atm - - # and if this is the case, we can't set debug level without link failing - IF(NOT wxWidgets_DEFINITIONS MATCHES "-DwxDEBUG_LEVEL=0") - ADD_DEFINITIONS(-DwxDEBUG_LEVEL=1) - ENDIF() ENDIF() -IF(APPLE) +IF(APPLE OR (WINDOWS AND FEDORA_HOST)) SET(wxWidgets_USE_STATIC ON) -ENDIF(APPLE) +ENDIF() SET(wxWidgets_USE_UNICODE ON) # adv is for wxAboutBox # xml, html is for xrc -SET( wxWidgets_USE_LIBS xrc xml html adv gl net core base ) +SET(wxWidgets_USE_LIBS xrc xml html adv gl net core base gl) #list(APPEND wxWidgets_CONFIG_OPTIONS --version=2.8) -FIND_PACKAGE ( wxWidgets REQUIRED ) +FIND_PACKAGE(wxWidgets REQUIRED) +INCLUDE_DIRECTORIES(${wxWidgets_INCLUDE_DIRS}) + +IF(CMAKE_BUILD_TYPE STREQUAL "Debug") + # tell wx to enable debug mode if possible, if the cmake module did not do it for us + EXECUTE_PROCESS(COMMAND "${wxWidgets_CONFIG_EXECUTABLE} --debug=yes" RESULT_VARIABLE WX_CONFIG_DEBUG OUTPUT_QUIET ERROR_QUIET) + + IF(WX_CONFIG_DEBUG EQUAL 0) + ADD_DEFINITIONS(-DwxDEBUG_LEVEL=1) + ENDIF() + + # this one should be safe in non-debug builds too + ADD_DEFINITIONS(-DWXDEBUG) +ENDIF() + +# check if this build of wx actually has OpenGL support + +SET(CURRENT_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) +SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${wxWidgets_LIBRARIES}) +SET(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} ${wxWidgets_CXX_FLAGS} -fPIC) +SET(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${wxWidgets_INCLUDE_DIRS}) + +FOREACH(DEF ${wxWidgets_DEFINITIONS}) + SET(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} "-D${DEF}") + ADD_DEFINITIONS("-D${DEF}") +ENDFOREACH() + +# CheckCXXSourceCompiles ignores compiler flags, so we have to stuff them into the definitions +SET(CURRENT_CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}) +SET(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} ${CMAKE_REQUIRED_FLAGS}) + +INCLUDE(CheckCXXSourceCompiles) + +CHECK_CXX_SOURCE_COMPILES(" +#include +#include + +int main(int argc, char** argv) { + wxGLCanvas canvas(NULL, wxID_ANY, NULL, wxPoint(0, 0), wxSize(300, 300), 0); + return 0; +}" WX_HAS_OPENGL) + +IF(NOT WX_HAS_OPENGL) + ADD_DEFINITIONS(-DNO_OGL) + LIST(REMOVE_ITEM wxWidgets_USE_LIBS gl) + FIND_PACKAGE(wxWidgets REQUIRED) +ENDIF() + +SET(CMAKE_REQUIRED_LIBRARIES ${CURRENT_CMAKE_REQUIRED_LIBRARIES}) +SET(CMAKE_REQUIRED_DEFINITIONS ${CURRENT_CMAKE_REQUIRED_DEFINITIONS}) + +# end of wx OpenGL check + +FOREACH(CXX_COMPILE_FLAG ${wxWidgets_CXX_FLAGS}) + ADD_COMPILE_OPTIONS($<$:${CXX_COMPILE_FLAG}>) +ENDFOREACH() + #EXECUTE_PROCESS(COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" --cxxflags) INCLUDE( ${wxWidgets_USE_FILE} ) FIND_PACKAGE ( Gettext REQUIRED ) @@ -248,6 +299,12 @@ TARGET_LINK_LIBRARIES ( ${DIRECTX_LIBRARIES} ${CAIRO_LIBRARIES} ) + +# Build a console app in debug mode on Windows +IF(WIN32 AND CMAKE_BUILD_TYPE STREQUAL Debug) + SET(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -Wl,--subsystem,console") +ENDIF() + IF(WIN32) INSTALL(PROGRAMS ${PROJECT_BINARY_DIR}/visualboyadvance-m${CMAKE_EXECUTABLE_SUFFIX} DESTINATION ${CMAKE_BINARY_DIR}) ENDIF(WIN32) diff --git a/src/wx/cmdevents.cpp b/src/wx/cmdevents.cpp index c0836784..55124451 100644 --- a/src/wx/cmdevents.cpp +++ b/src/wx/cmdevents.cpp @@ -2270,7 +2270,7 @@ EVT_HANDLER_MASK(DisplayConfigure, "Display options...", CMDEN_NREC_ANY) } if (panel->panel) { - panel->panel->Delete(); + panel->panel->Destroy(); panel->panel = NULL; } @@ -2288,7 +2288,7 @@ EVT_HANDLER_MASK(ChangeFilter, "Change Pixel Filter", CMDEN_NREC_ANY) update_opts(); if (panel->panel) { - panel->panel->Delete(); + panel->panel->Destroy(); panel->panel = NULL; } } @@ -2299,7 +2299,7 @@ EVT_HANDLER_MASK(ChangeIFB, "Change Interframe Blending", CMDEN_NREC_ANY) update_opts(); if (panel->panel) { - panel->panel->Delete(); + panel->panel->Destroy(); panel->panel = NULL; } } diff --git a/src/wx/drawing.h b/src/wx/drawing.h index 7fbe06f0..b2e14206 100644 --- a/src/wx/drawing.h +++ b/src/wx/drawing.h @@ -3,21 +3,33 @@ #include "wxvbam.h" -class BasicDrawingPanel : public DrawingPanel, public wxPanel { +class BasicDrawingPanel : public DrawingPanel { public: BasicDrawingPanel(wxWindow* parent, int _width, int _height); protected: void DrawArea(wxWindowDC& dc); virtual void DrawImage(wxWindowDC& dc, wxImage* im); - - DECLARE_CLASS() }; +// wx <= 2.8 may not be compiled with opengl support +#if !wxCHECK_VERSION(2, 9, 0) && !wxUSE_GLCANVAS + #define NO_OGL +#endif + #ifndef NO_OGL #include -class GLDrawingPanel : public DrawingPanel, public wxGLCanvas { +// shuffled parms for 2.9 indicates non-auto glcontext +// before 2.9, wxMAC does not have this (but wxGTK & wxMSW do) +#if !wxCHECK_VERSION(2, 9, 0) && defined(__WXMAC__) + #define wxglc(a, b, c, d, e, f) wxGLCanvas(a, b, d, e, f, wxEmptyString, c) + #define wxGL_IMPLICIT_CONTEXT +#else + #define wxglc wxGLCanvas +#endif + +class GLDrawingPanel : public DrawingPanelBase, public wxGLCanvas { public: GLDrawingPanel(wxWindow* parent, int _width, int _height); virtual ~GLDrawingPanel(); @@ -26,33 +38,29 @@ protected: void DrawArea(wxWindowDC& dc); void OnSize(wxSizeEvent& ev); void AdjustViewport(); -#if wxCHECK_VERSION(2, 9, 0) +#ifndef wxGL_IMPLICIT_CONTEXT wxGLContext* ctx; #endif void DrawingPanelInit(); GLuint texid, vlist; int texsize; - - DECLARE_CLASS() }; #endif #if defined(__WXMSW__) && !defined(NO_D3D) -class DXDrawingPanel : public DrawingPanel, public wxPanel { +class DXDrawingPanel : public DrawingPanel { public: DXDrawingPanel(wxWindow* parent, int _width, int _height); protected: void DrawArea(wxWindowDC&); - - DECLARE_CLASS() }; #endif #ifndef NO_CAIRO #include -class CairoDrawingPanel : public DrawingPanel, public wxPanel { +class CairoDrawingPanel : public DrawingPanel { public: CairoDrawingPanel(wxWindow* parent, int _width, int _height); ~CairoDrawingPanel(); @@ -60,8 +68,6 @@ public: protected: void DrawArea(wxWindowDC&); cairo_surface_t* conv_surf; - - DECLARE_CLASS() }; #endif @@ -70,9 +76,6 @@ class Quartz2DDrawingPanel : public BasicDrawingPanel { public: Quartz2DDrawingPanel(wxWindow* parent, int _width, int _height); virtual void DrawImage(wxWindowDC& dc, wxImage* im); - -protected: - DECLARE_CLASS() }; #endif diff --git a/src/wx/guiinit.cpp b/src/wx/guiinit.cpp index a63a6f22..5d8cf1b3 100644 --- a/src/wx/guiinit.cpp +++ b/src/wx/guiinit.cpp @@ -2899,10 +2899,10 @@ bool MainFrame::BindControls() tc = SafeXRCCTRL(d, n); \ tc->SetValidator(wxTextValidator(wxFILTER_NONE, &o)); \ } while (0) -#define getgtc(n, o) \ +#define getdtc(n, o) \ do { \ tc = SafeXRCCTRL(d, n); \ - tc->SetValidator(wxGenericValidator(&o)); \ + tc->SetValidator(wxPositiveDoubleValidator(&o)); \ } while (0) #ifndef NO_LINK { @@ -3297,9 +3297,9 @@ bool MainFrame::BindControls() /// On-Screen Display ch = GetValidatedChild(d, "SpeedIndicator", wxGenericValidator(&showSpeed)); /// Zoom + getdtc("DefaultScale", gopts.video_scale); // this was a choice, but I'd rather not have to make an off-by-one // validator just for this, and spinctrl is good enough. - getgtc("DefaultScale", gopts.video_scale); getsc("MaxScale", maxScale); /// Basic getrbi("OutputSimple", gopts.render_method, RND_SIMPLE); diff --git a/src/wx/macsupport.mm b/src/wx/macsupport.mm index 13f7eb0d..4b8aca64 100644 --- a/src/wx/macsupport.mm +++ b/src/wx/macsupport.mm @@ -41,8 +41,6 @@ void HiDPIAware::GetRealPixelClientSize(int* x, int* y) *y = backing_size.height; } -IMPLEMENT_CLASS(Quartz2DDrawingPanel, BasicDrawingPanel) - Quartz2DDrawingPanel::Quartz2DDrawingPanel(wxWindow* parent, int _width, int _height) : BasicDrawingPanel(parent, _width, _height) { diff --git a/src/wx/panel.cpp b/src/wx/panel.cpp index c9c1957e..e1ecdae6 100644 --- a/src/wx/panel.cpp +++ b/src/wx/panel.cpp @@ -532,7 +532,7 @@ void GameArea::UnloadGame(bool destruct) // in destructor, panel should be auto-deleted by wx since all panels // are derived from a window attached as child to GameArea if (panel) - panel->Delete(); + panel->Destroy(); panel = NULL; @@ -680,7 +680,7 @@ void GameArea::AddBorder() GetSizer()->Detach(panel->GetWindow()); if (panel) - panel->Delete(); + panel->Destroy(); panel = NULL; } @@ -699,7 +699,7 @@ void GameArea::DelBorder() GetSizer()->Detach(panel->GetWindow()); if (panel) - panel->Delete(); + panel->Destroy(); panel = NULL; } @@ -782,7 +782,7 @@ void GameArea::ShowFullScreen(bool full) // just in case screen mode is going to change, go ahead and preemptively // delete panel to be recreated immediately after resize if (panel) { - panel->Delete(); + panel->Destroy(); panel = NULL; } @@ -1035,9 +1035,14 @@ void GameArea::OnIdle(wxIdleEvent& event) // 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); + // set up event handlers + // use both CHAR_HOOK and KEY_DOWN in case CHAR_HOOK does not work for whatever reason + w->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(GameArea::OnKeyDown), NULL, this); + w->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GameArea::OnKeyDown), NULL, this); + w->Connect(wxEVT_KEY_UP, wxKeyEventHandler(GameArea::OnKeyUp), NULL, this); + w->Connect(wxEVT_PAINT, wxPaintEventHandler(GameArea::PaintEv), NULL, this); + w->Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(GameArea::EraseBackground), NULL, this); + w->Connect(wxEVT_SIZE, wxSizeEventHandler(GameArea::OnSize), NULL, this); } if (!paused && (!pauseWhenInactive || wxGetApp().frame->HasFocus())) { @@ -1223,6 +1228,28 @@ void GameArea::OnKeyUp(wxKeyEvent& ev) ev.Skip(!process_key_press(false, ev.GetKeyCode(), ev.GetModifiers())); } +// these three are forwarded to the DrawingPanel instance +void GameArea::PaintEv(wxPaintEvent& ev) +{ + DrawingPanelBase* panel = dynamic_cast(ev.GetEventObject()); + + panel->PaintEv(ev); +} + +void GameArea::EraseBackground(wxEraseEvent& ev) +{ + DrawingPanelBase* panel = dynamic_cast(ev.GetEventObject()); + + panel->EraseBackground(ev); +} + +void GameArea::OnSize(wxSizeEvent& ev) +{ + DrawingPanelBase* panel = dynamic_cast(ev.GetEventObject()); + + panel->OnSize(ev); +} + void GameArea::OnSDLJoy(wxSDLJoyEvent& ev) { int key = ev.GetControlIndex(); @@ -1256,11 +1283,8 @@ EVT_KEY_UP(GameArea::OnKeyUp) EVT_MOUSE_EVENTS(GameArea::MouseEvent) END_EVENT_TABLE() -IMPLEMENT_ABSTRACT_CLASS(DrawingPanel, wxEvtHandler) - -DrawingPanel::DrawingPanel(int _width, int _height) - : wxObject() - , width(_width) +DrawingPanelBase::DrawingPanelBase(int _width, int _height) + : width(_width) , height(_height) , scale(1) , todraw(0) @@ -1349,18 +1373,19 @@ DrawingPanel::DrawingPanel(int _width, int _height) utilUpdateSystemColorMaps(false); } -void DrawingPanel::DrawingPanelInit() +DrawingPanel::DrawingPanel(wxWindow* parent, int _width, int _height) + : DrawingPanelBase(_width, _height) + , wxPanel(parent, wxID_ANY, wxPoint(0, 0), parent->GetSize(), + wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) { - wxWindow* w = dynamic_cast(this); - - // this is not 2.8 compatible, sorry - w->Bind(wxEVT_PAINT, &DrawingPanel::PaintEv, this); - w->Bind(wxEVT_ERASE_BACKGROUND, &DrawingPanel::EraseBackground, this); +} +void DrawingPanelBase::DrawingPanelInit() +{ did_init = true; } -void DrawingPanel::PaintEv(wxPaintEvent& ev) +void DrawingPanelBase::PaintEv(wxPaintEvent& ev) { wxPaintDC dc(GetWindow()); @@ -1379,7 +1404,7 @@ void DrawingPanel::PaintEv(wxPaintEvent& ev) DrawOSD(dc); } -void DrawingPanel::EraseBackground(wxEraseEvent& ev) +void DrawingPanelBase::EraseBackground(wxEraseEvent& ev) { // do nothing, do not allow propagation } @@ -1611,7 +1636,7 @@ public: } }; -void DrawingPanel::DrawArea(uint8_t** data) +void DrawingPanelBase::DrawArea(uint8_t** data) { // double-buffer buffer: // if filtering, this is filter output, retained for redraws @@ -1766,7 +1791,7 @@ void DrawingPanel::DrawArea(uint8_t** data) DrawOSD(dc); } -void DrawingPanel::DrawOSD(wxWindowDC& dc) +void DrawingPanelBase::DrawOSD(wxWindowDC& dc) { // draw OSD message, if available // doing this here rather than using drawText() directly into the screen @@ -1854,7 +1879,12 @@ void DrawingPanel::DrawOSD(wxWindowDC& dc) } } -DrawingPanel::~DrawingPanel() +void DrawingPanelBase::OnSize(wxSizeEvent& ev) +{ + ev.Skip(true); +} + +DrawingPanelBase::~DrawingPanelBase() { // pixbuf1 freed by emulator if (pixbuf2) @@ -1876,12 +1906,8 @@ DrawingPanel::~DrawingPanel() } } -IMPLEMENT_CLASS2(BasicDrawingPanel, DrawingPanel, wxPanel) - BasicDrawingPanel::BasicDrawingPanel(wxWindow* parent, int _width, int _height) - : wxPanel(parent, wxID_ANY, wxPoint(0, 0), parent->GetSize(), - wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) - , DrawingPanel(_width, _height) + : DrawingPanel(parent, _width, _height) { // wxImage is 24-bit RGB, so 24-bit is preferred. Filters require // 16 or 32, though @@ -1957,32 +1983,23 @@ void BasicDrawingPanel::DrawImage(wxWindowDC& dc, wxImage* im) #include #endif #ifdef __WXMSW__ +#include #include #endif -IMPLEMENT_CLASS2(GLDrawingPanel, DrawingPanel, wxGLCanvas) - // This is supposed to be the default, but DOUBLEBUFFER doesn't seem to be // turned on by default for wxGTK. static int glopts[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 }; -#if wxCHECK_VERSION(2, 9, 0) -#define glc wxGLCanvas -#else -// shuffled parms for 2.9 indicates non-auto glcontext -// before 2.9, wxMAC does not have this (but wxGTK & wxMSW do) -#define glc(a, b, c, d, e, f) wxGLCanvas(a, b, d, e, f, wxEmptyString, c) -#endif - GLDrawingPanel::GLDrawingPanel(wxWindow* parent, int _width, int _height) - : glc(parent, wxID_ANY, glopts, wxPoint(0, 0), parent->GetSize(), + : DrawingPanelBase(_width, _height) + , wxglc(parent, wxID_ANY, glopts, wxPoint(0, 0), parent->GetSize(), wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) - , DrawingPanel(_width, _height) { RequestHighResolutionOpenGLSurface(); -#if wxCHECK_VERSION(2, 9, 0) +#ifndef wxGL_IMPLICIT_CONTEXT ctx = new wxGLContext(this); SetCurrent(*ctx); #endif @@ -1991,39 +2008,36 @@ GLDrawingPanel::GLDrawingPanel(wxWindow* parent, int _width, int _height) GLDrawingPanel::~GLDrawingPanel() { -#if wxCHECK_VERSION(2, 9, 0) - delete ctx; -#endif -#if 0 - - // this should be automatically deleted w/ context - // it's also unsafe if panel no longer displayed - if (did_init) - { -#if wxCHECK_VERSION(2, 9, 0) - SetContext(*ctx); + // this should be automatically deleted w/ context + // it's also unsafe if panel no longer displayed + if (did_init) + { +#ifndef wxGL_IMPLICIT_CONTEXT + SetCurrent(*ctx); #else - SetContext(); + SetCurrent(); #endif - glDeleteLists(vlist, 1); - glDeleteTextures(1, &texid); - } + glDeleteLists(vlist, 1); + glDeleteTextures(1, &texid); + } +#ifndef wxGL_IMPLICIT_CONTEXT + delete ctx; #endif } void GLDrawingPanel::DrawingPanelInit() { -#if wxCHECK_VERSION(2, 9, 0) +#ifndef wxGL_IMPLICIT_CONTEXT SetCurrent(*ctx); +#else + SetCurrent(); #endif - DrawingPanel::DrawingPanelInit(); + DrawingPanelBase::DrawingPanelInit(); AdjustViewport(); - Connect(wxEVT_SIZE, wxSizeEventHandler(GLDrawingPanel::OnSize), NULL, this); - // taken from GTK front end almost verbatim glDisable(GL_CULL_FACE); glEnable(GL_TEXTURE_2D); @@ -2105,7 +2119,18 @@ void GLDrawingPanel::DrawingPanelInit() void GLDrawingPanel::OnSize(wxSizeEvent& ev) { +#ifndef wxGL_IMPLICIT_CONTEXT + SetCurrent(*ctx); +#else + SetCurrent(); +#endif + SetSize(ev.GetSize()); + AdjustViewport(); + + Center(); + + ev.Skip(true); } void GLDrawingPanel::AdjustViewport() @@ -2118,13 +2143,13 @@ void GLDrawingPanel::AdjustViewport() // you can use this to check that the gl surface is indeed high res GLint m_viewport[4]; glGetIntegerv(GL_VIEWPORT, m_viewport); - vbamDebug("GL VIEWPORT: %d, %d, %d, %d", m_viewport[0], m_viewport[1], m_viewport[2], m_viewport[3]); + wxLogDebug(wxT("GL VIEWPORT: %d, %d, %d, %d"), m_viewport[0], m_viewport[1], m_viewport[2], m_viewport[3]); #endif } void GLDrawingPanel::DrawArea(wxWindowDC& dc) { -#if wxCHECK_VERSION(2, 9, 0) +#ifndef wxGL_IMPLICIT_CONTEXT SetCurrent(*ctx); #else SetCurrent(); @@ -2151,16 +2176,13 @@ void GLDrawingPanel::DrawArea(wxWindowDC& dc) SwapBuffers(); } -#endif + +#endif // GL support #ifndef NO_CAIRO -IMPLEMENT_CLASS(CairoDrawingPanel, DrawingPanel) - CairoDrawingPanel::CairoDrawingPanel(wxWindow* parent, int _width, int _height) - : wxPanel(parent, wxID_ANY, wxPoint(0, 0), parent->GetSize(), - wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) - , DrawingPanel(_width, _height) + : DrawingPanel(parent, _width, _height) { conv_surf = NULL; @@ -2280,12 +2302,8 @@ void CairoDrawingPanel::DrawArea(wxWindowDC& dc) #include //#include -IMPLEMENT_CLASS(DXDrawingPanel, DrawingPanel) - DXDrawingPanel::DXDrawingPanel(wxWindow* parent, int _width, int _height) - : wxPanel(parent, wxID_ANY, wxPoint(0, 0), parent->GetSize(), - wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) - , DrawingPanel(_width, _height) + : DrawingPanel(parent, _width, _height) { // FIXME: implement if (!did_init) DrawingPanelInit(); diff --git a/src/wx/widgets/wx/keyedit.h b/src/wx/widgets/wx/keyedit.h index 0a4bc22b..47aebff3 100644 --- a/src/wx/widgets/wx/keyedit.h +++ b/src/wx/widgets/wx/keyedit.h @@ -110,8 +110,8 @@ protected: const struct { wxKeyCode code; - const char *name; - const char *display_name; + wxString name; + wxString display_name; } keys_with_display_names[] = { { WXK_BACK, wxTRANSLATE("Back"), wxTRANSLATE("Backspace") }, { WXK_PAGEUP, wxTRANSLATE("PageUp"), wxTRANSLATE("Page Up") }, @@ -146,4 +146,10 @@ const struct { { WXK_NUMPAD_DIVIDE, wxTRANSLATE("KP_Divide"), wxTRANSLATE("Num /") }, }; +// 2.x does not have the WXK_RAW_XXX values for Mac +#if wxMAJOR_VERSION < 3 + #define WXK_RAW_CONTROL WXK_COMMAND + #define wxMOD_RAW_CONTROL wxMOD_CMD +#endif + #endif /* WX_KEYTEXT_H */ diff --git a/src/wx/widgets/wx/wxmisc.h b/src/wx/widgets/wx/wxmisc.h index f35926bc..fedacfde 100644 --- a/src/wx/widgets/wx/wxmisc.h +++ b/src/wx/widgets/wx/wxmisc.h @@ -3,6 +3,7 @@ // utility widgets #include +#include // simple radio button not under the same parent window // note that it must be checkbox, as wx radio buttons have rigid behavior @@ -59,6 +60,18 @@ protected: int val, mask, *vptr; }; +class wxPositiveDoubleValidator : public wxGenericValidator { +public: + wxPositiveDoubleValidator(double* _val); + bool TransferToWindow(); + bool TransferFromWindow(); + bool Validate(wxWindow* parent); + wxObject* Clone() const; +protected: + double* double_val; + wxString str_val; +}; + // boolean copy-only validator with reversed value // may be attached to radio button or checkbox class wxBoolRevValidator : public wxValidator { diff --git a/src/wx/widgets/wxmisc.cpp b/src/wx/widgets/wxmisc.cpp index 051a6e7f..9a10e7e4 100644 --- a/src/wx/widgets/wxmisc.cpp +++ b/src/wx/widgets/wxmisc.cpp @@ -375,3 +375,56 @@ static const wxChar* /* const */ val_unsdigits_s[] = { const wxArrayString val_unsdigits(sizeof(val_unsdigits_s) / sizeof(val_unsdigits_s[0]), val_unsdigits_s); + + +wxPositiveDoubleValidator::wxPositiveDoubleValidator(double* _val) + : wxGenericValidator(&str_val) + , double_val(_val) +{ + if (double_val) { + str_val = wxString::Format(wxT("%.1f"), *double_val); + TransferToWindow(); + } +} + +bool wxPositiveDoubleValidator::TransferToWindow() +{ + if (double_val) { + str_val = wxString::Format(wxT("%.1f"), *double_val); + return wxGenericValidator::TransferToWindow(); + } +} + +bool wxPositiveDoubleValidator::TransferFromWindow() +{ + if (wxGenericValidator::TransferFromWindow()) { + if (double_val) { + return str_val.ToDouble(double_val); + } + } + return false; +} + +bool wxPositiveDoubleValidator::Validate(wxWindow* parent) +{ + if (wxGenericValidator::Validate(parent)) { + wxTextCtrl* ctrl = wxDynamicCast(GetWindow(), wxTextCtrl); + + if (ctrl) { + wxString cur_txt = ctrl->GetValue(); + double val; + if (cur_txt.ToDouble(&val)) { + return val >= 0; + } + return false; + } + + return true; + } + return false; +} + +wxObject* wxPositiveDoubleValidator::Clone() const +{ + return new wxPositiveDoubleValidator(double_val); +} diff --git a/src/wx/wxvbam.cpp b/src/wx/wxvbam.cpp index 6443b879..51ba3328 100644 --- a/src/wx/wxvbam.cpp +++ b/src/wx/wxvbam.cpp @@ -5,6 +5,8 @@ // create & display main frame #include "wxvbam.h" +#include +#include #include #include #include @@ -30,24 +32,6 @@ IMPLEMENT_APP(wxvbamApp) IMPLEMENT_DYNAMIC_CLASS(MainFrame, wxFrame) -// For spewing stuff to terminal when debugging -void vbamDebug(const char* format, ...) { -#ifdef DEBUG - wxLog *active_log = wxLog::GetActiveTarget(); - - wxLogStderr log_to_stderr; - - wxLog::SetActiveTarget(&log_to_stderr); - - va_list argptr; - va_start(argptr, format); - wxVLogDebug(format, argptr); - va_end(argptr); - - wxLog::SetActiveTarget(active_log); -#endif -} - // generate config file path static void get_config_path(wxPathList& path, bool exists = true) { @@ -74,13 +58,13 @@ static void get_config_path(wxPathList& path, bool exists = true) 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())); + wxLogDebug(wxT("GetUserLocalDataDir(): %s"), stdp.GetUserLocalDataDir().c_str()); + wxLogDebug(wxT("GetUserDataDir(): %s"), stdp.GetUserDataDir().c_str()); + wxLogDebug(wxT("GetLocalizedResourcesDir(wxGetApp().locale.GetCanonicalName()): %s"), stdp.GetLocalizedResourcesDir(wxGetApp().locale.GetCanonicalName()).c_str()); + wxLogDebug(wxT("GetResourcesDir(): %s"), stdp.GetResourcesDir().c_str()); + wxLogDebug(wxT("GetDataDir(): %s"), stdp.GetDataDir().c_str()); + wxLogDebug(wxT("GetLocalDataDir(): %s"), stdp.GetLocalDataDir().c_str()); + wxLogDebug(wxT("GetPluginsDir(): %s"), stdp.GetPluginsDir().c_str()); debug_dumped = true; } @@ -165,6 +149,17 @@ wxString wxvbamApp::GetAbsolutePath(wxString path) bool wxvbamApp::OnInit() { + // set up logging +#ifndef NDEBUG + wxLog::SetLogLevel(wxLOG_Trace); +#endif + // turn off output buffering on Windows to support mintty +#ifdef __WXMSW__ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + dup2(1, 2); // redirect stderr to stdout +#endif + // use consistent names for config SetAppName(_("vbam")); #if (wxMAJOR_VERSION >= 3) diff --git a/src/wx/wxvbam.h b/src/wx/wxvbam.h index 4fc86eb3..cb772537 100644 --- a/src/wx/wxvbam.h +++ b/src/wx/wxvbam.h @@ -4,7 +4,12 @@ #include #include #include +#include +#include +#include +#include #include +#include #include "wx/joyedit.h" #include "wx/keyedit.h" @@ -27,6 +32,23 @@ #include "../gba/Globals.h" #include "../gba/Sound.h" +// make wxLogDebug work on non-debug builds of Wx, and make it use the console +// on Windows +// (this works on 2.8 too!) +#if !defined(NDEBUG) && (!wxDEBUG_LEVEL || defined(__WXMSW__)) + #ifdef __WXMSW__ + #define VBAM_DEBUG_STREAM stdout + #else + #define VBAM_DEBUG_STREAM stderr + #endif + #undef wxLogDebug + #define wxLogDebug(...) \ + do { \ + fputs(wxString::Format(wxDateTime::UNow().Format(wxT("%X")) + wxT(": Debug: ") + __VA_ARGS__).utf8_str(), VBAM_DEBUG_STREAM); \ + fputc('\n', VBAM_DEBUG_STREAM); \ + } while(0) +#endif + template void CheckPointer(T pointer) { @@ -38,9 +60,6 @@ void CheckPointer(T pointer) } } -// For spewing stuff to terminal -void vbamDebug(const char* format, ...); - /// Helper functions to convert WX's crazy string types to std::string inline std::string ToString(wxCharBuffer aString) @@ -441,7 +460,7 @@ enum audioapi { AUD_SDL, // display time, in ms #define OSD_TIME 3000 -class DrawingPanel; +class DrawingPanelBase; class GameArea : public wxPanel, public HiDPIAware { public: @@ -501,7 +520,7 @@ public: void AddBorder(); void DelBorder(); // Delete() & set to NULL to force reinit - DrawingPanel* panel; + DrawingPanelBase* panel; struct EmulatedSystem* emusys; // pause game or signal a long operation, similar to pausing @@ -585,6 +604,9 @@ protected: void OnKeyDown(wxKeyEvent& ev); void OnKeyUp(wxKeyEvent& ev); void OnSDLJoy(wxSDLJoyEvent& ev); + void PaintEv(wxPaintEvent& ev); + void EraseBackground(wxEraseEvent& ev); + void OnSize(wxSizeEvent& ev); #ifndef NO_FFMPEG MediaRecorder snd_rec, vid_rec; @@ -628,18 +650,17 @@ extern bool cmditem_lt(const struct cmditem& cmd1, const struct cmditem& cmd2); class FilterThread; -class DrawingPanel : public wxObject, public HiDPIAware { +class DrawingPanelBase : public HiDPIAware { public: - DrawingPanel(int _width, int _height); - ~DrawingPanel(); + DrawingPanelBase(int _width, int _height); + ~DrawingPanelBase(); void DrawArea(uint8_t** pixels); - // using dynamic_cast<> to not force trivial reimplementation in concrete classes - virtual wxWindow* GetWindow() { return dynamic_cast(this); } - virtual void Delete() { (dynamic_cast(this))->Destroy(); } - virtual void PaintEv(wxPaintEvent& ev); virtual void EraseBackground(wxEraseEvent& ev); + virtual void OnSize(wxSizeEvent& ev); + wxWindow* GetWindow() { return dynamic_cast(this); } + virtual bool Destroy() { return GetWindow()->Destroy(); } protected: virtual void DrawArea(wxWindowDC&) = 0; virtual void DrawOSD(wxWindowDC&); @@ -656,8 +677,12 @@ protected: const RENDER_PLUGIN_INFO* rpi; // also flag indicating plugin loaded // largest buffer required is 32-bit * (max width + 1) * (max height + 2) uint8_t delta[257 * 4 * 226]; +}; - DECLARE_ABSTRACT_CLASS() +// base class with a wxPanel when a subclass (such as wxGLCanvas) is not being used +class DrawingPanel : public DrawingPanelBase, public wxPanel { +public: + DrawingPanel(wxWindow* parent, int _width, int _height); }; class LogDialog : public wxDialog {