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
This commit is contained in:
Jake.Stine 2009-12-14 12:18:55 +00:00
parent cc65a5cd02
commit c17455c702
71 changed files with 1996 additions and 1118 deletions

View File

@ -126,6 +126,13 @@
# endif # endif
#endif #endif
#ifdef PCSX2_DEBUG
# define pxDebugCode(code) code
#else
# define pxDebugCode(code)
#endif
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// __aligned / __aligned16 / __pagealigned // __aligned / __aligned16 / __pagealigned
// //
@ -201,8 +208,8 @@ static const int __pagesize = PCSX2_PAGESIZE;
// Don't know if there are Visual C++ equivalents of these. // Don't know if there are Visual C++ equivalents of these.
# define __hot # define __hot
# define __cold # define __cold
# define likely(x) x # define likely(x) (!!(x))
# define unlikely(x) x # define unlikely(x) (!!(x))
# define CALLBACK __stdcall # define CALLBACK __stdcall

View File

@ -15,60 +15,136 @@
#pragma once #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 // Standard "nothrow" assertions. All assertions act as valid conditional statements that
// return the result of the specified conditional; useful for handling failed assertions in // return the result of the specified conditional; useful for handling failed assertions in
// a "graceful" fashion when utilizing the "ignore" feature of assertion debugging. // 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 // All macros return TRUE if the assertion succeeds, or FALSE if the assertion failed
// builds. If using assertions as part of a conditional, the conditional code *will* not // (thus matching the condition of the assertion itself).
// be optimized out, so use conditionals with caution.
// //
// pxAssertDev is an assertion tool for Devel builds, intended for sanity checking and/or // 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 // bounds checking variables in areas which are not performance critical. Another common
// use is for checking thread affinity on utility functions. // use is for checking thread affinity on utility functions.
// //
// Credits Notes: These macros are based on a combination of wxASSERT, MSVCRT's assert // Credits: These macros are based on a combination of wxASSERT, MSVCRT's assert and the
// and the ATL's Assertion/Assumption macros. the best of all worlds! // 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) #if defined(PCSX2_DEBUG)
# define pxAssertMsg(cond, msg) ( (!!(cond)) || \ # define pxAssertMsg(cond, msg) pxAssertRel(cond, msg)
(pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), likely(cond)) ) # 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 pxFail(msg) pxAssumeMsg(false, msg)
# define pxFailDev(msg) pxAssertDev(false, msg) # define pxFailDev(msg) pxAssumeDev(false, msg)
#elif defined(PCSX2_DEVBUILD) #elif defined(PCSX2_DEVBUILD)
// Devel builds use __assume for standard assertions and call pxOnAssertDevel // Devel builds use __assume for standard assertions and call pxOnAssertDevel
// for AssertDev brand assertions (which typically throws a LogicError exception). // 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)) || \ # define pxAssume(cond) (__assume(cond))
(pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), likely(cond)) ) # define pxAssumeDev(cond, msg) pxAssumeMsg(cond, msg)
# define pxFail(msg) (__assume(false), false) # define pxFail(msg) (__assume(false))
# define pxFailDev(msg ) pxAssertDev(false, msg) # define pxFailDev(msg) pxAssumeDev(false, msg)
#else #else
// Release Builds just use __assume as an optimization, and return the conditional // Release Builds just use __assume as an optimization, and return the conditional
// as a result (which is optimized to nil if unused). // as a result (which is optimized to nil if unused).
# define pxAssertMsg(cond, msg) (__assume(cond), likely(cond)) # define pxAssertMsg(cond, msg) (likely(cond))
# define pxAssertDev(cond, msg) (__assume(cond), likely(cond)) # define pxAssertDev(cond, msg) (likely(cond))
# define pxFail(msg) (__assume(false), false)
# define pxFailDev(msg) (__assume(false), false) # define pxAssume(cond) (__assume(cond))
# define pxAssumeDev(cond, msg) (__assume(cond))
# define pxFail(msg) (__assume(false))
# define pxFailDev(msg) (__assume(false))
#endif #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. // 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 // 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) ) ) 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 DiagnosticOrigin& origin, const wxChar* msg=NULL );
extern void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const char* msg); extern void pxOnAssert( const DiagnosticOrigin& origin, const char* msg );
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// jNO_DEFAULT -- disables the default case in a switch, which improves switch optimization // 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 // 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. // that it will not generate any code, LUTs, or conditionals to handle it.
// //
// * In debug builds the default case will cause an assertion. // * In debug/devel 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).
// //
#ifndef jNO_DEFAULT #ifndef jNO_DEFAULT
#if defined(__cplusplus) && defined(PCSX2_DEVBUILD) #if defined(__cplusplus) && defined(PCSX2_DEVBUILD)
# define jNO_DEFAULT \ # define jNO_DEFAULT \
default: \ default: \
{ \ { \
pxFailDev( "Incorrect usage of jNO_DEFAULT detected (default case is not unreachable!)" ); \ pxFailDev( "Incorrect usage of jNO_DEFAULT detected (default case is not unreachable!)" ); \
break; \ break; \
} }
#else #else
# define jNO_DEFAULT \ # define jNO_DEFAULT \
default: \ default: \
{ \ { \
jASSUME(0); \ jASSUME(0); \
break; \ break; \
} }
#endif #endif
#endif #endif

View File

@ -38,11 +38,7 @@
Console.Error( ex.what() ); \ Console.Error( ex.what() ); \
} }
#ifdef __GNUG__ #define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __pxFUNCTION__ )
# define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __PRETTY_FUNCTION__ )
#else
# define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __FUNCTION__ )
#endif
namespace Exception namespace Exception
{ {
@ -56,7 +52,7 @@ namespace Exception
// catch clause can optionally modify them and then re-throw to a top-level handler. // 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. // 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, // Because exceptions are the (only!) really useful example of multiple inheritance,
// this class has only a trivial constructor, and must be manually initialized using // 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 ); } 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 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.") ) 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 // 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") ) 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 class ParseError : public RuntimeError
{ {
public: public:

View File

@ -32,6 +32,9 @@ class wxTimeSpan;
namespace Threading namespace Threading
{ {
class PersistentThread; class PersistentThread;
PersistentThread* pxGetCurrentThread();
wxString pxGetCurrentThreadName();
} }
namespace Exception namespace Exception
@ -346,12 +349,11 @@ namespace Threading
DeclareNoncopyableObject(PersistentThread); DeclareNoncopyableObject(PersistentThread);
protected: protected:
typedef int (*PlainJoeFP)();
wxString m_name; // diagnostic name for our thread. wxString m_name; // diagnostic name for our thread.
pthread_t m_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 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. 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 Start();
virtual void Cancel( bool isBlocking = true ); virtual void Cancel( bool isBlocking = true );
virtual bool Cancel( const wxTimeSpan& timeout );
virtual bool Detach(); virtual bool Detach();
virtual void Block(); virtual void Block();
virtual void RethrowException() const; virtual void RethrowException() const;
@ -419,12 +422,13 @@ namespace Threading
void FrankenMutex( Mutex& mutex ); void FrankenMutex( Mutex& mutex );
bool AffinityAssert_AllowFromSelf() const; bool AffinityAssert_AllowFromSelf( const DiagnosticOrigin& origin ) const;
bool AffinityAssert_DisallowFromSelf() const; bool AffinityAssert_DisallowFromSelf( const DiagnosticOrigin& origin ) const;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Section of methods for internal use only. // Section of methods for internal use only.
bool _basecancel();
void _selfRunningTest( const wxChar* name ) const; void _selfRunningTest( const wxChar* name ) const;
void _DoSetThreadName( const wxString& name ); void _DoSetThreadName( const wxString& name );
void _DoSetThreadName( const char* name ); void _DoSetThreadName( const char* name );

View File

@ -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 ); extern void operator+=( wxSizer& target, pxCheckBox& src );
extern void operator+=( wxSizer* target, pxCheckBox& src );
template<> template<>
inline void operator+=( wxSizer& target, const pxWindowAndFlags<pxCheckBox>& src ) inline void operator+=( wxSizer& target, const pxWindowAndFlags<pxCheckBox>& src )
{ {
target.Add( src.window, src.flags ); target.Add( src.window, src.flags );
} }
template<>
inline void operator+=( wxSizer* target, const pxWindowAndFlags<pxCheckBox>& src )
{
target->Add( src.window, src.flags );
}

View File

@ -111,7 +111,8 @@ public:
pxRadioPanel& SetToolTip( int idx, const wxString& tip ); pxRadioPanel& SetToolTip( int idx, const wxString& tip );
pxRadioPanel& SetSelection( int idx ); pxRadioPanel& SetSelection( int idx );
pxRadioPanel& SetDefault( int idx ); pxRadioPanel& SetDefaultItem( int idx );
pxRadioPanel& EnableItem( int idx, bool enable=true );
int GetSelection() const; int GetSelection() const;
wxWindowID GetSelectionId() const; wxWindowID GetSelectionId() const;

View File

@ -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 ); extern void operator+=( wxSizer& target, pxStaticText& src );
extern void operator+=( wxSizer* target, pxStaticText& src );
template<> template<>
inline void operator+=( wxSizer& target, const pxWindowAndFlags<pxStaticText>& src ) 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 ); //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 // pxStaticHeading
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -52,15 +52,25 @@ struct pxAlignmentType
wxSizerFlags Apply( wxSizerFlags flags=wxSizerFlags() ) const; 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 operator wxSizerFlags() const
{ {
return Apply(); return Apply();
} }
wxSizerFlags operator | ( const wxSizerFlags& _flgs )
{
return Apply( _flgs );
}
}; };
struct pxStretchType struct pxStretchType
@ -78,15 +88,47 @@ struct pxStretchType
wxSizerFlags Apply( wxSizerFlags flags=wxSizerFlags() ) const; 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 operator wxSizerFlags() const
{ {
return Apply(); 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 ); return Apply( _flgs );
} }
operator wxSizerFlags() const
{
return Apply();
}
}; };
extern const pxAlignmentType 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, const wxSizerFlags& _flgs2 );
//extern wxSizerFlags operator , ( const wxSizerFlags& _flgs, pxStretchType stretch );
template< typename WinType > template< typename WinType >
pxWindowAndFlags<WinType> operator | ( WinType* _win, const wxSizerFlags& _flgs ) 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, wxWindow& src );
extern void operator+=( wxSizer& target, wxSizer& 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+=( wxSizer& target, int spacer );
extern void operator+=( wxWindow& target, int spacer ); extern void operator+=( wxWindow& target, int spacer );
// ----------------------------------------------------------------------------
// Important: This template is needed in order to retain window type information and // 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 // invoke the proper overloaded version of += (which is used by pxStaticText and other
// classes to perform special actions when added to sizers). // classes to perform special actions when added to sizers).
@ -178,6 +223,14 @@ void operator+=( wxWindow& target, WinType* src )
template< typename WinType > template< typename WinType >
void operator+=( wxWindow& target, WinType& src ) 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; *target.GetSizer() += src;
} }
@ -187,11 +240,31 @@ void operator+=( wxSizer& target, const pxWindowAndFlags<WinType>& src )
target.Add( src.window, src.flags ); 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 > template< typename WinType >
void operator+=( wxWindow& target, const pxWindowAndFlags<WinType>& src ) void operator+=( wxWindow* target, WinType& src )
{ {
if( !pxAssert( target.GetSizer() != NULL ) ) return; if( !pxAssert( target != NULL ) ) return;
*target.GetSizer() += src; 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: public:
wxDialogWithHelpers(); 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(); virtual ~wxDialogWithHelpers() throw();
void AddOkCancel( wxSizer& sizer, bool hasApply=false ); void Init();
pxStaticText* Text( const wxString& label ); void AddOkCancel( wxSizer& sizer, bool hasApply=false );
pxStaticHeading* Heading( const wxString& label );
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; } int GetIdealWidth() const { return m_idealWidth; }
bool HasIdealWidth() const { return m_idealWidth != wxDefaultCoord; } bool HasIdealWidth() const { return m_idealWidth != wxDefaultCoord; }
protected: protected:
void OnActivate(wxActivateEvent& evt); 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 bool pxIsValidWindowPosition( const wxWindow& window, const wxPoint& windowPos );
extern wxRect wxGetDisplayArea(); 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 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 #endif

View File

@ -40,7 +40,7 @@ struct x86CPU_INFO
u32 PhysicalCores; u32 PhysicalCores;
u32 LogicalCores; u32 LogicalCores;
char VendorName[16]; // Vendor/Creator ID char VendorName[16]; // Vendor/Creator ID
char TypeName[20]; // cpu type char TypeName[20]; // cpu type
char FamilyName[50]; // the original cpu name char FamilyName[50]; // the original cpu name

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include <wx/app.h> #include <wx/app.h>
#include "Threading.h"
wxString GetEnglish( const char* msg ) 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. // That's ok. What we don't want is the *same* thread recurse-asserting.
static __threadlocal int s_assert_guard = 0; 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) pxDoAssertFnType* pxDoAssert = pxAssertImpl_LogIt;
{
#ifdef PCSX2_DEVBUILD
RecursionGuard guard( s_assert_guard );
if( guard.IsReentrant() ) return;
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. // Note: Format uses MSVC's syntax for output window hotlinking.
wxLogError( wxsFormat( L"%s(%d): Assertion failed in %s: %s\n", trapit = pxAssertImpl_LogIt( origin, msg );
file, line, fromUTF8(func).c_str(), msg )
);
} }
else else
{ {
#ifdef __WXDEBUG__ trapit = pxDoAssert( origin, msg );
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
} }
#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) );
} }

View File

