diff --git a/CMakeLists.txt b/CMakeLists.txt index ece4cb40..84208222 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,7 +59,11 @@ IF( NOT VERSION ) endif(EXISTS ${PROJECT_SOURCE_DIR}/.git) ENDIF( NOT VERSION ) - +IF(CMAKE_BUILD_TYPE STREQUAL "Debug") + ADD_DEFINITIONS(-DDEBUG) +ELSE() + ADD_DEFINITIONS(-NDEBUG) +ENDIF() # Fill in SDLMAIN_LIBRARY on OS X manually to avoid using SDLMain.m # OS X users will have to compile and install SDL from source. @@ -68,18 +72,38 @@ if( APPLE AND ENABLE_SDL ) SET(SDL2MAIN_LIBRARY "-lSDL2main") endif( APPLE AND ENABLE_SDL ) -# Add support for MacPorts and Homebrew on OS X -# and ObjectiveC code +# Add support for Homebrew, MacPorts and Fink on OS X +# as well as for ObjectiveC code IF(APPLE) - SET(CMAKE_INCLUDE_PATH "${CMAKE_INCLUDE_PATH};/usr/local/include;/opt/local/include") - SET(CMAKE_LIBRARY_PATH "${CMAKE_LIBRARY_PATH};/usr/local/lib;/opt/local/lib") - SET(CMAKE_PROGRAM_PATH "${CMAKE_PROGRAM_PATH};/usr/local/bin;/opt/local/bin") + IF(EXISTS /usr/local/include) + SET(CMAKE_INCLUDE_PATH "${CMAKE_INCLUDE_PATH};/usr/local/include") + INCLUDE_DIRECTORIES("/usr/local/include") + ELSEIF(EXISTS /opt/local/include) + SET(CMAKE_INCLUDE_PATH "${CMAKE_INCLUDE_PATH};/opt/local/include") + INCLUDE_DIRECTORIES("/opt/local/include") + ELSEIF(EXISTS /sw/include) + SET(CMAKE_INCLUDE_PATH "${CMAKE_INCLUDE_PATH};/sw/include") + INCLUDE_DIRECTORIES("/sw/include") + ENDIF() - link_directories("/usr/local/lib") - include_directories("/usr/local/include") + IF(EXISTS /usr/local/lib) + SET(CMAKE_LIBRARY_PATH "${CMAKE_LIBRARY_PATH};/usr/local/lib") + LINK_DIRECTORIES("/usr/local/lib") + ELSEIF(EXISTS /opt/local/lib) + SET(CMAKE_LIBRARY_PATH "${CMAKE_LIBRARY_PATH};/opt/local/lib") + LINK_DIRECTORIES("/opt/local/lib") + ELSEIF(EXISTS /sw/lib) + SET(CMAKE_LIBRARY_PATH "${CMAKE_LIBRARY_PATH};/sw/lib") + LINK_DIRECTORIES("/sw/lib") + ENDIF() - link_directories("/opt/local/lib") - include_directories("/opt/local/include") + IF(EXISTS /usr/local/bin) + SET(CMAKE_PROGRAM_PATH "${CMAKE_PROGRAM_PATH};/usr/local/bin") + ELSEIF(EXISTS /opt/local/bin) + SET(CMAKE_PROGRAM_PATH "${CMAKE_PROGRAM_PATH};/opt/local/bin") + ELSEIF(EXISTS /sw/bin) + SET(CMAKE_PROGRAM_PATH "${CMAKE_PROGRAM_PATH};/sw/bin") + ENDIF() # and compile as Objective-C++ for ObjectiveC #ifdefs SET(CMAKE_CXX_COMPILE_OBJECT " -x objective-c++ -o -c ") diff --git a/src/wx/CMakeLists.txt b/src/wx/CMakeLists.txt index 222096b7..d492fe7f 100644 --- a/src/wx/CMakeLists.txt +++ b/src/wx/CMakeLists.txt @@ -29,10 +29,15 @@ else(ENABLE_OPENAL) ADD_DEFINITIONS (-DNO_OAL) 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 -IF(CMAKE_BUILD_TYPE EQUAL "Debug") - SET(wxWidgets_USE_DEBUG ON) + # 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() + SET(wxWidgets_USE_UNICODE ON) # adv is for wxAboutBox # xml, html is for xrc diff --git a/src/wx/drawing.h b/src/wx/drawing.h index df17bfdc..4d095a68 100644 --- a/src/wx/drawing.h +++ b/src/wx/drawing.h @@ -47,13 +47,11 @@ protected: { PaintEv(ev); } - void OnSize(wxSizeEvent&); void DrawArea(wxWindowDC& dc); -#if wxCHECK_VERSION(2, 9, 0) || !defined(__WXMAC__) - wxGLContext ctx; +#if wxCHECK_VERSION(2, 9, 0) + wxGLContext* ctx; #endif - bool did_init; - void Init(); + void DrawingPanelInit(); GLuint texid, vlist; int texsize; @@ -81,8 +79,7 @@ protected: PaintEv(ev); } void DrawArea(wxWindowDC&); - bool did_init; - void Init(); + void DrawingPanelInit(); DECLARE_CLASS() DECLARE_EVENT_TABLE() diff --git a/src/wx/guiinit.cpp b/src/wx/guiinit.cpp index 59f4938e..2b024cc0 100644 --- a/src/wx/guiinit.cpp +++ b/src/wx/guiinit.cpp @@ -2522,6 +2522,8 @@ bool MainFrame::BindControls() return false; } + panel->SetMainFrame(this); + panel->AdjustSize(false); // only the panel does idle events (the emulator loop) // however, do not enable until end of init, since errors will start diff --git a/src/wx/opts.cpp b/src/wx/opts.cpp index 3a9e85d5..e47becf9 100644 --- a/src/wx/opts.cpp +++ b/src/wx/opts.cpp @@ -21,6 +21,10 @@ { \ wxT(c), (n), d, NULL, &v, NULL, min, max \ } +#define DOUBLEOPT(c, n, d, v, min, max) \ + { \ + wxT(c), (n), d, NULL, NULL, NULL, min, max, NULL, &v \ + } #define BOOLOPT(c, n, d, v) \ { \ wxT(c), (n), d, NULL, NULL, NULL, 0, 0, &v \ @@ -156,7 +160,7 @@ opt_desc opts[] = { #else ENUMOPT("Display/RenderMethod", "", wxTRANSLATE("Render method; if unsupported, simple method will be used"), gopts.render_method, wxTRANSLATE("simple|opengl|cairo")), #endif - INTOPT("Display/Scale", "", wxTRANSLATE("Default scale factor"), gopts.video_scale, 1, 6), + DOUBLEOPT("Display/Scale", "", wxTRANSLATE("Default scale factor"), gopts.video_scale, 1, 6), BOOLOPT("Display/Stretch", "RetainAspect", wxTRANSLATE("Retain aspect ratio when resizing"), gopts.retain_aspect), /// GB @@ -523,6 +527,13 @@ void load_opts() wxLogWarning(_("Invalid value %d for option %s; valid values are %d - %d"), opt.curint, opt.opt, opt.min, opt.max); } else *opt.intopt = opt.curint; + } else if (opt.doubleopt) { + cfg->Read(opt.opt, &opt.curdouble, *opt.doubleopt); + + if (opt.curdouble < opt.min || opt.curdouble > opt.max) { + wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), opt.curdouble, opt.opt, opt.min, opt.max); + } else + *opt.doubleopt = opt.curdouble; } else if (opt.boolopt) { cfg->Read(opt.opt, opt.boolopt, *opt.boolopt); opt.curbool = *opt.boolopt; @@ -648,6 +659,9 @@ void update_opts() } else if (opt.intopt) { if (*opt.intopt != opt.curint) cfg->Write(opt.opt, (opt.curint = *opt.intopt)); + } else if (opt.doubleopt) { + if (*opt.doubleopt != opt.curdouble) + cfg->Write(opt.opt, (opt.curdouble = *opt.doubleopt)); } else if (opt.boolopt) { if (*opt.boolopt != opt.curbool) cfg->Write(opt.opt, (opt.curbool = *opt.boolopt)); @@ -805,6 +819,14 @@ bool opt_set(const wxChar* name, const wxChar* val) wxLogWarning(_("Invalid value %d for option %s; valid values are %d - %d"), ival, name, opt->min, opt->max); else *opt->intopt = ival; + } else if (opt->doubleopt) { + const wxString s(val); + double dval; + + if (!s.ToDouble(&dval) || dval < opt->min || dval > opt->max) + wxLogWarning(_("Invalid value %f for option %s; valid values are %f - %f"), dval, name, opt->min, opt->max); + else + *opt->doubleopt = dval; } else { // GB/Palette[0-2] is virtual for (int i = 0; i < 3; i++) { diff --git a/src/wx/opts.h b/src/wx/opts.h index c00d05c8..01853b71 100644 --- a/src/wx/opts.h +++ b/src/wx/opts.h @@ -18,7 +18,7 @@ extern struct opts_t { wxVideoMode fs_mode; int max_threads; int render_method; - int video_scale; + double video_scale; bool retain_aspect; bool keep_on_top; @@ -90,11 +90,13 @@ extern struct opt_desc { wxString* stropt; int* intopt; const wxChar* enumvals; - int min, max; + double min, max; bool* boolopt; + double* doubleopt; // current configured value wxString curstr; int curint; + double curdouble; #define curbool curint } opts[]; extern const int num_opts; diff --git a/src/wx/panel.cpp b/src/wx/panel.cpp index 37ee3ba8..ebec1255 100644 --- a/src/wx/panel.cpp +++ b/src/wx/panel.cpp @@ -1,5 +1,10 @@ #include +#ifdef __WXMAC__ +#import +#import +#endif + #include "../../version.h" #include "../common/ConfigManager.h" #include "../common/Patch.h" @@ -13,6 +18,19 @@ int emulating; +double HiDPIAware::HiDPIScaleFactor() +{ + if (hidpi_scale_factor == 0) { +#ifdef __WXMAC__ + hidpi_scale_factor = [[(NSView*)GetWindow()->GetHandle() window] backingScaleFactor]; +#else + hidpi_scale_factor = 1.0; +#endif + } + + return hidpi_scale_factor; +} + IMPLEMENT_DYNAMIC_CLASS(GameArea, wxPanel) GameArea::GameArea() @@ -703,10 +721,13 @@ void GameArea::DelBorder() void GameArea::AdjustMinSize() { - wxWindow* frame = wxGetApp().frame; + wxWindow* frame = wxGetApp().frame; + double hidpi_scale_factor = HiDPIScaleFactor(); + // note: could safely set min size to 1x or less regardless of video_scale // but setting it to scaled size makes resizing to default easier - wxSize sz(basic_width * gopts.video_scale, basic_height * gopts.video_scale); + wxSize sz((std::ceil(basic_width * gopts.video_scale) / hidpi_scale_factor), + (std::ceil(basic_height * gopts.video_scale) / hidpi_scale_factor)); SetMinSize(sz); #if wxCHECK_VERSION(2, 8, 8) sz = frame->ClientToWindowSize(sz); @@ -718,8 +739,12 @@ void GameArea::AdjustMinSize() void GameArea::LowerMinSize() { - wxWindow* frame = wxGetApp().frame; - wxSize sz(basic_width, basic_height); + wxWindow* frame = wxGetApp().frame; + double hidpi_scale_factor = HiDPIScaleFactor(); + + wxSize sz(std::ceil(basic_width / hidpi_scale_factor), + std::ceil(basic_height / hidpi_scale_factor)); + SetMinSize(sz); // do not take decorations into account frame->SetMinSize(sz); @@ -732,7 +757,9 @@ void GameArea::AdjustSize(bool force) if (fullscreen) return; - const wxSize newsz(basic_width * gopts.video_scale, basic_height * gopts.video_scale); + double hidpi_scale_factor = HiDPIScaleFactor(); + const wxSize newsz((std::ceil(basic_width * gopts.video_scale) / hidpi_scale_factor), + (std::ceil(basic_height * gopts.video_scale) / hidpi_scale_factor)); if (!force) { wxSize sz = GetSize(); @@ -756,6 +783,15 @@ void GameArea::ShowFullScreen(bool full) return; } + // Some kbd accels can send a menu open event without a close event, + // this happens on Mac in HiDPI mode for the fullscreen toggle accel. + main_frame->SetMenusOpened(false); + +// on Mac maximize is native fullscreen, so ignore fullscreen requests +#ifdef __WXMAC__ + if (full && main_frame->IsMaximized()) return; +#endif + fullscreen = full; // just in case screen mode is going to change, go ahead and preemptively @@ -1300,12 +1336,12 @@ DrawingPanel::DrawingPanel(int _width, int _height) // unused (as is rpi->Handle) _rpi->Output = (RENDPLUG_Output)filt_plugin.GetSymbol(wxT("RenderPluginOutput")); - scale = (_rpi->Flags & RPI_OUT_SCLMSK) >> RPI_OUT_SCLSH; + scale *= (_rpi->Flags & RPI_OUT_SCLMSK) >> RPI_OUT_SCLSH; rpi = _rpi; gopts.filter = FF_PLUGIN; // now that there is a valid plugin } while (0); } else { - scale = builtin_ff_scale(gopts.filter); + scale *= builtin_ff_scale(gopts.filter); #define out_16 (systemColorDepth == 16) systemColorDepth = 32; } @@ -1336,6 +1372,11 @@ DrawingPanel::DrawingPanel(int _width, int _height) utilUpdateSystemColorMaps(false); } +void DrawingPanel::DrawingPanelInit() +{ + did_init = true; +} + void DrawingPanel::PaintEv(wxPaintEvent& ev) { wxPaintDC dc(GetWindow()); @@ -1388,7 +1429,8 @@ public: // Set these params before running int nthreads, threadno; - int width, height, scale; + int width, height; + double scale; const RENDER_PLUGIN_INFO* rpi; uint8_t *dst, *delta; @@ -1407,10 +1449,10 @@ public: int instride = (width + inrb) * inbpp; int outbpp = out_16 ? 2 : systemColorDepth == 24 ? 3 : 4; int outrb = systemColorDepth == 24 ? 0 : 4; - int outstride = width * outbpp * scale + outrb; + int outstride = std::ceil(width * outbpp * scale) + outrb; delta += instride * procy; // + 1 for stupid top border - dst += outstride * (procy + 1) * scale; + dst += (int)std::ceil(outstride * (procy + 1) * scale); while (nthreads == 1 || sig.Wait() == wxCOND_NO_ERROR) { if (!src /* && nthreads > 1 */) { @@ -1561,10 +1603,10 @@ public: outdesc.SrcH = height; // + scale / 2 outdesc.DstPtr = dst; outdesc.DstPitch = outstride; - outdesc.DstW = width * scale; + outdesc.DstW = std::ceil(width * scale); // on the other hand, there is at least 1 line below, so I'll add // that to dest in case safety checks in plugin use < instead of <= - outdesc.DstH = height * scale + 1; // + scale * (scale / 2) + outdesc.DstH = std::ceil(height * scale) + 1; // + scale * (scale / 2) rpi->Output(&outdesc); break; @@ -1588,18 +1630,18 @@ void DrawingPanel::DrawArea(uint8_t** data) // if not filtering, we still retain current image for redraws int outbpp = out_16 ? 2 : systemColorDepth == 24 ? 3 : 4; int outrb = systemColorDepth == 24 ? 0 : 4; - int outstride = width * outbpp * scale + outrb; + int outstride = std::ceil(width * outbpp * scale) + outrb; if (!pixbuf2) { int allocstride = outstride, alloch = height; // gb may write borders, so allocate enough for them if (width == GameArea::GBWidth && height == GameArea::GBHeight) { - allocstride = GameArea::SGBWidth * outbpp * scale + outrb; + allocstride = std::ceil(GameArea::SGBWidth * outbpp * scale) + outrb; alloch = GameArea::SGBHeight; } - pixbuf2 = (uint8_t*)calloc(allocstride, (alloch + 2) * scale); + pixbuf2 = (uint8_t*)calloc(allocstride, std::ceil((alloch + 2) * scale)); } if (gopts.filter == FF_NONE) { @@ -1701,7 +1743,7 @@ void DrawingPanel::DrawArea(uint8_t** data) if (!disableStatusMessages && !panel->osdtext.empty()) { if (systemGetClock() - panel->osdtime < OSD_TIME) { std::string message = ToString(panel->osdtext); - int linelen = (width * scale - 20) / 8; + int linelen = std::ceil(width * scale - 20) / 8; int nlines = (message.length() + linelen - 1) / linelen; int cury = height - 14 - nlines * 10; char* ptr = const_cast(message.c_str()); @@ -1862,6 +1904,7 @@ BasicDrawingPanel::BasicDrawingPanel(wxWindow* parent, int _width, int _height) if (gopts.filter == FF_NONE && gopts.ifb == IFB_NONE) // changing from 32 to 24 does not require regenerating color tables systemColorDepth = 24; + if (!did_init) DrawingPanelInit(); } void BasicDrawingPanel::DrawArea(wxWindowDC& dc) @@ -1874,12 +1917,12 @@ void BasicDrawingPanel::DrawArea(wxWindowDC& dc) bm = new wxBitmap(im); } else if (out_16) { // scaled by filters, top/right borders, transform to 24-bit - wxImage im(width * scale, height * scale, false); - uint16_t* src = (uint16_t*)todraw + (width + 2) * scale; // skip top border + wxImage im(std::ceil(width * scale), std::ceil(height * scale), false); + uint16_t* src = (uint16_t*)todraw + (int)std::ceil((width + 2) * scale); // skip top border uint8_t* dst = im.GetData(); - for (int y = 0; y < height * scale; y++) { - for (int x = 0; x < width * scale; x++, src++) { + for (int y = 0; y < std::ceil(height * scale); y++) { + for (int x = 0; x < std::ceil(width * scale); x++, src++) { *dst++ = ((*src >> systemRedShift) & 0x1f) << 3; *dst++ = ((*src >> systemGreenShift) & 0x1f) << 3; *dst++ = ((*src >> systemBlueShift) & 0x1f) << 3; @@ -1892,12 +1935,12 @@ void BasicDrawingPanel::DrawArea(wxWindowDC& dc) } else // 32-bit { // scaled by filters, top/right borders, transform to 24-bit - wxImage im(width * scale, height * scale, false); - uint32_t* src = (uint32_t*)todraw + (width + 1) * scale; // skip top border + wxImage im(std::ceil(width * scale), std::ceil(height * scale), false); + uint32_t* src = (uint32_t*)todraw + (int)std::ceil((width + 1) * scale); // skip top border uint8_t* dst = im.GetData(); - for (int y = 0; y < height * scale; y++) { - for (int x = 0; x < width * scale; x++, src++) { + for (int y = 0; y < std::ceil(height * scale); y++) { + for (int x = 0; x < std::ceil(width * scale); x++, src++) { *dst++ = *src >> (systemRedShift - 3); *dst++ = *src >> (systemGreenShift - 3); *dst++ = *src >> (systemBlueShift - 3); @@ -1912,8 +1955,8 @@ void BasicDrawingPanel::DrawArea(wxWindowDC& dc) double sx, sy; int w, h; GetClientSize(&w, &h); - sx = (double)w / (double)(width * scale); - sy = (double)h / (double)(height * scale); + sx = w / (width * scale); + sy = h / (height * scale); dc.SetUserScale(sx, sy); dc.DrawBitmap(*bm, 0, 0); delete bm; @@ -1936,7 +1979,6 @@ IMPLEMENT_CLASS2(GLDrawingPanel, DrawingPanel, wxGLCanvas) // this would be easier in 2.9 BEGIN_EVENT_TABLE(GLDrawingPanel, wxGLCanvas) EVT_PAINT(GLDrawingPanel::PaintEv2) -EVT_SIZE(GLDrawingPanel::OnSize) END_EVENT_TABLE() // This is supposed to be the default, but DOUBLEBUFFER doesn't seem to be @@ -1945,7 +1987,7 @@ static int glopts[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 }; -#if wxCHECK_VERSION(2, 9, 0) || !defined(__WXMAC__) +#if wxCHECK_VERSION(2, 9, 0) #define glc wxGLCanvas #else // shuffled parms for 2.9 indicates non-auto glcontext @@ -1957,23 +1999,30 @@ GLDrawingPanel::GLDrawingPanel(wxWindow* parent, int _width, int _height) : glc(parent, wxID_ANY, glopts, wxPoint(0, 0), parent->GetSize(), wxFULL_REPAINT_ON_RESIZE) , DrawingPanel(_width, _height) - , did_init(false) -#if wxCHECK_VERSION(2, 9, 0) || !defined(__WXMAC__) - , ctx(this) -#endif { +#ifdef __WXMAC__ + [(NSView *)GetHandle() setWantsBestResolutionOpenGLSurface:YES]; +#endif +#if wxCHECK_VERSION(2, 9, 0) + ctx = new wxGLContext(this); + SetCurrent(*ctx); +#endif + if (!did_init) DrawingPanelInit(); } 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) || !defined(__WXMAC__) - SetContext(ctx); +#if wxCHECK_VERSION(2, 9, 0) + SetContext(*ctx); #else SetContext(); #endif @@ -1984,8 +2033,21 @@ GLDrawingPanel::~GLDrawingPanel() #endif } -void GLDrawingPanel::Init() +void GLDrawingPanel::DrawingPanelInit() { +#if wxCHECK_VERSION(2, 9, 0) + SetCurrent(*ctx); +#endif + + DrawingPanel::DrawingPanelInit(); + +#ifdef DEBUG + // 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]); +#endif + // taken from GTK front end almost verbatim glDisable(GL_CULL_FACE); glEnable(GL_TEXTURE_2D); @@ -2013,12 +2075,13 @@ void GLDrawingPanel::Init() gopts.bilinear ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gopts.bilinear ? GL_LINEAR : GL_NEAREST); + #define int_fmt out_16 ? GL_RGB5 : GL_RGB #define tex_fmt out_16 ? GL_BGRA : GL_RGBA, \ out_16 ? GL_UNSIGNED_SHORT_1_5_5_5_REV : GL_UNSIGNED_BYTE #if 0 texsize = width > height ? width : height; - texsize *= scale; + texsize = std::ceil(texsize * scale); // texsize = 1 << ffs(texsize); texsize = texsize | (texsize >> 1); texsize = texsize | (texsize >> 2); @@ -2029,7 +2092,7 @@ void GLDrawingPanel::Init() #else // but really, most cards support non-p2 and rect // if not, use cairo or wx renderer - glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, width * scale, height * scale, 0, tex_fmt, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, std::ceil(width * scale), std::ceil(height * scale), 0, tex_fmt, NULL); #endif glClearColor(0.0, 0.0, 0.0, 1.0); // non-portable vsync code @@ -2062,22 +2125,21 @@ void GLDrawingPanel::Init() #endif #endif #endif - did_init = true; } void GLDrawingPanel::DrawArea(wxWindowDC& dc) { -#if wxCHECK_VERSION(2, 9, 0) || !defined(__WXMAC__) - SetCurrent(ctx); +#if wxCHECK_VERSION(2, 9, 0) + SetCurrent(*ctx); #else SetCurrent(); #endif if (!did_init) - Init(); + DrawingPanelInit(); if (todraw) { - int rowlen = width * scale + (out_16 ? 2 : 1); + int rowlen = std::ceil(width * scale) + (out_16 ? 2 : 1); glPixelStorei(GL_UNPACK_ROW_LENGTH, rowlen); #if wxBYTE_ORDER == wxBIG_ENDIAN @@ -2086,30 +2148,14 @@ void GLDrawingPanel::DrawArea(wxWindowDC& dc) glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); #endif - glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, width * scale, height * scale, - 0, tex_fmt, todraw + rowlen * (out_16 ? 2 : 4) * scale); + glTexImage2D(GL_TEXTURE_2D, 0, int_fmt, std::ceil(width * scale), (int)std::ceil(height * scale), + 0, tex_fmt, todraw + (int)std::ceil(rowlen * (out_16 ? 2 : 4) * scale)); glCallList(vlist); } else glClear(GL_COLOR_BUFFER_BIT); SwapBuffers(); } - -void GLDrawingPanel::OnSize(wxSizeEvent& ev) -{ - if (!did_init) - return; - - int w, h; - GetClientSize(&w, &h); -#if wxCHECK_VERSION(2, 9, 0) || !defined(__WXMAC__) - SetCurrent(ctx); -#else - SetCurrent(); -#endif - glViewport(0, 0, w, h); - ev.Skip(); // propagate to parent -} #endif #ifndef NO_CAIRO @@ -2141,6 +2187,8 @@ CairoDrawingPanel::CairoDrawingPanel(wxWindow* parent, int _width, int _height) // FIXME: should be "true" for GBA carts if lcd mode selected utilUpdateSystemColorMaps(false); + + if (!did_init) DrawingPanelInit(); } CairoDrawingPanel::~CairoDrawingPanel() @@ -2194,8 +2242,8 @@ void CairoDrawingPanel::DrawArea(wxWindowDC& dc) else { if (!conv_surf) conv_surf = cairo_image_surface_create(CAIRO_FORMAT_RGB24, - width * scale, - height * scale); + std::ceil(width * scale), + std::ceil(height * scale)); if (!conv_surf) { wxLogError(_("Cannot create conversion buffer")); @@ -2203,11 +2251,11 @@ void CairoDrawingPanel::DrawArea(wxWindowDC& dc) } surf = cairo_surface_reference(conv_surf); - uint16_t* src = (uint16_t*)todraw + (width + 2) * scale; // skip top border + uint16_t* src = (uint16_t*)todraw + (int)std::ceil((width + 2) * scale); // skip top border uint32_t* dst = (uint32_t*)cairo_image_surface_get_data(surf); - for (int y = 0; y < height * scale; y++) { - for (int x = 0; x < width * scale; x++, src++) { + for (int y = 0; y < std::ceil(height * scale); y++) { + for (int x = 0; x < std::ceil(width * scale); x++, src++) { *dst++ = (((*src >> systemRedShift) & 0x1f) << 19) | (((*src >> systemGreenShift) & 0x1f) << 11) | (((*src >> systemBlueShift) & 0x1f) << 3); } @@ -2222,8 +2270,8 @@ void CairoDrawingPanel::DrawArea(wxWindowDC& dc) double sx, sy; int w, h; GetClientSize(&w, &h); - sx = (double)width / (double)w; - sy = (double)height / (double)h; + sx = (double)width / w; + sy = (double)height / h; cairo_matrix_t mat; cairo_matrix_init_scale(&mat, sx, sy); cairo_pattern_set_matrix(pat, &mat); @@ -2253,6 +2301,7 @@ DXDrawingPanel::DXDrawingPanel(wxWindow* parent, int _width, int _height) , DrawingPanel(_width, _height) { // FIXME: implement + if (!did_init) DrawingPanelInit(); } void DXDrawingPanel::DrawArea(wxWindowDC& dc) @@ -2401,7 +2450,8 @@ void GameArea::HidePointer() return; // FIXME: make time configurable - if (fullscreen || (systemGetClock() - mouse_active_time) > 3000) { + if ((fullscreen || (systemGetClock() - mouse_active_time) > 3000) && + !(main_frame && (main_frame->MenusOpened() || main_frame->DialogOpened()))) { pointer_blanked = true; SetCursor(wxCursor(wxCURSOR_BLANK)); diff --git a/src/wx/wxvbam.cpp b/src/wx/wxvbam.cpp index df33e42c..196ae4a5 100644 --- a/src/wx/wxvbam.cpp +++ b/src/wx/wxvbam.cpp @@ -30,9 +30,31 @@ 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) { + // we want paths with "visualboyadvance-m" not "vbam", so change appname temporarily + wxString current_app_name = wxGetApp().GetAppName(); + wxGetApp().SetAppName(_("visualboyadvance-m")); + // local config dir first, then global // locale-specific res first, then main wxStandardPathsBase& stdp = wxStandardPaths::Get(); @@ -44,6 +66,15 @@ static void get_config_path(wxPathList& path, bool exists = true) if ((wxDirExists(s) && wxIsWritable(s)) || ((!exists || !wxDirExists(s)) && parent.IsDirWritable())) \ 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())); + // NOTE: this does not support XDG (freedesktop.org) paths add_path(GetUserLocalDataDir()); add_path(GetUserDataDir()); @@ -52,6 +83,8 @@ static void get_config_path(wxPathList& path, bool exists = true) add_path(GetDataDir()); add_path(GetLocalDataDir()); add_path(GetPluginsDir()); + + wxGetApp().SetAppName(current_app_name); } static void tack_full_path(wxString& s, const wxString& app = wxEmptyString) @@ -459,7 +492,7 @@ bool wxvbamApp::OnCmdLineParsed(wxCmdLineParser& cl) for (int i = 0; i < num_opts; i++) { wxPrintf(wxT("%s (%s"), opts[i].opt, - opts[i].boolopt ? (const wxChar*)wxT("flag") : opts[i].stropt ? (const wxChar*)wxT("string") : opts[i].enumvals ? opts[i].enumvals : opts[i].intopt ? (const wxChar*)wxT("int") : (const wxChar*)wxT("string")); + opts[i].boolopt ? (const wxChar*)wxT("flag") : opts[i].stropt ? (const wxChar*)wxT("string") : opts[i].enumvals ? opts[i].enumvals : opts[i].intopt ? (const wxChar*)wxT("int") : opts[i].doubleopt ? (const wxChar*)wxT("decimal") : (const wxChar*)wxT("string")); if (opts[i].enumvals) { const wxChar* evx = wxGetTranslation(opts[i].enumvals); @@ -557,6 +590,7 @@ EVT_DROP_FILES(MainFrame::OnDropFile) // pause game if menu pops up EVT_MENU_OPEN(MainFrame::MenuPopped) EVT_MENU_CLOSE(MainFrame::MenuPopped) +EVT_MENU_HIGHLIGHT_ALL(MainFrame::MenuPopped) END_EVENT_TABLE() void MainFrame::OnActivate(wxActivateEvent& event) @@ -950,7 +984,7 @@ int MainFrame::newest_state_slot() // Removing the nesting counter may help, but on wxGTK I still get lockups. void MainFrame::MenuPopped(wxMenuEvent& evt) { - bool popped = evt.GetEventType() == wxEVT_MENU_OPEN; + bool popped = evt.GetEventType() != wxEVT_MENU_CLOSE; #if 0 if (popped) @@ -985,6 +1019,21 @@ void MainFrame::MenuPopped(wxMenuEvent& evt) panel->Resume(); } +void MainFrame::SetMenusOpened(bool state) +{ + if (state) { + menus_opened = 1; + paused = true; + panel->Pause(); + } + else { + menus_opened = 0; + paused = false; + pause_next = false; + panel->Resume(); + } +} + // ShowModal that also disables emulator loop // uses dialog_opened as a nesting counter int MainFrame::ShowModal(wxDialog* dlg) diff --git a/src/wx/wxvbam.h b/src/wx/wxvbam.h index 520a85b9..78227fff 100644 --- a/src/wx/wxvbam.h +++ b/src/wx/wxvbam.h @@ -38,6 +38,9 @@ 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) @@ -278,6 +281,12 @@ public: // Check for online updates to the emulator bool CheckForUpdates(); + virtual bool MenusOpened() { return menus_opened != 0; } + + virtual void SetMenusOpened(bool state); + + virtual bool DialogOpened() { return dialog_opened != 0; } + // required for building from xrc DECLARE_DYNAMIC_CLASS(); // required for event handling @@ -333,6 +342,15 @@ private: #include "cmdhandlers.h" }; +// helper class to add HiDPI awareness (mostly for Mac OS X) +class HiDPIAware { +public: + double HiDPIScaleFactor(); + virtual wxWindow* GetWindow() = 0; +private: + double hidpi_scale_factor = 0; +}; + // a helper class to avoid forgetting StopModal() class ModalPause { public: @@ -420,11 +438,13 @@ enum audioapi { AUD_SDL, class DrawingPanel; -class GameArea : public wxPanel { +class GameArea : public wxPanel, public HiDPIAware { public: GameArea(); virtual ~GameArea(); + virtual void SetMainFrame(MainFrame* parent) { main_frame = parent; } + // set to game title + link info void SetFrameTitle(); @@ -540,7 +560,11 @@ public: void StartGamePlayback(const wxString& fname); void StopGamePlayback(); + virtual wxWindow* GetWindow() { return this; } + protected: + MainFrame* main_frame; + // set minsize of frame & panel to unscaled screen size void LowerMinSize(); // set minsize of frame & panel to scaled screen size @@ -599,7 +623,7 @@ extern bool cmditem_lt(const struct cmditem& cmd1, const struct cmditem& cmd2); class FilterThread; -class DrawingPanel : public wxObject { +class DrawingPanel : public wxObject, public HiDPIAware { public: DrawingPanel(int _width, int _height); ~DrawingPanel(); @@ -611,7 +635,10 @@ public: protected: virtual void DrawArea(wxWindowDC&) = 0; virtual void DrawOSD(wxWindowDC&); - int width, height, scale; + int width, height; + double scale; + virtual void DrawingPanelInit(); + bool did_init = false; uint8_t* todraw; uint8_t *pixbuf1, *pixbuf2; FilterThread* threads;