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

View File

@ -15,60 +15,136 @@
#pragma once
#ifndef __pxFUNCTION__
#if defined(__GNUG__)
# define __pxFUNCTION__ __PRETTY_FUNCTION__
#else
# define __pxFUNCTION__ __FUNCTION__
#endif
#endif
#ifndef wxNullChar
# define wxNullChar ((wxChar*)NULL)
#endif
// FnChar_t - function name char type; typedef'd in case it ever changes between compilers
// (ie, a compiler decides to wchar_t it instead of char/UTF8).
typedef char FnChar_t;
// --------------------------------------------------------------------------------------
// DiagnosticOrigin
// --------------------------------------------------------------------------------------
struct DiagnosticOrigin
{
const wxChar* srcfile;
const FnChar_t* function;
const wxChar* condition;
int line;
DiagnosticOrigin( const wxChar *_file, int _line, const FnChar_t *_func, const wxChar* _cond = NULL )
: srcfile( _file )
, function( _func )
, condition( _cond )
, line( _line )
{
}
wxString ToString( const wxChar* msg=NULL ) const;
};
// Returns ture if the assertion is to trap into the debugger, or false if execution
// of the program should continue unimpeded.
typedef bool pxDoAssertFnType(const DiagnosticOrigin& origin, const wxChar *msg);
extern pxDoAssertFnType pxAssertImpl_LogIt;
extern pxDoAssertFnType* pxDoAssert;
// ----------------------------------------------------------------------------------------
// pxAssert / pxAssertDev / pxFail / pxFailDev
// pxAssert / pxAssertDev
// ----------------------------------------------------------------------------------------
// Standard "nothrow" assertions. All assertions act as valid conditional statements that
// return the result of the specified conditional; useful for handling failed assertions in
// a "graceful" fashion when utilizing the "ignore" feature of assertion debugging.
// These macros are mostly intended for "pseudo-weak" assumptions within code, most often for
// testing threaded user interface code (threading of the UI is a prime example since often
// even very robust assertions can fail in very rare conditions, due to the complex variety
// of ways the user can invoke UI events).
//
// Performance: All assertion types optimize into __assume()/likely() directives in Release
// builds. If using assertions as part of a conditional, the conditional code *will* not
// be optimized out, so use conditionals with caution.
// All macros return TRUE if the assertion succeeds, or FALSE if the assertion failed
// (thus matching the condition of the assertion itself).
//
// pxAssertDev is an assertion tool for Devel builds, intended for sanity checking and/or
// bounds checking variables in areas which are not performance critical. Another common
// use is for checking thread affinity on utility functions.
//
// Credits Notes: These macros are based on a combination of wxASSERT, MSVCRT's assert
// and the ATL's Assertion/Assumption macros. the best of all worlds!
// Credits: These macros are based on a combination of wxASSERT, MSVCRT's assert and the
// ATL's Assertion/Assumption macros. the best of all worlds!
// --------------------------------------------------------------------------------------
// pxAssume / pxAssumeDev / pxFail / pxFailDev
// --------------------------------------------------------------------------------------
// Assumptions are like "extra rigid" assertions, which should never fail under any circum-
// stance in release build optimized code.
//
// Performance: All assumption/fail types optimize into __assume()/likely() directives in
// Release builds (non-dev varieties optimize as such in Devel builds as well). If using
#define pxDiagSpot DiagnosticOrigin( __TFILE__, __LINE__, __pxFUNCTION__ )
#define pxAssertSpot(cond) DiagnosticOrigin( __TFILE__, __LINE__, __pxFUNCTION__, _T(#cond) )
// pxAssertRel ->
// Special release-mode assertion. Limited use since stack traces in release mode builds
// (especially with LTCG) are highly suspect. But when troubleshooting crashes that only
// rear ugly heads in optimized builds, this is one of the few tools we have.
#define pxAssertRel(cond, msg) ( (likely(cond)) || (pxOnAssert(pxAssertSpot(cond), msg), false) )
#define pxAssumeMsg(cond, msg) ((void) ( (!likely(cond)) && (pxOnAssert(pxAssertSpot(cond), msg), false) ))
#if defined(PCSX2_DEBUG)
# define pxAssertMsg(cond, msg) ( (!!(cond)) || \
(pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), likely(cond)) )
# define pxAssertMsg(cond, msg) pxAssertRel(cond, msg)
# define pxAssertDev(cond, msg) pxAssertMsg(cond, msg)
# define pxAssertDev(cond,msg) pxAssertMsg(cond, msg)
# define pxAssume(cond) pxAssumeMsg(cond, wxNullChar)
# define pxAssumeDev(cond, msg) pxAssumeMsg(cond, msg)
# define pxFail(msg) pxAssertMsg(false, msg)
# define pxFailDev(msg) pxAssertDev(false, msg)
# define pxFail(msg) pxAssumeMsg(false, msg)
# define pxFailDev(msg) pxAssumeDev(false, msg)
#elif defined(PCSX2_DEVBUILD)
// Devel builds use __assume for standard assertions and call pxOnAssertDevel
// for AssertDev brand assertions (which typically throws a LogicError exception).
# define pxAssertMsg(cond, msg) (__assume(cond), likely(cond))
# define pxAssertMsg(cond, msg) (likely(cond))
# define pxAssertDev(cond, msg) pxAssertRel(cond, msg)
# define pxAssertDev(cond, msg) ( (!!(cond)) || \
(pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), likely(cond)) )
# define pxAssume(cond) (__assume(cond))
# define pxAssumeDev(cond, msg) pxAssumeMsg(cond, msg)
# define pxFail(msg) (__assume(false), false)
# define pxFailDev(msg ) pxAssertDev(false, msg)
# define pxFail(msg) (__assume(false))
# define pxFailDev(msg) pxAssumeDev(false, msg)
#else
// Release Builds just use __assume as an optimization, and return the conditional
// as a result (which is optimized to nil if unused).
# define pxAssertMsg(cond, msg) (__assume(cond), likely(cond))
# define pxAssertDev(cond, msg) (__assume(cond), likely(cond))
# define pxFail(msg) (__assume(false), false)
# define pxFailDev(msg) (__assume(false), false)
# define pxAssertMsg(cond, msg) (likely(cond))
# define pxAssertDev(cond, msg) (likely(cond))
# define pxAssume(cond) (__assume(cond))
# define pxAssumeDev(cond, msg) (__assume(cond))
# define pxFail(msg) (__assume(false))
# define pxFailDev(msg) (__assume(false))
#endif
#define pxAssert(cond) pxAssertMsg(cond, (wxChar*)NULL)
#define pxAssert(cond) pxAssertMsg(cond, wxNullChar)
#define pxAssertRelease( cond, msg )
// Performs an unsigned index bounds check, and generates a debug assertion if the check fails.
// For stricter checking in Devel builds as well as debug builds (but possibly slower), use
@ -81,8 +157,8 @@
wxsFormat( L"Array index out of bounds accessing object '%s' (index=%d, size=%d)", objname, (idx), (sze) ) )
extern void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const wxChar* msg);
extern void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const char* msg);
extern void pxOnAssert( const DiagnosticOrigin& origin, const wxChar* msg=NULL );
extern void pxOnAssert( const DiagnosticOrigin& origin, const char* msg );
//////////////////////////////////////////////////////////////////////////////////////////
// jNO_DEFAULT -- disables the default case in a switch, which improves switch optimization
@ -92,29 +168,23 @@ extern void pxOnAssert( const wxChar* file, int line, const char* func, const wx
// in the 'default:' case of a switch tells the compiler that the case is unreachable, so
// that it will not generate any code, LUTs, or conditionals to handle it.
//
// * In debug builds the default case will cause an assertion.
// * In devel builds the default case will cause a LogicError exception (C++ only)
// (either meaning the jNO_DEFAULT has been used incorrectly, and that the default case is in
// fact used and needs to be handled).
//
// MSVC Note: To stacktrace LogicError exceptions, add Exception::LogicError to the C++ First-
// Chance Exception list (under Debug->Exceptions menu).
// * In debug/devel builds the default case will cause an assertion.
//
#ifndef jNO_DEFAULT
#if defined(__cplusplus) && defined(PCSX2_DEVBUILD)
# define jNO_DEFAULT \
default: \
{ \
pxFailDev( "Incorrect usage of jNO_DEFAULT detected (default case is not unreachable!)" ); \
break; \
}
default: \
{ \
pxFailDev( "Incorrect usage of jNO_DEFAULT detected (default case is not unreachable!)" ); \
break; \
}
#else
# define jNO_DEFAULT \
default: \
{ \
jASSUME(0); \
break; \
}
default: \
{ \
jASSUME(0); \
break; \
}
#endif
#endif

View File

@ -38,11 +38,7 @@
Console.Error( ex.what() ); \
}
#ifdef __GNUG__
# define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __PRETTY_FUNCTION__ )
#else
# define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __FUNCTION__ )
#endif
#define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL( __pxFUNCTION__ )
namespace Exception
{
@ -56,7 +52,7 @@ namespace Exception
// catch clause can optionally modify them and then re-throw to a top-level handler.
//
// Note, this class is "abstract" which means you shouldn't use it directly like, ever.
// Use Exception::RuntimeError or Exception::LogicError instead for generic exceptions.
// Use Exception::RuntimeError instead for generic exceptions.
//
// Because exceptions are the (only!) really useful example of multiple inheritance,
// this class has only a trivial constructor, and must be manually initialized using
@ -150,7 +146,7 @@ namespace Exception
explicit classname( const wxString& msg_eng ) { BaseException::InitBaseEx( msg_eng, wxEmptyString ); }
// ---------------------------------------------------------------------------------------
// RuntimeError / LogicError - Generalized Exceptions
// RuntimeError - Generalized Exceptions with Recoverable Traits!
// ---------------------------------------------------------------------------------------
class RuntimeError : public virtual BaseException
@ -161,14 +157,6 @@ namespace Exception
DEFINE_RUNTIME_EXCEPTION( RuntimeError, wxLt("An unhandled runtime error has occurred, somewhere in the depths of Pcsx2's cluttered brain-matter.") )
};
// LogicErrors do not need translated versions, since they are typically obscure, and the
// user wouldn't benefit from being able to understand them anyway. :)
class LogicError : public virtual BaseException
{
public:
DEFINE_LOGIC_EXCEPTION( LogicError, wxLt("An unhandled logic error has occurred.") )
};
// --------------------------------------------------------------------------------------
// CancelAppEvent - Exception for canceling an event in a non-verbose fashion
// --------------------------------------------------------------------------------------
@ -227,24 +215,6 @@ namespace Exception
DEFINE_RUNTIME_EXCEPTION( OutOfMemory, wxLt("Out of Memory") )
};
// This exception thrown any time an operation is attempted when an object
// is in an uninitialized state.
//
class InvalidOperation : public virtual LogicError
{
public:
DEFINE_LOGIC_EXCEPTION( InvalidOperation, "Attempted method call is invalid for the current object or program state." )
};
// This exception thrown any time an operation is attempted when an object
// is in an uninitialized state.
//
class InvalidArgument : public virtual LogicError
{
public:
DEFINE_LOGIC_EXCEPTION( InvalidArgument, "Invalid argument passed to a function." )
};
class ParseError : public RuntimeError
{
public:

View File

@ -32,6 +32,9 @@ class wxTimeSpan;
namespace Threading
{
class PersistentThread;
PersistentThread* pxGetCurrentThread();
wxString pxGetCurrentThreadName();
}
namespace Exception
@ -346,12 +349,11 @@ namespace Threading
DeclareNoncopyableObject(PersistentThread);
protected:
typedef int (*PlainJoeFP)();
wxString m_name; // diagnostic name for our thread.
pthread_t m_thread;
Semaphore m_sem_event; // general wait event that's needed by most threads.
Semaphore m_sem_event; // general wait event that's needed by most threads
Semaphore m_sem_startup; // startup sync tool
Mutex m_lock_InThread; // used for canceling and closing threads in a deadlock-safe manner
MutexLockRecursive m_lock_start; // used to lock the Start() code from starting simultaneous threads accidentally.
@ -369,6 +371,7 @@ namespace Threading
virtual void Start();
virtual void Cancel( bool isBlocking = true );
virtual bool Cancel( const wxTimeSpan& timeout );
virtual bool Detach();
virtual void Block();
virtual void RethrowException() const;
@ -419,12 +422,13 @@ namespace Threading
void FrankenMutex( Mutex& mutex );
bool AffinityAssert_AllowFromSelf() const;
bool AffinityAssert_DisallowFromSelf() const;
bool AffinityAssert_AllowFromSelf( const DiagnosticOrigin& origin ) const;
bool AffinityAssert_DisallowFromSelf( const DiagnosticOrigin& origin ) const;
// ----------------------------------------------------------------------------
// Section of methods for internal use only.
bool _basecancel();
void _selfRunningTest( const wxChar* name ) const;
void _DoSetThreadName( const wxString& name );
void _DoSetThreadName( const char* name );

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

View File

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

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 );
template<>
inline void operator+=( wxSizer& target, const pxWindowAndFlags<pxStaticText>& src )
@ -94,6 +95,13 @@ inline void operator+=( wxSizer& target, const pxWindowAndFlags<pxStaticText>& s
//target.Add( src.window, src.flags );
}
template<>
inline void operator+=( wxSizer* target, const pxWindowAndFlags<pxStaticText>& src )
{
src.window->AddTo( target, src.flags );
//target.Add( src.window, src.flags );
}
// --------------------------------------------------------------------------------------
// pxStaticHeading
// --------------------------------------------------------------------------------------

View File

@ -52,15 +52,25 @@ struct pxAlignmentType
wxSizerFlags Apply( wxSizerFlags flags=wxSizerFlags() ) const;
wxSizerFlags operator& ( const wxSizerFlags& _flgs ) const
{
return Apply( _flgs );
}
wxSizerFlags Border( int dir, int padding ) const
{
return Apply().Border( dir, padding );
}
wxSizerFlags Proportion( int prop ) const
{
return Apply().Proportion( intval );
}
operator wxSizerFlags() const
{
return Apply();
}
wxSizerFlags operator | ( const wxSizerFlags& _flgs )
{
return Apply( _flgs );
}
};
struct pxStretchType
@ -78,15 +88,47 @@ struct pxStretchType
wxSizerFlags Apply( wxSizerFlags flags=wxSizerFlags() ) const;
wxSizerFlags operator& ( const wxSizerFlags& _flgs ) const
{
return Apply( _flgs );
}
wxSizerFlags Border( int dir, int padding ) const
{
return Apply().Border( dir, padding );
}
wxSizerFlags Proportion( int prop ) const
{
return Apply().Proportion( intval );
}
operator wxSizerFlags() const
{
return Apply();
}
};
wxSizerFlags operator | ( const wxSizerFlags& _flgs )
class pxProportion
{
int intval;
pxProportion( int prop )
{
intval = prop;
}
wxSizerFlags Apply( wxSizerFlags flags=wxSizerFlags() ) const;
wxSizerFlags operator& ( const wxSizerFlags& _flgs ) const
{
return Apply( _flgs );
}
operator wxSizerFlags() const
{
return Apply();
}
};
extern const pxAlignmentType
@ -128,8 +170,7 @@ struct pxWindowAndFlags
};
extern wxSizerFlags operator , ( const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2 ); //pxAlignmentType align );
//extern wxSizerFlags operator , ( const wxSizerFlags& _flgs, pxStretchType stretch );
extern wxSizerFlags operator& ( const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2 );
template< typename WinType >
pxWindowAndFlags<WinType> operator | ( WinType* _win, const wxSizerFlags& _flgs )
@ -162,9 +203,13 @@ extern void operator+=( wxSizer& target, wxSizer* src );
extern void operator+=( wxSizer& target, wxWindow& src );
extern void operator+=( wxSizer& target, wxSizer& src );
extern void operator+=( wxSizer* target, wxWindow& src );
extern void operator+=( wxSizer* target, wxSizer& src );
extern void operator+=( wxSizer& target, int spacer );
extern void operator+=( wxWindow& target, int spacer );
// ----------------------------------------------------------------------------
// Important: This template is needed in order to retain window type information and
// invoke the proper overloaded version of += (which is used by pxStaticText and other
// classes to perform special actions when added to sizers).
@ -178,6 +223,14 @@ void operator+=( wxWindow& target, WinType* src )
template< typename WinType >
void operator+=( wxWindow& target, WinType& src )
{
if( !pxAssert( target.GetSizer() != NULL ) ) return;
*target.GetSizer() += src;
}
template< typename WinType >
void operator+=( wxWindow& target, const pxWindowAndFlags<WinType>& src )
{
if( !pxAssert( target.GetSizer() != NULL ) ) return;
*target.GetSizer() += src;
}
@ -187,11 +240,31 @@ void operator+=( wxSizer& target, const pxWindowAndFlags<WinType>& src )
target.Add( src.window, src.flags );
}
// ----------------------------------------------------------------------------
// Pointer Versions! (note that C++ requires one of the two operator params be a
// "poper" object type (non-pointer), so that's why some of these are missing.
template< typename WinType >
void operator+=( wxWindow& target, const pxWindowAndFlags<WinType>& src )
void operator+=( wxWindow* target, WinType& src )
{
if( !pxAssert( target.GetSizer() != NULL ) ) return;
*target.GetSizer() += src;
if( !pxAssert( target != NULL ) ) return;
if( !pxAssert( target->GetSizer() != NULL ) ) return;
*target->GetSizer() += src;
}
template< typename WinType >
void operator+=( wxWindow* target, const pxWindowAndFlags<WinType>& src )
{
if( !pxAssert( target != NULL ) ) return;
if( !pxAssert( target->GetSizer() != NULL ) ) return;
*target->GetSizer() += src;
}
template< typename WinType >
void operator+=( wxSizer* target, const pxWindowAndFlags<WinType>& src )
{
if( !pxAssert( target != NULL ) ) return;
target.Add( src.window, src.flags );
}
// ----------------------------------------------------------------------------
@ -230,19 +303,29 @@ protected:
public:
wxDialogWithHelpers();
wxDialogWithHelpers(wxWindow* parent, int id, const wxString& title, bool hasContextHelp, const wxPoint& pos=wxDefaultPosition, const wxSize& size=wxDefaultSize );
wxDialogWithHelpers(wxWindow* parent, const wxString& title, bool hasContextHelp=false, bool resizable=false );
wxDialogWithHelpers(wxWindow* parent, const wxString& title, wxOrientation orient);
virtual ~wxDialogWithHelpers() throw();
void AddOkCancel( wxSizer& sizer, bool hasApply=false );
pxStaticText* Text( const wxString& label );
pxStaticHeading* Heading( const wxString& label );
void Init();
void AddOkCancel( wxSizer& sizer, bool hasApply=false );
virtual void SmartCenterFit();
virtual int ShowModal();
virtual bool Show( bool show=true );
virtual pxStaticText* Text( const wxString& label );
virtual pxStaticHeading* Heading( const wxString& label );
virtual wxDialogWithHelpers& SetIdealWidth( int newWidth ) { m_idealWidth = newWidth; return *this; }
wxDialogWithHelpers& SetIdealWidth( int newWidth ) { m_idealWidth = newWidth; return *this; }
int GetIdealWidth() const { return m_idealWidth; }
bool HasIdealWidth() const { return m_idealWidth != wxDefaultCoord; }
protected:
void OnActivate(wxActivateEvent& evt);
void OnOkCancel(wxCommandEvent& evt);
void OnCloseWindow(wxCloseEvent& event);
};
// --------------------------------------------------------------------------------------
@ -412,7 +495,7 @@ public:
//////////////////////////////////////////////////////////////////////////////////////////////
extern bool pxDialogExists( wxWindowID id );
extern bool pxDialogExists( const wxString& name );
extern bool pxIsValidWindowPosition( const wxWindow& window, const wxPoint& windowPos );
extern wxRect wxGetDisplayArea();
@ -420,5 +503,7 @@ extern wxString pxFormatToolTipText( wxWindow* wind, const wxString& src );
extern void pxSetToolTip( wxWindow* wind, const wxString& src );
extern void pxSetToolTip( wxWindow& wind, const wxString& src );
extern wxFont pxGetFixedFont( int ptsize=8, int weight=wxNORMAL );
#endif

View File

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

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h"
#include <wx/app.h>
#include "Threading.h"
wxString GetEnglish( const char* msg )
{
@ -42,36 +43,66 @@ wxString GetTranslation( const char* msg )
// That's ok. What we don't want is the *same* thread recurse-asserting.
static __threadlocal int s_assert_guard = 0;
DEVASSERT_INLINE void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const wxChar* msg)
{
#ifdef PCSX2_DEVBUILD
RecursionGuard guard( s_assert_guard );
if( guard.IsReentrant() ) return;
pxDoAssertFnType* pxDoAssert = pxAssertImpl_LogIt;
if( wxTheApp == NULL )
// make life easier for people using VC++ IDE by using this format, which allows double-click
// response times from the Output window...
wxString DiagnosticOrigin::ToString( const wxChar* msg ) const
{
wxString message;
message.reserve( 2048 );
message.Printf( L"%s(%d) : assertion failed:\n", srcfile, line );
if( function != NULL )
message += L" Function: " + fromUTF8(function) + L"\n";
message += L" Thread: " + Threading::pxGetCurrentThreadName() + L"\n";
if( condition != NULL )
message += L" Condition: " + wxString(condition) + L"\n";
if( msg != NULL )
message += L" Message: " + wxString(msg) + L"\n";
return message;
}
bool pxAssertImpl_LogIt( const DiagnosticOrigin& origin, const wxChar *msg )
{
wxLogError( origin.ToString( msg ) );
return false;
}
DEVASSERT_INLINE void pxOnAssert( const DiagnosticOrigin& origin, const wxChar* msg )
{
RecursionGuard guard( s_assert_guard );
if( guard.IsReentrant() ) { return wxTrap(); }
// wxWidgets doesn't come with debug builds on some Linux distros, and other distros make
// it difficult to use the debug build (compilation failures). To handle these I've had to
// bypass the internal wxWidgets assertion handler entirely, since it may not exist even if
// PCSX2 itself is compiled in debug mode (assertions enabled).
bool trapit;
if( pxDoAssert == NULL )
{
// Note: Format uses MSVC's syntax for output window hotlinking.
wxLogError( wxsFormat( L"%s(%d): Assertion failed in %s: %s\n",
file, line, fromUTF8(func).c_str(), msg )
);
trapit = pxAssertImpl_LogIt( origin, msg );
}
else
{
#ifdef __WXDEBUG__
wxTheApp->OnAssertFailure( file, line, fromUTF8(func), cond, msg );
#elif wxUSE_GUI
// FIXME: this should create a popup dialog for devel builds.
wxLogError( msg );
#else
wxLogError( msg );
#endif
trapit = pxDoAssert( origin, msg );
}
#endif
if( trapit ) { wxTrap(); }
}
__forceinline void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const char* msg)
__forceinline void pxOnAssert( const DiagnosticOrigin& origin, const char* msg)
{
pxOnAssert( file, line, func, cond, fromUTF8(msg) );
pxOnAssert( origin, fromUTF8(msg) );
}

View File

@ -37,6 +37,61 @@ const wxTimeSpan Threading::def_yieldgui_interval( 0, 0, 0, 100 );
// three second interval for deadlock protection on waitgui.
const wxTimeSpan Threading::def_deadlock_timeout( 0, 0, 3, 0 );
//static __threadlocal PersistentThread* tls_current_thread = NULL;
static pthread_key_t curthread_key = NULL;
static s32 total_key_count = 0;
static Mutex total_key_lock;
static void make_curthread_key()
{
ScopedLock lock( total_key_lock );
if( total_key_count++ != 0 ) return;
if( 0 != pthread_key_create(&curthread_key, NULL) )
{
Console.Error( "Thread key creation failed (probably out of memory >_<)" );
curthread_key = NULL;
}
}
static void unmake_curthread_key()
{
ScopedLock lock( total_key_lock );
if( --total_key_count > 0 ) return;
if( curthread_key != NULL )
pthread_key_delete( curthread_key );
curthread_key = NULL;
}
// Returns a handle to the current persistent thread. If the current thread does not belong
// to the PersistentThread table, NULL is returned. Since the main/ui thread is not created
// through PersistentThread it will also return NULL. Callers can use wxThread::IsMain() to
// test if the NULL thread is the main thread.
PersistentThread* Threading::pxGetCurrentThread()
{
return (curthread_key==NULL) ? NULL : (PersistentThread*)pthread_getspecific( curthread_key );
}
// returns the name of the current thread, or "Unknown" if the thread is neither a PersistentThread
// nor the Main/UI thread.
wxString Threading::pxGetCurrentThreadName()
{
if( PersistentThread* thr = pxGetCurrentThread() )
{
return thr->GetName();
}
else if( wxThread::IsMain() )
{
return L"Main/UI";
}
return L"Unknown";
}
// (intended for internal use only)
// Returns true if the Wait is recursive, or false if the Wait is safe and should be
// handled via normal yielding methods.
@ -81,7 +136,7 @@ Threading::PersistentThread::PersistentThread()
// This destructor performs basic "last chance" cleanup, which is a blocking join
// against the thread. Extending classes should almost always implement their own
// thread closure process, since any PersistentThread will, by design, not terminate
// unless it has been properly canceled.
// unless it has been properly canceled (resulting in deadlock).
//
// Thread safety: This class must not be deleted from its own thread. That would be
// like marrying your sister, and then cheating on her with your daughter.
@ -116,14 +171,24 @@ Threading::PersistentThread::~PersistentThread() throw()
DESTRUCTOR_CATCHALL
}
bool Threading::PersistentThread::AffinityAssert_AllowFromSelf() const
bool Threading::PersistentThread::AffinityAssert_AllowFromSelf( const DiagnosticOrigin& origin ) const
{
return pxAssertMsg( IsSelf(), wxsFormat( L"Thread affinity violation: Call allowed from '%s' thread only.", m_name.c_str() ) );
if( IsSelf() ) return true;
if( IsDevBuild )
pxOnAssert( origin, wxsFormat( L"Thread affinity violation: Call allowed from '%s' thread only.", m_name.c_str() ) );
return false;
}
bool Threading::PersistentThread::AffinityAssert_DisallowFromSelf() const
bool Threading::PersistentThread::AffinityAssert_DisallowFromSelf( const DiagnosticOrigin& origin ) const
{
return pxAssertMsg( !IsSelf(), wxsFormat( L"Thread affinity violation: Call is *not* allowed from '%s' thread.", m_name.c_str() ) );
if( !IsSelf() ) return true;
if( IsDevBuild )
pxOnAssert( origin, wxsFormat( L"Thread affinity violation: Call is *not* allowed from '%s' thread.", m_name.c_str() ) );
return false;
}
void Threading::PersistentThread::FrankenMutex( Mutex& mutex )
@ -157,7 +222,29 @@ void Threading::PersistentThread::Start()
m_except = NULL;
if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 )
throw Exception::ThreadCreationError();
throw Exception::ThreadCreationError( this );
if( !m_sem_startup.WaitWithoutYield( wxTimeSpan( 0, 0, 3, 0 ) ) )
{
RethrowException();
// And if the thread threw nothing of its own:
throw Exception::ThreadCreationError( this, "(%s thread) Start error: created thread never posted startup semaphore." );
}
// Event Rationale (above): Performing this semaphore wait on the created thread is "slow" in the
// sense that it stalls the calling thread completely until the new thread is created
// (which may not always be desirable). But too bad. In order to safely use 'running' locks
// and detachment management, this *has* to be done. By rule, starting new threads shouldn't
// be done very often anyway, hence the concept of Threadpooling for rapidly rotating tasks.
// (and indeed, this semaphore wait might, in fact, be very swift compared to other kernel
// overhead in starting threads).
// (this could also be done using operating system specific calls, since any threaded OS has
// functions that allow us to see if a thread is running or not, and to block against it even if
// it's been detached -- removing the need for m_lock_InThread and the semaphore wait above. But
// pthreads kinda lacks that stuff, since pthread_join() has no timeout option making it im-
// possible to safely block against a running thread)
}
// Returns: TRUE if the detachment was performed, or FALSE if the thread was
@ -165,13 +252,28 @@ void Threading::PersistentThread::Start()
// This function should not be called from the owner thread.
bool Threading::PersistentThread::Detach()
{
AffinityAssert_DisallowFromSelf();
AffinityAssert_DisallowFromSelf(pxDiagSpot);
if( _InterlockedExchange( &m_detached, true ) ) return false;
pthread_detach( m_thread );
return true;
}
bool Threading::PersistentThread::_basecancel()
{
// Prevent simultaneous startup and cancel:
if( !m_running ) return false;
if( m_detached )
{
Console.Warning( "(Thread Warning) Ignoring attempted cancellation of detached thread." );
return false;
}
pthread_cancel( m_thread );
return true;
}
// Remarks:
// Provision of non-blocking Cancel() is probably academic, since destroying a PersistentThread
// object performs a blocking Cancel regardless of if you explicitly do a non-blocking Cancel()
@ -183,33 +285,40 @@ bool Threading::PersistentThread::Detach()
// Parameters:
// isBlocking - indicates if the Cancel action should block for thread completion or not.
//
// Exceptions raised by the blocking thread will be re-thrown into the main thread. If isBlocking
// is false then no exceptions will occur.
//
void Threading::PersistentThread::Cancel( bool isBlocking )
{
AffinityAssert_DisallowFromSelf();
AffinityAssert_DisallowFromSelf( pxDiagSpot );
{
// Prevent simultaneous startup and cancel:
ScopedLock startlock( m_lock_start );
if( !m_running ) return;
// Prevent simultaneous startup and cancel, necessary to avoid
ScopedLock startlock( m_lock_start );
if( m_detached )
{
Console.Warning( "(Thread Warning) Ignoring attempted cancellation of detached thread." );
return;
}
pthread_cancel( m_thread );
}
if( !_basecancel() ) return;
if( isBlocking )
{
// FIXME: Add deadlock detection and handling here... ?
m_lock_InThread.Wait();
WaitOnSelf( m_lock_InThread );
Detach();
}
}
bool Threading::PersistentThread::Cancel( const wxTimeSpan& timespan )
{
AffinityAssert_DisallowFromSelf( pxDiagSpot );
// Prevent simultaneous startup and cancel:
ScopedLock startlock( m_lock_start );
if( !_basecancel() ) return true;
if( !WaitOnSelf( m_lock_InThread, timespan ) ) return false;
Detach();
return true;
}
// Blocks execution of the calling thread until this thread completes its task. The
// caller should make sure to signal the thread to exit, or else blocking may deadlock the
// calling thread. Classes which extend PersistentThread should override this method
@ -218,10 +327,12 @@ void Threading::PersistentThread::Cancel( bool isBlocking )
// Returns the return code of the thread.
// This method is roughly the equivalent of pthread_join().
//
// Exceptions raised by the blocking thread will be re-thrown into the main thread.
//
void Threading::PersistentThread::Block()
{
AffinityAssert_DisallowFromSelf();
m_lock_InThread.Wait();
AffinityAssert_DisallowFromSelf(pxDiagSpot);
WaitOnSelf( m_lock_InThread );
}
bool Threading::PersistentThread::IsSelf() const
@ -277,7 +388,7 @@ void Threading::PersistentThread::_selfRunningTest( const wxChar* name ) const
//
void Threading::PersistentThread::WaitOnSelf( Semaphore& sem ) const
{
if( !AffinityAssert_DisallowFromSelf() ) return;
if( !AffinityAssert_DisallowFromSelf(pxDiagSpot) ) return;
while( true )
{
@ -301,7 +412,7 @@ void Threading::PersistentThread::WaitOnSelf( Semaphore& sem ) const
//
void Threading::PersistentThread::WaitOnSelf( Mutex& mutex ) const
{
if( !AffinityAssert_DisallowFromSelf() ) return;
if( !AffinityAssert_DisallowFromSelf(pxDiagSpot) ) return;
while( true )
{
@ -314,7 +425,7 @@ static const wxTimeSpan SelfWaitInterval( 0,0,0,333 );
bool Threading::PersistentThread::WaitOnSelf( Semaphore& sem, const wxTimeSpan& timeout ) const
{
if( !AffinityAssert_DisallowFromSelf() ) return true;
if( !AffinityAssert_DisallowFromSelf(pxDiagSpot) ) return true;
wxTimeSpan runningout( timeout );
@ -330,7 +441,7 @@ bool Threading::PersistentThread::WaitOnSelf( Semaphore& sem, const wxTimeSpan&
bool Threading::PersistentThread::WaitOnSelf( Mutex& mutex, const wxTimeSpan& timeout ) const
{
if( !AffinityAssert_DisallowFromSelf() ) return true;
if( !AffinityAssert_DisallowFromSelf(pxDiagSpot) ) return true;
wxTimeSpan runningout( timeout );
@ -350,7 +461,7 @@ bool Threading::PersistentThread::WaitOnSelf( Mutex& mutex, const wxTimeSpan& ti
// and cleanup, or use the DoThreadCleanup() override to perform resource cleanup).
void Threading::PersistentThread::TestCancel() const
{
AffinityAssert_AllowFromSelf();
AffinityAssert_AllowFromSelf(pxDiagSpot);
pthread_testcancel();
}
@ -393,24 +504,16 @@ void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::*
}
#ifndef PCSX2_DEVBUILD
// ----------------------------------------------------------------------------
// Allow logic errors to propagate out of the thread in release builds, so that they might be
// handled in non-fatal ways. On Devbuilds let them loose, so that they produce debug stack
// traces and such.
catch( std::logic_error& ex )
// Bleh... don't bother with std::exception. runtime_error should catch anything
// useful coming out of the core STL libraries anyway, and these are best handled by
// the MSVC debugger (or by silent random annoying fail on debug-less linux).
/*catch( std::logic_error& ex )
{
throw Exception::LogicError( wxsFormat( L"(thread: %s) STL Logic Error: %s\n\t%s",
throw Exception::BaseException( wxsFormat( L"(thread: %s) STL Logic Error: %s\n\t%s",
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
);
}
catch( Exception::LogicError& ex )
{
m_except = ex.Clone();
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
}
// ----------------------------------------------------------------------------
// Bleh... don't bother with std::exception. std::logic_error and runtime_error should catch
// anything coming out of the core STL libraries anyway.
/*catch( std::exception& ex )
catch( std::exception& ex )
{
throw Exception::BaseException( wxsFormat( L"(thread: %s) STL exception: %s\n\t%s",
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
@ -431,7 +534,7 @@ void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::*
// OnCleanupInThread() to extend cleanup functionality.
void Threading::PersistentThread::_ThreadCleanup()
{
AffinityAssert_AllowFromSelf();
AffinityAssert_AllowFromSelf(pxDiagSpot);
_try_virtual_invoke( &PersistentThread::OnCleanupInThread );
m_lock_InThread.Release();
}
@ -442,42 +545,59 @@ wxString Threading::PersistentThread::GetName() const
}
// This override is called by PeristentThread when the thread is first created, prior to
// calling ExecuteTaskInThread. This is useful primarily for "base" classes that extend
// from PersistentThread, giving them the ability to bind startup code to all threads that
// derive from them. (the alternative would have been to make ExecuteTaskInThread a
// private member, and provide a new Task executor by a different name).
// calling ExecuteTaskInThread, and after the initial InThread lock has been claimed.
// This code is also executed within a "safe" environment, where the creating thread is
// blocked against m_sem_event. Make sure to do any necessary variable setup here, without
// worry that the calling thread might attempt to test the status of those variables
// before initialization has completed.
//
void Threading::PersistentThread::OnStartInThread()
{
m_running = true;
m_detached = false;
m_running = true;
}
void Threading::PersistentThread::_internal_execute()
{
m_lock_InThread.Acquire();
OnStartInThread();
_DoSetThreadName( m_name );
make_curthread_key();
if( curthread_key != NULL )
pthread_setspecific( curthread_key, this );
OnStartInThread();
m_sem_startup.Post();
_try_virtual_invoke( &PersistentThread::ExecuteTaskInThread );
}
// Called by Start, prior to actual starting of the thread, and after any previous
// running thread has been canceled or detached.
void Threading::PersistentThread::OnStart()
{
FrankenMutex( m_lock_InThread );
m_sem_event.Reset();
m_sem_startup.Reset();
}
// Extending classes that override this method shoul always call it last from their
// personal implementations.
void Threading::PersistentThread::OnCleanupInThread()
{
m_running = false;
if( curthread_key != NULL )
pthread_setspecific( curthread_key, NULL );
unmake_curthread_key();
}
// passed into pthread_create, and is used to dispatch the thread's object oriented
// callback function
void* Threading::PersistentThread::_internal_callback( void* itsme )
{
pxAssert( itsme != NULL );
if( !pxAssertDev( itsme != NULL, wxNullChar ) ) return NULL;
PersistentThread& owner = *((PersistentThread*)itsme);
pthread_cleanup_push( _pt_callback_cleanup, itsme );
@ -493,8 +613,6 @@ void Threading::PersistentThread::_DoSetThreadName( const wxString& name )
void Threading::PersistentThread::_DoSetThreadName( const char* name )
{
if( !AffinityAssert_AllowFromSelf() ) return;
// This feature needs Windows headers and MSVC's SEH support:
#if defined(_WINDOWS_) && defined (_MSC_VER)

View File

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

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( m_IsRealized && m_DefaultIdx != -1 )
{
wxFont def( GetFont() );
wxFont def(GetFont());
m_objects[m_DefaultIdx].LabelObj->SetFont( def );
m_objects[m_DefaultIdx].LabelObj->SetForegroundColour( GetForegroundColour() );
}
@ -166,6 +166,20 @@ pxRadioPanel& pxRadioPanel::SetDefault( int idx )
return *this;
}
pxRadioPanel& pxRadioPanel::EnableItem( int idx, bool enable )
{
pxAssertDev( m_IsRealized, "RadioPanel must be realized first, prior to enabling or disabling individual items." );
if( m_objects[idx].LabelObj )
m_objects[idx].LabelObj->Enable( enable );
if( m_objects[idx].SubTextObj )
m_objects[idx].SubTextObj->Enable( enable );
return *this;
}
int pxRadioPanel::GetSelection() const
{
if( !VerifyRealizedState() ) return 0;

View File

@ -131,3 +131,8 @@ void operator+=( wxSizer& target, pxStaticText& src )
{
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;
}
wxSizerFlags operator , ( const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2 )
wxSizerFlags pxProportion::Apply( wxSizerFlags flags ) const
{
return flags.Proportion( intval );
}
wxSizerFlags operator& ( const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2 )
{
//return align.Apply( _flgs );
wxSizerFlags retval;
@ -118,11 +123,8 @@ wxSizerFlags operator , ( const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2
return retval;
}
/*wxSizerFlags operator | ( const wxSizerFlags& _flgs, pxStretchType stretch )
{
return stretch.Apply( _flgs );
}*/
// ----------------------------------------------------------------------------
// Reference/Handle versions!
void operator+=( wxSizer& target, wxWindow* src )
{
@ -148,6 +150,23 @@ void operator+=( wxSizer& target, int spacer )
{
target.AddSpacer( spacer );
}
// ----------------------------------------------------------------------------
// Pointer versions! (note that C++ requires one of the two operator params be a
// "poper" object type (non-pointer), so that's why there's only a couple of these.
void operator+=( wxSizer* target, wxWindow& src )
{
if( !pxAssert( target != NULL ) ) return;
target->Add( &src );
}
void operator+=( wxSizer* target, wxSizer& src )
{
if( !pxAssert( target != NULL ) ) return;
target->Add( &src );
}
// ----------------------------------------------------------------------------
void operator+=( wxWindow& target, int spacer )
{
@ -431,3 +450,14 @@ void pxSetToolTip( wxWindow& wind, const wxString& src )
{
pxSetToolTip( &wind, src );
}
wxFont pxGetFixedFont( int ptsize, int weight )
{
return wxFont(
ptsize, wxMODERN, wxNORMAL, weight, false,
#ifdef __WXMSW__
L"Lucida Console" // better than courier new (win32 only)
#endif
);
}

View File

@ -28,13 +28,9 @@ using namespace pxSizerFlags;
// wxDialogWithHelpers Class Implementations
// =====================================================================================================
HashTools::HashMap< wxWindowID, int > m_DialogIdents( 0, wxID_ANY );
bool pxDialogExists( wxWindowID id )
bool pxDialogExists( const wxString& name )
{
int dest = 0;
m_DialogIdents.TryGetValue( id, dest );
return (dest > 0);
return wxFindWindowByName( name ) != NULL;
}
// --------------------------------------------------------------------------------------
@ -44,20 +40,41 @@ IMPLEMENT_DYNAMIC_CLASS(wxDialogWithHelpers, wxDialog)
wxDialogWithHelpers::wxDialogWithHelpers()
{
m_idealWidth = wxDefaultCoord;
m_hasContextHelp = false;
m_extraButtonSizer = NULL;
Init();
}
wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, int id, const wxString& title, bool hasContextHelp, const wxPoint& pos, const wxSize& size )
: wxDialog( parent, id, title, pos, size , wxDEFAULT_DIALOG_STYLE) //, (wxCAPTION | wxMAXIMIZE | wxCLOSE_BOX | wxRESIZE_BORDER) ), // flags for resizable dialogs, currently unused.
wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, const wxString& title, bool hasContextHelp, bool resizable )
: wxDialog( parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | (resizable ? wxRESIZE_BORDER : 0)
)
{
++m_DialogIdents[GetId()];
m_hasContextHelp = hasContextHelp;
Init();
}
wxDialogWithHelpers::wxDialogWithHelpers(wxWindow* parent, const wxString& title, wxOrientation orient)
: wxDialog( parent, wxID_ANY, title )
{
m_hasContextHelp = false;
SetSizer( new wxBoxSizer( orient ) );
Init();
m_idealWidth = 500;
*this += StdPadding;
}
wxDialogWithHelpers::~wxDialogWithHelpers() throw()
{
}
void wxDialogWithHelpers::Init()
{
m_idealWidth = wxDefaultCoord;
m_extraButtonSizer = NULL;
m_hasContextHelp = hasContextHelp;
if( m_hasContextHelp )
delete wxHelpProvider::Set( new wxSimpleHelpProvider() );
@ -65,14 +82,54 @@ wxDialogWithHelpers::wxDialogWithHelpers( wxWindow* parent, int id, const wxStr
// indicate that it should, so I presume the problem is in wxWidgets and that (hopefully!)
// an updated version will fix it later. I tried to fix it using a manual Connect but it
// didn't do any good. (problem could also be my Co-Linux / x-window manager)
//Connect( wxEVT_ACTIVATE, wxActivateEventHandler(wxDialogWithHelpers::OnActivate) );
Connect( wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (wxDialogWithHelpers::OnOkCancel) );
Connect( wxID_CANCEL, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (wxDialogWithHelpers::OnOkCancel) );
Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler (wxDialogWithHelpers::OnCloseWindow) );
}
wxDialogWithHelpers::~wxDialogWithHelpers() throw()
void wxDialogWithHelpers::SmartCenterFit()
{
--m_DialogIdents[GetId()];
pxAssert( m_DialogIdents[GetId()] >= 0 );
Fit();
// Smart positioning logic! If our parent window is larger than our window by some
// good amount, then we center on that. If not, center relative to the screen. This
// avoids the popup automatically eclipsing the parent window (which happens in PCSX2
// a lot since the main window is small).
bool centerfail = true;
if( wxWindow* parent = GetParent() )
{
const wxSize parentSize( parent->GetSize() );
if( (parentSize.x > ((int)GetSize().x * 1.75)) && (parentSize.y > ((int)GetSize().y * 1.75)) )
{
CenterOnParent();
centerfail = false;
}
}
if( centerfail ) CenterOnScreen();
}
// Overrides wxDialog behavior to include automatic Fit() and CenterOnParent/Screen. The centering
// is based on a heuristic the centers against the parent window if the parent window is at least
// 75% larger than the fitted dialog.
int wxDialogWithHelpers::ShowModal()
{
SmartCenterFit();
return wxDialog::ShowModal();
}
// Overrides wxDialog behavior to include automatic Fit() and CenterOnParent/Screen. The centering
// is based on a heuristic the centers against the parent window if the parent window is at least
// 75% larger than the fitted dialog.
bool wxDialogWithHelpers::Show( bool show )
{
if( show ) SmartCenterFit();
return wxDialog::Show( show );
}
pxStaticText* wxDialogWithHelpers::Text( const wxString& label )
@ -85,6 +142,19 @@ pxStaticHeading* wxDialogWithHelpers::Heading( const wxString& label )
return new pxStaticHeading( this, label );
}
void wxDialogWithHelpers::OnCloseWindow( wxCloseEvent& evt )
{
if( !IsModal() ) Destroy();
evt.Skip();
}
void wxDialogWithHelpers::OnOkCancel( wxCommandEvent& evt )
{
Close();
evt.Skip();
}
void wxDialogWithHelpers::OnActivate(wxActivateEvent& evt)
{
//evt.Skip();
@ -119,7 +189,7 @@ void wxDialogWithHelpers::AddOkCancel( wxSizer &sizer, bool hasApply )
flex.AddGrowableCol( 1, 15 );
flex += m_extraButtonSizer | pxAlignLeft;
flex += s_buttons | pxExpand, pxCenter;
flex += s_buttons | (pxExpand & pxCenter);
sizer += flex | StdExpand();

View File

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

View File

@ -745,7 +745,7 @@ __forceinline void cdvdReadInterrupt()
// An arbitrary delay of some number of cycles probably makes more sense here,
// but for now it's based on the cdvd.ReadTime value. -- air
assert((int)cdvd.ReadTime > 0 );
pxAssume((int)cdvd.ReadTime > 0 );
CDVDREAD_INT(cdvd.ReadTime/4);
return;
}

View File

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

View File

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

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() );
if( folders[pid].IsEmpty() )
throw Exception::InvalidArgument( "Empty plugin filename." );
throw Exception::PluginInitError( pi->id, "Empty plugin filename." );
m_info[pid].Filename = folders[pid];
@ -985,6 +985,14 @@ void PluginManager::Close( PluginsEnum_t pid )
void PluginManager::Close( bool closegs )
{
// Spam stopper: If all plugins are already closed, then return before writing any logs. >_<
const PluginInfo* pi = tbl_PluginInfo; do {
if( m_info[pi->id].IsOpened && (closegs || (pi->id != PluginId_GS)) ) break;
} while( ++pi, pi->shortname != NULL );
if( pi->shortname == NULL ) return;
DbgCon.WriteLn( Color_StrongBlue, "Closing plugins..." );
// Close plugins in reverse order of the initialization procedure.

View File

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

View File

@ -3,7 +3,7 @@
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
* ation, either version 3 of te License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
@ -22,8 +22,11 @@
class _BaseStateThread;
// Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
static SafeArray<u8> state_buffer;
_BaseStateThread* current_state_thread = NULL;
// Simple lock boolean for the state buffer being in use by a thread.
static NonblockingMutex state_buffer_lock;
@ -56,9 +59,22 @@ class _BaseStateThread : public PersistentThread
protected:
EventListenerBinding<AppEventType> m_bind_OnExit;
bool m_isStarted;
// Holds the pause/suspend state of the emulator when the state load/stave chain of action is started,
// so that the proper state can be restoed automatically on completion.
bool m_resume_when_done;
public:
virtual ~_BaseStateThread() throw()
{
if( !m_isStarted ) return;
// Assertion fails because C++ changes the 'this' pointer to the base class since
// derived classes have been deallocated at this point the destructor!
//pxAssumeDev( current_state_thread == this, wxCharNull );
current_state_thread = NULL;
state_buffer_lock.Release(); // just in case;
}
@ -67,14 +83,18 @@ protected:
m_bind_OnExit( wxGetApp().Source_AppStatus(), EventListener<AppEventType>( this, StateThread_OnAppStatus ) )
{
Callback_FreezeFinished = onFinished;
m_name = L"StateThread::" + fromUTF8(name);
m_name = L"StateThread::" + fromUTF8(name);
m_isStarted = false;
m_resume_when_done = false;
}
void OnStart()
{
if( !state_buffer_lock.TryAcquire() )
throw Exception::CancelEvent( m_name + L"request ignored: state copy buffer is already locked!" );
current_state_thread = this;
m_isStarted = true;
_parent::OnStart();
}
@ -83,6 +103,7 @@ protected:
wxCommandEvent evt( pxEVT_FreezeThreadFinished );
evt.SetClientData( this );
evt.SetInt( type );
evt.SetExtraLong( m_resume_when_done );
wxGetApp().AddPendingEvent( evt );
}
@ -107,7 +128,7 @@ protected:
{
_parent::OnStart();
++sys_resume_lock;
CoreThread.Pause();
m_resume_when_done = CoreThread.Pause();
}
void ExecuteTaskInThread()
@ -122,43 +143,6 @@ protected:
}
};
// --------------------------------------------------------------------------------------
// StateThread_Thaw
// --------------------------------------------------------------------------------------
class StateThread_Thaw : public _BaseStateThread
{
typedef _BaseStateThread _parent;
public:
StateThread_Thaw( FnType_OnThreadComplete* onFinished ) : _BaseStateThread( "Thaw", onFinished ) { }
protected:
void OnStart()
{
_parent::OnStart();
if( state_buffer.IsDisposed() )
{
state_buffer_lock.Release();
throw Exception::RuntimeError( "ThawState request made, but no valid state exists!" );
}
++sys_resume_lock;
CoreThread.Pause();
}
void ExecuteTaskInThread()
{
memLoadingState( state_buffer ).FreezeAll();
}
void OnCleanupInThread()
{
SendFinishEvent( StateThreadAction_Restore );
_parent::OnCleanupInThread();
}
};
// --------------------------------------------------------------------------------------
// StateThread_ZipToDisk
// --------------------------------------------------------------------------------------
@ -171,14 +155,15 @@ protected:
gzFile m_gzfp;
public:
StateThread_ZipToDisk( FnType_OnThreadComplete* onFinished, const wxString& file ) :
_BaseStateThread( "ZipToDisk", onFinished )
, m_filename( file )
, m_gzfp( NULL )
StateThread_ZipToDisk( FnType_OnThreadComplete* onFinished, bool resume_done, const wxString& file )
: _BaseStateThread( "ZipToDisk", onFinished )
, m_filename( file )
{
m_gzfp = NULL;
m_resume_when_done = resume_done;
}
~StateThread_ZipToDisk() throw()
virtual ~StateThread_ZipToDisk() throw()
{
if( m_gzfp != NULL ) gzclose( m_gzfp );
}
@ -232,15 +217,16 @@ protected:
bool m_finished;
public:
StateThread_UnzipFromDisk( FnType_OnThreadComplete* onFinished, const wxString& file ) :
_BaseStateThread( "UnzipFromDisk", onFinished )
, m_filename( file )
, m_gzfp( NULL )
, m_finished( false )
StateThread_UnzipFromDisk( FnType_OnThreadComplete* onFinished, bool resume_done, const wxString& file )
: _BaseStateThread( "UnzipFromDisk", onFinished )
, m_filename( file )
{
m_gzfp = NULL;
m_finished = false;
m_resume_when_done = resume_done;
}
~StateThread_UnzipFromDisk() throw()
virtual ~StateThread_UnzipFromDisk() throw()
{
if( m_gzfp != NULL ) gzclose( m_gzfp );
}
@ -297,32 +283,17 @@ void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt )
}
if( fn_tmp != NULL ) fn_tmp( evt );
//m_evtsrc_FreezeThreadFinished.Dispatch( evt );
}
void OnFinished_Resume( const wxCommandEvent& evt )
static void OnFinished_Resume( const wxCommandEvent& evt )
{
if( evt.GetInt() == StateThreadAction_Restore )
{
// Successfully restored state, so remove the copy. Don't remove it sooner
// because the thread may have failed with some exception/error.
state_buffer.Dispose();
SysClearExecutionCache();
}
CoreThread.Resume();
}
void OnFinished_Dispose( const wxCommandEvent& evt )
{
state_buffer.Dispose();
CoreThread.RecoverState();
if( evt.GetExtraLong() ) CoreThread.Resume();
}
static wxString zip_dest_filename;
void OnFinished_ZipToDisk( const wxCommandEvent& evt )
static void OnFinished_ZipToDisk( const wxCommandEvent& evt )
{
if( !pxAssertDev( evt.GetInt() == StateThreadAction_Create, "Unexpected StateThreadAction value, aborting save." ) ) return;
@ -331,21 +302,13 @@ void OnFinished_ZipToDisk( const wxCommandEvent& evt )
Console.Warning( "Cannot save state to disk: empty filename specified." );
return;
}
// Phase 2: Record to disk!!
(new StateThread_ZipToDisk( OnFinished_Dispose, zip_dest_filename ))->Start();
(new StateThread_ZipToDisk( NULL, !!evt.GetExtraLong(), zip_dest_filename ))->Start();
CoreThread.Resume();
}
void OnFinished_Restore( const wxCommandEvent& evt )
{
if( !pxAssertDev( evt.GetInt() == StateThreadAction_UnzipFromDisk, "Unexpected StateThreadAction value, aborting restore." ) ) return;
// Phase 2: Restore over existing VM state!!
(new StateThread_Thaw( OnFinished_Resume ))->Start();
}
// =====================================================================================================
// StateCopy Public Interface
@ -362,8 +325,8 @@ void StateCopy_SaveToFile( const wxString& file )
void StateCopy_LoadFromFile( const wxString& file )
{
if( state_buffer_lock.IsLocked() ) return;
CoreThread.Pause();
(new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start();
bool resume_when_done = CoreThread.Pause();
(new StateThread_UnzipFromDisk( OnFinished_Resume, resume_when_done, file ))->Start();
}
// Saves recovery state info to the given saveslot, or saves the active emulation state
@ -394,13 +357,13 @@ void StateCopy_LoadFromSlot( uint slot )
Console.WriteLn( Color_StrongGreen, "Loading savestate from slot %d...", slot );
Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() );
CoreThread.Pause();
(new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start();
bool resume_when_done = CoreThread.Pause();
(new StateThread_UnzipFromDisk( OnFinished_Resume, resume_when_done, file ))->Start();
}
bool StateCopy_IsValid()
{
return !state_buffer.IsDisposed();
return !state_buffer.IsDisposed();
}
const SafeArray<u8>* StateCopy_GetBuffer()
@ -412,20 +375,45 @@ const SafeArray<u8>* StateCopy_GetBuffer()
void StateCopy_FreezeToMem()
{
if( state_buffer_lock.IsLocked() ) return;
(new StateThread_Freeze( OnFinished_Restore ))->Start();
(new StateThread_Freeze( OnFinished_Resume ))->Start();
}
void StateCopy_ThawFromMem()
static void _acquire_and_block()
{
if( state_buffer_lock.IsLocked() ) return;
new StateThread_Thaw( OnFinished_Restore );
if( state_buffer_lock.TryAcquire() ) return;
/*
// If the state buffer is locked and we're being called from the main thread then we need
// to cancel the current action. This is needed because state_buffer_lock is only updated
// from events handled on the main thread.
if( wxThread::IsMain() )
throw Exception::CancelEvent( "Blocking ThawFromMem canceled due to existing state buffer lock." );
else*/
{
pxAssume( current_state_thread != NULL );
do {
current_state_thread->Block();
wxGetApp().ProcessPendingEvents(); // Trying this for now, may or may not work due to recursive pitfalls (see above)
} while ( !state_buffer_lock.TryAcquire() );
}
}
void State_ThawFromMem_Blocking()
void StateCopy_FreezeToMem_Blocking()
{
if( !state_buffer_lock.TryAcquire() )
_acquire_and_block();
memSavingState( state_buffer ).FreezeAll();
state_buffer_lock.Release();
}
// Copies the saved state into the active VM, and automatically free's the saved state data.
void StateCopy_ThawFromMem_Blocking()
{
_acquire_and_block();
memLoadingState( state_buffer ).FreezeAll();
state_buffer.Dispose();
state_buffer_lock.Release();
}
@ -439,3 +427,4 @@ bool StateCopy_IsBusy()
{
return state_buffer_lock.IsLocked();
}

View File

@ -204,7 +204,9 @@ public:
extern bool StateCopy_IsValid();
extern void StateCopy_FreezeToMem();
extern void StateCopy_ThawFromMem();
extern void StateCopy_FreezeToMem_Blocking();
extern void StateCopy_ThawFromMem_Blocking();
extern void StateCopy_SaveToFile( const wxString& file );
extern void StateCopy_LoadFromFile( const wxString& file );
extern void StateCopy_SaveToSlot( uint num );

View File

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

View File

@ -62,7 +62,7 @@ protected:
// implemented by the provisioning interface.
extern SysCoreAllocations& GetSysCoreAlloc();
extern void SysDetect(); // Detects cpu type and fills cpuInfo structs.
extern void SysLogMachineCaps(); // Detects cpu type and fills cpuInfo structs.
extern void SysClearExecutionCache(); // clears recompiled execution caches!
@ -106,6 +106,7 @@ extern void NTFS_CompressFile( const wxString& file, bool compressStatus=true );
//
class pxMessageBoxEvent;
class pxAssertionEvent;
namespace Msgbox
{
@ -116,10 +117,9 @@ namespace Msgbox
extern bool YesNo( const wxString& text, const wxString& caption=L"PCSX2 Message", int icon=wxICON_QUESTION );
extern int Assertion( const wxString& text, const wxString& stacktrace );
extern void Except( const Exception::BaseException& src );
}
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEVT_MSGBOX, -1 )
DECLARE_EVENT_TYPE( pxEVT_CallStackBox, -1 )
DECLARE_EVENT_TYPE( pxEVT_ASSERTION, -1 )
END_DECLARE_EVENT_TYPES()

