Mac OS X HiDPI fixes + other misc
Use a high-res surface on HiDPI (e.g. retina) Macs for the OpenGL renderer, and scale window accordingly. Also fix fullscreen toggle not working in HiDPI mode. And some other stuff. Aside from the .app not being linked statically, the app is now fully functional on Mac and ready to ship. Full change details: * add -DDEBUG or -DNDEBUG based on CMAKE_BUILD_TYPE (-DNDEBUG for Release and -DDEBUG for Debug) * properly search for homebrew/macports/fink directories, and only add them if they exist * fix building with wx debug support, when available * use explicit OpenGL context on Mac too for new versions of wx * add main_frame pointer in GameArea back to MainFrame, and a SetMainFrame(MainFrame* parent) public method to set it in guinit * add full support for reading and writing double value config options (the GUI still needs to be updated for options that can take doubles, such as video_scale) * change video_scale option (Display/Scale) to a double, putting a double value such as 3.6 in the config file works correctly * add a HiDPIAware mixin class for GameArea and DrawingPanel, with the method HiDPIScaleFactor which returns the current window's backingScaleFactor * change the GameArea sizing methods (DelBorder(), AdjustMinSize() and AdjustSize()) to divide the window size by the HiDPIAware::HiDPIScaleFactor so that the window is scaled properly * change GameArea::ShowFullScreen to ignore fullscreen events for a maximized window on Mac, because maximized windows on OS X are actually native fullscreen windows * change scale variables to double from int, and use std::ceil() to round scaled pixel or memory size values * make a default base class DrawingPane::DrawingPanelInit() virtual method, call by all concrete class constructors based on the did_init flag * call setWantsBestResolutionOpenGLSurface:YES on the view backing the wxGLCanvas for the OpenGL renderer (GLDrawingPanel) to get a high res OpenGL surface in HiDPI mode * remove GLDrawingPanel::OnSize event, the OpenGL viewport resizes automatically without the need to call glViewport() * do not hide the mouse pointer if the main frame has menus or dialogs open * add variadic vbamDebug(const char* format, ...) function, active only #ifdef DEBUG, which sets the wx log target to STDERR and logs a message to it * use full name of app "visualboyadvance-m" instead of "vbam" when getting configuration paths, this way the config is saved to ~/Library/Application Support/visualboyadvance-m rather than ~/Library/Application Support/vbam * listen to the MENU_HIGHLIGHT_ALL event as well, as an extra way to check when the menus are open * add public MainFrame::SetMenusOpened(bool state) method to force the main frame to change the internal menus_opened state, this is necessary because in HiDPI mode on Mac the keyboard accelerator for toggle fullscreen sends a menu open event, but not a menu close event, so on switch to fullscreen the state is changed to menus closed and the emu is unpaused TODO: * GUI option to change toggle fullscreen behavior between native and non-native fullscreen on Mac * GUI support for double config values like Display/Scale * add HiDPI support to simple renderer * fix SDL sound, or disable the option * fix Cairo suport on Mac, or disable the option * use dynamic_cast<> to implement GetWindow() for DrawingPanel instead of pure virtual method, likewise for Delete() * link .app statically by default so it can be shipped * add Homebrew formula
This commit is contained in:
parent
258c7fec40
commit
7a7a72f2bd
|
@ -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>")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
186
src/wx/panel.cpp
186
src/wx/panel.cpp
|
@ -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()
|
||||
|
@ -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<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));
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue