From c17455c702da41746ef0181bfa8ad316813fce2e Mon Sep 17 00:00:00 2001 From: "Jake.Stine" <Jake.Stine@96395faa-99c1-11dd-bbfe-3dabce05a288> Date: Mon, 14 Dec 2009 12:18:55 +0000 Subject: [PATCH] User Interface code cleanups and bugfixes. some highlights: * Configuration panels are all modal-less now, so that you can open the config panel and leave it open while running games. * Handful of thread sync improvements. * Fixed on-the-fly interpreter/recompiler configuration. * Fixed plugin hotswapping (mostly works, but still a little funny at times) * All new assertion dialogs and popup message handlers. * RecentIsoList defaults to 12 instead of 6 Dev Notes: * I had to create a new set of assertion functions called pxAssume*. Originally I hoped to avoid that complexity, and just use a single one-assert-fits-all case, but turned out blanketly using __assume() for all assertion cases wasn't reliable. * wxGuiTools: Replaced the operator, with operator& -- the latter has proper order of precedence, the former required () to scope correctly. >_< git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2339 96395faa-99c1-11dd-bbfe-3dabce05a288 --- common/include/Pcsx2Defs.h | 11 +- common/include/Utilities/Assertions.h | 150 +++++++--- common/include/Utilities/Exceptions.h | 36 +-- common/include/Utilities/Threading.h | 14 +- common/include/Utilities/pxCheckBox.h | 7 + common/include/Utilities/pxRadioPanel.h | 3 +- common/include/Utilities/pxStaticText.h | 8 + common/include/Utilities/wxGuiTools.h | 119 ++++++-- common/include/x86emitter/tools.h | 2 +- common/src/Utilities/Exceptions.cpp | 71 +++-- common/src/Utilities/ThreadTools.cpp | 226 ++++++++++---- common/src/Utilities/pxCheckBox.cpp | 5 + common/src/Utilities/pxRadioPanel.cpp | 18 +- common/src/Utilities/pxStaticText.cpp | 5 + common/src/Utilities/wxGuiTools.cpp | 42 ++- common/src/Utilities/wxHelpers.cpp | 102 ++++++- common/src/Utilities/x86/MemcpyFast.cpp | 4 +- pcsx2/CDVD/CDVD.cpp | 2 +- pcsx2/Counters.cpp | 2 +- pcsx2/Interpreter.cpp | 11 +- pcsx2/PluginManager.cpp | 10 +- pcsx2/R5900.cpp | 5 - pcsx2/RecoverySystem.cpp | 173 +++++------ pcsx2/SaveState.h | 4 +- pcsx2/System.cpp | 14 +- pcsx2/System.h | 6 +- pcsx2/System/SysCoreThread.cpp | 105 +++++-- pcsx2/System/SysThreadBase.cpp | 19 +- pcsx2/System/SysThreads.h | 20 +- pcsx2/VU0micro.cpp | 6 +- pcsx2/VUmicro.h | 7 +- pcsx2/Vif0Dma.cpp | 8 +- pcsx2/Vif1Dma.cpp | 10 +- pcsx2/VifDma.cpp | 20 +- pcsx2/gui/App.h | 19 +- pcsx2/gui/AppAssert.cpp | 153 ++++++---- pcsx2/gui/AppConfig.cpp | 4 +- pcsx2/gui/AppConfig.h | 2 +- pcsx2/gui/AppCoreThread.cpp | 10 + pcsx2/gui/AppInit.cpp | 124 ++++++-- pcsx2/gui/AppMain.cpp | 149 +++++----- pcsx2/gui/ConsoleLogger.cpp | 15 +- pcsx2/gui/ConsoleLogger.h | 2 +- pcsx2/gui/Dialogs/AboutBoxDialog.cpp | 42 +-- pcsx2/gui/Dialogs/AssertionDialog.cpp | 67 +++++ pcsx2/gui/Dialogs/ConfigurationDialog.cpp | 25 +- pcsx2/gui/Dialogs/ConfigurationDialog.h | 11 +- pcsx2/gui/Dialogs/ConfirmationDialogs.cpp | 218 +++++++++----- pcsx2/gui/Dialogs/FirstTimeWizard.cpp | 49 ++-- pcsx2/gui/Dialogs/ImportSettingsDialog.cpp | 26 +- pcsx2/gui/Dialogs/LogOptionsDialog.cpp | 6 +- pcsx2/gui/Dialogs/LogOptionsDialog.h | 5 +- pcsx2/gui/Dialogs/ModalPopups.h | 103 +++---- pcsx2/gui/Dialogs/PickUserModeDialog.cpp | 8 +- pcsx2/gui/FrameForGS.cpp | 37 ++- pcsx2/gui/IniInterface.cpp | 6 +- pcsx2/gui/IsoDropTarget.cpp | 22 +- pcsx2/gui/MainFrame.h | 3 + pcsx2/gui/MainMenuClicks.cpp | 34 ++- pcsx2/gui/MessageBoxes.cpp | 325 ++++++++++++++------- pcsx2/gui/Panels/CpuPanel.cpp | 21 +- pcsx2/gui/Panels/PluginSelectorPanel.cpp | 42 ++- pcsx2/gui/RecentIsoList.cpp | 4 +- pcsx2/gui/i18n.cpp | 2 +- pcsx2/gui/pxLogTextCtrl.cpp | 27 +- pcsx2/windows/VCprojects/pcsx2_2008.vcproj | 4 + pcsx2/windows/WinCompressNTFS.cpp | 5 +- pcsx2/windows/WinConsolePipe.cpp | 49 +++- pcsx2/x86/ix86-32/iR5900-32.cpp | 157 +++++----- pcsx2/x86/microVU.cpp | 89 +++--- pcsx2/x86/microVU_Misc.h | 4 +- 71 files changed, 1996 insertions(+), 1118 deletions(-) create mode 100644 pcsx2/gui/Dialogs/AssertionDialog.cpp diff --git a/common/include/Pcsx2Defs.h b/common/include/Pcsx2Defs.h index d0ee44a3ff..0ea3cdcaba 100644 --- a/common/include/Pcsx2Defs.h +++ b/common/include/Pcsx2Defs.h @@ -126,6 +126,13 @@ # endif #endif +#ifdef PCSX2_DEBUG +# define pxDebugCode(code) code +#else +# define pxDebugCode(code) +#endif + + ////////////////////////////////////////////////////////////////////////////////////////// // __aligned / __aligned16 / __pagealigned // @@ -201,8 +208,8 @@ static const int __pagesize = PCSX2_PAGESIZE; // Don't know if there are Visual C++ equivalents of these. # define __hot # define __cold -# define likely(x) x -# define unlikely(x) x +# define likely(x) (!!(x)) +# define unlikely(x) (!!(x)) # define CALLBACK __stdcall diff --git a/common/include/Utilities/Assertions.h b/common/include/Utilities/Assertions.h index bafc2b9d93..7d9c916852 100644 --- a/common/include/Utilities/Assertions.h +++ b/common/include/Utilities/Assertions.h @@ -15,60 +15,136 @@ #pragma once +#ifndef __pxFUNCTION__ +#if defined(__GNUG__) +# define __pxFUNCTION__ __PRETTY_FUNCTION__ +#else +# define __pxFUNCTION__ __FUNCTION__ +#endif +#endif + +#ifndef wxNullChar +# define wxNullChar ((wxChar*)NULL) +#endif + +// FnChar_t - function name char type; typedef'd in case it ever changes between compilers +// (ie, a compiler decides to wchar_t it instead of char/UTF8). +typedef char FnChar_t; + +// -------------------------------------------------------------------------------------- +// DiagnosticOrigin +// -------------------------------------------------------------------------------------- +struct DiagnosticOrigin +{ + const wxChar* srcfile; + const FnChar_t* function; + const wxChar* condition; + int line; + + DiagnosticOrigin( const wxChar *_file, int _line, const FnChar_t *_func, const wxChar* _cond = NULL ) + : srcfile( _file ) + , function( _func ) + , condition( _cond ) + , line( _line ) + { + } + + wxString ToString( const wxChar* msg=NULL ) const; +}; + +// Returns ture if the assertion is to trap into the debugger, or false if execution +// of the program should continue unimpeded. +typedef bool pxDoAssertFnType(const DiagnosticOrigin& origin, const wxChar *msg); + +extern pxDoAssertFnType pxAssertImpl_LogIt; + +extern pxDoAssertFnType* pxDoAssert; + // ---------------------------------------------------------------------------------------- -// pxAssert / pxAssertDev / pxFail / pxFailDev +// pxAssert / pxAssertDev // ---------------------------------------------------------------------------------------- // Standard "nothrow" assertions. All assertions act as valid conditional statements that // return the result of the specified conditional; useful for handling failed assertions in // a "graceful" fashion when utilizing the "ignore" feature of assertion debugging. +// These macros are mostly intended for "pseudo-weak" assumptions within code, most often for +// testing threaded user interface code (threading of the UI is a prime example since often +// even very robust assertions can fail in very rare conditions, due to the complex variety +// of ways the user can invoke UI events). // -// Performance: All assertion types optimize into __assume()/likely() directives in Release -// builds. If using assertions as part of a conditional, the conditional code *will* not -// be optimized out, so use conditionals with caution. +// All macros return TRUE if the assertion succeeds, or FALSE if the assertion failed +// (thus matching the condition of the assertion itself). // // pxAssertDev is an assertion tool for Devel builds, intended for sanity checking and/or // bounds checking variables in areas which are not performance critical. Another common // use is for checking thread affinity on utility functions. // -// Credits Notes: These macros are based on a combination of wxASSERT, MSVCRT's assert -// and the ATL's Assertion/Assumption macros. the best of all worlds! +// Credits: These macros are based on a combination of wxASSERT, MSVCRT's assert and the +// ATL's Assertion/Assumption macros. the best of all worlds! + +// -------------------------------------------------------------------------------------- +// pxAssume / pxAssumeDev / pxFail / pxFailDev +// -------------------------------------------------------------------------------------- +// Assumptions are like "extra rigid" assertions, which should never fail under any circum- +// stance in release build optimized code. +// +// Performance: All assumption/fail types optimize into __assume()/likely() directives in +// Release builds (non-dev varieties optimize as such in Devel builds as well). If using + +#define pxDiagSpot DiagnosticOrigin( __TFILE__, __LINE__, __pxFUNCTION__ ) +#define pxAssertSpot(cond) DiagnosticOrigin( __TFILE__, __LINE__, __pxFUNCTION__, _T(#cond) ) + +// pxAssertRel -> +// Special release-mode assertion. Limited use since stack traces in release mode builds +// (especially with LTCG) are highly suspect. But when troubleshooting crashes that only +// rear ugly heads in optimized builds, this is one of the few tools we have. + +#define pxAssertRel(cond, msg) ( (likely(cond)) || (pxOnAssert(pxAssertSpot(cond), msg), false) ) +#define pxAssumeMsg(cond, msg) ((void) ( (!likely(cond)) && (pxOnAssert(pxAssertSpot(cond), msg), false) )) #if defined(PCSX2_DEBUG) -# define pxAssertMsg(cond, msg) ( (!!(cond)) || \ - (pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), likely(cond)) ) +# define pxAssertMsg(cond, msg) pxAssertRel(cond, msg) +# define pxAssertDev(cond, msg) pxAssertMsg(cond, msg) -# define pxAssertDev(cond,msg) pxAssertMsg(cond, msg) +# define pxAssume(cond) pxAssumeMsg(cond, wxNullChar) +# define pxAssumeDev(cond, msg) pxAssumeMsg(cond, msg) -# define pxFail(msg) pxAssertMsg(false, msg) -# define pxFailDev(msg) pxAssertDev(false, msg) +# define pxFail(msg) pxAssumeMsg(false, msg) +# define pxFailDev(msg) pxAssumeDev(false, msg) #elif defined(PCSX2_DEVBUILD) // Devel builds use __assume for standard assertions and call pxOnAssertDevel // for AssertDev brand assertions (which typically throws a LogicError exception). -# define pxAssertMsg(cond, msg) (__assume(cond), likely(cond)) +# define pxAssertMsg(cond, msg) (likely(cond)) +# define pxAssertDev(cond, msg) pxAssertRel(cond, msg) -# define pxAssertDev(cond, msg) ( (!!(cond)) || \ - (pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), likely(cond)) ) +# define pxAssume(cond) (__assume(cond)) +# define pxAssumeDev(cond, msg) pxAssumeMsg(cond, msg) -# define pxFail(msg) (__assume(false), false) -# define pxFailDev(msg ) pxAssertDev(false, msg) +# define pxFail(msg) (__assume(false)) +# define pxFailDev(msg) pxAssumeDev(false, msg) #else // Release Builds just use __assume as an optimization, and return the conditional // as a result (which is optimized to nil if unused). -# define pxAssertMsg(cond, msg) (__assume(cond), likely(cond)) -# define pxAssertDev(cond, msg) (__assume(cond), likely(cond)) -# define pxFail(msg) (__assume(false), false) -# define pxFailDev(msg) (__assume(false), false) +# define pxAssertMsg(cond, msg) (likely(cond)) +# define pxAssertDev(cond, msg) (likely(cond)) + +# define pxAssume(cond) (__assume(cond)) +# define pxAssumeDev(cond, msg) (__assume(cond)) + +# define pxFail(msg) (__assume(false)) +# define pxFailDev(msg) (__assume(false)) #endif -#define pxAssert(cond) pxAssertMsg(cond, (wxChar*)NULL) +#define pxAssert(cond) pxAssertMsg(cond, wxNullChar) + +#define pxAssertRelease( cond, msg ) // Performs an unsigned index bounds check, and generates a debug assertion if the check fails. // For stricter checking in Devel builds as well as debug builds (but possibly slower), use @@ -81,8 +157,8 @@ wxsFormat( L"Array index out of bounds accessing object '%s' (index=%d, size=%d)", objname, (idx), (sze) ) ) -extern void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const wxChar* msg); -extern void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const char* msg); +extern void pxOnAssert( const DiagnosticOrigin& origin, const wxChar* msg=NULL ); +extern void pxOnAssert( const DiagnosticOrigin& origin, const char* msg ); ////////////////////////////////////////////////////////////////////////////////////////// // jNO_DEFAULT -- disables the default case in a switch, which improves switch optimization @@ -92,29 +168,23 @@ extern void pxOnAssert( const wxChar* file, int line, const char* func, const wx // in the 'default:' case of a switch tells the compiler that the case is unreachable, so // that it will not generate any code, LUTs, or conditionals to handle it. // -// * In debug builds the default case will cause an assertion. -// * In devel builds the default case will cause a LogicError exception (C++ only) -// (either meaning the jNO_DEFAULT has been used incorrectly, and that the default case is in -// fact used and needs to be handled). -// -// MSVC Note: To stacktrace LogicError exceptions, add Exception::LogicError to the C++ First- -// Chance Exception list (under Debug->Exceptions menu). +// * In debug/devel builds the default case will cause an assertion. // #ifndef jNO_DEFAULT #if defined(__cplusplus) && defined(PCSX2_DEVBUILD) # define jNO_DEFAULT \ - default: \ - { \ - pxFailDev( "Incorrect usage of jNO_DEFAULT detected (default case is not unreachable!)" ); \ - break; \ - } + default: \ + { \ + pxFailDev( "Incorrect usage of jNO_DEFAULT detected (default case is not unreachable!)" ); \ + break; \ + } #else # define jNO_DEFAULT \ - default: \ - { \ - jASSUME(0); \ - break; \ - } + default: \ + { \ + jASSUME(0); \ + break; \ + } #endif #endif diff --git a/common/include/Utilities/Exceptions.h b/common/include/Utilities/Exceptions.h index bee8eac33c..df2f2fec9a 100644 --- a/common/include/Utilities/Exceptions.h +++ b/common/include/Utilities/Exceptions.h @@ -38,11 +38,7 @@ Console.Error( ex.what() ); \ } -#ifdef __GNUG__ -# define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __PRETTY_FUNCTION__ ) -#else -# define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __FUNCTION__ ) -#endif +#define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __pxFUNCTION__ ) namespace Exception { @@ -56,7 +52,7 @@ namespace Exception // catch clause can optionally modify them and then re-throw to a top-level handler. // // Note, this class is "abstract" which means you shouldn't use it directly like, ever. - // Use Exception::RuntimeError or Exception::LogicError instead for generic exceptions. + // Use Exception::RuntimeError instead for generic exceptions. // // Because exceptions are the (only!) really useful example of multiple inheritance, // this class has only a trivial constructor, and must be manually initialized using @@ -150,7 +146,7 @@ namespace Exception explicit classname( const wxString& msg_eng ) { BaseException::InitBaseEx( msg_eng, wxEmptyString ); } // --------------------------------------------------------------------------------------- - // RuntimeError / LogicError - Generalized Exceptions + // RuntimeError - Generalized Exceptions with Recoverable Traits! // --------------------------------------------------------------------------------------- class RuntimeError : public virtual BaseException @@ -161,14 +157,6 @@ namespace Exception DEFINE_RUNTIME_EXCEPTION( RuntimeError, wxLt("An unhandled runtime error has occurred, somewhere in the depths of Pcsx2's cluttered brain-matter.") ) }; - // LogicErrors do not need translated versions, since they are typically obscure, and the - // user wouldn't benefit from being able to understand them anyway. :) - class LogicError : public virtual BaseException - { - public: - DEFINE_LOGIC_EXCEPTION( LogicError, wxLt("An unhandled logic error has occurred.") ) - }; - // -------------------------------------------------------------------------------------- // CancelAppEvent - Exception for canceling an event in a non-verbose fashion // -------------------------------------------------------------------------------------- @@ -227,24 +215,6 @@ namespace Exception DEFINE_RUNTIME_EXCEPTION( OutOfMemory, wxLt("Out of Memory") ) }; - // This exception thrown any time an operation is attempted when an object - // is in an uninitialized state. - // - class InvalidOperation : public virtual LogicError - { - public: - DEFINE_LOGIC_EXCEPTION( InvalidOperation, "Attempted method call is invalid for the current object or program state." ) - }; - - // This exception thrown any time an operation is attempted when an object - // is in an uninitialized state. - // - class InvalidArgument : public virtual LogicError - { - public: - DEFINE_LOGIC_EXCEPTION( InvalidArgument, "Invalid argument passed to a function." ) - }; - class ParseError : public RuntimeError { public: diff --git a/common/include/Utilities/Threading.h b/common/include/Utilities/Threading.h index d0ce327de2..d9018c7ea5 100644 --- a/common/include/Utilities/Threading.h +++ b/common/include/Utilities/Threading.h @@ -32,6 +32,9 @@ class wxTimeSpan; namespace Threading { class PersistentThread; + + PersistentThread* pxGetCurrentThread(); + wxString pxGetCurrentThreadName(); } namespace Exception @@ -346,12 +349,11 @@ namespace Threading DeclareNoncopyableObject(PersistentThread); protected: - typedef int (*PlainJoeFP)(); - wxString m_name; // diagnostic name for our thread. pthread_t m_thread; - Semaphore m_sem_event; // general wait event that's needed by most threads. + Semaphore m_sem_event; // general wait event that's needed by most threads + Semaphore m_sem_startup; // startup sync tool Mutex m_lock_InThread; // used for canceling and closing threads in a deadlock-safe manner MutexLockRecursive m_lock_start; // used to lock the Start() code from starting simultaneous threads accidentally. @@ -369,6 +371,7 @@ namespace Threading virtual void Start(); virtual void Cancel( bool isBlocking = true ); + virtual bool Cancel( const wxTimeSpan& timeout ); virtual bool Detach(); virtual void Block(); virtual void RethrowException() const; @@ -419,12 +422,13 @@ namespace Threading void FrankenMutex( Mutex& mutex ); - bool AffinityAssert_AllowFromSelf() const; - bool AffinityAssert_DisallowFromSelf() const; + bool AffinityAssert_AllowFromSelf( const DiagnosticOrigin& origin ) const; + bool AffinityAssert_DisallowFromSelf( const DiagnosticOrigin& origin ) const; // ---------------------------------------------------------------------------- // Section of methods for internal use only. + bool _basecancel(); void _selfRunningTest( const wxChar* name ) const; void _DoSetThreadName( const wxString& name ); void _DoSetThreadName( const char* name ); diff --git a/common/include/Utilities/pxCheckBox.h b/common/include/Utilities/pxCheckBox.h index 1c22d6c43c..9fda5589d5 100644 --- a/common/include/Utilities/pxCheckBox.h +++ b/common/include/Utilities/pxCheckBox.h @@ -58,9 +58,16 @@ protected: extern void operator+=( wxSizer& target, pxCheckBox* src ); extern void operator+=( wxSizer& target, pxCheckBox& src ); +extern void operator+=( wxSizer* target, pxCheckBox& src ); template<> inline void operator+=( wxSizer& target, const pxWindowAndFlags<pxCheckBox>& src ) { target.Add( src.window, src.flags ); } + +template<> +inline void operator+=( wxSizer* target, const pxWindowAndFlags<pxCheckBox>& src ) +{ + target->Add( src.window, src.flags ); +} diff --git a/common/include/Utilities/pxRadioPanel.h b/common/include/Utilities/pxRadioPanel.h index d546687374..1d1a98d029 100644 --- a/common/include/Utilities/pxRadioPanel.h +++ b/common/include/Utilities/pxRadioPanel.h @@ -111,7 +111,8 @@ public: pxRadioPanel& SetToolTip( int idx, const wxString& tip ); pxRadioPanel& SetSelection( int idx ); - pxRadioPanel& SetDefault( int idx ); + pxRadioPanel& SetDefaultItem( int idx ); + pxRadioPanel& EnableItem( int idx, bool enable=true ); int GetSelection() const; wxWindowID GetSelectionId() const; diff --git a/common/include/Utilities/pxStaticText.h b/common/include/Utilities/pxStaticText.h index df79a30b5c..aa2d6196ea 100644 --- a/common/include/Utilities/pxStaticText.h +++ b/common/include/Utilities/pxStaticText.h @@ -86,6 +86,7 @@ protected: extern void operator+=( wxSizer& target, pxStaticText* src ); extern void operator+=( wxSizer& target, pxStaticText& src ); +extern void operator+=( wxSizer* target, pxStaticText& src ); template<> inline void operator+=( wxSizer& target, const pxWindowAndFlags<pxStaticText>& src ) @@ -94,6 +95,13 @@ inline void operator+=( wxSizer& target, const pxWindowAndFlags<pxStaticText>& s //target.Add( src.window, src.flags ); } +template<> +inline void operator+=( wxSizer* target, const pxWindowAndFlags<pxStaticText>& src ) +{ + src.window->AddTo( target, src.flags ); + //target.Add( src.window, src.flags ); +} + // -------------------------------------------------------------------------------------- // pxStaticHeading // -------------------------------------------------------------------------------------- diff --git a/common/include/Utilities/wxGuiTools.h b/common/include/Utilities/wxGuiTools.h index 7317bf8656..3b09bb2dfe 100644 --- a/common/include/Utilities/wxGuiTools.h +++ b/common/include/Utilities/wxGuiTools.h @@ -52,15 +52,25 @@ struct pxAlignmentType wxSizerFlags Apply( wxSizerFlags flags=wxSizerFlags() ) const; + wxSizerFlags operator& ( const wxSizerFlags& _flgs ) const + { + return Apply( _flgs ); + } + + wxSizerFlags Border( int dir, int padding ) const + { + return Apply().Border( dir, padding ); + } + + wxSizerFlags Proportion( int prop ) const + { + return Apply().Proportion( intval ); + } + operator wxSizerFlags() const { return Apply(); } - - wxSizerFlags operator | ( const wxSizerFlags& _flgs ) - { - return Apply( _flgs ); - } }; struct pxStretchType @@ -78,15 +88,47 @@ struct pxStretchType wxSizerFlags Apply( wxSizerFlags flags=wxSizerFlags() ) const; + wxSizerFlags operator& ( const wxSizerFlags& _flgs ) const + { + return Apply( _flgs ); + } + + wxSizerFlags Border( int dir, int padding ) const + { + return Apply().Border( dir, padding ); + } + + wxSizerFlags Proportion( int prop ) const + { + return Apply().Proportion( intval ); + } + operator wxSizerFlags() const { return Apply(); } +}; - wxSizerFlags operator | ( const wxSizerFlags& _flgs ) +class pxProportion +{ + int intval; + + pxProportion( int prop ) + { + intval = prop; + } + + wxSizerFlags Apply( wxSizerFlags flags=wxSizerFlags() ) const; + + wxSizerFlags operator& ( const wxSizerFlags& _flgs ) const { return Apply( _flgs ); } + + operator wxSizerFlags() const + { + return Apply(); + } }; extern const pxAlignmentType @@ -128,8 +170,7 @@ struct pxWindowAndFlags }; -extern wxSizerFlags operator , ( const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2 ); //pxAlignmentType align ); -//extern wxSizerFlags operator , ( const wxSizerFlags& _flgs, pxStretchType stretch ); +extern wxSizerFlags operator& ( const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2 ); template< typename WinType > pxWindowAndFlags<WinType> operator | ( WinType* _win, const wxSizerFlags& _flgs ) @@ -162,9 +203,13 @@ extern void operator+=( wxSizer& target, wxSizer* src ); extern void operator+=( wxSizer& target, wxWindow& src ); extern void operator+=( wxSizer& target, wxSizer& src ); +extern void operator+=( wxSizer* target, wxWindow& src ); +extern void operator+=( wxSizer* target, wxSizer& src ); + extern void operator+=( wxSizer& target, int spacer ); extern void operator+=( wxWindow& target, int spacer ); +// ---------------------------------------------------------------------------- // Important: This template is needed in order to retain window type information and // invoke the proper overloaded version of += (which is used by pxStaticText and other // classes to perform special actions when added to sizers). @@ -178,6 +223,14 @@ void operator+=( wxWindow& target, WinType* src ) template< typename WinType > void operator+=( wxWindow& target, WinType& src ) { + if( !pxAssert( target.GetSizer() != NULL ) ) return; + *target.GetSizer() += src; +} + +template< typename WinType > +void operator+=( wxWindow& target, const pxWindowAndFlags<WinType>& src ) +{ + if( !pxAssert( target.GetSizer() != NULL ) ) return; *target.GetSizer() += src; } @@ -187,11 +240,31 @@ void operator+=( wxSizer& target, const pxWindowAndFlags<WinType>& src ) target.Add( src.window, src.flags ); } +// ---------------------------------------------------------------------------- +// Pointer Versions! (note that C++ requires one of the two operator params be a +// "poper" object type (non-pointer), so that's why some of these are missing. + template< typename WinType > -void operator+=( wxWindow& target, const pxWindowAndFlags<WinType>& src ) +void operator+=( wxWindow* target, WinType& src ) { - if( !pxAssert( target.GetSizer() != NULL ) ) return; - *target.GetSizer() += src; + if( !pxAssert( target != NULL ) ) return; + if( !pxAssert( target->GetSizer() != NULL ) ) return; + *target->GetSizer() += src; +} + +template< typename WinType > +void operator+=( wxWindow* target, const pxWindowAndFlags<WinType>& src ) +{ + if( !pxAssert( target != NULL ) ) return; + if( !pxAssert( target->GetSizer() != NULL ) ) return; + *target->GetSizer() += src; +} + +template< typename WinType > +void operator+=( wxSizer* target, const pxWindowAndFlags<WinType>& src ) +{ + if( !pxAssert( target != NULL ) ) return; + target.Add( src.window, src.flags ); } // ---------------------------------------------------------------------------- @@ -230,19 +303,29 @@ protected: public: wxDialogWithHelpers(); - wxDialogWithHelpers(wxWindow* parent, int id, const wxString& title, bool hasContextHelp, const wxPoint& pos=wxDefaultPosition, const wxSize& size=wxDefaultSize ); + wxDialogWithHelpers(wxWindow* parent, const wxString& title, bool hasContextHelp=false, bool resizable=false ); + wxDialogWithHelpers(wxWindow* parent, const wxString& title, wxOrientation orient); virtual ~wxDialogWithHelpers() throw(); - void AddOkCancel( wxSizer& sizer, bool hasApply=false ); - pxStaticText* Text( const wxString& label ); - pxStaticHeading* Heading( const wxString& label ); + void Init(); + void AddOkCancel( wxSizer& sizer, bool hasApply=false ); + + virtual void SmartCenterFit(); + virtual int ShowModal(); + virtual bool Show( bool show=true ); + + virtual pxStaticText* Text( const wxString& label ); + virtual pxStaticHeading* Heading( const wxString& label ); + + virtual wxDialogWithHelpers& SetIdealWidth( int newWidth ) { m_idealWidth = newWidth; return *this; } - wxDialogWithHelpers& SetIdealWidth( int newWidth ) { m_idealWidth = newWidth; return *this; } int GetIdealWidth() const { return m_idealWidth; } bool HasIdealWidth() const { return m_idealWidth != wxDefaultCoord; } protected: void OnActivate(wxActivateEvent& evt); + void OnOkCancel(wxCommandEvent& evt); + void OnCloseWindow(wxCloseEvent& event); }; // -------------------------------------------------------------------------------------- @@ -412,7 +495,7 @@ public: ////////////////////////////////////////////////////////////////////////////////////////////// -extern bool pxDialogExists( wxWindowID id ); +extern bool pxDialogExists( const wxString& name ); extern bool pxIsValidWindowPosition( const wxWindow& window, const wxPoint& windowPos ); extern wxRect wxGetDisplayArea(); @@ -420,5 +503,7 @@ extern wxString pxFormatToolTipText( wxWindow* wind, const wxString& src ); extern void pxSetToolTip( wxWindow* wind, const wxString& src ); extern void pxSetToolTip( wxWindow& wind, const wxString& src ); +extern wxFont pxGetFixedFont( int ptsize=8, int weight=wxNORMAL ); + #endif diff --git a/common/include/x86emitter/tools.h b/common/include/x86emitter/tools.h index c9772295db..a460005100 100644 --- a/common/include/x86emitter/tools.h +++ b/common/include/x86emitter/tools.h @@ -40,7 +40,7 @@ struct x86CPU_INFO u32 PhysicalCores; u32 LogicalCores; - char VendorName[16]; // Vendor/Creator ID + char VendorName[16]; // Vendor/Creator ID char TypeName[20]; // cpu type char FamilyName[50]; // the original cpu name diff --git a/common/src/Utilities/Exceptions.cpp b/common/src/Utilities/Exceptions.cpp index 1fdde1c735..e838e6294b 100644 --- a/common/src/Utilities/Exceptions.cpp +++ b/common/src/Utilities/Exceptions.cpp @@ -16,6 +16,7 @@ #include "PrecompiledHeader.h" #include <wx/app.h> +#include "Threading.h" wxString GetEnglish( const char* msg ) { @@ -42,36 +43,66 @@ wxString GetTranslation( const char* msg ) // That's ok. What we don't want is the *same* thread recurse-asserting. static __threadlocal int s_assert_guard = 0; -DEVASSERT_INLINE void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const wxChar* msg) -{ -#ifdef PCSX2_DEVBUILD - RecursionGuard guard( s_assert_guard ); - if( guard.IsReentrant() ) return; +pxDoAssertFnType* pxDoAssert = pxAssertImpl_LogIt; - if( wxTheApp == NULL ) +// make life easier for people using VC++ IDE by using this format, which allows double-click +// response times from the Output window... +wxString DiagnosticOrigin::ToString( const wxChar* msg ) const +{ + wxString message; + message.reserve( 2048 ); + + message.Printf( L"%s(%d) : assertion failed:\n", srcfile, line ); + + if( function != NULL ) + message += L" Function: " + fromUTF8(function) + L"\n"; + + message += L" Thread: " + Threading::pxGetCurrentThreadName() + L"\n"; + + if( condition != NULL ) + message += L" Condition: " + wxString(condition) + L"\n"; + + if( msg != NULL ) + message += L" Message: " + wxString(msg) + L"\n"; + + return message; +} + + +bool pxAssertImpl_LogIt( const DiagnosticOrigin& origin, const wxChar *msg ) +{ + wxLogError( origin.ToString( msg ) ); + return false; +} + +DEVASSERT_INLINE void pxOnAssert( const DiagnosticOrigin& origin, const wxChar* msg ) +{ + RecursionGuard guard( s_assert_guard ); + if( guard.IsReentrant() ) { return wxTrap(); } + + // wxWidgets doesn't come with debug builds on some Linux distros, and other distros make + // it difficult to use the debug build (compilation failures). To handle these I've had to + // bypass the internal wxWidgets assertion handler entirely, since it may not exist even if + // PCSX2 itself is compiled in debug mode (assertions enabled). + + bool trapit; + + if( pxDoAssert == NULL ) { // Note: Format uses MSVC's syntax for output window hotlinking. - wxLogError( wxsFormat( L"%s(%d): Assertion failed in %s: %s\n", - file, line, fromUTF8(func).c_str(), msg ) - ); + trapit = pxAssertImpl_LogIt( origin, msg ); } else { - #ifdef __WXDEBUG__ - wxTheApp->OnAssertFailure( file, line, fromUTF8(func), cond, msg ); - #elif wxUSE_GUI - // FIXME: this should create a popup dialog for devel builds. - wxLogError( msg ); - #else - wxLogError( msg ); - #endif + trapit = pxDoAssert( origin, msg ); } -#endif + + if( trapit ) { wxTrap(); } } -__forceinline void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const char* msg) +__forceinline void pxOnAssert( const DiagnosticOrigin& origin, const char* msg) { - pxOnAssert( file, line, func, cond, fromUTF8(msg) ); + pxOnAssert( origin, fromUTF8(msg) ); } diff --git a/common/src/Utilities/ThreadTools.cpp b/common/src/Utilities/ThreadTools.cpp index 8236a50db9..ef696e7624 100644 --- a/common/src/Utilities/ThreadTools.cpp +++ b/common/src/Utilities/ThreadTools.cpp @@ -37,6 +37,61 @@ const wxTimeSpan Threading::def_yieldgui_interval( 0, 0, 0, 100 ); // three second interval for deadlock protection on waitgui. const wxTimeSpan Threading::def_deadlock_timeout( 0, 0, 3, 0 ); +//static __threadlocal PersistentThread* tls_current_thread = NULL; + +static pthread_key_t curthread_key = NULL; +static s32 total_key_count = 0; +static Mutex total_key_lock; + +static void make_curthread_key() +{ + ScopedLock lock( total_key_lock ); + if( total_key_count++ != 0 ) return; + + if( 0 != pthread_key_create(&curthread_key, NULL) ) + { + Console.Error( "Thread key creation failed (probably out of memory >_<)" ); + curthread_key = NULL; + } +} + +static void unmake_curthread_key() +{ + ScopedLock lock( total_key_lock ); + if( --total_key_count > 0 ) return; + + if( curthread_key != NULL ) + pthread_key_delete( curthread_key ); + + curthread_key = NULL; +} + + +// Returns a handle to the current persistent thread. If the current thread does not belong +// to the PersistentThread table, NULL is returned. Since the main/ui thread is not created +// through PersistentThread it will also return NULL. Callers can use wxThread::IsMain() to +// test if the NULL thread is the main thread. +PersistentThread* Threading::pxGetCurrentThread() +{ + return (curthread_key==NULL) ? NULL : (PersistentThread*)pthread_getspecific( curthread_key ); +} + +// returns the name of the current thread, or "Unknown" if the thread is neither a PersistentThread +// nor the Main/UI thread. +wxString Threading::pxGetCurrentThreadName() +{ + if( PersistentThread* thr = pxGetCurrentThread() ) + { + return thr->GetName(); + } + else if( wxThread::IsMain() ) + { + return L"Main/UI"; + } + + return L"Unknown"; +} + // (intended for internal use only) // Returns true if the Wait is recursive, or false if the Wait is safe and should be // handled via normal yielding methods. @@ -81,7 +136,7 @@ Threading::PersistentThread::PersistentThread() // This destructor performs basic "last chance" cleanup, which is a blocking join // against the thread. Extending classes should almost always implement their own // thread closure process, since any PersistentThread will, by design, not terminate -// unless it has been properly canceled. +// unless it has been properly canceled (resulting in deadlock). // // Thread safety: This class must not be deleted from its own thread. That would be // like marrying your sister, and then cheating on her with your daughter. @@ -116,14 +171,24 @@ Threading::PersistentThread::~PersistentThread() throw() DESTRUCTOR_CATCHALL } -bool Threading::PersistentThread::AffinityAssert_AllowFromSelf() const +bool Threading::PersistentThread::AffinityAssert_AllowFromSelf( const DiagnosticOrigin& origin ) const { - return pxAssertMsg( IsSelf(), wxsFormat( L"Thread affinity violation: Call allowed from '%s' thread only.", m_name.c_str() ) ); + if( IsSelf() ) return true; + + if( IsDevBuild ) + pxOnAssert( origin, wxsFormat( L"Thread affinity violation: Call allowed from '%s' thread only.", m_name.c_str() ) ); + + return false; } -bool Threading::PersistentThread::AffinityAssert_DisallowFromSelf() const +bool Threading::PersistentThread::AffinityAssert_DisallowFromSelf( const DiagnosticOrigin& origin ) const { - return pxAssertMsg( !IsSelf(), wxsFormat( L"Thread affinity violation: Call is *not* allowed from '%s' thread.", m_name.c_str() ) ); + if( !IsSelf() ) return true; + + if( IsDevBuild ) + pxOnAssert( origin, wxsFormat( L"Thread affinity violation: Call is *not* allowed from '%s' thread.", m_name.c_str() ) ); + + return false; } void Threading::PersistentThread::FrankenMutex( Mutex& mutex ) @@ -157,7 +222,29 @@ void Threading::PersistentThread::Start() m_except = NULL; if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 ) - throw Exception::ThreadCreationError(); + throw Exception::ThreadCreationError( this ); + + if( !m_sem_startup.WaitWithoutYield( wxTimeSpan( 0, 0, 3, 0 ) ) ) + { + RethrowException(); + + // And if the thread threw nothing of its own: + throw Exception::ThreadCreationError( this, "(%s thread) Start error: created thread never posted startup semaphore." ); + } + + // Event Rationale (above): Performing this semaphore wait on the created thread is "slow" in the + // sense that it stalls the calling thread completely until the new thread is created + // (which may not always be desirable). But too bad. In order to safely use 'running' locks + // and detachment management, this *has* to be done. By rule, starting new threads shouldn't + // be done very often anyway, hence the concept of Threadpooling for rapidly rotating tasks. + // (and indeed, this semaphore wait might, in fact, be very swift compared to other kernel + // overhead in starting threads). + + // (this could also be done using operating system specific calls, since any threaded OS has + // functions that allow us to see if a thread is running or not, and to block against it even if + // it's been detached -- removing the need for m_lock_InThread and the semaphore wait above. But + // pthreads kinda lacks that stuff, since pthread_join() has no timeout option making it im- + // possible to safely block against a running thread) } // Returns: TRUE if the detachment was performed, or FALSE if the thread was @@ -165,13 +252,28 @@ void Threading::PersistentThread::Start() // This function should not be called from the owner thread. bool Threading::PersistentThread::Detach() { - AffinityAssert_DisallowFromSelf(); + AffinityAssert_DisallowFromSelf(pxDiagSpot); if( _InterlockedExchange( &m_detached, true ) ) return false; pthread_detach( m_thread ); return true; } +bool Threading::PersistentThread::_basecancel() +{ + // Prevent simultaneous startup and cancel: + if( !m_running ) return false; + + if( m_detached ) + { + Console.Warning( "(Thread Warning) Ignoring attempted cancellation of detached thread." ); + return false; + } + + pthread_cancel( m_thread ); + return true; +} + // Remarks: // Provision of non-blocking Cancel() is probably academic, since destroying a PersistentThread // object performs a blocking Cancel regardless of if you explicitly do a non-blocking Cancel() @@ -183,33 +285,40 @@ bool Threading::PersistentThread::Detach() // Parameters: // isBlocking - indicates if the Cancel action should block for thread completion or not. // +// Exceptions raised by the blocking thread will be re-thrown into the main thread. If isBlocking +// is false then no exceptions will occur. +// void Threading::PersistentThread::Cancel( bool isBlocking ) { - AffinityAssert_DisallowFromSelf(); + AffinityAssert_DisallowFromSelf( pxDiagSpot ); - { - // Prevent simultaneous startup and cancel: - ScopedLock startlock( m_lock_start ); - if( !m_running ) return; + // Prevent simultaneous startup and cancel, necessary to avoid + ScopedLock startlock( m_lock_start ); - if( m_detached ) - { - Console.Warning( "(Thread Warning) Ignoring attempted cancellation of detached thread." ); - return; - } - - pthread_cancel( m_thread ); - - } + if( !_basecancel() ) return; if( isBlocking ) { - // FIXME: Add deadlock detection and handling here... ? - m_lock_InThread.Wait(); + WaitOnSelf( m_lock_InThread ); Detach(); } } +bool Threading::PersistentThread::Cancel( const wxTimeSpan& timespan ) +{ + AffinityAssert_DisallowFromSelf( pxDiagSpot ); + + // Prevent simultaneous startup and cancel: + ScopedLock startlock( m_lock_start ); + + if( !_basecancel() ) return true; + + if( !WaitOnSelf( m_lock_InThread, timespan ) ) return false; + Detach(); + return true; +} + + // Blocks execution of the calling thread until this thread completes its task. The // caller should make sure to signal the thread to exit, or else blocking may deadlock the // calling thread. Classes which extend PersistentThread should override this method @@ -218,10 +327,12 @@ void Threading::PersistentThread::Cancel( bool isBlocking ) // Returns the return code of the thread. // This method is roughly the equivalent of pthread_join(). // +// Exceptions raised by the blocking thread will be re-thrown into the main thread. +// void Threading::PersistentThread::Block() { - AffinityAssert_DisallowFromSelf(); - m_lock_InThread.Wait(); + AffinityAssert_DisallowFromSelf(pxDiagSpot); + WaitOnSelf( m_lock_InThread ); } bool Threading::PersistentThread::IsSelf() const @@ -277,7 +388,7 @@ void Threading::PersistentThread::_selfRunningTest( const wxChar* name ) const // void Threading::PersistentThread::WaitOnSelf( Semaphore& sem ) const { - if( !AffinityAssert_DisallowFromSelf() ) return; + if( !AffinityAssert_DisallowFromSelf(pxDiagSpot) ) return; while( true ) { @@ -301,7 +412,7 @@ void Threading::PersistentThread::WaitOnSelf( Semaphore& sem ) const // void Threading::PersistentThread::WaitOnSelf( Mutex& mutex ) const { - if( !AffinityAssert_DisallowFromSelf() ) return; + if( !AffinityAssert_DisallowFromSelf(pxDiagSpot) ) return; while( true ) { @@ -314,7 +425,7 @@ static const wxTimeSpan SelfWaitInterval( 0,0,0,333 ); bool Threading::PersistentThread::WaitOnSelf( Semaphore& sem, const wxTimeSpan& timeout ) const { - if( !AffinityAssert_DisallowFromSelf() ) return true; + if( !AffinityAssert_DisallowFromSelf(pxDiagSpot) ) return true; wxTimeSpan runningout( timeout ); @@ -330,7 +441,7 @@ bool Threading::PersistentThread::WaitOnSelf( Semaphore& sem, const wxTimeSpan& bool Threading::PersistentThread::WaitOnSelf( Mutex& mutex, const wxTimeSpan& timeout ) const { - if( !AffinityAssert_DisallowFromSelf() ) return true; + if( !AffinityAssert_DisallowFromSelf(pxDiagSpot) ) return true; wxTimeSpan runningout( timeout ); @@ -350,7 +461,7 @@ bool Threading::PersistentThread::WaitOnSelf( Mutex& mutex, const wxTimeSpan& ti // and cleanup, or use the DoThreadCleanup() override to perform resource cleanup). void Threading::PersistentThread::TestCancel() const { - AffinityAssert_AllowFromSelf(); + AffinityAssert_AllowFromSelf(pxDiagSpot); pthread_testcancel(); } @@ -393,24 +504,16 @@ void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::* } #ifndef PCSX2_DEVBUILD // ---------------------------------------------------------------------------- - // Allow logic errors to propagate out of the thread in release builds, so that they might be - // handled in non-fatal ways. On Devbuilds let them loose, so that they produce debug stack - // traces and such. - catch( std::logic_error& ex ) + // Bleh... don't bother with std::exception. runtime_error should catch anything + // useful coming out of the core STL libraries anyway, and these are best handled by + // the MSVC debugger (or by silent random annoying fail on debug-less linux). + /*catch( std::logic_error& ex ) { - throw Exception::LogicError( wxsFormat( L"(thread: %s) STL Logic Error: %s\n\t%s", + throw Exception::BaseException( wxsFormat( L"(thread: %s) STL Logic Error: %s\n\t%s", GetName().c_str(), fromUTF8( ex.what() ).c_str() ) ); } - catch( Exception::LogicError& ex ) - { - m_except = ex.Clone(); - m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg(); - } - // ---------------------------------------------------------------------------- - // Bleh... don't bother with std::exception. std::logic_error and runtime_error should catch - // anything coming out of the core STL libraries anyway. - /*catch( std::exception& ex ) + catch( std::exception& ex ) { throw Exception::BaseException( wxsFormat( L"(thread: %s) STL exception: %s\n\t%s", GetName().c_str(), fromUTF8( ex.what() ).c_str() ) @@ -431,7 +534,7 @@ void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::* // OnCleanupInThread() to extend cleanup functionality. void Threading::PersistentThread::_ThreadCleanup() { - AffinityAssert_AllowFromSelf(); + AffinityAssert_AllowFromSelf(pxDiagSpot); _try_virtual_invoke( &PersistentThread::OnCleanupInThread ); m_lock_InThread.Release(); } @@ -442,42 +545,59 @@ wxString Threading::PersistentThread::GetName() const } // This override is called by PeristentThread when the thread is first created, prior to -// calling ExecuteTaskInThread. This is useful primarily for "base" classes that extend -// from PersistentThread, giving them the ability to bind startup code to all threads that -// derive from them. (the alternative would have been to make ExecuteTaskInThread a -// private member, and provide a new Task executor by a different name). +// calling ExecuteTaskInThread, and after the initial InThread lock has been claimed. +// This code is also executed within a "safe" environment, where the creating thread is +// blocked against m_sem_event. Make sure to do any necessary variable setup here, without +// worry that the calling thread might attempt to test the status of those variables +// before initialization has completed. +// void Threading::PersistentThread::OnStartInThread() { - m_running = true; m_detached = false; + m_running = true; } void Threading::PersistentThread::_internal_execute() { m_lock_InThread.Acquire(); - OnStartInThread(); _DoSetThreadName( m_name ); + make_curthread_key(); + if( curthread_key != NULL ) + pthread_setspecific( curthread_key, this ); + + OnStartInThread(); + m_sem_startup.Post(); _try_virtual_invoke( &PersistentThread::ExecuteTaskInThread ); } +// Called by Start, prior to actual starting of the thread, and after any previous +// running thread has been canceled or detached. void Threading::PersistentThread::OnStart() { FrankenMutex( m_lock_InThread ); m_sem_event.Reset(); + m_sem_startup.Reset(); } +// Extending classes that override this method shoul always call it last from their +// personal implementations. void Threading::PersistentThread::OnCleanupInThread() { m_running = false; + + if( curthread_key != NULL ) + pthread_setspecific( curthread_key, NULL ); + + unmake_curthread_key(); } // passed into pthread_create, and is used to dispatch the thread's object oriented // callback function void* Threading::PersistentThread::_internal_callback( void* itsme ) { - pxAssert( itsme != NULL ); + if( !pxAssertDev( itsme != NULL, wxNullChar ) ) return NULL; PersistentThread& owner = *((PersistentThread*)itsme); pthread_cleanup_push( _pt_callback_cleanup, itsme ); @@ -493,8 +613,6 @@ void Threading::PersistentThread::_DoSetThreadName( const wxString& name ) void Threading::PersistentThread::_DoSetThreadName( const char* name ) { - if( !AffinityAssert_AllowFromSelf() ) return; - // This feature needs Windows headers and MSVC's SEH support: #if defined(_WINDOWS_) && defined (_MSC_VER) diff --git a/common/src/Utilities/pxCheckBox.cpp b/common/src/Utilities/pxCheckBox.cpp index 4663b06788..300b752974 100644 --- a/common/src/Utilities/pxCheckBox.cpp +++ b/common/src/Utilities/pxCheckBox.cpp @@ -81,3 +81,8 @@ void operator+=( wxSizer& target, pxCheckBox& src ) { target.Add( &src, wxSF.Expand() ); } + +void operator+=( wxSizer* target, pxCheckBox& src ) +{ + target->Add( &src, wxSF.Expand() ); +} diff --git a/common/src/Utilities/pxRadioPanel.cpp b/common/src/Utilities/pxRadioPanel.cpp index 3af711786e..920e1b979c 100644 --- a/common/src/Utilities/pxRadioPanel.cpp +++ b/common/src/Utilities/pxRadioPanel.cpp @@ -149,13 +149,13 @@ void pxRadioPanel::_RealizeDefaultOption() } } -pxRadioPanel& pxRadioPanel::SetDefault( int idx ) +pxRadioPanel& pxRadioPanel::SetDefaultItem( int idx ) { if( idx == m_DefaultIdx ) return *this; if( m_IsRealized && m_DefaultIdx != -1 ) { - wxFont def( GetFont() ); + wxFont def(GetFont()); m_objects[m_DefaultIdx].LabelObj->SetFont( def ); m_objects[m_DefaultIdx].LabelObj->SetForegroundColour( GetForegroundColour() ); } @@ -166,6 +166,20 @@ pxRadioPanel& pxRadioPanel::SetDefault( int idx ) return *this; } +pxRadioPanel& pxRadioPanel::EnableItem( int idx, bool enable ) +{ + pxAssertDev( m_IsRealized, "RadioPanel must be realized first, prior to enabling or disabling individual items." ); + + if( m_objects[idx].LabelObj ) + m_objects[idx].LabelObj->Enable( enable ); + + if( m_objects[idx].SubTextObj ) + m_objects[idx].SubTextObj->Enable( enable ); + + return *this; +} + + int pxRadioPanel::GetSelection() const { if( !VerifyRealizedState() ) return 0; diff --git a/common/src/Utilities/pxStaticText.cpp b/common/src/Utilities/pxStaticText.cpp index d52f1d7fd7..28ddf7e0fb 100644 --- a/common/src/Utilities/pxStaticText.cpp +++ b/common/src/Utilities/pxStaticText.cpp @@ -131,3 +131,8 @@ void operator+=( wxSizer& target, pxStaticText& src ) { src.AddTo( target ); } + +void operator+=( wxSizer* target, pxStaticText& src ) +{ + src.AddTo( target ); +} diff --git a/common/src/Utilities/wxGuiTools.cpp b/common/src/Utilities/wxGuiTools.cpp index 37e53f2ea2..4348e773bb 100644 --- a/common/src/Utilities/wxGuiTools.cpp +++ b/common/src/Utilities/wxGuiTools.cpp @@ -99,7 +99,12 @@ wxSizerFlags pxStretchType::Apply( wxSizerFlags flags ) const return flags; } -wxSizerFlags operator , ( const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2 ) +wxSizerFlags pxProportion::Apply( wxSizerFlags flags ) const +{ + return flags.Proportion( intval ); +} + +wxSizerFlags operator& ( const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2 ) { //return align.Apply( _flgs ); wxSizerFlags retval; @@ -118,11 +123,8 @@ wxSizerFlags operator , ( const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2 return retval; } -/*wxSizerFlags operator | ( const wxSizerFlags& _flgs, pxStretchType stretch ) -{ - return stretch.Apply( _flgs ); -}*/ - +// ---------------------------------------------------------------------------- +// Reference/Handle versions! void operator+=( wxSizer& target, wxWindow* src ) { @@ -148,6 +150,23 @@ void operator+=( wxSizer& target, int spacer ) { target.AddSpacer( spacer ); } +// ---------------------------------------------------------------------------- +// Pointer versions! (note that C++ requires one of the two operator params be a +// "poper" object type (non-pointer), so that's why there's only a couple of these. + +void operator+=( wxSizer* target, wxWindow& src ) +{ + if( !pxAssert( target != NULL ) ) return; + target->Add( &src ); +} + +void operator+=( wxSizer* target, wxSizer& src ) +{ + if( !pxAssert( target != NULL ) ) return; + target->Add( &src ); +} + +// ---------------------------------------------------------------------------- void operator+=( wxWindow& target, int spacer ) { @@ -431,3 +450,14 @@ void pxSetToolTip( wxWindow& wind, const wxString& src ) { pxSetToolTip( &wind, src ); } + + +wxFont pxGetFixedFont( int ptsize, int weight ) +{ + return wxFont( + ptsize, wxMODERN, wxNORMAL, weight, false, +#ifdef __WXMSW__ + L"Lucida Console" // better than courier new (win32 only) +#endif + ); +} diff --git a/common/src/Utilities/wxHelpers.cpp b/common/src/Utilities/wxHelpers.cpp index 32d6c711ef..d97f4ee15f 100644 --- a/common/src/Utilities/wxHelpers.cpp +++ b/common/src/Utilities/wxHelpers.cpp @@ -28,13 +28,9 @@ using namespace pxSizerFlags; // wxDialogWithHelpers Class Implementations // ===================================================================================================== -HashTools::HashMap< wxWindowID, int > m_DialogIdents( 0, wxID_ANY ); - -bool pxDialogExists( wxWindowID id ) +bool pxDialogExists( const wxString& name ) { - int dest = 0; - m_DialogIdents.TryGetValue( id, dest ); - return (dest > 0); + return wxFindWindowByName( name ) != NULL; } // -------------------------------------------------------------------------------------- @@ -44,20 +40,41 @@ IMPLEMENT_DYNAMIC_CLASS(wxDialogWithHelpers, wxDialog) wxDialogWithHelpers::wxDialogWithHelpers() { - m_idealWidth = wxDefaultCoord; m_hasContextHelp = false; m_extraButtonSizer = NULL; + + Init(); } -wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, int id, const wxString& title, bool hasContextHelp, const wxPoint& pos, const wxSize& size ) - : wxDialog( parent, id, title, pos, size , wxDEFAULT_DIALOG_STYLE) //, (wxCAPTION | wxMAXIMIZE | wxCLOSE_BOX | wxRESIZE_BORDER) ), // flags for resizable dialogs, currently unused. +wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, const wxString& title, bool hasContextHelp, bool resizable ) + : wxDialog( parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE | (resizable ? wxRESIZE_BORDER : 0) + ) { - ++m_DialogIdents[GetId()]; + m_hasContextHelp = hasContextHelp; + Init(); +} +wxDialogWithHelpers::wxDialogWithHelpers(wxWindow* parent, const wxString& title, wxOrientation orient) + : wxDialog( parent, wxID_ANY, title ) +{ + m_hasContextHelp = false; + SetSizer( new wxBoxSizer( orient ) ); + Init(); + + m_idealWidth = 500; + *this += StdPadding; +} + +wxDialogWithHelpers::~wxDialogWithHelpers() throw() +{ +} + +void wxDialogWithHelpers::Init() +{ m_idealWidth = wxDefaultCoord; m_extraButtonSizer = NULL; - m_hasContextHelp = hasContextHelp; if( m_hasContextHelp ) delete wxHelpProvider::Set( new wxSimpleHelpProvider() ); @@ -65,14 +82,54 @@ wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, int id, const wxStr // indicate that it should, so I presume the problem is in wxWidgets and that (hopefully!) // an updated version will fix it later. I tried to fix it using a manual Connect but it // didn't do any good. (problem could also be my Co-Linux / x-window manager) - + //Connect( wxEVT_ACTIVATE, wxActivateEventHandler(wxDialogWithHelpers::OnActivate) ); + + Connect( wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (wxDialogWithHelpers::OnOkCancel) ); + Connect( wxID_CANCEL, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (wxDialogWithHelpers::OnOkCancel) ); + Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler (wxDialogWithHelpers::OnCloseWindow) ); } -wxDialogWithHelpers::~wxDialogWithHelpers() throw() +void wxDialogWithHelpers::SmartCenterFit() { - --m_DialogIdents[GetId()]; - pxAssert( m_DialogIdents[GetId()] >= 0 ); + Fit(); + + // Smart positioning logic! If our parent window is larger than our window by some + // good amount, then we center on that. If not, center relative to the screen. This + // avoids the popup automatically eclipsing the parent window (which happens in PCSX2 + // a lot since the main window is small). + + bool centerfail = true; + if( wxWindow* parent = GetParent() ) + { + const wxSize parentSize( parent->GetSize() ); + + if( (parentSize.x > ((int)GetSize().x * 1.75)) && (parentSize.y > ((int)GetSize().y * 1.75)) ) + { + CenterOnParent(); + centerfail = false; + } + } + + if( centerfail ) CenterOnScreen(); +} + +// Overrides wxDialog behavior to include automatic Fit() and CenterOnParent/Screen. The centering +// is based on a heuristic the centers against the parent window if the parent window is at least +// 75% larger than the fitted dialog. +int wxDialogWithHelpers::ShowModal() +{ + SmartCenterFit(); + return wxDialog::ShowModal(); +} + +// Overrides wxDialog behavior to include automatic Fit() and CenterOnParent/Screen. The centering +// is based on a heuristic the centers against the parent window if the parent window is at least +// 75% larger than the fitted dialog. +bool wxDialogWithHelpers::Show( bool show ) +{ + if( show ) SmartCenterFit(); + return wxDialog::Show( show ); } pxStaticText* wxDialogWithHelpers::Text( const wxString& label ) @@ -85,6 +142,19 @@ pxStaticHeading* wxDialogWithHelpers::Heading( const wxString& label ) return new pxStaticHeading( this, label ); } +void wxDialogWithHelpers::OnCloseWindow( wxCloseEvent& evt ) +{ + if( !IsModal() ) Destroy(); + evt.Skip(); +} + +void wxDialogWithHelpers::OnOkCancel( wxCommandEvent& evt ) +{ + Close(); + evt.Skip(); +} + + void wxDialogWithHelpers::OnActivate(wxActivateEvent& evt) { //evt.Skip(); @@ -119,7 +189,7 @@ void wxDialogWithHelpers::AddOkCancel( wxSizer &sizer, bool hasApply ) flex.AddGrowableCol( 1, 15 ); flex += m_extraButtonSizer | pxAlignLeft; - flex += s_buttons | pxExpand, pxCenter; + flex += s_buttons | (pxExpand & pxCenter); sizer += flex | StdExpand(); diff --git a/common/src/Utilities/x86/MemcpyFast.cpp b/common/src/Utilities/x86/MemcpyFast.cpp index 949d735caa..c89af64980 100644 --- a/common/src/Utilities/x86/MemcpyFast.cpp +++ b/common/src/Utilities/x86/MemcpyFast.cpp @@ -592,7 +592,7 @@ $memcpy_final: // (zerofrog) u8 memcmp_mmx(const void* src1, const void* src2, int cmpsize) { - assert( (cmpsize&7) == 0 ); + pxAssert( (cmpsize&7) == 0 ); __asm { push esi @@ -766,7 +766,7 @@ End: // returns the xor of all elements, cmpsize has to be mult of 8 void memxor_mmx(void* dst, const void* src1, int cmpsize) { - assert( (cmpsize&7) == 0 ); + pxAssert( (cmpsize&7) == 0 ); __asm { mov ecx, cmpsize diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp index 3e8702bb00..7958d78634 100644 --- a/pcsx2/CDVD/CDVD.cpp +++ b/pcsx2/CDVD/CDVD.cpp @@ -745,7 +745,7 @@ __forceinline void cdvdReadInterrupt() // An arbitrary delay of some number of cycles probably makes more sense here, // but for now it's based on the cdvd.ReadTime value. -- air - assert((int)cdvd.ReadTime > 0 ); + pxAssume((int)cdvd.ReadTime > 0 ); CDVDREAD_INT(cdvd.ReadTime/4); return; } diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp index 536b668835..f5cceed009 100644 --- a/pcsx2/Counters.cpp +++ b/pcsx2/Counters.cpp @@ -340,7 +340,7 @@ static __forceinline void frameLimit() static __forceinline void VSyncStart(u32 sCycle) { Cpu->CheckExecutionState(); - SysCoreThread::Get().VsyncInThread(); + GetCoreThread().VsyncInThread(); EECNT_LOG( "///////// EE COUNTER VSYNC START (frame: %6d) \\\\\\\\\\\\\\\\\\\\ ", iFrame ); diff --git a/pcsx2/Interpreter.cpp b/pcsx2/Interpreter.cpp index d57a9a3a4b..b8c39320c4 100644 --- a/pcsx2/Interpreter.cpp +++ b/pcsx2/Interpreter.cpp @@ -382,15 +382,16 @@ static void intExecute() // Mem protection should be handled by the caller here so that it can be // done in a more optimized fashion. - while( true ) - { - execI(); - } + try { + while( true ) + execI(); + } catch( Exception::ForceDispatcherReg& ) { } } static void intCheckExecutionState() { - SysCoreThread::Get().StateCheckInThread(); + if( GetCoreThread().HasPendingStateChangeRequest() ) + throw Exception::ForceDispatcherReg(); } static void intStep() diff --git a/pcsx2/PluginManager.cpp b/pcsx2/PluginManager.cpp index c8de501e21..56b13c0609 100644 --- a/pcsx2/PluginManager.cpp +++ b/pcsx2/PluginManager.cpp @@ -680,7 +680,7 @@ PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] ) Console.WriteLn( L"Binding %s\t: %s ", tbl_PluginInfo[pid].GetShortname().c_str(), folders[pid].c_str() ); if( folders[pid].IsEmpty() ) - throw Exception::InvalidArgument( "Empty plugin filename." ); + throw Exception::PluginInitError( pi->id, "Empty plugin filename." ); m_info[pid].Filename = folders[pid]; @@ -985,6 +985,14 @@ void PluginManager::Close( PluginsEnum_t pid ) void PluginManager::Close( bool closegs ) { + // Spam stopper: If all plugins are already closed, then return before writing any logs. >_< + + const PluginInfo* pi = tbl_PluginInfo; do { + if( m_info[pi->id].IsOpened && (closegs || (pi->id != PluginId_GS)) ) break; + } while( ++pi, pi->shortname != NULL ); + + if( pi->shortname == NULL ) return; + DbgCon.WriteLn( Color_StrongBlue, "Closing plugins..." ); // Close plugins in reverse order of the initialization procedure. diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp index a0bc0ca8ae..9c44bed69d 100644 --- a/pcsx2/R5900.cpp +++ b/pcsx2/R5900.cpp @@ -39,7 +39,6 @@ R5900cpu *Cpu = NULL; bool g_ExecBiosHack = false; // set if the BIOS has already been executed -static bool cpuIsInitialized = false; static const uint eeWaitCycles = 3072; bool eeEventTestIsActive = false; @@ -52,10 +51,6 @@ void cpuReset() if( GetMTGS().IsOpen() ) GetMTGS().WaitGS(); // GS better be done processing before we reset the EE, just in case. - SysClearExecutionCache(); - - cpuIsInitialized = true; - memReset(); psxMemReset(); vuMicroMemReset(); diff --git a/pcsx2/RecoverySystem.cpp b/pcsx2/RecoverySystem.cpp index ce37e796b2..b79088be68 100644 --- a/pcsx2/RecoverySystem.cpp +++ b/pcsx2/RecoverySystem.cpp @@ -3,7 +3,7 @@ * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. + * ation, either version 3 of te License, or (at your option) any later version. * * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR @@ -22,8 +22,11 @@ class _BaseStateThread; +// Used to hold the current state backup (fullcopy of PS2 memory and plugin states). static SafeArray<u8> state_buffer; +_BaseStateThread* current_state_thread = NULL; + // Simple lock boolean for the state buffer being in use by a thread. static NonblockingMutex state_buffer_lock; @@ -56,9 +59,22 @@ class _BaseStateThread : public PersistentThread protected: EventListenerBinding<AppEventType> m_bind_OnExit; + bool m_isStarted; + + // Holds the pause/suspend state of the emulator when the state load/stave chain of action is started, + // so that the proper state can be restoed automatically on completion. + bool m_resume_when_done; + public: virtual ~_BaseStateThread() throw() { + if( !m_isStarted ) return; + + // Assertion fails because C++ changes the 'this' pointer to the base class since + // derived classes have been deallocated at this point the destructor! + + //pxAssumeDev( current_state_thread == this, wxCharNull ); + current_state_thread = NULL; state_buffer_lock.Release(); // just in case; } @@ -67,14 +83,18 @@ protected: m_bind_OnExit( wxGetApp().Source_AppStatus(), EventListener<AppEventType>( this, StateThread_OnAppStatus ) ) { Callback_FreezeFinished = onFinished; - m_name = L"StateThread::" + fromUTF8(name); + m_name = L"StateThread::" + fromUTF8(name); + m_isStarted = false; + m_resume_when_done = false; } void OnStart() { if( !state_buffer_lock.TryAcquire() ) throw Exception::CancelEvent( m_name + L"request ignored: state copy buffer is already locked!" ); - + + current_state_thread = this; + m_isStarted = true; _parent::OnStart(); } @@ -83,6 +103,7 @@ protected: wxCommandEvent evt( pxEVT_FreezeThreadFinished ); evt.SetClientData( this ); evt.SetInt( type ); + evt.SetExtraLong( m_resume_when_done ); wxGetApp().AddPendingEvent( evt ); } @@ -107,7 +128,7 @@ protected: { _parent::OnStart(); ++sys_resume_lock; - CoreThread.Pause(); + m_resume_when_done = CoreThread.Pause(); } void ExecuteTaskInThread() @@ -122,43 +143,6 @@ protected: } }; -// -------------------------------------------------------------------------------------- -// StateThread_Thaw -// -------------------------------------------------------------------------------------- -class StateThread_Thaw : public _BaseStateThread -{ - typedef _BaseStateThread _parent; - -public: - StateThread_Thaw( FnType_OnThreadComplete* onFinished ) : _BaseStateThread( "Thaw", onFinished ) { } - -protected: - void OnStart() - { - _parent::OnStart(); - - if( state_buffer.IsDisposed() ) - { - state_buffer_lock.Release(); - throw Exception::RuntimeError( "ThawState request made, but no valid state exists!" ); - } - - ++sys_resume_lock; - CoreThread.Pause(); - } - - void ExecuteTaskInThread() - { - memLoadingState( state_buffer ).FreezeAll(); - } - - void OnCleanupInThread() - { - SendFinishEvent( StateThreadAction_Restore ); - _parent::OnCleanupInThread(); - } -}; - // -------------------------------------------------------------------------------------- // StateThread_ZipToDisk // -------------------------------------------------------------------------------------- @@ -171,14 +155,15 @@ protected: gzFile m_gzfp; public: - StateThread_ZipToDisk( FnType_OnThreadComplete* onFinished, const wxString& file ) : - _BaseStateThread( "ZipToDisk", onFinished ) - , m_filename( file ) - , m_gzfp( NULL ) + StateThread_ZipToDisk( FnType_OnThreadComplete* onFinished, bool resume_done, const wxString& file ) + : _BaseStateThread( "ZipToDisk", onFinished ) + , m_filename( file ) { + m_gzfp = NULL; + m_resume_when_done = resume_done; } - ~StateThread_ZipToDisk() throw() + virtual ~StateThread_ZipToDisk() throw() { if( m_gzfp != NULL ) gzclose( m_gzfp ); } @@ -232,15 +217,16 @@ protected: bool m_finished; public: - StateThread_UnzipFromDisk( FnType_OnThreadComplete* onFinished, const wxString& file ) : - _BaseStateThread( "UnzipFromDisk", onFinished ) - , m_filename( file ) - , m_gzfp( NULL ) - , m_finished( false ) + StateThread_UnzipFromDisk( FnType_OnThreadComplete* onFinished, bool resume_done, const wxString& file ) + : _BaseStateThread( "UnzipFromDisk", onFinished ) + , m_filename( file ) { + m_gzfp = NULL; + m_finished = false; + m_resume_when_done = resume_done; } - ~StateThread_UnzipFromDisk() throw() + virtual ~StateThread_UnzipFromDisk() throw() { if( m_gzfp != NULL ) gzclose( m_gzfp ); } @@ -297,32 +283,17 @@ void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt ) } if( fn_tmp != NULL ) fn_tmp( evt ); - - //m_evtsrc_FreezeThreadFinished.Dispatch( evt ); } -void OnFinished_Resume( const wxCommandEvent& evt ) +static void OnFinished_Resume( const wxCommandEvent& evt ) { - if( evt.GetInt() == StateThreadAction_Restore ) - { - // Successfully restored state, so remove the copy. Don't remove it sooner - // because the thread may have failed with some exception/error. - - state_buffer.Dispose(); - SysClearExecutionCache(); - } - - CoreThread.Resume(); -} - -void OnFinished_Dispose( const wxCommandEvent& evt ) -{ - state_buffer.Dispose(); + CoreThread.RecoverState(); + if( evt.GetExtraLong() ) CoreThread.Resume(); } static wxString zip_dest_filename; -void OnFinished_ZipToDisk( const wxCommandEvent& evt ) +static void OnFinished_ZipToDisk( const wxCommandEvent& evt ) { if( !pxAssertDev( evt.GetInt() == StateThreadAction_Create, "Unexpected StateThreadAction value, aborting save." ) ) return; @@ -331,21 +302,13 @@ void OnFinished_ZipToDisk( const wxCommandEvent& evt ) Console.Warning( "Cannot save state to disk: empty filename specified." ); return; } - + // Phase 2: Record to disk!! - (new StateThread_ZipToDisk( OnFinished_Dispose, zip_dest_filename ))->Start(); + (new StateThread_ZipToDisk( NULL, !!evt.GetExtraLong(), zip_dest_filename ))->Start(); CoreThread.Resume(); } -void OnFinished_Restore( const wxCommandEvent& evt ) -{ - if( !pxAssertDev( evt.GetInt() == StateThreadAction_UnzipFromDisk, "Unexpected StateThreadAction value, aborting restore." ) ) return; - - // Phase 2: Restore over existing VM state!! - (new StateThread_Thaw( OnFinished_Resume ))->Start(); -} - // ===================================================================================================== // StateCopy Public Interface @@ -362,8 +325,8 @@ void StateCopy_SaveToFile( const wxString& file ) void StateCopy_LoadFromFile( const wxString& file ) { if( state_buffer_lock.IsLocked() ) return; - CoreThread.Pause(); - (new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start(); + bool resume_when_done = CoreThread.Pause(); + (new StateThread_UnzipFromDisk( OnFinished_Resume, resume_when_done, file ))->Start(); } // Saves recovery state info to the given saveslot, or saves the active emulation state @@ -394,13 +357,13 @@ void StateCopy_LoadFromSlot( uint slot ) Console.WriteLn( Color_StrongGreen, "Loading savestate from slot %d...", slot ); Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() ); - CoreThread.Pause(); - (new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start(); + bool resume_when_done = CoreThread.Pause(); + (new StateThread_UnzipFromDisk( OnFinished_Resume, resume_when_done, file ))->Start(); } bool StateCopy_IsValid() { - return !state_buffer.IsDisposed(); +return !state_buffer.IsDisposed(); } const SafeArray<u8>* StateCopy_GetBuffer() @@ -412,20 +375,45 @@ const SafeArray<u8>* StateCopy_GetBuffer() void StateCopy_FreezeToMem() { if( state_buffer_lock.IsLocked() ) return; - (new StateThread_Freeze( OnFinished_Restore ))->Start(); + (new StateThread_Freeze( OnFinished_Resume ))->Start(); } -void StateCopy_ThawFromMem() +static void _acquire_and_block() { - if( state_buffer_lock.IsLocked() ) return; - new StateThread_Thaw( OnFinished_Restore ); + if( state_buffer_lock.TryAcquire() ) return; + + /* + // If the state buffer is locked and we're being called from the main thread then we need + // to cancel the current action. This is needed because state_buffer_lock is only updated + // from events handled on the main thread. + + if( wxThread::IsMain() ) + throw Exception::CancelEvent( "Blocking ThawFromMem canceled due to existing state buffer lock." ); + else*/ + { + pxAssume( current_state_thread != NULL ); + do { + current_state_thread->Block(); + wxGetApp().ProcessPendingEvents(); // Trying this for now, may or may not work due to recursive pitfalls (see above) + } while ( !state_buffer_lock.TryAcquire() ); + } } -void State_ThawFromMem_Blocking() +void StateCopy_FreezeToMem_Blocking() { - if( !state_buffer_lock.TryAcquire() ) + _acquire_and_block(); + + memSavingState( state_buffer ).FreezeAll(); + state_buffer_lock.Release(); +} + +// Copies the saved state into the active VM, and automatically free's the saved state data. +void StateCopy_ThawFromMem_Blocking() +{ + _acquire_and_block(); memLoadingState( state_buffer ).FreezeAll(); + state_buffer.Dispose(); state_buffer_lock.Release(); } @@ -439,3 +427,4 @@ bool StateCopy_IsBusy() { return state_buffer_lock.IsLocked(); } + \ No newline at end of file diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h index a2da5563db..85e27c6a9d 100644 --- a/pcsx2/SaveState.h +++ b/pcsx2/SaveState.h @@ -204,7 +204,9 @@ public: extern bool StateCopy_IsValid(); extern void StateCopy_FreezeToMem(); -extern void StateCopy_ThawFromMem(); +extern void StateCopy_FreezeToMem_Blocking(); +extern void StateCopy_ThawFromMem_Blocking(); + extern void StateCopy_SaveToFile( const wxString& file ); extern void StateCopy_LoadFromFile( const wxString& file ); extern void StateCopy_SaveToSlot( uint num ); diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp index bacb59245d..ac93877e56 100644 --- a/pcsx2/System.cpp +++ b/pcsx2/System.cpp @@ -57,7 +57,7 @@ TraceLogFilters& SetTraceConfig() // This function should be called once during program execution. -void SysDetect() +void SysLogMachineCaps() { Console.WriteLn( Color_StrongGreen, "PCSX2 %d.%d.%d.r%d %s - compiled on " __DATE__, PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo, SVN_REV, SVN_MODS ? "(modded)" : "" @@ -66,8 +66,6 @@ void SysDetect() Console.WriteLn( "Savestate version: 0x%x", g_SaveVersion); Console.Newline(); - cpudetectInit(); - Console.WriteLn( Color_StrongBlack, "x86-32 Init:" ); Console.Indent().WriteLn( @@ -149,13 +147,15 @@ CpuInitializer< CpuType >::CpuInitializer() } catch( Exception::RuntimeError& ex ) { - Console.Error( L"MicroVU0 Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() ); - MyCpu->Shutdown(); + Console.Error( L"CPU provider error:\n\t" + ex.FormatDiagnosticMessage() ); + if( MyCpu ) + MyCpu = NULL; } catch( std::runtime_error& ex ) { - Console.Error( L"MicroVU0 Recompiler Allocation Failed (STL Exception)\n\tDetails:" + fromUTF8( ex.what() ) ); - MyCpu->Shutdown(); + Console.Error( L"CPU provider error (STL Exception)\n\tDetails:" + fromUTF8( ex.what() ) ); + if( MyCpu ) + MyCpu = NULL; } } diff --git a/pcsx2/System.h b/pcsx2/System.h index a7a3100e52..b151375433 100644 --- a/pcsx2/System.h +++ b/pcsx2/System.h @@ -62,7 +62,7 @@ protected: // implemented by the provisioning interface. extern SysCoreAllocations& GetSysCoreAlloc(); -extern void SysDetect(); // Detects cpu type and fills cpuInfo structs. +extern void SysLogMachineCaps(); // Detects cpu type and fills cpuInfo structs. extern void SysClearExecutionCache(); // clears recompiled execution caches! @@ -106,6 +106,7 @@ extern void NTFS_CompressFile( const wxString& file, bool compressStatus=true ); // class pxMessageBoxEvent; +class pxAssertionEvent; namespace Msgbox { @@ -116,10 +117,9 @@ namespace Msgbox extern bool YesNo( const wxString& text, const wxString& caption=L"PCSX2 Message", int icon=wxICON_QUESTION ); extern int Assertion( const wxString& text, const wxString& stacktrace ); - extern void Except( const Exception::BaseException& src ); } BEGIN_DECLARE_EVENT_TYPES() DECLARE_EVENT_TYPE( pxEVT_MSGBOX, -1 ) - DECLARE_EVENT_TYPE( pxEVT_CallStackBox, -1 ) + DECLARE_EVENT_TYPE( pxEVT_ASSERTION, -1 ) END_DECLARE_EVENT_TYPES() diff --git a/pcsx2/System/SysCoreThread.cpp b/pcsx2/System/SysCoreThread.cpp index dc975e0ae3..e0030cdeb4 100644 --- a/pcsx2/System/SysCoreThread.cpp +++ b/pcsx2/System/SysCoreThread.cpp @@ -56,6 +56,18 @@ void SysCoreThread::Cancel( bool isBlocking ) { m_CoreCancelDamnit = true; _parent::Cancel(); + ReleaseResumeLock(); +} + +bool SysCoreThread::Cancel( const wxTimeSpan& span ) +{ + m_CoreCancelDamnit = true; + if( _parent::Cancel( span ) ) + { + ReleaseResumeLock(); + return true; + } + return false; } void SysCoreThread::Start() @@ -78,21 +90,26 @@ void SysCoreThread::Start() void SysCoreThread::OnResumeReady() { if( m_resetVirtualMachine ) - { - cpuReset(); - m_resetVirtualMachine = false; - m_hasValidState = false; - } + m_hasValidState = false; if( !m_hasValidState ) m_resetRecompilers = true; } +// Tells the thread to recover from the in-memory state copy when it resumes. (thread must be +// resumed manually). +void SysCoreThread::RecoverState() +{ + Pause(); + m_resetVirtualMachine = true; + m_hasValidState = false; +} + void SysCoreThread::Reset() { Suspend(); - m_resetVirtualMachine = true; - m_hasValidState = false; + m_resetVirtualMachine = true; + m_hasValidState = false; } // This function *will* reset the emulator in order to allow the specified elf file to @@ -159,7 +176,8 @@ void SysCoreThread::ApplySettings( const Pcsx2Config& src ) ScopedCoreThreadPause sys_paused; - m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks ); + m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Recompiler != EmuConfig.Recompiler ) || + ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks ); m_resetProfilers = ( src.Profiler != EmuConfig.Profiler ); m_resetVsyncTimers = ( src.GS != EmuConfig.GS ); @@ -213,10 +231,50 @@ struct ScopedBool_ClearOnError void Success() { m_success = true; } }; +void SysCoreThread::_reset_stuff_as_needed() +{ + if( m_resetVirtualMachine || m_resetRecompilers || m_resetProfilers ) + { + SysClearExecutionCache(); + memBindConditionalHandlers(); + m_resetRecompilers = false; + m_resetProfilers = false; + } + + if( m_resetVirtualMachine ) + { + cpuReset(); + m_resetVirtualMachine = false; + m_resetRecompilers = true; + } + + if( m_resetVsyncTimers ) + { + UpdateVSyncRate(); + frameLimitReset(); + m_resetVsyncTimers = false; + } + + SetCPUState( EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVUMXCSR ); +} + void SysCoreThread::CpuInitializeMess() { if( m_hasValidState ) return; + if( StateCopy_IsValid() ) + { + // Automatic recovery system if a state exists in memory. This is executed here + // in order to ensure the plugins are in the proper (loaded/opened) state. + + SysClearExecutionCache(); + StateCopy_ThawFromMem_Blocking(); + + m_hasValidState = true; + m_resetVirtualMachine = false; + return; + } + _reset_stuff_as_needed(); ScopedBool_ClearOnError sbcoe( m_hasValidState ); @@ -262,24 +320,6 @@ void SysCoreThread::CpuInitializeMess() sbcoe.Success(); } -void SysCoreThread::_reset_stuff_as_needed() -{ - if( m_resetRecompilers || m_resetProfilers ) - { - SysClearExecutionCache(); - memBindConditionalHandlers(); - m_resetRecompilers = false; - m_resetProfilers = false; - } - - if( m_resetVsyncTimers ) - { - UpdateVSyncRate(); - frameLimitReset(); - m_resetVsyncTimers = false; - } -} - // Called by the VsyncInThread() if a valid keyEvent is pending and is unhandled by other // PS2 core plugins. void SysCoreThread::DispatchKeyEventToUI( const keyEvent& evt ) @@ -315,10 +355,11 @@ void SysCoreThread::StateCheckInThread() { GetMTGS().RethrowException(); _parent::StateCheckInThread(); + if( !m_hasValidState ) throw Exception::RuntimeError( "Invalid emulation state detected; Virtual machine threads have been cancelled." ); - _reset_stuff_as_needed(); + _reset_stuff_as_needed(); // kinda redundant but could catch unexpected threaded state changes... } void SysCoreThread::ExecuteTaskInThread() @@ -330,9 +371,10 @@ void SysCoreThread::ExecuteTaskInThread() m_mxcsr_saved.bitmask = _mm_getcsr(); PCSX2_PAGEFAULT_PROTECT { - StateCheckInThread(); - SetCPUState( EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVUMXCSR ); - Cpu->Execute(); + do { + StateCheckInThread(); + Cpu->Execute(); + } while( true ); } PCSX2_PAGEFAULT_EXCEPT; } @@ -347,8 +389,7 @@ void SysCoreThread::OnResumeInThread( bool isSuspended ) if( g_plugins != NULL ) g_plugins->Open(); - if( isSuspended ) - CpuInitializeMess(); + CpuInitializeMess(); } diff --git a/pcsx2/System/SysThreadBase.cpp b/pcsx2/System/SysThreadBase.cpp index 2e3a70db96..27497fd8f9 100644 --- a/pcsx2/System/SysThreadBase.cpp +++ b/pcsx2/System/SysThreadBase.cpp @@ -36,22 +36,9 @@ SysThreadBase::~SysThreadBase() throw() void SysThreadBase::Start() { _parent::Start(); - m_ExecMode = ExecMode_Closing; Sleep( 1 ); - if( !m_ResumeEvent.WaitWithoutYield( wxTimeSpan(0, 0, 1, 500) ) ) - { - RethrowException(); - if( pxAssertDev( m_ExecMode == ExecMode_Closing, "Unexpected thread status during SysThread startup." ) ) - { - throw Exception::ThreadCreationError( *this, - L"Timeout occurred while attempting to start the '%s' thread.", - wxEmptyString - ); - } - } - pxAssertDev( (m_ExecMode == ExecMode_Closing) || (m_ExecMode == ExecMode_Closed), "Unexpected thread status during SysThread startup." ); @@ -118,7 +105,7 @@ bool SysThreadBase::Suspend( bool isBlocking ) break; } - pxAssertDev( m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing..." ); + pxAssumeDev( m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing..." ); m_sem_event.Post(); } @@ -164,7 +151,7 @@ bool SysThreadBase::Pause() retval = true; } - pxAssertDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." ); + pxAssumeDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." ); m_sem_event.Post(); } @@ -247,7 +234,7 @@ void SysThreadBase::OnStartInThread() { m_RunningLock.Acquire(); _parent::OnStartInThread(); - m_ResumeEvent.Post(); + m_ExecMode = ExecMode_Closing; } void SysThreadBase::OnCleanupInThread() diff --git a/pcsx2/System/SysThreads.h b/pcsx2/System/SysThreads.h index 3cdd1947c7..1e625e82c0 100644 --- a/pcsx2/System/SysThreads.h +++ b/pcsx2/System/SysThreads.h @@ -20,22 +20,6 @@ using namespace Threading; - -#if !PCSX2_SEH -# include <setjmp.h> - - // Platforms without SEH need to use SetJmp / LongJmp to deal with exiting the recompiled - // code execution pipelines in an efficient manner, since standard C++ exceptions cannot - // unwind across dynamically recompiled code. - - enum - { - SetJmp_Dispatcher = 1, - SetJmp_Exit, - }; - -#endif - // -------------------------------------------------------------------------------------- // ISysThread // -------------------------------------------------------------------------------------- @@ -208,8 +192,10 @@ public: virtual void ApplySettings( const Pcsx2Config& src ); virtual void OnResumeReady(); virtual void Reset(); + virtual void RecoverState(); virtual void Cancel( bool isBlocking=true ); - + virtual bool Cancel( const wxTimeSpan& timeout ); + bool HasValidState() { return m_hasValidState; diff --git a/pcsx2/VU0micro.cpp b/pcsx2/VU0micro.cpp index 5be613d9cf..189e9d18bb 100644 --- a/pcsx2/VU0micro.cpp +++ b/pcsx2/VU0micro.cpp @@ -65,14 +65,12 @@ void __fastcall vu0ExecMicro(u32 addr) { } void VU0unknown() { - assert(0); - + pxFailDev("Unknown VU micromode opcode called"); CPU_LOG("Unknown VU micromode opcode called"); } void VU0regsunknown(_VURegsNum *VUregsn) { - assert(0); - + pxFailDev("Unknown VU micromode opcode called"); CPU_LOG("Unknown VU micromode opcode called"); } diff --git a/pcsx2/VUmicro.h b/pcsx2/VUmicro.h index ab348a895c..1a81b9f85d 100644 --- a/pcsx2/VUmicro.h +++ b/pcsx2/VUmicro.h @@ -39,7 +39,7 @@ public: // this boolean indicates to some generic logging facilities if the VU's registers // are valid for logging or not. (see DisVU1Micro.cpp, etc) [kinda hacky, might // be removed in the future] - bool IsInterpreter; + bool IsInterpreter; public: BaseCpuProvider() @@ -105,6 +105,7 @@ public: protected: BaseVUmicroCPU() {} + virtual ~BaseVUmicroCPU() throw() {} }; @@ -115,6 +116,7 @@ class InterpVU0 : public BaseVUmicroCPU { public: InterpVU0(); + virtual ~InterpVU0() throw() { Shutdown(); } const char* GetShortName() const { return "intVU0"; } wxString GetLongName() const { return L"VU0 Interpreter"; } @@ -132,6 +134,7 @@ class InterpVU1 : public BaseVUmicroCPU { public: InterpVU1(); + virtual ~InterpVU1() throw() { Shutdown(); } const char* GetShortName() const { return "intVU1"; } wxString GetLongName() const { return L"VU1 Interpreter"; } @@ -152,6 +155,7 @@ class recMicroVU0 : public BaseVUmicroCPU { public: recMicroVU0(); + virtual ~recMicroVU0() throw() { Shutdown(); } const char* GetShortName() const { return "mVU0"; } wxString GetLongName() const { return L"microVU0 Recompiler"; } @@ -169,6 +173,7 @@ class recMicroVU1 : public BaseVUmicroCPU { public: recMicroVU1(); + virtual ~recMicroVU1() throw() { Shutdown(); } const char* GetShortName() const { return "mVU1"; } wxString GetLongName() const { return L"microVU1 Recompiler"; } diff --git a/pcsx2/Vif0Dma.cpp b/pcsx2/Vif0Dma.cpp index 5ff8c6d029..53c5316352 100644 --- a/pcsx2/Vif0Dma.cpp +++ b/pcsx2/Vif0Dma.cpp @@ -130,9 +130,9 @@ static int __fastcall Vif0TransSTRow(u32 *data) // STROW u32* pmem = &vif0Regs->r0 + (vif0.tag.addr << 2); u32* pmem2 = g_vifmask.Row0 + vif0.tag.addr; - pxAssert(vif0.tag.addr < 4); + pxAssume(vif0.tag.addr < 4); ret = min(4 - vif0.tag.addr, vif0.vifpacketsize); - pxAssert(ret > 0); + pxAssume(ret > 0); switch (ret) { @@ -752,7 +752,7 @@ void vif0Write32(u32 mem, u32 value) case VIF0_R1: case VIF0_R2: case VIF0_R3: - pxAssert((mem&0xf) == 0); + pxAssume((mem&0xf) == 0); g_vifmask.Row0[(mem>>4) & 3] = value; break; @@ -760,7 +760,7 @@ void vif0Write32(u32 mem, u32 value) case VIF0_C1: case VIF0_C2: case VIF0_C3: - pxAssert((mem&0xf) == 0); + pxAssume((mem&0xf) == 0); g_vifmask.Col0[(mem>>4) & 3] = value; break; diff --git a/pcsx2/Vif1Dma.cpp b/pcsx2/Vif1Dma.cpp index fff6ca0405..01231ca75e 100644 --- a/pcsx2/Vif1Dma.cpp +++ b/pcsx2/Vif1Dma.cpp @@ -112,7 +112,7 @@ static __forceinline void vif1mpgTransfer(u32 addr, u32 *data, int size) fwrite(data, 1, size*4, f); fclose(f); }*/ - pxAssert(VU1.Micro > 0); + pxAssume(VU1.Micro > 0); if (memcmp(VU1.Micro + addr, data, size << 2)) { CpuVU1->Clear(addr, size << 2); // Clear before writing! :/ @@ -148,9 +148,9 @@ static int __fastcall Vif1TransSTRow(u32 *data) // STROW u32* pmem = &vif1Regs->r0 + (vif1.tag.addr << 2); u32* pmem2 = g_vifmask.Row1 + vif1.tag.addr; - pxAssert(vif1.tag.addr < 4); + pxAssume(vif1.tag.addr < 4); ret = min(4 - vif1.tag.addr, vif1.vifpacketsize); - pxAssert(ret > 0); + pxAssume(ret > 0); switch (ret) { @@ -1143,7 +1143,7 @@ void vif1Write32(u32 mem, u32 value) case VIF1_R1: case VIF1_R2: case VIF1_R3: - pxAssert((mem&0xf) == 0); + pxAssume((mem&0xf) == 0); g_vifmask.Row1[(mem>>4) & 3] = value; break; @@ -1151,7 +1151,7 @@ void vif1Write32(u32 mem, u32 value) case VIF1_C1: case VIF1_C2: case VIF1_C3: - pxAssert((mem&0xf) == 0); + pxAssume((mem&0xf) == 0); g_vifmask.Col1[(mem>>4) & 3] = value; break; diff --git a/pcsx2/VifDma.cpp b/pcsx2/VifDma.cpp index 6a941e92aa..2ed529de1a 100644 --- a/pcsx2/VifDma.cpp +++ b/pcsx2/VifDma.cpp @@ -283,9 +283,7 @@ template<const u32 VIFdmanum> u32 VIFalign(u32 *data, vifCode *v, u32 size) vif = &vif1; vifRow = g_vifmask.Row1; } -#ifdef PCSX2_DEBUG - pxAssert(v->addr < memsize); -#endif + pxAssume(v->addr < memsize); dest = (u32*)(VU->Mem + v->addr); @@ -476,9 +474,7 @@ template<const u32 VIFdmanum> void VIFunpack(u32 *data, vifCode *v, u32 size) u32 tempsize = 0; const u32 memlimit = vif_size(VIFdmanum); -#ifdef PCSX2_DEBUG - u32 memsize = memlimit; -#endif + pxDebugCode( u32 memsize = memlimit ); _mm_prefetch((char*)data, _MM_HINT_NTA); @@ -489,9 +485,7 @@ template<const u32 VIFdmanum> void VIFunpack(u32 *data, vifCode *v, u32 size) vifMaskRegs = g_vif0Masks; vif = &vif0; vifRow = g_vifmask.Row0; -#ifdef PCSX2_DEBUG - pxAssert(v->addr < memsize); -#endif + pxDebugCode( pxAssume(v->addr < memsize) ); } else { @@ -501,9 +495,7 @@ template<const u32 VIFdmanum> void VIFunpack(u32 *data, vifCode *v, u32 size) vifMaskRegs = g_vif1Masks; vif = &vif1; vifRow = g_vifmask.Row1; -#ifdef PCSX2_DEBUG - pxAssert(v->addr < memsize); -#endif + pxDebugCode( pxAssume(v->addr < memsize) ); } dest = (u32*)(VU->Mem + v->addr); @@ -523,9 +515,7 @@ template<const u32 VIFdmanum> void VIFunpack(u32 *data, vifCode *v, u32 size) size <<= 2; -#ifdef PCSX2_DEBUG - memsize = size; -#endif + pxDebugCode( memsize = size ); if (vifRegs->cycle.cl >= vifRegs->cycle.wl) // skipping write { diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h index 3261c18c31..ded5d1bbb2 100644 --- a/pcsx2/gui/App.h +++ b/pcsx2/gui/App.h @@ -38,7 +38,6 @@ class AppCoreThread; #include "System.h" #include "System/SysThreads.h" - typedef void FnType_OnThreadComplete(const wxCommandEvent& evt); BEGIN_DECLARE_EVENT_TYPES() @@ -151,14 +150,6 @@ enum MenuIdentifiers MenuId_Config_ResetAll, }; -enum DialogIdentifiers -{ - DialogId_CoreSettings = 0x800, - DialogId_BiosSelector, - DialogId_LogOptions, - DialogId_About, -}; - enum AppEventType { // Maybe this will be expanded upon later..? @@ -331,9 +322,9 @@ struct MsgboxEventResult Semaphore WaitForMe; int result; - MsgboxEventResult() : - WaitForMe(), result( 0 ) + MsgboxEventResult() { + result = 0; } }; @@ -406,7 +397,7 @@ public: void PostPadKey( wxKeyEvent& evt ); void PostMenuAction( MenuIdentifiers menu_id ) const; - int ThreadedModalDialog( DialogIdentifiers dialogId ); + int IssueModalDialog( const wxString& dlgName ); bool PrepForExit( bool canCancel ); @@ -532,6 +523,7 @@ public: virtual bool Suspend( bool isBlocking=true ); virtual void Resume(); virtual void Reset(); + virtual void Cancel( bool isBlocking=true ); virtual void StateCheckInThread(); virtual void ApplySettings( const Pcsx2Config& src ); virtual void ChangeCdvdSource( CDVD_SourceType type ); @@ -610,6 +602,9 @@ public: virtual ~SaveSinglePluginHelper() throw(); }; + +extern pxDoAssertFnType AppDoAssert; + // -------------------------------------------------------------------------------------- // External App-related Globals and Shortcuts // -------------------------------------------------------------------------------------- diff --git a/pcsx2/gui/AppAssert.cpp b/pcsx2/gui/AppAssert.cpp index 5597c14471..dcfb867390 100644 --- a/pcsx2/gui/AppAssert.cpp +++ b/pcsx2/gui/AppAssert.cpp @@ -18,18 +18,27 @@ #include <wx/stackwalk.h> - -static wxString pxGetStackTrace() +static wxString pxGetStackTrace( const FnChar_t* calledFrom ) { wxString stackTrace; class StackDump : public wxStackWalker { protected: - wxString m_stackTrace; + wxString m_stackTrace; + wxString m_srcFuncName; + bool m_ignoreDone; + int m_skipped; public: - StackDump() { } + StackDump( const FnChar_t* src_function_name ) + { + if( src_function_name != NULL ) + m_srcFuncName = fromUTF8(src_function_name); + + m_ignoreDone = false; + m_skipped = 0; + } const wxString& GetStackTrace() const { return m_stackTrace; } @@ -38,92 +47,108 @@ static wxString pxGetStackTrace() { wxString name( frame.GetName() ); if( name.IsEmpty() ) + { name = wxsFormat( L"%p ", frame.GetAddress() ); + } + /*else if( m_srcFuncName.IsEmpty() || m_srcFuncName == name ) + { + // FIXME: This logic isn't reliable yet. + // It's possible for our debug information to not match the function names returned by + // __pxFUNCTION__ (might happen in linux a lot, and could happen in win32 due to + // inlining on Dev aserts). The better approach is a system the queues up all the + // stacktrace info in individual wxStrings, and does a two-pass check -- first pass + // for the function name and, if not found, a second pass that just skips the first + // few stack entries. + + // It's important we only walk the stack once because Linux (argh, always linux!) has + // a really god aweful slow stack walker. + + // I'm not doing it right now because I've worked on this mess enough for one week. --air - m_stackTrace += wxString::Format( L"[%02d] %-46s ", - wx_truncate_cast(int, frame.GetLevel()), name.c_str() + m_ignoreDone = true; + } + + if( !m_ignoreDone ) + { + m_skipped++; + return; + }*/ + + //wxString briefName; + wxString essenName; + + if( frame.HasSourceLocation() ) + { + wxFileName wxfn(frame.GetFileName()); + //briefName.Printf( L"(%s:%d)", wxfn.GetFullName().c_str(), frame.GetLine() ); + + wxfn.SetVolume( wxEmptyString ); + int count = wxfn.GetDirCount(); + for( int i=0; i<2; ++i ) + wxfn.RemoveDir(0); + + essenName.Printf( L"%s:%d", wxfn.GetFullPath().c_str(), frame.GetLine() ); + } + + m_stackTrace += wxString::Format( L"[%02d] %-44s %s\n", + frame.GetLevel()-m_skipped, + name.c_str(), + essenName.c_str() ); - - if ( frame.HasSourceLocation() ) - m_stackTrace += wxsFormat( L"%s:%d", frame.GetFileName().c_str(), frame.GetLine() ); - - m_stackTrace += L'\n'; } }; - // [TODO] : Replace this with a textbox dialog setup. - static const int maxLines = 20; - - StackDump dump; - dump.Walk(2, maxLines); // don't show OnAssert() call itself - stackTrace = dump.GetStackTrace(); - - const int count = stackTrace.Freq( L'\n' ); - for ( int i = 0; i < count - maxLines; i++ ) - stackTrace = stackTrace.BeforeLast( L'\n' ); - - return stackTrace; + StackDump dump( calledFrom ); + dump.Walk( 3 ); + return dump.GetStackTrace(); } -static __threadlocal bool _reentrant_lock = false; - #ifdef __WXDEBUG__ +static __threadlocal int _reentrant_lock = 0; + // This override of wx's implementation provides thread safe assertion message reporting. If we aren't // on the main gui thread then the assertion message box needs to be passed off to the main gui thread // via messages. void Pcsx2App::OnAssertFailure( const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg ) { - // Used to allow the user to suppress future assertions during this application's session. - static bool disableAsserts = false; - if( disableAsserts ) return; + // Re-entrant assertions are bad mojo -- trap immediately. + RecursionGuard guard( _reentrant_lock ); + if( guard.IsReentrant() ) wxTrap(); - if( _reentrant_lock ) + wxCharBuffer bleh( wxString(func).ToUTF8() ); + if( AppDoAssert( DiagnosticOrigin( file, line, bleh, cond ), msg ) ) { - // Re-entrant assertions are bad mojo -- trap immediately. wxTrap(); } +} - _reentrant_lock = true; +#endif - wxString dbgmsg; - dbgmsg.reserve( 2048 ); +bool AppDoAssert( const DiagnosticOrigin& origin, const wxChar *msg ) +{ + // Used to allow the user to suppress future assertions during this application's session. + static bool disableAsserts = false; + if( disableAsserts ) return false; - wxString message; - if( msg == NULL ) - message = cond; - else - message.Printf( L"%s (%s)", msg, cond ); - - // make life easier for people using VC++ IDE by using this format, which allows double-click - // response times from the Output window... - dbgmsg.Printf( L"%s(%d) : assertion failed%s%s: %s\n", file, line, - (func==NULL) ? wxEmptyString : L" in ", - (func==NULL) ? wxEmptyString : func, - message.c_str() - ); - - wxString trace( L"Call stack:\n" + pxGetStackTrace() ); + wxString trace( pxGetStackTrace(origin.function) ); + wxString dbgmsg( origin.ToString( msg ) ); wxMessageOutputDebug().Printf( dbgmsg ); + Console.Error( dbgmsg ); Console.WriteLn( trace ); - int retval = Msgbox::Assertion( dbgmsg, trace ); - - switch( retval ) - { - case wxID_YES: - wxTrap(); - break; - - case wxID_NO: break; - - case wxID_CANCEL: // ignores future assertions. - disableAsserts = true; - break; - } + wxString windowmsg( L"Assertion failed: " ); + if( msg != NULL ) + windowmsg += msg; + else if( origin.condition != NULL ) + windowmsg += origin.condition; - _reentrant_lock = false; + int retval = Msgbox::Assertion( windowmsg, dbgmsg + L"\nStacktrace:\n" + trace ); + + if( retval == wxID_YES ) return true; + if( retval == wxID_IGNORE ) disableAsserts = true; + + return false; } -#endif diff --git a/pcsx2/gui/AppConfig.cpp b/pcsx2/gui/AppConfig.cpp index 8d18c17e04..3c41be2310 100644 --- a/pcsx2/gui/AppConfig.cpp +++ b/pcsx2/gui/AppConfig.cpp @@ -312,7 +312,7 @@ AppConfig::AppConfig() , DeskTheme( L"default" ) { LanguageId = wxLANGUAGE_DEFAULT; - RecentFileCount = 6; + RecentIsoCount = 12; Listbook_ImageSize = 32; Toolbar_ImageSize = 24; Toolbar_ShowLabels = true; @@ -378,7 +378,7 @@ void AppConfig::LoadSaveRootItems( IniInterface& ini ) IniEntry( MainGuiPosition ); IniEntry( SettingsTabName ); ini.EnumEntry( L"LanguageId", LanguageId, NULL, defaults.LanguageId ); - IniEntry( RecentFileCount ); + IniEntry( RecentIsoCount ); IniEntry( DeskTheme ); IniEntry( Listbook_ImageSize ); IniEntry( Toolbar_ImageSize ); diff --git a/pcsx2/gui/AppConfig.h b/pcsx2/gui/AppConfig.h index 8eb272bbc2..7c6c385372 100644 --- a/pcsx2/gui/AppConfig.h +++ b/pcsx2/gui/AppConfig.h @@ -154,7 +154,7 @@ public: // Current language in use (correlates to a wxWidgets wxLANGUAGE specifier) wxLanguage LanguageId; - int RecentFileCount; // number of files displayed in the Recent Isos list. + int RecentIsoCount; // number of files displayed in the Recent Isos list. // String value describing the desktop theme to use for pcsk2 (icons and background images) // The theme name is used to look up files in the themes folder (relative to the executable). diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp index 1eaab5a48a..1855bff585 100644 --- a/pcsx2/gui/AppCoreThread.cpp +++ b/pcsx2/gui/AppCoreThread.cpp @@ -28,6 +28,16 @@ AppCoreThread::AppCoreThread() : SysCoreThread() AppCoreThread::~AppCoreThread() throw() { + AppCoreThread::Cancel(); +} + +void AppCoreThread::Cancel( bool isBlocking ) +{ + if( !_parent::Cancel( wxTimeSpan( 0,0,1,0 ) ) ) + { + // Possible deadlock! + throw Exception::ThreadTimedOut( this ); + } } void AppCoreThread::Reset() diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp index c5feea509a..5edb0bf61c 100644 --- a/pcsx2/gui/AppInit.cpp +++ b/pcsx2/gui/AppInit.cpp @@ -45,6 +45,32 @@ namespace Exception }; } +static void CpuCheckSSE2() +{ + if( x86caps.hasStreamingSIMD2Extensions ) return; + + // Only check once per process session: + static bool checked = false; + if( checked ) return; + checked = true; + + wxDialogWithHelpers exconf( NULL, _("PCSX2 - SSE2 Recommended"), wxVERTICAL ); + + exconf += exconf.Heading( pxE( ".Error:Startup:NoSSE2", + L"Warning: Your computer does not support SSE2, which is required by many PCSX2 recompilers and plugins. " + L"Your options will be limited and emulation will be *very* slow." ) + ); + + pxIssueConfirmation( exconf, MsgButtons().OK(), L"Error:Startup:NoSSE2" ); + + // Auto-disable anything that needs SSE2: + + g_Conf->EmuOptions.Cpu.Recompiler.EnableEE = false; + g_Conf->EmuOptions.Cpu.Recompiler.EnableVU0 = false; + g_Conf->EmuOptions.Cpu.Recompiler.EnableVU1 = false; +} + + void Pcsx2App::OpenWizardConsole() { if( !IsDebugBuild ) return; @@ -81,8 +107,7 @@ void Pcsx2App::ReadUserModeSettings() if (IOP_ENABLE_SIF_HACK == 1) { - wxDialogWithHelpers hackedVersion( NULL, wxID_ANY, _("It will devour your young! - PCSX2 Shub-Niggurath edition"), false ); - hackedVersion.SetIdealWidth( 575 ); + wxDialogWithHelpers hackedVersion( NULL, _("It will devour your young! - PCSX2 Shub-Niggurath edition"), wxVERTICAL ); hackedVersion.SetSizer( new wxBoxSizer( wxVERTICAL ) ); hackedVersion += new pxStaticText( &hackedVersion, @@ -92,8 +117,6 @@ void Pcsx2App::ReadUserModeSettings() ); hackedVersion += new wxButton( &hackedVersion, wxID_OK ) | pxSizerFlags::StdCenter(); - hackedVersion.Fit(); - hackedVersion.CentreOnScreen(); hackedVersion.ShowModal(); } @@ -101,8 +124,7 @@ void Pcsx2App::ReadUserModeSettings() { // Pre-Alpha Warning! Why didn't I think to add this sooner?! - wxDialogWithHelpers preAlpha( NULL, wxID_ANY, _("It might devour your kittens! - PCSX2 0.9.7 Pre-Alpha"), false ); - preAlpha.SetIdealWidth( 575 ); + wxDialogWithHelpers preAlpha( NULL, _("It might devour your kittens! - PCSX2 0.9.7 Pre-Alpha"), wxVERTICAL ); preAlpha.SetSizer( new wxBoxSizer( wxVERTICAL ) ); preAlpha += new pxStaticText( &preAlpha, @@ -113,8 +135,6 @@ void Pcsx2App::ReadUserModeSettings() ); preAlpha += new wxButton( &preAlpha, wxID_OK ) | pxSizerFlags::StdCenter(); - preAlpha.Fit(); - preAlpha.CentreOnScreen(); preAlpha.ShowModal(); // first time startup, so give the user the choice of user mode: @@ -243,6 +263,15 @@ typedef void (wxEvtHandler::*pxMessageBoxEventFunction)(pxMessageBoxEvent&); // ------------------------------------------------------------------------ bool Pcsx2App::OnInit() { +#define pxMessageBoxEventThing(func) \ + (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxMessageBoxEventFunction, &func ) + + Connect( pxEVT_MSGBOX, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) ); + Connect( pxEVT_ASSERTION, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) ); + Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) ); + + pxDoAssert = AppDoAssert; + g_Conf = new AppConfig(); EnableAllLogging(); @@ -253,12 +282,6 @@ bool Pcsx2App::OnInit() m_StderrRedirHandle = NewPipeRedir(stderr); wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() ); -#define pxMessageBoxEventThing(func) \ - (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxMessageBoxEventFunction, &func ) - - Connect( pxEVT_MSGBOX, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) ); - Connect( pxEVT_CallStackBox, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) ); - Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) ); Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) ); Connect( pxEVT_SysExecute, wxCommandEventHandler( Pcsx2App::OnSysExecute ) ); @@ -286,9 +309,21 @@ bool Pcsx2App::OnInit() m_Resources = new pxAppResources(); + cpudetectInit(); + + if( !x86caps.hasMultimediaExtensions ) + { + // Note: due to memcpy_fast, we need minimum MMX even for interpreters. This will + // hopefully change later once we have a dynamically recompiled memcpy. + Msgbox::Alert( _("PCSX2 requires cpu with MMX instruction to run. Press OK to close."), _("PCSX2 - MMX Required") ); + return false; + } + ReadUserModeSettings(); AppConfig_OnChangedSettingsFolder(); + CpuCheckSSE2(); + m_MainFrame = new MainEmuFrame( NULL, L"PCSX2" ); m_MainFrame->PushEventHandler( &GetRecentIsoList() ); @@ -307,7 +342,8 @@ bool Pcsx2App::OnInit() SetExitOnFrameDelete( true ); // but being explicit doesn't hurt... m_MainFrame->Show(); - SysDetect(); + SysLogMachineCaps(); + AppApplySettings(); #ifdef __WXMSW__ @@ -317,47 +353,72 @@ bool Pcsx2App::OnInit() m_CoreAllocs = new SysCoreAllocations(); + if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) ) { // HadSomeFailures only returns 'true' if an *enabled* cpu type fails to init. If // the user already has all interps configured, for example, then no point in // popping up this dialog. + + wxDialogWithHelpers exconf( NULL, _("PCSX2 Recompiler Error(s)"), wxVERTICAL ); - // TODO : This should be redone using the ExtensibleConfirmation, and a sub-window - // (static text or something with a vertical scrollbar). + exconf += 12; + exconf += exconf.Heading( pxE( ".Error:RecompilerInit", + L"Warning: Some of the configured PS2 recompilers failed to initialize and will not be available for this session:\n" ) + ); - wxString message( _("The following cpu recompilers failed to initialize and will not be available:\n\n") ); + wxTextCtrl* scrollableTextArea = new wxTextCtrl( + &exconf, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, + wxTE_READONLY | wxTE_MULTILINE | wxTE_WORDWRAP + ); + exconf += scrollableTextArea | pxSizerFlags::StdExpand(); + if( !m_CoreAllocs->IsRecAvailable_EE() ) { - message += L"\t* R5900 (EE)\n"; + scrollableTextArea->AppendText( L"* R5900 (EE)\n\n" ); + + g_Conf->EmuOptions.Recompiler.EnableEE = false; } if( !m_CoreAllocs->IsRecAvailable_IOP() ) { - message += L"\t* R3000A (IOP)\n"; + scrollableTextArea->AppendText( L"* R3000A (IOP)\n\n" ); + g_Conf->EmuOptions.Recompiler.EnableIOP = false; } if( !m_CoreAllocs->IsRecAvailable_MicroVU0() ) { - message += L"\t* microVU0\n"; + scrollableTextArea->AppendText( L"* microVU0\n\n" ); + g_Conf->EmuOptions.Recompiler.UseMicroVU0 = false; + g_Conf->EmuOptions.Recompiler.EnableVU0 = g_Conf->EmuOptions.Recompiler.EnableVU0 && m_CoreAllocs->IsRecAvailable_SuperVU0(); } if( !m_CoreAllocs->IsRecAvailable_MicroVU1() ) { - message += L"\t* microVU1\n"; + scrollableTextArea->AppendText( L"* microVU1\n\n" ); + g_Conf->EmuOptions.Recompiler.UseMicroVU1 = false; + g_Conf->EmuOptions.Recompiler.EnableVU1 = g_Conf->EmuOptions.Recompiler.EnableVU1 && m_CoreAllocs->IsRecAvailable_SuperVU1(); } if( !m_CoreAllocs->IsRecAvailable_SuperVU0() ) { - message += L"\t* SuperVU0\n"; + scrollableTextArea->AppendText( L"* SuperVU0\n\n" ); + g_Conf->EmuOptions.Recompiler.UseMicroVU0 = m_CoreAllocs->IsRecAvailable_MicroVU0(); + g_Conf->EmuOptions.Recompiler.EnableVU0 = g_Conf->EmuOptions.Recompiler.EnableVU0 && g_Conf->EmuOptions.Recompiler.UseMicroVU0; } if( !m_CoreAllocs->IsRecAvailable_SuperVU1() ) { - message += L"\t* SuperVU1\n"; + scrollableTextArea->AppendText( L"* SuperVU1\n\n" ); + g_Conf->EmuOptions.Recompiler.UseMicroVU1 = m_CoreAllocs->IsRecAvailable_MicroVU1(); + g_Conf->EmuOptions.Recompiler.EnableVU1 = g_Conf->EmuOptions.Recompiler.EnableVU1 && g_Conf->EmuOptions.Recompiler.UseMicroVU1; } + exconf += new ModalButtonPanel( &exconf, MsgButtons().OK() ) | pxSizerFlags::StdCenter(); + + exconf.ShowModal(); + // Failures can be SSE-related OR memory related. Should do per-cpu error reports instead... /*message += pxE( ".Popup Error:EmuCore:MemoryForRecs", @@ -367,8 +428,8 @@ bool Pcsx2App::OnInit() L"Interpreters can be very slow, so don't get too excited. Press OK to continue or CANCEL to close PCSX2." );*/ - if( !Msgbox::OkCancel( message, _("PCSX2 Initialization Error"), wxICON_ERROR ) ) - return false; + //if( !Msgbox::OkCancel( message, _("PCSX2 Initialization Error"), wxICON_ERROR ) ) + // return false; } LoadPluginsPassive( NULL ); @@ -408,13 +469,16 @@ void Pcsx2App::CleanupMess() if( m_CorePlugins ) m_CorePlugins->Shutdown(); } + catch( Exception::ThreadTimedOut& ) { throw; } + catch( Exception::CancelEvent& ) { throw; } catch( Exception::RuntimeError& ex ) { // Handle runtime errors gracefully during shutdown. Mostly these are things // that we just don't care about by now, and just want to "get 'er done!" so // we can exit the app. ;) - Console.Error( ex.FormatDiagnosticMessage() ); + Console.Error( L"Runtime exception handled during CleanupMess:\n" ); + Console.Indent().Error( ex.FormatDiagnosticMessage() ); } // Notice: deleting the plugin manager (unloading plugins) here causes Lilypad to crash, @@ -427,6 +491,8 @@ void Pcsx2App::CleanupMess() while( wxGetLocale() != NULL ) delete wxGetLocale(); + + pxDoAssert = pxAssertImpl_LogIt; } Pcsx2App::Pcsx2App() @@ -441,6 +507,8 @@ Pcsx2App::Pcsx2App() Pcsx2App::~Pcsx2App() { + pxDoAssert = pxAssertImpl_LogIt; + // Typically OnExit cleans everything up before we get here, *unless* we cancel // out of program startup in OnInit (return false) -- then remaining cleanup needs // to happen here in the destructor. @@ -485,6 +553,6 @@ struct CrtDebugBreak } }; -//CrtDebugBreak breakAt( 4327 ); +//CrtDebugBreak breakAt( 1175 ); #endif \ No newline at end of file diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index 1440b4cc68..db5a82ae51 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -53,7 +53,7 @@ ConfigOverrides OverrideOptions; static bool HandlePluginError( Exception::PluginError& ex ) { - if( pxDialogExists( DialogId_CoreSettings ) ) return true; + if( pxDialogExists( L"CoreSettings" ) ) return true; bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() + _("\n\nPress Ok to go to the Plugin Configuration Panel.") @@ -64,7 +64,7 @@ static bool HandlePluginError( Exception::PluginError& ex ) g_Conf->SettingsTabName = L"Plugins"; // fixme: Send a message to the panel to select the failed plugin. - if( Dialogs::ConfigurationDialog().ShowModal() == wxID_CANCEL ) + if( wxGetApp().IssueModalDialog( Dialogs::ConfigurationDialog::GetNameStatic() ) == wxID_CANCEL ) return false; } return result; @@ -101,18 +101,6 @@ void Pcsx2App::PostPadKey( wxKeyEvent& evt ) } } -int Pcsx2App::ThreadedModalDialog( DialogIdentifiers dialogId ) -{ - AffinityAssert_AllowFromMain(); - - MsgboxEventResult result; - wxCommandEvent joe( pxEVT_OpenModalDialog, dialogId ); - joe.SetClientData( &result ); - AddPendingEvent( joe ); - result.WaitForMe.WaitNoCancel(); - return result.result; -} - // ---------------------------------------------------------------------------- // Pcsx2App Event Handlers // ---------------------------------------------------------------------------- @@ -130,54 +118,69 @@ void Pcsx2App::OnCoreThreadStatus( wxCommandEvent& evt ) void Pcsx2App::OnOpenModalDialog( wxCommandEvent& evt ) { - using namespace Dialogs; + pxAssertDev( !evt.GetString().IsEmpty(), wxNullChar ); - MsgboxEventResult& evtres( *((MsgboxEventResult*)evt.GetClientData()) ); - switch( evt.GetId() ) + MsgboxEventResult* evtres = (MsgboxEventResult*)evt.GetClientData(); + + wxWindowID result = IssueModalDialog( evt.GetString() ); + + if( evtres != NULL ) { - case DialogId_CoreSettings: - { - static int _guard = 0; - RecursionGuard guard( _guard ); - if( guard.IsReentrant() ) return; - evtres.result = ConfigurationDialog().ShowModal(); - } - break; - - case DialogId_BiosSelector: - { - static int _guard = 0; - RecursionGuard guard( _guard ); - if( guard.IsReentrant() ) return; - evtres.result = BiosSelectorDialog().ShowModal(); - } - break; - - case DialogId_LogOptions: - { - static int _guard = 0; - RecursionGuard guard( _guard ); - if( guard.IsReentrant() ) return; - evtres.result = LogOptionsDialog().ShowModal(); - } - break; - - case DialogId_About: - { - static int _guard = 0; - RecursionGuard guard( _guard ); - if( guard.IsReentrant() ) return; - evtres.result = AboutBoxDialog().ShowModal(); - } - break; + evtres->result = result; + evtres->WaitForMe.Post(); } - - evtres.WaitForMe.Post(); } -void Pcsx2App::OnMessageBox( pxMessageBoxEvent& evt ) +int Pcsx2App::IssueModalDialog( const wxString& dlgName ) { - Msgbox::OnEvent( evt ); + if( dlgName.IsEmpty() ) return wxID_CANCEL; + + if( !wxThread::IsMain() ) + { + MsgboxEventResult result; + wxCommandEvent joe( pxEVT_OpenModalDialog ); + joe.SetString( dlgName ); + joe.SetClientData( &result ); + AddPendingEvent( joe ); + result.WaitForMe.WaitNoCancel(); + return result.result; + } + + if( wxWindow* window = wxFindWindowByName( dlgName ) ) + { + if( wxIsKindOf( window, wxDialog ) ) + { + wxDialog* dialog = (wxDialog*)window; + + window->SetFocus(); + + // It's legal to call ShowModal on a non-modal dialog, therefore making + // it modal in nature for the needs of whatever other thread of action wants + // to block against it: + + if( !dialog->IsModal() ) + { + int result = dialog->ShowModal(); + dialog->Destroy(); + return result; + } + } + } + else + { + using namespace Dialogs; + + if( dlgName == ConfigurationDialog::GetNameStatic() ) + return ConfigurationDialog().ShowModal(); + if( dlgName == BiosSelectorDialog::GetNameStatic() ) + return BiosSelectorDialog().ShowModal(); + if( dlgName == LogOptionsDialog::GetNameStatic() ) + return LogOptionsDialog().ShowModal(); + if( dlgName == AboutBoxDialog::GetNameStatic() ) + return AboutBoxDialog().ShowModal(); + } + + return wxID_CANCEL; } HashTools::HashMap<int, const GlobalCommandDescriptor*> GlobalAccels( 0, 0xffffffff ); @@ -227,15 +230,14 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent& // ---------------------------------------------------------------------------- catch( Exception::BiosLoadFailed& ex ) { - bool result = Dialogs::ExtensibleConfirmation( NULL, ConfButtons().OK().Cancel(), - L"PS2 BIOS Error", - ex.FormatDisplayMessage() + BIOS_GetMsg_Required() + _("\nPress Ok to go to the BIOS Configuration Panel.") - ).ShowModal() != wxID_CANCEL; - - if( !result ) + wxDialogWithHelpers dialog( NULL, _("PS2 BIOS Error"), wxVERTICAL ); + dialog += dialog.Heading( ex.FormatDisplayMessage() + BIOS_GetMsg_Required() + _("\nPress Ok to go to the BIOS Configuration Panel.") ); + dialog += new ModalButtonPanel( &dialog, MsgButtons().OKCancel() ); + + if( dialog.ShowModal() == wxID_CANCEL ) Console.Warning( "User denied option to re-configure BIOS." ); - if( Dialogs::BiosSelectorDialog().ShowModal() != wxID_CANCEL ) + if( IssueModalDialog( Dialogs::BiosSelectorDialog::GetNameStatic() ) != wxID_CANCEL ) { SysExecute(); } @@ -268,15 +270,22 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent& // ---------------------------------------------------------------------------- catch( Exception::ThreadTimedOut& ex ) { + // [TODO] Bind a listener to the CoreThread status, and automatically close the dialog + // if the thread starts responding while we're waiting (not hard in fact, but I'm getting + // a little tired, so maybe later!) --air + Console.Warning( ex.FormatDiagnosticMessage() ); - int result = Dialogs::ExtensibleConfirmation( NULL, ConfButtons().Ignore().Cancel().Custom( _("Terminate") ), - _("PCSX2 Unresponsive Thread"), ex.FormatDisplayMessage() + L"\n\n" + + wxDialogWithHelpers dialog( NULL, _("PCSX2 Unresponsive Thread"), wxVERTICAL ); + + dialog += dialog.Heading( ex.FormatDisplayMessage() + L"\n\n" + pxE( ".Popup Error:Thread Deadlock Actions", L"'Ignore' to continue waiting for the thread to respond.\n" L"'Cancel' to attempt to cancel the thread.\n" L"'Terminate' to quit PCSX2 immediately.\n" ) - ).ShowModal(); + ); + + int result = pxIssueConfirmation( dialog, MsgButtons().Ignore().Cancel().Custom( _("Terminate") ) ); if( result == pxID_CUSTOM ) { @@ -386,14 +395,14 @@ int Pcsx2App::OnExit() // is a matter of programmer preference). MainEmuFrame& Pcsx2App::GetMainFrame() const { - pxAssert( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) ); - pxAssert( m_MainFrame != NULL ); + pxAssume( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) ); + pxAssume( m_MainFrame != NULL ); return *m_MainFrame; } GSFrame& Pcsx2App::GetGSFrame() const { - pxAssert( m_gsFrame != NULL ); + pxAssume( m_gsFrame != NULL ); return *m_gsFrame; } @@ -594,6 +603,8 @@ void Pcsx2App::OnSysExecute( wxCommandEvent& evt ) // Full system reset stops the core thread and unloads all core plugins *completely*. void Pcsx2App::SysReset() { + StateCopy_Clear(); + CoreThread.Reset(); CoreThread.ReleaseResumeLock(); m_CorePlugins = NULL; diff --git a/pcsx2/gui/ConsoleLogger.cpp b/pcsx2/gui/ConsoleLogger.cpp index d49e14d87a..8b181725c3 100644 --- a/pcsx2/gui/ConsoleLogger.cpp +++ b/pcsx2/gui/ConsoleLogger.cpp @@ -176,8 +176,15 @@ ConsoleLogFrame::ColorArray::~ColorArray() void ConsoleLogFrame::ColorArray::Create( int fontsize ) { - const wxFont fixed( fontsize, wxMODERN, wxNORMAL, wxNORMAL ); - const wxFont fixedB( fontsize, wxMODERN, wxNORMAL, wxBOLD ); + // pxGetFixedFont selects Andale Mono on Win32, which is nice visually but + // unfortunately has inconsistently spaced bold versions, so it's not good + // for our console. + + const wxFont fixed( pxGetFixedFont( fontsize ) ); + const wxFont fixedB( pxGetFixedFont( fontsize+1, wxBOLD ) ); + + //const wxFont fixed( fontsize, wxMODERN, wxNORMAL, wxNORMAL ); + //const wxFont fixedB( fontsize, wxMODERN, wxNORMAL, wxBOLD ); // Standard R, G, B format: new (&m_table[Color_Default]) wxTextAttr( wxColor( 0, 0, 0 ), wxNullColour, fixed ); @@ -262,6 +269,10 @@ ConsoleLogFrame::ConsoleLogFrame( MainEmuFrame *parent, const wxString& title, A m_TextCtrl.SetBackgroundColour( wxColor( 230, 235, 242 ) ); m_TextCtrl.SetDefaultStyle( m_ColorTable[DefaultConsoleColor] ); + // SetDefaultStyle only sets the style of text in the control. We need to + // also set the font of the control, so that sizing logic knows what font we use: + m_TextCtrl.SetFont( m_ColorTable[DefaultConsoleColor].GetFont() ); + wxMenu& menuLog (*new wxMenu()); wxMenu& menuAppear (*new wxMenu()); wxMenu& menuSources (*new wxMenu()); diff --git a/pcsx2/gui/ConsoleLogger.h b/pcsx2/gui/ConsoleLogger.h index 5e8a827539..586826948d 100644 --- a/pcsx2/gui/ConsoleLogger.h +++ b/pcsx2/gui/ConsoleLogger.h @@ -96,10 +96,10 @@ protected: EventListenerBinding<PluginEventType> m_Listener_CorePluginStatus; #ifdef __WXMSW__ - int m_win32_StupidRefreshTricks; int m_win32_LinesPerPage; int m_win32_LinesPerScroll; #endif + bool m_IsPaused; bool m_FreezeWrites; public: diff --git a/pcsx2/gui/Dialogs/AboutBoxDialog.cpp b/pcsx2/gui/Dialogs/AboutBoxDialog.cpp index b79dce20d0..c050e48e92 100644 --- a/pcsx2/gui/Dialogs/AboutBoxDialog.cpp +++ b/pcsx2/gui/Dialogs/AboutBoxDialog.cpp @@ -25,6 +25,8 @@ #include <wx/mstream.h> #include <wx/hyperlink.h> +using namespace pxSizerFlags; + namespace Dialogs { // Helper class for creating wxStaticText labels which are aligned to center. @@ -45,11 +47,14 @@ namespace Dialogs // AboutBoxDialog Implementation // -------------------------------------------------------------------------------------- -Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ): - wxDialogWithHelpers( parent, id, _("About PCSX2"), false ), - m_bitmap_dualshock( this, wxID_ANY, wxBitmap( EmbeddedImage<res_Dualshock>().Get() ), - wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN ) +Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent ) + : wxDialogWithHelpers( parent, _("About PCSX2"), wxVERTICAL ) + , m_bitmap_dualshock( this, wxID_ANY, wxBitmap( EmbeddedImage<res_Dualshock>().Get() ), + wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN + ) { + SetName( GetNameStatic() ); + static const wxString LabelAuthors = fromUTF8( "Developers" "\n\n" @@ -80,10 +85,6 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ): "F|RES, MrBrown, razorblade, Seta-san, Skarmeth, feal87" ); - SetSizer( new wxBoxSizer( wxVERTICAL ) ); - - *this += Text(_("PCSX2 - Playstation 2 Emulator")); - // This sizer holds text of the authors and a logo! wxBoxSizer& AuthLogoSizer = *new wxBoxSizer( wxHORIZONTAL ); @@ -99,18 +100,21 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ): label_auth->Wrap( 340 ); label_greets->Wrap( 200 ); - aboutUs.Add( label_auth, pxSizerFlags::StdExpand() ); - contribs.Add( label_greets, pxSizerFlags::StdExpand() ); + aboutUs += label_auth | StdExpand(); + contribs += label_greets | StdExpand(); - AuthLogoSizer.Add( &aboutUs ); - AuthLogoSizer.AddSpacer( 7 ); - AuthLogoSizer.Add( &contribs ); + AuthLogoSizer += aboutUs; + AuthLogoSizer += 7; + AuthLogoSizer += contribs; ContribSizer.AddStretchSpacer( 1 ); - ContribSizer.Add( &m_bitmap_dualshock, pxSizerFlags::StdSpace() ); + ContribSizer += m_bitmap_dualshock | StdSpace(); ContribSizer.AddStretchSpacer( 1 ); - *this += AuthLogoSizer | pxSizerFlags::StdSpace(); + // Main (top-level) layout + + *this += Text(_("PCSX2 - Playstation 2 Emulator")); + *this += AuthLogoSizer | StdSpace(); *this += new wxHyperlinkCtrl( this, wxID_ANY, _("Pcsx2 Official Website and Forums"), L"http://www.pcsx2.net" @@ -120,10 +124,6 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ): _("Pcsx2 Official Svn Repository at Googlecode"), L"http://code.google.com/p/pcsx2" ) | wxSizerFlags(1).Center().Border( wxALL, 3 ); - *this += ContribSizer | pxSizerFlags::StdExpand(); - - *this += new wxButton( this, wxID_OK, L"I've seen enough") | pxSizerFlags::StdCenter(); - - Fit(); - CenterOnScreen(); + *this += ContribSizer | StdExpand(); + *this += new wxButton( this, wxID_OK, L"I've seen enough") | StdCenter(); } diff --git a/pcsx2/gui/Dialogs/AssertionDialog.cpp b/pcsx2/gui/Dialogs/AssertionDialog.cpp new file mode 100644 index 0000000000..7517bb6101 --- /dev/null +++ b/pcsx2/gui/Dialogs/AssertionDialog.cpp @@ -0,0 +1,67 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see <http://www.gnu.org/licenses/>. + */ + +#include "PrecompiledHeader.h" +#include "App.h" +#include "Dialogs/ModalPopups.h" + +using namespace pxSizerFlags; + +Dialogs::AssertionDialog::AssertionDialog( const wxString& text, const wxString& stacktrace ) + : wxDialogWithHelpers( NULL, _("PCSX2 Assertion Failure"), false, !stacktrace.IsEmpty() ) +{ + m_idealWidth = 720; + + wxFlexGridSizer* flexgrid = new wxFlexGridSizer( 1 ); + flexgrid->AddGrowableCol( 0 ); + SetSizer( flexgrid ); + + wxTextCtrl* traceArea = NULL; + + if( !stacktrace.IsEmpty() ) + { + flexgrid->AddGrowableRow( 1 ); + + traceArea = new wxTextCtrl( + this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, + wxTE_READONLY | wxTE_MULTILINE | wxTE_RICH2 | wxHSCROLL + ); + + traceArea->SetDefaultStyle( wxTextAttr( wxNullColour, wxNullColour, pxGetFixedFont() ) ); + traceArea->SetFont( pxGetFixedFont() ); + + int fonty; + traceArea->GetTextExtent( L"blaH yeah", NULL, &fonty ); + + traceArea->WriteText( stacktrace ); + traceArea->SetMinSize( wxSize( GetIdealWidth()-24, (fonty+1)*18 ) ); + traceArea->ShowPosition(0); + } + + *this += Heading( text ); + + if( traceArea != NULL ) *this += traceArea | pxExpand.Border(wxTOP|wxLEFT|wxRIGHT,8); + + *this += Heading( + L"\nDo you want to stop the program [Yes/No]?" + L"\nOr press [Ignore] to suppress further assertions." + ); + + *this += new ModalButtonPanel( this, MsgButtons().YesNo().Ignore() ) | StdCenter(); + + if( wxWindow* idyes = FindWindowById( wxID_YES ) ) + idyes->SetFocus(); +} + diff --git a/pcsx2/gui/Dialogs/ConfigurationDialog.cpp b/pcsx2/gui/Dialogs/ConfigurationDialog.cpp index 1355dc7468..7abd217d59 100644 --- a/pcsx2/gui/Dialogs/ConfigurationDialog.cpp +++ b/pcsx2/gui/Dialogs/ConfigurationDialog.cpp @@ -55,10 +55,12 @@ void Dialogs::ConfigurationDialog::AddPage( const char* label, int iconid ) ( labelstr == g_Conf->SettingsTabName ), iconid ); } -Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent, int id ) - : wxDialogWithHelpers( parent, id, _("PCSX2 Configuration"), true ) +Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent ) + : wxDialogWithHelpers( parent, _("PCSX2 Configuration"), true ) , m_listbook( *new wxListbook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_orient ) ) { + SetName( GetNameStatic() ); + m_idealWidth = 600; m_listbook.SetImageList( &wxGetApp().GetImgList_Config() ); @@ -85,9 +87,6 @@ Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent, int id ) FindWindow( wxID_APPLY )->Disable(); - Fit(); - CenterOnScreen(); - #ifdef __WXMSW__ // Depending on Windows version and user appearance settings, the default icon spacing can be // way over generous. This little bit of Win32-specific code ensures proper icon spacing, scaled @@ -103,6 +102,8 @@ Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent, int id ) Connect( wxID_APPLY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigurationDialog::OnApply_Click ) ); Connect( wxID_SAVE, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigurationDialog::OnScreenshot_Click ) ); + Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler(ConfigurationDialog::OnCloseWindow) ); + // ---------------------------------------------------------------------------- // Bind a variety of standard "something probably changed" events. If the user invokes // any of these, we'll automatically de-gray the Apply button for this dialog box. :) @@ -126,6 +127,12 @@ Dialogs::ConfigurationDialog::~ConfigurationDialog() throw() g_ApplyState.DoCleanup(); } +void Dialogs::ConfigurationDialog::OnCloseWindow( wxCloseEvent& evt ) +{ + if( !IsModal() ) Destroy(); + evt.Skip(); +} + void Dialogs::ConfigurationDialog::OnOk_Click( wxCommandEvent& evt ) { if( g_ApplyState.ApplyAll() ) @@ -133,8 +140,6 @@ void Dialogs::ConfigurationDialog::OnOk_Click( wxCommandEvent& evt ) FindWindow( wxID_APPLY )->Disable(); g_Conf->SettingsTabName = m_labels[m_listbook.GetSelection()]; AppSaveSettings(); - - Close(); evt.Skip(); } } @@ -181,9 +186,11 @@ void Dialogs::ConfigurationDialog::OnScreenshot_Click( wxCommandEvent& evt ) } // ---------------------------------------------------------------------------- -Dialogs::BiosSelectorDialog::BiosSelectorDialog( wxWindow* parent, int id ) - : wxDialogWithHelpers( parent, id, _("BIOS Selector"), false ) +Dialogs::BiosSelectorDialog::BiosSelectorDialog( wxWindow* parent ) + : wxDialogWithHelpers( parent, _("BIOS Selector"), false ) { + SetName( GetNameStatic() ); + m_idealWidth = 500; wxBoxSizer& bleh( *new wxBoxSizer( wxVERTICAL ) ); diff --git a/pcsx2/gui/Dialogs/ConfigurationDialog.h b/pcsx2/gui/Dialogs/ConfigurationDialog.h index 82b21fe6d2..3b93469f83 100644 --- a/pcsx2/gui/Dialogs/ConfigurationDialog.h +++ b/pcsx2/gui/Dialogs/ConfigurationDialog.h @@ -33,7 +33,9 @@ namespace Dialogs public: virtual ~ConfigurationDialog() throw(); - ConfigurationDialog(wxWindow* parent=NULL, int id=DialogId_CoreSettings); + ConfigurationDialog(wxWindow* parent=NULL); + + static const wxChar* GetNameStatic() { return L"Dialog:CoreSettings"; } protected: template< typename T > @@ -43,6 +45,8 @@ namespace Dialogs void OnCancel_Click( wxCommandEvent& evt ); void OnApply_Click( wxCommandEvent& evt ); void OnScreenshot_Click( wxCommandEvent& evt ); + + void OnCloseWindow( wxCloseEvent& evt ); virtual void OnSomethingChanged( wxCommandEvent& evt ) { @@ -54,14 +58,15 @@ namespace Dialogs } }; - class BiosSelectorDialog : public wxDialogWithHelpers { protected: public: virtual ~BiosSelectorDialog() throw() {} - BiosSelectorDialog( wxWindow* parent=NULL, int id=DialogId_BiosSelector ); + BiosSelectorDialog( wxWindow* parent=NULL ); + + static const wxChar* GetNameStatic() { return L"Dialog:BiosSelector"; } protected: void OnOk_Click( wxCommandEvent& evt ); diff --git a/pcsx2/gui/Dialogs/ConfirmationDialogs.cpp b/pcsx2/gui/Dialogs/ConfirmationDialogs.cpp index 1f15bb26ee..bb6e4bfda2 100644 --- a/pcsx2/gui/Dialogs/ConfirmationDialogs.cpp +++ b/pcsx2/gui/Dialogs/ConfirmationDialogs.cpp @@ -20,7 +20,9 @@ #include "ModalPopups.h" #include "Utilities/StringHelpers.h" -bool ConfButtons::Allows( wxWindowID id ) const +using namespace pxSizerFlags; + +bool MsgButtons::Allows( wxWindowID id ) const { switch( id ) { @@ -63,7 +65,7 @@ static wxString ResultToString( int result ) return wxEmptyString; } -static wxWindowID ParseThatResult( const wxString& src, const ConfButtons& validTypes ) +static wxWindowID ParseThatResult( const wxString& src, const MsgButtons& validTypes ) { if( !pxAssert( !src.IsEmpty() ) ) return wxID_ANY; @@ -85,7 +87,53 @@ static wxWindowID ParseThatResult( const wxString& src, const ConfButtons& valid return wxID_ANY; } -wxWindowID Dialogs::IssueConfirmation( ExtensibleConfirmation& confirmDlg, const wxString& disablerKey ) +static bool pxTrySetFocus( wxWindow& parent, wxWindowID id ) +{ + if( wxWindow* found = parent.FindWindowById( id ) ) + { + found->SetFocus(); + return true; + } + + return false; +} + +static bool pxTrySetFocus( wxWindow* parent, wxWindowID id ) +{ + if( parent == NULL ) return false; + pxTrySetFocus( *parent, id ); +} + +void MsgButtons::SetBestFocus( wxWindow& dialog ) const +{ + if( HasOK() && pxTrySetFocus( dialog, wxID_OK ) ) return; + if( HasNo() && pxTrySetFocus( dialog, wxID_NO ) ) return; + if( HasClose() && pxTrySetFocus( dialog, wxID_CLOSE ) ) return; + if( HasRetry() && pxTrySetFocus( dialog, wxID_RETRY ) ) return; + + // Other confirmational types of buttons must be explicitly focused by the user or + // by an implementing dialog. We won't do it here implicitly because accidental + // "on focus" typed keys could invoke really unwanted actions. + + // (typically close/ok/retry/etc. aren't so bad that accidental clicking does terrible things) +} + +void MsgButtons::SetBestFocus( wxWindow* dialog ) const +{ + if( dialog == NULL ) return; + SetBestFocus( *dialog ); +} + + +wxWindowID pxIssueConfirmation( wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons ) +{ + confirmDlg += new ModalButtonPanel( &confirmDlg, buttons ) | pxCenter.Border( wxTOP, 8 ); + buttons.SetBestFocus( confirmDlg ); + return confirmDlg.ShowModal(); +} + + +wxWindowID pxIssueConfirmation( wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons, const wxString& disablerKey ) { wxConfigBase* cfg = GetAppConfig(); @@ -112,135 +160,143 @@ wxWindowID Dialogs::IssueConfirmation( ExtensibleConfirmation& confirmDlg, const result = split[0]; if( result == L"disabled" || result == L"off" || result == L"no" ) { - int result = ParseThatResult( split[1], confirmDlg.GetButtons() ); + int result = ParseThatResult( split[1], buttons ); if( result != wxID_ANY ) return result; } } } - if( cfg == NULL ) return confirmDlg.ShowModal(); - - // Add an option that allows the user to disable this popup from showing again. - // (and if the config hasn't been initialized yet, then assume the dialog as non-disablable) - - pxCheckBox& DisablerCtrl( *new pxCheckBox(&confirmDlg, _("Do not show this dialog again.")) ); - confirmDlg.GetExtensibleSizer().Add( &DisablerCtrl, wxSizerFlags().Centre() ); - - if( confirmDlg.GetButtons() != ConfButtons().OK() ) - pxSetToolTip(&DisablerCtrl, _("Disables this popup and whatever response you select here will be automatically used from now on.")); - else - pxSetToolTip(&DisablerCtrl, _("The popup will not be shown again. This setting can be undone from the settings panels.")); - - confirmDlg.Fit(); - - int modalResult = confirmDlg.ShowModal(); - - wxString cfgResult = ResultToString( modalResult ); - if( DisablerCtrl.IsChecked() && !cfgResult.IsEmpty() ) + pxCheckBox* DisablerCtrl = NULL; + if( cfg != NULL ) { - cfg->SetPath( L"/PopupDisablers" ); - cfg->Write( disablerKey, L"disabled," + cfgResult ); - cfg->SetPath( L"/" ); + // Add an option that allows the user to disable this popup from showing again. + // (and if the config hasn't been initialized yet, then assume the dialog as non-disablable) + + DisablerCtrl = new pxCheckBox(&confirmDlg, _("Do not show this dialog again.")); + + confirmDlg += 8; + confirmDlg += DisablerCtrl | wxSF.Centre(); + + if( buttons != MsgButtons().OK() ) + pxSetToolTip(DisablerCtrl, _("Disables this popup and whatever response you select here will be automatically used from now on.")); + else + pxSetToolTip(DisablerCtrl, _("The popup will not be shown again. This setting can be undone from the settings panels.")); + + } + + int modalResult = pxIssueConfirmation( confirmDlg, buttons ); + + if( cfg != NULL ) + { + wxString cfgResult = ResultToString( modalResult ); + if( DisablerCtrl->IsChecked() && !cfgResult.IsEmpty() ) + { + cfg->SetPath( L"/PopupDisablers" ); + cfg->Write( disablerKey, L"disabled," + cfgResult ); + cfg->SetPath( L"/" ); + cfg->Flush(); + } } return modalResult; } -Dialogs::ExtensibleConfirmation::ExtensibleConfirmation( wxWindow* parent, const ConfButtons& type, const wxString& title, const wxString& msg ) - : wxDialogWithHelpers( parent, wxID_ANY, title, false ) - , m_ExtensibleSizer( *new wxBoxSizer( wxVERTICAL ) ) - , m_ButtonSizer( *new wxBoxSizer( wxHORIZONTAL ) ) +ModalButtonPanel::ModalButtonPanel( wxWindow* parent, const MsgButtons& buttons ) + : wxPanelWithHelpers( parent, wxHORIZONTAL ) { - m_Buttons = type; - m_idealWidth = 500; - - SetSizer( new wxBoxSizer(wxVERTICAL) ); - // Populate the Button Sizer. // We prefer this over the built-in wxWidgets ButtonSizer stuff used for other types of // dialogs because we offer more button types, and we don't want the MSW default behavior // of right-justified buttons. - if( type.HasCustom() ) - AddCustomButton( pxID_CUSTOM, type.GetCustomLabel() ); - + if( buttons.HasCustom() ) + AddCustomButton( pxID_CUSTOM, buttons.GetCustomLabel() ); + // Order of wxID_RESET and custom button have been picked fairly arbitrarily, since there's // no standard governing those. - #ifdef __WXGTK__ +#ifdef __WXGTK__ // GTK+ / Linux inverts OK/CANCEL order -- cancel / no first, OK / Yes later. >_< - if( type.HasCancel() ) + if( buttons.HasCancel() ) AddActionButton( wxID_CANCEL ); - if( type.HasNo() ) + if( buttons.HasNo() ) { AddActionButton( wxID_NO ); - if( type.AllowsToAll() ) AddActionButton( wxID_NOTOALL ); + if( buttons.AllowsToAll() ) AddActionButton( wxID_NOTOALL ); } - if( type.HasOK() || type.HasYes() ) // Extra space between Affirm and Cancel Actions - m_ButtonSizer.Add(0, 0, 1, wxEXPAND, 0); - #endif - if( type.HasOK() ) + if( buttons.HasIgnore() ) + AddCustomButton( wxID_IGNORE, _("Ignore") ); + + if( buttons.HasOK() || buttons.HasYes() ) // Extra space between Affirm and Cancel Actions + GetSizer()->Add(0, 0, 1, wxEXPAND, 0); +#endif + + if( buttons.HasOK() ) AddActionButton( wxID_OK ); - if( type.HasYes() ) + if( buttons.HasYes() ) { AddActionButton( wxID_YES ); - if( type.AllowsToAll() ) + if( buttons.AllowsToAll() ) AddActionButton( wxID_YESTOALL ); } - if( type.HasReset() ) +#ifdef __WXGTK__ + if( buttons.HasRetry() ) + AddActionButton( wxID_RETRY ); + + if( buttons.HasAbort() ) + AddActionButton( wxID_ABORT ); +#else + if( buttons.HasAbort() ) + AddActionButton( wxID_ABORT ); + + if( buttons.HasRetry() ) + AddActionButton( wxID_RETRY ); +#endif + + if( buttons.HasReset() ) AddCustomButton( wxID_RESET, _("Reset") ); - if( type.HasClose() ) + if( buttons.HasClose() ) AddActionButton( wxID_CLOSE ); - #ifndef __WXGTK__ - if( type.HasNo() || type.HasCancel() ) // Extra space between Affirm and Cancel Actions - m_ButtonSizer.Add(0, 0, 1, wxEXPAND, 0); - - if( type.HasNo() ) +#ifndef __WXGTK__ + if( buttons.HasNo() ) { AddActionButton( wxID_NO ); - if( type.AllowsToAll() ) + if( buttons.AllowsToAll() ) AddActionButton( wxID_NOTOALL ); } - if( type.HasCancel() ) + if( buttons.HasIgnore() ) + AddCustomButton( wxID_IGNORE, _("Ignore") ); + + if( buttons.HasCancel() ) AddActionButton( wxID_CANCEL ); - #endif - - // -------------------------------- - // Finalize Sizers and Layout - // -------------------------------- - - // Add the message padded some (StdCenter gives us a 5 pt padding). Helps emphasize it a bit. - wxBoxSizer& msgPadSizer( *new wxBoxSizer(wxVERTICAL) ); - msgPadSizer += Heading( msg ); - - *this += msgPadSizer | pxSizerFlags::StdCenter(); - *this += m_ExtensibleSizer | pxCentre; - *this += m_ButtonSizer | pxSizerFlags::StdCenter(); - - Fit(); - CenterOnScreen(); +#endif } -void Dialogs::ExtensibleConfirmation::OnActionButtonClicked( wxCommandEvent& evt ) +void ModalButtonPanel::OnActionButtonClicked( wxCommandEvent& evt ) { - EndModal( evt.GetId() ); + evt.Skip(); + wxWindow* toplevel = wxGetTopLevelParent( this ); + if( toplevel != NULL && wxIsKindOf(toplevel, wxDialog) ) + ((wxDialog*)toplevel)->EndModal( evt.GetId() ); } -void Dialogs::ExtensibleConfirmation::AddCustomButton( wxWindowID id, const wxString& label ) +void ModalButtonPanel::AddCustomButton( wxWindowID id, const wxString& label ) { - m_ButtonSizer.Add( new wxButton( this, id, label ), pxSizerFlags::StdButton() )->SetProportion( 6 ); - Connect( id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ExtensibleConfirmation::OnActionButtonClicked ) ); + *this += new wxButton( this, id, label ) | StdButton().Proportion(6); + Connect( id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ModalButtonPanel::OnActionButtonClicked ) ); } -void Dialogs::ExtensibleConfirmation::AddActionButton( wxWindowID id ) +// This is for buttons that are defined internally by wxWidgets, such as wxID_CANCEL, wxID_ABORT, etc. +// wxWidgets will assign the labels and stuff for us. :D +void ModalButtonPanel::AddActionButton( wxWindowID id ) { - m_ButtonSizer.Add( new wxButton( this, id ), pxSizerFlags::StdButton() )->SetProportion( 6 ); - Connect( id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ExtensibleConfirmation::OnActionButtonClicked ) ); + *this += new wxButton( this, id ) | StdButton().Proportion(6); + Connect( id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ModalButtonPanel::OnActionButtonClicked ) ); } diff --git a/pcsx2/gui/Dialogs/FirstTimeWizard.cpp b/pcsx2/gui/Dialogs/FirstTimeWizard.cpp index 22704c082b..5c852abc3f 100644 --- a/pcsx2/gui/Dialogs/FirstTimeWizard.cpp +++ b/pcsx2/gui/Dialogs/FirstTimeWizard.cpp @@ -22,6 +22,7 @@ #include <wx/file.h> using namespace Panels; +using namespace pxSizerFlags; template< typename T > static T& MakeWizWidget( int pageid, wxWizardPage* src ) @@ -48,8 +49,6 @@ Panels::SettingsDirPickerPanel::SettingsDirPickerPanel( wxWindow* parent ) : ), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE ), wxSizerFlags().Expand().Border( wxBOTTOM, 6 ) ); - - //SetSizerAndFit( GetSizer(), false ); } // ---------------------------------------------------------------------------- @@ -58,24 +57,22 @@ FirstTimeWizard::UsermodePage::UsermodePage( wxWizard* parent ) : { SetSizer( new wxBoxSizer( wxVERTICAL ) ); - wxPanelWithHelpers* panel = new wxPanelWithHelpers( this, wxVERTICAL ); - panel->SetIdealWidth( 640 ); - wxSizer& s_panel( *panel->GetSizer() ); + wxPanelWithHelpers& panel( *new wxPanelWithHelpers( this, wxVERTICAL ) ); + panel.SetIdealWidth( 640 ); - m_dirpick_settings = new SettingsDirPickerPanel( panel ); - m_panel_LangSel = new LanguageSelectionPanel( panel ); - m_panel_UserSel = new UsermodeSelectionPanel( panel ); + m_dirpick_settings = new SettingsDirPickerPanel( &panel ); + m_panel_LangSel = new LanguageSelectionPanel( &panel ); + m_panel_UserSel = new UsermodeSelectionPanel( &panel ); - (new pxStaticHeading( panel, _("PCSX2 is starting from a new or unknown folder and needs to be configured.") )) - ->AddTo( s_panel ); + panel += panel.Heading(_("PCSX2 is starting from a new or unknown folder and needs to be configured.")); - s_panel.Add( m_panel_LangSel, pxSizerFlags::StdCenter() ); - s_panel.Add( m_panel_UserSel, wxSizerFlags().Expand().Border( wxALL, 8 ) ); + panel += m_panel_LangSel | StdCenter(); + panel += m_panel_UserSel | pxExpand.Border( wxALL, 8 ); - s_panel.AddSpacer( 6 ); - s_panel.Add( m_dirpick_settings, pxSizerFlags::SubGroup() ); + panel += 6; + panel += m_dirpick_settings | SubGroup(); - GetSizer()->Add( panel, wxSizerFlags().Expand() ); + panel += panel | pxExpand; Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler(FirstTimeWizard::UsermodePage::OnUsermodeChanged) ); } @@ -98,14 +95,13 @@ FirstTimeWizard::FirstTimeWizard( wxWindow* parent ) , m_panel_BiosSel ( MakeWizWidget<BiosSelectorPanel>( 2, &m_page_bios ) ) { // Page 2 - Plugins Panel - wxBoxSizer& pluginSizer( *new wxBoxSizer( wxVERTICAL ) ); - pluginSizer.Add( &m_panel_PluginSel, pxSizerFlags::StdExpand() ); - m_page_plugins.SetSizer( &pluginSizer ); - // Page 3 - Bios Panel - wxBoxSizer& biosSizer( *new wxBoxSizer( wxVERTICAL ) ); - biosSizer.Add( &m_panel_BiosSel, pxSizerFlags::StdExpand() ); - m_page_bios.SetSizer( &biosSizer ); + + m_page_plugins. SetSizer( new wxBoxSizer( wxVERTICAL ) ); + m_page_bios. SetSizer( new wxBoxSizer( wxVERTICAL ) ); + + m_page_plugins += m_panel_PluginSel | StdExpand(); + m_page_bios += m_panel_BiosSel | StdExpand(); // Assign page indexes as client data m_page_usermode.SetClientData ( (void*)0 ); @@ -117,8 +113,13 @@ FirstTimeWizard::FirstTimeWizard( wxWindow* parent ) m_page_usermode.SetNext ( &m_page_plugins ); m_page_plugins.SetNext ( &m_page_bios ); - GetPageAreaSizer()->Add( &m_page_usermode ); - GetPageAreaSizer()->Add( &m_page_plugins ); + GetPageAreaSizer() += m_page_usermode; + GetPageAreaSizer() += m_page_plugins; + + // this doesn't descent from wxDialogWithHelpers, so we need to explicitly + // fit and center it. :( + + Fit(); CenterOnScreen(); Connect( wxEVT_WIZARD_PAGE_CHANGED, wxWizardEventHandler( FirstTimeWizard::OnPageChanged ) ); diff --git a/pcsx2/gui/Dialogs/ImportSettingsDialog.cpp b/pcsx2/gui/Dialogs/ImportSettingsDialog.cpp index 569118a202..4f5557c661 100644 --- a/pcsx2/gui/Dialogs/ImportSettingsDialog.cpp +++ b/pcsx2/gui/Dialogs/ImportSettingsDialog.cpp @@ -18,8 +18,10 @@ #include "ModalPopups.h" +using namespace pxSizerFlags; + Dialogs::ImportSettingsDialog::ImportSettingsDialog( wxWindow* parent ) - : wxDialogWithHelpers( parent, wxID_ANY, _("Import Existing Settings?"), false ) + : wxDialogWithHelpers( parent, _("Import Existing Settings?"), wxVERTICAL ) { m_idealWidth = 440; @@ -28,7 +30,6 @@ Dialogs::ImportSettingsDialog::ImportSettingsDialog( wxWindow* parent ) L"Would you like to import these settings or overwrite them with PCSX2 default values?" L"\n\n(or press Cancel to select a different settings folder)" ) ); - heading->SetMinSize( wxSize( m_idealWidth-8, wxDefaultCoord ) ); wxBoxSizer& s_buttons = *new wxBoxSizer( wxHORIZONTAL ); wxButton* b_import = new wxButton( this, wxID_ANY, _("Import") ); @@ -37,19 +38,16 @@ Dialogs::ImportSettingsDialog::ImportSettingsDialog( wxWindow* parent ) // -------------------------------------------------------------------- // Layout Some Shizat... - wxBoxSizer& sizer( *new wxBoxSizer( wxVERTICAL ) ); + s_buttons += b_import | StdButton(); + s_buttons += 16; + s_buttons += b_over | StdButton(); + s_buttons += 16; + s_buttons += new wxButton( this, wxID_CANCEL ) | StdButton(); - s_buttons.Add( b_import,pxSizerFlags::StdButton() ); - s_buttons.AddSpacer( 16 ); - s_buttons.Add( b_over, pxSizerFlags::StdButton() ); - s_buttons.AddSpacer( 16 ); - s_buttons.Add( new wxButton( this, wxID_CANCEL ), pxSizerFlags::StdButton() ); - - sizer.AddSpacer( 4 ); - heading->AddTo( sizer ); - sizer.AddSpacer( 12 ); - sizer.Add( &s_buttons, pxSizerFlags::StdCenter() ); - SetSizerAndFit( &sizer ); + *this += 4; + *this += heading; + *this += 12; + *this += &s_buttons | StdCenter(); Connect( b_import->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ImportSettingsDialog::OnImport_Click) ); Connect( b_over->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ImportSettingsDialog::OnOverwrite_Click) ); diff --git a/pcsx2/gui/Dialogs/LogOptionsDialog.cpp b/pcsx2/gui/Dialogs/LogOptionsDialog.cpp index 0350813702..68c60b09a6 100644 --- a/pcsx2/gui/Dialogs/LogOptionsDialog.cpp +++ b/pcsx2/gui/Dialogs/LogOptionsDialog.cpp @@ -21,9 +21,11 @@ using namespace Panels; -Dialogs::LogOptionsDialog::LogOptionsDialog( wxWindow* parent, int id ) - : wxDialogWithHelpers( parent, id, _("High Volume Logging"), true ) +Dialogs::LogOptionsDialog::LogOptionsDialog( wxWindow* parent ) + : wxDialogWithHelpers( parent, _("Trace Logging"), true ) { + SetName( GetNameStatic() ); + m_idealWidth = 480; wxBoxSizer& mainsizer = *new wxBoxSizer( wxVERTICAL ); diff --git a/pcsx2/gui/Dialogs/LogOptionsDialog.h b/pcsx2/gui/Dialogs/LogOptionsDialog.h index e2ac7f8708..35f77ae71a 100644 --- a/pcsx2/gui/Dialogs/LogOptionsDialog.h +++ b/pcsx2/gui/Dialogs/LogOptionsDialog.h @@ -28,8 +28,11 @@ namespace Dialogs { class LogOptionsDialog: public wxDialogWithHelpers { public: - LogOptionsDialog( wxWindow* parent=NULL, int id=DialogId_LogOptions ); + LogOptionsDialog( wxWindow* parent=NULL ); + virtual ~LogOptionsDialog() throw() { } + static const wxChar* GetNameStatic() { return L"Dialog:TraceLogSettings"; } + protected: void OnOk_Click( wxCommandEvent& evt ); void OnApply_Click( wxCommandEvent& evt ); diff --git a/pcsx2/gui/Dialogs/ModalPopups.h b/pcsx2/gui/Dialogs/ModalPopups.h index 18e01251a5..0433852978 100644 --- a/pcsx2/gui/Dialogs/ModalPopups.h +++ b/pcsx2/gui/Dialogs/ModalPopups.h @@ -66,7 +66,7 @@ protected: virtual void OnDoubleClicked( wxCommandEvent& evt ); }; -class ConfButtons +class MsgButtons { protected: BITFIELD32() @@ -87,29 +87,29 @@ protected: wxString m_CustomLabel; public: - ConfButtons() : bitset( 0 ) { } + MsgButtons() : bitset( 0 ) { } - ConfButtons& OK() { m_OK = true; return *this; } - ConfButtons& Cancel() { m_Cancel = true; return *this; } - ConfButtons& Apply() { m_Apply = true; return *this; } - ConfButtons& Yes() { m_Yes = true; return *this; } - ConfButtons& No() { m_No = true; return *this; } - ConfButtons& ToAll() { m_AllowToAll = true; return *this; } + MsgButtons& OK() { m_OK = true; return *this; } + MsgButtons& Cancel() { m_Cancel = true; return *this; } + MsgButtons& Apply() { m_Apply = true; return *this; } + MsgButtons& Yes() { m_Yes = true; return *this; } + MsgButtons& No() { m_No = true; return *this; } + MsgButtons& ToAll() { m_AllowToAll = true; return *this; } - ConfButtons& Abort() { m_Abort = true; return *this; } - ConfButtons& Retry() { m_Retry = true; return *this; } - ConfButtons& Ignore() { m_Ignore = true; return *this; } - ConfButtons& Reset() { m_Reset = true; return *this; } - ConfButtons& Close() { m_Close = true; return *this; } + MsgButtons& Abort() { m_Abort = true; return *this; } + MsgButtons& Retry() { m_Retry = true; return *this; } + MsgButtons& Ignore() { m_Ignore = true; return *this; } + MsgButtons& Reset() { m_Reset = true; return *this; } + MsgButtons& Close() { m_Close = true; return *this; } - ConfButtons& Custom( const wxString& label) + MsgButtons& Custom( const wxString& label) { m_CustomLabel = label; return *this; } - ConfButtons& OKCancel() { m_OK = m_Cancel = true; return *this; } - ConfButtons& YesNo() { m_Yes = m_No = true; return *this; } + MsgButtons& OKCancel() { m_OK = m_Cancel = true; return *this; } + MsgButtons& YesNo() { m_Yes = m_No = true; return *this; } bool HasOK() const { return m_OK; } bool HasCancel() const { return m_Cancel; } @@ -128,29 +128,45 @@ public: const wxString& GetCustomLabel() const { return m_CustomLabel; } bool Allows( wxWindowID id ) const; + void SetBestFocus( wxWindow* dialog ) const; + void SetBestFocus( wxWindow& dialog ) const; - bool operator ==( const ConfButtons& right ) const + bool operator ==( const MsgButtons& right ) const { return OpEqu( bitset ); } - bool operator !=( const ConfButtons& right ) const + bool operator !=( const MsgButtons& right ) const { return !OpEqu( bitset ); } }; +class ModalButtonPanel : public wxPanelWithHelpers +{ +public: + ModalButtonPanel( wxWindow* window, const MsgButtons& buttons ); + virtual ~ModalButtonPanel() throw() { } + + virtual void AddActionButton( wxWindowID id ); + virtual void AddCustomButton( wxWindowID id, const wxString& label ); + + virtual void OnActionButtonClicked( wxCommandEvent& evt ); +}; + namespace Dialogs { class AboutBoxDialog: public wxDialogWithHelpers { - public: - AboutBoxDialog( wxWindow* parent=NULL, int id=DialogId_About ); - virtual ~AboutBoxDialog() throw() {} - protected: //wxStaticBitmap m_bitmap_logo; wxStaticBitmap m_bitmap_dualshock; + + public: + AboutBoxDialog( wxWindow* parent=NULL ); + virtual ~AboutBoxDialog() throw() {} + + static const wxChar* GetNameStatic() { return L"Dialog:AboutBox"; } }; @@ -161,7 +177,7 @@ namespace Dialogs Panels::LanguageSelectionPanel* m_panel_langsel; public: - PickUserModeDialog( wxWindow* parent, int id=wxID_ANY ); + PickUserModeDialog( wxWindow* parent ); virtual ~PickUserModeDialog() throw() {} protected: @@ -180,42 +196,15 @@ namespace Dialogs void OnOverwrite_Click( wxCommandEvent& evt ); }; - - // -------------------------------------------------------------------------------------- - // ExtensibleConfirmation Dialog - // -------------------------------------------------------------------------------------- - // Purpose: This dialog is a simple framework for providing common popups that have three - // main sections of content: - // - // [Upper Paragraph] - // [Optional Interior Content] - // [OK] [CANCEL] [ETC] - // - // The Upper Paragraph and buttons are specified in the constructor. The Interior Content - // can be added by fetching the ExtensibleSizer provided by th created dialog. Add your - // content, dance around a bit, call Fit(), and then ShowModal. :) - - class ExtensibleConfirmation : public wxDialogWithHelpers + class AssertionDialog : public wxDialogWithHelpers { - protected: - wxBoxSizer& m_ExtensibleSizer; - wxBoxSizer& m_ButtonSizer; - - ConfButtons m_Buttons; - public: - ExtensibleConfirmation( wxWindow* parent, const ConfButtons& type, const wxString& title, const wxString& msg ); - virtual ~ExtensibleConfirmation() throw() {} - - const ConfButtons& GetButtons() const { return m_Buttons; } - virtual wxBoxSizer& GetExtensibleSizer() const { return m_ExtensibleSizer; } - - protected: - virtual void AddActionButton( wxWindowID id ); - virtual void AddCustomButton( wxWindowID id, const wxString& label ); - virtual void OnActionButtonClicked( wxCommandEvent& evt ); + AssertionDialog( const wxString& text, const wxString& stacktrace ); + virtual ~AssertionDialog() throw() {} + + }; - - wxWindowID IssueConfirmation( ExtensibleConfirmation& confirmDlg, const wxString& disablerKey ); } +wxWindowID pxIssueConfirmation( wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons ); +wxWindowID pxIssueConfirmation( wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons, const wxString& disablerKey ); diff --git a/pcsx2/gui/Dialogs/PickUserModeDialog.cpp b/pcsx2/gui/Dialogs/PickUserModeDialog.cpp index 9172a78a6e..45ae7d88cb 100644 --- a/pcsx2/gui/Dialogs/PickUserModeDialog.cpp +++ b/pcsx2/gui/Dialogs/PickUserModeDialog.cpp @@ -20,21 +20,17 @@ using namespace Panels; -Dialogs::PickUserModeDialog::PickUserModeDialog( wxWindow* parent, int id ) - : wxDialogWithHelpers( parent, id, _("PCSX2 First Time configuration"), false ) +Dialogs::PickUserModeDialog::PickUserModeDialog( wxWindow* parent ) + : wxDialogWithHelpers( parent, _("PCSX2 First Time configuration"), wxVERTICAL ) { m_panel_usersel = new UsermodeSelectionPanel( this, false ); m_panel_langsel = new LanguageSelectionPanel( this ); - SetSizer( new wxBoxSizer( wxVERTICAL ) ); - *this += new pxStaticHeading( this, _("PCSX2 is starting from a new or unknown folder and needs to be configured.") ); *this += m_panel_langsel | pxSizerFlags::StdCenter(); *this += m_panel_usersel | wxSizerFlags().Expand().Border( wxALL, 8 ); AddOkCancel( *GetSizer() ); - Fit(); - CenterOnScreen(); Connect( wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PickUserModeDialog::OnOk_Click ) ); // TODO : Add a command event handler for language changes, that dynamically re-update contents of this window. diff --git a/pcsx2/gui/FrameForGS.cpp b/pcsx2/gui/FrameForGS.cpp index e4cce855b4..679aaef28b 100644 --- a/pcsx2/gui/FrameForGS.cpp +++ b/pcsx2/gui/FrameForGS.cpp @@ -49,8 +49,9 @@ GSPanel::GSPanel( wxWindow* parent ) , m_Listener_SettingsApplied( wxGetApp().Source_SettingsApplied(), EventListener<int> ( this, OnSettingsApplied ) ) , m_HideMouseTimer( this ) { - m_CursorShown = true; - + m_CursorShown = true; + m_HasFocus = false; + if ( !wxWindow::Create(parent, wxID_ANY) ) throw Exception::RuntimeError( "GSPanel constructor esplode!!" ); @@ -68,6 +69,9 @@ GSPanel::GSPanel( wxWindow* parent ) Connect( wxEVT_SIZE, wxSizeEventHandler (GSPanel::OnResize) ); Connect( wxEVT_KEY_DOWN, wxKeyEventHandler (GSPanel::OnKeyDown) ); + Connect( wxEVT_SET_FOCUS, wxFocusEventHandler(GSPanel::OnFocus) ); + Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler(GSPanel::OnFocusLost) ); + Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(GSPanel::OnShowMouse) ); Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(GSPanel::OnShowMouse) ); Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(GSPanel::OnShowMouse) ); @@ -131,6 +135,7 @@ void GSPanel::DoResize() void GSPanel::OnResize(wxSizeEvent& event) { + if( IsBeingDeleted() ) return; DoResize(); //Console.Error( "Size? %d x %d", GetSize().x, GetSize().y ); //event. @@ -144,12 +149,14 @@ void GSPanel::OnCloseWindow(wxCloseEvent& evt) void GSPanel::OnShowMouse( wxMouseEvent& evt ) { + if( IsBeingDeleted() ) return; evt.Skip(); DoShowMouse(); } void GSPanel::OnHideMouseTimeout( wxTimerEvent& evt ) { + if( IsBeingDeleted() || !m_HasFocus ) return; if( CoreThread.GetExecutionMode() != SysThreadBase::ExecMode_Opened ) return; SetCursor( wxCursor( wxCURSOR_BLANK ) ); @@ -180,6 +187,26 @@ void GSPanel::OnKeyDown( wxKeyEvent& evt ) } } +void GSPanel::OnFocus( wxFocusEvent& evt ) +{ + evt.Skip(); + m_HasFocus = true; + + if( g_Conf->GSWindow.AlwaysHideMouse ) + { + SetCursor( wxCursor(wxCURSOR_BLANK) ); + m_CursorShown = false; + } + else + DoShowMouse(); +} + +void GSPanel::OnFocusLost( wxFocusEvent& evt ) +{ + evt.Skip(); + m_HasFocus = false; + DoShowMouse(); +} void __evt_fastcall GSPanel::OnSettingsApplied( void* obj, int& evt ) { @@ -234,12 +261,18 @@ wxWindow* GSFrame::GetViewport() void GSFrame::OnActivate( wxActivateEvent& evt ) { + if( IsBeingDeleted() ) return; + evt.Skip(); if( wxWindow* gsPanel = FindWindowByName(L"GSPanel") ) gsPanel->SetFocus(); } void GSFrame::OnMove( wxMoveEvent& evt ) { + if( IsBeingDeleted() ) return; + + evt.Skip(); + // evt.GetPosition() returns the client area position, not the window frame position. if( !IsMaximized() && IsVisible() ) g_Conf->GSWindow.WindowPos = GetScreenPosition(); diff --git a/pcsx2/gui/IniInterface.cpp b/pcsx2/gui/IniInterface.cpp index 70ffce709b..6d32e03289 100644 --- a/pcsx2/gui/IniInterface.cpp +++ b/pcsx2/gui/IniInterface.cpp @@ -61,11 +61,7 @@ void IniInterface::Flush() IniScopedGroup::IniScopedGroup( IniInterface& mommy, const wxString& group ) : m_mom( mommy ) { - if( IsDevBuild ) - { - if( wxStringTokenize( group, L"/" ).Count() > 1 ) - throw Exception::InvalidArgument( "Cannot nest more than one group deep per instance of IniScopedGroup." ); - } + pxAssertDev( wxStringTokenize( group, L"/" ).Count() <= 1, L"Cannot nest more than one group deep per instance of IniScopedGroup." ); m_mom.SetPath( group ); } diff --git a/pcsx2/gui/IsoDropTarget.cpp b/pcsx2/gui/IsoDropTarget.cpp index 9e68b7a4a1..738ab89124 100644 --- a/pcsx2/gui/IsoDropTarget.cpp +++ b/pcsx2/gui/IsoDropTarget.cpp @@ -38,10 +38,9 @@ bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filen if( filenames.GetCount() > 1 ) { - Dialogs::ExtensibleConfirmation( m_WindowBound, ConfButtons().Cancel(), - _("Drag and Drop Error"), - _("It is an error to drop multiple files onto a PCSX2 window. One at a time please, thank you.") - ); + wxDialogWithHelpers dialog( m_WindowBound, _("Drag and Drop Error"), wxVERTICAL ); + dialog += dialog.Heading( _("It is an error to drop multiple files onto a PCSX2 window. One at a time please, thank you.") ); + pxIssueConfirmation( dialog, MsgButtons().Cancel() ); return false; } @@ -69,13 +68,14 @@ bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filen bool confirmed = true; if( SysHasValidState() ) { - Dialogs::ExtensibleConfirmation dialog( m_WindowBound, ConfButtons().Reset().Cancel(), - _("Confirm PS2 Reset"), + wxDialogWithHelpers dialog( m_WindowBound, _("Confirm PS2 Reset"), wxVERTICAL ); + + dialog += dialog.Heading( _("You have dropped the following ELF binary into PCSX2:\n\n") + filenames[0] + L"\n\n" + GetMsg_ConfirmSysReset() ); - confirmed = (Dialogs::IssueConfirmation( dialog, L"DragDrop:BootELF" ) != wxID_CANCEL); + confirmed = (pxIssueConfirmation( dialog, MsgButtons().Reset().Cancel(), L"DragDrop:BootELF" ) != wxID_CANCEL); } if( confirmed ) @@ -111,14 +111,14 @@ bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filen if( SysHasValidState() ) { - Dialogs::ExtensibleConfirmation dialog( m_WindowBound, ConfButtons().Reset().Cancel().Custom(_("Swap Disc")), - _("Confirm PS2 Reset"), - _("You have dropped the following ISO image into PCSX2:\n\n") + + wxDialogWithHelpers dialog( m_WindowBound, _("Confirm PS2 Reset"), wxVERTICAL ); + + dialog += dialog.Heading(_("You have dropped the following ISO image into PCSX2:\n\n") + filenames[0] + L"\n\n" + _("Do you want to swap discs or boot the new image (via system reset)?") ); - result = Dialogs::IssueConfirmation( dialog, L"DragDrop:BootIso" ); + result = pxIssueConfirmation( dialog, MsgButtons().Reset().Cancel().Custom(_("Swap Disc")), L"DragDrop:BootIso" ); } if( result != wxID_CANCEL ) diff --git a/pcsx2/gui/MainFrame.h b/pcsx2/gui/MainFrame.h index fbf8a39093..57f3355846 100644 --- a/pcsx2/gui/MainFrame.h +++ b/pcsx2/gui/MainFrame.h @@ -31,6 +31,7 @@ protected: EventListenerBinding<int> m_Listener_SettingsApplied; wxTimer m_HideMouseTimer; bool m_CursorShown; + bool m_HasFocus; public: GSPanel( wxWindow* parent ); @@ -49,6 +50,8 @@ protected: void OnShowMouse( wxMouseEvent& evt ); void OnHideMouseTimeout( wxTimerEvent& evt ); void OnKeyDown( wxKeyEvent& evt ); + void OnFocus( wxFocusEvent& evt ); + void OnFocusLost( wxFocusEvent& evt ); }; // -------------------------------------------------------------------------------------- diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp index 49b0fad77c..cac19d967d 100644 --- a/pcsx2/gui/MainMenuClicks.cpp +++ b/pcsx2/gui/MainMenuClicks.cpp @@ -28,6 +28,15 @@ using namespace Dialogs; +template<typename DialogType> +void AppOpenDialog( wxWindow* parent ) +{ + if( wxWindow* window = wxFindWindowByName( DialogType::GetNameStatic() ) ) + window->SetFocus(); + else + (new DialogType( parent ))->Show(); +} + extern wxString GetMsg_ConfirmSysReset(); void MainEmuFrame::SaveEmuOptions() @@ -41,12 +50,12 @@ void MainEmuFrame::SaveEmuOptions() void MainEmuFrame::Menu_ConfigSettings_Click(wxCommandEvent &event) { - Dialogs::ConfigurationDialog( this ).ShowModal(); + AppOpenDialog<ConfigurationDialog>( this ); } void MainEmuFrame::Menu_SelectBios_Click(wxCommandEvent &event) { - Dialogs::BiosSelectorDialog( this ).ShowModal(); + AppOpenDialog<BiosSelectorDialog>( this ); } void MainEmuFrame::Menu_CdvdSource_Click( wxCommandEvent &event ) @@ -118,11 +127,14 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event ) // User has an iso selected from a previous run, but it doesn't exist anymore. // Issue a courtesy popup and then an Iso Selector to choose a new one. - Dialogs::ExtensibleConfirmation( this, ConfButtons().OK(), _("ISO file not found!"), + wxDialogWithHelpers dialog( this, _("ISO file not found!"), wxVERTICAL ); + dialog += dialog.Heading( _("An error occurred while trying to open the file:\n\n") + g_Conf->CurrentIso + L"\n\n" + _("Error: The configured ISO file does not exist. Click OK to select a new ISO source for CDVD.") - ).ShowModal(); - + ); + + pxIssueConfirmation( dialog, MsgButtons().OK() ); + selector = true; } @@ -141,11 +153,9 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event ) if( SysHasValidState() ) { - ExtensibleConfirmation dialog( this, ConfButtons().Yes().Cancel(), - _("Confirm PS2 Reset"), GetMsg_ConfirmSysReset() - ); - - bool confirmed = (IssueConfirmation( dialog, L"BootCdvd:ConfirmReset" ) != wxID_CANCEL); + wxDialogWithHelpers dialog( this, _("Confirm PS2 Reset"), wxVERTICAL ); + dialog += dialog.Heading( GetMsg_ConfirmSysReset() ); + bool confirmed = (pxIssueConfirmation( dialog, MsgButtons().Yes().Cancel(), L"BootCdvd:ConfirmReset" ) != wxID_CANCEL); if( !confirmed ) { @@ -290,7 +300,7 @@ void MainEmuFrame::Menu_Debug_MemoryDump_Click(wxCommandEvent &event) void MainEmuFrame::Menu_Debug_Logging_Click(wxCommandEvent &event) { - LogOptionsDialog( this, wxID_ANY ).ShowModal(); + AppOpenDialog<LogOptionsDialog>( this ); } void MainEmuFrame::Menu_ShowConsole(wxCommandEvent &event) @@ -316,5 +326,5 @@ void MainEmuFrame::Menu_PrintCDVD_Info(wxCommandEvent &event) void MainEmuFrame::Menu_ShowAboutBox(wxCommandEvent &event) { - AboutBoxDialog( this, wxID_ANY ).ShowModal(); + AppOpenDialog<AboutBoxDialog>( this ); } diff --git a/pcsx2/gui/MessageBoxes.cpp b/pcsx2/gui/MessageBoxes.cpp index 40d78b4e25..887d95f0d2 100644 --- a/pcsx2/gui/MessageBoxes.cpp +++ b/pcsx2/gui/MessageBoxes.cpp @@ -15,130 +15,277 @@ #include "PrecompiledHeader.h" #include "App.h" +#include "Dialogs/ModalPopups.h" DEFINE_EVENT_TYPE( pxEVT_MSGBOX ); -DEFINE_EVENT_TYPE( pxEVT_CallStackBox ); +DEFINE_EVENT_TYPE( pxEVT_ASSERTION ); using namespace Threading; +using namespace pxSizerFlags; -// Thread Safety: Must be called from the GUI thread ONLY. -static int pxMessageDialog( const wxString& content, const wxString& caption, long flags ) +// Thread Safety: Must be called from the GUI thread ONLY. Will assert otherwise. +// +// [TODO] Add support for icons? +// +static int pxMessageDialog( const wxString& caption, const wxString& content, const MsgButtons& buttons ) { - if( IsDevBuild && !wxThread::IsMain() ) - throw Exception::InvalidOperation( "Function must be called by the main GUI thread only." ); + if( !AffinityAssert_AllowFromMain() ) return wxID_CANCEL; - // fixme: If the emulator is currently active and is running in fullscreen mode, then we - // need to either: + // fixme: If the emulator is currently active and is running in exclusive mode (forced + // fullscreen), then we need to either: // 1) Exit fullscreen mode before issuing the popup. // 2) Issue the popup with wxSTAY_ON_TOP specified so that the user will see it. // // And in either case the emulation should be paused/suspended for the user. - return wxMessageDialog( NULL, content, caption, flags ).ShowModal(); + wxDialogWithHelpers dialog( NULL, caption, wxVERTICAL ); + dialog += dialog.Heading( content ); + return pxIssueConfirmation( dialog, buttons ); } -// Thread Safety: Must be called from the GUI thread ONLY. -// fixme: this function should use a custom dialog box that has a wxTextCtrl for the callstack, and -// uses fixed-width (modern) fonts. -static int pxCallstackDialog( const wxString& content, const wxString& caption, long flags ) +class BaseMessageBoxEvent : public wxEvent { - if( IsDevBuild && !wxThread::IsMain() ) - throw Exception::InvalidOperation( "Function must be called by the main GUI thread only." ); + DECLARE_DYNAMIC_CLASS_NO_ASSIGN(BaseMessageBoxEvent) - return wxMessageDialog( NULL, content, caption, flags ).ShowModal(); -} +protected: + MsgboxEventResult* m_Instdata; + wxString m_Content; + +public: + explicit BaseMessageBoxEvent( int msgtype=pxEVT_MSGBOX, const wxString& content=wxEmptyString ) + : wxEvent( 0, msgtype ) + , m_Content( content ) + { + m_Instdata = NULL; + } + + virtual ~BaseMessageBoxEvent() throw() { } + virtual BaseMessageBoxEvent *Clone() const { return new BaseMessageBoxEvent(*this); } + + BaseMessageBoxEvent( MsgboxEventResult& instdata, const wxString& content ) + : wxEvent( 0, pxEVT_MSGBOX ) + , m_Instdata( &instdata ) + , m_Content( content ) + { + } + + BaseMessageBoxEvent( const wxString& content ) + : wxEvent( 0, pxEVT_MSGBOX ) + , m_Instdata( NULL ) + , m_Content( content ) + { + } + + BaseMessageBoxEvent( const BaseMessageBoxEvent& event ) + : wxEvent( event ) + , m_Instdata( event.m_Instdata ) + , m_Content( event.m_Content ) + { + } + + BaseMessageBoxEvent& SetInstData( MsgboxEventResult& instdata ) + { + m_Instdata = &instdata; + return *this; + } + + // Thread Safety: Must be called from the GUI thread ONLY. + virtual void IssueDialog() + { + AffinityAssert_AllowFromMain(); + + int result = _DoDialog(); + + if( m_Instdata != NULL ) + { + m_Instdata->result = result; + m_Instdata->WaitForMe.Post(); + } + } + +protected: + virtual int _DoDialog() const + { + pxFailDev( "Abstract Base MessageBox Event." ); + return wxID_CANCEL; + } +}; // -------------------------------------------------------------------------------------- // pxMessageBoxEvent // -------------------------------------------------------------------------------------- -class pxMessageBoxEvent : public wxEvent +// This event type is used to transfer message boxes to the main UI thread, and return the +// result of the box. It's the only way a message box can be issued from non-main threads +// with complete safety in wx2.8. +// +// For simplicity sake this message box only supports two basic designs. The main design +// is a generic message box with confirmation buttons of your choosing. Additionally you +// can specify a "scrollableContent" text string, which is added into a read-only richtext +// control similar to the console logs and such. +// +// Future consideration: If wxWidgets 3.0 has improved thread safety, then it should probably +// be reasonable for it to work with a more flexable model where the dialog can be created +// on a child thread, passed to the main thread, where ShowModal() is run (keeping the nested +// message pumps on the main thread where they belong). But so far this is not possible, +// because of various subtle issues in wx2.8 design. +// +class pxMessageBoxEvent : public BaseMessageBoxEvent { -protected: - MsgboxEventResult& m_Instdata; - wxString m_Title; - wxString m_Content; - long m_Flags; + typedef BaseMessageBoxEvent _parent; + DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxMessageBoxEvent) +protected: + wxString m_Title; + MsgButtons m_Buttons; + public: - pxMessageBoxEvent() - : wxEvent( 0, pxEVT_MSGBOX ) - , m_Instdata( *(MsgboxEventResult*)NULL ) - , m_Title() - , m_Content() + pxMessageBoxEvent( int msgtype=pxEVT_MSGBOX ) + : BaseMessageBoxEvent( msgtype ) { - m_Flags = 0; } - pxMessageBoxEvent( MsgboxEventResult& instdata, const wxString& title, const wxString& content, long flags ) - : wxEvent( 0, pxEVT_MSGBOX ) - , m_Instdata( instdata ) + virtual ~pxMessageBoxEvent() throw() { } + virtual pxMessageBoxEvent *Clone() const { return new pxMessageBoxEvent(*this); } + + pxMessageBoxEvent( MsgboxEventResult& instdata, const wxString& title, const wxString& content, const MsgButtons& buttons ) + : BaseMessageBoxEvent( instdata, content ) , m_Title( title ) - , m_Content( content ) + , m_Buttons( buttons ) + { + } + + pxMessageBoxEvent( const wxString& title, const wxString& content, const MsgButtons& buttons ) + : BaseMessageBoxEvent( content ) + , m_Title( title ) + , m_Buttons( buttons ) { - m_Flags = flags; } pxMessageBoxEvent( const pxMessageBoxEvent& event ) - : wxEvent( event ) - , m_Instdata( event.m_Instdata ) + : BaseMessageBoxEvent( event ) , m_Title( event.m_Title ) - , m_Content( event.m_Content ) + , m_Buttons( event.m_Buttons ) { - m_Flags = event.m_Flags; } - // Thread Safety: Must be called from the GUI thread ONLY. - void DoTheDialog() + pxMessageBoxEvent& SetInstData( MsgboxEventResult& instdata ) { - int result; - - if( m_id == pxEVT_MSGBOX ) - result = pxMessageDialog( m_Content, m_Title, m_Flags ); - else - result = pxCallstackDialog( m_Content, m_Title, m_Flags ); - m_Instdata.result = result; - m_Instdata.WaitForMe.Post(); + _parent::SetInstData( instdata ); + return *this; } - virtual wxEvent *Clone() const { return new pxMessageBoxEvent(*this); } - -private: - DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxMessageBoxEvent) +protected: + virtual int _DoDialog() const + { + return pxMessageDialog( m_Content, m_Title, m_Buttons ); + } }; -IMPLEMENT_DYNAMIC_CLASS( pxMessageBoxEvent, wxEvent ) +// -------------------------------------------------------------------------------------- +// pxAssertionEvent +// -------------------------------------------------------------------------------------- +class pxAssertionEvent : public BaseMessageBoxEvent +{ + typedef BaseMessageBoxEvent _parent; + DECLARE_DYNAMIC_CLASS_NO_ASSIGN( pxAssertionEvent ) + +protected: + wxString m_Stacktrace; + +public: + pxAssertionEvent() + : BaseMessageBoxEvent( pxEVT_ASSERTION ) + { + } + + virtual ~pxAssertionEvent() throw() { } + + virtual pxAssertionEvent *Clone() const { return new pxAssertionEvent(*this); } + + pxAssertionEvent( MsgboxEventResult& instdata, const wxString& content, const wxString& trace ) + : BaseMessageBoxEvent( pxEVT_ASSERTION ) + , m_Stacktrace( trace ) + { + } + + pxAssertionEvent( const wxString& content, const wxString& trace ) + : BaseMessageBoxEvent( pxEVT_ASSERTION, content ) + , m_Stacktrace( trace ) + { + } + + pxAssertionEvent( const pxAssertionEvent& event ) + : BaseMessageBoxEvent( event ) + , m_Stacktrace( event.m_Stacktrace ) + { + } + + pxAssertionEvent& SetInstData( MsgboxEventResult& instdata ) + { + _parent::SetInstData( instdata ); + return *this; + } + + pxAssertionEvent& SetStacktrace( const wxString& trace ) + { + m_Stacktrace = trace; + return *this; + } + +protected: + virtual int _DoDialog() const + { + return Dialogs::AssertionDialog( m_Content, m_Stacktrace ).ShowModal(); + } +}; + +IMPLEMENT_DYNAMIC_CLASS( BaseMessageBoxEvent, wxEvent ) +IMPLEMENT_DYNAMIC_CLASS( pxMessageBoxEvent, BaseMessageBoxEvent ) +IMPLEMENT_DYNAMIC_CLASS( pxAssertionEvent, BaseMessageBoxEvent ) namespace Msgbox { - // parameters: - // flags - messagebox type flags, such as wxOK, wxCANCEL, etc. - // - static int ThreadedMessageBox( const wxString& content, const wxString& title, long flags, int boxType=pxEVT_MSGBOX ) + static int ThreadedMessageBox( BaseMessageBoxEvent& evt ) + { + MsgboxEventResult instdat; + evt.SetInstData( instdat ); + + if( wxThread::IsMain() ) + { + // main thread can handle the message immediately. + wxGetApp().ProcessEvent( evt ); + } + else + { + // Not on main thread, must post the message there for handling instead: + wxGetApp().AddPendingEvent( evt ); + instdat.WaitForMe.WaitNoCancel(); // Important! disable cancellation since we're using local stack vars. + } + return instdat.result; + } + + static int ThreadedMessageBox( const wxString& title, const wxString& content, const MsgButtons& buttons ) { // must pass the message to the main gui thread, and then stall this thread, to avoid // threaded chaos where our thread keeps running while the popup is awaiting input. MsgboxEventResult instdat; - pxMessageBoxEvent tevt( instdat, title, content, flags ); + pxMessageBoxEvent tevt( instdat, title, content, buttons ); wxGetApp().AddPendingEvent( tevt ); instdat.WaitForMe.WaitNoCancel(); // Important! disable cancellation since we're using local stack vars. return instdat.result; } - void OnEvent( pxMessageBoxEvent& evt ) - { - evt.DoTheDialog(); - } - // Pops up an alert Dialog Box with a singular "OK" button. // Always returns false. bool Alert( const wxString& text, const wxString& caption, int icon ) { - icon |= wxOK; + MsgButtons buttons( MsgButtons().OK() ); + if( wxThread::IsMain() ) - pxMessageDialog( text, caption, icon ); + pxMessageDialog( caption, text, buttons ); else - ThreadedMessageBox( text, caption, icon ); + ThreadedMessageBox( caption, text, buttons ); return false; } @@ -146,60 +293,40 @@ namespace Msgbox // true if OK, false if cancel. bool OkCancel( const wxString& text, const wxString& caption, int icon ) { - icon |= wxOK | wxCANCEL; + MsgButtons buttons( MsgButtons().OKCancel() ); + if( wxThread::IsMain() ) { - return wxID_OK == pxMessageDialog( text, caption, icon ); + return wxID_OK == pxMessageDialog( caption, text, buttons ); } else { - return wxID_OK == ThreadedMessageBox( text, caption, icon ); + return wxID_OK == ThreadedMessageBox( caption, text, buttons ); } } bool YesNo( const wxString& text, const wxString& caption, int icon ) { - icon |= wxYES_NO; - if( wxThread::IsMain() ) - { - return wxID_YES == pxMessageDialog( text, caption, icon ); - } - else - { - return wxID_YES == ThreadedMessageBox( text, caption, icon ); - } - } - - // [TODO] : This should probably be a fancier looking dialog box with the stacktrace - // displayed inside a wxTextCtrl. - static int CallStack( const wxString& errormsg, const wxString& stacktrace, const wxString& prompt, const wxString& caption, int buttons ) - { - buttons |= wxICON_STOP; - - wxString text( errormsg + L"\n\n" + stacktrace + L"\n" + prompt ); + MsgButtons buttons( MsgButtons().YesNo() ); if( wxThread::IsMain() ) { - return pxCallstackDialog( text, caption, buttons ); + return wxID_YES == pxMessageDialog( caption, text, buttons ); } else { - return ThreadedMessageBox( text, caption, buttons, pxEVT_CallStackBox ); + return wxID_YES == ThreadedMessageBox( caption, text, buttons ); } } int Assertion( const wxString& text, const wxString& stacktrace ) { - return CallStack( text, stacktrace, - L"\nDo you want to stop the program?" - L"\nOr press [Cancel] to suppress further assertions.", - L"PCSX2 Assertion Failure", - wxYES_NO | wxCANCEL - ); - } - - void Except( const Exception::BaseException& src ) - { - CallStack( src.FormatDisplayMessage(), src.FormatDiagnosticMessage(), wxEmptyString, L"PCSX2 Unhandled Exception", wxOK ); + pxAssertionEvent tevt( text, stacktrace ); + return ThreadedMessageBox( tevt ); } } + +void Pcsx2App::OnMessageBox( pxMessageBoxEvent& evt ) +{ + evt.IssueDialog(); +} diff --git a/pcsx2/gui/Panels/CpuPanel.cpp b/pcsx2/gui/Panels/CpuPanel.cpp index dcecdc4e29..4af176ed6f 100644 --- a/pcsx2/gui/Panels/CpuPanel.cpp +++ b/pcsx2/gui/Panels/CpuPanel.cpp @@ -46,8 +46,8 @@ Panels::BaseAdvancedCpuOptions::BaseAdvancedCpuOptions( wxWindow* parent ) // Highlight Default Options: - m_RoundModePanel->SetDefault( 3 ); - m_ClampModePanel->SetDefault( 1 ); + m_RoundModePanel->SetDefaultItem( 3 ); + m_ClampModePanel->SetDefaultItem( 1 ); // --------------------------------- // The Fitting And Sizing Area @@ -146,8 +146,8 @@ Panels::CpuPanelEE::CpuPanelEE( wxWindow* parent ) }; - m_panel_RecEE = &(new pxRadioPanel( this, tbl_CpuTypes_EE ))->SetDefault( 1 ); - m_panel_RecIOP = &(new pxRadioPanel( this, tbl_CpuTypes_IOP ))->SetDefault( 1 ); + m_panel_RecEE = &(new pxRadioPanel( this, tbl_CpuTypes_EE ))->SetDefaultItem( 1 ); + m_panel_RecIOP = &(new pxRadioPanel( this, tbl_CpuTypes_IOP ))->SetDefaultItem( 1 ); m_panel_RecEE->Realize(); m_panel_RecIOP->Realize(); @@ -193,8 +193,8 @@ Panels::CpuPanelVU::CpuPanelVU( wxWindow* parent ) .SetToolTip(_("Useful for diagnosing bugs or clamping issues in the new mVU recompiler.")) }; - m_panel_VU0 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefault( 1 ); - m_panel_VU1 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefault( 1 ); + m_panel_VU0 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefaultItem( 1 ); + m_panel_VU1 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefaultItem( 1 ); m_panel_VU0->Realize(); m_panel_VU1->Realize(); @@ -233,6 +233,9 @@ void Panels::CpuPanelEE::Apply() void Panels::CpuPanelEE::OnSettingsChanged() { + m_panel_RecEE->Enable( x86caps.hasStreamingSIMD2Extensions ); + m_panel_RecIOP->Enable( x86caps.hasStreamingSIMD2Extensions ); + const Pcsx2Config::RecompilerOptions& recOps( g_Conf->EmuOptions.Cpu.Recompiler ); m_panel_RecEE->SetSelection( (int)recOps.EnableEE ); m_panel_RecIOP->SetSelection( (int)recOps.EnableIOP ); @@ -250,6 +253,12 @@ void Panels::CpuPanelVU::Apply() void Panels::CpuPanelVU::OnSettingsChanged() { + m_panel_VU0->EnableItem( 1, x86caps.hasStreamingSIMD2Extensions ); + m_panel_VU0->EnableItem( 2, x86caps.hasStreamingSIMD2Extensions ); + + m_panel_VU1->EnableItem( 1, x86caps.hasStreamingSIMD2Extensions ); + m_panel_VU1->EnableItem( 2, x86caps.hasStreamingSIMD2Extensions ); + Pcsx2Config::RecompilerOptions& recOps( g_Conf->EmuOptions.Cpu.Recompiler ); if( recOps.UseMicroVU0 ) m_panel_VU0->SetSelection( recOps.EnableVU0 ? 1 : 0 ); diff --git a/pcsx2/gui/Panels/PluginSelectorPanel.cpp b/pcsx2/gui/Panels/PluginSelectorPanel.cpp index 0e2a541f16..4f8229eede 100644 --- a/pcsx2/gui/Panels/PluginSelectorPanel.cpp +++ b/pcsx2/gui/Panels/PluginSelectorPanel.cpp @@ -16,6 +16,7 @@ #include "PrecompiledHeader.h" #include "App.h" #include "Plugins.h" +#include "SaveState.h" #include "Utilities/ScopedPtr.h" #include "ConfigurationPanels.h" #include "Dialogs/ModalPopups.h" @@ -309,37 +310,43 @@ void Panels::PluginSelectorPanel::Apply() break; } while( ++pi, pi->shortname != NULL ); + bool isSuspended = false; + if( pi->shortname != NULL ) { if( CoreThread.IsRunning() ) { // [TODO] : Post notice that this shuts down existing emulation, and may not safely recover. - Dialogs::ExtensibleConfirmation dialog( this, ConfButtons().OK().Cancel(), - - _("Shutdown PS2 virtual machine?"), - - pxE( ".Popup:PluginSelector:ConfirmShutdown", - L"Warning! Changing plugins requires a complete shutdown and reset of the PS2 virtual machine. " - L"PCSX2 will attempt to save and restore the state, but if the newly selected plugins are " - L"incompatible the recovery may fail, and current progress will be lost." - L"\n\n" - L"Are you sure you want to apply settings now?" - ) - ); + wxDialogWithHelpers dialog( this, _("Shutdown PS2 virtual machine?"), wxVERTICAL ); - int result = Dialogs::IssueConfirmation( dialog, L"PluginSelector:ConfirmShutdown" ); + dialog += dialog.Heading( pxE( ".Popup:PluginSelector:ConfirmShutdown", + L"Warning! Changing plugins requires a complete shutdown and reset of the PS2 virtual machine. " + L"PCSX2 will attempt to save and restore the state, but if the newly selected plugins are " + L"incompatible the recovery may fail, and current progress will be lost." + L"\n\n" + L"Are you sure you want to apply settings now?" + ) ); + + int result = pxIssueConfirmation( dialog, MsgButtons().OK().Cancel(), L"PluginSelector:ConfirmShutdown" ); if( result == wxID_CANCEL ) throw Exception::CannotApplySettings( this, "Cannot apply settings: canceled by user because plugins changed while the emulation state was active.", false ); + + // FIXME : We only actually have to save plugins here, except the recovery code + // in SysCoreThread isn't quite set up yet to handle that (I think...) --air + + isSuspended = CoreThread.Suspend(); + StateCopy_FreezeToMem_Blocking(); } - sApp.SysReset(); + // Don't use SysShutdown, it clears the StateCopy. + CoreThread.Cancel(); + wxGetApp().m_CorePlugins = NULL; } if( !wxGetApp().m_CorePlugins ) { - try - { + try { LoadPluginsImmediate(); } catch( Exception::PluginError& ex ) @@ -359,6 +366,8 @@ void Panels::PluginSelectorPanel::Apply() ); } } + + if( isSuspended ) CoreThread.Resume(); } void Panels::PluginSelectorPanel::CancelRefresh() @@ -591,6 +600,7 @@ void Panels::PluginSelectorPanel::EnumThread::ExecuteTaskInThread() { DevCon.WriteLn( "Plugin Enumeration Thread started..." ); + Sleep( 15 ); // give the window some time to paint. YieldToMain(); for( int curidx=0; curidx < m_master.FileCount(); ++curidx ) diff --git a/pcsx2/gui/RecentIsoList.cpp b/pcsx2/gui/RecentIsoList.cpp index d621dd23e7..31e10b8dd3 100644 --- a/pcsx2/gui/RecentIsoList.cpp +++ b/pcsx2/gui/RecentIsoList.cpp @@ -26,7 +26,7 @@ RecentIsoManager::RecentIsoManager( wxMenu* menu ) : m_Menu( menu ) - , m_MaxLength( g_Conf->RecentFileCount ) + , m_MaxLength( g_Conf->RecentIsoCount ) , m_Listener_SettingsLoadSave ( wxGetApp().Source_SettingsLoadSave(), EventListener<IniInterface>( this, OnSettingsLoadSave ) ) , m_Listener_SettingsApplied ( wxGetApp().Source_SettingsApplied(), EventListener<int>( this, OnSettingsApplied ) ) { @@ -166,7 +166,7 @@ void RecentIsoManager::DoSettingsLoadSave( IniInterface& ini ) { RemoveAllFromMenu(); - m_MaxLength = g_Conf->RecentFileCount; + m_MaxLength = g_Conf->RecentIsoCount; IniScopedGroup groupie( ini, L"RecentIso" ); for( uint i=0; i<m_MaxLength; ++i ) { diff --git a/pcsx2/gui/i18n.cpp b/pcsx2/gui/i18n.cpp index b30487e32c..38269d617b 100644 --- a/pcsx2/gui/i18n.cpp +++ b/pcsx2/gui/i18n.cpp @@ -141,7 +141,7 @@ const wxChar* __fastcall pxGetTranslation( const wxChar* message ) if( wxStrlen( message ) > 96 ) { Console.Warning( "pxGetTranslation: Long message detected, maybe use pxE() instead?" ); - Console.Indent().WriteLn( Color_Green, L"Message: %s", message ); + Console.WriteLn( Color_Green, L"Message: %s", message ); } } return wxGetTranslation( message ); diff --git a/pcsx2/gui/pxLogTextCtrl.cpp b/pcsx2/gui/pxLogTextCtrl.cpp index 861e755742..7879af1ecc 100644 --- a/pcsx2/gui/pxLogTextCtrl.cpp +++ b/pcsx2/gui/pxLogTextCtrl.cpp @@ -31,7 +31,6 @@ void __evt_fastcall pxLogTextCtrl::OnCoreThreadStatusChanged( void* obj, wxComma if( mframe->HasWriteLock() ) return; ::SendMessage((HWND)mframe->GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL); - mframe->m_win32_StupidRefreshTricks = 0; #endif } @@ -45,7 +44,6 @@ void __evt_fastcall pxLogTextCtrl::OnCorePluginStatusChanged( void* obj, PluginE if( mframe->HasWriteLock() ) return; ::SendMessage((HWND)mframe->GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL); - mframe->m_win32_StupidRefreshTricks = 0; #endif } @@ -58,10 +56,11 @@ pxLogTextCtrl::pxLogTextCtrl( wxWindow* parent ) , m_Listener_CorePluginStatus ( wxGetApp().Source_CorePluginStatus(), EventListener<PluginEventType> ( this, OnCorePluginStatusChanged ) ) { #ifdef __WXMSW__ - m_win32_StupidRefreshTricks = 0; - m_win32_LinesPerScroll = 10; + m_win32_LinesPerScroll = 10; + m_win32_LinesPerPage = 0; #endif - m_FreezeWrites = false; + m_IsPaused = false; + m_FreezeWrites = false; Connect( wxEVT_SCROLLWIN_THUMBTRACK, wxScrollWinEventHandler(pxLogTextCtrl::OnThumbTrack) ); Connect( wxEVT_SCROLLWIN_THUMBRELEASE, wxScrollWinEventHandler(pxLogTextCtrl::OnThumbRelease) ); @@ -85,14 +84,12 @@ void pxLogTextCtrl::OnResize( wxSizeEvent& evt ) int fonty; GetTextExtent( L"blaH yeah", NULL, &fonty ); m_win32_LinesPerPage = (ctrly / fonty) + 1; - m_win32_LinesPerScroll = m_win32_LinesPerPage * 0.25; + m_win32_LinesPerScroll = m_win32_LinesPerPage * 0.40; #endif evt.Skip(); } -bool m_IsPaused = false; - void pxLogTextCtrl::OnThumbTrack(wxScrollWinEvent& evt) { //Console.Warning( "Thumb Tracking!!!" ); @@ -131,16 +128,12 @@ void pxLogTextCtrl::ConcludeIssue( int lines ) // makes logging very slow, so we only send the message for status changes, so that the // log aligns itself nicely when we pause emulation or when errors occur. - //m_win32_StupidRefreshTricks += lines; - //if( m_win32_StupidRefreshTricks > m_win32_LinesPerScroll ) - { - wxTextPos showpos = XYToPosition( 1, GetNumberOfLines()-m_win32_LinesPerScroll ); - if( showpos > 0 ) - ShowPosition( showpos ); + wxTextPos showpos = XYToPosition( 1, GetNumberOfLines()-m_win32_LinesPerScroll ); + if( showpos > 0 ) + ShowPosition( showpos ); - m_win32_StupidRefreshTricks = 0; - //::SendMessage((HWND)GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL); - } + //::SendMessage((HWND)GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL); #endif } + diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj index 9e68d69425..92e16e5956 100644 --- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj +++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj @@ -1847,6 +1847,10 @@ RelativePath="..\..\gui\AdvancedDialog.h" > </File> + <File + RelativePath="..\..\gui\Dialogs\AssertionDialog.cpp" + > + </File> <File RelativePath="..\..\gui\Dialogs\ConfigurationDialog.cpp" > diff --git a/pcsx2/windows/WinCompressNTFS.cpp b/pcsx2/windows/WinCompressNTFS.cpp index b20d7d453a..0fc53ee1bd 100644 --- a/pcsx2/windows/WinCompressNTFS.cpp +++ b/pcsx2/windows/WinCompressNTFS.cpp @@ -25,7 +25,8 @@ void StreamException_ThrowFromErrno( const wxString& streamname, errno_t errcode switch( errcode ) { case EINVAL: - throw Exception::InvalidArgument( "Invalid argument" ); + pxFailDev( L"Invalid argument" ); + throw Exception::Stream( streamname, "Invalid argument" ); case EACCES: // Access denied! throw Exception::AccessDenied( streamname ); @@ -75,7 +76,7 @@ void StreamException_ThrowLastError( const wxString& streamname, HANDLE result ) throw Exception::AccessDenied( streamname ); case ERROR_INVALID_HANDLE: - throw Exception::InvalidOperation( "Stream object or handle is invalid" ); + throw Exception::BadStream( streamname, "Stream object or handle is invalid" ); case ERROR_SHARING_VIOLATION: throw Exception::AccessDenied( streamname, "Sharing violation" ); diff --git a/pcsx2/windows/WinConsolePipe.cpp b/pcsx2/windows/WinConsolePipe.cpp index eecb18bb61..5f82d11d09 100644 --- a/pcsx2/windows/WinConsolePipe.cpp +++ b/pcsx2/windows/WinConsolePipe.cpp @@ -99,7 +99,7 @@ public: protected: void ExecuteTaskInThread() { - SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL ); + ::SetThreadPriority( ::GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL ); if( m_outpipe == INVALID_HANDLE_VALUE ) return; try @@ -112,7 +112,7 @@ protected: if( !ReadFile(m_outpipe, s8_Buf, sizeof(s8_Buf)-1, &u32_Read, NULL) ) { DWORD result = GetLastError(); - if( result == ERROR_HANDLE_EOF ) break; + if( result == ERROR_HANDLE_EOF || result == ERROR_BROKEN_PIPE ) break; if( result == ERROR_IO_PENDING ) { Yield( 10 ); @@ -172,6 +172,10 @@ class WinPipeRedirection : public PipeRedirectionBase DeclareNoncopyableObject( WinPipeRedirection ); protected: + DWORD m_stdhandle; + FILE* m_stdfp; + FILE m_stdfp_copy; + HANDLE m_readpipe; HANDLE m_writepipe; int m_crtFile; @@ -187,21 +191,25 @@ public: }; WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) - : m_readpipe(INVALID_HANDLE_VALUE) - , m_writepipe(INVALID_HANDLE_VALUE) - , m_crtFile(-1) - , m_fp(NULL) - , m_Thread( m_readpipe, (stdstream == stderr) ? Color_Red : Color_Black ) + : m_Thread( m_readpipe, (stdstream == stderr) ? Color_Red : Color_Black ) { + m_stdhandle = ( stdstream == stderr ) ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE; + m_stdfp = stdstream; + m_stdfp_copy = *stdstream; + + m_readpipe = INVALID_HANDLE_VALUE; + m_writepipe = INVALID_HANDLE_VALUE; + m_crtFile = -1; + m_fp = NULL; + + pxAssume( (stdstream == stderr) || (stdstream == stdout) ); + try { - pxAssert( (stdstream == stderr) || (stdstream == stdout) ); - DWORD stdhandle = ( stdstream == stderr ) ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE; - if( 0 == CreatePipe( &m_readpipe, &m_writepipe, NULL, 0 ) ) throw Exception::Win32Error( "CreatePipe failed." ); - if( 0 == SetStdHandle( stdhandle, m_writepipe ) ) + if( 0 == SetStdHandle( m_stdhandle, m_writepipe ) ) throw Exception::Win32Error( "SetStdHandle failed." ); // Note: Don't use GetStdHandle to "confirm" the handle. @@ -222,11 +230,19 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) if( m_fp == NULL ) throw Exception::RuntimeError( "_fdopen returned NULL." ); - *stdstream = *m_fp; + *m_stdfp = *m_fp; // omg hack. but it works >_< setvbuf( stdstream, NULL, _IONBF, 0 ); m_Thread.Start(); } + catch( Exception::BaseThreadError& ex ) + { + // thread object will become invalid because of scoping after we leave + // the constructor, so re-pack a new exception: + + Cleanup(); + throw Exception::RuntimeError( ex.FormatDiagnosticMessage(), ex.FormatDisplayMessage() ); + } catch( Exception::BaseException& ex ) { Cleanup(); @@ -235,6 +251,9 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) } catch( ... ) { + // C++ doesn't execute the object destructor automatically, because it's fail++ + // (and I'm *not* encapsulating each handle into its own object >_<) + Cleanup(); throw; } @@ -247,6 +266,12 @@ WinPipeRedirection::~WinPipeRedirection() void WinPipeRedirection::Cleanup() throw() { + // restore the old handle we so graciously hacked earlier ;) + // (or don't and suffer CRT crashes! ahaha!) + + if( m_stdfp != NULL ) + *m_stdfp = m_stdfp_copy; + // Cleanup Order Notes: // * The redirection thread is most likely blocking on ReadFile(), so we can't Cancel yet, lest we deadlock -- // Closing the writepipe (either directly or through the fp/crt handles) issues an EOF to the thread, diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp index 9519a45c0a..7930d156e5 100644 --- a/pcsx2/x86/ix86-32/iR5900-32.cpp +++ b/pcsx2/x86/ix86-32/iR5900-32.cpp @@ -495,31 +495,44 @@ static const uint m_recBlockAllocSize = (((Ps2MemSize::Base + Ps2MemSize::Rom + Ps2MemSize::Rom1) / 4) * sizeof(BASEBLOCK)) + RECCONSTBUF_SIZE * sizeof(u32) + Ps2MemSize::Base; +static void recThrowHardwareDeficiency( const wxChar* extFail ) +{ + throw Exception::HardwareDeficiency( + wxsFormat( L"R5900-32 recompiler init failed: %s is not available.", extFail), + wxsFormat(_("%s Extensions not found. The R5900-32 recompiler requires a host CPU with MMX, SSE, and SSE2 extensions."), extFail ) + ); +} + static void recAlloc() { // Hardware Requirements Check... - if ( !( x86caps.hasMultimediaExtensions ) ) - throw Exception::HardwareDeficiency( "Processor doesn't support MMX" ); + if ( !x86caps.hasMultimediaExtensions ) + recThrowHardwareDeficiency( L"MMX" ); - if ( !( x86caps.hasStreamingSIMDExtensions ) ) - throw Exception::HardwareDeficiency( "Processor doesn't support SSE" ); + if ( !x86caps.hasStreamingSIMDExtensions ) + recThrowHardwareDeficiency( L"SSE" ); - if ( !( x86caps.hasStreamingSIMD2Extensions ) ) - throw Exception::HardwareDeficiency( "Processor doesn't support SSE2" ); + if ( !x86caps.hasStreamingSIMD2Extensions ) + recThrowHardwareDeficiency( L"SSE2" ); if( recMem == NULL ) { - // Note: the VUrec depends on being able to grab an allocation below the 0x10000000 line, - // so we give the EErec an address above that to try first as it's basemem address, hence - // the 0x20000000 pick. + // It's handy to have a constant base address for the EE recompiler buffer, since it + // allows me to key in the address directly in the debugger, and also recognize EE + // recompiled code from user-provisioned stack traces. But besides those, the recompiler + // has no actual restrictions on where it's compiled code buffer is located. + + // Note: the SuperVU recompiler depends on being able to grab an allocation below the + // 0x10000000 line, so we give the EErec an address above that to try first as it's + // basemem address, hence the 0x20000000 pick. const uint cachememsize = REC_CACHEMEM+0x1000; recMem = (u8*)SysMmapEx( 0x20000000, cachememsize, 0, "recAlloc(R5900)" ); } if( recMem == NULL ) - throw Exception::OutOfMemory( "R5900-32 > failed to allocate recompiler memory." ); + throw Exception::OutOfMemory( "R5900-32: Out of memory allocating recompiled code buffer." ); // Goal: Allocate BASEBLOCKs for every possible branch target in PS2 memory. // Any 4-byte aligned address makes a valid branch target as per MIPS design (all instructions are @@ -529,14 +542,14 @@ static void recAlloc() m_recBlockAlloc = (u8*) _aligned_malloc( m_recBlockAllocSize, 4096 ); if( m_recBlockAlloc == NULL ) - throw Exception::OutOfMemory( "R5900-32 Init > Failed to allocate memory for BASEBLOCK tables." ); + throw Exception::OutOfMemory( "R5900-32: Out of memory allocating BASEBLOCK tables." ); u8* curpos = m_recBlockAlloc; - recRAM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Base / 4) * sizeof(BASEBLOCK); - recROM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom / 4) * sizeof(BASEBLOCK); - recROM1 = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom1 / 4) * sizeof(BASEBLOCK); - recConstBuf = (u32*)curpos; curpos += RECCONSTBUF_SIZE * sizeof(u32); - recRAMCopy = (u32*)curpos; + recRAM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Base / 4) * sizeof(BASEBLOCK); + recROM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom / 4) * sizeof(BASEBLOCK); + recROM1 = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom1 / 4) * sizeof(BASEBLOCK); + recConstBuf = (u32*)curpos; curpos += RECCONSTBUF_SIZE * sizeof(u32); + recRAMCopy = (u32*)curpos; if( s_pInstCache == NULL ) { @@ -545,7 +558,7 @@ static void recAlloc() } if( s_pInstCache == NULL ) - throw Exception::OutOfMemory( "R5900-32 Init > failed to allocate memory for pInstCache." ); + throw Exception::OutOfMemory( "R5900-32: Out of memory allocating InstCache." ); // No errors.. Proceed with initialization: @@ -564,11 +577,13 @@ struct ManualPageTracking static __aligned16 u16 manual_page[Ps2MemSize::Base >> 12]; static __aligned16 u8 manual_counter[Ps2MemSize::Base >> 12]; -static bool eeRecIsReset = false; +static u32 eeRecIsReset = 0; //////////////////////////////////////////////////// void recResetEE( void ) { + if( AtomicExchange( eeRecIsReset, true ) ) return; + Console.WriteLn( Color_StrongBlack, "Issuing EE/iR5900-32 Recompiler Reset" ); maxrecmem = 0; @@ -630,7 +645,6 @@ void recResetEE( void ) x86FpuState = FPU_STATE; branch = 0; - eeRecIsReset = true; } static void recShutdown( void ) @@ -656,26 +670,21 @@ static jmp_buf m_SetJmp_StateCheck; static void recCheckExecutionState() { -#if PCSX2_SEH - SysCoreThread::Get().StateCheckInThread(); - - if( eeRecIsReset ) - throw Exception::ForceDispatcherReg(); -#else - - // Without SEH we'll need to hop to a safehouse point outside the scope of recompiled - // code. C++ exceptions can't cross the mighty chasm in the stackframe that the recompiler - // creates. However, the longjump is slow so we only want to do one when absolutely - // necessary: - pxAssert( !eeRecIsReset ); // should only be changed during suspended thread states - if( SysCoreThread::Get().HasPendingStateChangeRequest() ) + if( GetCoreThread().HasPendingStateChangeRequest() ) { - longjmp( m_SetJmp_StateCheck, SetJmp_Dispatcher ); - } +#if PCSX2_SEH + throw Exception::ForceDispatcherReg(); +#else + // Without SEH we'll need to hop to a safehouse point outside the scope of recompiled + // code. C++ exceptions can't cross the mighty chasm in the stackframe that the recompiler + // creates. However, the longjump is slow so we only want to do one when absolutely + // necessary: + longjmp( m_SetJmp_StateCheck, 1 ); #endif + } } static void recExecute() @@ -684,64 +693,36 @@ static void recExecute() // [TODO] fix this comment to explain various code entry/exit points, when I'm not so tired! #if PCSX2_SEH - try { - while( true ) - { - eeRecIsReset = false; - g_EEFreezeRegs = true; + eeRecIsReset = false; + g_EEFreezeRegs = true; - try { - EnterRecompiledCode(); - } - catch( Exception::ForceDispatcherReg& ) { } - } + try { + EnterRecompiledCode(); } - catch( Exception::ExitRecExecute& ) {} + catch( Exception::ForceDispatcherReg& ) { } #else - switch( setjmp( m_SetJmp_StateCheck ) ) + int oldstate; + + if( !setjmp( m_SetJmp_StateCheck ) ) { - case 0: // first run, fall through to Dispatcher - case SetJmp_Dispatcher: - while( true ) - { - int oldstate; + eeRecIsReset = false; + g_EEFreezeRegs = true; - // Important! Most of the console logging and such has cancel points in it. This is great - // in Windows, where SEH lets us safely kill a thread from anywhere we want. This is bad - // in Linux, which cannot have a C++ exception cross the recompiler. Hence the changing - // of the cancelstate here! + // Important! Most of the console logging and such has cancel points in it. This is great + // in Windows, where SEH lets us safely kill a thread from anywhere we want. This is bad + // in Linux, which cannot have a C++ exception cross the recompiler. Hence the changing + // of the cancelstate here! - pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate ); - SysCoreThread::Get().StateCheckInThread(); - pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate ); - - eeRecIsReset = false; - - #ifdef _WIN32 - __try { - #endif - - EnterRecompiledCode(); - - #ifdef _WIN32 - } __finally - { - // This assertion is designed to help me troubleshoot the setjmp behavior from Win32. - // If the recompiler throws an unhandled SEH exception with SEH support disabled (which - // is typically a pthread_cancel) then this will fire and let me know. - - // FIXME: Doesn't work because SEH is remarkably clever and executes the _finally block - // even when I use longjmp to restart the loop. Maybe a workaround exists? :/ - - //pxFailDev( "Recompiler threw an SEH exception with SEH disabled; possibly due to pthread_cancel." ); - } - #endif - } - break; - - case SetJmp_Exit: break; + pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate ); + EnterRecompiledCode(); + + // Generally unreachable code here ... + } + else + { + pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate ); } #endif } @@ -858,7 +839,7 @@ static void recExitExecution() #if PCSX2_SEH throw Exception::ExitRecExecute(); #else - longjmp( m_SetJmp_StateCheck, SetJmp_Exit ); + longjmp( m_SetJmp_StateCheck, 1 ); #endif } @@ -1319,7 +1300,7 @@ static void __fastcall recRecompile( const u32 startpc ) if (dumplog & 4) iDumpRegisters(startpc, 0); #endif - pxAssert( startpc ); + pxAssume( startpc ); // if recPtr reached the mem limit reset whole mem if ( ( (uptr)recPtr - (uptr)recMem ) >= REC_CACHEMEM-0x40000 || dumplog == 0xffffffff) { @@ -1348,7 +1329,7 @@ static void __fastcall recRecompile( const u32 startpc ) s_pCurBlockEx = recBlocks.New(HWADDR(startpc), (uptr)recPtr); - pxAssert(s_pCurBlockEx); + pxAssume(s_pCurBlockEx); branch = 0; @@ -1359,7 +1340,7 @@ static void __fastcall recRecompile( const u32 startpc ) g_cpuHasConstReg = g_cpuFlushedConstReg = 1; g_cpuPrevRegHasLive1 = g_cpuRegHasLive1 = 0xffffffff; g_cpuPrevRegHasSignExt = g_cpuRegHasSignExt = 0; - pxAssert( g_cpuConstRegs[0].UD[0] == 0 ); + pxAssume( g_cpuConstRegs[0].UD[0] == 0 ); _initX86regs(); _initXMMregs(); diff --git a/pcsx2/x86/microVU.cpp b/pcsx2/x86/microVU.cpp index e7b88c94fa..3818c49e83 100644 --- a/pcsx2/x86/microVU.cpp +++ b/pcsx2/x86/microVU.cpp @@ -67,20 +67,20 @@ const __aligned(32) mVU_Globals mVUglob = { // Micro VU - Main Functions //------------------------------------------------------------------ -microVUt(void) mVUthrowHardwareDeficiency( const wxChar* extFail ) +microVUt(void) mVUthrowHardwareDeficiency( const wxChar* extFail, int vuIndex ) { throw Exception::HardwareDeficiency( - L"microVU init error: SSE1 is not available!", - wxsFormat(_("%s Extensions not found. microVU requires a host CPU with MMX, SSE, and SSE2 extensions."), L"SSE" ) + wxsFormat( L"microVU%d recompiler init failed: %s is not available.", vuIndex, extFail), + wxsFormat(_("%s Extensions not found. microVU requires a host CPU with MMX, SSE, and SSE2 extensions."), extFail ) ); } // Only run this once per VU! ;) microVUt(void) mVUinit(VURegs* vuRegsPtr, int vuIndex) { - if(!x86caps.hasMultimediaExtensions) mVUthrowHardwareDeficiency( L"MMX" ); - if(!x86caps.hasStreamingSIMDExtensions) mVUthrowHardwareDeficiency( L"SSE" ); - if(!x86caps.hasStreamingSIMD2Extensions) mVUthrowHardwareDeficiency( L"SSE2" ); + if(!x86caps.hasMultimediaExtensions) mVUthrowHardwareDeficiency( L"MMX", vuIndex ); + if(!x86caps.hasStreamingSIMDExtensions) mVUthrowHardwareDeficiency( L"SSE", vuIndex ); + if(!x86caps.hasStreamingSIMD2Extensions) mVUthrowHardwareDeficiency( L"SSE2", vuIndex ); microVU* mVU = mVUx; memset(&mVU->prog, 0, sizeof(mVU->prog)); @@ -182,17 +182,19 @@ microVUt(void) mVUclear(mV, u32 addr, u32 size) { // Clears program data microVUf(void) mVUclearProg(int progIndex) { microVU* mVU = mVUx; - mVUprogI.used = 0; - mVUprogI.isDead = 1; - mVUprogI.isOld = 1; - mVUprogI.frame = mVU->prog.curFrame; - for (int j = 0; j <= mVUprogI.ranges.max; j++) { - mVUprogI.ranges.range[j][0] = -1; // Set range to - mVUprogI.ranges.range[j][1] = -1; // indeterminable status - mVUprogI.ranges.total = -1; + microProgram& program = mVU->prog.prog[progIndex]; + + program.used = 0; + program.isDead = 1; + program.isOld = 1; + program.frame = mVU->prog.curFrame; + for (int j = 0; j <= program.ranges.max; j++) { + program.ranges.range[j][0] = -1; // Set range to + program.ranges.range[j][1] = -1; // indeterminable status + program.ranges.total = -1; } for (u32 i = 0; i < (mVU->progSize / 2); i++) { - safe_delete(mVUprogI.block[i]); + safe_delete(program.block[i]); } } @@ -322,6 +324,9 @@ microVUf(int) mVUsearchProg() { return 1; // If !cleared, then we're still on the same program as last-time ;) } +static u32 mvu0_allocated = 0; +static u32 mvu1_allocated = 0; + // -------------------------------------------------------------------------------------- // recMicroVU0 // -------------------------------------------------------------------------------------- @@ -331,13 +336,21 @@ recMicroVU0::recMicroVU0() { } void recMicroVU0::Allocate() { - if( AtomicIncrement( m_AllocCount ) == 0 ) - mVUinit( &VU0, 0 ); + if( m_AllocCount == 0 ) + { + ++m_AllocCount; + if( AtomicExchange( mvu0_allocated, 1 ) == 0 ) + mVUinit( &VU0, 0 ); + } } void recMicroVU0::Shutdown() throw() { - if( AtomicDecrement( m_AllocCount ) == 0 ) - mVUclose(µVU0); + if( m_AllocCount > 0 ) + { + --m_AllocCount; + if( AtomicExchange( mvu0_allocated, 0 ) == 1 ) + mVUclose(µVU0); + } } void recMicroVU0::Reset() { @@ -356,7 +369,7 @@ void recMicroVU0::ExecuteBlock() { } void recMicroVU0::Clear(u32 addr, u32 size) { - pxAssert( m_AllocCount ); // please allocate me first! :| + pxAssert( mvu0_allocated ); // please allocate me first! :| mVUclear(µVU0, addr, size); } @@ -367,32 +380,35 @@ void recMicroVU0::Vsync() throw() { // -------------------------------------------------------------------------------------- // recMicroVU1 // -------------------------------------------------------------------------------------- -recMicroVU1::recMicroVU1() -{ +recMicroVU1::recMicroVU1() { IsInterpreter = false; } -void recMicroVU1::Allocate() -{ - if( AtomicIncrement( m_AllocCount ) == 0 ) - mVUinit( &VU1, 1 ); +void recMicroVU1::Allocate() { + if( m_AllocCount == 0 ) + { + ++m_AllocCount; + if( AtomicExchange( mvu1_allocated, 1 ) == 0 ) + mVUinit( &VU1, 1 ); + } } -void recMicroVU1::Shutdown() throw() -{ - if( AtomicDecrement( m_AllocCount ) == 0 ) - mVUclose(µVU1); +void recMicroVU1::Shutdown() throw() { + if( m_AllocCount > 0 ) + { + --m_AllocCount; + if( AtomicExchange( mvu1_allocated, 0 ) == 1 ) + mVUclose(µVU1); + } } -void recMicroVU1::Reset() -{ +void recMicroVU1::Reset() { if( !pxAssertDev( m_AllocCount, "MicroVU1 CPU Provider has not been allocated prior to reset!" ) ) return; mVUreset(µVU1); } -void recMicroVU1::ExecuteBlock() -{ - pxAssert( m_AllocCount ); // please allocate me first! :| +void recMicroVU1::ExecuteBlock() { + pxAssert( mvu1_allocated ); // please allocate me first! :| if ((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0) return; pxAssert( (VU1.VI[REG_TPC].UL&7) == 0 ); @@ -402,8 +418,7 @@ void recMicroVU1::ExecuteBlock() XMMRegisters::Thaw(); } -void recMicroVU1::Clear(u32 addr, u32 size) -{ +void recMicroVU1::Clear(u32 addr, u32 size) { pxAssert( m_AllocCount ); // please allocate me first! :| mVUclear(µVU1, addr, size); } diff --git a/pcsx2/x86/microVU_Misc.h b/pcsx2/x86/microVU_Misc.h index 1177cad6f0..bf3782970a 100644 --- a/pcsx2/x86/microVU_Misc.h +++ b/pcsx2/x86/microVU_Misc.h @@ -121,8 +121,8 @@ extern const __aligned(32) mVU_Globals mVUglob; // Function/Template Stuff #define mVUx (vuIndex ? µVU1 : µVU0) #define mVUop(opName) static void opName (mP) -#define microVUr(aType) __recInline aType -#define microVUt(aType) __forceinline aType +#define microVUr(aType) static __recInline aType +#define microVUt(aType) static __forceinline aType #define microVUx(aType) template<int vuIndex> aType #define microVUf(aType) template<int vuIndex> __forceinline aType