View File

@ -56,6 +56,18 @@ void SysCoreThread::Cancel( bool isBlocking )
{
m_CoreCancelDamnit = true;
_parent::Cancel();
ReleaseResumeLock();
}
bool SysCoreThread::Cancel( const wxTimeSpan& span )
{
m_CoreCancelDamnit = true;
if( _parent::Cancel( span ) )
{
ReleaseResumeLock();
return true;
}
return false;
}
void SysCoreThread::Start()
@ -78,21 +90,26 @@ void SysCoreThread::Start()
void SysCoreThread::OnResumeReady()
{
if( m_resetVirtualMachine )
{
cpuReset();
m_resetVirtualMachine = false;
m_hasValidState = false;
}
m_hasValidState = false;
if( !m_hasValidState )
m_resetRecompilers = true;
}
// Tells the thread to recover from the in-memory state copy when it resumes. (thread must be
// resumed manually).
void SysCoreThread::RecoverState()
{
Pause();
m_resetVirtualMachine = true;
m_hasValidState = false;
}
void SysCoreThread::Reset()
{
Suspend();
m_resetVirtualMachine = true;
m_hasValidState = false;
m_resetVirtualMachine = true;
m_hasValidState = false;
}
// This function *will* reset the emulator in order to allow the specified elf file to
@ -159,7 +176,8 @@ void SysCoreThread::ApplySettings( const Pcsx2Config& src )
ScopedCoreThreadPause sys_paused;
m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Recompiler != EmuConfig.Recompiler ) ||
( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
m_resetProfilers = ( src.Profiler != EmuConfig.Profiler );
m_resetVsyncTimers = ( src.GS != EmuConfig.GS );
@ -213,10 +231,50 @@ struct ScopedBool_ClearOnError
void Success() { m_success = true; }
};
void SysCoreThread::_reset_stuff_as_needed()
{
if( m_resetVirtualMachine || m_resetRecompilers || m_resetProfilers )
{
SysClearExecutionCache();
memBindConditionalHandlers();
m_resetRecompilers = false;
m_resetProfilers = false;
}
if( m_resetVirtualMachine )
{
cpuReset();
m_resetVirtualMachine = false;
m_resetRecompilers = true;
}
if( m_resetVsyncTimers )
{
UpdateVSyncRate();
frameLimitReset();
m_resetVsyncTimers = false;
}
SetCPUState( EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVUMXCSR );
}
void SysCoreThread::CpuInitializeMess()
{
if( m_hasValidState ) return;
if( StateCopy_IsValid() )
{
// Automatic recovery system if a state exists in memory. This is executed here
// in order to ensure the plugins are in the proper (loaded/opened) state.
SysClearExecutionCache();
StateCopy_ThawFromMem_Blocking();
m_hasValidState = true;
m_resetVirtualMachine = false;
return;
}
_reset_stuff_as_needed();
ScopedBool_ClearOnError sbcoe( m_hasValidState );
@ -262,24 +320,6 @@ void SysCoreThread::CpuInitializeMess()
sbcoe.Success();
}
void SysCoreThread::_reset_stuff_as_needed()
{
if( m_resetRecompilers || m_resetProfilers )
{
SysClearExecutionCache();
memBindConditionalHandlers();
m_resetRecompilers = false;
m_resetProfilers = false;
}
if( m_resetVsyncTimers )
{
UpdateVSyncRate();
frameLimitReset();
m_resetVsyncTimers = false;
}
}
// Called by the VsyncInThread() if a valid keyEvent is pending and is unhandled by other
// PS2 core plugins.
void SysCoreThread::DispatchKeyEventToUI( const keyEvent& evt )
@ -315,10 +355,11 @@ void SysCoreThread::StateCheckInThread()
{
GetMTGS().RethrowException();
_parent::StateCheckInThread();
if( !m_hasValidState )
throw Exception::RuntimeError( "Invalid emulation state detected; Virtual machine threads have been cancelled." );
_reset_stuff_as_needed();
_reset_stuff_as_needed(); // kinda redundant but could catch unexpected threaded state changes...
}
void SysCoreThread::ExecuteTaskInThread()
@ -330,9 +371,10 @@ void SysCoreThread::ExecuteTaskInThread()
m_mxcsr_saved.bitmask = _mm_getcsr();
PCSX2_PAGEFAULT_PROTECT {
StateCheckInThread();
SetCPUState( EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVUMXCSR );
Cpu->Execute();
do {
StateCheckInThread();
Cpu->Execute();
} while( true );
} PCSX2_PAGEFAULT_EXCEPT;
}
@ -347,8 +389,7 @@ void SysCoreThread::OnResumeInThread( bool isSuspended )
if( g_plugins != NULL )
g_plugins->Open();
if( isSuspended )
CpuInitializeMess();
CpuInitializeMess();
}

View File

@ -36,22 +36,9 @@ SysThreadBase::~SysThreadBase() throw()
void SysThreadBase::Start()
{
_parent::Start();
m_ExecMode = ExecMode_Closing;
Sleep( 1 );
if( !m_ResumeEvent.WaitWithoutYield( wxTimeSpan(0, 0, 1, 500) ) )
{
RethrowException();
if( pxAssertDev( m_ExecMode == ExecMode_Closing, "Unexpected thread status during SysThread startup." ) )
{
throw Exception::ThreadCreationError( *this,
L"Timeout occurred while attempting to start the '%s' thread.",
wxEmptyString
);
}
}
pxAssertDev( (m_ExecMode == ExecMode_Closing) || (m_ExecMode == ExecMode_Closed),
"Unexpected thread status during SysThread startup."
);
@ -118,7 +105,7 @@ bool SysThreadBase::Suspend( bool isBlocking )
break;
}
pxAssertDev( m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing..." );
pxAssumeDev( m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing..." );
m_sem_event.Post();
}
@ -164,7 +151,7 @@ bool SysThreadBase::Pause()
retval = true;
}
pxAssertDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
pxAssumeDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
m_sem_event.Post();
}
@ -247,7 +234,7 @@ void SysThreadBase::OnStartInThread()
{
m_RunningLock.Acquire();
_parent::OnStartInThread();
m_ResumeEvent.Post();
m_ExecMode = ExecMode_Closing;
}
void SysThreadBase::OnCleanupInThread()

View File

@ -20,22 +20,6 @@
using namespace Threading;
#if !PCSX2_SEH
# include <setjmp.h>
// Platforms without SEH need to use SetJmp / LongJmp to deal with exiting the recompiled
// code execution pipelines in an efficient manner, since standard C++ exceptions cannot
// unwind across dynamically recompiled code.
enum
{
SetJmp_Dispatcher = 1,
SetJmp_Exit,
};
#endif
// --------------------------------------------------------------------------------------
// ISysThread
// --------------------------------------------------------------------------------------
@ -208,8 +192,10 @@ public:
virtual void ApplySettings( const Pcsx2Config& src );
virtual void OnResumeReady();
virtual void Reset();
virtual void RecoverState();
virtual void Cancel( bool isBlocking=true );
virtual bool Cancel( const wxTimeSpan& timeout );
bool HasValidState()
{
return m_hasValidState;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,18 +18,27 @@
#include <wx/stackwalk.h>
static wxString pxGetStackTrace()
static wxString pxGetStackTrace( const FnChar_t* calledFrom )
{
wxString stackTrace;
class StackDump : public wxStackWalker
{
protected:
wxString m_stackTrace;
wxString m_stackTrace;
wxString m_srcFuncName;
bool m_ignoreDone;
int m_skipped;
public:
StackDump() { }
StackDump( const FnChar_t* src_function_name )
{
if( src_function_name != NULL )
m_srcFuncName = fromUTF8(src_function_name);
m_ignoreDone = false;
m_skipped = 0;
}
const wxString& GetStackTrace() const { return m_stackTrace; }
@ -38,92 +47,108 @@ static wxString pxGetStackTrace()
{
wxString name( frame.GetName() );
if( name.IsEmpty() )
{
name = wxsFormat( L"%p ", frame.GetAddress() );
}
/*else if( m_srcFuncName.IsEmpty() || m_srcFuncName == name )
{
// FIXME: This logic isn't reliable yet.
// It's possible for our debug information to not match the function names returned by
// __pxFUNCTION__ (might happen in linux a lot, and could happen in win32 due to
// inlining on Dev aserts). The better approach is a system the queues up all the
// stacktrace info in individual wxStrings, and does a two-pass check -- first pass
// for the function name and, if not found, a second pass that just skips the first
// few stack entries.
// It's important we only walk the stack once because Linux (argh, always linux!) has
// a really god aweful slow stack walker.
// I'm not doing it right now because I've worked on this mess enough for one week. --air
m_stackTrace += wxString::Format( L"[%02d] %-46s ",
wx_truncate_cast(int, frame.GetLevel()), name.c_str()
m_ignoreDone = true;
}
if( !m_ignoreDone )
{
m_skipped++;
return;
}*/
//wxString briefName;
wxString essenName;
if( frame.HasSourceLocation() )
{
wxFileName wxfn(frame.GetFileName());
//briefName.Printf( L"(%s:%d)", wxfn.GetFullName().c_str(), frame.GetLine() );
wxfn.SetVolume( wxEmptyString );
int count = wxfn.GetDirCount();
for( int i=0; i<2; ++i )
wxfn.RemoveDir(0);
essenName.Printf( L"%s:%d", wxfn.GetFullPath().c_str(), frame.GetLine() );
}
m_stackTrace += wxString::Format( L"[%02d] %-44s %s\n",
frame.GetLevel()-m_skipped,
name.c_str(),
essenName.c_str()
);
if ( frame.HasSourceLocation() )
m_stackTrace += wxsFormat( L"%s:%d", frame.GetFileName().c_str(), frame.GetLine() );
m_stackTrace += L'\n';
}
};
// [TODO] : Replace this with a textbox dialog setup.
static const int maxLines = 20;
StackDump dump;
dump.Walk(2, maxLines); // don't show OnAssert() call itself
stackTrace = dump.GetStackTrace();
const int count = stackTrace.Freq( L'\n' );
for ( int i = 0; i < count - maxLines; i++ )
stackTrace = stackTrace.BeforeLast( L'\n' );
return stackTrace;
StackDump dump( calledFrom );
dump.Walk( 3 );
return dump.GetStackTrace();
}
static __threadlocal bool _reentrant_lock = false;
#ifdef __WXDEBUG__
static __threadlocal int _reentrant_lock = 0;
// This override of wx's implementation provides thread safe assertion message reporting. If we aren't
// on the main gui thread then the assertion message box needs to be passed off to the main gui thread
// via messages.
void Pcsx2App::OnAssertFailure( const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg )
{
// Used to allow the user to suppress future assertions during this application's session.
static bool disableAsserts = false;
if( disableAsserts ) return;
// Re-entrant assertions are bad mojo -- trap immediately.
RecursionGuard guard( _reentrant_lock );
if( guard.IsReentrant() ) wxTrap();
if( _reentrant_lock )
wxCharBuffer bleh( wxString(func).ToUTF8() );
if( AppDoAssert( DiagnosticOrigin( file, line, bleh, cond ), msg ) )
{
// Re-entrant assertions are bad mojo -- trap immediately.
wxTrap();
}
}
_reentrant_lock = true;
#endif
wxString dbgmsg;
dbgmsg.reserve( 2048 );
bool AppDoAssert( const DiagnosticOrigin& origin, const wxChar *msg )
{
// Used to allow the user to suppress future assertions during this application's session.
static bool disableAsserts = false;
if( disableAsserts ) return false;
wxString message;
if( msg == NULL )
message = cond;
else
message.Printf( L"%s (%s)", msg, cond );
// make life easier for people using VC++ IDE by using this format, which allows double-click
// response times from the Output window...
dbgmsg.Printf( L"%s(%d) : assertion failed%s%s: %s\n", file, line,
(func==NULL) ? wxEmptyString : L" in ",
(func==NULL) ? wxEmptyString : func,
message.c_str()
);
wxString trace( L"Call stack:\n" + pxGetStackTrace() );
wxString trace( pxGetStackTrace(origin.function) );
wxString dbgmsg( origin.ToString( msg ) );
wxMessageOutputDebug().Printf( dbgmsg );
Console.Error( dbgmsg );
Console.WriteLn( trace );
int retval = Msgbox::Assertion( dbgmsg, trace );
switch( retval )
{
case wxID_YES:
wxTrap();
break;
case wxID_NO: break;
case wxID_CANCEL: // ignores future assertions.
disableAsserts = true;
break;
}
wxString windowmsg( L"Assertion failed: " );
if( msg != NULL )
windowmsg += msg;
else if( origin.condition != NULL )
windowmsg += origin.condition;
_reentrant_lock = false;
int retval = Msgbox::Assertion( windowmsg, dbgmsg + L"\nStacktrace:\n" + trace );
if( retval == wxID_YES ) return true;
if( retval == wxID_IGNORE ) disableAsserts = true;
return false;
}
#endif

View File

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

View File

@ -154,7 +154,7 @@ public:
// Current language in use (correlates to a wxWidgets wxLANGUAGE specifier)
wxLanguage LanguageId;
int RecentFileCount; // number of files displayed in the Recent Isos list.
int RecentIsoCount; // number of files displayed in the Recent Isos list.
// String value describing the desktop theme to use for pcsk2 (icons and background images)
// The theme name is used to look up files in the themes folder (relative to the executable).

View File

@ -28,6 +28,16 @@ AppCoreThread::AppCoreThread() : SysCoreThread()
AppCoreThread::~AppCoreThread() throw()
{
AppCoreThread::Cancel();
}
void AppCoreThread::Cancel( bool isBlocking )
{
if( !_parent::Cancel( wxTimeSpan( 0,0,1,0 ) ) )
{
// Possible deadlock!
throw Exception::ThreadTimedOut( this );
}
}
void AppCoreThread::Reset()

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()
{
if( !IsDebugBuild ) return;
@ -81,8 +107,7 @@ void Pcsx2App::ReadUserModeSettings()
if (IOP_ENABLE_SIF_HACK == 1)
{
wxDialogWithHelpers hackedVersion( NULL, wxID_ANY, _("It will devour your young! - PCSX2 Shub-Niggurath edition"), false );
hackedVersion.SetIdealWidth( 575 );
wxDialogWithHelpers hackedVersion( NULL, _("It will devour your young! - PCSX2 Shub-Niggurath edition"), wxVERTICAL );
hackedVersion.SetSizer( new wxBoxSizer( wxVERTICAL ) );
hackedVersion += new pxStaticText( &hackedVersion,
@ -92,8 +117,6 @@ void Pcsx2App::ReadUserModeSettings()
);
hackedVersion += new wxButton( &hackedVersion, wxID_OK ) | pxSizerFlags::StdCenter();
hackedVersion.Fit();
hackedVersion.CentreOnScreen();
hackedVersion.ShowModal();
}
@ -101,8 +124,7 @@ void Pcsx2App::ReadUserModeSettings()
{
// Pre-Alpha Warning! Why didn't I think to add this sooner?!
wxDialogWithHelpers preAlpha( NULL, wxID_ANY, _("It might devour your kittens! - PCSX2 0.9.7 Pre-Alpha"), false );
preAlpha.SetIdealWidth( 575 );
wxDialogWithHelpers preAlpha( NULL, _("It might devour your kittens! - PCSX2 0.9.7 Pre-Alpha"), wxVERTICAL );
preAlpha.SetSizer( new wxBoxSizer( wxVERTICAL ) );
preAlpha += new pxStaticText( &preAlpha,
@ -113,8 +135,6 @@ void Pcsx2App::ReadUserModeSettings()
);
preAlpha += new wxButton( &preAlpha, wxID_OK ) | pxSizerFlags::StdCenter();
preAlpha.Fit();
preAlpha.CentreOnScreen();
preAlpha.ShowModal();
// first time startup, so give the user the choice of user mode:
@ -243,6 +263,15 @@ typedef void (wxEvtHandler::*pxMessageBoxEventFunction)(pxMessageBoxEvent&);
// ------------------------------------------------------------------------
bool Pcsx2App::OnInit()
{
#define pxMessageBoxEventThing(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxMessageBoxEventFunction, &func )
Connect( pxEVT_MSGBOX, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_ASSERTION, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
pxDoAssert = AppDoAssert;
g_Conf = new AppConfig();
EnableAllLogging();
@ -253,12 +282,6 @@ bool Pcsx2App::OnInit()
m_StderrRedirHandle = NewPipeRedir(stderr);
wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() );
#define pxMessageBoxEventThing(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxMessageBoxEventFunction, &func )
Connect( pxEVT_MSGBOX, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_CallStackBox, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) );
Connect( pxEVT_SysExecute, wxCommandEventHandler( Pcsx2App::OnSysExecute ) );
@ -286,9 +309,21 @@ bool Pcsx2App::OnInit()
m_Resources = new pxAppResources();
cpudetectInit();
if( !x86caps.hasMultimediaExtensions )
{
// Note: due to memcpy_fast, we need minimum MMX even for interpreters. This will
// hopefully change later once we have a dynamically recompiled memcpy.
Msgbox::Alert( _("PCSX2 requires cpu with MMX instruction to run. Press OK to close."), _("PCSX2 - MMX Required") );
return false;
}
ReadUserModeSettings();
AppConfig_OnChangedSettingsFolder();
CpuCheckSSE2();
m_MainFrame = new MainEmuFrame( NULL, L"PCSX2" );
m_MainFrame->PushEventHandler( &GetRecentIsoList() );
@ -307,7 +342,8 @@ bool Pcsx2App::OnInit()
SetExitOnFrameDelete( true ); // but being explicit doesn't hurt...
m_MainFrame->Show();
SysDetect();
SysLogMachineCaps();
AppApplySettings();
#ifdef __WXMSW__
@ -317,47 +353,72 @@ bool Pcsx2App::OnInit()
m_CoreAllocs = new SysCoreAllocations();
if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) )
{
// HadSomeFailures only returns 'true' if an *enabled* cpu type fails to init. If
// the user already has all interps configured, for example, then no point in
// popping up this dialog.
wxDialogWithHelpers exconf( NULL, _("PCSX2 Recompiler Error(s)"), wxVERTICAL );
// TODO : This should be redone using the ExtensibleConfirmation, and a sub-window
// (static text or something with a vertical scrollbar).
exconf += 12;
exconf += exconf.Heading( pxE( ".Error:RecompilerInit",
L"Warning: Some of the configured PS2 recompilers failed to initialize and will not be available for this session:\n" )
);
wxString message( _("The following cpu recompilers failed to initialize and will not be available:\n\n") );
wxTextCtrl* scrollableTextArea = new wxTextCtrl(
&exconf, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_READONLY | wxTE_MULTILINE | wxTE_WORDWRAP
);
exconf += scrollableTextArea | pxSizerFlags::StdExpand();
if( !m_CoreAllocs->IsRecAvailable_EE() )
{
message += L"\t* R5900 (EE)\n";
scrollableTextArea->AppendText( L"* R5900 (EE)\n\n" );
g_Conf->EmuOptions.Recompiler.EnableEE = false;
}
if( !m_CoreAllocs->IsRecAvailable_IOP() )
{
message += L"\t* R3000A (IOP)\n";
scrollableTextArea->AppendText( L"* R3000A (IOP)\n\n" );
g_Conf->EmuOptions.Recompiler.EnableIOP = false;
}
if( !m_CoreAllocs->IsRecAvailable_MicroVU0() )
{
message += L"\t* microVU0\n";
scrollableTextArea->AppendText( L"* microVU0\n\n" );
g_Conf->EmuOptions.Recompiler.UseMicroVU0 = false;
g_Conf->EmuOptions.Recompiler.EnableVU0 = g_Conf->EmuOptions.Recompiler.EnableVU0 && m_CoreAllocs->IsRecAvailable_SuperVU0();
}
if( !m_CoreAllocs->IsRecAvailable_MicroVU1() )
{
message += L"\t* microVU1\n";
scrollableTextArea->AppendText( L"* microVU1\n\n" );
g_Conf->EmuOptions.Recompiler.UseMicroVU1 = false;
g_Conf->EmuOptions.Recompiler.EnableVU1 = g_Conf->EmuOptions.Recompiler.EnableVU1 && m_CoreAllocs->IsRecAvailable_SuperVU1();
}
if( !m_CoreAllocs->IsRecAvailable_SuperVU0() )
{
message += L"\t* SuperVU0\n";
scrollableTextArea->AppendText( L"* SuperVU0\n\n" );
g_Conf->EmuOptions.Recompiler.UseMicroVU0 = m_CoreAllocs->IsRecAvailable_MicroVU0();
g_Conf->EmuOptions.Recompiler.EnableVU0 = g_Conf->EmuOptions.Recompiler.EnableVU0 && g_Conf->EmuOptions.Recompiler.UseMicroVU0;
}
if( !m_CoreAllocs->IsRecAvailable_SuperVU1() )
{
message += L"\t* SuperVU1\n";
scrollableTextArea->AppendText( L"* SuperVU1\n\n" );
g_Conf->EmuOptions.Recompiler.UseMicroVU1 = m_CoreAllocs->IsRecAvailable_MicroVU1();
g_Conf->EmuOptions.Recompiler.EnableVU1 = g_Conf->EmuOptions.Recompiler.EnableVU1 && g_Conf->EmuOptions.Recompiler.UseMicroVU1;
}
exconf += new ModalButtonPanel( &exconf, MsgButtons().OK() ) | pxSizerFlags::StdCenter();
exconf.ShowModal();
// Failures can be SSE-related OR memory related. Should do per-cpu error reports instead...
/*message += pxE( ".Popup Error:EmuCore:MemoryForRecs",
@ -367,8 +428,8 @@ bool Pcsx2App::OnInit()
L"Interpreters can be very slow, so don't get too excited. Press OK to continue or CANCEL to close PCSX2."
);*/
if( !Msgbox::OkCancel( message, _("PCSX2 Initialization Error"), wxICON_ERROR ) )
return false;
//if( !Msgbox::OkCancel( message, _("PCSX2 Initialization Error"), wxICON_ERROR ) )
// return false;
}
LoadPluginsPassive( NULL );
@ -408,13 +469,16 @@ void Pcsx2App::CleanupMess()
if( m_CorePlugins )
m_CorePlugins->Shutdown();
}
catch( Exception::ThreadTimedOut& ) { throw; }
catch( Exception::CancelEvent& ) { throw; }
catch( Exception::RuntimeError& ex )
{
// Handle runtime errors gracefully during shutdown. Mostly these are things
// that we just don't care about by now, and just want to "get 'er done!" so
// we can exit the app. ;)
Console.Error( ex.FormatDiagnosticMessage() );
Console.Error( L"Runtime exception handled during CleanupMess:\n" );
Console.Indent().Error( ex.FormatDiagnosticMessage() );
}
// Notice: deleting the plugin manager (unloading plugins) here causes Lilypad to crash,
@ -427,6 +491,8 @@ void Pcsx2App::CleanupMess()
while( wxGetLocale() != NULL )
delete wxGetLocale();
pxDoAssert = pxAssertImpl_LogIt;
}
Pcsx2App::Pcsx2App()
@ -441,6 +507,8 @@ Pcsx2App::Pcsx2App()
Pcsx2App::~Pcsx2App()
{
pxDoAssert = pxAssertImpl_LogIt;
// Typically OnExit cleans everything up before we get here, *unless* we cancel
// out of program startup in OnInit (return false) -- then remaining cleanup needs
// to happen here in the destructor.
@ -485,6 +553,6 @@ struct CrtDebugBreak
}
};
//CrtDebugBreak breakAt( 4327 );
//CrtDebugBreak breakAt( 1175 );
#endif

