Merge pull request #26 from rkitover/mac_hidpi

Mac OS X HiDPI fixes + other misc
This commit is contained in:
Zach Bacon 2016-10-22 11:04:03 -04:00 committed by GitHub
commit db85f54b1c
9 changed files with 273 additions and 95 deletions

View File

@ -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 "<CMAKE_CXX_COMPILER> -x objective-c++ <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>")

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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++) {

View File

@ -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;

View File

@ -1,5 +1,10 @@
#include <wx/dcbuffer.h>
#ifdef __WXMAC__
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#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()
@ -704,9 +722,12 @@ void GameArea::DelBorder()
void GameArea::AdjustMinSize()
{
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);
@ -719,7 +740,11 @@ void GameArea::AdjustMinSize()
void GameArea::LowerMinSize()
{
wxWindow* frame = wxGetApp().frame;
wxSize sz(basic_width, basic_height);
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<char*>(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));

View File

@ -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<const char*>(stdp.GetUserLocalDataDir().utf8_str()));
vbamDebug("GetUserDataDir(): %s", static_cast<const char*>(stdp.GetUserDataDir().utf8_str()));
vbamDebug("GetLocalizedResourcesDir(wxGetApp().locale.GetCanonicalName()): %s", static_cast<const char*>(stdp.GetLocalizedResourcesDir(wxGetApp().locale.GetCanonicalName()).utf8_str()));
vbamDebug("GetResourcesDir(): %s", static_cast<const char*>(stdp.GetResourcesDir().utf8_str()));
vbamDebug("GetDataDir(): %s", static_cast<const char*>(stdp.GetDataDir().utf8_str()));
vbamDebug("GetLocalDataDir(): %s", static_cast<const char*>(stdp.GetLocalDataDir().utf8_str()));
vbamDebug("GetPluginsDir(): %s", static_cast<const char*>(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)

View File

@ -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;