@ -37,6 +37,61 @@ const wxTimeSpan Threading::def_yieldgui_interval( 0, 0, 0, 100 );
// three second interval for deadlock protection on waitgui. // three second interval for deadlock protection on waitgui.
const wxTimeSpan Threading::def_deadlock_timeout( 0, 0, 3, 0 ); 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) // (intended for internal use only)
// Returns true if the Wait is recursive, or false if the Wait is safe and should be // Returns true if the Wait is recursive, or false if the Wait is safe and should be
// handled via normal yielding methods. // handled via normal yielding methods.
@ -81,7 +136,7 @@ Threading::PersistentThread::PersistentThread()
// This destructor performs basic "last chance" cleanup, which is a blocking join // This destructor performs basic "last chance" cleanup, which is a blocking join
// against the thread. Extending classes should almost always implement their own // against the thread. Extending classes should almost always implement their own
// thread closure process, since any PersistentThread will, by design, not terminate // 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 // 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. // like marrying your sister, and then cheating on her with your daughter.
@ -116,14 +171,24 @@ Threading::PersistentThread::~PersistentThread() throw()
DESTRUCTOR_CATCHALL 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 ) void Threading::PersistentThread::FrankenMutex( Mutex& mutex )
@ -157,7 +222,29 @@ void Threading::PersistentThread::Start()
m_except = NULL; m_except = NULL;
if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 ) 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 // 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. // This function should not be called from the owner thread.
bool Threading::PersistentThread::Detach() bool Threading::PersistentThread::Detach()
{ {
AffinityAssert_DisallowFromSelf(); AffinityAssert_DisallowFromSelf(pxDiagSpot);
if( _InterlockedExchange( &m_detached, true ) ) return false; if( _InterlockedExchange( &m_detached, true ) ) return false;
pthread_detach( m_thread ); pthread_detach( m_thread );
return true; 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: // Remarks:
// Provision of non-blocking Cancel() is probably academic, since destroying a PersistentThread // 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() // object performs a blocking Cancel regardless of if you explicitly do a non-blocking Cancel()
@ -183,33 +285,40 @@ bool Threading::PersistentThread::Detach()
// Parameters: // Parameters:
// isBlocking - indicates if the Cancel action should block for thread completion or not. // 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 ) void Threading::PersistentThread::Cancel( bool isBlocking )
{ {
AffinityAssert_DisallowFromSelf(); AffinityAssert_DisallowFromSelf( pxDiagSpot );
{ // Prevent simultaneous startup and cancel, necessary to avoid
// Prevent simultaneous startup and cancel: ScopedLock startlock( m_lock_start );
ScopedLock startlock( m_lock_start );
if( !m_running ) return;
if( m_detached ) if( !_basecancel() ) return;
{
Console.Warning( "(Thread Warning) Ignoring attempted cancellation of detached thread." );
return;
}
pthread_cancel( m_thread );
}
if( isBlocking ) if( isBlocking )
{ {
// FIXME: Add deadlock detection and handling here... ? WaitOnSelf( m_lock_InThread );
m_lock_InThread.Wait();
Detach(); 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 // 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 // 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 // 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. // Returns the return code of the thread.
// This method is roughly the equivalent of pthread_join(). // 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() void Threading::PersistentThread::Block()
{ {
AffinityAssert_DisallowFromSelf(); AffinityAssert_DisallowFromSelf(pxDiagSpot);
m_lock_InThread.Wait(); WaitOnSelf( m_lock_InThread );
} }
bool Threading::PersistentThread::IsSelf() const bool Threading::PersistentThread::IsSelf() const
@ -277,7 +388,7 @@ void Threading::PersistentThread::_selfRunningTest( const wxChar* name ) const
// //
void Threading::PersistentThread::WaitOnSelf( Semaphore& sem ) const void Threading::PersistentThread::WaitOnSelf( Semaphore& sem ) const
{ {
if( !AffinityAssert_DisallowFromSelf() ) return; if( !AffinityAssert_DisallowFromSelf(pxDiagSpot) ) return;
while( true ) while( true )
{ {
@ -301,7 +412,7 @@ void Threading::PersistentThread::WaitOnSelf( Semaphore& sem ) const
// //
void Threading::PersistentThread::WaitOnSelf( Mutex& mutex ) const void Threading::PersistentThread::WaitOnSelf( Mutex& mutex ) const
{ {
if( !AffinityAssert_DisallowFromSelf() ) return; if( !AffinityAssert_DisallowFromSelf(pxDiagSpot) ) return;
while( true ) while( true )
{ {
@ -314,7 +425,7 @@ static const wxTimeSpan SelfWaitInterval( 0,0,0,333 );
bool Threading::PersistentThread::WaitOnSelf( Semaphore& sem, const wxTimeSpan& timeout ) const bool Threading::PersistentThread::WaitOnSelf( Semaphore& sem, const wxTimeSpan& timeout ) const
{ {
if( !AffinityAssert_DisallowFromSelf() ) return true; if( !AffinityAssert_DisallowFromSelf(pxDiagSpot) ) return true;
wxTimeSpan runningout( timeout ); 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 bool Threading::PersistentThread::WaitOnSelf( Mutex& mutex, const wxTimeSpan& timeout ) const
{ {
if( !AffinityAssert_DisallowFromSelf() ) return true; if( !AffinityAssert_DisallowFromSelf(pxDiagSpot) ) return true;
wxTimeSpan runningout( timeout ); 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). // and cleanup, or use the DoThreadCleanup() override to perform resource cleanup).
void Threading::PersistentThread::TestCancel() const void Threading::PersistentThread::TestCancel() const
{ {
AffinityAssert_AllowFromSelf(); AffinityAssert_AllowFromSelf(pxDiagSpot);
pthread_testcancel(); pthread_testcancel();
} }
@ -393,24 +504,16 @@ void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::*
} }
#ifndef PCSX2_DEVBUILD #ifndef PCSX2_DEVBUILD
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Allow logic errors to propagate out of the thread in release builds, so that they might be // Bleh... don't bother with std::exception. runtime_error should catch anything
// handled in non-fatal ways. On Devbuilds let them loose, so that they produce debug stack // useful coming out of the core STL libraries anyway, and these are best handled by
// traces and such. // the MSVC debugger (or by silent random annoying fail on debug-less linux).
catch( std::logic_error& ex ) /*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() ) GetName().c_str(), fromUTF8( ex.what() ).c_str() )
); );
} }
catch( Exception::LogicError& ex ) catch( std::exception& 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 )
{ {
throw Exception::BaseException( wxsFormat( L"(thread: %s) STL exception: %s\n\t%s", throw Exception::BaseException( wxsFormat( L"(thread: %s) STL exception: %s\n\t%s",
GetName().c_str(), fromUTF8( ex.what() ).c_str() ) 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. // OnCleanupInThread() to extend cleanup functionality.
void Threading::PersistentThread::_ThreadCleanup() void Threading::PersistentThread::_ThreadCleanup()
{ {
AffinityAssert_AllowFromSelf(); AffinityAssert_AllowFromSelf(pxDiagSpot);
_try_virtual_invoke( &PersistentThread::OnCleanupInThread ); _try_virtual_invoke( &PersistentThread::OnCleanupInThread );
m_lock_InThread.Release(); 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 // 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 // calling ExecuteTaskInThread, and after the initial InThread lock has been claimed.
// from PersistentThread, giving them the ability to bind startup code to all threads that // This code is also executed within a "safe" environment, where the creating thread is
// derive from them. (the alternative would have been to make ExecuteTaskInThread a // blocked against m_sem_event. Make sure to do any necessary variable setup here, without
// private member, and provide a new Task executor by a different name). // worry that the calling thread might attempt to test the status of those variables
// before initialization has completed.
//
void Threading::PersistentThread::OnStartInThread() void Threading::PersistentThread::OnStartInThread()
{ {
m_running = true;
m_detached = false; m_detached = false;
m_running = true;
} }
void Threading::PersistentThread::_internal_execute() void Threading::PersistentThread::_internal_execute()
{ {
m_lock_InThread.Acquire(); m_lock_InThread.Acquire();
OnStartInThread();
_DoSetThreadName( m_name ); _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 ); _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() void Threading::PersistentThread::OnStart()
{ {
FrankenMutex( m_lock_InThread ); FrankenMutex( m_lock_InThread );
m_sem_event.Reset(); 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() void Threading::PersistentThread::OnCleanupInThread()
{ {
m_running = false; 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 // passed into pthread_create, and is used to dispatch the thread's object oriented
// callback function // callback function
void* Threading::PersistentThread::_internal_callback( void* itsme ) void* Threading::PersistentThread::_internal_callback( void* itsme )
{ {
pxAssert( itsme != NULL ); if( !pxAssertDev( itsme != NULL, wxNullChar ) ) return NULL;
PersistentThread& owner = *((PersistentThread*)itsme); PersistentThread& owner = *((PersistentThread*)itsme);
pthread_cleanup_push( _pt_callback_cleanup, 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 ) void Threading::PersistentThread::_DoSetThreadName( const char* name )
{ {
if( !AffinityAssert_AllowFromSelf() ) return;
// This feature needs Windows headers and MSVC's SEH support: // This feature needs Windows headers and MSVC's SEH support:
#if defined(_WINDOWS_) && defined (_MSC_VER) #if defined(_WINDOWS_) && defined (_MSC_VER)

View File

@ -81,3 +81,8 @@ void operator+=( wxSizer& target, pxCheckBox& src )
{ {
target.Add( &src, wxSF.Expand() ); target.Add( &src, wxSF.Expand() );
} }
void operator+=( wxSizer* target, pxCheckBox& src )
{
target->Add( &src, wxSF.Expand() );
}

View File

@ -149,13 +149,13 @@ void pxRadioPanel::_RealizeDefaultOption()
} }
} }
pxRadioPanel& pxRadioPanel::SetDefault( int idx ) pxRadioPanel& pxRadioPanel::SetDefaultItem( int idx )
{ {
if( idx == m_DefaultIdx ) return *this; if( idx == m_DefaultIdx ) return *this;
if( m_IsRealized && m_DefaultIdx != -1 ) if( m_IsRealized && m_DefaultIdx != -1 )
{ {
wxFont def( GetFont() ); wxFont def(GetFont());
m_objects[m_DefaultIdx].LabelObj->SetFont( def ); m_objects[m_DefaultIdx].LabelObj->SetFont( def );
m_objects[m_DefaultIdx].LabelObj->SetForegroundColour( GetForegroundColour() ); m_objects[m_DefaultIdx].LabelObj->SetForegroundColour( GetForegroundColour() );
} }
@ -166,6 +166,20 @@ pxRadioPanel& pxRadioPanel::SetDefault( int idx )
return *this; 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 int pxRadioPanel::GetSelection() const
{ {
if( !VerifyRealizedState() ) return 0; if( !VerifyRealizedState() ) return 0;

View File

@ -131,3 +131,8 @@ void operator+=( wxSizer& target, pxStaticText& src )
{ {
src.AddTo( target ); src.AddTo( target );
} }
void operator+=( wxSizer* target, pxStaticText& src )
{
src.AddTo( target );
}

View File

@ -99,7 +99,12 @@ wxSizerFlags pxStretchType::Apply( wxSizerFlags flags ) const
return flags; 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 ); //return align.Apply( _flgs );
wxSizerFlags retval; wxSizerFlags retval;
@ -118,11 +123,8 @@ wxSizerFlags operator , ( const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2
return retval; return retval;
} }
/*wxSizerFlags operator | ( const wxSizerFlags& _flgs, pxStretchType stretch ) // ----------------------------------------------------------------------------
{ // Reference/Handle versions!
return stretch.Apply( _flgs );
}*/
void operator+=( wxSizer& target, wxWindow* src ) void operator+=( wxSizer& target, wxWindow* src )
{ {
@ -148,6 +150,23 @@ void operator+=( wxSizer& target, int spacer )
{ {
target.AddSpacer( 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 ) void operator+=( wxWindow& target, int spacer )
{ {
@ -431,3 +450,14 @@ void pxSetToolTip( wxWindow& wind, const wxString& src )
{ {
pxSetToolTip( &wind, 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
);
}

View File

@ -28,13 +28,9 @@ using namespace pxSizerFlags;
// wxDialogWithHelpers Class Implementations // wxDialogWithHelpers Class Implementations
// ===================================================================================================== // =====================================================================================================
HashTools::HashMap< wxWindowID, int > m_DialogIdents( 0, wxID_ANY ); bool pxDialogExists( const wxString& name )
bool pxDialogExists( wxWindowID id )
{ {
int dest = 0; return wxFindWindowByName( name ) != NULL;
m_DialogIdents.TryGetValue( id, dest );
return (dest > 0);
} }
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -44,20 +40,41 @@ IMPLEMENT_DYNAMIC_CLASS(wxDialogWithHelpers, wxDialog)
wxDialogWithHelpers::wxDialogWithHelpers() wxDialogWithHelpers::wxDialogWithHelpers()
{ {
m_idealWidth = wxDefaultCoord;
m_hasContextHelp = false; m_hasContextHelp = false;
m_extraButtonSizer = NULL; m_extraButtonSizer = NULL;
Init();
} }
wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, int id, const wxString& title, bool hasContextHelp, const wxPoint& pos, const wxSize& size ) wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, const wxString& title, bool hasContextHelp, bool resizable )
: wxDialog( parent, id, title, pos, size , wxDEFAULT_DIALOG_STYLE) //, (wxCAPTION | wxMAXIMIZE | wxCLOSE_BOX | wxRESIZE_BORDER) ), // flags for resizable dialogs, currently unused. : 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_idealWidth = wxDefaultCoord;
m_extraButtonSizer = NULL; m_extraButtonSizer = NULL;
m_hasContextHelp = hasContextHelp;
if( m_hasContextHelp ) if( m_hasContextHelp )
delete wxHelpProvider::Set( new wxSimpleHelpProvider() ); 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!) // 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 // 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) // didn't do any good. (problem could also be my Co-Linux / x-window manager)
//Connect( wxEVT_ACTIVATE, wxActivateEventHandler(wxDialogWithHelpers::OnActivate) ); //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()]; Fit();
pxAssert( m_DialogIdents[GetId()] >= 0 );
// 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 ) pxStaticText* wxDialogWithHelpers::Text( const wxString& label )
@ -85,6 +142,19 @@ pxStaticHeading* wxDialogWithHelpers::Heading( const wxString& label )
return new pxStaticHeading( this, 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) void wxDialogWithHelpers::OnActivate(wxActivateEvent& evt)
{ {
//evt.Skip(); //evt.Skip();
@ -119,7 +189,7 @@ void wxDialogWithHelpers::AddOkCancel( wxSizer &sizer, bool hasApply )
flex.AddGrowableCol( 1, 15 ); flex.AddGrowableCol( 1, 15 );
flex += m_extraButtonSizer | pxAlignLeft; flex += m_extraButtonSizer | pxAlignLeft;
flex += s_buttons | pxExpand, pxCenter; flex += s_buttons | (pxExpand & pxCenter);
sizer += flex | StdExpand(); sizer += flex | StdExpand();

View File

@ -592,7 +592,7 @@ $memcpy_final:
// (zerofrog) // (zerofrog)
u8 memcmp_mmx(const void* src1, const void* src2, int cmpsize) u8 memcmp_mmx(const void* src1, const void* src2, int cmpsize)
{ {
assert( (cmpsize&7) == 0 ); pxAssert( (cmpsize&7) == 0 );
__asm { __asm {
push esi push esi
@ -766,7 +766,7 @@ End:
// returns the xor of all elements, cmpsize has to be mult of 8 // returns the xor of all elements, cmpsize has to be mult of 8
void memxor_mmx(void* dst, const void* src1, int cmpsize) void memxor_mmx(void* dst, const void* src1, int cmpsize)
{ {
assert( (cmpsize&7) == 0 ); pxAssert( (cmpsize&7) == 0 );
__asm { __asm {
mov ecx, cmpsize mov ecx, cmpsize

View File

@ -745,7 +745,7 @@ __forceinline void cdvdReadInterrupt()
// An arbitrary delay of some number of cycles probably makes more sense here, // 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 // 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); CDVDREAD_INT(cdvd.ReadTime/4);
return; return;
} }

View File

@ -340,7 +340,7 @@ static __forceinline void frameLimit()
static __forceinline void VSyncStart(u32 sCycle) static __forceinline void VSyncStart(u32 sCycle)
{ {
Cpu->CheckExecutionState(); Cpu->CheckExecutionState();
SysCoreThread::Get().VsyncInThread(); GetCoreThread().VsyncInThread();
EECNT_LOG( "///////// EE COUNTER VSYNC START (frame: %6d) \\\\\\\\\\\\\\\\\\\\ ", iFrame ); EECNT_LOG( "///////// EE COUNTER VSYNC START (frame: %6d) \\\\\\\\\\\\\\\\\\\\ ", iFrame );

View File

@ -382,15 +382,16 @@ static void intExecute()
// Mem protection should be handled by the caller here so that it can be // Mem protection should be handled by the caller here so that it can be
// done in a more optimized fashion. // done in a more optimized fashion.
while( true ) try {
{ while( true )
execI(); execI();
} } catch( Exception::ForceDispatcherReg& ) { }
} }
static void intCheckExecutionState() static void intCheckExecutionState()
{ {
SysCoreThread::Get().StateCheckInThread(); if( GetCoreThread().HasPendingStateChangeRequest() )
throw Exception::ForceDispatcherReg();
} }
static void intStep() static void intStep()

View File

@ -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() ); Console.WriteLn( L"Binding %s\t: %s ", tbl_PluginInfo[pid].GetShortname().c_str(), folders[pid].c_str() );
if( folders[pid].IsEmpty() ) if( folders[pid].IsEmpty() )
throw Exception::InvalidArgument( "Empty plugin filename." ); throw Exception::PluginInitError( pi->id, "Empty plugin filename." );
m_info[pid].Filename = folders[pid]; m_info[pid].Filename = folders[pid];
@ -985,6 +985,14 @@ void PluginManager::Close( PluginsEnum_t pid )
void PluginManager::Close( bool closegs ) 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..." ); DbgCon.WriteLn( Color_StrongBlue, "Closing plugins..." );
// Close plugins in reverse order of the initialization procedure. // Close plugins in reverse order of the initialization procedure.

View File

@ -39,7 +39,6 @@ R5900cpu *Cpu = NULL;
bool g_ExecBiosHack = false; // set if the BIOS has already been executed bool g_ExecBiosHack = false; // set if the BIOS has already been executed
static bool cpuIsInitialized = false;
static const uint eeWaitCycles = 3072; static const uint eeWaitCycles = 3072;
bool eeEventTestIsActive = false; bool eeEventTestIsActive = false;
@ -52,10 +51,6 @@ void cpuReset()
if( GetMTGS().IsOpen() ) if( GetMTGS().IsOpen() )
GetMTGS().WaitGS(); // GS better be done processing before we reset the EE, just in case. GetMTGS().WaitGS(); // GS better be done processing before we reset the EE, just in case.
SysClearExecutionCache();
cpuIsInitialized = true;
memReset(); memReset();
psxMemReset(); psxMemReset();
vuMicroMemReset(); vuMicroMemReset();

View File

@ -3,7 +3,7 @@
* *
* PCSX2 is free software: you can redistribute it and/or modify it under the terms * 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- * 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; * 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 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
@ -22,8 +22,11 @@
class _BaseStateThread; class _BaseStateThread;
// Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
static SafeArray<u8> state_buffer; static SafeArray<u8> state_buffer;
_BaseStateThread* current_state_thread = NULL;
// Simple lock boolean for the state buffer being in use by a thread. // Simple lock boolean for the state buffer being in use by a thread.
static NonblockingMutex state_buffer_lock; static NonblockingMutex state_buffer_lock;
@ -56,9 +59,22 @@ class _BaseStateThread : public PersistentThread
protected: protected:
EventListenerBinding<AppEventType> m_bind_OnExit; 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: public:
virtual ~_BaseStateThread() throw() 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; state_buffer_lock.Release(); // just in case;
} }
@ -67,14 +83,18 @@ protected:
m_bind_OnExit( wxGetApp().Source_AppStatus(), EventListener<AppEventType>( this, StateThread_OnAppStatus ) ) m_bind_OnExit( wxGetApp().Source_AppStatus(), EventListener<AppEventType>( this, StateThread_OnAppStatus ) )
{ {
Callback_FreezeFinished = onFinished; 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() void OnStart()
{ {
if( !state_buffer_lock.TryAcquire() ) if( !state_buffer_lock.TryAcquire() )
throw Exception::CancelEvent( m_name + L"request ignored: state copy buffer is already locked!" ); throw Exception::CancelEvent( m_name + L"request ignored: state copy buffer is already locked!" );
current_state_thread = this;
m_isStarted = true;
_parent::OnStart(); _parent::OnStart();
} }
@ -83,6 +103,7 @@ protected:
wxCommandEvent evt( pxEVT_FreezeThreadFinished ); wxCommandEvent evt( pxEVT_FreezeThreadFinished );
evt.SetClientData( this ); evt.SetClientData( this );
evt.SetInt( type ); evt.SetInt( type );
evt.SetExtraLong( m_resume_when_done );
wxGetApp().AddPendingEvent( evt ); wxGetApp().AddPendingEvent( evt );
} }
@ -107,7 +128,7 @@ protected:
{ {
_parent::OnStart(); _parent::OnStart();
++sys_resume_lock; ++sys_resume_lock;
CoreThread.Pause(); m_resume_when_done = CoreThread.Pause();
} }
void ExecuteTaskInThread() 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 // StateThread_ZipToDisk
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -171,14 +155,15 @@ protected:
gzFile m_gzfp; gzFile m_gzfp;
public: public:
StateThread_ZipToDisk( FnType_OnThreadComplete* onFinished, const wxString& file ) : StateThread_ZipToDisk( FnType_OnThreadComplete* onFinished, bool resume_done, const wxString& file )
_BaseStateThread( "ZipToDisk", onFinished ) : _BaseStateThread( "ZipToDisk", onFinished )
, m_filename( file ) , m_filename( file )
, m_gzfp( NULL )
{ {
m_gzfp = NULL;
m_resume_when_done = resume_done;
} }
~StateThread_ZipToDisk() throw() virtual ~StateThread_ZipToDisk() throw()
{ {
if( m_gzfp != NULL ) gzclose( m_gzfp ); if( m_gzfp != NULL ) gzclose( m_gzfp );
} }
@ -232,15 +217,16 @@ protected:
bool m_finished; bool m_finished;
public: public:
StateThread_UnzipFromDisk( FnType_OnThreadComplete* onFinished, const wxString& file ) : StateThread_UnzipFromDisk( FnType_OnThreadComplete* onFinished, bool resume_done, const wxString& file )
_BaseStateThread( "UnzipFromDisk", onFinished ) : _BaseStateThread( "UnzipFromDisk", onFinished )
, m_filename( file ) , m_filename( file )
, m_gzfp( NULL )
, m_finished( false )
{ {
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 ); if( m_gzfp != NULL ) gzclose( m_gzfp );
} }
@ -297,32 +283,17 @@ void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt )
} }
if( fn_tmp != NULL ) fn_tmp( 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 ) CoreThread.RecoverState();
{ if( evt.GetExtraLong() ) CoreThread.Resume();
// 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();
} }
static wxString zip_dest_filename; 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; 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." ); Console.Warning( "Cannot save state to disk: empty filename specified." );
return; return;
} }
// Phase 2: Record to disk!! // 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(); 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 // StateCopy Public Interface
@ -362,8 +325,8 @@ void StateCopy_SaveToFile( const wxString& file )
void StateCopy_LoadFromFile( const wxString& file ) void StateCopy_LoadFromFile( const wxString& file )
{ {
if( state_buffer_lock.IsLocked() ) return; if( state_buffer_lock.IsLocked() ) return;
CoreThread.Pause(); bool resume_when_done = CoreThread.Pause();
(new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start(); (new StateThread_UnzipFromDisk( OnFinished_Resume, resume_when_done, file ))->Start();
} }
// Saves recovery state info to the given saveslot, or saves the active emulation state // 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.WriteLn( Color_StrongGreen, "Loading savestate from slot %d...", slot );
Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() ); Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() );
CoreThread.Pause(); bool resume_when_done = CoreThread.Pause();
(new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start(); (new StateThread_UnzipFromDisk( OnFinished_Resume, resume_when_done, file ))->Start();
} }
bool StateCopy_IsValid() bool StateCopy_IsValid()
{ {
return !state_buffer.IsDisposed(); return !state_buffer.IsDisposed();
} }
const SafeArray<u8>* StateCopy_GetBuffer() const SafeArray<u8>* StateCopy_GetBuffer()
@ -412,20 +375,45 @@ const SafeArray<u8>* StateCopy_GetBuffer()
void StateCopy_FreezeToMem() void StateCopy_FreezeToMem()
{ {
if( state_buffer_lock.IsLocked() ) return; 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; if( state_buffer_lock.TryAcquire() ) return;
new StateThread_Thaw( OnFinished_Restore );
/*
// 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(); memLoadingState( state_buffer ).FreezeAll();
state_buffer.Dispose();
state_buffer_lock.Release(); state_buffer_lock.Release();
} }
@ -439,3 +427,4 @@ bool StateCopy_IsBusy()
{ {
return state_buffer_lock.IsLocked(); return state_buffer_lock.IsLocked();
} }

View File

@ -204,7 +204,9 @@ public:
extern bool StateCopy_IsValid(); extern bool StateCopy_IsValid();
extern void StateCopy_FreezeToMem(); 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_SaveToFile( const wxString& file );
extern void StateCopy_LoadFromFile( const wxString& file ); extern void StateCopy_LoadFromFile( const wxString& file );
extern void StateCopy_SaveToSlot( uint num ); extern void StateCopy_SaveToSlot( uint num );

View File

@ -57,7 +57,7 @@ TraceLogFilters& SetTraceConfig()
// This function should be called once during program execution. // 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, 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)" : "" SVN_REV, SVN_MODS ? "(modded)" : ""
@ -66,8 +66,6 @@ void SysDetect()
Console.WriteLn( "Savestate version: 0x%x", g_SaveVersion); Console.WriteLn( "Savestate version: 0x%x", g_SaveVersion);
Console.Newline(); Console.Newline();
cpudetectInit();
Console.WriteLn( Color_StrongBlack, "x86-32 Init:" ); Console.WriteLn( Color_StrongBlack, "x86-32 Init:" );
Console.Indent().WriteLn( Console.Indent().WriteLn(
@ -149,13 +147,15 @@ CpuInitializer< CpuType >::CpuInitializer()
} }
catch( Exception::RuntimeError& ex ) catch( Exception::RuntimeError& ex )
{ {
Console.Error( L"MicroVU0 Recompiler Allocation Failed:\n" + ex.FormatDiagnosticMessage() ); Console.Error( L"CPU provider error:\n\t" + ex.FormatDiagnosticMessage() );
MyCpu->Shutdown(); if( MyCpu )
MyCpu = NULL;
} }
catch( std::runtime_error& ex ) catch( std::runtime_error& ex )
{ {
Console.Error( L"MicroVU0 Recompiler Allocation Failed (STL Exception)\n\tDetails:" + fromUTF8( ex.what() ) ); Console.Error( L"CPU provider error (STL Exception)\n\tDetails:" + fromUTF8( ex.what() ) );
MyCpu->Shutdown(); if( MyCpu )
MyCpu = NULL;
} }
} }

View File

@ -62,7 +62,7 @@ protected:
// implemented by the provisioning interface. // implemented by the provisioning interface.
extern SysCoreAllocations& GetSysCoreAlloc(); 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! extern void SysClearExecutionCache(); // clears recompiled execution caches!
@ -106,6 +106,7 @@ extern void NTFS_CompressFile( const wxString& file, bool compressStatus=true );
// //
class pxMessageBoxEvent; class pxMessageBoxEvent;
class pxAssertionEvent;
namespace Msgbox 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 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 int Assertion( const wxString& text, const wxString& stacktrace );
extern void Except( const Exception::BaseException& src );
} }
BEGIN_DECLARE_EVENT_TYPES() BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEVT_MSGBOX, -1 ) DECLARE_EVENT_TYPE( pxEVT_MSGBOX, -1 )
DECLARE_EVENT_TYPE( pxEVT_CallStackBox, -1 ) DECLARE_EVENT_TYPE( pxEVT_ASSERTION, -1 )
END_DECLARE_EVENT_TYPES() END_DECLARE_EVENT_TYPES()

View File

@ -56,6 +56,18 @@ void SysCoreThread::Cancel( bool isBlocking )
{ {
m_CoreCancelDamnit = true; m_CoreCancelDamnit = true;
_parent::Cancel(); _parent::Cancel();
ReleaseResumeLock();
}
bool SysCoreThread::Cancel( const wxTimeSpan& span )
{
m_CoreCancelDamnit = true;
if( _parent::Cancel( span ) )
{
ReleaseResumeLock();
return true;
}
return false;
} }
void SysCoreThread::Start() void SysCoreThread::Start()
@ -78,21 +90,26 @@ void SysCoreThread::Start()
void SysCoreThread::OnResumeReady() void SysCoreThread::OnResumeReady()
{ {
if( m_resetVirtualMachine ) if( m_resetVirtualMachine )
{ m_hasValidState = false;
cpuReset();
m_resetVirtualMachine = false;
m_hasValidState = false;
}
if( !m_hasValidState ) if( !m_hasValidState )
m_resetRecompilers = true; 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() void SysCoreThread::Reset()
{ {
Suspend(); Suspend();
m_resetVirtualMachine = true; m_resetVirtualMachine = true;
m_hasValidState = false; m_hasValidState = false;
} }
// This function *will* reset the emulator in order to allow the specified elf file to // 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; 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_resetProfilers = ( src.Profiler != EmuConfig.Profiler );
m_resetVsyncTimers = ( src.GS != EmuConfig.GS ); m_resetVsyncTimers = ( src.GS != EmuConfig.GS );
@ -213,10 +231,50 @@ struct ScopedBool_ClearOnError
void Success() { m_success = true; } 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() void SysCoreThread::CpuInitializeMess()
{ {
if( m_hasValidState ) return; 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(); _reset_stuff_as_needed();
ScopedBool_ClearOnError sbcoe( m_hasValidState ); ScopedBool_ClearOnError sbcoe( m_hasValidState );
@ -262,24 +320,6 @@ void SysCoreThread::CpuInitializeMess()
sbcoe.Success(); 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 // Called by the VsyncInThread() if a valid keyEvent is pending and is unhandled by other
// PS2 core plugins. // PS2 core plugins.
void SysCoreThread::DispatchKeyEventToUI( const keyEvent& evt ) void SysCoreThread::DispatchKeyEventToUI( const keyEvent& evt )
@ -315,10 +355,11 @@ void SysCoreThread::StateCheckInThread()
{ {
GetMTGS().RethrowException(); GetMTGS().RethrowException();
_parent::StateCheckInThread(); _parent::StateCheckInThread();
if( !m_hasValidState ) if( !m_hasValidState )
throw Exception::RuntimeError( "Invalid emulation state detected; Virtual machine threads have been cancelled." ); 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() void SysCoreThread::ExecuteTaskInThread()
@ -330,9 +371,10 @@ void SysCoreThread::ExecuteTaskInThread()
m_mxcsr_saved.bitmask = _mm_getcsr(); m_mxcsr_saved.bitmask = _mm_getcsr();
PCSX2_PAGEFAULT_PROTECT { PCSX2_PAGEFAULT_PROTECT {
StateCheckInThread(); do {
SetCPUState( EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVUMXCSR ); StateCheckInThread();
Cpu->Execute(); Cpu->Execute();
} while( true );
} PCSX2_PAGEFAULT_EXCEPT; } PCSX2_PAGEFAULT_EXCEPT;
} }
@ -347,8 +389,7 @@ void SysCoreThread::OnResumeInThread( bool isSuspended )
if( g_plugins != NULL ) if( g_plugins != NULL )
g_plugins->Open(); g_plugins->Open();
if( isSuspended ) CpuInitializeMess();
CpuInitializeMess();
} }

View File

@ -36,22 +36,9 @@ SysThreadBase::~SysThreadBase() throw()
void SysThreadBase::Start() void SysThreadBase::Start()
{ {
_parent::Start(); _parent::Start();
m_ExecMode = ExecMode_Closing;
Sleep( 1 ); 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), pxAssertDev( (m_ExecMode == ExecMode_Closing) || (m_ExecMode == ExecMode_Closed),
"Unexpected thread status during SysThread startup." "Unexpected thread status during SysThread startup."
); );
@ -118,7 +105,7 @@ bool SysThreadBase::Suspend( bool isBlocking )
break; 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(); m_sem_event.Post();
} }
@ -164,7 +151,7 @@ bool SysThreadBase::Pause()
retval = true; 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(); m_sem_event.Post();
} }
@ -247,7 +234,7 @@ void SysThreadBase::OnStartInThread()
{ {
m_RunningLock.Acquire(); m_RunningLock.Acquire();
_parent::OnStartInThread(); _parent::OnStartInThread();
m_ResumeEvent.Post(); m_ExecMode = ExecMode_Closing;
} }
void SysThreadBase::OnCleanupInThread() void SysThreadBase::OnCleanupInThread()

View File

@ -20,22 +20,6 @@
using namespace Threading; 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 // ISysThread
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -208,8 +192,10 @@ public:
virtual void ApplySettings( const Pcsx2Config& src ); virtual void ApplySettings( const Pcsx2Config& src );
virtual void OnResumeReady(); virtual void OnResumeReady();
virtual void Reset(); virtual void Reset();
virtual void RecoverState();
virtual void Cancel( bool isBlocking=true ); virtual void Cancel( bool isBlocking=true );
virtual bool Cancel( const wxTimeSpan& timeout );
bool HasValidState() bool HasValidState()
{ {
return m_hasValidState; return m_hasValidState;

View File

@ -65,14 +65,12 @@ void __fastcall vu0ExecMicro(u32 addr) {
} }
void VU0unknown() { void VU0unknown() {
assert(0); pxFailDev("Unknown VU micromode opcode called");
CPU_LOG("Unknown VU micromode opcode called"); CPU_LOG("Unknown VU micromode opcode called");
} }
void VU0regsunknown(_VURegsNum *VUregsn) { void VU0regsunknown(_VURegsNum *VUregsn) {
assert(0); pxFailDev("Unknown VU micromode opcode called");
CPU_LOG("Unknown VU micromode opcode called"); CPU_LOG("Unknown VU micromode opcode called");
} }

View File

@ -39,7 +39,7 @@ public:
// this boolean indicates to some generic logging facilities if the VU's registers // 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 // are valid for logging or not. (see DisVU1Micro.cpp, etc) [kinda hacky, might
// be removed in the future] // be removed in the future]
bool IsInterpreter; bool IsInterpreter;
public: public:
BaseCpuProvider() BaseCpuProvider()
@ -105,6 +105,7 @@ public:
protected: protected:
BaseVUmicroCPU() {} BaseVUmicroCPU() {}
virtual ~BaseVUmicroCPU() throw() {}
}; };
@ -115,6 +116,7 @@ class InterpVU0 : public BaseVUmicroCPU
{ {
public: public:
InterpVU0(); InterpVU0();
virtual ~InterpVU0() throw() { Shutdown(); }
const char* GetShortName() const { return "intVU0"; } const char* GetShortName() const { return "intVU0"; }
wxString GetLongName() const { return L"VU0 Interpreter"; } wxString GetLongName() const { return L"VU0 Interpreter"; }
@ -132,6 +134,7 @@ class InterpVU1 : public BaseVUmicroCPU
{ {
public: public:
InterpVU1(); InterpVU1();
virtual ~InterpVU1() throw() { Shutdown(); }
const char* GetShortName() const { return "intVU1"; } const char* GetShortName() const { return "intVU1"; }
wxString GetLongName() const { return L"VU1 Interpreter"; } wxString GetLongName() const { return L"VU1 Interpreter"; }
@ -152,6 +155,7 @@ class recMicroVU0 : public BaseVUmicroCPU
{ {
public: public:
recMicroVU0(); recMicroVU0();
virtual ~recMicroVU0() throw() { Shutdown(); }
const char* GetShortName() const { return "mVU0"; } const char* GetShortName() const { return "mVU0"; }
wxString GetLongName() const { return L"microVU0 Recompiler"; } wxString GetLongName() const { return L"microVU0 Recompiler"; }
@ -169,6 +173,7 @@ class recMicroVU1 : public BaseVUmicroCPU
{ {
public: public:
recMicroVU1(); recMicroVU1();
virtual ~recMicroVU1() throw() { Shutdown(); }
const char* GetShortName() const { return "mVU1"; } const char* GetShortName() const { return "mVU1"; }
wxString GetLongName() const { return L"microVU1 Recompiler"; } wxString GetLongName() const { return L"microVU1 Recompiler"; }

View File

@ -130,9 +130,9 @@ static int __fastcall Vif0TransSTRow(u32 *data) // STROW
u32* pmem = &vif0Regs->r0 + (vif0.tag.addr << 2); u32* pmem = &vif0Regs->r0 + (vif0.tag.addr << 2);
u32* pmem2 = g_vifmask.Row0 + vif0.tag.addr; 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); ret = min(4 - vif0.tag.addr, vif0.vifpacketsize);
pxAssert(ret > 0); pxAssume(ret > 0);
switch (ret) switch (ret)
{ {
@ -752,7 +752,7 @@ void vif0Write32(u32 mem, u32 value)
case VIF0_R1: case VIF0_R1:
case VIF0_R2: case VIF0_R2:
case VIF0_R3: case VIF0_R3:
pxAssert((mem&0xf) == 0); pxAssume((mem&0xf) == 0);
g_vifmask.Row0[(mem>>4) & 3] = value; g_vifmask.Row0[(mem>>4) & 3] = value;
break; break;
@ -760,7 +760,7 @@ void vif0Write32(u32 mem, u32 value)
case VIF0_C1: case VIF0_C1:
case VIF0_C2: case VIF0_C2:
case VIF0_C3: case VIF0_C3:
pxAssert((mem&0xf) == 0); pxAssume((mem&0xf) == 0);
g_vifmask.Col0[(mem>>4) & 3] = value; g_vifmask.Col0[(mem>>4) & 3] = value;
break; break;

View File

@ -112,7 +112,7 @@ static __forceinline void vif1mpgTransfer(u32 addr, u32 *data, int size)
fwrite(data, 1, size*4, f); fwrite(data, 1, size*4, f);
fclose(f); fclose(f);
}*/ }*/
pxAssert(VU1.Micro > 0); pxAssume(VU1.Micro > 0);
if (memcmp(VU1.Micro + addr, data, size << 2)) if (memcmp(VU1.Micro + addr, data, size << 2))
{ {
CpuVU1->Clear(addr, size << 2); // Clear before writing! :/ 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* pmem = &vif1Regs->r0 + (vif1.tag.addr << 2);
u32* pmem2 = g_vifmask.Row1 + vif1.tag.addr; 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); ret = min(4 - vif1.tag.addr, vif1.vifpacketsize);
pxAssert(ret > 0); pxAssume(ret > 0);
switch (ret) switch (ret)
{ {
@ -1143,7 +1143,7 @@ void vif1Write32(u32 mem, u32 value)
case VIF1_R1: case VIF1_R1:
case VIF1_R2: case VIF1_R2:
case VIF1_R3: case VIF1_R3:
pxAssert((mem&0xf) == 0); pxAssume((mem&0xf) == 0);
g_vifmask.Row1[(mem>>4) & 3] = value; g_vifmask.Row1[(mem>>4) & 3] = value;
break; break;
@ -1151,7 +1151,7 @@ void vif1Write32(u32 mem, u32 value)
case VIF1_C1: case VIF1_C1:
case VIF1_C2: case VIF1_C2:
case VIF1_C3: case VIF1_C3:
pxAssert((mem&0xf) == 0); pxAssume((mem&0xf) == 0);
g_vifmask.Col1[(mem>>4) & 3] = value; g_vifmask.Col1[(mem>>4) & 3] = value;
break; break;

View File

@ -283,9 +283,7 @@ template<const u32 VIFdmanum> u32 VIFalign(u32 *data, vifCode *v, u32 size)
vif = &vif1; vif = &vif1;
vifRow = g_vifmask.Row1; vifRow = g_vifmask.Row1;
} }
#ifdef PCSX2_DEBUG pxAssume(v->addr < memsize);
pxAssert(v->addr < memsize);
#endif
dest = (u32*)(VU->Mem + v->addr); 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; u32 tempsize = 0;
const u32 memlimit = vif_size(VIFdmanum); const u32 memlimit = vif_size(VIFdmanum);
#ifdef PCSX2_DEBUG pxDebugCode( u32 memsize = memlimit );
u32 memsize = memlimit;
#endif
_mm_prefetch((char*)data, _MM_HINT_NTA); _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; vifMaskRegs = g_vif0Masks;
vif = &vif0; vif = &vif0;
vifRow = g_vifmask.Row0; vifRow = g_vifmask.Row0;
#ifdef PCSX2_DEBUG pxDebugCode( pxAssume(v->addr < memsize) );
pxAssert(v->addr < memsize);
#endif
} }
else else
{ {
@ -501,9 +495,7 @@ template<const u32 VIFdmanum> void VIFunpack(u32 *data, vifCode *v, u32 size)
vifMaskRegs = g_vif1Masks; vifMaskRegs = g_vif1Masks;
vif = &vif1; vif = &vif1;
vifRow = g_vifmask.Row1; vifRow = g_vifmask.Row1;
#ifdef PCSX2_DEBUG pxDebugCode( pxAssume(v->addr < memsize) );
pxAssert(v->addr < memsize);
#endif
} }
dest = (u32*)(VU->Mem + v->addr); dest = (u32*)(VU->Mem + v->addr);
@ -523,9 +515,7 @@ template<const u32 VIFdmanum> void VIFunpack(u32 *data, vifCode *v, u32 size)
size <<= 2; size <<= 2;
#ifdef PCSX2_DEBUG pxDebugCode( memsize = size );
memsize = size;
#endif
if (vifRegs->cycle.cl >= vifRegs->cycle.wl) // skipping write if (vifRegs->cycle.cl >= vifRegs->cycle.wl) // skipping write
{ {

View File

@ -38,7 +38,6 @@ class AppCoreThread;
#include "System.h" #include "System.h"
#include "System/SysThreads.h" #include "System/SysThreads.h"
typedef void FnType_OnThreadComplete(const wxCommandEvent& evt); typedef void FnType_OnThreadComplete(const wxCommandEvent& evt);
BEGIN_DECLARE_EVENT_TYPES() BEGIN_DECLARE_EVENT_TYPES()
@ -151,14 +150,6 @@ enum MenuIdentifiers
MenuId_Config_ResetAll, MenuId_Config_ResetAll,
}; };
enum DialogIdentifiers
{
DialogId_CoreSettings = 0x800,
DialogId_BiosSelector,
DialogId_LogOptions,
DialogId_About,
};
enum AppEventType enum AppEventType
{ {
// Maybe this will be expanded upon later..? // Maybe this will be expanded upon later..?
@ -331,9 +322,9 @@ struct MsgboxEventResult
Semaphore WaitForMe; Semaphore WaitForMe;
int result; int result;
MsgboxEventResult() : MsgboxEventResult()
WaitForMe(), result( 0 )
{ {
result = 0;
} }
}; };
@ -406,7 +397,7 @@ public:
void PostPadKey( wxKeyEvent& evt ); void PostPadKey( wxKeyEvent& evt );
void PostMenuAction( MenuIdentifiers menu_id ) const; void PostMenuAction( MenuIdentifiers menu_id ) const;
int ThreadedModalDialog( DialogIdentifiers dialogId ); int IssueModalDialog( const wxString& dlgName );
bool PrepForExit( bool canCancel ); bool PrepForExit( bool canCancel );
@ -532,6 +523,7 @@ public:
virtual bool Suspend( bool isBlocking=true ); virtual bool Suspend( bool isBlocking=true );
virtual void Resume(); virtual void Resume();
virtual void Reset(); virtual void Reset();
virtual void Cancel( bool isBlocking=true );
virtual void StateCheckInThread(); virtual void StateCheckInThread();
virtual void ApplySettings( const Pcsx2Config& src ); virtual void ApplySettings( const Pcsx2Config& src );
virtual void ChangeCdvdSource( CDVD_SourceType type ); virtual void ChangeCdvdSource( CDVD_SourceType type );
@ -610,6 +602,9 @@ public:
virtual ~SaveSinglePluginHelper() throw(); virtual ~SaveSinglePluginHelper() throw();
}; };
extern pxDoAssertFnType AppDoAssert;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// External App-related Globals and Shortcuts // External App-related Globals and Shortcuts
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -18,18 +18,27 @@
#include <wx/stackwalk.h> #include <wx/stackwalk.h>
static wxString pxGetStackTrace( const FnChar_t* calledFrom )
static wxString pxGetStackTrace()
{ {
wxString stackTrace; wxString stackTrace;
class StackDump : public wxStackWalker class StackDump : public wxStackWalker
{ {
protected: protected:
wxString m_stackTrace; wxString m_stackTrace;
wxString m_srcFuncName;
bool m_ignoreDone;
int m_skipped;
public: 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; } const wxString& GetStackTrace() const { return m_stackTrace; }
@ -38,92 +47,108 @@ static wxString pxGetStackTrace()
{ {
wxString name( frame.GetName() ); wxString name( frame.GetName() );
if( name.IsEmpty() ) if( name.IsEmpty() )
{
name = wxsFormat( L"%p ", frame.GetAddress() ); 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 ", m_ignoreDone = true;
wx_truncate_cast(int, frame.GetLevel()), name.c_str() }
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. StackDump dump( calledFrom );
static const int maxLines = 20; dump.Walk( 3 );
return dump.GetStackTrace();
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;
} }
static __threadlocal bool _reentrant_lock = false;
#ifdef __WXDEBUG__ #ifdef __WXDEBUG__
static __threadlocal int _reentrant_lock = 0;
// This override of wx's implementation provides thread safe assertion message reporting. If we aren't // 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 // on the main gui thread then the assertion message box needs to be passed off to the main gui thread
// via messages. // via messages.
void Pcsx2App::OnAssertFailure( const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg ) 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. // Re-entrant assertions are bad mojo -- trap immediately.
static bool disableAsserts = false; RecursionGuard guard( _reentrant_lock );
if( disableAsserts ) return; 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(); wxTrap();
} }
}
_reentrant_lock = true; #endif
wxString dbgmsg; bool AppDoAssert( const DiagnosticOrigin& origin, const wxChar *msg )
dbgmsg.reserve( 2048 ); {
// Used to allow the user to suppress future assertions during this application's session.
static bool disableAsserts = false;
if( disableAsserts ) return false;
wxString message; wxString trace( pxGetStackTrace(origin.function) );
if( msg == NULL ) wxString dbgmsg( origin.ToString( msg ) );
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() );
wxMessageOutputDebug().Printf( dbgmsg ); wxMessageOutputDebug().Printf( dbgmsg );
Console.Error( dbgmsg ); Console.Error( dbgmsg );
Console.WriteLn( trace ); Console.WriteLn( trace );
int retval = Msgbox::Assertion( dbgmsg, trace ); wxString windowmsg( L"Assertion failed: " );
if( msg != NULL )
switch( retval ) windowmsg += msg;
{ else if( origin.condition != NULL )
case wxID_YES: windowmsg += origin.condition;
wxTrap();
break;
case wxID_NO: break;
case wxID_CANCEL: // ignores future assertions.
disableAsserts = true;
break;
}
_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

View File

@ -312,7 +312,7 @@ AppConfig::AppConfig()
, DeskTheme( L"default" ) , DeskTheme( L"default" )
{ {
LanguageId = wxLANGUAGE_DEFAULT; LanguageId = wxLANGUAGE_DEFAULT;
RecentFileCount = 6; RecentIsoCount = 12;
Listbook_ImageSize = 32; Listbook_ImageSize = 32;
Toolbar_ImageSize = 24; Toolbar_ImageSize = 24;
Toolbar_ShowLabels = true; Toolbar_ShowLabels = true;
@ -378,7 +378,7 @@ void AppConfig::LoadSaveRootItems( IniInterface& ini )
IniEntry( MainGuiPosition ); IniEntry( MainGuiPosition );
IniEntry( SettingsTabName ); IniEntry( SettingsTabName );
ini.EnumEntry( L"LanguageId", LanguageId, NULL, defaults.LanguageId ); ini.EnumEntry( L"LanguageId", LanguageId, NULL, defaults.LanguageId );
IniEntry( RecentFileCount ); IniEntry( RecentIsoCount );
IniEntry( DeskTheme ); IniEntry( DeskTheme );
IniEntry( Listbook_ImageSize ); IniEntry( Listbook_ImageSize );
IniEntry( Toolbar_ImageSize ); IniEntry( Toolbar_ImageSize );

View File

@ -154,7 +154,7 @@ public:
// Current language in use (correlates to a wxWidgets wxLANGUAGE specifier) // Current language in use (correlates to a wxWidgets wxLANGUAGE specifier)
wxLanguage LanguageId; 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) // 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). // The theme name is used to look up files in the themes folder (relative to the executable).

View File

@ -28,6 +28,16 @@ AppCoreThread::AppCoreThread() : SysCoreThread()
AppCoreThread::~AppCoreThread() throw() 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() void AppCoreThread::Reset()

View File

@ -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() void Pcsx2App::OpenWizardConsole()
{ {
if( !IsDebugBuild ) return; if( !IsDebugBuild ) return;
@ -81,8 +107,7 @@ void Pcsx2App::ReadUserModeSettings()
if (IOP_ENABLE_SIF_HACK == 1) if (IOP_ENABLE_SIF_HACK == 1)
{ {
wxDialogWithHelpers hackedVersion( NULL, wxID_ANY, _("It will devour your young! - PCSX2 Shub-Niggurath edition"), false ); wxDialogWithHelpers hackedVersion( NULL, _("It will devour your young! - PCSX2 Shub-Niggurath edition"), wxVERTICAL );
hackedVersion.SetIdealWidth( 575 );
hackedVersion.SetSizer( new wxBoxSizer( wxVERTICAL ) ); hackedVersion.SetSizer( new wxBoxSizer( wxVERTICAL ) );
hackedVersion += new pxStaticText( &hackedVersion, hackedVersion += new pxStaticText( &hackedVersion,
@ -92,8 +117,6 @@ void Pcsx2App::ReadUserModeSettings()
); );
hackedVersion += new wxButton( &hackedVersion, wxID_OK ) | pxSizerFlags::StdCenter(); hackedVersion += new wxButton( &hackedVersion, wxID_OK ) | pxSizerFlags::StdCenter();
hackedVersion.Fit();
hackedVersion.CentreOnScreen();
hackedVersion.ShowModal(); hackedVersion.ShowModal();
} }
@ -101,8 +124,7 @@ void Pcsx2App::ReadUserModeSettings()
{ {
// Pre-Alpha Warning! Why didn't I think to add this sooner?! // 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 ); wxDialogWithHelpers preAlpha( NULL, _("It might devour your kittens! - PCSX2 0.9.7 Pre-Alpha"), wxVERTICAL );
preAlpha.SetIdealWidth( 575 );
preAlpha.SetSizer( new wxBoxSizer( wxVERTICAL ) ); preAlpha.SetSizer( new wxBoxSizer( wxVERTICAL ) );
preAlpha += new pxStaticText( &preAlpha, preAlpha += new pxStaticText( &preAlpha,
@ -113,8 +135,6 @@ void Pcsx2App::ReadUserModeSettings()
); );
preAlpha += new wxButton( &preAlpha, wxID_OK ) | pxSizerFlags::StdCenter(); preAlpha += new wxButton( &preAlpha, wxID_OK ) | pxSizerFlags::StdCenter();
preAlpha.Fit();
preAlpha.CentreOnScreen();
preAlpha.ShowModal(); preAlpha.ShowModal();
// first time startup, so give the user the choice of user mode: // first time startup, so give the user the choice of user mode:
@ -243,6 +263,15 @@ typedef void (wxEvtHandler::*pxMessageBoxEventFunction)(pxMessageBoxEvent&);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool Pcsx2App::OnInit() 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(); g_Conf = new AppConfig();
EnableAllLogging(); EnableAllLogging();
@ -253,12 +282,6 @@ bool Pcsx2App::OnInit()
m_StderrRedirHandle = NewPipeRedir(stderr); m_StderrRedirHandle = NewPipeRedir(stderr);
wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() ); 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_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) );
Connect( pxEVT_SysExecute, wxCommandEventHandler( Pcsx2App::OnSysExecute ) ); Connect( pxEVT_SysExecute, wxCommandEventHandler( Pcsx2App::OnSysExecute ) );
@ -286,9 +309,21 @@ bool Pcsx2App::OnInit()
m_Resources = new pxAppResources(); 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(); ReadUserModeSettings();
AppConfig_OnChangedSettingsFolder(); AppConfig_OnChangedSettingsFolder();
CpuCheckSSE2();
m_MainFrame = new MainEmuFrame( NULL, L"PCSX2" ); m_MainFrame = new MainEmuFrame( NULL, L"PCSX2" );
m_MainFrame->PushEventHandler( &GetRecentIsoList() ); m_MainFrame->PushEventHandler( &GetRecentIsoList() );
@ -307,7 +342,8 @@ bool Pcsx2App::OnInit()
SetExitOnFrameDelete( true ); // but being explicit doesn't hurt... SetExitOnFrameDelete( true ); // but being explicit doesn't hurt...
m_MainFrame->Show(); m_MainFrame->Show();
SysDetect(); SysLogMachineCaps();
AppApplySettings(); AppApplySettings();
#ifdef __WXMSW__ #ifdef __WXMSW__
@ -317,47 +353,72 @@ bool Pcsx2App::OnInit()
m_CoreAllocs = new SysCoreAllocations(); m_CoreAllocs = new SysCoreAllocations();
if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) ) if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) )
{ {
// HadSomeFailures only returns 'true' if an *enabled* cpu type fails to init. If // 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 // the user already has all interps configured, for example, then no point in
// popping up this dialog. // popping up this dialog.
wxDialogWithHelpers exconf( NULL, _("PCSX2 Recompiler Error(s)"), wxVERTICAL );
// TODO : This should be redone using the ExtensibleConfirmation, and a sub-window exconf += 12;
// (static text or something with a vertical scrollbar). 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() ) 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() ) 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() ) 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() ) 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() ) 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() ) 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... // Failures can be SSE-related OR memory related. Should do per-cpu error reports instead...
/*message += pxE( ".Popup Error:EmuCore:MemoryForRecs", /*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." 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 ) ) //if( !Msgbox::OkCancel( message, _("PCSX2 Initialization Error"), wxICON_ERROR ) )
return false; // return false;
} }
LoadPluginsPassive( NULL ); LoadPluginsPassive( NULL );
@ -408,13 +469,16 @@ void Pcsx2App::CleanupMess()
if( m_CorePlugins ) if( m_CorePlugins )
m_CorePlugins->Shutdown(); m_CorePlugins->Shutdown();
} }
catch( Exception::ThreadTimedOut& ) { throw; }
catch( Exception::CancelEvent& ) { throw; }
catch( Exception::RuntimeError& ex ) catch( Exception::RuntimeError& ex )
{ {
// Handle runtime errors gracefully during shutdown. Mostly these are things // 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 // that we just don't care about by now, and just want to "get 'er done!" so
// we can exit the app. ;) // 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, // Notice: deleting the plugin manager (unloading plugins) here causes Lilypad to crash,
@ -427,6 +491,8 @@ void Pcsx2App::CleanupMess()
while( wxGetLocale() != NULL ) while( wxGetLocale() != NULL )
delete wxGetLocale(); delete wxGetLocale();
pxDoAssert = pxAssertImpl_LogIt;
} }
Pcsx2App::Pcsx2App() Pcsx2App::Pcsx2App()
@ -441,6 +507,8 @@ Pcsx2App::Pcsx2App()
Pcsx2App::~Pcsx2App() Pcsx2App::~Pcsx2App()
{ {
pxDoAssert = pxAssertImpl_LogIt;
// Typically OnExit cleans everything up before we get here, *unless* we cancel // Typically OnExit cleans everything up before we get here, *unless* we cancel
// out of program startup in OnInit (return false) -- then remaining cleanup needs // out of program startup in OnInit (return false) -- then remaining cleanup needs
// to happen here in the destructor. // to happen here in the destructor.
@ -485,6 +553,6 @@ struct CrtDebugBreak
} }
}; };
//CrtDebugBreak breakAt( 4327 ); //CrtDebugBreak breakAt( 1175 );
#endif #endif

View File

@ -53,7 +53,7 @@ ConfigOverrides OverrideOptions;
static bool HandlePluginError( Exception::PluginError& ex ) static bool HandlePluginError( Exception::PluginError& ex )
{ {
if( pxDialogExists( DialogId_CoreSettings ) ) return true; if( pxDialogExists( L"CoreSettings" ) ) return true;
bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() + bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() +
_("\n\nPress Ok to go to the Plugin Configuration Panel.") _("\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"; g_Conf->SettingsTabName = L"Plugins";
// fixme: Send a message to the panel to select the failed plugin. // 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 false;
} }
return result; 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 // Pcsx2App Event Handlers
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -130,54 +118,69 @@ void Pcsx2App::OnCoreThreadStatus( wxCommandEvent& evt )
void Pcsx2App::OnOpenModalDialog( wxCommandEvent& evt ) void Pcsx2App::OnOpenModalDialog( wxCommandEvent& evt )
{ {
using namespace Dialogs; pxAssertDev( !evt.GetString().IsEmpty(), wxNullChar );
MsgboxEventResult& evtres( *((MsgboxEventResult*)evt.GetClientData()) ); MsgboxEventResult* evtres = (MsgboxEventResult*)evt.GetClientData();
switch( evt.GetId() )
wxWindowID result = IssueModalDialog( evt.GetString() );
if( evtres != NULL )
{ {
case DialogId_CoreSettings: evtres->result = result;
{ evtres->WaitForMe.Post();
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.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 ); HashTools::HashMap<int, const GlobalCommandDescriptor*> GlobalAccels( 0, 0xffffffff );
@ -227,15 +230,14 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
catch( Exception::BiosLoadFailed& ex ) catch( Exception::BiosLoadFailed& ex )
{ {
bool result = Dialogs::ExtensibleConfirmation( NULL, ConfButtons().OK().Cancel(), wxDialogWithHelpers dialog( NULL, _("PS2 BIOS Error"), wxVERTICAL );
L"PS2 BIOS Error", dialog += dialog.Heading( ex.FormatDisplayMessage() + BIOS_GetMsg_Required() + _("\nPress Ok to go to the BIOS Configuration Panel.") );
ex.FormatDisplayMessage() + BIOS_GetMsg_Required() + _("\nPress Ok to go to the BIOS Configuration Panel.") dialog += new ModalButtonPanel( &dialog, MsgButtons().OKCancel() );
).ShowModal() != wxID_CANCEL;
if( dialog.ShowModal() == wxID_CANCEL )
if( !result )
Console.Warning( "User denied option to re-configure BIOS." ); Console.Warning( "User denied option to re-configure BIOS." );
if( Dialogs::BiosSelectorDialog().ShowModal() != wxID_CANCEL ) if( IssueModalDialog( Dialogs::BiosSelectorDialog::GetNameStatic() ) != wxID_CANCEL )
{ {
SysExecute(); SysExecute();
} }
@ -268,15 +270,22 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
catch( Exception::ThreadTimedOut& ex ) 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() ); Console.Warning( ex.FormatDiagnosticMessage() );
int result = Dialogs::ExtensibleConfirmation( NULL, ConfButtons().Ignore().Cancel().Custom( _("Terminate") ), wxDialogWithHelpers dialog( NULL, _("PCSX2 Unresponsive Thread"), wxVERTICAL );
_("PCSX2 Unresponsive Thread"), ex.FormatDisplayMessage() + L"\n\n" +
dialog += dialog.Heading( ex.FormatDisplayMessage() + L"\n\n" +
pxE( ".Popup Error:Thread Deadlock Actions", pxE( ".Popup Error:Thread Deadlock Actions",
L"'Ignore' to continue waiting for the thread to respond.\n" L"'Ignore' to continue waiting for the thread to respond.\n"
L"'Cancel' to attempt to cancel the thread.\n" L"'Cancel' to attempt to cancel the thread.\n"
L"'Terminate' to quit PCSX2 immediately.\n" L"'Terminate' to quit PCSX2 immediately.\n"
) )
).ShowModal(); );
int result = pxIssueConfirmation( dialog, MsgButtons().Ignore().Cancel().Custom( _("Terminate") ) );
if( result == pxID_CUSTOM ) if( result == pxID_CUSTOM )
{ {
@ -386,14 +395,14 @@ int Pcsx2App::OnExit()
// is a matter of programmer preference). // is a matter of programmer preference).
MainEmuFrame& Pcsx2App::GetMainFrame() const MainEmuFrame& Pcsx2App::GetMainFrame() const
{ {
pxAssert( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) ); pxAssume( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) );
pxAssert( m_MainFrame != NULL ); pxAssume( m_MainFrame != NULL );
return *m_MainFrame; return *m_MainFrame;
} }
GSFrame& Pcsx2App::GetGSFrame() const GSFrame& Pcsx2App::GetGSFrame() const
{ {
pxAssert( m_gsFrame != NULL ); pxAssume( m_gsFrame != NULL );
return *m_gsFrame; 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*. // Full system reset stops the core thread and unloads all core plugins *completely*.
void Pcsx2App::SysReset() void Pcsx2App::SysReset()
{ {
StateCopy_Clear();
CoreThread.Reset(); CoreThread.Reset();
CoreThread.ReleaseResumeLock(); CoreThread.ReleaseResumeLock();
m_CorePlugins = NULL; m_CorePlugins = NULL;

View File

@ -176,8 +176,15 @@ ConsoleLogFrame::ColorArray::~ColorArray()
void ConsoleLogFrame::ColorArray::Create( int fontsize ) void ConsoleLogFrame::ColorArray::Create( int fontsize )
{ {
const wxFont fixed( fontsize, wxMODERN, wxNORMAL, wxNORMAL ); // pxGetFixedFont selects Andale Mono on Win32, which is nice visually but
const wxFont fixedB( fontsize, wxMODERN, wxNORMAL, wxBOLD ); // 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: // Standard R, G, B format:
new (&m_table[Color_Default]) wxTextAttr( wxColor( 0, 0, 0 ), wxNullColour, fixed ); 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.SetBackgroundColour( wxColor( 230, 235, 242 ) );
m_TextCtrl.SetDefaultStyle( m_ColorTable[DefaultConsoleColor] ); 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& menuLog (*new wxMenu());
wxMenu& menuAppear (*new wxMenu()); wxMenu& menuAppear (*new wxMenu());
wxMenu& menuSources (*new wxMenu()); wxMenu& menuSources (*new wxMenu());

View File

@ -96,10 +96,10 @@ protected:
EventListenerBinding<PluginEventType> m_Listener_CorePluginStatus; EventListenerBinding<PluginEventType> m_Listener_CorePluginStatus;
#ifdef __WXMSW__ #ifdef __WXMSW__
int m_win32_StupidRefreshTricks;
int m_win32_LinesPerPage; int m_win32_LinesPerPage;
int m_win32_LinesPerScroll; int m_win32_LinesPerScroll;
#endif #endif
bool m_IsPaused;
bool m_FreezeWrites; bool m_FreezeWrites;
public: public:

View File

@ -25,6 +25,8 @@
#include <wx/mstream.h> #include <wx/mstream.h>
#include <wx/hyperlink.h> #include <wx/hyperlink.h>
using namespace pxSizerFlags;
namespace Dialogs namespace Dialogs
{ {
// Helper class for creating wxStaticText labels which are aligned to center. // Helper class for creating wxStaticText labels which are aligned to center.
@ -45,11 +47,14 @@ namespace Dialogs
// AboutBoxDialog Implementation // AboutBoxDialog Implementation
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ): Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent )
wxDialogWithHelpers( parent, id, _("About PCSX2"), false ), : wxDialogWithHelpers( parent, _("About PCSX2"), wxVERTICAL )
m_bitmap_dualshock( this, wxID_ANY, wxBitmap( EmbeddedImage<res_Dualshock>().Get() ), , m_bitmap_dualshock( this, wxID_ANY, wxBitmap( EmbeddedImage<res_Dualshock>().Get() ),
wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN ) wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN
)
{ {
SetName( GetNameStatic() );
static const wxString LabelAuthors = fromUTF8( static const wxString LabelAuthors = fromUTF8(
"Developers" "Developers"
"\n\n" "\n\n"
@ -80,10 +85,6 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
"F|RES, MrBrown, razorblade, Seta-san, Skarmeth, feal87" "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! // This sizer holds text of the authors and a logo!
wxBoxSizer& AuthLogoSizer = *new wxBoxSizer( wxHORIZONTAL ); wxBoxSizer& AuthLogoSizer = *new wxBoxSizer( wxHORIZONTAL );
@ -99,18 +100,21 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
label_auth->Wrap( 340 ); label_auth->Wrap( 340 );
label_greets->Wrap( 200 ); label_greets->Wrap( 200 );
aboutUs.Add( label_auth, pxSizerFlags::StdExpand() ); aboutUs += label_auth | StdExpand();
contribs.Add( label_greets, pxSizerFlags::StdExpand() ); contribs += label_greets | StdExpand();
AuthLogoSizer.Add( &aboutUs ); AuthLogoSizer += aboutUs;
AuthLogoSizer.AddSpacer( 7 ); AuthLogoSizer += 7;
AuthLogoSizer.Add( &contribs ); AuthLogoSizer += contribs;
ContribSizer.AddStretchSpacer( 1 ); ContribSizer.AddStretchSpacer( 1 );
ContribSizer.Add( &m_bitmap_dualshock, pxSizerFlags::StdSpace() ); ContribSizer += m_bitmap_dualshock | StdSpace();
ContribSizer.AddStretchSpacer( 1 ); 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, *this += new wxHyperlinkCtrl( this, wxID_ANY,
_("Pcsx2 Official Website and Forums"), L"http://www.pcsx2.net" _("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" _("Pcsx2 Official Svn Repository at Googlecode"), L"http://code.google.com/p/pcsx2"
) | wxSizerFlags(1).Center().Border( wxALL, 3 ); ) | wxSizerFlags(1).Center().Border( wxALL, 3 );
*this += ContribSizer | pxSizerFlags::StdExpand(); *this += ContribSizer | StdExpand();
*this += new wxButton( this, wxID_OK, L"I've seen enough") | StdCenter();
*this += new wxButton( this, wxID_OK, L"I've seen enough") | pxSizerFlags::StdCenter();
Fit();
CenterOnScreen();
} }

View File

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

View File

@ -55,10 +55,12 @@ void Dialogs::ConfigurationDialog::AddPage( const char* label, int iconid )
( labelstr == g_Conf->SettingsTabName ), iconid ); ( labelstr == g_Conf->SettingsTabName ), iconid );
} }
Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent, int id ) Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent )
: wxDialogWithHelpers( parent, id, _("PCSX2 Configuration"), true ) : wxDialogWithHelpers( parent, _("PCSX2 Configuration"), true )
, m_listbook( *new wxListbook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_orient ) ) , m_listbook( *new wxListbook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_orient ) )
{ {
SetName( GetNameStatic() );
m_idealWidth = 600; m_idealWidth = 600;
m_listbook.SetImageList( &wxGetApp().GetImgList_Config() ); m_listbook.SetImageList( &wxGetApp().GetImgList_Config() );
@ -85,9 +87,6 @@ Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent, int id )
FindWindow( wxID_APPLY )->Disable(); FindWindow( wxID_APPLY )->Disable();
Fit();
CenterOnScreen();
#ifdef __WXMSW__ #ifdef __WXMSW__
// Depending on Windows version and user appearance settings, the default icon spacing can be // 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 // 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_APPLY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigurationDialog::OnApply_Click ) );
Connect( wxID_SAVE, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigurationDialog::OnScreenshot_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 // 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. :) // 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(); g_ApplyState.DoCleanup();
} }
void Dialogs::ConfigurationDialog::OnCloseWindow( wxCloseEvent& evt )
{
if( !IsModal() ) Destroy();
evt.Skip();
}
void Dialogs::ConfigurationDialog::OnOk_Click( wxCommandEvent& evt ) void Dialogs::ConfigurationDialog::OnOk_Click( wxCommandEvent& evt )
{ {
if( g_ApplyState.ApplyAll() ) if( g_ApplyState.ApplyAll() )
@ -133,8 +140,6 @@ void Dialogs::ConfigurationDialog::OnOk_Click( wxCommandEvent& evt )
FindWindow( wxID_APPLY )->Disable(); FindWindow( wxID_APPLY )->Disable();
g_Conf->SettingsTabName = m_labels[m_listbook.GetSelection()]; g_Conf->SettingsTabName = m_labels[m_listbook.GetSelection()];
AppSaveSettings(); AppSaveSettings();
Close();
evt.Skip(); evt.Skip();
} }
} }
@ -181,9 +186,11 @@ void Dialogs::ConfigurationDialog::OnScreenshot_Click( wxCommandEvent& evt )
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
Dialogs::BiosSelectorDialog::BiosSelectorDialog( wxWindow* parent, int id ) Dialogs::BiosSelectorDialog::BiosSelectorDialog( wxWindow* parent )
: wxDialogWithHelpers( parent, id, _("BIOS Selector"), false ) : wxDialogWithHelpers( parent, _("BIOS Selector"), false )
{ {
SetName( GetNameStatic() );
m_idealWidth = 500; m_idealWidth = 500;
wxBoxSizer& bleh( *new wxBoxSizer( wxVERTICAL ) ); wxBoxSizer& bleh( *new wxBoxSizer( wxVERTICAL ) );

View File

@ -33,7 +33,9 @@ namespace Dialogs
public: public:
virtual ~ConfigurationDialog() throw(); virtual ~ConfigurationDialog() throw();
ConfigurationDialog(wxWindow* parent=NULL, int id=DialogId_CoreSettings); ConfigurationDialog(wxWindow* parent=NULL);
static const wxChar* GetNameStatic() { return L"Dialog:CoreSettings"; }
protected: protected:
template< typename T > template< typename T >
@ -43,6 +45,8 @@ namespace Dialogs
void OnCancel_Click( wxCommandEvent& evt ); void OnCancel_Click( wxCommandEvent& evt );
void OnApply_Click( wxCommandEvent& evt ); void OnApply_Click( wxCommandEvent& evt );
void OnScreenshot_Click( wxCommandEvent& evt ); void OnScreenshot_Click( wxCommandEvent& evt );
void OnCloseWindow( wxCloseEvent& evt );
virtual void OnSomethingChanged( wxCommandEvent& evt ) virtual void OnSomethingChanged( wxCommandEvent& evt )
{ {
@ -54,14 +58,15 @@ namespace Dialogs
} }
}; };
class BiosSelectorDialog : public wxDialogWithHelpers class BiosSelectorDialog : public wxDialogWithHelpers
{ {
protected: protected:
public: public:
virtual ~BiosSelectorDialog() throw() {} virtual ~BiosSelectorDialog() throw() {}
BiosSelectorDialog( wxWindow* parent=NULL, int id=DialogId_BiosSelector ); BiosSelectorDialog( wxWindow* parent=NULL );
static const wxChar* GetNameStatic() { return L"Dialog:BiosSelector"; }
protected: protected:
void OnOk_Click( wxCommandEvent& evt ); void OnOk_Click( wxCommandEvent& evt );

View File

@ -20,7 +20,9 @@
#include "ModalPopups.h" #include "ModalPopups.h"
#include "Utilities/StringHelpers.h" #include "Utilities/StringHelpers.h"
bool ConfButtons::Allows( wxWindowID id ) const using namespace pxSizerFlags;
bool MsgButtons::Allows( wxWindowID id ) const
{ {
switch( id ) switch( id )
{ {
@ -63,7 +65,7 @@ static wxString ResultToString( int result )
return wxEmptyString; 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; if( !pxAssert( !src.IsEmpty() ) ) return wxID_ANY;
@ -85,7 +87,53 @@ static wxWindowID ParseThatResult( const wxString& src, const ConfButtons& valid
return wxID_ANY; 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(); wxConfigBase* cfg = GetAppConfig();
@ -112,135 +160,143 @@ wxWindowID Dialogs::IssueConfirmation( ExtensibleConfirmation& confirmDlg, const
result = split[0]; result = split[0];
if( result == L"disabled" || result == L"off" || result == L"no" ) 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( result != wxID_ANY ) return result;
} }
} }
} }
if( cfg == NULL ) return confirmDlg.ShowModal(); pxCheckBox* DisablerCtrl = NULL;
if( cfg != NULL )
// 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() )
{ {
cfg->SetPath( L"/PopupDisablers" ); // Add an option that allows the user to disable this popup from showing again.
cfg->Write( disablerKey, L"disabled," + cfgResult ); // (and if the config hasn't been initialized yet, then assume the dialog as non-disablable)
cfg->SetPath( L"/" );
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; return modalResult;
} }
Dialogs::ExtensibleConfirmation::ExtensibleConfirmation( wxWindow* parent, const ConfButtons& type, const wxString& title, const wxString& msg ) ModalButtonPanel::ModalButtonPanel( wxWindow* parent, const MsgButtons& buttons )
: wxDialogWithHelpers( parent, wxID_ANY, title, false ) : wxPanelWithHelpers( parent, wxHORIZONTAL )
, m_ExtensibleSizer( *new wxBoxSizer( wxVERTICAL ) )
, m_ButtonSizer( *new wxBoxSizer( wxHORIZONTAL ) )
{ {
m_Buttons = type;
m_idealWidth = 500;
SetSizer( new wxBoxSizer(wxVERTICAL) );
// Populate the Button Sizer. // Populate the Button Sizer.
// We prefer this over the built-in wxWidgets ButtonSizer stuff used for other types of // 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 // dialogs because we offer more button types, and we don't want the MSW default behavior
// of right-justified buttons. // of right-justified buttons.
if( type.HasCustom() ) if( buttons.HasCustom() )
AddCustomButton( pxID_CUSTOM, type.GetCustomLabel() ); AddCustomButton( pxID_CUSTOM, buttons.GetCustomLabel() );
// Order of wxID_RESET and custom button have been picked fairly arbitrarily, since there's // Order of wxID_RESET and custom button have been picked fairly arbitrarily, since there's
// no standard governing those. // no standard governing those.
#ifdef __WXGTK__ #ifdef __WXGTK__
// GTK+ / Linux inverts OK/CANCEL order -- cancel / no first, OK / Yes later. >_< // GTK+ / Linux inverts OK/CANCEL order -- cancel / no first, OK / Yes later. >_<
if( type.HasCancel() ) if( buttons.HasCancel() )
AddActionButton( wxID_CANCEL ); AddActionButton( wxID_CANCEL );
if( type.HasNo() ) if( buttons.HasNo() )
{ {
AddActionButton( wxID_NO ); 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 ); AddActionButton( wxID_OK );
if( type.HasYes() ) if( buttons.HasYes() )
{ {
AddActionButton( wxID_YES ); AddActionButton( wxID_YES );
if( type.AllowsToAll() ) if( buttons.AllowsToAll() )
AddActionButton( wxID_YESTOALL ); 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") ); AddCustomButton( wxID_RESET, _("Reset") );
if( type.HasClose() ) if( buttons.HasClose() )
AddActionButton( wxID_CLOSE ); AddActionButton( wxID_CLOSE );
#ifndef __WXGTK__ #ifndef __WXGTK__
if( type.HasNo() || type.HasCancel() ) // Extra space between Affirm and Cancel Actions if( buttons.HasNo() )
m_ButtonSizer.Add(0, 0, 1, wxEXPAND, 0);
if( type.HasNo() )
{ {
AddActionButton( wxID_NO ); AddActionButton( wxID_NO );
if( type.AllowsToAll() ) if( buttons.AllowsToAll() )
AddActionButton( wxID_NOTOALL ); AddActionButton( wxID_NOTOALL );
} }
if( type.HasCancel() ) if( buttons.HasIgnore() )
AddCustomButton( wxID_IGNORE, _("Ignore") );
if( buttons.HasCancel() )
AddActionButton( wxID_CANCEL ); AddActionButton( wxID_CANCEL );
#endif #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();
} }
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 ); *this += new wxButton( this, id, label ) | StdButton().Proportion(6);
Connect( id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ExtensibleConfirmation::OnActionButtonClicked ) ); 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 ); *this += new wxButton( this, id ) | StdButton().Proportion(6);
Connect( id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ExtensibleConfirmation::OnActionButtonClicked ) ); Connect( id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ModalButtonPanel::OnActionButtonClicked ) );
} }

View File

@ -22,6 +22,7 @@
#include <wx/file.h> #include <wx/file.h>
using namespace Panels; using namespace Panels;
using namespace pxSizerFlags;
template< typename T > template< typename T >
static T& MakeWizWidget( int pageid, wxWizardPage* src ) static T& MakeWizWidget( int pageid, wxWizardPage* src )
@ -48,8 +49,6 @@ Panels::SettingsDirPickerPanel::SettingsDirPickerPanel( wxWindow* parent ) :
), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE ), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE
), wxSizerFlags().Expand().Border( wxBOTTOM, 6 ) ), wxSizerFlags().Expand().Border( wxBOTTOM, 6 )
); );
//SetSizerAndFit( GetSizer(), false );
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -58,24 +57,22 @@ FirstTimeWizard::UsermodePage::UsermodePage( wxWizard* parent ) :
{ {
SetSizer( new wxBoxSizer( wxVERTICAL ) ); SetSizer( new wxBoxSizer( wxVERTICAL ) );
wxPanelWithHelpers* panel = new wxPanelWithHelpers( this, wxVERTICAL ); wxPanelWithHelpers& panel( *new wxPanelWithHelpers( this, wxVERTICAL ) );
panel->SetIdealWidth( 640 ); panel.SetIdealWidth( 640 );
wxSizer& s_panel( *panel->GetSizer() );
m_dirpick_settings = new SettingsDirPickerPanel( panel ); m_dirpick_settings = new SettingsDirPickerPanel( &panel );
m_panel_LangSel = new LanguageSelectionPanel( panel ); m_panel_LangSel = new LanguageSelectionPanel( &panel );
m_panel_UserSel = new UsermodeSelectionPanel( panel ); m_panel_UserSel = new UsermodeSelectionPanel( &panel );
(new pxStaticHeading( panel, _("PCSX2 is starting from a new or unknown folder and needs to be configured.") )) panel += panel.Heading(_("PCSX2 is starting from a new or unknown folder and needs to be configured."));
->AddTo( s_panel );
s_panel.Add( m_panel_LangSel, pxSizerFlags::StdCenter() ); panel += m_panel_LangSel | StdCenter();
s_panel.Add( m_panel_UserSel, wxSizerFlags().Expand().Border( wxALL, 8 ) ); panel += m_panel_UserSel | pxExpand.Border( wxALL, 8 );
s_panel.AddSpacer( 6 ); panel += 6;
s_panel.Add( m_dirpick_settings, pxSizerFlags::SubGroup() ); panel += m_dirpick_settings | SubGroup();
GetSizer()->Add( panel, wxSizerFlags().Expand() ); panel += panel | pxExpand;
Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler(FirstTimeWizard::UsermodePage::OnUsermodeChanged) ); 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 ) ) , m_panel_BiosSel ( MakeWizWidget<BiosSelectorPanel>( 2, &m_page_bios ) )
{ {
// Page 2 - Plugins Panel // 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 // Page 3 - Bios Panel
wxBoxSizer& biosSizer( *new wxBoxSizer( wxVERTICAL ) );
biosSizer.Add( &m_panel_BiosSel, pxSizerFlags::StdExpand() ); m_page_plugins. SetSizer( new wxBoxSizer( wxVERTICAL ) );
m_page_bios.SetSizer( &biosSizer ); 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 // Assign page indexes as client data
m_page_usermode.SetClientData ( (void*)0 ); m_page_usermode.SetClientData ( (void*)0 );
@ -117,8 +113,13 @@ FirstTimeWizard::FirstTimeWizard( wxWindow* parent )
m_page_usermode.SetNext ( &m_page_plugins ); m_page_usermode.SetNext ( &m_page_plugins );
m_page_plugins.SetNext ( &m_page_bios ); m_page_plugins.SetNext ( &m_page_bios );
GetPageAreaSizer()->Add( &m_page_usermode ); GetPageAreaSizer() += m_page_usermode;
GetPageAreaSizer()->Add( &m_page_plugins ); GetPageAreaSizer() += m_page_plugins;
// this doesn't descent from wxDialogWithHelpers, so we need to explicitly
// fit and center it. :(
Fit();
CenterOnScreen(); CenterOnScreen();
Connect( wxEVT_WIZARD_PAGE_CHANGED, wxWizardEventHandler( FirstTimeWizard::OnPageChanged ) ); Connect( wxEVT_WIZARD_PAGE_CHANGED, wxWizardEventHandler( FirstTimeWizard::OnPageChanged ) );

View File

@ -18,8 +18,10 @@
#include "ModalPopups.h" #include "ModalPopups.h"
using namespace pxSizerFlags;
Dialogs::ImportSettingsDialog::ImportSettingsDialog( wxWindow* parent ) Dialogs::ImportSettingsDialog::ImportSettingsDialog( wxWindow* parent )
: wxDialogWithHelpers( parent, wxID_ANY, _("Import Existing Settings?"), false ) : wxDialogWithHelpers( parent, _("Import Existing Settings?"), wxVERTICAL )
{ {
m_idealWidth = 440; 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"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)" ) 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 ); wxBoxSizer& s_buttons = *new wxBoxSizer( wxHORIZONTAL );
wxButton* b_import = new wxButton( this, wxID_ANY, _("Import") ); wxButton* b_import = new wxButton( this, wxID_ANY, _("Import") );
@ -37,19 +38,16 @@ Dialogs::ImportSettingsDialog::ImportSettingsDialog( wxWindow* parent )
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// Layout Some Shizat... // 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() ); *this += 4;
s_buttons.AddSpacer( 16 ); *this += heading;
s_buttons.Add( b_over, pxSizerFlags::StdButton() ); *this += 12;
s_buttons.AddSpacer( 16 ); *this += &s_buttons | StdCenter();
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 );
Connect( b_import->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ImportSettingsDialog::OnImport_Click) ); Connect( b_import->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ImportSettingsDialog::OnImport_Click) );
Connect( b_over->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ImportSettingsDialog::OnOverwrite_Click) ); Connect( b_over->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ImportSettingsDialog::OnOverwrite_Click) );

View File

@ -21,9 +21,11 @@
using namespace Panels; using namespace Panels;
Dialogs::LogOptionsDialog::LogOptionsDialog( wxWindow* parent, int id ) Dialogs::LogOptionsDialog::LogOptionsDialog( wxWindow* parent )
: wxDialogWithHelpers( parent, id, _("High Volume Logging"), true ) : wxDialogWithHelpers( parent, _("Trace Logging"), true )
{ {
SetName( GetNameStatic() );
m_idealWidth = 480; m_idealWidth = 480;
wxBoxSizer& mainsizer = *new wxBoxSizer( wxVERTICAL ); wxBoxSizer& mainsizer = *new wxBoxSizer( wxVERTICAL );

View File

@ -28,8 +28,11 @@ namespace Dialogs {
class LogOptionsDialog: public wxDialogWithHelpers class LogOptionsDialog: public wxDialogWithHelpers
{ {
public: 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: protected:
void OnOk_Click( wxCommandEvent& evt ); void OnOk_Click( wxCommandEvent& evt );
void OnApply_Click( wxCommandEvent& evt ); void OnApply_Click( wxCommandEvent& evt );

View File

@ -66,7 +66,7 @@ protected:
virtual void OnDoubleClicked( wxCommandEvent& evt ); virtual void OnDoubleClicked( wxCommandEvent& evt );
}; };
class ConfButtons class MsgButtons
{ {
protected: protected:
BITFIELD32() BITFIELD32()
@ -87,29 +87,29 @@ protected:
wxString m_CustomLabel; wxString m_CustomLabel;
public: public:
ConfButtons() : bitset( 0 ) { } MsgButtons() : bitset( 0 ) { }
ConfButtons& OK() { m_OK = true; return *this; } MsgButtons& OK() { m_OK = true; return *this; }
ConfButtons& Cancel() { m_Cancel = true; return *this; } MsgButtons& Cancel() { m_Cancel = true; return *this; }
ConfButtons& Apply() { m_Apply = true; return *this; } MsgButtons& Apply() { m_Apply = true; return *this; }
ConfButtons& Yes() { m_Yes = true; return *this; } MsgButtons& Yes() { m_Yes = true; return *this; }
ConfButtons& No() { m_No = true; return *this; } MsgButtons& No() { m_No = true; return *this; }
ConfButtons& ToAll() { m_AllowToAll = true; return *this; } MsgButtons& ToAll() { m_AllowToAll = true; return *this; }
ConfButtons& Abort() { m_Abort = true; return *this; } MsgButtons& Abort() { m_Abort = true; return *this; }
ConfButtons& Retry() { m_Retry = true; return *this; } MsgButtons& Retry() { m_Retry = true; return *this; }
ConfButtons& Ignore() { m_Ignore = true; return *this; } MsgButtons& Ignore() { m_Ignore = true; return *this; }
ConfButtons& Reset() { m_Reset = true; return *this; } MsgButtons& Reset() { m_Reset = true; return *this; }
ConfButtons& Close() { m_Close = true; return *this; } MsgButtons& Close() { m_Close = true; return *this; }
ConfButtons& Custom( const wxString& label) MsgButtons& Custom( const wxString& label)
{ {
m_CustomLabel = label; m_CustomLabel = label;
return *this; return *this;
} }
ConfButtons& OKCancel() { m_OK = m_Cancel = true; return *this; } MsgButtons& OKCancel() { m_OK = m_Cancel = true; return *this; }
ConfButtons& YesNo() { m_Yes = m_No = true; return *this; } MsgButtons& YesNo() { m_Yes = m_No = true; return *this; }
bool HasOK() const { return m_OK; } bool HasOK() const { return m_OK; }
bool HasCancel() const { return m_Cancel; } bool HasCancel() const { return m_Cancel; }
@ -128,29 +128,45 @@ public:
const wxString& GetCustomLabel() const { return m_CustomLabel; } const wxString& GetCustomLabel() const { return m_CustomLabel; }
bool Allows( wxWindowID id ) const; 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 ); return OpEqu( bitset );
} }
bool operator !=( const ConfButtons& right ) const bool operator !=( const MsgButtons& right ) const
{ {
return !OpEqu( bitset ); 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 namespace Dialogs
{ {
class AboutBoxDialog: public wxDialogWithHelpers class AboutBoxDialog: public wxDialogWithHelpers
{ {
public:
AboutBoxDialog( wxWindow* parent=NULL, int id=DialogId_About );
virtual ~AboutBoxDialog() throw() {}
protected: protected:
//wxStaticBitmap m_bitmap_logo; //wxStaticBitmap m_bitmap_logo;
wxStaticBitmap m_bitmap_dualshock; 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; Panels::LanguageSelectionPanel* m_panel_langsel;
public: public:
PickUserModeDialog( wxWindow* parent, int id=wxID_ANY ); PickUserModeDialog( wxWindow* parent );
virtual ~PickUserModeDialog() throw() {} virtual ~PickUserModeDialog() throw() {}
protected: protected:
@ -180,42 +196,15 @@ namespace Dialogs
void OnOverwrite_Click( wxCommandEvent& evt ); void OnOverwrite_Click( wxCommandEvent& evt );
}; };
class AssertionDialog : public wxDialogWithHelpers
// --------------------------------------------------------------------------------------
// 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
{ {
protected:
wxBoxSizer& m_ExtensibleSizer;
wxBoxSizer& m_ButtonSizer;
ConfButtons m_Buttons;
public: public:
ExtensibleConfirmation( wxWindow* parent, const ConfButtons& type, const wxString& title, const wxString& msg ); AssertionDialog( const wxString& text, const wxString& stacktrace );
virtual ~ExtensibleConfirmation() throw() {} virtual ~AssertionDialog() 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 );
}; };
wxWindowID IssueConfirmation( ExtensibleConfirmation& confirmDlg, const wxString& disablerKey );
} }
wxWindowID pxIssueConfirmation( wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons );
wxWindowID pxIssueConfirmation( wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons, const wxString& disablerKey );

View File

@ -20,21 +20,17 @@
using namespace Panels; using namespace Panels;
Dialogs::PickUserModeDialog::PickUserModeDialog( wxWindow* parent, int id ) Dialogs::PickUserModeDialog::PickUserModeDialog( wxWindow* parent )
: wxDialogWithHelpers( parent, id, _("PCSX2 First Time configuration"), false ) : wxDialogWithHelpers( parent, _("PCSX2 First Time configuration"), wxVERTICAL )
{ {
m_panel_usersel = new UsermodeSelectionPanel( this, false ); m_panel_usersel = new UsermodeSelectionPanel( this, false );
m_panel_langsel = new LanguageSelectionPanel( this ); 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 += 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_langsel | pxSizerFlags::StdCenter();
*this += m_panel_usersel | wxSizerFlags().Expand().Border( wxALL, 8 ); *this += m_panel_usersel | wxSizerFlags().Expand().Border( wxALL, 8 );
AddOkCancel( *GetSizer() ); AddOkCancel( *GetSizer() );
Fit();
CenterOnScreen();
Connect( wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PickUserModeDialog::OnOk_Click ) ); 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. // TODO : Add a command event handler for language changes, that dynamically re-update contents of this window.

View File

@ -49,8 +49,9 @@ GSPanel::GSPanel( wxWindow* parent )
, m_Listener_SettingsApplied( wxGetApp().Source_SettingsApplied(), EventListener<int> ( this, OnSettingsApplied ) ) , m_Listener_SettingsApplied( wxGetApp().Source_SettingsApplied(), EventListener<int> ( this, OnSettingsApplied ) )
, m_HideMouseTimer( this ) , m_HideMouseTimer( this )
{ {
m_CursorShown = true; m_CursorShown = true;
m_HasFocus = false;
if ( !wxWindow::Create(parent, wxID_ANY) ) if ( !wxWindow::Create(parent, wxID_ANY) )
throw Exception::RuntimeError( "GSPanel constructor esplode!!" ); throw Exception::RuntimeError( "GSPanel constructor esplode!!" );
@ -68,6 +69,9 @@ GSPanel::GSPanel( wxWindow* parent )
Connect( wxEVT_SIZE, wxSizeEventHandler (GSPanel::OnResize) ); Connect( wxEVT_SIZE, wxSizeEventHandler (GSPanel::OnResize) );
Connect( wxEVT_KEY_DOWN, wxKeyEventHandler (GSPanel::OnKeyDown) ); 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_DOWN, wxMouseEventHandler(GSPanel::OnShowMouse) );
Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(GSPanel::OnShowMouse) ); Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(GSPanel::OnShowMouse) );
Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(GSPanel::OnShowMouse) ); Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(GSPanel::OnShowMouse) );
@ -131,6 +135,7 @@ void GSPanel::DoResize()
void GSPanel::OnResize(wxSizeEvent& event) void GSPanel::OnResize(wxSizeEvent& event)
{ {
if( IsBeingDeleted() ) return;
DoResize(); DoResize();
//Console.Error( "Size? %d x %d", GetSize().x, GetSize().y ); //Console.Error( "Size? %d x %d", GetSize().x, GetSize().y );
//event. //event.
@ -144,12 +149,14 @@ void GSPanel::OnCloseWindow(wxCloseEvent& evt)
void GSPanel::OnShowMouse( wxMouseEvent& evt ) void GSPanel::OnShowMouse( wxMouseEvent& evt )
{ {
if( IsBeingDeleted() ) return;
evt.Skip(); evt.Skip();
DoShowMouse(); DoShowMouse();
} }
void GSPanel::OnHideMouseTimeout( wxTimerEvent& evt ) void GSPanel::OnHideMouseTimeout( wxTimerEvent& evt )
{ {
if( IsBeingDeleted() || !m_HasFocus ) return;
if( CoreThread.GetExecutionMode() != SysThreadBase::ExecMode_Opened ) return; if( CoreThread.GetExecutionMode() != SysThreadBase::ExecMode_Opened ) return;
SetCursor( wxCursor( wxCURSOR_BLANK ) ); 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 ) void __evt_fastcall GSPanel::OnSettingsApplied( void* obj, int& evt )
{ {
@ -234,12 +261,18 @@ wxWindow* GSFrame::GetViewport()
void GSFrame::OnActivate( wxActivateEvent& evt ) void GSFrame::OnActivate( wxActivateEvent& evt )
{ {
if( IsBeingDeleted() ) return;
evt.Skip(); evt.Skip();
if( wxWindow* gsPanel = FindWindowByName(L"GSPanel") ) gsPanel->SetFocus(); if( wxWindow* gsPanel = FindWindowByName(L"GSPanel") ) gsPanel->SetFocus();
} }
void GSFrame::OnMove( wxMoveEvent& evt ) void GSFrame::OnMove( wxMoveEvent& evt )
{ {
if( IsBeingDeleted() ) return;
evt.Skip();
// evt.GetPosition() returns the client area position, not the window frame position. // evt.GetPosition() returns the client area position, not the window frame position.
if( !IsMaximized() && IsVisible() ) if( !IsMaximized() && IsVisible() )
g_Conf->GSWindow.WindowPos = GetScreenPosition(); g_Conf->GSWindow.WindowPos = GetScreenPosition();

View File

@ -61,11 +61,7 @@ void IniInterface::Flush()
IniScopedGroup::IniScopedGroup( IniInterface& mommy, const wxString& group ) : IniScopedGroup::IniScopedGroup( IniInterface& mommy, const wxString& group ) :
m_mom( mommy ) m_mom( mommy )
{ {
if( IsDevBuild ) pxAssertDev( wxStringTokenize( group, L"/" ).Count() <= 1, L"Cannot nest more than one group deep per instance of IniScopedGroup." );
{
if( wxStringTokenize( group, L"/" ).Count() > 1 )
throw Exception::InvalidArgument( "Cannot nest more than one group deep per instance of IniScopedGroup." );
}
m_mom.SetPath( group ); m_mom.SetPath( group );
} }

View File

@ -38,10 +38,9 @@ bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filen
if( filenames.GetCount() > 1 ) if( filenames.GetCount() > 1 )
{ {
Dialogs::ExtensibleConfirmation( m_WindowBound, ConfButtons().Cancel(), wxDialogWithHelpers dialog( m_WindowBound, _("Drag and Drop Error"), wxVERTICAL );
_("Drag and Drop Error"), dialog += dialog.Heading( _("It is an error to drop multiple files onto a PCSX2 window. One at a time please, thank you.") );
_("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; return false;
} }
@ -69,13 +68,14 @@ bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filen
bool confirmed = true; bool confirmed = true;
if( SysHasValidState() ) if( SysHasValidState() )
{ {
Dialogs::ExtensibleConfirmation dialog( m_WindowBound, ConfButtons().Reset().Cancel(), wxDialogWithHelpers dialog( m_WindowBound, _("Confirm PS2 Reset"), wxVERTICAL );
_("Confirm PS2 Reset"),
dialog += dialog.Heading(
_("You have dropped the following ELF binary into PCSX2:\n\n") + _("You have dropped the following ELF binary into PCSX2:\n\n") +
filenames[0] + L"\n\n" + GetMsg_ConfirmSysReset() 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 ) if( confirmed )
@ -111,14 +111,14 @@ bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filen
if( SysHasValidState() ) if( SysHasValidState() )
{ {
Dialogs::ExtensibleConfirmation dialog( m_WindowBound, ConfButtons().Reset().Cancel().Custom(_("Swap Disc")), wxDialogWithHelpers dialog( m_WindowBound, _("Confirm PS2 Reset"), wxVERTICAL );
_("Confirm PS2 Reset"),
_("You have dropped the following ISO image into PCSX2:\n\n") + dialog += dialog.Heading(_("You have dropped the following ISO image into PCSX2:\n\n") +
filenames[0] + L"\n\n" + filenames[0] + L"\n\n" +
_("Do you want to swap discs or boot the new image (via system reset)?") _("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 ) if( result != wxID_CANCEL )

View File

@ -31,6 +31,7 @@ protected:
EventListenerBinding<int> m_Listener_SettingsApplied; EventListenerBinding<int> m_Listener_SettingsApplied;
wxTimer m_HideMouseTimer; wxTimer m_HideMouseTimer;
bool m_CursorShown; bool m_CursorShown;
bool m_HasFocus;
public: public:
GSPanel( wxWindow* parent ); GSPanel( wxWindow* parent );
@ -49,6 +50,8 @@ protected:
void OnShowMouse( wxMouseEvent& evt ); void OnShowMouse( wxMouseEvent& evt );
void OnHideMouseTimeout( wxTimerEvent& evt ); void OnHideMouseTimeout( wxTimerEvent& evt );
void OnKeyDown( wxKeyEvent& evt ); void OnKeyDown( wxKeyEvent& evt );
void OnFocus( wxFocusEvent& evt );
void OnFocusLost( wxFocusEvent& evt );
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -28,6 +28,15 @@
using namespace Dialogs; 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(); extern wxString GetMsg_ConfirmSysReset();
void MainEmuFrame::SaveEmuOptions() void MainEmuFrame::SaveEmuOptions()
@ -41,12 +50,12 @@ void MainEmuFrame::SaveEmuOptions()
void MainEmuFrame::Menu_ConfigSettings_Click(wxCommandEvent &event) void MainEmuFrame::Menu_ConfigSettings_Click(wxCommandEvent &event)
{ {
Dialogs::ConfigurationDialog( this ).ShowModal(); AppOpenDialog<ConfigurationDialog>( this );
} }
void MainEmuFrame::Menu_SelectBios_Click(wxCommandEvent &event) void MainEmuFrame::Menu_SelectBios_Click(wxCommandEvent &event)
{ {
Dialogs::BiosSelectorDialog( this ).ShowModal(); AppOpenDialog<BiosSelectorDialog>( this );
} }
void MainEmuFrame::Menu_CdvdSource_Click( wxCommandEvent &event ) 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. // 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. // 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" + _("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.") _("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; selector = true;
} }
@ -141,11 +153,9 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
if( SysHasValidState() ) if( SysHasValidState() )
{ {
ExtensibleConfirmation dialog( this, ConfButtons().Yes().Cancel(), wxDialogWithHelpers dialog( this, _("Confirm PS2 Reset"), wxVERTICAL );
_("Confirm PS2 Reset"), GetMsg_ConfirmSysReset() dialog += dialog.Heading( GetMsg_ConfirmSysReset() );
); bool confirmed = (pxIssueConfirmation( dialog, MsgButtons().Yes().Cancel(), L"BootCdvd:ConfirmReset" ) != wxID_CANCEL);
bool confirmed = (IssueConfirmation( dialog, L"BootCdvd:ConfirmReset" ) != wxID_CANCEL);
if( !confirmed ) if( !confirmed )
{ {
@ -290,7 +300,7 @@ void MainEmuFrame::Menu_Debug_MemoryDump_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_Debug_Logging_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) void MainEmuFrame::Menu_ShowConsole(wxCommandEvent &event)
@ -316,5 +326,5 @@ void MainEmuFrame::Menu_PrintCDVD_Info(wxCommandEvent &event)
void MainEmuFrame::Menu_ShowAboutBox(wxCommandEvent &event) void MainEmuFrame::Menu_ShowAboutBox(wxCommandEvent &event)
{ {
AboutBoxDialog( this, wxID_ANY ).ShowModal(); AppOpenDialog<AboutBoxDialog>( this );
} }

View File

@ -15,130 +15,277 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "App.h" #include "App.h"
#include "Dialogs/ModalPopups.h"
DEFINE_EVENT_TYPE( pxEVT_MSGBOX ); DEFINE_EVENT_TYPE( pxEVT_MSGBOX );
DEFINE_EVENT_TYPE( pxEVT_CallStackBox ); DEFINE_EVENT_TYPE( pxEVT_ASSERTION );
using namespace Threading; using namespace Threading;
using namespace pxSizerFlags;
// Thread Safety: Must be called from the GUI thread ONLY. // Thread Safety: Must be called from the GUI thread ONLY. Will assert otherwise.
static int pxMessageDialog( const wxString& content, const wxString& caption, long flags ) //
// [TODO] Add support for icons?
//
static int pxMessageDialog( const wxString& caption, const wxString& content, const MsgButtons& buttons )
{ {
if( IsDevBuild && !wxThread::IsMain() ) if( !AffinityAssert_AllowFromMain() ) return wxID_CANCEL;
throw Exception::InvalidOperation( "Function must be called by the main GUI thread only." );
// fixme: If the emulator is currently active and is running in fullscreen mode, then we // fixme: If the emulator is currently active and is running in exclusive mode (forced
// need to either: // fullscreen), then we need to either:
// 1) Exit fullscreen mode before issuing the popup. // 1) Exit fullscreen mode before issuing the popup.
// 2) Issue the popup with wxSTAY_ON_TOP specified so that the user will see it. // 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. // 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. class BaseMessageBoxEvent : public wxEvent
// 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 )
{ {
if( IsDevBuild && !wxThread::IsMain() ) DECLARE_DYNAMIC_CLASS_NO_ASSIGN(BaseMessageBoxEvent)
throw Exception::InvalidOperation( "Function must be called by the main GUI thread only." );
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 // 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: typedef BaseMessageBoxEvent _parent;
MsgboxEventResult& m_Instdata; DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxMessageBoxEvent)
wxString m_Title;
wxString m_Content;
long m_Flags;
protected:
wxString m_Title;
MsgButtons m_Buttons;
public: public:
pxMessageBoxEvent() pxMessageBoxEvent( int msgtype=pxEVT_MSGBOX )
: wxEvent( 0, pxEVT_MSGBOX ) : BaseMessageBoxEvent( msgtype )
, m_Instdata( *(MsgboxEventResult*)NULL )
, m_Title()
, m_Content()
{ {
m_Flags = 0;
} }
pxMessageBoxEvent( MsgboxEventResult& instdata, const wxString& title, const wxString& content, long flags ) virtual ~pxMessageBoxEvent() throw() { }
: wxEvent( 0, pxEVT_MSGBOX ) virtual pxMessageBoxEvent *Clone() const { return new pxMessageBoxEvent(*this); }
, m_Instdata( instdata )
pxMessageBoxEvent( MsgboxEventResult& instdata, const wxString& title, const wxString& content, const MsgButtons& buttons )
: BaseMessageBoxEvent( instdata, content )
, m_Title( title ) , 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 ) pxMessageBoxEvent( const pxMessageBoxEvent& event )
: wxEvent( event ) : BaseMessageBoxEvent( event )
, m_Instdata( event.m_Instdata )
, m_Title( event.m_Title ) , 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. pxMessageBoxEvent& SetInstData( MsgboxEventResult& instdata )
void DoTheDialog()
{ {
int result; _parent::SetInstData( instdata );
return *this;
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();
} }
virtual wxEvent *Clone() const { return new pxMessageBoxEvent(*this); } protected:
virtual int _DoDialog() const
private: {
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxMessageBoxEvent) 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 namespace Msgbox
{ {
// parameters: static int ThreadedMessageBox( BaseMessageBoxEvent& evt )
// flags - messagebox type flags, such as wxOK, wxCANCEL, etc. {
// MsgboxEventResult instdat;
static int ThreadedMessageBox( const wxString& content, const wxString& title, long flags, int boxType=pxEVT_MSGBOX ) 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 // 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. // threaded chaos where our thread keeps running while the popup is awaiting input.
MsgboxEventResult instdat; MsgboxEventResult instdat;
pxMessageBoxEvent tevt( instdat, title, content, flags ); pxMessageBoxEvent tevt( instdat, title, content, buttons );
wxGetApp().AddPendingEvent( tevt ); wxGetApp().AddPendingEvent( tevt );
instdat.WaitForMe.WaitNoCancel(); // Important! disable cancellation since we're using local stack vars. instdat.WaitForMe.WaitNoCancel(); // Important! disable cancellation since we're using local stack vars.
return instdat.result; return instdat.result;
} }
void OnEvent( pxMessageBoxEvent& evt )
{
evt.DoTheDialog();
}
// Pops up an alert Dialog Box with a singular "OK" button. // Pops up an alert Dialog Box with a singular "OK" button.
// Always returns false. // Always returns false.
bool Alert( const wxString& text, const wxString& caption, int icon ) bool Alert( const wxString& text, const wxString& caption, int icon )
{ {
icon |= wxOK; MsgButtons buttons( MsgButtons().OK() );
if( wxThread::IsMain() ) if( wxThread::IsMain() )
pxMessageDialog( text, caption, icon ); pxMessageDialog( caption, text, buttons );
else else
ThreadedMessageBox( text, caption, icon ); ThreadedMessageBox( caption, text, buttons );
return false; return false;
} }
@ -146,60 +293,40 @@ namespace Msgbox
// true if OK, false if cancel. // true if OK, false if cancel.
bool OkCancel( const wxString& text, const wxString& caption, int icon ) bool OkCancel( const wxString& text, const wxString& caption, int icon )
{ {
icon |= wxOK | wxCANCEL; MsgButtons buttons( MsgButtons().OKCancel() );
if( wxThread::IsMain() ) if( wxThread::IsMain() )
{ {
return wxID_OK == pxMessageDialog( text, caption, icon ); return wxID_OK == pxMessageDialog( caption, text, buttons );
} }
else 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 ) bool YesNo( const wxString& text, const wxString& caption, int icon )
{ {
icon |= wxYES_NO; MsgButtons buttons( MsgButtons().YesNo() );
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 );
if( wxThread::IsMain() ) if( wxThread::IsMain() )
{ {
return pxCallstackDialog( text, caption, buttons ); return wxID_YES == pxMessageDialog( caption, text, buttons );
} }
else else
{ {
return ThreadedMessageBox( text, caption, buttons, pxEVT_CallStackBox ); return wxID_YES == ThreadedMessageBox( caption, text, buttons );
} }
} }
int Assertion( const wxString& text, const wxString& stacktrace ) int Assertion( const wxString& text, const wxString& stacktrace )
{ {
return CallStack( text, stacktrace, pxAssertionEvent tevt( text, stacktrace );
L"\nDo you want to stop the program?" return ThreadedMessageBox( tevt );
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 );
} }
} }
void Pcsx2App::OnMessageBox( pxMessageBoxEvent& evt )
{
evt.IssueDialog();
}

View File

@ -46,8 +46,8 @@ Panels::BaseAdvancedCpuOptions::BaseAdvancedCpuOptions( wxWindow* parent )
// Highlight Default Options: // Highlight Default Options:
m_RoundModePanel->SetDefault( 3 ); m_RoundModePanel->SetDefaultItem( 3 );
m_ClampModePanel->SetDefault( 1 ); m_ClampModePanel->SetDefaultItem( 1 );
// --------------------------------- // ---------------------------------
// The Fitting And Sizing Area // 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_RecEE = &(new pxRadioPanel( this, tbl_CpuTypes_EE ))->SetDefaultItem( 1 );
m_panel_RecIOP = &(new pxRadioPanel( this, tbl_CpuTypes_IOP ))->SetDefault( 1 ); m_panel_RecIOP = &(new pxRadioPanel( this, tbl_CpuTypes_IOP ))->SetDefaultItem( 1 );
m_panel_RecEE->Realize(); m_panel_RecEE->Realize();
m_panel_RecIOP->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.")) .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_VU0 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefaultItem( 1 );
m_panel_VU1 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefault( 1 ); m_panel_VU1 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefaultItem( 1 );
m_panel_VU0->Realize(); m_panel_VU0->Realize();
m_panel_VU1->Realize(); m_panel_VU1->Realize();
@ -233,6 +233,9 @@ void Panels::CpuPanelEE::Apply()
void Panels::CpuPanelEE::OnSettingsChanged() 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 ); const Pcsx2Config::RecompilerOptions& recOps( g_Conf->EmuOptions.Cpu.Recompiler );
m_panel_RecEE->SetSelection( (int)recOps.EnableEE ); m_panel_RecEE->SetSelection( (int)recOps.EnableEE );
m_panel_RecIOP->SetSelection( (int)recOps.EnableIOP ); m_panel_RecIOP->SetSelection( (int)recOps.EnableIOP );
@ -250,6 +253,12 @@ void Panels::CpuPanelVU::Apply()
void Panels::CpuPanelVU::OnSettingsChanged() 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 ); Pcsx2Config::RecompilerOptions& recOps( g_Conf->EmuOptions.Cpu.Recompiler );
if( recOps.UseMicroVU0 ) if( recOps.UseMicroVU0 )
m_panel_VU0->SetSelection( recOps.EnableVU0 ? 1 : 0 ); m_panel_VU0->SetSelection( recOps.EnableVU0 ? 1 : 0 );

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "App.h" #include "App.h"
#include "Plugins.h" #include "Plugins.h"
#include "SaveState.h"
#include "Utilities/ScopedPtr.h" #include "Utilities/ScopedPtr.h"
#include "ConfigurationPanels.h" #include "ConfigurationPanels.h"
#include "Dialogs/ModalPopups.h" #include "Dialogs/ModalPopups.h"
@ -309,37 +310,43 @@ void Panels::PluginSelectorPanel::Apply()
break; break;
} while( ++pi, pi->shortname != NULL ); } while( ++pi, pi->shortname != NULL );
bool isSuspended = false;
if( pi->shortname != NULL ) if( pi->shortname != NULL )
{ {
if( CoreThread.IsRunning() ) if( CoreThread.IsRunning() )
{ {
// [TODO] : Post notice that this shuts down existing emulation, and may not safely recover. // [TODO] : Post notice that this shuts down existing emulation, and may not safely recover.
Dialogs::ExtensibleConfirmation dialog( this, ConfButtons().OK().Cancel(), wxDialogWithHelpers dialog( this, _("Shutdown PS2 virtual machine?"), wxVERTICAL );
_("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?"
)
);
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 ) if( result == wxID_CANCEL )
throw Exception::CannotApplySettings( this, "Cannot apply settings: canceled by user because plugins changed while the emulation state was active.", false ); 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 ) if( !wxGetApp().m_CorePlugins )
{ {
try try {
{
LoadPluginsImmediate(); LoadPluginsImmediate();
} }
catch( Exception::PluginError& ex ) catch( Exception::PluginError& ex )
@ -359,6 +366,8 @@ void Panels::PluginSelectorPanel::Apply()
); );
} }
} }
if( isSuspended ) CoreThread.Resume();
} }
void Panels::PluginSelectorPanel::CancelRefresh() void Panels::PluginSelectorPanel::CancelRefresh()
@ -591,6 +600,7 @@ void Panels::PluginSelectorPanel::EnumThread::ExecuteTaskInThread()
{ {
DevCon.WriteLn( "Plugin Enumeration Thread started..." ); DevCon.WriteLn( "Plugin Enumeration Thread started..." );
Sleep( 15 ); // give the window some time to paint.
YieldToMain(); YieldToMain();
for( int curidx=0; curidx < m_master.FileCount(); ++curidx ) for( int curidx=0; curidx < m_master.FileCount(); ++curidx )

View File

@ -26,7 +26,7 @@
RecentIsoManager::RecentIsoManager( wxMenu* menu ) RecentIsoManager::RecentIsoManager( wxMenu* menu )
: m_Menu( 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_SettingsLoadSave ( wxGetApp().Source_SettingsLoadSave(), EventListener<IniInterface>( this, OnSettingsLoadSave ) )
, m_Listener_SettingsApplied ( wxGetApp().Source_SettingsApplied(), EventListener<int>( this, OnSettingsApplied ) ) , m_Listener_SettingsApplied ( wxGetApp().Source_SettingsApplied(), EventListener<int>( this, OnSettingsApplied ) )
{ {
@ -166,7 +166,7 @@ void RecentIsoManager::DoSettingsLoadSave( IniInterface& ini )
{ {
RemoveAllFromMenu(); RemoveAllFromMenu();
m_MaxLength = g_Conf->RecentFileCount; m_MaxLength = g_Conf->RecentIsoCount;
IniScopedGroup groupie( ini, L"RecentIso" ); IniScopedGroup groupie( ini, L"RecentIso" );
for( uint i=0; i<m_MaxLength; ++i ) for( uint i=0; i<m_MaxLength; ++i )
{ {

View File

@ -141,7 +141,7 @@ const wxChar* __fastcall pxGetTranslation( const wxChar* message )
if( wxStrlen( message ) > 96 ) if( wxStrlen( message ) > 96 )
{ {
Console.Warning( "pxGetTranslation: Long message detected, maybe use pxE() instead?" ); 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 ); return wxGetTranslation( message );

View File

@ -31,7 +31,6 @@ void __evt_fastcall pxLogTextCtrl::OnCoreThreadStatusChanged( void* obj, wxComma
if( mframe->HasWriteLock() ) return; if( mframe->HasWriteLock() ) return;
::SendMessage((HWND)mframe->GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL); ::SendMessage((HWND)mframe->GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL);
mframe->m_win32_StupidRefreshTricks = 0;
#endif #endif
} }
@ -45,7 +44,6 @@ void __evt_fastcall pxLogTextCtrl::OnCorePluginStatusChanged( void* obj, PluginE
if( mframe->HasWriteLock() ) return; if( mframe->HasWriteLock() ) return;
::SendMessage((HWND)mframe->GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL); ::SendMessage((HWND)mframe->GetHWND(), WM_VSCROLL, SB_BOTTOM, (LPARAM)NULL);
mframe->m_win32_StupidRefreshTricks = 0;
#endif #endif
} }
@ -58,10 +56,11 @@ pxLogTextCtrl::pxLogTextCtrl( wxWindow* parent )
, m_Listener_CorePluginStatus ( wxGetApp().Source_CorePluginStatus(), EventListener<PluginEventType> ( this, OnCorePluginStatusChanged ) ) , m_Listener_CorePluginStatus ( wxGetApp().Source_CorePluginStatus(), EventListener<PluginEventType> ( this, OnCorePluginStatusChanged ) )
{ {
#ifdef __WXMSW__ #ifdef __WXMSW__
m_win32_StupidRefreshTricks = 0; m_win32_LinesPerScroll = 10;
m_win32_LinesPerScroll = 10; m_win32_LinesPerPage = 0;
#endif #endif
m_FreezeWrites = false; m_IsPaused = false;
m_FreezeWrites = false;
Connect( wxEVT_SCROLLWIN_THUMBTRACK, wxScrollWinEventHandler(pxLogTextCtrl::OnThumbTrack) ); Connect( wxEVT_SCROLLWIN_THUMBTRACK, wxScrollWinEventHandler(pxLogTextCtrl::OnThumbTrack) );
Connect( wxEVT_SCROLLWIN_THUMBRELEASE, wxScrollWinEventHandler(pxLogTextCtrl::OnThumbRelease) ); Connect( wxEVT_SCROLLWIN_THUMBRELEASE, wxScrollWinEventHandler(pxLogTextCtrl::OnThumbRelease) );
@ -85,14 +84,12 @@ void pxLogTextCtrl::OnResize( wxSizeEvent& evt )
int fonty; int fonty;
GetTextExtent( L"blaH yeah", NULL, &fonty ); GetTextExtent( L"blaH yeah", NULL, &fonty );
m_win32_LinesPerPage = (ctrly / fonty) + 1; m_win32_LinesPerPage = (ctrly / fonty) + 1;
m_win32_LinesPerScroll = m_win32_LinesPerPage * 0.25; m_win32_LinesPerScroll = m_win32_LinesPerPage * 0.40;
#endif #endif
evt.Skip(); evt.Skip();
} }
bool m_IsPaused = false;
void pxLogTextCtrl::OnThumbTrack(wxScrollWinEvent& evt) void pxLogTextCtrl::OnThumbTrack(wxScrollWinEvent& evt)
{ {
//Console.Warning( "Thumb Tracking!!!" ); //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 // 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. // log aligns itself nicely when we pause emulation or when errors occur.
//m_win32_StupidRefreshTricks += lines; wxTextPos showpos = XYToPosition( 1, GetNumberOfLines()-m_win32_LinesPerScroll );
//if( m_win32_StupidRefreshTricks > 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 #endif
} }

View File

@ -1847,6 +1847,10 @@
RelativePath="..\..\gui\AdvancedDialog.h" RelativePath="..\..\gui\AdvancedDialog.h"
> >
</File> </File>
<File
RelativePath="..\..\gui\Dialogs\AssertionDialog.cpp"
>
</File>
<File <File
RelativePath="..\..\gui\Dialogs\ConfigurationDialog.cpp" RelativePath="..\..\gui\Dialogs\ConfigurationDialog.cpp"
> >

View File

@ -25,7 +25,8 @@ void StreamException_ThrowFromErrno( const wxString& streamname, errno_t errcode
switch( errcode ) switch( errcode )
{ {
case EINVAL: case EINVAL:
throw Exception::InvalidArgument( "Invalid argument" ); pxFailDev( L"Invalid argument" );
throw Exception::Stream( streamname, "Invalid argument" );
case EACCES: // Access denied! case EACCES: // Access denied!
throw Exception::AccessDenied( streamname ); throw Exception::AccessDenied( streamname );
@ -75,7 +76,7 @@ void StreamException_ThrowLastError( const wxString& streamname, HANDLE result )
throw Exception::AccessDenied( streamname ); throw Exception::AccessDenied( streamname );
case ERROR_INVALID_HANDLE: 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: case ERROR_SHARING_VIOLATION:
throw Exception::AccessDenied( streamname, "Sharing violation" ); throw Exception::AccessDenied( streamname, "Sharing violation" );

View File

@ -99,7 +99,7 @@ public:
protected: protected:
void ExecuteTaskInThread() void ExecuteTaskInThread()
{ {
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL ); ::SetThreadPriority( ::GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL );
if( m_outpipe == INVALID_HANDLE_VALUE ) return; if( m_outpipe == INVALID_HANDLE_VALUE ) return;
try try
@ -112,7 +112,7 @@ protected:
if( !ReadFile(m_outpipe, s8_Buf, sizeof(s8_Buf)-1, &u32_Read, NULL) ) if( !ReadFile(m_outpipe, s8_Buf, sizeof(s8_Buf)-1, &u32_Read, NULL) )
{ {
DWORD result = GetLastError(); DWORD result = GetLastError();
if( result == ERROR_HANDLE_EOF ) break; if( result == ERROR_HANDLE_EOF || result == ERROR_BROKEN_PIPE ) break;
if( result == ERROR_IO_PENDING ) if( result == ERROR_IO_PENDING )
{ {
Yield( 10 ); Yield( 10 );
@ -172,6 +172,10 @@ class WinPipeRedirection : public PipeRedirectionBase
DeclareNoncopyableObject( WinPipeRedirection ); DeclareNoncopyableObject( WinPipeRedirection );
protected: protected:
DWORD m_stdhandle;
FILE* m_stdfp;
FILE m_stdfp_copy;
HANDLE m_readpipe; HANDLE m_readpipe;
HANDLE m_writepipe; HANDLE m_writepipe;
int m_crtFile; int m_crtFile;
@ -187,21 +191,25 @@ public:
}; };
WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) WinPipeRedirection::WinPipeRedirection( FILE* stdstream )
: m_readpipe(INVALID_HANDLE_VALUE) : m_Thread( m_readpipe, (stdstream == stderr) ? Color_Red : Color_Black )
, m_writepipe(INVALID_HANDLE_VALUE)
, m_crtFile(-1)
, m_fp(NULL)
, 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 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 ) ) if( 0 == CreatePipe( &m_readpipe, &m_writepipe, NULL, 0 ) )
throw Exception::Win32Error( "CreatePipe failed." ); throw Exception::Win32Error( "CreatePipe failed." );
if( 0 == SetStdHandle( stdhandle, m_writepipe ) ) if( 0 == SetStdHandle( m_stdhandle, m_writepipe ) )
throw Exception::Win32Error( "SetStdHandle failed." ); throw Exception::Win32Error( "SetStdHandle failed." );
// Note: Don't use GetStdHandle to "confirm" the handle. // Note: Don't use GetStdHandle to "confirm" the handle.
@ -222,11 +230,19 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream )
if( m_fp == NULL ) if( m_fp == NULL )
throw Exception::RuntimeError( "_fdopen returned NULL." ); throw Exception::RuntimeError( "_fdopen returned NULL." );
*stdstream = *m_fp; *m_stdfp = *m_fp; // omg hack. but it works >_<
setvbuf( stdstream, NULL, _IONBF, 0 ); setvbuf( stdstream, NULL, _IONBF, 0 );
m_Thread.Start(); 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 ) catch( Exception::BaseException& ex )
{ {
Cleanup(); Cleanup();
@ -235,6 +251,9 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream )
} }
catch( ... ) 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(); Cleanup();
throw; throw;
} }
@ -247,6 +266,12 @@ WinPipeRedirection::~WinPipeRedirection()
void WinPipeRedirection::Cleanup() throw() 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: // Cleanup Order Notes:
// * The redirection thread is most likely blocking on ReadFile(), so we can't Cancel yet, lest we deadlock -- // * 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, // Closing the writepipe (either directly or through the fp/crt handles) issues an EOF to the thread,

View File

@ -495,31 +495,44 @@ static const uint m_recBlockAllocSize =
(((Ps2MemSize::Base + Ps2MemSize::Rom + Ps2MemSize::Rom1) / 4) * sizeof(BASEBLOCK)) (((Ps2MemSize::Base + Ps2MemSize::Rom + Ps2MemSize::Rom1) / 4) * sizeof(BASEBLOCK))
+ RECCONSTBUF_SIZE * sizeof(u32) + Ps2MemSize::Base; + 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() static void recAlloc()
{ {
// Hardware Requirements Check... // Hardware Requirements Check...
if ( !( x86caps.hasMultimediaExtensions ) ) if ( !x86caps.hasMultimediaExtensions )
throw Exception::HardwareDeficiency( "Processor doesn't support MMX" ); recThrowHardwareDeficiency( L"MMX" );
if ( !( x86caps.hasStreamingSIMDExtensions ) ) if ( !x86caps.hasStreamingSIMDExtensions )
throw Exception::HardwareDeficiency( "Processor doesn't support SSE" ); recThrowHardwareDeficiency( L"SSE" );
if ( !( x86caps.hasStreamingSIMD2Extensions ) ) if ( !x86caps.hasStreamingSIMD2Extensions )
throw Exception::HardwareDeficiency( "Processor doesn't support SSE2" ); recThrowHardwareDeficiency( L"SSE2" );
if( recMem == NULL ) if( recMem == NULL )
{ {
// Note: the VUrec depends on being able to grab an allocation below the 0x10000000 line, // It's handy to have a constant base address for the EE recompiler buffer, since it
// so we give the EErec an address above that to try first as it's basemem address, hence // allows me to key in the address directly in the debugger, and also recognize EE
// the 0x20000000 pick. // 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; const uint cachememsize = REC_CACHEMEM+0x1000;
recMem = (u8*)SysMmapEx( 0x20000000, cachememsize, 0, "recAlloc(R5900)" ); recMem = (u8*)SysMmapEx( 0x20000000, cachememsize, 0, "recAlloc(R5900)" );
} }
if( recMem == NULL ) 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. // 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 // 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 ); m_recBlockAlloc = (u8*) _aligned_malloc( m_recBlockAllocSize, 4096 );
if( m_recBlockAlloc == NULL ) 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; u8* curpos = m_recBlockAlloc;
recRAM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Base / 4) * sizeof(BASEBLOCK); recRAM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Base / 4) * sizeof(BASEBLOCK);
recROM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom / 4) * sizeof(BASEBLOCK); recROM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom / 4) * sizeof(BASEBLOCK);
recROM1 = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom1 / 4) * sizeof(BASEBLOCK); recROM1 = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom1 / 4) * sizeof(BASEBLOCK);
recConstBuf = (u32*)curpos; curpos += RECCONSTBUF_SIZE * sizeof(u32); recConstBuf = (u32*)curpos; curpos += RECCONSTBUF_SIZE * sizeof(u32);
recRAMCopy = (u32*)curpos; recRAMCopy = (u32*)curpos;
if( s_pInstCache == NULL ) if( s_pInstCache == NULL )
{ {
@ -545,7 +558,7 @@ static void recAlloc()
} }
if( s_pInstCache == NULL ) 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: // No errors.. Proceed with initialization:
@ -564,11 +577,13 @@ struct ManualPageTracking
static __aligned16 u16 manual_page[Ps2MemSize::Base >> 12]; static __aligned16 u16 manual_page[Ps2MemSize::Base >> 12];
static __aligned16 u8 manual_counter[Ps2MemSize::Base >> 12]; static __aligned16 u8 manual_counter[Ps2MemSize::Base >> 12];
static bool eeRecIsReset = false; static u32 eeRecIsReset = 0;
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
void recResetEE( void ) void recResetEE( void )
{ {
if( AtomicExchange( eeRecIsReset, true ) ) return;
Console.WriteLn( Color_StrongBlack, "Issuing EE/iR5900-32 Recompiler Reset" ); Console.WriteLn( Color_StrongBlack, "Issuing EE/iR5900-32 Recompiler Reset" );
maxrecmem = 0; maxrecmem = 0;
@ -630,7 +645,6 @@ void recResetEE( void )
x86FpuState = FPU_STATE; x86FpuState = FPU_STATE;
branch = 0; branch = 0;
eeRecIsReset = true;
} }
static void recShutdown( void ) static void recShutdown( void )
@ -656,26 +670,21 @@ static jmp_buf m_SetJmp_StateCheck;
static void recCheckExecutionState() 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 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 #endif
}
} }
static void recExecute() 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! // [TODO] fix this comment to explain various code entry/exit points, when I'm not so tired!
#if PCSX2_SEH #if PCSX2_SEH
try { eeRecIsReset = false;
while( true ) g_EEFreezeRegs = true;
{
eeRecIsReset = false;
g_EEFreezeRegs = true;
try { try {
EnterRecompiledCode(); EnterRecompiledCode();
}
catch( Exception::ForceDispatcherReg& ) { }
}
} }
catch( Exception::ExitRecExecute& ) {} catch( Exception::ForceDispatcherReg& ) { }
#else #else
switch( setjmp( m_SetJmp_StateCheck ) ) int oldstate;
if( !setjmp( m_SetJmp_StateCheck ) )
{ {
case 0: // first run, fall through to Dispatcher eeRecIsReset = false;
case SetJmp_Dispatcher: g_EEFreezeRegs = true;
while( true )
{
int oldstate;
// Important! Most of the console logging and such has cancel points in it. This is great // 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 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 // in Linux, which cannot have a C++ exception cross the recompiler. Hence the changing
// of the cancelstate here! // of the cancelstate here!
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate ); pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
SysCoreThread::Get().StateCheckInThread(); EnterRecompiledCode();
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
// Generally unreachable code here ...
eeRecIsReset = false; }
else
#ifdef _WIN32 {
__try { pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
#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;
} }
#endif #endif
} }
@ -858,7 +839,7 @@ static void recExitExecution()
#if PCSX2_SEH #if PCSX2_SEH
throw Exception::ExitRecExecute(); throw Exception::ExitRecExecute();
#else #else
longjmp( m_SetJmp_StateCheck, SetJmp_Exit ); longjmp( m_SetJmp_StateCheck, 1 );
#endif #endif
} }
@ -1319,7 +1300,7 @@ static void __fastcall recRecompile( const u32 startpc )
if (dumplog & 4) iDumpRegisters(startpc, 0); if (dumplog & 4) iDumpRegisters(startpc, 0);
#endif #endif
pxAssert( startpc ); pxAssume( startpc );
// if recPtr reached the mem limit reset whole mem // if recPtr reached the mem limit reset whole mem
if ( ( (uptr)recPtr - (uptr)recMem ) >= REC_CACHEMEM-0x40000 || dumplog == 0xffffffff) { 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); s_pCurBlockEx = recBlocks.New(HWADDR(startpc), (uptr)recPtr);
pxAssert(s_pCurBlockEx); pxAssume(s_pCurBlockEx);
branch = 0; branch = 0;
@ -1359,7 +1340,7 @@ static void __fastcall recRecompile( const u32 startpc )
g_cpuHasConstReg = g_cpuFlushedConstReg = 1; g_cpuHasConstReg = g_cpuFlushedConstReg = 1;
g_cpuPrevRegHasLive1 = g_cpuRegHasLive1 = 0xffffffff; g_cpuPrevRegHasLive1 = g_cpuRegHasLive1 = 0xffffffff;
g_cpuPrevRegHasSignExt = g_cpuRegHasSignExt = 0; g_cpuPrevRegHasSignExt = g_cpuRegHasSignExt = 0;
pxAssert( g_cpuConstRegs[0].UD[0] == 0 ); pxAssume( g_cpuConstRegs[0].UD[0] == 0 );
_initX86regs(); _initX86regs();
_initXMMregs(); _initXMMregs();

View File

@ -67,20 +67,20 @@ const __aligned(32) mVU_Globals mVUglob = {
// Micro VU - Main Functions // Micro VU - Main Functions
//------------------------------------------------------------------ //------------------------------------------------------------------
microVUt(void) mVUthrowHardwareDeficiency( const wxChar* extFail ) microVUt(void) mVUthrowHardwareDeficiency( const wxChar* extFail, int vuIndex )
{ {
throw Exception::HardwareDeficiency( throw Exception::HardwareDeficiency(
L"microVU init error: SSE1 is not available!", 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."), L"SSE" ) wxsFormat(_("%s Extensions not found. microVU requires a host CPU with MMX, SSE, and SSE2 extensions."), extFail )
); );
} }
// Only run this once per VU! ;) // Only run this once per VU! ;)
microVUt(void) mVUinit(VURegs* vuRegsPtr, int vuIndex) { microVUt(void) mVUinit(VURegs* vuRegsPtr, int vuIndex) {
if(!x86caps.hasMultimediaExtensions) mVUthrowHardwareDeficiency( L"MMX" ); if(!x86caps.hasMultimediaExtensions) mVUthrowHardwareDeficiency( L"MMX", vuIndex );
if(!x86caps.hasStreamingSIMDExtensions) mVUthrowHardwareDeficiency( L"SSE" ); if(!x86caps.hasStreamingSIMDExtensions) mVUthrowHardwareDeficiency( L"SSE", vuIndex );
if(!x86caps.hasStreamingSIMD2Extensions) mVUthrowHardwareDeficiency( L"SSE2" ); if(!x86caps.hasStreamingSIMD2Extensions) mVUthrowHardwareDeficiency( L"SSE2", vuIndex );
microVU* mVU = mVUx; microVU* mVU = mVUx;
memset(&mVU->prog, 0, sizeof(mVU->prog)); memset(&mVU->prog, 0, sizeof(mVU->prog));
@ -182,17 +182,19 @@ microVUt(void) mVUclear(mV, u32 addr, u32 size) {
// Clears program data // Clears program data
microVUf(void) mVUclearProg(int progIndex) { microVUf(void) mVUclearProg(int progIndex) {
microVU* mVU = mVUx; microVU* mVU = mVUx;
mVUprogI.used = 0; microProgram& program = mVU->prog.prog[progIndex];
mVUprogI.isDead = 1;
mVUprogI.isOld = 1; program.used = 0;
mVUprogI.frame = mVU->prog.curFrame; program.isDead = 1;
for (int j = 0; j <= mVUprogI.ranges.max; j++) { program.isOld = 1;
mVUprogI.ranges.range[j][0] = -1; // Set range to program.frame = mVU->prog.curFrame;
mVUprogI.ranges.range[j][1] = -1; // indeterminable status for (int j = 0; j <= program.ranges.max; j++) {
mVUprogI.ranges.total = -1; 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++) { 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 ;) 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 // recMicroVU0
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -331,13 +336,21 @@ recMicroVU0::recMicroVU0() {
} }
void recMicroVU0::Allocate() { void recMicroVU0::Allocate() {
if( AtomicIncrement( m_AllocCount ) == 0 ) if( m_AllocCount == 0 )
mVUinit( &VU0, 0 ); {
++m_AllocCount;
if( AtomicExchange( mvu0_allocated, 1 ) == 0 )
mVUinit( &VU0, 0 );
}
} }
void recMicroVU0::Shutdown() throw() { void recMicroVU0::Shutdown() throw() {
if( AtomicDecrement( m_AllocCount ) == 0 ) if( m_AllocCount > 0 )
mVUclose(&microVU0); {
--m_AllocCount;
if( AtomicExchange( mvu0_allocated, 0 ) == 1 )
mVUclose(&microVU0);
}
} }
void recMicroVU0::Reset() { void recMicroVU0::Reset() {
@ -356,7 +369,7 @@ void recMicroVU0::ExecuteBlock() {
} }
void recMicroVU0::Clear(u32 addr, u32 size) { void recMicroVU0::Clear(u32 addr, u32 size) {
pxAssert( m_AllocCount ); // please allocate me first! :| pxAssert( mvu0_allocated ); // please allocate me first! :|
mVUclear(&microVU0, addr, size); mVUclear(&microVU0, addr, size);
} }
@ -367,32 +380,35 @@ void recMicroVU0::Vsync() throw() {
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// recMicroVU1 // recMicroVU1
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
recMicroVU1::recMicroVU1() recMicroVU1::recMicroVU1() {
{
IsInterpreter = false; IsInterpreter = false;
} }
void recMicroVU1::Allocate() void recMicroVU1::Allocate() {
{ if( m_AllocCount == 0 )
if( AtomicIncrement( m_AllocCount ) == 0 ) {
mVUinit( &VU1, 1 ); ++m_AllocCount;
if( AtomicExchange( mvu1_allocated, 1 ) == 0 )
mVUinit( &VU1, 1 );
}
} }
void recMicroVU1::Shutdown() throw() void recMicroVU1::Shutdown() throw() {
{ if( m_AllocCount > 0 )
if( AtomicDecrement( m_AllocCount ) == 0 ) {
mVUclose(&microVU1); --m_AllocCount;
if( AtomicExchange( mvu1_allocated, 0 ) == 1 )
mVUclose(&microVU1);
}
} }
void recMicroVU1::Reset() void recMicroVU1::Reset() {
{
if( !pxAssertDev( m_AllocCount, "MicroVU1 CPU Provider has not been allocated prior to reset!" ) ) return; if( !pxAssertDev( m_AllocCount, "MicroVU1 CPU Provider has not been allocated prior to reset!" ) ) return;
mVUreset(&microVU1); mVUreset(&microVU1);
} }
void recMicroVU1::ExecuteBlock() void recMicroVU1::ExecuteBlock() {
{ pxAssert( mvu1_allocated ); // please allocate me first! :|
pxAssert( m_AllocCount ); // please allocate me first! :|
if ((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0) return; if ((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0) return;
pxAssert( (VU1.VI[REG_TPC].UL&7) == 0 ); pxAssert( (VU1.VI[REG_TPC].UL&7) == 0 );
@ -402,8 +418,7 @@ void recMicroVU1::ExecuteBlock()
XMMRegisters::Thaw(); XMMRegisters::Thaw();
} }
void recMicroVU1::Clear(u32 addr, u32 size) void recMicroVU1::Clear(u32 addr, u32 size) {
{
pxAssert( m_AllocCount ); // please allocate me first! :| pxAssert( m_AllocCount ); // please allocate me first! :|
mVUclear(&microVU1, addr, size); mVUclear(&microVU1, addr, size);
} }

View File

@ -121,8 +121,8 @@ extern const __aligned(32) mVU_Globals mVUglob;
// Function/Template Stuff // Function/Template Stuff
#define mVUx (vuIndex ? &microVU1 : &microVU0) #define mVUx (vuIndex ? &microVU1 : &microVU0)
#define mVUop(opName) static void opName (mP) #define mVUop(opName) static void opName (mP)
#define microVUr(aType) __recInline aType #define microVUr(aType) static __recInline aType
#define microVUt(aType) __forceinline aType #define microVUt(aType) static __forceinline aType
#define microVUx(aType) template<int vuIndex> aType #define microVUx(aType) template<int vuIndex> aType
#define microVUf(aType) template<int vuIndex> __forceinline aType #define microVUf(aType) template<int vuIndex> __forceinline aType