View File

@ -53,7 +53,7 @@ ConfigOverrides OverrideOptions;
static bool HandlePluginError( Exception::PluginError& ex )
{
if( pxDialogExists( DialogId_CoreSettings ) ) return true;
if( pxDialogExists( L"CoreSettings" ) ) return true;
bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() +
_("\n\nPress Ok to go to the Plugin Configuration Panel.")
@ -64,7 +64,7 @@ static bool HandlePluginError( Exception::PluginError& ex )
g_Conf->SettingsTabName = L"Plugins";
// fixme: Send a message to the panel to select the failed plugin.
if( Dialogs::ConfigurationDialog().ShowModal() == wxID_CANCEL )
if( wxGetApp().IssueModalDialog( Dialogs::ConfigurationDialog::GetNameStatic() ) == wxID_CANCEL )
return false;
}
return result;
@ -101,18 +101,6 @@ void Pcsx2App::PostPadKey( wxKeyEvent& evt )
}
}
int Pcsx2App::ThreadedModalDialog( DialogIdentifiers dialogId )
{
AffinityAssert_AllowFromMain();
MsgboxEventResult result;
wxCommandEvent joe( pxEVT_OpenModalDialog, dialogId );
joe.SetClientData( &result );
AddPendingEvent( joe );
result.WaitForMe.WaitNoCancel();
return result.result;
}
// ----------------------------------------------------------------------------
// Pcsx2App Event Handlers
// ----------------------------------------------------------------------------
@ -130,54 +118,69 @@ void Pcsx2App::OnCoreThreadStatus( wxCommandEvent& evt )
void Pcsx2App::OnOpenModalDialog( wxCommandEvent& evt )
{
using namespace Dialogs;
pxAssertDev( !evt.GetString().IsEmpty(), wxNullChar );
MsgboxEventResult& evtres( *((MsgboxEventResult*)evt.GetClientData()) );
switch( evt.GetId() )
MsgboxEventResult* evtres = (MsgboxEventResult*)evt.GetClientData();
wxWindowID result = IssueModalDialog( evt.GetString() );
if( evtres != NULL )
{
case DialogId_CoreSettings:
{
static int _guard = 0;
RecursionGuard guard( _guard );
if( guard.IsReentrant() ) return;
evtres.result = ConfigurationDialog().ShowModal();
}
break;
case DialogId_BiosSelector:
{
static int _guard = 0;
RecursionGuard guard( _guard );
if( guard.IsReentrant() ) return;
evtres.result = BiosSelectorDialog().ShowModal();
}
break;
case DialogId_LogOptions:
{
static int _guard = 0;
RecursionGuard guard( _guard );
if( guard.IsReentrant() ) return;
evtres.result = LogOptionsDialog().ShowModal();
}
break;
case DialogId_About:
{
static int _guard = 0;
RecursionGuard guard( _guard );
if( guard.IsReentrant() ) return;
evtres.result = AboutBoxDialog().ShowModal();
}
break;
evtres->result = result;
evtres->WaitForMe.Post();
}
evtres.WaitForMe.Post();
}
void Pcsx2App::OnMessageBox( pxMessageBoxEvent& evt )
int Pcsx2App::IssueModalDialog( const wxString& dlgName )
{
Msgbox::OnEvent( evt );
if( dlgName.IsEmpty() ) return wxID_CANCEL;
if( !wxThread::IsMain() )
{
MsgboxEventResult result;
wxCommandEvent joe( pxEVT_OpenModalDialog );
joe.SetString( dlgName );
joe.SetClientData( &result );
AddPendingEvent( joe );
result.WaitForMe.WaitNoCancel();
return result.result;
}
if( wxWindow* window = wxFindWindowByName( dlgName ) )
{
if( wxIsKindOf( window, wxDialog ) )
{
wxDialog* dialog = (wxDialog*)window;
window->SetFocus();
// It's legal to call ShowModal on a non-modal dialog, therefore making
// it modal in nature for the needs of whatever other thread of action wants
// to block against it:
if( !dialog->IsModal() )
{
int result = dialog->ShowModal();
dialog->Destroy();
return result;
}
}
}
else
{
using namespace Dialogs;
if( dlgName == ConfigurationDialog::GetNameStatic() )
return ConfigurationDialog().ShowModal();
if( dlgName == BiosSelectorDialog::GetNameStatic() )
return BiosSelectorDialog().ShowModal();
if( dlgName == LogOptionsDialog::GetNameStatic() )
return LogOptionsDialog().ShowModal();
if( dlgName == AboutBoxDialog::GetNameStatic() )
return AboutBoxDialog().ShowModal();
}
return wxID_CANCEL;
}
HashTools::HashMap<int, const GlobalCommandDescriptor*> GlobalAccels( 0, 0xffffffff );
@ -227,15 +230,14 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
// ----------------------------------------------------------------------------
catch( Exception::BiosLoadFailed& ex )
{
bool result = Dialogs::ExtensibleConfirmation( NULL, ConfButtons().OK().Cancel(),
L"PS2 BIOS Error",
ex.FormatDisplayMessage() + BIOS_GetMsg_Required() + _("\nPress Ok to go to the BIOS Configuration Panel.")
).ShowModal() != wxID_CANCEL;
if( !result )
wxDialogWithHelpers dialog( NULL, _("PS2 BIOS Error"), wxVERTICAL );
dialog += dialog.Heading( ex.FormatDisplayMessage() + BIOS_GetMsg_Required() + _("\nPress Ok to go to the BIOS Configuration Panel.") );
dialog += new ModalButtonPanel( &dialog, MsgButtons().OKCancel() );
if( dialog.ShowModal() == wxID_CANCEL )
Console.Warning( "User denied option to re-configure BIOS." );
if( Dialogs::BiosSelectorDialog().ShowModal() != wxID_CANCEL )
if( IssueModalDialog( Dialogs::BiosSelectorDialog::GetNameStatic() ) != wxID_CANCEL )
{
SysExecute();
}
@ -268,15 +270,22 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
// ----------------------------------------------------------------------------
catch( Exception::ThreadTimedOut& ex )
{
// [TODO] Bind a listener to the CoreThread status, and automatically close the dialog
// if the thread starts responding while we're waiting (not hard in fact, but I'm getting
// a little tired, so maybe later!) --air
Console.Warning( ex.FormatDiagnosticMessage() );
int result = Dialogs::ExtensibleConfirmation( NULL, ConfButtons().Ignore().Cancel().Custom( _("Terminate") ),
_("PCSX2 Unresponsive Thread"), ex.FormatDisplayMessage() + L"\n\n" +
wxDialogWithHelpers dialog( NULL, _("PCSX2 Unresponsive Thread"), wxVERTICAL );
dialog += dialog.Heading( ex.FormatDisplayMessage() + L"\n\n" +
pxE( ".Popup Error:Thread Deadlock Actions",
L"'Ignore' to continue waiting for the thread to respond.\n"
L"'Cancel' to attempt to cancel the thread.\n"
L"'Terminate' to quit PCSX2 immediately.\n"
)
).ShowModal();
);
int result = pxIssueConfirmation( dialog, MsgButtons().Ignore().Cancel().Custom( _("Terminate") ) );
if( result == pxID_CUSTOM )
{
@ -386,14 +395,14 @@ int Pcsx2App::OnExit()
// is a matter of programmer preference).
MainEmuFrame& Pcsx2App::GetMainFrame() const
{
pxAssert( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) );
pxAssert( m_MainFrame != NULL );
pxAssume( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) );
pxAssume( m_MainFrame != NULL );
return *m_MainFrame;
}
GSFrame& Pcsx2App::GetGSFrame() const
{
pxAssert( m_gsFrame != NULL );
pxAssume( m_gsFrame != NULL );
return *m_gsFrame;
}
@ -594,6 +603,8 @@ void Pcsx2App::OnSysExecute( wxCommandEvent& evt )
// Full system reset stops the core thread and unloads all core plugins *completely*.
void Pcsx2App::SysReset()
{
StateCopy_Clear();
CoreThread.Reset();
CoreThread.ReleaseResumeLock();
m_CorePlugins = NULL;

View File

@ -176,8 +176,15 @@ ConsoleLogFrame::ColorArray::~ColorArray()
void ConsoleLogFrame::ColorArray::Create( int fontsize )
{
const wxFont fixed( fontsize, wxMODERN, wxNORMAL, wxNORMAL );
const wxFont fixedB( fontsize, wxMODERN, wxNORMAL, wxBOLD );
// pxGetFixedFont selects Andale Mono on Win32, which is nice visually but
// unfortunately has inconsistently spaced bold versions, so it's not good
// for our console.
const wxFont fixed( pxGetFixedFont( fontsize ) );
const wxFont fixedB( pxGetFixedFont( fontsize+1, wxBOLD ) );
//const wxFont fixed( fontsize, wxMODERN, wxNORMAL, wxNORMAL );
//const wxFont fixedB( fontsize, wxMODERN, wxNORMAL, wxBOLD );
// Standard R, G, B format:
new (&m_table[Color_Default]) wxTextAttr( wxColor( 0, 0, 0 ), wxNullColour, fixed );
@ -262,6 +269,10 @@ ConsoleLogFrame::ConsoleLogFrame( MainEmuFrame *parent, const wxString& title, A
m_TextCtrl.SetBackgroundColour( wxColor( 230, 235, 242 ) );
m_TextCtrl.SetDefaultStyle( m_ColorTable[DefaultConsoleColor] );
// SetDefaultStyle only sets the style of text in the control. We need to
// also set the font of the control, so that sizing logic knows what font we use:
m_TextCtrl.SetFont( m_ColorTable[DefaultConsoleColor].GetFont() );
wxMenu& menuLog (*new wxMenu());
wxMenu& menuAppear (*new wxMenu());
wxMenu& menuSources (*new wxMenu());

View File

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

View File

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

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 );
}
Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent, int id )
: wxDialogWithHelpers( parent, id, _("PCSX2 Configuration"), true )
Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent )
: wxDialogWithHelpers( parent, _("PCSX2 Configuration"), true )
, m_listbook( *new wxListbook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_orient ) )
{
SetName( GetNameStatic() );
m_idealWidth = 600;
m_listbook.SetImageList( &wxGetApp().GetImgList_Config() );
@ -85,9 +87,6 @@ Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent, int id )
FindWindow( wxID_APPLY )->Disable();
Fit();
CenterOnScreen();
#ifdef __WXMSW__
// Depending on Windows version and user appearance settings, the default icon spacing can be
// way over generous. This little bit of Win32-specific code ensures proper icon spacing, scaled
@ -103,6 +102,8 @@ Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent, int id )
Connect( wxID_APPLY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigurationDialog::OnApply_Click ) );
Connect( wxID_SAVE, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigurationDialog::OnScreenshot_Click ) );
Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler(ConfigurationDialog::OnCloseWindow) );
// ----------------------------------------------------------------------------
// Bind a variety of standard "something probably changed" events. If the user invokes
// any of these, we'll automatically de-gray the Apply button for this dialog box. :)
@ -126,6 +127,12 @@ Dialogs::ConfigurationDialog::~ConfigurationDialog() throw()
g_ApplyState.DoCleanup();
}
void Dialogs::ConfigurationDialog::OnCloseWindow( wxCloseEvent& evt )
{
if( !IsModal() ) Destroy();
evt.Skip();
}
void Dialogs::ConfigurationDialog::OnOk_Click( wxCommandEvent& evt )
{
if( g_ApplyState.ApplyAll() )
@ -133,8 +140,6 @@ void Dialogs::ConfigurationDialog::OnOk_Click( wxCommandEvent& evt )
FindWindow( wxID_APPLY )->Disable();
g_Conf->SettingsTabName = m_labels[m_listbook.GetSelection()];
AppSaveSettings();
Close();
evt.Skip();
}
}
@ -181,9 +186,11 @@ void Dialogs::ConfigurationDialog::OnScreenshot_Click( wxCommandEvent& evt )
}
// ----------------------------------------------------------------------------
Dialogs::BiosSelectorDialog::BiosSelectorDialog( wxWindow* parent, int id )
: wxDialogWithHelpers( parent, id, _("BIOS Selector"), false )
Dialogs::BiosSelectorDialog::BiosSelectorDialog( wxWindow* parent )
: wxDialogWithHelpers( parent, _("BIOS Selector"), false )
{
SetName( GetNameStatic() );
m_idealWidth = 500;
wxBoxSizer& bleh( *new wxBoxSizer( wxVERTICAL ) );

View File

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

View File

@ -20,7 +20,9 @@
#include "ModalPopups.h"
#include "Utilities/StringHelpers.h"
bool ConfButtons::Allows( wxWindowID id ) const
using namespace pxSizerFlags;
bool MsgButtons::Allows( wxWindowID id ) const
{
switch( id )
{
@ -63,7 +65,7 @@ static wxString ResultToString( int result )
return wxEmptyString;
}
static wxWindowID ParseThatResult( const wxString& src, const ConfButtons& validTypes )
static wxWindowID ParseThatResult( const wxString& src, const MsgButtons& validTypes )
{
if( !pxAssert( !src.IsEmpty() ) ) return wxID_ANY;
@ -85,7 +87,53 @@ static wxWindowID ParseThatResult( const wxString& src, const ConfButtons& valid
return wxID_ANY;
}
wxWindowID Dialogs::IssueConfirmation( ExtensibleConfirmation& confirmDlg, const wxString& disablerKey )
static bool pxTrySetFocus( wxWindow& parent, wxWindowID id )
{
if( wxWindow* found = parent.FindWindowById( id ) )
{
found->SetFocus();
return true;
}
return false;
}
static bool pxTrySetFocus( wxWindow* parent, wxWindowID id )
{
if( parent == NULL ) return false;
pxTrySetFocus( *parent, id );
}
void MsgButtons::SetBestFocus( wxWindow& dialog ) const
{
if( HasOK() && pxTrySetFocus( dialog, wxID_OK ) ) return;
if( HasNo() && pxTrySetFocus( dialog, wxID_NO ) ) return;
if( HasClose() && pxTrySetFocus( dialog, wxID_CLOSE ) ) return;
if( HasRetry() && pxTrySetFocus( dialog, wxID_RETRY ) ) return;
// Other confirmational types of buttons must be explicitly focused by the user or
// by an implementing dialog. We won't do it here implicitly because accidental
// "on focus" typed keys could invoke really unwanted actions.
// (typically close/ok/retry/etc. aren't so bad that accidental clicking does terrible things)
}
void MsgButtons::SetBestFocus( wxWindow* dialog ) const
{
if( dialog == NULL ) return;
SetBestFocus( *dialog );
}
wxWindowID pxIssueConfirmation( wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons )
{
confirmDlg += new ModalButtonPanel( &confirmDlg, buttons ) | pxCenter.Border( wxTOP, 8 );
buttons.SetBestFocus( confirmDlg );
return confirmDlg.ShowModal();
}
wxWindowID pxIssueConfirmation( wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons, const wxString& disablerKey )
{
wxConfigBase* cfg = GetAppConfig();
@ -112,135 +160,143 @@ wxWindowID Dialogs::IssueConfirmation( ExtensibleConfirmation& confirmDlg, const
result = split[0];
if( result == L"disabled" || result == L"off" || result == L"no" )
{
int result = ParseThatResult( split[1], confirmDlg.GetButtons() );
int result = ParseThatResult( split[1], buttons );
if( result != wxID_ANY ) return result;
}
}
}
if( cfg == NULL ) return confirmDlg.ShowModal();
// Add an option that allows the user to disable this popup from showing again.
// (and if the config hasn't been initialized yet, then assume the dialog as non-disablable)
pxCheckBox& DisablerCtrl( *new pxCheckBox(&confirmDlg, _("Do not show this dialog again.")) );
confirmDlg.GetExtensibleSizer().Add( &DisablerCtrl, wxSizerFlags().Centre() );
if( confirmDlg.GetButtons() != ConfButtons().OK() )
pxSetToolTip(&DisablerCtrl, _("Disables this popup and whatever response you select here will be automatically used from now on."));
else
pxSetToolTip(&DisablerCtrl, _("The popup will not be shown again. This setting can be undone from the settings panels."));
confirmDlg.Fit();
int modalResult = confirmDlg.ShowModal();
wxString cfgResult = ResultToString( modalResult );
if( DisablerCtrl.IsChecked() && !cfgResult.IsEmpty() )
pxCheckBox* DisablerCtrl = NULL;
if( cfg != NULL )
{
cfg->SetPath( L"/PopupDisablers" );
cfg->Write( disablerKey, L"disabled," + cfgResult );
cfg->SetPath( L"/" );
// Add an option that allows the user to disable this popup from showing again.
// (and if the config hasn't been initialized yet, then assume the dialog as non-disablable)
DisablerCtrl = new pxCheckBox(&confirmDlg, _("Do not show this dialog again."));
confirmDlg += 8;
confirmDlg += DisablerCtrl | wxSF.Centre();
if( buttons != MsgButtons().OK() )
pxSetToolTip(DisablerCtrl, _("Disables this popup and whatever response you select here will be automatically used from now on."));
else
pxSetToolTip(DisablerCtrl, _("The popup will not be shown again. This setting can be undone from the settings panels."));
}
int modalResult = pxIssueConfirmation( confirmDlg, buttons );
if( cfg != NULL )
{
wxString cfgResult = ResultToString( modalResult );
if( DisablerCtrl->IsChecked() && !cfgResult.IsEmpty() )
{
cfg->SetPath( L"/PopupDisablers" );
cfg->Write( disablerKey, L"disabled," + cfgResult );
cfg->SetPath( L"/" );
cfg->Flush();
}
}
return modalResult;
}
Dialogs::ExtensibleConfirmation::ExtensibleConfirmation( wxWindow* parent, const ConfButtons& type, const wxString& title, const wxString& msg )
: wxDialogWithHelpers( parent, wxID_ANY, title, false )
, m_ExtensibleSizer( *new wxBoxSizer( wxVERTICAL ) )
, m_ButtonSizer( *new wxBoxSizer( wxHORIZONTAL ) )
ModalButtonPanel::ModalButtonPanel( wxWindow* parent, const MsgButtons& buttons )
: wxPanelWithHelpers( parent, wxHORIZONTAL )
{
m_Buttons = type;
m_idealWidth = 500;
SetSizer( new wxBoxSizer(wxVERTICAL) );
// Populate the Button Sizer.
// We prefer this over the built-in wxWidgets ButtonSizer stuff used for other types of
// dialogs because we offer more button types, and we don't want the MSW default behavior
// of right-justified buttons.
if( type.HasCustom() )
AddCustomButton( pxID_CUSTOM, type.GetCustomLabel() );
if( buttons.HasCustom() )
AddCustomButton( pxID_CUSTOM, buttons.GetCustomLabel() );
// Order of wxID_RESET and custom button have been picked fairly arbitrarily, since there's
// no standard governing those.
#ifdef __WXGTK__
#ifdef __WXGTK__
// GTK+ / Linux inverts OK/CANCEL order -- cancel / no first, OK / Yes later. >_<
if( type.HasCancel() )
if( buttons.HasCancel() )
AddActionButton( wxID_CANCEL );
if( type.HasNo() )
if( buttons.HasNo() )
{
AddActionButton( wxID_NO );
if( type.AllowsToAll() ) AddActionButton( wxID_NOTOALL );
if( buttons.AllowsToAll() ) AddActionButton( wxID_NOTOALL );
}
if( type.HasOK() || type.HasYes() ) // Extra space between Affirm and Cancel Actions
m_ButtonSizer.Add(0, 0, 1, wxEXPAND, 0);
#endif
if( type.HasOK() )
if( buttons.HasIgnore() )
AddCustomButton( wxID_IGNORE, _("Ignore") );
if( buttons.HasOK() || buttons.HasYes() ) // Extra space between Affirm and Cancel Actions
GetSizer()->Add(0, 0, 1, wxEXPAND, 0);
#endif
if( buttons.HasOK() )
AddActionButton( wxID_OK );
if( type.HasYes() )
if( buttons.HasYes() )
{
AddActionButton( wxID_YES );
if( type.AllowsToAll() )
if( buttons.AllowsToAll() )
AddActionButton( wxID_YESTOALL );
}
if( type.HasReset() )
#ifdef __WXGTK__
if( buttons.HasRetry() )
AddActionButton( wxID_RETRY );
if( buttons.HasAbort() )
AddActionButton( wxID_ABORT );
#else
if( buttons.HasAbort() )
AddActionButton( wxID_ABORT );
if( buttons.HasRetry() )
AddActionButton( wxID_RETRY );
#endif
if( buttons.HasReset() )
AddCustomButton( wxID_RESET, _("Reset") );
if( type.HasClose() )
if( buttons.HasClose() )
AddActionButton( wxID_CLOSE );
#ifndef __WXGTK__
if( type.HasNo() || type.HasCancel() ) // Extra space between Affirm and Cancel Actions
m_ButtonSizer.Add(0, 0, 1, wxEXPAND, 0);
if( type.HasNo() )
#ifndef __WXGTK__
if( buttons.HasNo() )
{
AddActionButton( wxID_NO );
if( type.AllowsToAll() )
if( buttons.AllowsToAll() )
AddActionButton( wxID_NOTOALL );
}
if( type.HasCancel() )
if( buttons.HasIgnore() )
AddCustomButton( wxID_IGNORE, _("Ignore") );
if( buttons.HasCancel() )
AddActionButton( wxID_CANCEL );
#endif
// --------------------------------
// Finalize Sizers and Layout
// --------------------------------
// Add the message padded some (StdCenter gives us a 5 pt padding). Helps emphasize it a bit.
wxBoxSizer& msgPadSizer( *new wxBoxSizer(wxVERTICAL) );
msgPadSizer += Heading( msg );
*this += msgPadSizer | pxSizerFlags::StdCenter();
*this += m_ExtensibleSizer | pxCentre;
*this += m_ButtonSizer | pxSizerFlags::StdCenter();
Fit();
CenterOnScreen();
#endif
}
void Dialogs::ExtensibleConfirmation::OnActionButtonClicked( wxCommandEvent& evt )
void ModalButtonPanel::OnActionButtonClicked( wxCommandEvent& evt )
{
EndModal( evt.GetId() );
evt.Skip();
wxWindow* toplevel = wxGetTopLevelParent( this );
if( toplevel != NULL && wxIsKindOf(toplevel, wxDialog) )
((wxDialog*)toplevel)->EndModal( evt.GetId() );
}
void Dialogs::ExtensibleConfirmation::AddCustomButton( wxWindowID id, const wxString& label )
void ModalButtonPanel::AddCustomButton( wxWindowID id, const wxString& label )
{
m_ButtonSizer.Add( new wxButton( this, id, label ), pxSizerFlags::StdButton() )->SetProportion( 6 );
Connect( id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ExtensibleConfirmation::OnActionButtonClicked ) );
*this += new wxButton( this, id, label ) | StdButton().Proportion(6);
Connect( id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ModalButtonPanel::OnActionButtonClicked ) );
}
void Dialogs::ExtensibleConfirmation::AddActionButton( wxWindowID id )
// This is for buttons that are defined internally by wxWidgets, such as wxID_CANCEL, wxID_ABORT, etc.
// wxWidgets will assign the labels and stuff for us. :D
void ModalButtonPanel::AddActionButton( wxWindowID id )
{
m_ButtonSizer.Add( new wxButton( this, id ), pxSizerFlags::StdButton() )->SetProportion( 6 );
Connect( id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ExtensibleConfirmation::OnActionButtonClicked ) );
*this += new wxButton( this, id ) | StdButton().Proportion(6);
Connect( id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ModalButtonPanel::OnActionButtonClicked ) );
}

View File

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

View File

@ -18,8 +18,10 @@
#include "ModalPopups.h"
using namespace pxSizerFlags;
Dialogs::ImportSettingsDialog::ImportSettingsDialog( wxWindow* parent )
: wxDialogWithHelpers( parent, wxID_ANY, _("Import Existing Settings?"), false )
: wxDialogWithHelpers( parent, _("Import Existing Settings?"), wxVERTICAL )
{
m_idealWidth = 440;
@ -28,7 +30,6 @@ Dialogs::ImportSettingsDialog::ImportSettingsDialog( wxWindow* parent )
L"Would you like to import these settings or overwrite them with PCSX2 default values?"
L"\n\n(or press Cancel to select a different settings folder)" )
);
heading->SetMinSize( wxSize( m_idealWidth-8, wxDefaultCoord ) );
wxBoxSizer& s_buttons = *new wxBoxSizer( wxHORIZONTAL );
wxButton* b_import = new wxButton( this, wxID_ANY, _("Import") );
@ -37,19 +38,16 @@ Dialogs::ImportSettingsDialog::ImportSettingsDialog( wxWindow* parent )
// --------------------------------------------------------------------
// Layout Some Shizat...
wxBoxSizer& sizer( *new wxBoxSizer( wxVERTICAL ) );
s_buttons += b_import | StdButton();
s_buttons += 16;
s_buttons += b_over | StdButton();
s_buttons += 16;
s_buttons += new wxButton( this, wxID_CANCEL ) | StdButton();
s_buttons.Add( b_import,pxSizerFlags::StdButton() );
s_buttons.AddSpacer( 16 );
s_buttons.Add( b_over, pxSizerFlags::StdButton() );
s_buttons.AddSpacer( 16 );
s_buttons.Add( new wxButton( this, wxID_CANCEL ), pxSizerFlags::StdButton() );
sizer.AddSpacer( 4 );
heading->AddTo( sizer );
sizer.AddSpacer( 12 );
sizer.Add( &s_buttons, pxSizerFlags::StdCenter() );
SetSizerAndFit( &sizer );
*this += 4;
*this += heading;
*this += 12;
*this += &s_buttons | StdCenter();
Connect( b_import->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ImportSettingsDialog::OnImport_Click) );
Connect( b_over->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ImportSettingsDialog::OnOverwrite_Click) );

View File

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

View File

@ -28,8 +28,11 @@ namespace Dialogs {
class LogOptionsDialog: public wxDialogWithHelpers
{
public:
LogOptionsDialog( wxWindow* parent=NULL, int id=DialogId_LogOptions );
LogOptionsDialog( wxWindow* parent=NULL );
virtual ~LogOptionsDialog() throw() { }
static const wxChar* GetNameStatic() { return L"Dialog:TraceLogSettings"; }
protected:
void OnOk_Click( wxCommandEvent& evt );
void OnApply_Click( wxCommandEvent& evt );

View File

@ -66,7 +66,7 @@ protected:
virtual void OnDoubleClicked( wxCommandEvent& evt );
};
class ConfButtons
class MsgButtons
{
protected:
BITFIELD32()
@ -87,29 +87,29 @@ protected:
wxString m_CustomLabel;
public:
ConfButtons() : bitset( 0 ) { }
MsgButtons() : bitset( 0 ) { }
ConfButtons& OK() { m_OK = true; return *this; }
ConfButtons& Cancel() { m_Cancel = true; return *this; }
ConfButtons& Apply() { m_Apply = true; return *this; }
ConfButtons& Yes() { m_Yes = true; return *this; }
ConfButtons& No() { m_No = true; return *this; }
ConfButtons& ToAll() { m_AllowToAll = true; return *this; }
MsgButtons& OK() { m_OK = true; return *this; }
MsgButtons& Cancel() { m_Cancel = true; return *this; }
MsgButtons& Apply() { m_Apply = true; return *this; }
MsgButtons& Yes() { m_Yes = true; return *this; }
MsgButtons& No() { m_No = true; return *this; }
MsgButtons& ToAll() { m_AllowToAll = true; return *this; }
ConfButtons& Abort() { m_Abort = true; return *this; }
ConfButtons& Retry() { m_Retry = true; return *this; }
ConfButtons& Ignore() { m_Ignore = true; return *this; }
ConfButtons& Reset() { m_Reset = true; return *this; }
ConfButtons& Close() { m_Close = true; return *this; }
MsgButtons& Abort() { m_Abort = true; return *this; }
MsgButtons& Retry() { m_Retry = true; return *this; }
MsgButtons& Ignore() { m_Ignore = true; return *this; }
MsgButtons& Reset() { m_Reset = true; return *this; }
MsgButtons& Close() { m_Close = true; return *this; }
ConfButtons& Custom( const wxString& label)
MsgButtons& Custom( const wxString& label)
{
m_CustomLabel = label;
return *this;
}
ConfButtons& OKCancel() { m_OK = m_Cancel = true; return *this; }
ConfButtons& YesNo() { m_Yes = m_No = true; return *this; }
MsgButtons& OKCancel() { m_OK = m_Cancel = true; return *this; }
MsgButtons& YesNo() { m_Yes = m_No = true; return *this; }
bool HasOK() const { return m_OK; }
bool HasCancel() const { return m_Cancel; }
@ -128,29 +128,45 @@ public:
const wxString& GetCustomLabel() const { return m_CustomLabel; }
bool Allows( wxWindowID id ) const;
void SetBestFocus( wxWindow* dialog ) const;
void SetBestFocus( wxWindow& dialog ) const;
bool operator ==( const ConfButtons& right ) const
bool operator ==( const MsgButtons& right ) const
{
return OpEqu( bitset );
}
bool operator !=( const ConfButtons& right ) const
bool operator !=( const MsgButtons& right ) const
{
return !OpEqu( bitset );
}
};
class ModalButtonPanel : public wxPanelWithHelpers
{
public:
ModalButtonPanel( wxWindow* window, const MsgButtons& buttons );
virtual ~ModalButtonPanel() throw() { }
virtual void AddActionButton( wxWindowID id );
virtual void AddCustomButton( wxWindowID id, const wxString& label );
virtual void OnActionButtonClicked( wxCommandEvent& evt );
};
namespace Dialogs
{
class AboutBoxDialog: public wxDialogWithHelpers
{
public:
AboutBoxDialog( wxWindow* parent=NULL, int id=DialogId_About );
virtual ~AboutBoxDialog() throw() {}
protected:
//wxStaticBitmap m_bitmap_logo;
wxStaticBitmap m_bitmap_dualshock;
public:
AboutBoxDialog( wxWindow* parent=NULL );
virtual ~AboutBoxDialog() throw() {}
static const wxChar* GetNameStatic() { return L"Dialog:AboutBox"; }
};
@ -161,7 +177,7 @@ namespace Dialogs
Panels::LanguageSelectionPanel* m_panel_langsel;
public:
PickUserModeDialog( wxWindow* parent, int id=wxID_ANY );
PickUserModeDialog( wxWindow* parent );
virtual ~PickUserModeDialog() throw() {}
protected:
@ -180,42 +196,15 @@ namespace Dialogs
void OnOverwrite_Click( wxCommandEvent& evt );
};
// --------------------------------------------------------------------------------------
// ExtensibleConfirmation Dialog
// --------------------------------------------------------------------------------------
// Purpose: This dialog is a simple framework for providing common popups that have three
// main sections of content:
//
// [Upper Paragraph]
// [Optional Interior Content]
// [OK] [CANCEL] [ETC]
//
// The Upper Paragraph and buttons are specified in the constructor. The Interior Content
// can be added by fetching the ExtensibleSizer provided by th created dialog. Add your
// content, dance around a bit, call Fit(), and then ShowModal. :)
class ExtensibleConfirmation : public wxDialogWithHelpers
class AssertionDialog : public wxDialogWithHelpers
{
protected:
wxBoxSizer& m_ExtensibleSizer;
wxBoxSizer& m_ButtonSizer;
ConfButtons m_Buttons;
public:
ExtensibleConfirmation( wxWindow* parent, const ConfButtons& type, const wxString& title, const wxString& msg );
virtual ~ExtensibleConfirmation() throw() {}
const ConfButtons& GetButtons() const { return m_Buttons; }
virtual wxBoxSizer& GetExtensibleSizer() const { return m_ExtensibleSizer; }
protected:
virtual void AddActionButton( wxWindowID id );
virtual void AddCustomButton( wxWindowID id, const wxString& label );
virtual void OnActionButtonClicked( wxCommandEvent& evt );
AssertionDialog( const wxString& text, const wxString& stacktrace );
virtual ~AssertionDialog() throw() {}
};
wxWindowID IssueConfirmation( ExtensibleConfirmation& confirmDlg, const wxString& disablerKey );
}
wxWindowID pxIssueConfirmation( wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons );
wxWindowID pxIssueConfirmation( wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons, const wxString& disablerKey );

View File

@ -20,21 +20,17 @@
using namespace Panels;
Dialogs::PickUserModeDialog::PickUserModeDialog( wxWindow* parent, int id )
: wxDialogWithHelpers( parent, id, _("PCSX2 First Time configuration"), false )
Dialogs::PickUserModeDialog::PickUserModeDialog( wxWindow* parent )
: wxDialogWithHelpers( parent, _("PCSX2 First Time configuration"), wxVERTICAL )
{
m_panel_usersel = new UsermodeSelectionPanel( this, false );
m_panel_langsel = new LanguageSelectionPanel( this );
SetSizer( new wxBoxSizer( wxVERTICAL ) );
*this += new pxStaticHeading( this, _("PCSX2 is starting from a new or unknown folder and needs to be configured.") );
*this += m_panel_langsel | pxSizerFlags::StdCenter();
*this += m_panel_usersel | wxSizerFlags().Expand().Border( wxALL, 8 );
AddOkCancel( *GetSizer() );
Fit();
CenterOnScreen();
Connect( wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PickUserModeDialog::OnOk_Click ) );
// TODO : Add a command event handler for language changes, that dynamically re-update contents of this window.

View File

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

View File

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

View File

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

View File

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

View File

@ -28,6 +28,15 @@
using namespace Dialogs;
template<typename DialogType>
void AppOpenDialog( wxWindow* parent )
{
if( wxWindow* window = wxFindWindowByName( DialogType::GetNameStatic() ) )
window->SetFocus();
else
(new DialogType( parent ))->Show();
}
extern wxString GetMsg_ConfirmSysReset();
void MainEmuFrame::SaveEmuOptions()
@ -41,12 +50,12 @@ void MainEmuFrame::SaveEmuOptions()
void MainEmuFrame::Menu_ConfigSettings_Click(wxCommandEvent &event)
{
Dialogs::ConfigurationDialog( this ).ShowModal();
AppOpenDialog<ConfigurationDialog>( this );
}
void MainEmuFrame::Menu_SelectBios_Click(wxCommandEvent &event)
{
Dialogs::BiosSelectorDialog( this ).ShowModal();
AppOpenDialog<BiosSelectorDialog>( this );
}
void MainEmuFrame::Menu_CdvdSource_Click( wxCommandEvent &event )
@ -118,11 +127,14 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
// User has an iso selected from a previous run, but it doesn't exist anymore.
// Issue a courtesy popup and then an Iso Selector to choose a new one.
Dialogs::ExtensibleConfirmation( this, ConfButtons().OK(), _("ISO file not found!"),
wxDialogWithHelpers dialog( this, _("ISO file not found!"), wxVERTICAL );
dialog += dialog.Heading(
_("An error occurred while trying to open the file:\n\n") + g_Conf->CurrentIso + L"\n\n" +
_("Error: The configured ISO file does not exist. Click OK to select a new ISO source for CDVD.")
).ShowModal();
);
pxIssueConfirmation( dialog, MsgButtons().OK() );
selector = true;
}
@ -141,11 +153,9 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
if( SysHasValidState() )
{
ExtensibleConfirmation dialog( this, ConfButtons().Yes().Cancel(),
_("Confirm PS2 Reset"), GetMsg_ConfirmSysReset()
);
bool confirmed = (IssueConfirmation( dialog, L"BootCdvd:ConfirmReset" ) != wxID_CANCEL);
wxDialogWithHelpers dialog( this, _("Confirm PS2 Reset"), wxVERTICAL );
dialog += dialog.Heading( GetMsg_ConfirmSysReset() );
bool confirmed = (pxIssueConfirmation( dialog, MsgButtons().Yes().Cancel(), L"BootCdvd:ConfirmReset" ) != wxID_CANCEL);
if( !confirmed )
{
@ -290,7 +300,7 @@ void MainEmuFrame::Menu_Debug_MemoryDump_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_Debug_Logging_Click(wxCommandEvent &event)
{
LogOptionsDialog( this, wxID_ANY ).ShowModal();
AppOpenDialog<LogOptionsDialog>( this );
}
void MainEmuFrame::Menu_ShowConsole(wxCommandEvent &event)
@ -316,5 +326,5 @@ void MainEmuFrame::Menu_PrintCDVD_Info(wxCommandEvent &event)
void MainEmuFrame::Menu_ShowAboutBox(wxCommandEvent &event)
{
AboutBoxDialog( this, wxID_ANY ).ShowModal();
AppOpenDialog<AboutBoxDialog>( this );
}

View File

@ -15,130 +15,277 @@
#include "PrecompiledHeader.h"
#include "App.h"
#include "Dialogs/ModalPopups.h"
DEFINE_EVENT_TYPE( pxEVT_MSGBOX );
DEFINE_EVENT_TYPE( pxEVT_CallStackBox );
DEFINE_EVENT_TYPE( pxEVT_ASSERTION );
using namespace Threading;
using namespace pxSizerFlags;
// Thread Safety: Must be called from the GUI thread ONLY.
static int pxMessageDialog( const wxString& content, const wxString& caption, long flags )
// Thread Safety: Must be called from the GUI thread ONLY. Will assert otherwise.
//
// [TODO] Add support for icons?
//
static int pxMessageDialog( const wxString& caption, const wxString& content, const MsgButtons& buttons )
{
if( IsDevBuild && !wxThread::IsMain() )
throw Exception::InvalidOperation( "Function must be called by the main GUI thread only." );
if( !AffinityAssert_AllowFromMain() ) return wxID_CANCEL;
// fixme: If the emulator is currently active and is running in fullscreen mode, then we
// need to either:
// fixme: If the emulator is currently active and is running in exclusive mode (forced
// fullscreen), then we need to either:
// 1) Exit fullscreen mode before issuing the popup.
// 2) Issue the popup with wxSTAY_ON_TOP specified so that the user will see it.
//
// And in either case the emulation should be paused/suspended for the user.
return wxMessageDialog( NULL, content, caption, flags ).ShowModal();
wxDialogWithHelpers dialog( NULL, caption, wxVERTICAL );
dialog += dialog.Heading( content );
return pxIssueConfirmation( dialog, buttons );
}
// Thread Safety: Must be called from the GUI thread ONLY.
// fixme: this function should use a custom dialog box that has a wxTextCtrl for the callstack, and
// uses fixed-width (modern) fonts.
static int pxCallstackDialog( const wxString& content, const wxString& caption, long flags )
class BaseMessageBoxEvent : public wxEvent
{
if( IsDevBuild && !wxThread::IsMain() )
throw Exception::InvalidOperation( "Function must be called by the main GUI thread only." );
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(BaseMessageBoxEvent)
return wxMessageDialog( NULL, content, caption, flags ).ShowModal();
}
protected:
MsgboxEventResult* m_Instdata;
wxString m_Content;
public:
explicit BaseMessageBoxEvent( int msgtype=pxEVT_MSGBOX, const wxString& content=wxEmptyString )
: wxEvent( 0, msgtype )
, m_Content( content )
{
m_Instdata = NULL;
}
virtual ~BaseMessageBoxEvent() throw() { }
virtual BaseMessageBoxEvent *Clone() const { return new BaseMessageBoxEvent(*this); }
BaseMessageBoxEvent( MsgboxEventResult& instdata, const wxString& content )
: wxEvent( 0, pxEVT_MSGBOX )
, m_Instdata( &instdata )
, m_Content( content )
{
}
BaseMessageBoxEvent( const wxString& content )
: wxEvent( 0, pxEVT_MSGBOX )
, m_Instdata( NULL )
, m_Content( content )
{
}
BaseMessageBoxEvent( const BaseMessageBoxEvent& event )
: wxEvent( event )
, m_Instdata( event.m_Instdata )
, m_Content( event.m_Content )
{
}
BaseMessageBoxEvent& SetInstData( MsgboxEventResult& instdata )
{
m_Instdata = &instdata;
return *this;
}
// Thread Safety: Must be called from the GUI thread ONLY.
virtual void IssueDialog()
{
AffinityAssert_AllowFromMain();
int result = _DoDialog();
if( m_Instdata != NULL )
{
m_Instdata->result = result;
m_Instdata->WaitForMe.Post();
}
}
protected:
virtual int _DoDialog() const
{
pxFailDev( "Abstract Base MessageBox Event." );
return wxID_CANCEL;
}
};
// --------------------------------------------------------------------------------------
// pxMessageBoxEvent
// --------------------------------------------------------------------------------------
class pxMessageBoxEvent : public wxEvent
// This event type is used to transfer message boxes to the main UI thread, and return the
// result of the box. It's the only way a message box can be issued from non-main threads
// with complete safety in wx2.8.
//
// For simplicity sake this message box only supports two basic designs. The main design
// is a generic message box with confirmation buttons of your choosing. Additionally you
// can specify a "scrollableContent" text string, which is added into a read-only richtext
// control similar to the console logs and such.
//
// Future consideration: If wxWidgets 3.0 has improved thread safety, then it should probably
// be reasonable for it to work with a more flexable model where the dialog can be created
// on a child thread, passed to the main thread, where ShowModal() is run (keeping the nested
// message pumps on the main thread where they belong). But so far this is not possible,
// because of various subtle issues in wx2.8 design.
//
class pxMessageBoxEvent : public BaseMessageBoxEvent
{
protected:
MsgboxEventResult& m_Instdata;
wxString m_Title;
wxString m_Content;
long m_Flags;
typedef BaseMessageBoxEvent _parent;
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxMessageBoxEvent)
protected:
wxString m_Title;
MsgButtons m_Buttons;
public:
pxMessageBoxEvent()
: wxEvent( 0, pxEVT_MSGBOX )
, m_Instdata( *(MsgboxEventResult*)NULL )
, m_Title()
, m_Content()
pxMessageBoxEvent( int msgtype=pxEVT_MSGBOX )
: BaseMessageBoxEvent( msgtype )
{
m_Flags = 0;
}
pxMessageBoxEvent( MsgboxEventResult& instdata, const wxString& title, const wxString& content, long flags )
: wxEvent( 0, pxEVT_MSGBOX )
, m_Instdata( instdata )
virtual ~pxMessageBoxEvent() throw() { }
virtual pxMessageBoxEvent *Clone() const { return new pxMessageBoxEvent(*this); }
pxMessageBoxEvent( MsgboxEventResult& instdata, const wxString& title, const wxString& content, const MsgButtons& buttons )
: BaseMessageBoxEvent( instdata, content )
, m_Title( title )
, m_Content( content )
, m_Buttons( buttons )
{
}
pxMessageBoxEvent( const wxString& title, const wxString& content, const MsgButtons& buttons )
: BaseMessageBoxEvent( content )
, m_Title( title )
, m_Buttons( buttons )
{
m_Flags = flags;
}
pxMessageBoxEvent( const pxMessageBoxEvent& event )
: wxEvent( event )
, m_Instdata( event.m_Instdata )
: BaseMessageBoxEvent( event )
, m_Title( event.m_Title )
, m_Content( event.m_Content )
, m_Buttons( event.m_Buttons )
{
m_Flags = event.m_Flags;
}
// Thread Safety: Must be called from the GUI thread ONLY.
void DoTheDialog()
pxMessageBoxEvent& SetInstData( MsgboxEventResult& instdata )
{
int result;
if( m_id == pxEVT_MSGBOX )
result = pxMessageDialog( m_Content, m_Title, m_Flags );
else
result = pxCallstackDialog( m_Content, m_Title, m_Flags );
m_Instdata.result = result;
m_Instdata.WaitForMe.Post();
_parent::SetInstData( instdata );
return *this;
}
virtual wxEvent *Clone() const { return new pxMessageBoxEvent(*this); }
private:
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxMessageBoxEvent)
protected:
virtual int _DoDialog() const
{
return pxMessageDialog( m_Content, m_Title, m_Buttons );
}
};
IMPLEMENT_DYNAMIC_CLASS( pxMessageBoxEvent, wxEvent )
// --------------------------------------------------------------------------------------
// pxAssertionEvent
// --------------------------------------------------------------------------------------
class pxAssertionEvent : public BaseMessageBoxEvent
{
typedef BaseMessageBoxEvent _parent;
DECLARE_DYNAMIC_CLASS_NO_ASSIGN( pxAssertionEvent )
protected:
wxString m_Stacktrace;
public:
pxAssertionEvent()
: BaseMessageBoxEvent( pxEVT_ASSERTION )
{
}
virtual ~pxAssertionEvent() throw() { }
virtual pxAssertionEvent *Clone() const { return new pxAssertionEvent(*this); }
pxAssertionEvent( MsgboxEventResult& instdata, const wxString& content, const wxString& trace )
: BaseMessageBoxEvent( pxEVT_ASSERTION )
, m_Stacktrace( trace )
{
}
pxAssertionEvent( const wxString& content, const wxString& trace )
: BaseMessageBoxEvent( pxEVT_ASSERTION, content )
, m_Stacktrace( trace )
{
}
pxAssertionEvent( const pxAssertionEvent& event )
: BaseMessageBoxEvent( event )
, m_Stacktrace( event.m_Stacktrace )
{
}
pxAssertionEvent& SetInstData( MsgboxEventResult& instdata )
{
_parent::SetInstData( instdata );
return *this;
}
pxAssertionEvent& SetStacktrace( const wxString& trace )
{
m_Stacktrace = trace;
return *this;
}
protected:
virtual int _DoDialog() const
{
return Dialogs::AssertionDialog( m_Content, m_Stacktrace ).ShowModal();
}
};
IMPLEMENT_DYNAMIC_CLASS( BaseMessageBoxEvent, wxEvent )
IMPLEMENT_DYNAMIC_CLASS( pxMessageBoxEvent, BaseMessageBoxEvent )
IMPLEMENT_DYNAMIC_CLASS( pxAssertionEvent, BaseMessageBoxEvent )
namespace Msgbox
{
// parameters:
// flags - messagebox type flags, such as wxOK, wxCANCEL, etc.
//
static int ThreadedMessageBox( const wxString& content, const wxString& title, long flags, int boxType=pxEVT_MSGBOX )
static int ThreadedMessageBox( BaseMessageBoxEvent& evt )
{
MsgboxEventResult instdat;
evt.SetInstData( instdat );
if( wxThread::IsMain() )
{
// main thread can handle the message immediately.
wxGetApp().ProcessEvent( evt );
}
else
{
// Not on main thread, must post the message there for handling instead:
wxGetApp().AddPendingEvent( evt );
instdat.WaitForMe.WaitNoCancel(); // Important! disable cancellation since we're using local stack vars.
}
return instdat.result;
}
static int ThreadedMessageBox( const wxString& title, const wxString& content, const MsgButtons& buttons )
{
// must pass the message to the main gui thread, and then stall this thread, to avoid
// threaded chaos where our thread keeps running while the popup is awaiting input.
MsgboxEventResult instdat;
pxMessageBoxEvent tevt( instdat, title, content, flags );
pxMessageBoxEvent tevt( instdat, title, content, buttons );
wxGetApp().AddPendingEvent( tevt );
instdat.WaitForMe.WaitNoCancel(); // Important! disable cancellation since we're using local stack vars.
return instdat.result;
}
void OnEvent( pxMessageBoxEvent& evt )
{
evt.DoTheDialog();
}
// Pops up an alert Dialog Box with a singular "OK" button.
// Always returns false.
bool Alert( const wxString& text, const wxString& caption, int icon )
{
icon |= wxOK;
MsgButtons buttons( MsgButtons().OK() );
if( wxThread::IsMain() )
pxMessageDialog( text, caption, icon );
pxMessageDialog( caption, text, buttons );
else
ThreadedMessageBox( text, caption, icon );
ThreadedMessageBox( caption, text, buttons );
return false;
}
@ -146,60 +293,40 @@ namespace Msgbox
// true if OK, false if cancel.
bool OkCancel( const wxString& text, const wxString& caption, int icon )
{
icon |= wxOK | wxCANCEL;
MsgButtons buttons( MsgButtons().OKCancel() );
if( wxThread::IsMain() )
{
return wxID_OK == pxMessageDialog( text, caption, icon );
return wxID_OK == pxMessageDialog( caption, text, buttons );
}
else
{
return wxID_OK == ThreadedMessageBox( text, caption, icon );
return wxID_OK == ThreadedMessageBox( caption, text, buttons );
}
}
bool YesNo( const wxString& text, const wxString& caption, int icon )
{
icon |= wxYES_NO;
if( wxThread::IsMain() )
{
return wxID_YES == pxMessageDialog( text, caption, icon );
}
else
{
return wxID_YES == ThreadedMessageBox( text, caption, icon );
}
}
// [TODO] : This should probably be a fancier looking dialog box with the stacktrace
// displayed inside a wxTextCtrl.
static int CallStack( const wxString& errormsg, const wxString& stacktrace, const wxString& prompt, const wxString& caption, int buttons )
{
buttons |= wxICON_STOP;
wxString text( errormsg + L"\n\n" + stacktrace + L"\n" + prompt );
MsgButtons buttons( MsgButtons().YesNo() );
if( wxThread::IsMain() )
{
return pxCallstackDialog( text, caption, buttons );
return wxID_YES == pxMessageDialog( caption, text, buttons );
}
else
{
return ThreadedMessageBox( text, caption, buttons, pxEVT_CallStackBox );
return wxID_YES == ThreadedMessageBox( caption, text, buttons );
}
}
int Assertion( const wxString& text, const wxString& stacktrace )
{
return CallStack( text, stacktrace,
L"\nDo you want to stop the program?"
L"\nOr press [Cancel] to suppress further assertions.",
L"PCSX2 Assertion Failure",
wxYES_NO | wxCANCEL
);
}
void Except( const Exception::BaseException& src )
{
CallStack( src.FormatDisplayMessage(), src.FormatDiagnosticMessage(), wxEmptyString, L"PCSX2 Unhandled Exception", wxOK );
pxAssertionEvent tevt( text, stacktrace );
return ThreadedMessageBox( tevt );
}
}
void Pcsx2App::OnMessageBox( pxMessageBoxEvent& evt )
{
evt.IssueDialog();
}

View File

@ -46,8 +46,8 @@ Panels::BaseAdvancedCpuOptions::BaseAdvancedCpuOptions( wxWindow* parent )
// Highlight Default Options:
m_RoundModePanel->SetDefault( 3 );
m_ClampModePanel->SetDefault( 1 );
m_RoundModePanel->SetDefaultItem( 3 );
m_ClampModePanel->SetDefaultItem( 1 );
// ---------------------------------
// The Fitting And Sizing Area
@ -146,8 +146,8 @@ Panels::CpuPanelEE::CpuPanelEE( wxWindow* parent )
};
m_panel_RecEE = &(new pxRadioPanel( this, tbl_CpuTypes_EE ))->SetDefault( 1 );
m_panel_RecIOP = &(new pxRadioPanel( this, tbl_CpuTypes_IOP ))->SetDefault( 1 );
m_panel_RecEE = &(new pxRadioPanel( this, tbl_CpuTypes_EE ))->SetDefaultItem( 1 );
m_panel_RecIOP = &(new pxRadioPanel( this, tbl_CpuTypes_IOP ))->SetDefaultItem( 1 );
m_panel_RecEE->Realize();
m_panel_RecIOP->Realize();
@ -193,8 +193,8 @@ Panels::CpuPanelVU::CpuPanelVU( wxWindow* parent )
.SetToolTip(_("Useful for diagnosing bugs or clamping issues in the new mVU recompiler."))
};
m_panel_VU0 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefault( 1 );
m_panel_VU1 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefault( 1 );
m_panel_VU0 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefaultItem( 1 );
m_panel_VU1 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefaultItem( 1 );
m_panel_VU0->Realize();
m_panel_VU1->Realize();
@ -233,6 +233,9 @@ void Panels::CpuPanelEE::Apply()
void Panels::CpuPanelEE::OnSettingsChanged()
{
m_panel_RecEE->Enable( x86caps.hasStreamingSIMD2Extensions );
m_panel_RecIOP->Enable( x86caps.hasStreamingSIMD2Extensions );
const Pcsx2Config::RecompilerOptions& recOps( g_Conf->EmuOptions.Cpu.Recompiler );
m_panel_RecEE->SetSelection( (int)recOps.EnableEE );
m_panel_RecIOP->SetSelection( (int)recOps.EnableIOP );
@ -250,6 +253,12 @@ void Panels::CpuPanelVU::Apply()
void Panels::CpuPanelVU::OnSettingsChanged()
{
m_panel_VU0->EnableItem( 1, x86caps.hasStreamingSIMD2Extensions );
m_panel_VU0->EnableItem( 2, x86caps.hasStreamingSIMD2Extensions );
m_panel_VU1->EnableItem( 1, x86caps.hasStreamingSIMD2Extensions );
m_panel_VU1->EnableItem( 2, x86caps.hasStreamingSIMD2Extensions );
Pcsx2Config::RecompilerOptions& recOps( g_Conf->EmuOptions.Cpu.Recompiler );
if( recOps.UseMicroVU0 )
m_panel_VU0->SetSelection( recOps.EnableVU0 ? 1 : 0 );

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h"
#include "App.h"
#include "Plugins.h"
#include "SaveState.h"
#include "Utilities/ScopedPtr.h"
#include "ConfigurationPanels.h"
#include "Dialogs/ModalPopups.h"
@ -309,37 +310,43 @@ void Panels::PluginSelectorPanel::Apply()
break;
} while( ++pi, pi->shortname != NULL );
bool isSuspended = false;
if( pi->shortname != NULL )
{
if( CoreThread.IsRunning() )
{
// [TODO] : Post notice that this shuts down existing emulation, and may not safely recover.
Dialogs::ExtensibleConfirmation dialog( this, ConfButtons().OK().Cancel(),
_("Shutdown PS2 virtual machine?"),
pxE( ".Popup:PluginSelector:ConfirmShutdown",
L"Warning! Changing plugins requires a complete shutdown and reset of the PS2 virtual machine. "
L"PCSX2 will attempt to save and restore the state, but if the newly selected plugins are "
L"incompatible the recovery may fail, and current progress will be lost."
L"\n\n"
L"Are you sure you want to apply settings now?"
)
);
wxDialogWithHelpers dialog( this, _("Shutdown PS2 virtual machine?"), wxVERTICAL );
int result = Dialogs::IssueConfirmation( dialog, L"PluginSelector:ConfirmShutdown" );
dialog += dialog.Heading( pxE( ".Popup:PluginSelector:ConfirmShutdown",
L"Warning! Changing plugins requires a complete shutdown and reset of the PS2 virtual machine. "
L"PCSX2 will attempt to save and restore the state, but if the newly selected plugins are "
L"incompatible the recovery may fail, and current progress will be lost."
L"\n\n"
L"Are you sure you want to apply settings now?"
) );
int result = pxIssueConfirmation( dialog, MsgButtons().OK().Cancel(), L"PluginSelector:ConfirmShutdown" );
if( result == wxID_CANCEL )
throw Exception::CannotApplySettings( this, "Cannot apply settings: canceled by user because plugins changed while the emulation state was active.", false );
// FIXME : We only actually have to save plugins here, except the recovery code
// in SysCoreThread isn't quite set up yet to handle that (I think...) --air
isSuspended = CoreThread.Suspend();
StateCopy_FreezeToMem_Blocking();
}
sApp.SysReset();
// Don't use SysShutdown, it clears the StateCopy.
CoreThread.Cancel();
wxGetApp().m_CorePlugins = NULL;
}
if( !wxGetApp().m_CorePlugins )
{
try
{
try {
LoadPluginsImmediate();
}
catch( Exception::PluginError& ex )
@ -359,6 +366,8 @@ void Panels::PluginSelectorPanel::Apply()
);
}
}
if( isSuspended ) CoreThread.Resume();
}
void Panels::PluginSelectorPanel::CancelRefresh()
@ -591,6 +600,7 @@ void Panels::PluginSelectorPanel::EnumThread::ExecuteTaskInThread()
{
DevCon.WriteLn( "Plugin Enumeration Thread started..." );
Sleep( 15 ); // give the window some time to paint.
YieldToMain();
for( int curidx=0; curidx < m_master.FileCount(); ++curidx )

View File

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

View File

@ -141,7 +141,7 @@ const wxChar* __fastcall pxGetTranslation( const wxChar* message )
if( wxStrlen( message ) > 96 )
{
Console.Warning( "pxGetTranslation: Long message detected, maybe use pxE() instead?" );
Console.Indent().WriteLn( Color_Green, L"Message: %s", message );
Console.WriteLn( Color_Green, L"Message: %s", message );
}
}
return wxGetTranslation( message );

View File

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

View File

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

View File

@ -25,7 +25,8 @@ void StreamException_ThrowFromErrno( const wxString& streamname, errno_t errcode
switch( errcode )
{
case EINVAL:
throw Exception::InvalidArgument( "Invalid argument" );
pxFailDev( L"Invalid argument" );
throw Exception::Stream( streamname, "Invalid argument" );
case EACCES: // Access denied!
throw Exception::AccessDenied( streamname );
@ -75,7 +76,7 @@ void StreamException_ThrowLastError( const wxString& streamname, HANDLE result )
throw Exception::AccessDenied( streamname );
case ERROR_INVALID_HANDLE:
throw Exception::InvalidOperation( "Stream object or handle is invalid" );
throw Exception::BadStream( streamname, "Stream object or handle is invalid" );
case ERROR_SHARING_VIOLATION:
throw Exception::AccessDenied( streamname, "Sharing violation" );

View File

@ -99,7 +99,7 @@ public:
protected:
void ExecuteTaskInThread()
{
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL );
::SetThreadPriority( ::GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL );
if( m_outpipe == INVALID_HANDLE_VALUE ) return;
try
@ -112,7 +112,7 @@ protected:
if( !ReadFile(m_outpipe, s8_Buf, sizeof(s8_Buf)-1, &u32_Read, NULL) )
{
DWORD result = GetLastError();
if( result == ERROR_HANDLE_EOF ) break;
if( result == ERROR_HANDLE_EOF || result == ERROR_BROKEN_PIPE ) break;
if( result == ERROR_IO_PENDING )
{
Yield( 10 );
@ -172,6 +172,10 @@ class WinPipeRedirection : public PipeRedirectionBase
DeclareNoncopyableObject( WinPipeRedirection );
protected:
DWORD m_stdhandle;
FILE* m_stdfp;
FILE m_stdfp_copy;
HANDLE m_readpipe;
HANDLE m_writepipe;
int m_crtFile;
@ -187,21 +191,25 @@ public:
};
WinPipeRedirection::WinPipeRedirection( FILE* stdstream )
: m_readpipe(INVALID_HANDLE_VALUE)
, m_writepipe(INVALID_HANDLE_VALUE)
, m_crtFile(-1)
, m_fp(NULL)
, m_Thread( m_readpipe, (stdstream == stderr) ? Color_Red : Color_Black )
: m_Thread( m_readpipe, (stdstream == stderr) ? Color_Red : Color_Black )
{
m_stdhandle = ( stdstream == stderr ) ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE;
m_stdfp = stdstream;
m_stdfp_copy = *stdstream;
m_readpipe = INVALID_HANDLE_VALUE;
m_writepipe = INVALID_HANDLE_VALUE;
m_crtFile = -1;
m_fp = NULL;
pxAssume( (stdstream == stderr) || (stdstream == stdout) );
try
{
pxAssert( (stdstream == stderr) || (stdstream == stdout) );
DWORD stdhandle = ( stdstream == stderr ) ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE;
if( 0 == CreatePipe( &m_readpipe, &m_writepipe, NULL, 0 ) )
throw Exception::Win32Error( "CreatePipe failed." );
if( 0 == SetStdHandle( stdhandle, m_writepipe ) )
if( 0 == SetStdHandle( m_stdhandle, m_writepipe ) )
throw Exception::Win32Error( "SetStdHandle failed." );
// Note: Don't use GetStdHandle to "confirm" the handle.
@ -222,11 +230,19 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream )
if( m_fp == NULL )
throw Exception::RuntimeError( "_fdopen returned NULL." );
*stdstream = *m_fp;
*m_stdfp = *m_fp; // omg hack. but it works >_<
setvbuf( stdstream, NULL, _IONBF, 0 );
m_Thread.Start();
}
catch( Exception::BaseThreadError& ex )
{
// thread object will become invalid because of scoping after we leave
// the constructor, so re-pack a new exception:
Cleanup();
throw Exception::RuntimeError( ex.FormatDiagnosticMessage(), ex.FormatDisplayMessage() );
}
catch( Exception::BaseException& ex )
{
Cleanup();
@ -235,6 +251,9 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream )
}
catch( ... )
{
// C++ doesn't execute the object destructor automatically, because it's fail++
// (and I'm *not* encapsulating each handle into its own object >_<)
Cleanup();
throw;
}
@ -247,6 +266,12 @@ WinPipeRedirection::~WinPipeRedirection()
void WinPipeRedirection::Cleanup() throw()
{
// restore the old handle we so graciously hacked earlier ;)
// (or don't and suffer CRT crashes! ahaha!)
if( m_stdfp != NULL )
*m_stdfp = m_stdfp_copy;
// Cleanup Order Notes:
// * The redirection thread is most likely blocking on ReadFile(), so we can't Cancel yet, lest we deadlock --
// Closing the writepipe (either directly or through the fp/crt handles) issues an EOF to the thread,

View File

@ -495,31 +495,44 @@ static const uint m_recBlockAllocSize =
(((Ps2MemSize::Base + Ps2MemSize::Rom + Ps2MemSize::Rom1) / 4) * sizeof(BASEBLOCK))
+ RECCONSTBUF_SIZE * sizeof(u32) + Ps2MemSize::Base;
static void recThrowHardwareDeficiency( const wxChar* extFail )
{
throw Exception::HardwareDeficiency(
wxsFormat( L"R5900-32 recompiler init failed: %s is not available.", extFail),
wxsFormat(_("%s Extensions not found. The R5900-32 recompiler requires a host CPU with MMX, SSE, and SSE2 extensions."), extFail )
);
}
static void recAlloc()
{
// Hardware Requirements Check...
if ( !( x86caps.hasMultimediaExtensions ) )
throw Exception::HardwareDeficiency( "Processor doesn't support MMX" );
if ( !x86caps.hasMultimediaExtensions )
recThrowHardwareDeficiency( L"MMX" );
if ( !( x86caps.hasStreamingSIMDExtensions ) )
throw Exception::HardwareDeficiency( "Processor doesn't support SSE" );
if ( !x86caps.hasStreamingSIMDExtensions )
recThrowHardwareDeficiency( L"SSE" );
if ( !( x86caps.hasStreamingSIMD2Extensions ) )
throw Exception::HardwareDeficiency( "Processor doesn't support SSE2" );
if ( !x86caps.hasStreamingSIMD2Extensions )
recThrowHardwareDeficiency( L"SSE2" );
if( recMem == NULL )
{
// Note: the VUrec depends on being able to grab an allocation below the 0x10000000 line,
// so we give the EErec an address above that to try first as it's basemem address, hence
// the 0x20000000 pick.
// It's handy to have a constant base address for the EE recompiler buffer, since it
// allows me to key in the address directly in the debugger, and also recognize EE
// recompiled code from user-provisioned stack traces. But besides those, the recompiler
// has no actual restrictions on where it's compiled code buffer is located.
// Note: the SuperVU recompiler depends on being able to grab an allocation below the
// 0x10000000 line, so we give the EErec an address above that to try first as it's
// basemem address, hence the 0x20000000 pick.
const uint cachememsize = REC_CACHEMEM+0x1000;
recMem = (u8*)SysMmapEx( 0x20000000, cachememsize, 0, "recAlloc(R5900)" );
}
if( recMem == NULL )
throw Exception::OutOfMemory( "R5900-32 > failed to allocate recompiler memory." );
throw Exception::OutOfMemory( "R5900-32: Out of memory allocating recompiled code buffer." );
// Goal: Allocate BASEBLOCKs for every possible branch target in PS2 memory.
// Any 4-byte aligned address makes a valid branch target as per MIPS design (all instructions are
@ -529,14 +542,14 @@ static void recAlloc()
m_recBlockAlloc = (u8*) _aligned_malloc( m_recBlockAllocSize, 4096 );
if( m_recBlockAlloc == NULL )
throw Exception::OutOfMemory( "R5900-32 Init > Failed to allocate memory for BASEBLOCK tables." );
throw Exception::OutOfMemory( "R5900-32: Out of memory allocating BASEBLOCK tables." );
u8* curpos = m_recBlockAlloc;
recRAM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Base / 4) * sizeof(BASEBLOCK);
recROM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom / 4) * sizeof(BASEBLOCK);
recROM1 = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom1 / 4) * sizeof(BASEBLOCK);
recConstBuf = (u32*)curpos; curpos += RECCONSTBUF_SIZE * sizeof(u32);
recRAMCopy = (u32*)curpos;
recRAM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Base / 4) * sizeof(BASEBLOCK);
recROM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom / 4) * sizeof(BASEBLOCK);
recROM1 = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom1 / 4) * sizeof(BASEBLOCK);
recConstBuf = (u32*)curpos; curpos += RECCONSTBUF_SIZE * sizeof(u32);
recRAMCopy = (u32*)curpos;
if( s_pInstCache == NULL )
{
@ -545,7 +558,7 @@ static void recAlloc()
}
if( s_pInstCache == NULL )
throw Exception::OutOfMemory( "R5900-32 Init > failed to allocate memory for pInstCache." );
throw Exception::OutOfMemory( "R5900-32: Out of memory allocating InstCache." );
// No errors.. Proceed with initialization:
@ -564,11 +577,13 @@ struct ManualPageTracking
static __aligned16 u16 manual_page[Ps2MemSize::Base >> 12];
static __aligned16 u8 manual_counter[Ps2MemSize::Base >> 12];
static bool eeRecIsReset = false;
static u32 eeRecIsReset = 0;
////////////////////////////////////////////////////
void recResetEE( void )
{
if( AtomicExchange( eeRecIsReset, true ) ) return;
Console.WriteLn( Color_StrongBlack, "Issuing EE/iR5900-32 Recompiler Reset" );
maxrecmem = 0;
@ -630,7 +645,6 @@ void recResetEE( void )
x86FpuState = FPU_STATE;
branch = 0;
eeRecIsReset = true;
}
static void recShutdown( void )
@ -656,26 +670,21 @@ static jmp_buf m_SetJmp_StateCheck;
static void recCheckExecutionState()
{
#if PCSX2_SEH
SysCoreThread::Get().StateCheckInThread();
if( eeRecIsReset )
throw Exception::ForceDispatcherReg();
#else
// Without SEH we'll need to hop to a safehouse point outside the scope of recompiled
// code. C++ exceptions can't cross the mighty chasm in the stackframe that the recompiler
// creates. However, the longjump is slow so we only want to do one when absolutely
// necessary:
pxAssert( !eeRecIsReset ); // should only be changed during suspended thread states
if( SysCoreThread::Get().HasPendingStateChangeRequest() )
if( GetCoreThread().HasPendingStateChangeRequest() )
{
longjmp( m_SetJmp_StateCheck, SetJmp_Dispatcher );
}
#if PCSX2_SEH
throw Exception::ForceDispatcherReg();
#else
// Without SEH we'll need to hop to a safehouse point outside the scope of recompiled
// code. C++ exceptions can't cross the mighty chasm in the stackframe that the recompiler
// creates. However, the longjump is slow so we only want to do one when absolutely
// necessary:
longjmp( m_SetJmp_StateCheck, 1 );
#endif
}
}
static void recExecute()
@ -684,64 +693,36 @@ static void recExecute()
// [TODO] fix this comment to explain various code entry/exit points, when I'm not so tired!
#if PCSX2_SEH
try {
while( true )
{
eeRecIsReset = false;
g_EEFreezeRegs = true;
eeRecIsReset = false;
g_EEFreezeRegs = true;
try {
EnterRecompiledCode();
}
catch( Exception::ForceDispatcherReg& ) { }
}
try {
EnterRecompiledCode();
}
catch( Exception::ExitRecExecute& ) {}
catch( Exception::ForceDispatcherReg& ) { }
#else
switch( setjmp( m_SetJmp_StateCheck ) )
int oldstate;
if( !setjmp( m_SetJmp_StateCheck ) )
{
case 0: // first run, fall through to Dispatcher
case SetJmp_Dispatcher:
while( true )
{
int oldstate;
eeRecIsReset = false;
g_EEFreezeRegs = true;
// Important! Most of the console logging and such has cancel points in it. This is great
// in Windows, where SEH lets us safely kill a thread from anywhere we want. This is bad
// in Linux, which cannot have a C++ exception cross the recompiler. Hence the changing
// of the cancelstate here!
// Important! Most of the console logging and such has cancel points in it. This is great
// in Windows, where SEH lets us safely kill a thread from anywhere we want. This is bad
// in Linux, which cannot have a C++ exception cross the recompiler. Hence the changing
// of the cancelstate here!
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
SysCoreThread::Get().StateCheckInThread();
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
eeRecIsReset = false;
#ifdef _WIN32
__try {
#endif
EnterRecompiledCode();
#ifdef _WIN32
} __finally
{
// This assertion is designed to help me troubleshoot the setjmp behavior from Win32.
// If the recompiler throws an unhandled SEH exception with SEH support disabled (which
// is typically a pthread_cancel) then this will fire and let me know.
// FIXME: Doesn't work because SEH is remarkably clever and executes the _finally block
// even when I use longjmp to restart the loop. Maybe a workaround exists? :/
//pxFailDev( "Recompiler threw an SEH exception with SEH disabled; possibly due to pthread_cancel." );
}
#endif
}
break;
case SetJmp_Exit: break;
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
EnterRecompiledCode();
// Generally unreachable code here ...
}
else
{
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
}
#endif
}
@ -858,7 +839,7 @@ static void recExitExecution()
#if PCSX2_SEH
throw Exception::ExitRecExecute();
#else
longjmp( m_SetJmp_StateCheck, SetJmp_Exit );
longjmp( m_SetJmp_StateCheck, 1 );
#endif
}
@ -1319,7 +1300,7 @@ static void __fastcall recRecompile( const u32 startpc )
if (dumplog & 4) iDumpRegisters(startpc, 0);
#endif
pxAssert( startpc );
pxAssume( startpc );
// if recPtr reached the mem limit reset whole mem
if ( ( (uptr)recPtr - (uptr)recMem ) >= REC_CACHEMEM-0x40000 || dumplog == 0xffffffff) {
@ -1348,7 +1329,7 @@ static void __fastcall recRecompile( const u32 startpc )
s_pCurBlockEx = recBlocks.New(HWADDR(startpc), (uptr)recPtr);
pxAssert(s_pCurBlockEx);
pxAssume(s_pCurBlockEx);
branch = 0;
@ -1359,7 +1340,7 @@ static void __fastcall recRecompile( const u32 startpc )
g_cpuHasConstReg = g_cpuFlushedConstReg = 1;
g_cpuPrevRegHasLive1 = g_cpuRegHasLive1 = 0xffffffff;
g_cpuPrevRegHasSignExt = g_cpuRegHasSignExt = 0;
pxAssert( g_cpuConstRegs[0].UD[0] == 0 );
pxAssume( g_cpuConstRegs[0].UD[0] == 0 );
_initX86regs();
_initXMMregs();

View File

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

View File

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