UI Bugfixes:

* Fixed several obscure deadlock issues.
 * Savestate actions block; such that only one load or save is permitted at a time.
 * Savestates now work through temp files, so that the existing file is not corrupted in case errors occur.
 * Exiting the emu while a savestate is saving will still save the state before completely quitting.
 * Plugin init errors are handled more gracefully.

Developer Notes:
 * Added some event diagnostic trace logging stuff.
 * More preliminary work on memorycards -- moved them to their own dialog box and started implementing things, but still a lot of work to do before it's ready for use.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2936 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-05-03 13:51:46 +00:00
parent b9c8ac3ead
commit 1507d72cad
52 changed files with 1419 additions and 657 deletions

View File

@ -161,6 +161,7 @@
<Unit filename="../../include/Utilities/ScopedPtr.h" /> <Unit filename="../../include/Utilities/ScopedPtr.h" />
<Unit filename="../../include/Utilities/StringHelpers.h" /> <Unit filename="../../include/Utilities/StringHelpers.h" />
<Unit filename="../../include/Utilities/Threading.h" /> <Unit filename="../../include/Utilities/Threading.h" />
<Unit filename="../../include/Utilities/ThreadingDialogs.h" />
<Unit filename="../../include/Utilities/lnx_memzero.h" /> <Unit filename="../../include/Utilities/lnx_memzero.h" />
<Unit filename="../../include/Utilities/pxCheckBox.h" /> <Unit filename="../../include/Utilities/pxCheckBox.h" />
<Unit filename="../../include/Utilities/pxEvents.h" /> <Unit filename="../../include/Utilities/pxEvents.h" />
@ -188,6 +189,7 @@
<Unit filename="../../src/Utilities/Semaphore.cpp" /> <Unit filename="../../src/Utilities/Semaphore.cpp" />
<Unit filename="../../src/Utilities/StringHelpers.cpp" /> <Unit filename="../../src/Utilities/StringHelpers.cpp" />
<Unit filename="../../src/Utilities/ThreadTools.cpp" /> <Unit filename="../../src/Utilities/ThreadTools.cpp" />
<Unit filename="../../src/Utilities/ThreadingDialogs.cpp" />
<Unit filename="../../src/Utilities/ThreadingInternal.h" /> <Unit filename="../../src/Utilities/ThreadingInternal.h" />
<Unit filename="../../src/Utilities/pxCheckBox.cpp" /> <Unit filename="../../src/Utilities/pxCheckBox.cpp" />
<Unit filename="../../src/Utilities/pxRadioPanel.cpp" /> <Unit filename="../../src/Utilities/pxRadioPanel.cpp" />

View File

@ -430,6 +430,10 @@
RelativePath="..\..\src\Utilities\Semaphore.cpp" RelativePath="..\..\src\Utilities\Semaphore.cpp"
> >
</File> </File>
<File
RelativePath="..\..\src\Utilities\ThreadingDialogs.cpp"
>
</File>
<File <File
RelativePath="..\..\src\Utilities\ThreadingInternal.h" RelativePath="..\..\src\Utilities\ThreadingInternal.h"
> >
@ -568,6 +572,10 @@
RelativePath="..\..\include\Utilities\Threading.h" RelativePath="..\..\include\Utilities\Threading.h"
> >
</File> </File>
<File
RelativePath="..\..\include\Utilities\ThreadingDialogs.h"
>
</File>
<File <File
RelativePath="..\..\include\Utilities\TlsVariable.inl" RelativePath="..\..\include\Utilities\TlsVariable.inl"
> >

View File

@ -128,11 +128,24 @@ namespace Exception
// The text string will be passed through the translator, so if it's int he gettext database // The text string will be passed through the translator, so if it's int he gettext database
// it will be optionally translated. // it will be optionally translated.
// //
// BUGZ?? I'd rather use 'classname' on the Clone() prototype, but for some reason it generates
// ambiguity errors on virtual inheritence (it really shouldn't!). So I have to force it to the
// BaseException base class. Not sure if this is Stupid Standard Tricks or Stupid MSVC Tricks. --air
//
// (update: web searches indicate it's MSVC specific -- happens in 2008, not sure about 2010).
//
#define DEFINE_EXCEPTION_COPYTORS( classname ) \ #define DEFINE_EXCEPTION_COPYTORS( classname ) \
virtual ~classname() throw() {} \ virtual ~classname() throw() {} \
virtual void Rethrow() const { throw *this; } \ virtual void Rethrow() const { throw *this; } \
virtual BaseException* Clone() const { return new classname( *this ); } virtual BaseException* Clone() const { return new classname( *this ); }
// This is here because MSVC's support for covariant return types on Clone() is broken, and will
// not work with virtual class inheritance (see DEFINE_EXCEPTION_COPYTORS for details)
#define DEFINE_EXCEPTION_COPYTORS_COVARIANT( classname ) \
virtual ~classname() throw() {} \
virtual void Rethrow() const { throw *this; } \
virtual classname* Clone() const { return new classname( *this ); }
#define DEFINE_RUNTIME_EXCEPTION( classname, defmsg ) \ #define DEFINE_RUNTIME_EXCEPTION( classname, defmsg ) \
DEFINE_EXCEPTION_COPYTORS( classname ) \ DEFINE_EXCEPTION_COPYTORS( classname ) \
\ \

View File

@ -312,6 +312,10 @@ namespace Threading
explicit ScopedLock( const Mutex& locker ); explicit ScopedLock( const Mutex& locker );
void AssignAndLock( const Mutex& locker ); void AssignAndLock( const Mutex& locker );
void AssignAndLock( const Mutex* locker ); void AssignAndLock( const Mutex* locker );
void Assign( const Mutex& locker );
void Assign( const Mutex* locker );
void Release(); void Release();
void Acquire(); void Acquire();

View File

@ -0,0 +1,55 @@
/* 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/>.
*/
#pragma once
#include "Threading.h"
#include "wxAppWithHelpers.h"
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE(pxEvt_ThreadedTaskComplete, -1)
END_DECLARE_EVENT_TYPES()
namespace Threading
{
// --------------------------------------------------------------------------------------
// WaitForTaskDialog
// --------------------------------------------------------------------------------------
// This dialog is displayed whenever the main thread is recursively waiting on multiple
// mutexes or semaphores. wxwidgets does not support recursive yielding to pending events
// but it *does* support opening a modal dialog, which disables the interface (preventing
// the user from starting additional actions), and processes messages (allowing the system
// to continue to manage threads and process logging).
//
class WaitForTaskDialog : public wxDialogWithHelpers
{
DECLARE_DYNAMIC_CLASS_NO_COPY(WaitForTaskDialog)
typedef wxDialogWithHelpers _parent;
protected:
SynchronousActionState m_sync;
public:
WaitForTaskDialog( const wxString& title=wxEmptyString, const wxString& heading=wxEmptyString );
virtual ~WaitForTaskDialog() throw() {}
virtual int ShowModal();
protected:
void OnTaskComplete( wxCommandEvent& evt );
//void OnTimer( wxTimerEvent& evt );
};
}

View File

@ -31,12 +31,12 @@ class SynchronousActionState
DeclareNoncopyableObject( SynchronousActionState ); DeclareNoncopyableObject( SynchronousActionState );
protected: protected:
bool m_posted; bool m_posted;
Threading::Semaphore m_sema; Threading::Semaphore m_sema;
ScopedPtr<BaseException> m_exception; ScopedPtr<BaseException> m_exception;
public: public:
sptr return_value; sptr return_value;
SynchronousActionState() SynchronousActionState()
{ {
@ -59,35 +59,12 @@ public:
Threading::Semaphore& GetSemaphore() { return m_sema; } Threading::Semaphore& GetSemaphore() { return m_sema; }
const Threading::Semaphore& GetSemaphore() const { return m_sema; } const Threading::Semaphore& GetSemaphore() const { return m_sema; }
void RethrowException() const void RethrowException() const;
{ int WaitForResult();
if( m_exception ) m_exception->Rethrow(); int WaitForResult_NoExceptions();
} void PostResult( int res );
void ClearResult();
int WaitForResult() void PostResult();
{
m_sema.WaitNoCancel();
RethrowException();
return return_value;
}
void PostResult( int res )
{
return_value = res;
PostResult();
}
void ClearResult()
{
m_posted = false;
}
void PostResult()
{
if( m_posted ) return;
m_posted = true;
m_sema.Post();
}
}; };
@ -117,7 +94,7 @@ public:
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// pxInvokeActionEvent // pxInvokeActionEvent
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class pxInvokeActionEvent : public wxEvent, public virtual IActionInvocation class pxInvokeActionEvent : public wxEvent
{ {
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxInvokeActionEvent) DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxInvokeActionEvent)
@ -143,10 +120,10 @@ public:
virtual void SetException( BaseException* ex ); virtual void SetException( BaseException* ex );
void SetException( const BaseException& ex ); void SetException( const BaseException& ex );
virtual void InvokeAction(); virtual void _DoInvokeEvent();
protected: protected:
virtual void _DoInvoke() {}; virtual void InvokeEvent() {}
}; };
@ -168,11 +145,14 @@ public:
pxExceptionEvent( const BaseException& ex ); pxExceptionEvent( const BaseException& ex );
virtual ~pxExceptionEvent() throw() { } virtual ~pxExceptionEvent() throw()
{
}
virtual pxExceptionEvent *Clone() const { return new pxExceptionEvent(*this); } virtual pxExceptionEvent *Clone() const { return new pxExceptionEvent(*this); }
protected: protected:
void _DoInvoke(); void InvokeEvent();
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -226,7 +206,7 @@ public:
BaseMessageBoxEvent( const BaseMessageBoxEvent& event ); BaseMessageBoxEvent( const BaseMessageBoxEvent& event );
protected: protected:
virtual void _DoInvoke(); virtual void InvokeEvent();
virtual int _DoDialog() const; virtual int _DoDialog() const;
}; };

View File

@ -315,6 +315,7 @@ public:
void Init(); void Init();
void AddOkCancel( wxSizer& sizer, bool hasApply=false ); void AddOkCancel( wxSizer& sizer, bool hasApply=false );
void AddOkCancel( wxSizer* sizer=NULL, bool hasApply=false );
virtual void SmartCenterFit(); virtual void SmartCenterFit();
virtual int ShowModal(); virtual int ShowModal();

View File

@ -141,9 +141,6 @@ bool Threading::Mutex::TryAcquire()
// This is a wxApp-safe rendition of AcquireWithoutYield, which makes sure to execute pending app events // This is a wxApp-safe rendition of AcquireWithoutYield, which makes sure to execute pending app events
// and messages *if* the lock is performed from the main GUI thread. // and messages *if* the lock is performed from the main GUI thread.
// //
// Exceptions:
// ThreadDeadlock - See description of ThreadDeadlock for details
//
void Threading::Mutex::Acquire() void Threading::Mutex::Acquire()
{ {
#if wxUSE_GUI #if wxUSE_GUI
@ -151,23 +148,22 @@ void Threading::Mutex::Acquire()
{ {
pthread_mutex_lock( &m_mutex ); pthread_mutex_lock( &m_mutex );
} }
else if( _WaitGui_RecursionGuard( "Mutex::Acquire" ) ) else if( _WaitGui_RecursionGuard( L"Mutex::Acquire" ) )
{ {
AcquireWithoutYield(); ScopedBusyCursor hourglass( Cursor_ReallyBusy );
pthread_mutex_lock( &m_mutex );
} }
else else
{ {
//ScopedBusyCursor hourglass( Cursor_KindaBusy );
while( !AcquireWithoutYield(def_yieldgui_interval) ) while( !AcquireWithoutYield(def_yieldgui_interval) )
wxTheApp->Yield( true ); YieldToMain();
} }
#else #else
pthread_mutex_lock( &m_mutex ); pthread_mutex_lock( &m_mutex );
#endif #endif
} }
// Exceptions:
// ThreadDeadlock - See description of ThreadDeadlock for details
//
bool Threading::Mutex::Acquire( const wxTimeSpan& timeout ) bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
{ {
#if wxUSE_GUI #if wxUSE_GUI
@ -175,19 +171,19 @@ bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
{ {
return AcquireWithoutYield(timeout); return AcquireWithoutYield(timeout);
} }
else if( _WaitGui_RecursionGuard( "Mutex::Acquire(timeout)" ) ) else if( _WaitGui_RecursionGuard( L"Mutex::TimedAcquire" ) )
{ {
ScopedBusyCursor hourglass( Cursor_ReallyBusy ); ScopedBusyCursor hourglass( Cursor_ReallyBusy );
return AcquireWithoutYield( timeout ); return AcquireWithoutYield( timeout );
} }
else else
{ {
ScopedBusyCursor hourglass( Cursor_KindaBusy ); //ScopedBusyCursor hourglass( Cursor_KindaBusy );
wxTimeSpan countdown( (timeout) ); wxTimeSpan countdown( (timeout) );
do { do {
if( AcquireWithoutYield( def_yieldgui_interval ) ) break; if( AcquireWithoutYield( def_yieldgui_interval ) ) break;
wxTheApp->Yield(true); YieldToMain();
countdown -= def_yieldgui_interval; countdown -= def_yieldgui_interval;
} while( countdown.GetMilliseconds() > 0 ); } while( countdown.GetMilliseconds() > 0 );
@ -206,9 +202,6 @@ bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
// //
// Implemented internally as a simple Acquire/Release pair. // Implemented internally as a simple Acquire/Release pair.
// //
// Exceptions:
// ThreadDeadlock - See description of ThreadDeadlock for details
//
void Threading::Mutex::Wait() void Threading::Mutex::Wait()
{ {
Acquire(); Acquire();
@ -228,9 +221,6 @@ void Threading::Mutex::WaitWithoutYield()
// true if the mutex was freed and is in an unlocked state; or false if the wait timed out // true if the mutex was freed and is in an unlocked state; or false if the wait timed out
// and the mutex is still locked by another thread. // and the mutex is still locked by another thread.
// //
// Exceptions:
// ThreadDeadlock - See description of ThreadDeadlock for details
//
bool Threading::Mutex::Wait( const wxTimeSpan& timeout ) bool Threading::Mutex::Wait( const wxTimeSpan& timeout )
{ {
if( Acquire(timeout) ) if( Acquire(timeout) )
@ -285,6 +275,16 @@ void Threading::ScopedLock::AssignAndLock( const Mutex* locker )
m_lock->Acquire(); m_lock->Acquire();
} }
void Threading::ScopedLock::Assign( const Mutex& locker )
{
m_lock = const_cast<Mutex*>(&locker);
}
void Threading::ScopedLock::Assign( const Mutex* locker )
{
m_lock = const_cast<Mutex*>(locker);
}
// Provides manual unlocking of a scoped lock prior to object destruction. // Provides manual unlocking of a scoped lock prior to object destruction.
void Threading::ScopedLock::Release() void Threading::ScopedLock::Release()
{ {

View File

@ -79,9 +79,6 @@ bool Threading::Semaphore::WaitWithoutYield( const wxTimeSpan& timeout )
// user input continues to be handled and that windoes continue to repaint. If the Wait is // user input continues to be handled and that windoes continue to repaint. If the Wait is
// called from another thread, no message pumping is performed. // called from another thread, no message pumping is performed.
// //
// Exceptions:
// ThreadDeadlock - See description of ThreadDeadlock for details
//
void Threading::Semaphore::Wait() void Threading::Semaphore::Wait()
{ {
#if wxUSE_GUI #if wxUSE_GUI
@ -89,16 +86,16 @@ void Threading::Semaphore::Wait()
{ {
sem_wait( &m_sema ); sem_wait( &m_sema );
} }
else if( _WaitGui_RecursionGuard( "Semaphore::Wait" ) ) else if( _WaitGui_RecursionGuard( L"Semaphore::Wait" ) )
{ {
ScopedBusyCursor hourglass( Cursor_ReallyBusy ); ScopedBusyCursor hourglass( Cursor_ReallyBusy );
WaitWithoutYield(); sem_wait( &m_sema );
} }
else else
{ {
ScopedBusyCursor hourglass( Cursor_KindaBusy ); //ScopedBusyCursor hourglass( Cursor_KindaBusy );
while( !WaitWithoutYield( def_yieldgui_interval ) ) while( !WaitWithoutYield( def_yieldgui_interval ) )
wxTheApp->Yield( true ); YieldToMain();
} }
#else #else
sem_wait( &m_sema ); sem_wait( &m_sema );
@ -114,9 +111,6 @@ void Threading::Semaphore::Wait()
// false if the wait timed out before the semaphore was signaled, or true if the signal was // false if the wait timed out before the semaphore was signaled, or true if the signal was
// reached prior to timeout. // reached prior to timeout.
// //
// Exceptions:
// ThreadDeadlock - See description of ThreadDeadlock for details
//
bool Threading::Semaphore::Wait( const wxTimeSpan& timeout ) bool Threading::Semaphore::Wait( const wxTimeSpan& timeout )
{ {
#if wxUSE_GUI #if wxUSE_GUI
@ -124,19 +118,19 @@ bool Threading::Semaphore::Wait( const wxTimeSpan& timeout )
{ {
return WaitWithoutYield( timeout ); return WaitWithoutYield( timeout );
} }
else if( _WaitGui_RecursionGuard( "Semaphore::Wait(timeout)" ) ) else if( _WaitGui_RecursionGuard( L"Semaphore::TimedWait" ) )
{ {
ScopedBusyCursor hourglass( Cursor_ReallyBusy ); ScopedBusyCursor hourglass( Cursor_ReallyBusy );
return WaitWithoutYield( timeout ); return WaitWithoutYield( timeout );
} }
else else
{ {
ScopedBusyCursor hourglass( Cursor_KindaBusy ); //ScopedBusyCursor hourglass( Cursor_KindaBusy );
wxTimeSpan countdown( (timeout) ); wxTimeSpan countdown( (timeout) );
do { do {
if( WaitWithoutYield( def_yieldgui_interval ) ) break; if( WaitWithoutYield( def_yieldgui_interval ) ) break;
wxTheApp->Yield(true); YieldToMain();
countdown -= def_yieldgui_interval; countdown -= def_yieldgui_interval;
} while( countdown.GetMilliseconds() > 0 ); } while( countdown.GetMilliseconds() > 0 );

View File

@ -125,8 +125,10 @@ void Threading::pxYield( int ms )
// (intended for internal use only) // (intended for internal use only)
// Returns true if the Wait is recursive, or false if the Wait is safe and should be // Returns true if the Wait is recursive, or false if the Wait is safe and should be
// handled via normal yielding methods. // handled via normal yielding methods.
bool Threading::_WaitGui_RecursionGuard( const char* guardname ) bool Threading::_WaitGui_RecursionGuard( const wxChar* name )
{ {
AffinityAssert_AllowFrom_MainUI();
// In order to avoid deadlock we need to make sure we cut some time to handle messages. // In order to avoid deadlock we need to make sure we cut some time to handle messages.
// But this can result in recursive yield calls, which would crash the app. Protect // But this can result in recursive yield calls, which would crash the app. Protect
// against them here and, if recursion is detected, perform a standard blocking wait. // against them here and, if recursion is detected, perform a standard blocking wait.
@ -134,12 +136,11 @@ bool Threading::_WaitGui_RecursionGuard( const char* guardname )
static int __Guard = 0; static int __Guard = 0;
RecursionGuard guard( __Guard ); RecursionGuard guard( __Guard );
if( guard.IsReentrant() ) //if( pxAssertDev( !guard.IsReentrant(), "Recursion during UI-bound threading wait object." ) ) return false;
{
Console.WriteLn( "(Thread Log) Possible yield recursion detected in %s; performing blocking wait.", guardname ); if( !guard.IsReentrant() ) return false;
return true; Console.WriteLn( "(Thread:%s) Yield recursion in %s; opening modal dialog.", pxGetCurrentThreadName().c_str(), name );
} return true;
return false;
} }
__forceinline void Threading::Timeslice() __forceinline void Threading::Timeslice()
@ -383,11 +384,26 @@ void Threading::PersistentThread::RethrowException() const
m_except->Rethrow(); m_except->Rethrow();
} }
static bool m_BlockDeletions = false;
bool Threading::AllowDeletions()
{
AffinityAssert_AllowFrom_MainUI();
return !m_BlockDeletions;
}
void Threading::YieldToMain()
{
m_BlockDeletions = true;
wxTheApp->Yield( true );
m_BlockDeletions = false;
}
void Threading::PersistentThread::_selfRunningTest( const wxChar* name ) const void Threading::PersistentThread::_selfRunningTest( const wxChar* name ) const
{ {
if( HasPendingException() ) if( HasPendingException() )
{ {
Console.Error( L"(Thread Error) An exception was thrown from blocking thread '%s' while waiting on a %s.", Console.Error( L"(Thread:%s) An exception was thrown while waiting on a %s.",
GetName().c_str(), name GetName().c_str(), name
); );
RethrowException(); RethrowException();
@ -400,6 +416,13 @@ void Threading::PersistentThread::_selfRunningTest( const wxChar* name ) const
GetName().c_str(), name ) GetName().c_str(), name )
); );
} }
// Thread is still alive and kicking (for now) -- yield to other messages and hope
// that impending chaos does not ensue. [it shouldn't since we block PersistentThread
// objects from being deleted until outside the scope of a mutex/semaphore wait).
if( (wxTheApp != NULL) && wxThread::IsMain() && !_WaitGui_RecursionGuard( L"WaitForSelf" ) )
Threading::YieldToMain();
} }
// This helper function is a deadlock-safe method of waiting on a semaphore in a PersistentThread. If the // This helper function is a deadlock-safe method of waiting on a semaphore in a PersistentThread. If the
@ -666,7 +689,7 @@ void Threading::BaseTaskThread::WaitForResult()
{ {
if( m_detached || !m_running ) return; if( m_detached || !m_running ) return;
if( m_TaskPending ) if( m_TaskPending )
#ifdef wxUSE_GUI #if wxUSE_GUI
m_post_TaskComplete.Wait(); m_post_TaskComplete.Wait();
#else #else
m_post_TaskComplete.WaitWithoutYield(); m_post_TaskComplete.WaitWithoutYield();

View File

@ -0,0 +1,85 @@
/* 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 "ThreadingDialogs.h"
#include "pxStaticText.h"
DEFINE_EVENT_TYPE(pxEvt_ThreadedTaskComplete);
// --------------------------------------------------------------------------------------
// WaitForTaskDialog Implementations
// --------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(WaitForTaskDialog, wxDialogWithHelpers)
Threading::WaitForTaskDialog::WaitForTaskDialog( const wxString& title, const wxString& heading )
: wxDialogWithHelpers( NULL, _("Waiting for tasks..."), wxVERTICAL )
//, m_Timer(this)
{
//m_sem = sem;
//m_mutex = mutex;
wxString m_title( title );
wxString m_heading( heading );
if( m_title.IsEmpty() ) m_title = _("Waiting for task...");
if( m_heading.IsEmpty() ) m_heading = m_title;
Connect( pxEvt_ThreadedTaskComplete, wxCommandEventHandler(WaitForTaskDialog::OnTaskComplete) );
wxBoxSizer& paddedMsg( *new wxBoxSizer( wxHORIZONTAL ) );
paddedMsg += 24;
paddedMsg += Heading(m_heading);
paddedMsg += 24;
*this += 12;
*this += paddedMsg;
*this += 12;
// TODO : Implement a cancel button. Not quite sure the best way to do
// that, since it requires a thread or event handler context, or something.
//applyDlg += new wxButton( &applyDlg, wxID_CANCEL ) | pxCenter;
//applyDlg += 6;
//Connect( m_Timer.GetId(), wxEVT_TIMER, wxTimerEventHandler(WaitForTaskDialog::OnTimer) );
//m_Timer.Start( 200 );
//GetSysExecutorThread().PostEvent( new SysExecEvent_ApplyPlugins( this, m_sync ) );
}
void Threading::WaitForTaskDialog::OnTaskComplete( wxCommandEvent& evt )
{
evt.Skip();
// Note: we don't throw exceptions from the pending task here.
// Instead we wait until we exit the modal loop below -- this gives
// the caller a chance to handle the exception themselves, and if
// not the exception will still fall back on the standard app-level
// exception handler.
// (this also avoids any sticky business with the modal dialog not getting
// closed out right due to stack unwinding skipping dialog closure crap)
m_sync.WaitForResult_NoExceptions();
EndModal( wxID_OK );
}
int Threading::WaitForTaskDialog::ShowModal()
{
int result = _parent::ShowModal();
m_sync.RethrowException();
return result;
}

View File

@ -23,6 +23,9 @@ namespace Threading
{ {
extern const wxTimeSpan def_yieldgui_interval; extern const wxTimeSpan def_yieldgui_interval;
extern bool _WaitGui_RecursionGuard( const char* guardname ); extern bool _WaitGui_RecursionGuard( const wxChar* name );
extern void YieldToMain();
extern bool AllowDeletions();
} }

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "wxAppWithHelpers.h" #include "wxAppWithHelpers.h"
#include "ThreadingInternal.h"
#include "PersistentThread.h" #include "PersistentThread.h"
DEFINE_EVENT_TYPE( pxEvt_DeleteObject ); DEFINE_EVENT_TYPE( pxEvt_DeleteObject );
@ -35,6 +36,47 @@ void BaseDeletableObject::DoDeletion()
app->DeleteObject( *this ); app->DeleteObject( *this );
} }
// --------------------------------------------------------------------------------------
// SynchronousActionState Implementations
// --------------------------------------------------------------------------------------
void SynchronousActionState::RethrowException() const
{
if( m_exception ) m_exception->Rethrow();
}
int SynchronousActionState::WaitForResult()
{
m_sema.WaitNoCancel();
RethrowException();
return return_value;
}
int SynchronousActionState::WaitForResult_NoExceptions()
{
m_sema.WaitNoCancel();
return return_value;
}
void SynchronousActionState::PostResult( int res )
{
return_value = res;
PostResult();
}
void SynchronousActionState::ClearResult()
{
m_posted = false;
}
void SynchronousActionState::PostResult()
{
if( m_posted ) return;
m_posted = true;
m_sema.Post();
}
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// pxInvokeActionEvent Implementations // pxInvokeActionEvent Implementations
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -181,7 +223,7 @@ public:
} }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
if( m_Method ) m_Method(); if( m_Method ) m_Method();
} }
@ -197,7 +239,7 @@ pxExceptionEvent::pxExceptionEvent( const BaseException& ex )
m_except = ex.Clone(); m_except = ex.Clone();
} }
void pxExceptionEvent::_DoInvoke() void pxExceptionEvent::InvokeEvent()
{ {
ScopedPtr<BaseException> deleteMe( m_except ); ScopedPtr<BaseException> deleteMe( m_except );
if( deleteMe ) deleteMe->Rethrow(); if( deleteMe ) deleteMe->Rethrow();
@ -322,12 +364,12 @@ void wxAppWithHelpers::CleanUp()
_parent::CleanUp(); _parent::CleanUp();
} }
void pxInvokeActionEvent::InvokeAction() void pxInvokeActionEvent::_DoInvokeEvent()
{ {
AffinityAssert_AllowFrom_MainUI(); AffinityAssert_AllowFrom_MainUI();
try { try {
_DoInvoke(); InvokeEvent();
} }
catch( BaseException& ex ) catch( BaseException& ex )
{ {
@ -387,10 +429,21 @@ void wxAppWithHelpers::IdleEventDispatcher( const char* action )
DbgCon.WriteLn( Color_Gray, "App IdleQueue (%s) -> %u events.", action, size ); DbgCon.WriteLn( Color_Gray, "App IdleQueue (%s) -> %u events.", action, size );
std::vector<wxEvent*> postponed;
for( size_t i=0; i<size; ++i ) for( size_t i=0; i<size; ++i )
ProcessEvent( *m_IdleEventQueue[i] ); {
if( !Threading::AllowDeletions() && (m_IdleEventQueue[i]->GetEventType() == pxEvt_DeleteThread) )
postponed.push_back(m_IdleEventQueue[i]);
else
{
lock.Release();
ProcessEvent( *m_IdleEventQueue[i] );
lock.Acquire();
}
}
m_IdleEventQueue.clear(); m_IdleEventQueue = postponed;
} }
void wxAppWithHelpers::OnIdleEvent( wxIdleEvent& evt ) void wxAppWithHelpers::OnIdleEvent( wxIdleEvent& evt )
@ -464,7 +517,7 @@ void wxAppWithHelpers::ProcessAction( pxInvokeActionEvent& evt )
sync.WaitForResult(); sync.WaitForResult();
} }
else else
evt.InvokeAction(); evt._DoInvokeEvent();
} }
@ -508,7 +561,7 @@ bool wxAppWithHelpers::OnInit()
void wxAppWithHelpers::OnInvokeAction( pxInvokeActionEvent& evt ) void wxAppWithHelpers::OnInvokeAction( pxInvokeActionEvent& evt )
{ {
evt.InvokeAction(); // wow this is easy! evt._DoInvokeEvent(); // wow this is easy!
} }
void wxAppWithHelpers::OnDeleteObject( wxCommandEvent& evt ) void wxAppWithHelpers::OnDeleteObject( wxCommandEvent& evt )

View File

@ -263,6 +263,13 @@ void wxDialogWithHelpers::AddOkCancel( wxSizer &sizer, bool hasApply )
s_buttons.Realize(); s_buttons.Realize();
} }
void wxDialogWithHelpers::AddOkCancel( wxSizer *sizer, bool hasApply )
{
if( sizer == NULL ) sizer = GetSizer();
pxAssume( sizer );
AddOkCancel( *sizer, hasApply );
}
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// wxPanelWithHelpers Implementations // wxPanelWithHelpers Implementations
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -377,6 +377,7 @@
<Unit filename="../gui/Dialogs/ImportSettingsDialog.cpp" /> <Unit filename="../gui/Dialogs/ImportSettingsDialog.cpp" />
<Unit filename="../gui/Dialogs/LogOptionsDialog.cpp" /> <Unit filename="../gui/Dialogs/LogOptionsDialog.cpp" />
<Unit filename="../gui/Dialogs/LogOptionsDialog.h" /> <Unit filename="../gui/Dialogs/LogOptionsDialog.h" />
<Unit filename="../gui/Dialogs/McdConfigDialog.cpp" />
<Unit filename="../gui/Dialogs/ModalPopups.h" /> <Unit filename="../gui/Dialogs/ModalPopups.h" />
<Unit filename="../gui/Dialogs/PickUserModeDialog.cpp" /> <Unit filename="../gui/Dialogs/PickUserModeDialog.cpp" />
<Unit filename="../gui/Dialogs/StuckThreadDialog.cpp" /> <Unit filename="../gui/Dialogs/StuckThreadDialog.cpp" />
@ -408,7 +409,7 @@
<Unit filename="../gui/Panels/LogOptionsPanels.cpp" /> <Unit filename="../gui/Panels/LogOptionsPanels.cpp" />
<Unit filename="../gui/Panels/LogOptionsPanels.h" /> <Unit filename="../gui/Panels/LogOptionsPanels.h" />
<Unit filename="../gui/Panels/MemoryCardListPanel.cpp" /> <Unit filename="../gui/Panels/MemoryCardListPanel.cpp" />
<Unit filename="../gui/Panels/MemoryCardListView.h" /> <Unit filename="../gui/Panels/MemoryCardPanels.h" />
<Unit filename="../gui/Panels/MemoryCardsPanel.cpp" /> <Unit filename="../gui/Panels/MemoryCardsPanel.cpp" />
<Unit filename="../gui/Panels/MiscPanelStuff.cpp" /> <Unit filename="../gui/Panels/MiscPanelStuff.cpp" />
<Unit filename="../gui/Panels/PathsPanel.cpp" /> <Unit filename="../gui/Panels/PathsPanel.cpp" />

View File

@ -86,7 +86,7 @@ namespace Exception
class PluginLoadError : public virtual PluginError, public virtual BadStream class PluginLoadError : public virtual PluginError, public virtual BadStream
{ {
public: public:
DEFINE_EXCEPTION_COPYTORS( PluginLoadError ) DEFINE_EXCEPTION_COPYTORS_COVARIANT( PluginLoadError )
PluginLoadError( PluginsEnum_t pid, const wxString& objname, const char* eng ); PluginLoadError( PluginsEnum_t pid, const wxString& objname, const char* eng );
@ -103,7 +103,7 @@ namespace Exception
class PluginInitError : public virtual PluginError class PluginInitError : public virtual PluginError
{ {
public: public:
DEFINE_EXCEPTION_COPYTORS( PluginInitError ) DEFINE_EXCEPTION_COPYTORS_COVARIANT( PluginInitError )
explicit PluginInitError( PluginsEnum_t pid, explicit PluginInitError( PluginsEnum_t pid,
const char* msg=wxLt("%s plugin failed to initialize. Your system may have insufficient memory or resources needed.") ) const char* msg=wxLt("%s plugin failed to initialize. Your system may have insufficient memory or resources needed.") )
@ -118,7 +118,7 @@ namespace Exception
class PluginOpenError : public virtual PluginError class PluginOpenError : public virtual PluginError
{ {
public: public:
DEFINE_EXCEPTION_COPYTORS( PluginOpenError ) DEFINE_EXCEPTION_COPYTORS_COVARIANT( PluginOpenError )
explicit PluginOpenError( PluginsEnum_t pid, explicit PluginOpenError( PluginsEnum_t pid,
const char* msg=wxLt("%s plugin failed to open. Your computer may have insufficient resources, or incompatible hardware/drivers.") ) const char* msg=wxLt("%s plugin failed to open. Your computer may have insufficient resources, or incompatible hardware/drivers.") )

View File

@ -79,9 +79,6 @@ void SysThreadBase::OnStart()
// Suspension must cancel itself forcefully or risk crashing whatever other action is // Suspension must cancel itself forcefully or risk crashing whatever other action is
// in progress. // in progress.
// //
// ThreadDeadlock - thrown if isBlocking is true and the thread to suspend fails to
// respond within the timeout period returned by GetDeadlockTimeout().
//
void SysThreadBase::Suspend( bool isBlocking ) void SysThreadBase::Suspend( bool isBlocking )
{ {
if( IsSelf() || !IsRunning() ) return; if( IsSelf() || !IsRunning() ) return;

View File

@ -67,15 +67,19 @@ protected:
const wxString m_filename; const wxString m_filename;
ScopedPtr< SafeArray< u8 > > m_src_buffer; ScopedPtr< SafeArray< u8 > > m_src_buffer;
bool m_PendingSaveFlag;
BaseCompressThread( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeHeader=NULL) BaseCompressThread( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeHeader=NULL)
: m_filename( file ) : m_filename( file )
, m_src_buffer( srcdata ) , m_src_buffer( srcdata )
{ {
m_WriteHeaderInThread = writeHeader; m_WriteHeaderInThread = writeHeader;
m_PendingSaveFlag = false;
} }
virtual ~BaseCompressThread() throw() {} virtual ~BaseCompressThread() throw();
void SetPendingSave();
public: public:
wxString GetStreamName() const { return m_filename; } wxString GetStreamName() const { return m_filename; }

View File

@ -20,6 +20,17 @@
#include "ThreadedZipTools.h" #include "ThreadedZipTools.h"
BaseCompressThread::~BaseCompressThread() throw()
{
if( m_PendingSaveFlag ) wxGetApp().ClearPendingSave();
}
void BaseCompressThread::SetPendingSave()
{
wxGetApp().StartPendingSave();
m_PendingSaveFlag = true;
}
CompressThread_gzip::CompressThread_gzip( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeheader ) CompressThread_gzip::CompressThread_gzip( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeheader )
: BaseCompressThread( file, srcdata, writeheader ) : BaseCompressThread( file, srcdata, writeheader )
{ {
@ -35,6 +46,8 @@ CompressThread_gzip::CompressThread_gzip( const wxString& file, ScopedPtr< SafeA
CompressThread_gzip::~CompressThread_gzip() throw() CompressThread_gzip::~CompressThread_gzip() throw()
{ {
_parent::Cancel();
if( m_gzfp ) gzclose( m_gzfp ); if( m_gzfp ) gzclose( m_gzfp );
} }
@ -46,11 +59,21 @@ void CompressThread_gzip::Write( const void* data, size_t size )
void CompressThread_gzip::ExecuteTaskInThread() void CompressThread_gzip::ExecuteTaskInThread()
{ {
// TODO : Add an API to PersistentThread for this! :) --air
//SetThreadPriority( THREAD_PRIORITY_BELOW_NORMAL );
if( !m_src_buffer ) return; if( !m_src_buffer ) return;
SetPendingSave();
Yield( 3 ); Yield( 3 );
if( !(m_gzfp = gzopen(m_filename.ToUTF8(), "wb")) ) // Safeguard against corruption by writing to a temp file, and then
// copying the final result over the original:
wxString tempfile( m_filename + L".tmp" );
if( !(m_gzfp = gzopen(tempfile.ToUTF8(), "wb")) )
throw Exception::CannotCreateStream( m_filename ); throw Exception::CannotCreateStream( m_filename );
gzsetparams(m_gzfp, Z_BEST_SPEED, Z_FILTERED); // Best speed at good compression gzsetparams(m_gzfp, Z_BEST_SPEED, Z_FILTERED); // Best speed at good compression
@ -67,14 +90,21 @@ void CompressThread_gzip::ExecuteTaskInThread()
if( gzwrite( m_gzfp, m_src_buffer->GetPtr(curidx), thisBlockSize ) < thisBlockSize ) if( gzwrite( m_gzfp, m_src_buffer->GetPtr(curidx), thisBlockSize ) < thisBlockSize )
throw Exception::BadStream( m_filename ); throw Exception::BadStream( m_filename );
curidx += thisBlockSize; curidx += thisBlockSize;
Yield( 3 ); Yield( 2 );
} while( curidx < m_src_buffer->GetSizeInBytes() ); } while( curidx < m_src_buffer->GetSizeInBytes() );
gzclose( m_gzfp );
m_gzfp = NULL;
if( !wxRenameFile( tempfile, m_filename, true ) )
throw Exception::BadStream( m_filename, "Failed to move or copy the temporary archive to the destination filename." );
Console.WriteLn( "(gzipThread) Data saved to disk without error." ); Console.WriteLn( "(gzipThread) Data saved to disk without error." );
} }
void CompressThread_gzip::OnCleanupInThread() void CompressThread_gzip::OnCleanupInThread()
{ {
_parent::OnCleanupInThread();
wxGetApp().DeleteThread( this ); wxGetApp().DeleteThread( this );
} }

View File

@ -108,6 +108,7 @@ enum MenuIdentifiers
// Config Subsection // Config Subsection
MenuId_Config_SysSettings, MenuId_Config_SysSettings,
MenuId_Config_McdSettings,
MenuId_Config_AppSettings, MenuId_Config_AppSettings,
MenuId_Config_BIOS, MenuId_Config_BIOS,
@ -635,7 +636,7 @@ DECLARE_APP(Pcsx2App)
// AppOpenDialog // AppOpenDialog
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
template<typename DialogType> template<typename DialogType>
void AppOpenDialog( wxWindow* parent ) void AppOpenDialog( wxWindow* parent=NULL )
{ {
if( wxWindow* window = wxFindWindowByName( DialogType::GetNameStatic() ) ) if( wxWindow* window = wxFindWindowByName( DialogType::GetNameStatic() ) )
window->SetFocus(); window->SetFocus();
@ -674,6 +675,9 @@ extern __aligned16 AppPluginManager CorePlugins;
extern void UI_UpdateSysControls(); extern void UI_UpdateSysControls();
extern void UI_DisableStateActions();
extern void UI_EnableStateActions();
extern void UI_DisableSysActions(); extern void UI_DisableSysActions();
extern void UI_EnableSysActions(); extern void UI_EnableSysActions();

View File

@ -106,14 +106,13 @@ static wxString pxGetStackTrace( const FnChar_t* calledFrom )
#ifdef __WXDEBUG__ #ifdef __WXDEBUG__
static TlsVariable< 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
// This override of wx's implementation provides thread safe assertion message reporting. If we aren't // off to the main gui thread via messages.
// 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 ) void Pcsx2App::OnAssertFailure( const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg )
{ {
// Re-entrant assertions are bad mojo -- trap immediately. // Re-entrant assertions are bad mojo -- trap immediately.
static DeclareTls(int) _reentrant_lock( 0 );
RecursionGuard guard( _reentrant_lock ); RecursionGuard guard( _reentrant_lock );
if( guard.IsReentrant() ) wxTrap(); if( guard.IsReentrant() ) wxTrap();

View File

@ -344,6 +344,7 @@ wxString AppConfig::FullpathToMcd( uint port, uint slot ) const
AppConfig::AppConfig() AppConfig::AppConfig()
: MainGuiPosition( wxDefaultPosition ) : MainGuiPosition( wxDefaultPosition )
, SysSettingsTabName( L"Cpu" ) , SysSettingsTabName( L"Cpu" )
, McdSettingsTabName( L"Standard" )
, AppSettingsTabName( L"GS Window" ) , AppSettingsTabName( L"GS Window" )
, DeskTheme( L"default" ) , DeskTheme( L"default" )
{ {
@ -353,7 +354,8 @@ AppConfig::AppConfig()
Toolbar_ImageSize = 24; Toolbar_ImageSize = 24;
Toolbar_ShowLabels = true; Toolbar_ShowLabels = true;
McdEnableNTFS = true; McdCompressNTFS = true;
McdEnableEjection = true;
EnableSpeedHacks = false; EnableSpeedHacks = false;
EnableGameFixes = false; EnableGameFixes = false;
@ -423,6 +425,7 @@ void AppConfig::LoadSaveRootItems( IniInterface& ini )
IniEntry( MainGuiPosition ); IniEntry( MainGuiPosition );
IniEntry( SysSettingsTabName ); IniEntry( SysSettingsTabName );
IniEntry( McdSettingsTabName );
IniEntry( AppSettingsTabName ); IniEntry( AppSettingsTabName );
ini.EnumEntry( L"LanguageId", LanguageId, NULL, defaults.LanguageId ); ini.EnumEntry( L"LanguageId", LanguageId, NULL, defaults.LanguageId );
IniEntry( RecentIsoCount ); IniEntry( RecentIsoCount );
@ -436,6 +439,9 @@ void AppConfig::LoadSaveRootItems( IniInterface& ini )
IniEntry( EnableSpeedHacks ); IniEntry( EnableSpeedHacks );
IniEntry( EnableGameFixes ); IniEntry( EnableGameFixes );
IniEntry( McdCompressNTFS );
IniEntry( McdEnableEjection );
ini.EnumEntry( L"CdvdSource", CdvdSource, CDVD_SourceLabels, defaults.CdvdSource ); ini.EnumEntry( L"CdvdSource", CdvdSource, CDVD_SourceLabels, defaults.CdvdSource );
} }

View File

@ -182,6 +182,7 @@ public:
// Because remembering the last used tab on the settings panel is cool (tab is remembered // Because remembering the last used tab on the settings panel is cool (tab is remembered
// by it's UTF/ASCII name). // by it's UTF/ASCII name).
wxString SysSettingsTabName; wxString SysSettingsTabName;
wxString McdSettingsTabName;
wxString AppSettingsTabName; wxString AppSettingsTabName;
// Current language in use (correlates to a wxWidgets wxLANGUAGE specifier) // Current language in use (correlates to a wxWidgets wxLANGUAGE specifier)
@ -204,8 +205,13 @@ public:
// Enables display of toolbar text labels. // Enables display of toolbar text labels.
bool Toolbar_ShowLabels; bool Toolbar_ShowLabels;
// enables automatic ntfs compression of memory cards (Win32 only) #ifdef __WXMSW__
bool McdEnableNTFS; // uses automatic ntfs compression when creating new memory cards (Win32 only)
bool McdCompressNTFS;
#endif
// Force-ejects modified memory cards when loading savestates (avoids corruption)
bool McdEnableEjection;
// Master toggle for enabling or disabling all speedhacks in one fail-free swoop. // Master toggle for enabling or disabling all speedhacks in one fail-free swoop.
// (the toggle is applied when a new EmuConfig is sent through AppCoreThread::ApplySettings) // (the toggle is applied when a new EmuConfig is sent through AppCoreThread::ApplySettings)

View File

@ -76,7 +76,7 @@ public:
PluginEventType GetEventType() { return m_evt; } PluginEventType GetEventType() { return m_evt; }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
sApp.DispatchEvent( m_evt ); sApp.DispatchEvent( m_evt );
} }
@ -154,7 +154,7 @@ public:
} }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
if( m_method ) (CorePlugins.*m_method)(); if( m_method ) (CorePlugins.*m_method)();
} }
@ -257,7 +257,7 @@ public:
} }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
CorePlugins.Load( m_folders ); CorePlugins.Load( m_folders );
} }
@ -337,13 +337,15 @@ void ScopedCoreThreadClose::LoadPlugins()
class SysExecEvent_UnloadPlugins : public SysExecEvent class SysExecEvent_UnloadPlugins : public SysExecEvent
{ {
public: public:
wxString GetEventName() const { return L"UnloadPlugins"; }
virtual ~SysExecEvent_UnloadPlugins() throw() {} virtual ~SysExecEvent_UnloadPlugins() throw() {}
SysExecEvent_UnloadPlugins* Clone() const { return new SysExecEvent_UnloadPlugins(*this); } SysExecEvent_UnloadPlugins* Clone() const { return new SysExecEvent_UnloadPlugins(*this); }
virtual bool AllowCancelOnExit() const { return false; } virtual bool AllowCancelOnExit() const { return false; }
virtual bool IsCriticalEvent() const { return true; } virtual bool IsCriticalEvent() const { return true; }
void _DoInvoke() void InvokeEvent()
{ {
CoreThread.Cancel(); CoreThread.Cancel();
CorePlugins.Unload(); CorePlugins.Unload();
@ -353,13 +355,15 @@ public:
class SysExecEvent_ShutdownPlugins : public SysExecEvent class SysExecEvent_ShutdownPlugins : public SysExecEvent
{ {
public: public:
wxString GetEventName() const { return L"ShutdownPlugins"; }
virtual ~SysExecEvent_ShutdownPlugins() throw() {} virtual ~SysExecEvent_ShutdownPlugins() throw() {}
SysExecEvent_ShutdownPlugins* Clone() const { return new SysExecEvent_ShutdownPlugins(*this); } SysExecEvent_ShutdownPlugins* Clone() const { return new SysExecEvent_ShutdownPlugins(*this); }
virtual bool AllowCancelOnExit() const { return false; } virtual bool AllowCancelOnExit() const { return false; }
virtual bool IsCriticalEvent() const { return true; } virtual bool IsCriticalEvent() const { return true; }
void _DoInvoke() void InvokeEvent()
{ {
CoreThread.Cancel(); CoreThread.Cancel();
CorePlugins.Shutdown(); CorePlugins.Shutdown();

View File

@ -17,6 +17,8 @@
#include "App.h" #include "App.h"
#include "AppSaveStates.h" #include "AppSaveStates.h"
#include "Utilities/TlsVariable.inl"
#include "ps2/BiosTools.h" #include "ps2/BiosTools.h"
#include "GS.h" #include "GS.h"
@ -78,7 +80,7 @@ public:
} }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
if( m_method ) (CoreThread.*m_method)(); if( m_method ) (CoreThread.*m_method)();
} }
@ -99,7 +101,7 @@ static void _Suspend()
void AppCoreThread::Suspend( bool isBlocking ) void AppCoreThread::Suspend( bool isBlocking )
{ {
if( !GetSysExecutorThread().SelfProcessMethod( _Suspend ) ) if( !GetSysExecutorThread().ProcessMethodSelf( _Suspend ) )
_parent::Suspend(true); _parent::Suspend(true);
} }
@ -107,7 +109,13 @@ static int resume_tries = 0;
void AppCoreThread::Resume() void AppCoreThread::Resume()
{ {
if( !AffinityAssert_AllowFrom_SysExecutor() ) return; //if( !AffinityAssert_AllowFrom_SysExecutor() ) return;
if( !GetSysExecutorThread().IsSelf() )
{
GetSysExecutorThread().PostEvent( SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::Resume) );
return;
}
if( m_ExecMode == ExecMode_Opened || (m_CloseTemporary > 0) ) return; if( m_ExecMode == ExecMode_Opened || (m_CloseTemporary > 0) ) return;
if( !pxAssert( CorePlugins.AreLoaded() ) ) return; if( !pxAssert( CorePlugins.AreLoaded() ) ) return;
@ -327,25 +335,27 @@ protected:
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// SysExecEvent_FullStop // SysExecEvent_CoreThreadClose
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class SysExecEvent_FullStop : public BaseSysExecEvent_ScopedCore class SysExecEvent_CoreThreadClose : public BaseSysExecEvent_ScopedCore
{ {
public: public:
virtual ~SysExecEvent_FullStop() throw() {} wxString GetEventName() const { return L"CloseCoreThread"; }
SysExecEvent_FullStop* Clone() const
virtual ~SysExecEvent_CoreThreadClose() throw() {}
SysExecEvent_CoreThreadClose* Clone() const
{ {
return new SysExecEvent_FullStop( *this ); return new SysExecEvent_CoreThreadClose( *this );
} }
SysExecEvent_FullStop( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL ) SysExecEvent_CoreThreadClose( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL )
: BaseSysExecEvent_ScopedCore( sync, resume_sync, mtx_resume ) { } : BaseSysExecEvent_ScopedCore( sync, resume_sync, mtx_resume ) { }
SysExecEvent_FullStop( SynchronousActionState& sync, SynchronousActionState& resume_sync, Threading::Mutex& mtx_resume ) SysExecEvent_CoreThreadClose( SynchronousActionState& sync, SynchronousActionState& resume_sync, Threading::Mutex& mtx_resume )
: BaseSysExecEvent_ScopedCore( &sync, &resume_sync, &mtx_resume ) { } : BaseSysExecEvent_ScopedCore( &sync, &resume_sync, &mtx_resume ) { }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
ScopedCoreThreadClose closed_core; ScopedCoreThreadClose closed_core;
_post_and_wait(closed_core); _post_and_wait(closed_core);
@ -354,25 +364,27 @@ protected:
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// SysExecEvent_FullStop // SysExecEvent_CoreThreadPause
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class SysExecEvent_Pause : public BaseSysExecEvent_ScopedCore class SysExecEvent_CoreThreadPause : public BaseSysExecEvent_ScopedCore
{ {
public: public:
virtual ~SysExecEvent_Pause() throw() {} wxString GetEventName() const { return L"PauseCoreThread"; }
SysExecEvent_Pause* Clone() const
virtual ~SysExecEvent_CoreThreadPause() throw() {}
SysExecEvent_CoreThreadPause* Clone() const
{ {
return new SysExecEvent_Pause( *this ); return new SysExecEvent_CoreThreadPause( *this );
} }
SysExecEvent_Pause( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL ) SysExecEvent_CoreThreadPause( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL )
: BaseSysExecEvent_ScopedCore( sync, resume_sync, mtx_resume ) { } : BaseSysExecEvent_ScopedCore( sync, resume_sync, mtx_resume ) { }
SysExecEvent_Pause( SynchronousActionState& sync, SynchronousActionState& resume_sync, Threading::Mutex& mtx_resume ) SysExecEvent_CoreThreadPause( SynchronousActionState& sync, SynchronousActionState& resume_sync, Threading::Mutex& mtx_resume )
: BaseSysExecEvent_ScopedCore( &sync, &resume_sync, &mtx_resume ) { } : BaseSysExecEvent_ScopedCore( &sync, &resume_sync, &mtx_resume ) { }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
ScopedCoreThreadPause paused_core; ScopedCoreThreadPause paused_core;
_post_and_wait(paused_core); _post_and_wait(paused_core);
@ -384,8 +396,8 @@ protected:
// ScopedCoreThreadClose / ScopedCoreThreadPause // ScopedCoreThreadClose / ScopedCoreThreadPause
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
static __threadlocal bool ScopedCore_IsPaused = false; static DeclareTls(bool) ScopedCore_IsPaused = false;
static __threadlocal bool ScopedCore_IsFullyClosed = false; static DeclareTls(bool) ScopedCore_IsFullyClosed = false;
BaseScopedCoreThread::BaseScopedCoreThread() BaseScopedCoreThread::BaseScopedCoreThread()
{ {
@ -440,7 +452,7 @@ ScopedCoreThreadClose::ScopedCoreThreadClose()
{ {
//DbgCon.WriteLn("(ScopedCoreThreadClose) Threaded Scope Created!"); //DbgCon.WriteLn("(ScopedCoreThreadClose) Threaded Scope Created!");
GetSysExecutorThread().PostEvent( SysExecEvent_FullStop(m_sync, m_sync_resume, m_mtx_resume) ); GetSysExecutorThread().PostEvent( SysExecEvent_CoreThreadClose(m_sync, m_sync_resume, m_mtx_resume) );
m_sync.WaitForResult(); m_sync.WaitForResult();
m_sync.RethrowException(); m_sync.RethrowException();
} }
@ -470,7 +482,7 @@ ScopedCoreThreadPause::ScopedCoreThreadPause()
{ {
//DbgCon.WriteLn("(ScopedCoreThreadPause) Threaded Scope Created!"); //DbgCon.WriteLn("(ScopedCoreThreadPause) Threaded Scope Created!");
GetSysExecutorThread().PostEvent( SysExecEvent_Pause(m_sync, m_sync_resume, m_mtx_resume) ); GetSysExecutorThread().PostEvent( SysExecEvent_CoreThreadPause(m_sync, m_sync_resume, m_mtx_resume) );
m_sync.WaitForResult(); m_sync.WaitForResult();
m_sync.RethrowException(); m_sync.RethrowException();
} }

View File

@ -266,5 +266,5 @@ public:
CoreThreadStatus GetEventType() { return m_evt; } CoreThreadStatus GetEventType() { return m_evt; }
protected: protected:
void _DoInvoke(); void InvokeEvent();
}; };

View File

@ -168,7 +168,7 @@ CoreThreadStatusEvent::CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousA
m_evt = evt; m_evt = evt;
} }
void CoreThreadStatusEvent::_DoInvoke() void CoreThreadStatusEvent::InvokeEvent()
{ {
sApp.DispatchEvent( m_evt ); sApp.DispatchEvent( m_evt );
} }

View File

@ -562,7 +562,7 @@ public:
protected: protected:
// When the SysExec message queue is finally empty, we should check the state of // When the SysExec message queue is finally empty, we should check the state of
// the menus and make sure they're all consistent to the current emulation states. // the menus and make sure they're all consistent to the current emulation states.
void DoIdle() void _DoIdle()
{ {
UI_UpdateSysControls(); UI_UpdateSysControls();
} }

View File

@ -47,94 +47,106 @@ bool UseDefaultSettingsFolder = true;
ScopedPtr<AppConfig> g_Conf; ScopedPtr<AppConfig> g_Conf;
ConfigOverrides OverrideOptions; ConfigOverrides OverrideOptions;
class NamedDialogBoxEvent : public BaseMessageBoxEvent template<typename DialogType>
int AppOpenModalDialog( wxWindow* parent=NULL )
{ {
typedef BaseMessageBoxEvent _parent; if( wxWindow* window = wxFindWindowByName( DialogType::GetNameStatic() ) )
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(NamedDialogBoxEvent)
public:
virtual ~NamedDialogBoxEvent() throw() { }
virtual NamedDialogBoxEvent *Clone() const { return new NamedDialogBoxEvent(*this); }
NamedDialogBoxEvent() {}
NamedDialogBoxEvent( const wxString& name, SynchronousActionState& sync )
: BaseMessageBoxEvent( name, sync ) {}
NamedDialogBoxEvent( const wxString& name, SynchronousActionState* sync=NULL )
: BaseMessageBoxEvent( name, sync ) {}
protected:
int _DoDialog() const
{ {
const wxString& dlgName( m_Content ); window->SetFocus();
if( wxDialog* dialog = wxDynamicCast( window, wxDialog ) )
if( dlgName.IsEmpty() ) return wxID_CANCEL;
if( wxWindow* window = wxFindWindowByName( dlgName ) )
{ {
if( wxDialog* dialog = wxDynamicCast( window, wxDialog ) ) // 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() )
{ {
window->SetFocus(); int result = dialog->ShowModal();
dialog->Destroy();
// It's legal to call ShowModal on a non-modal dialog, therefore making return result;
// 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 pxFailDev( "Can only show wxDialog class windows as modal!" );
{
using namespace Dialogs;
if( dlgName == SysConfigDialog::GetNameStatic() )
return SysConfigDialog().ShowModal();
if( dlgName == AppConfigDialog::GetNameStatic() )
return AppConfigDialog().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; return wxID_CANCEL;
} }
}; else
return DialogType( parent ).ShowModal();
IMPLEMENT_DYNAMIC_CLASS( NamedDialogBoxEvent, BaseMessageBoxEvent );
// Opens the specified standard dialog as a modal dialog, or forces the an existing
// instance of the dialog (ie, it's already open) to be modal. This is needed for
// items which are
static int IssueDialogAsModal( const wxString& dlgName )
{
BaseMessageBoxEvent tevt( dlgName );
return Msgbox::ShowModal( tevt );
} }
static bool HandlePluginError( Exception::PluginError& ex ) static bool HandlePluginError( Exception::BaseException& ex )
{ {
if( pxDialogExists( L"CoreSettings" ) ) return true; if( !pxDialogExists( L"CoreSettings" ) )
bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() +
_("\n\nPress Ok to go to the Plugin Configuration Panel.")
);
if( result )
{ {
g_Conf->SysSettingsTabName = L"Plugins"; if( !Msgbox::OkCancel( ex.FormatDisplayMessage() +
_("\n\nPress Ok to go to the Plugin Configuration Panel.")
// fixme: Send a message to the panel to select the failed plugin. ) )
if( IssueDialogAsModal( Dialogs::SysConfigDialog::GetNameStatic() ) == wxID_CANCEL ) return false;
return false; }
g_Conf->SysSettingsTabName = L"Plugins";
// TODO: Send a message to the panel to select the failed plugin.
return AppOpenModalDialog<Dialogs::SysConfigDialog>() != wxID_CANCEL;
}
class PluginErrorEvent : public pxExceptionEvent
{
typedef pxExceptionEvent _parent;
public:
PluginErrorEvent( BaseException* ex=NULL ) : _parent( ex ) {}
PluginErrorEvent( const BaseException& ex ) : _parent( ex ) {}
virtual ~PluginErrorEvent() throw() { }
virtual PluginErrorEvent *Clone() const { return new PluginErrorEvent(*this); }
protected:
void InvokeEvent();
};
class PluginInitErrorEvent : public pxExceptionEvent
{
typedef pxExceptionEvent _parent;
public:
PluginInitErrorEvent( BaseException* ex=NULL ) : _parent( ex ) {}
PluginInitErrorEvent( const BaseException& ex ) : _parent( ex ) {}
virtual ~PluginInitErrorEvent() throw() { }
virtual PluginInitErrorEvent *Clone() const { return new PluginInitErrorEvent(*this); }
protected:
void InvokeEvent();
};
void PluginErrorEvent::InvokeEvent()
{
if( !m_except ) return;
ScopedPtr<BaseException> deleteMe( m_except );
m_except = NULL;
if( !HandlePluginError( *deleteMe ) )
{
Console.Error( L"User-canceled plugin configuration; Plugins not loaded!" );
Msgbox::Alert( _("Warning! System plugins have not been loaded. PCSX2 may be inoperable.") );
}
}
void PluginInitErrorEvent::InvokeEvent()
{
if( !m_except ) return;
ScopedPtr<BaseException> deleteMe( m_except );
m_except = NULL;
if( !HandlePluginError( *deleteMe ) )
{
Console.Error( L"User-canceled plugin configuration after plugin initialization failure. Plugins unloaded." );
Msgbox::Alert( _("Warning! System plugins have not been loaded. PCSX2 may be inoperable.") );
} }
return result;
} }
// Allows for activating menu actions from anywhere in PCSX2. // Allows for activating menu actions from anywhere in PCSX2.
@ -194,7 +206,7 @@ public:
} }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
if( m_Method ) (wxGetApp().*m_Method)(); if( m_Method ) (wxGetApp().*m_Method)();
} }
@ -324,25 +336,6 @@ void Pcsx2App::LogicalVsync()
// Pcsx2App Event Handlers // Pcsx2App Event Handlers
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/*int Pcsx2App::DoStuckThread( PersistentThread& stuck_thread )
{
if( !wxThread::IsMain() )
{
//PostCommand( &stuck_thread, pxEvt_OpenDialog_StuckThread );
}
// Parent the dialog to the GS window if it belongs to PCSX2. If not
// we should bind it to the Main window, and if that's not around, use NULL.
wxWindow* parent = GetGsFramePtr();
if( parent == NULL )
parent = GetMainFramePtr();
pxStuckThreadEvent evt( stuck_thread );
return Msgbox::ShowModal( evt );
}*/
HashTools::HashMap<int, const GlobalCommandDescriptor*> GlobalAccels( 0, 0xffffffff ); HashTools::HashMap<int, const GlobalCommandDescriptor*> GlobalAccels( 0, 0xffffffff );
void Pcsx2App::OnEmuKeyDown( wxKeyEvent& evt ) void Pcsx2App::OnEmuKeyDown( wxKeyEvent& evt )
@ -403,10 +396,8 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
if( dialog.ShowModal() == wxID_CANCEL ) if( dialog.ShowModal() == wxID_CANCEL )
Console.Warning( "User denied option to re-configure BIOS." ); Console.Warning( "User denied option to re-configure BIOS." );
if( IssueDialogAsModal( Dialogs::BiosSelectorDialog::GetNameStatic() ) != wxID_CANCEL ) if( AppOpenModalDialog<Dialogs::BiosSelectorDialog>() != wxID_CANCEL )
{
SysExecute(); SysExecute();
}
else else
Console.Warning( "User canceled BIOS configuration." ); Console.Warning( "User canceled BIOS configuration." );
} }
@ -418,27 +409,20 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
StateCopy_Clear(); StateCopy_Clear();
CoreThread.Resume(); CoreThread.Resume();
} }
// ----------------------------------------------------------------------------
catch( Exception::PluginInitError& ex ) catch( Exception::PluginInitError& ex )
{ {
CorePlugins.Shutdown(); ShutdownPlugins();
Console.Error( ex.FormatDiagnosticMessage() ); Console.Error( ex.FormatDiagnosticMessage() );
if( !HandlePluginError( ex ) ) AddIdleEvent( PluginInitErrorEvent(ex) );
{
Console.Error( L"User-canceled plugin configuration after plugin initialization failure. Plugins unloaded." );
Msgbox::Alert( _("Warning! System plugins have not been loaded. PCSX2 may be inoperable.") );
}
} }
catch( Exception::PluginError& ex ) catch( Exception::PluginError& ex )
{ {
CorePlugins.Close(); UnloadPlugins();
Console.Error( ex.FormatDiagnosticMessage() ); Console.Error( ex.FormatDiagnosticMessage() );
if( !HandlePluginError( ex ) ) AddIdleEvent( PluginErrorEvent(ex) );
{
Console.Error( L"User-canceled plugin configuration; Plugins not loaded!" );
Msgbox::Alert( _("Warning! System plugins have not been loaded. PCSX2 may be inoperable.") );
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
#if 0 #if 0
@ -501,7 +485,7 @@ void Pcsx2App::StartPendingSave()
void Pcsx2App::ClearPendingSave() void Pcsx2App::ClearPendingSave()
{ {
if( PostAppMethodMyself(&Pcsx2App::StartPendingSave) ) return; if( PostAppMethodMyself(&Pcsx2App::ClearPendingSave) ) return;
--m_PendingSaves; --m_PendingSaves;
pxAssumeDev( m_PendingSaves >= 0, "Pending saves count mismatch (pending count is less than 0)" ); pxAssumeDev( m_PendingSaves >= 0, "Pending saves count mismatch (pending count is less than 0)" );
@ -529,8 +513,13 @@ void Pcsx2App::PrepForExit()
if( m_PendingSaves != 0 ) if( m_PendingSaves != 0 )
{ {
// When the thread finishes, it will call ClearPendingSave, which in turn will
// exit the app once all pending saves have "logged out." (if one does not track
// itself using our PendingSaves feature, it would be lost -- too bad!)
Console.WriteLn( "App: Saves are pending; exit postponed..." ); Console.WriteLn( "App: Saves are pending; exit postponed..." );
sApp.SetExitOnFrameDelete( false ); SetExitOnFrameDelete( false );
m_ScheduledTermination = true;
return; return;
} }
@ -600,7 +589,7 @@ void AppApplySettings( const AppConfig* oldconf )
// Update the compression attribute on the Memcards folder. // Update the compression attribute on the Memcards folder.
// Memcards generally compress very well via NTFS compression. // Memcards generally compress very well via NTFS compression.
NTFS_CompressFile( g_Conf->Folders.MemoryCards.ToString(), g_Conf->McdEnableNTFS ); NTFS_CompressFile( g_Conf->Folders.MemoryCards.ToString(), g_Conf->McdCompressNTFS );
sApp.DispatchEvent( AppStatus_SettingsApplied ); sApp.DispatchEvent( AppStatus_SettingsApplied );
paused_core.AllowResume(); paused_core.AllowResume();
@ -838,9 +827,9 @@ void Pcsx2App::OnMainFrameClosed( wxWindowID id )
} }
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// SysExecuteEvent // SysExecEvent_Execute
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class SysExecuteEvent : public SysExecEvent class SysExecEvent_Execute : public SysExecEvent
{ {
protected: protected:
bool m_UseCDVDsrc; bool m_UseCDVDsrc;
@ -849,8 +838,8 @@ protected:
wxString m_elf_override; wxString m_elf_override;
public: public:
virtual ~SysExecuteEvent() throw() {} virtual ~SysExecEvent_Execute() throw() {}
SysExecuteEvent* Clone() const { return new SysExecuteEvent(*this); } SysExecEvent_Execute* Clone() const { return new SysExecEvent_Execute(*this); }
wxString GetEventName() const wxString GetEventName() const
{ {
@ -862,13 +851,13 @@ public:
return _("Executing PS2 Virtual Machine..."); return _("Executing PS2 Virtual Machine...");
} }
SysExecuteEvent() SysExecEvent_Execute()
: m_UseCDVDsrc(false) : m_UseCDVDsrc(false)
, m_UseELFOverride(false) , m_UseELFOverride(false)
{ {
} }
SysExecuteEvent( CDVD_SourceType srctype, const wxString& elf_override ) SysExecEvent_Execute( CDVD_SourceType srctype, const wxString& elf_override )
: m_UseCDVDsrc(true) : m_UseCDVDsrc(true)
, m_UseELFOverride(true) , m_UseELFOverride(true)
, m_cdvdsrc_type(srctype) , m_cdvdsrc_type(srctype)
@ -877,7 +866,7 @@ public:
} }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
wxGetApp().ProcessMethod( AppSaveSettings ); wxGetApp().ProcessMethod( AppSaveSettings );
@ -905,7 +894,7 @@ protected:
// configured CDVD source device. // configured CDVD source device.
void Pcsx2App::SysExecute() void Pcsx2App::SysExecute()
{ {
SysExecutorThread.PostEvent( new SysExecuteEvent() ); SysExecutorThread.PostEvent( new SysExecEvent_Execute() );
} }
// Executes the specified cdvd source and optional elf file. This command performs a // Executes the specified cdvd source and optional elf file. This command performs a
@ -913,7 +902,7 @@ void Pcsx2App::SysExecute()
// sources. // sources.
void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc, const wxString& elf_override ) void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc, const wxString& elf_override )
{ {
SysExecutorThread.PostEvent( new SysExecuteEvent(cdvdsrc, elf_override) ); SysExecutorThread.PostEvent( new SysExecEvent_Execute(cdvdsrc, elf_override) );
} }
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -933,7 +922,7 @@ public:
} }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
StateCopy_Clear(); StateCopy_Clear();
CoreThread.Shutdown(); CoreThread.Shutdown();

View File

@ -151,7 +151,7 @@ ConsoleLogFrame::ColorArray::ColorArray( int fontsize ) :
Create( fontsize ); Create( fontsize );
} }
ConsoleLogFrame::ColorArray::~ColorArray() ConsoleLogFrame::ColorArray::~ColorArray() throw()
{ {
Cleanup(); Cleanup();
} }
@ -351,7 +351,7 @@ void ConsoleLogFrame::Write( ConsoleColors color, const wxString& text )
{ {
pthread_testcancel(); pthread_testcancel();
ScopedLock lock( m_QueueLock ); ScopedLock lock( m_mtx_Queue );
if( m_QueueColorSection.GetLength() == 0 ) if( m_QueueColorSection.GetLength() == 0 )
{ {
@ -378,10 +378,22 @@ void ConsoleLogFrame::Write( ConsoleColors color, const wxString& text )
if( !m_pendingFlushMsg ) if( !m_pendingFlushMsg )
{ {
m_pendingFlushMsg = true;
// wxWidgets may have aggressive locks on event processing, so best to release
// our own mutex lest wx get hung for an extended period of time and cause all
// of our own stuff to get sluggish.
lock.Release();
wxCommandEvent evt( pxEvt_FlushQueue ); wxCommandEvent evt( pxEvt_FlushQueue );
evt.SetInt( 0 ); evt.SetInt( 0 );
GetEventHandler()->AddPendingEvent( evt ); if( wxThread::IsMain() )
m_pendingFlushMsg = true; GetEventHandler()->ProcessEvent( evt );
else
{
GetEventHandler()->AddPendingEvent( evt );
}
lock.Acquire();
} }
++m_pendingFlushes; ++m_pendingFlushes;
@ -624,7 +636,7 @@ void ConsoleLogFrame::OnFlushLimiterTimer( wxTimerEvent& )
void ConsoleLogFrame::OnFlushEvent( wxCommandEvent& ) void ConsoleLogFrame::OnFlushEvent( wxCommandEvent& )
{ {
ScopedLock locker( m_QueueLock ); ScopedLock locker( m_mtx_Queue );
m_pendingFlushMsg = false; m_pendingFlushMsg = false;
// recursion guard needed due to Mutex lock/acquire code below, which can end up yielding // recursion guard needed due to Mutex lock/acquire code below, which can end up yielding

View File

@ -144,7 +144,7 @@ protected:
wxTextAttr m_color_default; wxTextAttr m_color_default;
public: public:
virtual ~ColorArray(); virtual ~ColorArray() throw();
ColorArray( int fontsize=8 ); ColorArray( int fontsize=8 );
void Create( int fontsize ); void Create( int fontsize );
@ -217,7 +217,7 @@ protected:
// Lock object for accessing or modifying the following three vars: // Lock object for accessing or modifying the following three vars:
// m_QueueBuffer, m_QueueColorSelection, m_CurQueuePos // m_QueueBuffer, m_QueueColorSelection, m_CurQueuePos
MutexRecursive m_QueueLock; MutexRecursive m_mtx_Queue;
// Describes a series of colored text sections in the m_QueueBuffer. // Describes a series of colored text sections in the m_QueueBuffer.
SafeList<ColorSection> m_QueueColorSection; SafeList<ColorSection> m_QueueColorSection;

View File

@ -56,7 +56,7 @@ Dialogs::BaseApplicableDialog::~BaseApplicableDialog() throw()
} }
Dialogs::BaseConfigurationDialog::BaseConfigurationDialog( wxWindow* parent, const wxString& title, wxImageList& bookicons, int idealWidth ) Dialogs::BaseConfigurationDialog::BaseConfigurationDialog( wxWindow* parent, const wxString& title, wxImageList& bookicons, int idealWidth )
: BaseApplicableDialog( parent, title, wxVERTICAL ) : _parent( parent, title, wxVERTICAL )
, m_listbook( *new wxListbook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_orient ) ) , m_listbook( *new wxListbook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_orient ) )
{ {
m_idealWidth = idealWidth; m_idealWidth = idealWidth;
@ -64,13 +64,7 @@ Dialogs::BaseConfigurationDialog::BaseConfigurationDialog( wxWindow* parent, con
m_listbook.SetImageList( &bookicons ); m_listbook.SetImageList( &bookicons );
m_ApplyState.StartBook( &m_listbook ); m_ApplyState.StartBook( &m_listbook );
wxBitmapButton& screenshotButton( *new wxBitmapButton( this, wxID_SAVE, EmbeddedImage<res_ButtonIcon_Camera>().Get() ) ); *this += m_listbook | pxExpand.Border( wxLEFT | wxRIGHT, 2 );
screenshotButton.SetToolTip( _("Saves a snapshot of this settings panel to a PNG file.") );
*this += m_listbook;
AddOkCancel( *GetSizer(), true );
*m_extraButtonSizer += screenshotButton;
Connect( wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BaseConfigurationDialog::OnOk_Click ) ); Connect( wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BaseConfigurationDialog::OnOk_Click ) );
Connect( wxID_CANCEL, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BaseConfigurationDialog::OnCancel_Click ) ); Connect( wxID_CANCEL, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BaseConfigurationDialog::OnCancel_Click ) );
@ -98,8 +92,17 @@ Dialogs::BaseConfigurationDialog::BaseConfigurationDialog( wxWindow* parent, con
ConnectSomethingChanged( SPINCTRL_UPDATED ); ConnectSomethingChanged( SPINCTRL_UPDATED );
ConnectSomethingChanged( SLIDER_UPDATED ); ConnectSomethingChanged( SLIDER_UPDATED );
ConnectSomethingChanged( DIRPICKER_CHANGED ); ConnectSomethingChanged( DIRPICKER_CHANGED );
}
void Dialogs::BaseConfigurationDialog::AddOkCancel( wxSizer* sizer )
{
_parent::AddOkCancel( sizer, true );
FindWindow( wxID_APPLY )->Disable(); FindWindow( wxID_APPLY )->Disable();
wxBitmapButton& screenshotButton( *new wxBitmapButton( this, wxID_SAVE, EmbeddedImage<res_ButtonIcon_Camera>().Get() ) );
screenshotButton.SetToolTip( _("Saves a snapshot of this settings panel to a PNG file.") );
*m_extraButtonSizer += screenshotButton;
} }
Dialogs::BaseConfigurationDialog::~BaseConfigurationDialog() throw() Dialogs::BaseConfigurationDialog::~BaseConfigurationDialog() throw()
@ -112,7 +115,7 @@ void Dialogs::BaseConfigurationDialog::OnSomethingChanged( wxCommandEvent& evt )
evt.Skip(); evt.Skip();
if( (evt.GetId() != wxID_OK) && (evt.GetId() != wxID_CANCEL) && (evt.GetId() != wxID_APPLY) ) if( (evt.GetId() != wxID_OK) && (evt.GetId() != wxID_CANCEL) && (evt.GetId() != wxID_APPLY) )
{ {
FindWindow( wxID_APPLY )->Enable(); if( wxWindow *apply = FindWindow( wxID_APPLY ) ) apply->Enable();
} }
} }

View File

@ -45,6 +45,8 @@ namespace Dialogs
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class BaseConfigurationDialog : public BaseApplicableDialog class BaseConfigurationDialog : public BaseApplicableDialog
{ {
typedef BaseApplicableDialog _parent;
protected: protected:
wxListbook& m_listbook; wxListbook& m_listbook;
wxArrayString m_labels; wxArrayString m_labels;
@ -53,10 +55,13 @@ namespace Dialogs
virtual ~BaseConfigurationDialog() throw(); virtual ~BaseConfigurationDialog() throw();
BaseConfigurationDialog(wxWindow* parent, const wxString& title, wxImageList& bookicons, int idealWidth); BaseConfigurationDialog(wxWindow* parent, const wxString& title, wxImageList& bookicons, int idealWidth);
protected: public:
void AddOkCancel( wxSizer* sizer=NULL );
template< typename T > template< typename T >
void AddPage( const char* label, int iconid ); void AddPage( const char* label, int iconid );
protected:
void OnOk_Click( wxCommandEvent& evt ); void OnOk_Click( wxCommandEvent& evt );
void OnCancel_Click( wxCommandEvent& evt ); void OnCancel_Click( wxCommandEvent& evt );
void OnApply_Click( wxCommandEvent& evt ); void OnApply_Click( wxCommandEvent& evt );
@ -72,8 +77,6 @@ namespace Dialogs
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class SysConfigDialog : public BaseConfigurationDialog class SysConfigDialog : public BaseConfigurationDialog
{ {
protected:
public: public:
virtual ~SysConfigDialog() throw() {} virtual ~SysConfigDialog() throw() {}
SysConfigDialog(wxWindow* parent=NULL); SysConfigDialog(wxWindow* parent=NULL);
@ -83,6 +86,20 @@ namespace Dialogs
virtual wxString& GetConfSettingsTabName() const { return g_Conf->SysSettingsTabName; } virtual wxString& GetConfSettingsTabName() const { return g_Conf->SysSettingsTabName; }
}; };
// --------------------------------------------------------------------------------------
// McdConfigDialog
// --------------------------------------------------------------------------------------
class McdConfigDialog : public BaseConfigurationDialog
{
public:
virtual ~McdConfigDialog() throw() {}
McdConfigDialog(wxWindow* parent=NULL);
static const wxChar* GetNameStatic() { return L"Dialog:MemoryCardSettings"; }
protected:
virtual wxString& GetConfSettingsTabName() const { return g_Conf->McdSettingsTabName; }
};
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// AppConfigDialog // AppConfigDialog
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -21,6 +21,8 @@
using namespace pxSizerFlags; using namespace pxSizerFlags;
extern wxString GetMsg_McdNtfsCompress();
wxFilePickerCtrl* CreateMemoryCardFilePicker( wxWindow* parent, uint portidx, uint slotidx, const wxString& filename=wxEmptyString ) wxFilePickerCtrl* CreateMemoryCardFilePicker( wxWindow* parent, uint portidx, uint slotidx, const wxString& filename=wxEmptyString )
{ {
return new wxFilePickerCtrl( parent, wxID_ANY, filename, return new wxFilePickerCtrl( parent, wxID_ANY, filename,
@ -47,10 +49,7 @@ Dialogs::CreateMemoryCardDialog::CreateMemoryCardDialog( wxWindow* parent, uint
#ifdef __WXMSW__ #ifdef __WXMSW__
m_check_CompressNTFS = new pxCheckBox( this, m_check_CompressNTFS = new pxCheckBox( this,
_("Use NTFS compression on this card"), _("Use NTFS compression on this card"),
pxE( ".Dialog:Memorycards:NtfsCompress", GetMsg_McdNtfsCompress()
L"NTFS compression is built-in, fast, and completely reliable. Memorycards typically compress "
L"very well, and run faster as a result, so this option is highly recommended."
)
); );
#endif #endif

View File

@ -0,0 +1,157 @@
/* 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 "ConfigurationDialog.h"
#include "BaseConfigurationDialog.inl"
#include "ModalPopups.h"
#include "MSWstuff.h"
#include "Panels/ConfigurationPanels.h"
#include "Panels/MemoryCardPanels.h"
using namespace pxSizerFlags;
namespace Panels
{
// Helper class since the 'AddPage' template system needs a single-parameter constructor.
class McdConfigPanel_Multitap2 : public McdConfigPanel_Multitap
{
public:
McdConfigPanel_Multitap2( wxWindow* parent ) : McdConfigPanel_Multitap( parent, 1 ) {}
virtual ~McdConfigPanel_Multitap2() throw() { }
};
}
wxString GetMsg_McdNtfsCompress()
{
return pxE( ".Dialog:Memorycards:NtfsCompress",
L"NTFS compression is built-in, fast, and completely reliable; and typically compresses MemoryCards "
L"very well (this option is highly recommended)."
);
}
Panels::McdConfigPanel_Toggles::McdConfigPanel_Toggles(wxWindow *parent)
: _parent( parent )
{
m_idealWidth -= 48;
m_check_Ejection = new pxCheckBox( this,
_("Auto-eject memorycards when loading savestates"),
pxE( ".Dialog:Memorycards:EnableEjection",
L"Avoids memorycard corruption by forcing games to re-index card contents after "
L"loading from savestates. May not be compatible with all games (Guitar Hero)."
)
);
#ifdef __WXMSW__
m_check_CompressNTFS = new pxCheckBox( this,
_("Enable NTFS Compression on all cards by default."),
GetMsg_McdNtfsCompress()
);
#endif
*this += m_check_Ejection | pxExpand;
*this += m_check_CompressNTFS | pxExpand;
}
void Panels::McdConfigPanel_Toggles::Apply()
{
g_Conf->McdEnableEjection = m_check_Ejection->GetValue();
g_Conf->McdCompressNTFS = m_check_CompressNTFS->GetValue();
}
void Panels::McdConfigPanel_Toggles::AppStatusEvent_OnSettingsApplied()
{
m_check_Ejection ->SetValue( g_Conf->McdEnableEjection );
m_check_CompressNTFS ->SetValue( g_Conf->McdCompressNTFS );
}
Panels::McdConfigPanel_Standard::McdConfigPanel_Standard(wxWindow *parent) : _parent( parent )
{
m_panel_cardinfo[0] = new MemoryCardInfoPanel( this, 0, 0 );
m_panel_cardinfo[1] = new MemoryCardInfoPanel( this, 1, 0 );
for( uint port=0; port<2; ++port )
{
wxStaticBoxSizer& portSizer( *new wxStaticBoxSizer( wxVERTICAL, this, wxsFormat(_("Port %u"), port+1) ) );
portSizer += m_panel_cardinfo[port] | pxExpand;
*this += portSizer | StdExpand();
}
}
void Panels::McdConfigPanel_Standard::Apply()
{
}
void Panels::McdConfigPanel_Standard::AppStatusEvent_OnSettingsApplied()
{
}
Panels::McdConfigPanel_Multitap::McdConfigPanel_Multitap(wxWindow *parent, int port) : _parent( parent )
{
m_port = port;
m_check_Multitap = new pxCheckBox( this, wxsFormat(_("Enable Multitap on Port %u"), m_port+1) );
m_check_Multitap->SetFont( wxFont( m_check_Multitap->GetFont().GetPointSize()+1, wxFONTFAMILY_MODERN, wxNORMAL, wxNORMAL, false, L"Lucida Console" ) );
Connect( m_check_Multitap->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(McdConfigPanel_Multitap::OnMultitapChecked));
}
void Panels::McdConfigPanel_Multitap::OnMultitapChecked( wxCommandEvent& evt )
{
}
void Panels::McdConfigPanel_Multitap::Apply()
{
}
void Panels::McdConfigPanel_Multitap::AppStatusEvent_OnSettingsApplied()
{
}
using namespace Panels;
using namespace pxSizerFlags;
Dialogs::McdConfigDialog::McdConfigDialog( wxWindow* parent )
: BaseConfigurationDialog( parent, _("MemoryCard Settings - PCSX2"), wxGetApp().GetImgList_Config(), 600 )
{
SetName( GetNameStatic() );
// [TODO] : Discover and use a good multitap port icon! Possibility might be a
// simple 3x memorycards icon, in cascading form.
// (for now everything defaults to the redundant memorycard icon)
const AppImageIds::ConfigIds& cfgid( wxGetApp().GetImgId().Config );
AddPage<McdConfigPanel_Toggles> ( wxLt("Settings"), cfgid.MemoryCard );
AddPage<McdConfigPanel_Standard> ( wxLt("Slots 1/2"), cfgid.MemoryCard );
AddPage<McdConfigPanel_Multitap> ( wxLt("Multitap 1"), cfgid.MemoryCard );
AddPage<McdConfigPanel_Multitap2> ( wxLt("Multitap 2"), cfgid.MemoryCard );
//MSW_ListView_SetIconSpacing( m_listbook, m_idealWidth );
*this += StdPadding;
*this += new wxStaticLine( this ) | pxExpand;
*this += StdPadding;
*this += new MemoryCardListPanel( this ) | pxExpand;
AddOkCancel();
}

View File

@ -23,7 +23,7 @@
#include "ModalPopups.h" #include "ModalPopups.h"
#include "Panels/ConfigurationPanels.h" #include "Panels/ConfigurationPanels.h"
#include <wx/filepicker.h> //#include <wx/filepicker.h>
using namespace Panels; using namespace Panels;
@ -34,7 +34,6 @@ Dialogs::SysConfigDialog::SysConfigDialog(wxWindow* parent)
const AppImageIds::ConfigIds& cfgid( wxGetApp().GetImgId().Config ); const AppImageIds::ConfigIds& cfgid( wxGetApp().GetImgId().Config );
AddPage<MemoryCardsPanel> ( wxLt("MemoryCards"), cfgid.MemoryCard );
AddPage<CpuPanelEE> ( wxLt("EE/IOP"), cfgid.Cpu ); AddPage<CpuPanelEE> ( wxLt("EE/IOP"), cfgid.Cpu );
AddPage<CpuPanelVU> ( wxLt("VUs"), cfgid.Cpu ); AddPage<CpuPanelVU> ( wxLt("VUs"), cfgid.Cpu );
AddPage<VideoPanel> ( wxLt("GS"), cfgid.Video ); AddPage<VideoPanel> ( wxLt("GS"), cfgid.Video );
@ -44,8 +43,7 @@ Dialogs::SysConfigDialog::SysConfigDialog(wxWindow* parent)
MSW_ListView_SetIconSpacing( m_listbook, m_idealWidth ); MSW_ListView_SetIconSpacing( m_listbook, m_idealWidth );
// For some reason adding pages un-does the Apply button, so we need to re-disable it here. AddOkCancel();
FindWindow( wxID_APPLY )->Disable();
} }
Dialogs::AppConfigDialog::AppConfigDialog(wxWindow* parent) Dialogs::AppConfigDialog::AppConfigDialog(wxWindow* parent)
@ -60,6 +58,5 @@ Dialogs::AppConfigDialog::AppConfigDialog(wxWindow* parent)
MSW_ListView_SetIconSpacing( m_listbook, GetClientSize().GetWidth() ); MSW_ListView_SetIconSpacing( m_listbook, GetClientSize().GetWidth() );
// For some reason adding pages un-does the Apply button, so we need to re-disable it here. AddOkCancel();
FindWindow( wxID_APPLY )->Disable();
} }

View File

@ -17,13 +17,38 @@
#include "App.h" #include "App.h"
// This is called from InvokeAction after various affinity and state checks have verified the wxString SysExecEvent::GetEventName() const
// message as executable. Override this when possible. Only override InvokeAction if you {
pxFail( "Warning: Unnamed SysExecutor Event! Please overload GetEventName() in all SysExecEvent derived classes." );
return wxEmptyString;
}
wxString SysExecEvent::GetEventMessage() const
{
return GetEventName();
}
// This is called from _DoInvokeEvent after various affinity and state checks have verified the
// message as executable. Override this when possible. Only override _DoInvokeEvent if you
// need some kind of additional low-level ability. // need some kind of additional low-level ability.
void SysExecEvent::_DoInvoke() void SysExecEvent::InvokeEvent()
{ {
} }
// This is called by _DoInvokeEvent *always* -- even when exceptions occur during InvokeEvent(),
// making this function a bit like a C# 'finally' block (try/catch/finally -- a nice feature lacking
// from C++ prior to the new C++0x10 standard).
//
// This function calls PostResult by default, and should be invoked by derived classes overriding
// CleanupEvent(), unless you want to change the PostResult behavior.
void SysExecEvent::CleanupEvent()
{
PostResult();
}
// Transports the specified exception to the thread/context that invoked this event.
// Events are run from a try/catch block in the event handler that automatically transports
// exceptions as neeeded, so there shouldn't be much need to use this method directly.
void SysExecEvent::SetException( BaseException* ex ) void SysExecEvent::SetException( BaseException* ex )
{ {
if( !ex ) return; if( !ex ) return;
@ -55,14 +80,15 @@ void SysExecEvent::SetException( const BaseException& ex )
} }
// This method calls _DoInvoke after performing some setup and affinity checks. // This method calls _DoInvoke after performing some setup, exception handling, and
// Override _DoInvoke instead, which is the intended method of implementing derived class invocation. // affinity checks. For implementing behavior of your event, override _DoInvoke
void SysExecEvent::InvokeAction() // instead, which is the intended method of implementing derived class invocation.
void SysExecEvent::_DoInvokeEvent()
{ {
//pxAssumeDev( !IsBeingDeleted(), "Attempted to process a deleted SysExecutor event." ); //pxAssumeDev( !IsBeingDeleted(), "Attempted to process a deleted SysExecutor event." );
AffinityAssert_AllowFrom_SysExecutor(); AffinityAssert_AllowFrom_SysExecutor();
try { try {
_DoInvoke(); InvokeEvent();
} }
catch( BaseException& ex ) catch( BaseException& ex )
{ {
@ -73,19 +99,32 @@ void SysExecEvent::InvokeAction()
SetException( new Exception::RuntimeError(ex) ); SetException( new Exception::RuntimeError(ex) );
} }
PostResult(); CleanupEvent();
} }
// Posts an empty result to the invoking context/thread of this message, if one exists.
// If the invoking thread posted the event in non-blocking fashion then no action is
// taken.
void SysExecEvent::PostResult() const void SysExecEvent::PostResult() const
{ {
if( m_sync ) m_sync->PostResult(); if( m_sync ) m_sync->PostResult();
} }
// --------------------------------------------------------------------------------------
// pxEvtHandler Implementations
// --------------------------------------------------------------------------------------
pxEvtHandler::pxEvtHandler() pxEvtHandler::pxEvtHandler()
{ {
AtomicExchange( m_Quitting, false ); AtomicExchange( m_Quitting, false );
m_qpc_Start = 0;
} }
// Puts the event queue into Shutdown mode, which does *not* immediately stop nor cancel
// the queue's processing. Instead it marks the queue inaccessible to all new events
// and continues procesing queued events for critical events that should not be ignored.
// (typically these are shutdown events critical to closing the app cleanly). Once
// all such events have been processed, the thread is stopped.
//
void pxEvtHandler::ShutdownQueue() void pxEvtHandler::ShutdownQueue()
{ {
if( m_Quitting ) return; if( m_Quitting ) return;
@ -108,48 +147,73 @@ struct ScopedThreadCancelDisable
} }
}; };
void pxEvtHandler::ProcessPendingEvents() void pxEvtHandler::ProcessEvents( pxEvtList& list )
{ {
ScopedLock synclock( m_mtx_pending ); ScopedLock synclock( m_mtx_pending );
pxEvtList::iterator node; pxEvtList::iterator node;
while( node = m_pendingEvents.begin(), node != m_pendingEvents.end() ) while( node = list.begin(), node != list.end() )
{ {
ScopedPtr<SysExecEvent> deleteMe(wx_static_cast(SysExecEvent *, *node)); ScopedPtr<SysExecEvent> deleteMe(*node);
m_pendingEvents.erase( node ); list.erase( node );
if( !m_Quitting || deleteMe->IsCriticalEvent() ) if( !m_Quitting || deleteMe->IsCriticalEvent() )
{ {
// Some messages can be blocking, so we should release the mutex lock // Some messages can be blocking, so we should release the mutex lock while
// to avoid having cases where the main thread deadlocks simply trying // processing, to avoid having cases where the main thread deadlocks simply
// to add a message to the queue. // trying to add a message to the queue due to the basic mutex acquire needed.
m_qpc_Start = GetCPUTicks();
synclock.Release(); synclock.Release();
DevCon.WriteLn( L"(pxEvtHandler) Executing Event: %s [%s]", deleteMe->GetEventName().c_str(), deleteMe->AllowCancelOnExit() ? L"Cancelable" : L"Noncancelable" );
if( deleteMe->AllowCancelOnExit() ) if( deleteMe->AllowCancelOnExit() )
deleteMe->InvokeAction(); deleteMe->_DoInvokeEvent();
else else
{ {
ScopedThreadCancelDisable thr_cancel_scope; ScopedThreadCancelDisable thr_cancel_scope;
deleteMe->InvokeAction(); deleteMe->_DoInvokeEvent();
} }
u64 qpc_end = GetCPUTicks();
DevCon.WriteLn( L"(pxEvtHandler) Event '%s' completed in %dms", deleteMe->GetEventName().c_str(), ((qpc_end-m_qpc_Start)*100) / GetTickFrequency() );
synclock.Acquire(); synclock.Acquire();
m_qpc_Start = 0; // lets the main thread know the message completed.
} }
else else
{ {
Console.WriteLn( L"(pxEvtHandler:Skipping Event) %s", deleteMe->GetEventName().c_str() ); Console.WriteLn( L"(pxEvtHandler) Skipping Event: %s", deleteMe->GetEventName().c_str() );
deleteMe->PostResult(); deleteMe->PostResult();
} }
} }
} }
// This method is provided for wxWidgets API conformance. void pxEvtHandler::ProcessIdleEvents()
{
ProcessEvents( m_idleEvents );
}
void pxEvtHandler::ProcessPendingEvents()
{
ProcessEvents( m_pendingEvents );
}
// This method is provided for wxWidgets API conformance. I like to use PostEvent instead
// since it's remenicient of PostMessage in Windows (and behaves rather similarly).
void pxEvtHandler::AddPendingEvent( SysExecEvent& evt ) void pxEvtHandler::AddPendingEvent( SysExecEvent& evt )
{ {
PostEvent( evt ); PostEvent( evt );
} }
// Adds an event to the event queue in non-blocking fashion. The thread executing this
// event queue will be woken up if it's idle/sleeping.
// IMPORTANT: The pointer version of this function will *DELETE* the event object passed
// to it automatically when the event has been executed. If you are using a scoped event
// you should use the Reference/Handle overload instead!
//
void pxEvtHandler::PostEvent( SysExecEvent* evt ) void pxEvtHandler::PostEvent( SysExecEvent* evt )
{ {
if( !evt ) return; if( !evt ) return;
@ -171,16 +235,33 @@ void pxEvtHandler::PostEvent( SysExecEvent* evt )
void pxEvtHandler::PostEvent( const SysExecEvent& evt ) void pxEvtHandler::PostEvent( const SysExecEvent& evt )
{ {
PostEvent( evt.Clone() );
}
void pxEvtHandler::PostIdleEvent( SysExecEvent* evt )
{
if( !evt ) return;
if( m_Quitting ) if( m_Quitting )
{ {
evt.PostResult(); evt->PostResult();
return; return;
} }
ScopedLock synclock( m_mtx_pending ); ScopedLock synclock( m_mtx_pending );
m_pendingEvents.push_back( evt.Clone() );
if( m_pendingEvents.size() == 1) if( m_idleEvents.size() == 0)
{
m_pendingEvents.push_back( evt );
m_wakeup.Post(); m_wakeup.Post();
}
else
m_idleEvents.push_back( evt );
}
void pxEvtHandler::PostIdleEvent( const SysExecEvent& evt )
{
PostIdleEvent( evt.Clone() );
} }
void pxEvtHandler::ProcessEvent( SysExecEvent& evt ) void pxEvtHandler::ProcessEvent( SysExecEvent& evt )
@ -193,23 +274,7 @@ void pxEvtHandler::ProcessEvent( SysExecEvent& evt )
sync.WaitForResult(); sync.WaitForResult();
} }
else else
evt.InvokeAction(); evt._DoInvokeEvent();
}
bool pxEvtHandler::SelfProcessMethod( FnType_Void* method )
{
if( wxThread::GetCurrentId() != m_OwnerThreadId )
{
SynchronousActionState sync;
SysExecEvent_Method evt(method);
evt.SetSyncState( sync );
PostEvent( evt );
sync.WaitForResult();
return true;
}
return false;
} }
void pxEvtHandler::ProcessEvent( SysExecEvent* evt ) void pxEvtHandler::ProcessEvent( SysExecEvent* evt )
@ -226,13 +291,38 @@ void pxEvtHandler::ProcessEvent( SysExecEvent* evt )
else else
{ {
ScopedPtr<SysExecEvent> deleteMe( evt ); ScopedPtr<SysExecEvent> deleteMe( evt );
deleteMe->InvokeAction(); deleteMe->_DoInvokeEvent();
} }
} }
bool pxEvtHandler::ProcessMethodSelf( FnType_Void* method )
{
if( wxThread::GetCurrentId() != m_OwnerThreadId )
{
SynchronousActionState sync;
SysExecEvent_MethodVoid evt(method);
evt.SetSyncState( sync );
PostEvent( evt );
sync.WaitForResult();
return true;
}
return false;
}
// This method invokes the derived class Idle implementations (if any) and then enters
// the sleep state until such time that new messages are received.
//
// FUTURE: Processes idle messages from the idle message queue (not implemented yet).
//
// Extending: Derived classes should override _DoIdle instead, unless it is necessary
// to implement post-wakeup behavior.
//
void pxEvtHandler::Idle() void pxEvtHandler::Idle()
{ {
DoIdle(); ProcessIdleEvents();
_DoIdle();
m_wakeup.WaitWithoutYield(); m_wakeup.WaitWithoutYield();
} }
@ -244,6 +334,9 @@ void pxEvtHandler::SetActiveThread()
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// WaitingForThreadedTaskDialog // WaitingForThreadedTaskDialog
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Note: currently unused (legacy code). May be utilized at a later date, so I'm leaving
// it in (for now!)
//
class WaitingForThreadedTaskDialog class WaitingForThreadedTaskDialog
: public wxDialogWithHelpers : public wxDialogWithHelpers
{ {
@ -303,6 +396,7 @@ ExecutorThread::ExecutorThread( pxEvtHandler* evthandler )
m_EvtHandler = evthandler; m_EvtHandler = evthandler;
} }
// Exposes the internal pxEvtHandler::ShutdownQueue API. See pxEvtHandler for details.
void ExecutorThread::ShutdownQueue() void ExecutorThread::ShutdownQueue()
{ {
if( !m_EvtHandler || m_EvtHandler->IsShuttingDown() ) return; if( !m_EvtHandler || m_EvtHandler->IsShuttingDown() ) return;
@ -310,6 +404,7 @@ void ExecutorThread::ShutdownQueue()
Block(); Block();
} }
// Exposes the internal pxEvtHandler::PostEvent API. See pxEvtHandler for details.
void ExecutorThread::PostEvent( SysExecEvent* evt ) void ExecutorThread::PostEvent( SysExecEvent* evt )
{ {
if( !pxAssert( m_EvtHandler ) ) return; if( !pxAssert( m_EvtHandler ) ) return;
@ -322,6 +417,20 @@ void ExecutorThread::PostEvent( const SysExecEvent& evt )
m_EvtHandler->PostEvent( evt ); m_EvtHandler->PostEvent( evt );
} }
// Exposes the internal pxEvtHandler::PostIdleEvent API. See pxEvtHandler for details.
void ExecutorThread::PostIdleEvent( SysExecEvent* evt )
{
if( !pxAssert( m_EvtHandler ) ) return;
m_EvtHandler->PostIdleEvent( evt );
}
void ExecutorThread::PostIdleEvent( const SysExecEvent& evt )
{
if( !pxAssert( m_EvtHandler ) ) return;
m_EvtHandler->PostIdleEvent( evt );
}
// Exposes the internal pxEvtHandler::ProcessEvent API. See pxEvtHandler for details.
void ExecutorThread::ProcessEvent( SysExecEvent* evt ) void ExecutorThread::ProcessEvent( SysExecEvent* evt )
{ {
if( m_EvtHandler ) if( m_EvtHandler )
@ -329,7 +438,7 @@ void ExecutorThread::ProcessEvent( SysExecEvent* evt )
else else
{ {
ScopedPtr<SysExecEvent> deleteMe( evt ); ScopedPtr<SysExecEvent> deleteMe( evt );
deleteMe->InvokeAction(); deleteMe->_DoInvokeEvent();
} }
} }
@ -338,7 +447,7 @@ void ExecutorThread::ProcessEvent( SysExecEvent& evt )
if( m_EvtHandler ) if( m_EvtHandler )
m_EvtHandler->ProcessEvent( evt ); m_EvtHandler->ProcessEvent( evt );
else else
evt.InvokeAction(); evt._DoInvokeEvent();
} }
void ExecutorThread::OnStart() void ExecutorThread::OnStart()
@ -379,7 +488,7 @@ void ExecutorThread::OnCleanupInThread()
// This event is called when the SysExecutorThread's timer triggers, which means the // This event is called when the SysExecutorThread's timer triggers, which means the
// VM/system task has taken an oddly long period of time to complete. The task is able // VM/system task has taken an oddly long period of time to complete. The task is able
// to invoke a modal dialog from here that will grant the user some options for handling // to invoke a modal dialog from here that will grant the user some options for handling
// the unresponsive thread. // the unresponsive task.
void Pcsx2App::OnSysExecutorTaskTimeout( wxTimerEvent& evt ) void Pcsx2App::OnSysExecutorTaskTimeout( wxTimerEvent& evt )
{ {
if( !SysExecutorThread.IsRunning() ) return; if( !SysExecutorThread.IsRunning() ) return;

View File

@ -155,7 +155,8 @@ void MainEmuFrame::ConnectMenus()
#define ConnectMenuRange( id_start, inc, handler ) \ #define ConnectMenuRange( id_start, inc, handler ) \
Connect( id_start, id_start + inc, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainEmuFrame::handler) ) Connect( id_start, id_start + inc, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainEmuFrame::handler) )
ConnectMenu( MenuId_Config_SysSettings, Menu_ConfigSettings_Click ); ConnectMenu( MenuId_Config_SysSettings, Menu_SysSettings_Click );
ConnectMenu( MenuId_Config_McdSettings, Menu_McdSettings_Click );
ConnectMenu( MenuId_Config_AppSettings, Menu_AppSettings_Click ); ConnectMenu( MenuId_Config_AppSettings, Menu_AppSettings_Click );
ConnectMenu( MenuId_Config_BIOS, Menu_SelectBios_Click ); ConnectMenu( MenuId_Config_BIOS, Menu_SelectBios_Click );
ConnectMenu( MenuId_Config_ResetAll, Menu_ResetAllSettings_Click ); ConnectMenu( MenuId_Config_ResetAll, Menu_ResetAllSettings_Click );
@ -412,6 +413,7 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
m_menuConfig.Append(MenuId_Config_SysSettings, _("Emulation &Settings") ); m_menuConfig.Append(MenuId_Config_SysSettings, _("Emulation &Settings") );
m_menuConfig.Append(MenuId_Config_AppSettings, _("App Settings") ); m_menuConfig.Append(MenuId_Config_AppSettings, _("App Settings") );
m_menuConfig.Append(MenuId_Config_McdSettings, _("&MemoryCards") );
m_menuConfig.AppendSeparator(); m_menuConfig.AppendSeparator();

View File

@ -162,7 +162,8 @@ protected:
void OnFocus( wxFocusEvent& evt ); void OnFocus( wxFocusEvent& evt );
void OnActivate( wxActivateEvent& evt ); void OnActivate( wxActivateEvent& evt );
void Menu_ConfigSettings_Click(wxCommandEvent &event); void Menu_SysSettings_Click(wxCommandEvent &event);
void Menu_McdSettings_Click(wxCommandEvent &event);
void Menu_AppSettings_Click(wxCommandEvent &event); void Menu_AppSettings_Click(wxCommandEvent &event);
void Menu_SelectBios_Click(wxCommandEvent &event); void Menu_SelectBios_Click(wxCommandEvent &event);
void Menu_ResetAllSettings_Click(wxCommandEvent &event); void Menu_ResetAllSettings_Click(wxCommandEvent &event);

View File

@ -38,11 +38,16 @@ void MainEmuFrame::SaveEmuOptions()
} }
} }
void MainEmuFrame::Menu_ConfigSettings_Click(wxCommandEvent &event) void MainEmuFrame::Menu_SysSettings_Click(wxCommandEvent &event)
{ {
AppOpenDialog<SysConfigDialog>( this ); AppOpenDialog<SysConfigDialog>( this );
} }
void MainEmuFrame::Menu_McdSettings_Click(wxCommandEvent &event)
{
AppOpenDialog<McdConfigDialog>( this );
}
void MainEmuFrame::Menu_AppSettings_Click(wxCommandEvent &event) void MainEmuFrame::Menu_AppSettings_Click(wxCommandEvent &event)
{ {
AppOpenDialog<AppConfigDialog>( this ); AppOpenDialog<AppConfigDialog>( this );
@ -425,7 +430,7 @@ public:
wxString GetEventName() const { return L"ToggleSuspendResume"; } wxString GetEventName() const { return L"ToggleSuspendResume"; }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
if( CoreThread.IsOpen() ) if( CoreThread.IsOpen() )
CoreThread.Suspend(); CoreThread.Suspend();
@ -447,7 +452,7 @@ public:
} }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
sApp.SysShutdown(); sApp.SysShutdown();
sApp.SysExecute(); sApp.SysExecute();

View File

@ -102,7 +102,7 @@ FileMemoryCard::FileMemoryCard()
// [TODO] : Add memcard size detection and report it to the console log. // [TODO] : Add memcard size detection and report it to the console log.
// (8MB, 256Mb, whatever) // (8MB, 256Mb, whatever)
NTFS_CompressFile( str, g_Conf->McdEnableNTFS ); NTFS_CompressFile( str, g_Conf->McdCompressNTFS );
if( !m_file[port][slot].Open( str.c_str(), L"r+b" ) ) if( !m_file[port][slot].Open( str.c_str(), L"r+b" ) )
{ {

View File

@ -64,7 +64,7 @@ BaseMessageBoxEvent::BaseMessageBoxEvent( const BaseMessageBoxEvent& event )
} }
// Thread Safety: Must be called from the GUI thread ONLY. // Thread Safety: Must be called from the GUI thread ONLY.
void BaseMessageBoxEvent::_DoInvoke() void BaseMessageBoxEvent::InvokeEvent()
{ {
int result = _DoDialog(); int result = _DoDialog();
if( m_state ) m_state->PostResult( result ); if( m_state ) m_state->PostResult( result );
@ -104,7 +104,7 @@ pxMessageBoxEvent::pxMessageBoxEvent( const pxMessageBoxEvent& event )
int pxMessageBoxEvent::_DoDialog() const int pxMessageBoxEvent::_DoDialog() const
{ {
return pxMessageDialog( m_Content, m_Title, m_Buttons ); return pxMessageDialog( m_Title, m_Content, m_Buttons );
} }
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -15,7 +15,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "ConfigurationPanels.h" #include "ConfigurationPanels.h"
#include "MemoryCardListView.h" #include "MemoryCardPanels.h"
#include <wx/filepicker.h> #include <wx/filepicker.h>
#include <wx/ffile.h> #include <wx/ffile.h>
@ -81,7 +81,7 @@ static int EnumerateMemoryCards( McdList& dest, const wxArrayString& files )
// ===================================================================================================== // =====================================================================================================
// MemoryCardListPanel // MemoryCardListPanel
// ===================================================================================================== // =====================================================================================================
MemoryCardListPanel::MemoryCardListPanel( wxWindow* parent ) Panels::MemoryCardListPanel::MemoryCardListPanel( wxWindow* parent )
: BaseSelectorPanel( parent ) : BaseSelectorPanel( parent )
{ {
m_FolderPicker = new DirPickerPanel( this, FolderId_MemoryCards, m_FolderPicker = new DirPickerPanel( this, FolderId_MemoryCards,
@ -113,18 +113,16 @@ MemoryCardListPanel::MemoryCardListPanel( wxWindow* parent )
*this += m_listview | pxExpand; *this += m_listview | pxExpand;
*this += s_buttons | pxExpand; *this += s_buttons | pxExpand;
m_listview->SetMinSize( wxSize( wxDefaultCoord, 120 ) ); m_listview->SetMinSize( wxSize( m_idealWidth, 120 ) );
AppStatusEvent_OnSettingsApplied();
Disable(); Disable();
} }
void MemoryCardListPanel::Apply() void Panels::MemoryCardListPanel::Apply()
{ {
} }
void MemoryCardListPanel::AppStatusEvent_OnSettingsApplied() void Panels::MemoryCardListPanel::AppStatusEvent_OnSettingsApplied()
{ {
} }
@ -139,7 +137,7 @@ bool Panels::MemoryCardListPanel::OnDropFiles(wxCoord x, wxCoord y, const wxArra
return false; return false;
} }
bool MemoryCardListPanel::ValidateEnumerationStatus() bool Panels::MemoryCardListPanel::ValidateEnumerationStatus()
{ {
bool validated = true; bool validated = true;
@ -163,7 +161,7 @@ bool MemoryCardListPanel::ValidateEnumerationStatus()
return validated; return validated;
} }
void MemoryCardListPanel::DoRefresh() void Panels::MemoryCardListPanel::DoRefresh()
{ {
if( !m_KnownCards ) return; if( !m_KnownCards ) return;

View File

@ -1,135 +0,0 @@
/* 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/>.
*/
#pragma once
#include "AppCommon.h"
#include <wx/dnd.h>
#include <wx/listctrl.h>
namespace Panels
{
// --------------------------------------------------------------------------------------
// McdListItem
// --------------------------------------------------------------------------------------
struct McdListItem
{
uint SizeInMB; // size, in megabytes!
bool IsFormatted;
wxDateTime DateCreated;
wxDateTime DateModified;
wxFileName Filename; // full pathname
int Port;
int Slot;
McdListItem()
{
Port = -1;
Slot = -1;
}
bool operator==( const McdListItem& right ) const
{
return
OpEqu(SizeInMB) && OpEqu(IsFormatted) &&
OpEqu(DateCreated) && OpEqu(DateModified) &&
OpEqu(Filename);
}
bool operator!=( const McdListItem& right ) const
{
return operator==( right );
}
};
typedef std::vector<McdListItem> McdList;
// --------------------------------------------------------------------------------------
// MemoryCardListView
// --------------------------------------------------------------------------------------
class MemoryCardListView : public wxListView
{
typedef wxListView _parent;
protected:
McdList* m_KnownCards;
public:
virtual ~MemoryCardListView() throw() { }
MemoryCardListView( wxWindow* parent );
virtual void OnListDrag(wxListEvent& evt);
virtual void CreateColumns();
virtual void AssignCardsList( McdList* knownCards, int length=0 );
protected:
// Overrides for wxLC_VIRTUAL
virtual wxString OnGetItemText(long item, long column) const;
virtual int OnGetItemImage(long item) const;
virtual int OnGetItemColumnImage(long item, long column) const;
virtual wxListItemAttr *OnGetItemAttr(long item) const;
};
// --------------------------------------------------------------------------------------
// MemoryCardListPanel
// --------------------------------------------------------------------------------------
class MemoryCardListPanel
: public BaseSelectorPanel
, public wxFileDropTarget
{
protected:
DirPickerPanel* m_FolderPicker;
MemoryCardListView* m_listview;
ScopedPtr<McdList> m_KnownCards;
public:
virtual ~MemoryCardListPanel() throw() {}
MemoryCardListPanel( wxWindow* parent );
protected:
virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames);
virtual void Apply();
virtual void AppStatusEvent_OnSettingsApplied();
virtual void DoRefresh();
virtual bool ValidateEnumerationStatus();
};
// --------------------------------------------------------------------------------------
// MemoryCardInfoPanel
// --------------------------------------------------------------------------------------
class MemoryCardInfoPanel : public BaseApplicableConfigPanel
{
protected:
uint m_port;
uint m_slot;
//wxStaticText* m_label;
wxTextCtrl* m_label;
public:
virtual ~MemoryCardInfoPanel() throw() {}
MemoryCardInfoPanel( wxWindow* parent, uint port, uint slot );
void Apply();
protected:
void AppStatusEvent_OnSettingsApplied();
void paintEvent( wxPaintEvent& evt );
};
};

View File

@ -0,0 +1,192 @@
/* 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/>.
*/
#pragma once
#include "AppCommon.h"
#include <wx/dnd.h>
#include <wx/listctrl.h>
// --------------------------------------------------------------------------------------
// McdListItem
// --------------------------------------------------------------------------------------
struct McdListItem
{
uint SizeInMB; // size, in megabytes!
bool IsFormatted;
wxDateTime DateCreated;
wxDateTime DateModified;
wxFileName Filename; // full pathname
int Port;
int Slot;
McdListItem()
{
Port = -1;
Slot = -1;
}
bool operator==( const McdListItem& right ) const
{
return
OpEqu(SizeInMB) && OpEqu(IsFormatted) &&
OpEqu(DateCreated) && OpEqu(DateModified) &&
OpEqu(Filename);
}
bool operator!=( const McdListItem& right ) const
{
return operator==( right );
}
};
typedef std::vector<McdListItem> McdList;
// --------------------------------------------------------------------------------------
// MemoryCardListView
// --------------------------------------------------------------------------------------
class MemoryCardListView : public wxListView
{
typedef wxListView _parent;
protected:
McdList* m_KnownCards;
public:
virtual ~MemoryCardListView() throw() { }
MemoryCardListView( wxWindow* parent );
virtual void OnListDrag(wxListEvent& evt);
virtual void CreateColumns();
virtual void AssignCardsList( McdList* knownCards, int length=0 );
protected:
// Overrides for wxLC_VIRTUAL
virtual wxString OnGetItemText(long item, long column) const;
virtual int OnGetItemImage(long item) const;
virtual int OnGetItemColumnImage(long item, long column) const;
virtual wxListItemAttr *OnGetItemAttr(long item) const;
};
namespace Panels
{
// --------------------------------------------------------------------------------------
// MemoryCardListPanel
// --------------------------------------------------------------------------------------
class MemoryCardListPanel
: public BaseSelectorPanel
, public wxFileDropTarget
{
protected:
DirPickerPanel* m_FolderPicker;
MemoryCardListView* m_listview;
ScopedPtr<McdList> m_KnownCards;
public:
virtual ~MemoryCardListPanel() throw() {}
MemoryCardListPanel( wxWindow* parent );
protected:
virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames);
virtual void Apply();
virtual void AppStatusEvent_OnSettingsApplied();
virtual void DoRefresh();
virtual bool ValidateEnumerationStatus();
};
// --------------------------------------------------------------------------------------
// MemoryCardInfoPanel
// --------------------------------------------------------------------------------------
class MemoryCardInfoPanel : public BaseApplicableConfigPanel
{
protected:
uint m_port;
uint m_slot;
wxFileName m_filename;
public:
virtual ~MemoryCardInfoPanel() throw() {}
MemoryCardInfoPanel( wxWindow* parent, uint port, uint slot );
void Apply();
protected:
void AppStatusEvent_OnSettingsApplied();
void paintEvent( wxPaintEvent& evt );
};
// --------------------------------------------------------------------------------------
// McdConfigPanel_Toggles / McdConfigPanel_Standard / McdConfigPanel_Multitap
// --------------------------------------------------------------------------------------
class McdConfigPanel_Toggles : public BaseApplicableConfigPanel
{
typedef BaseApplicableConfigPanel _parent;
protected:
pxCheckBox* m_check_Ejection;
#ifdef __WXMSW__
pxCheckBox* m_check_CompressNTFS;
#endif
public:
McdConfigPanel_Toggles( wxWindow* parent );
virtual ~McdConfigPanel_Toggles() throw() { }
void Apply();
protected:
void AppStatusEvent_OnSettingsApplied();
};
class McdConfigPanel_Standard : public BaseApplicableConfigPanel
{
typedef BaseApplicableConfigPanel _parent;
protected:
MemoryCardInfoPanel* m_panel_cardinfo[2];
public:
McdConfigPanel_Standard( wxWindow* parent );
virtual ~McdConfigPanel_Standard() throw() { }
void Apply();
protected:
void AppStatusEvent_OnSettingsApplied();
};
class McdConfigPanel_Multitap : public BaseApplicableConfigPanel
{
typedef BaseApplicableConfigPanel _parent;
protected:
int m_port;
pxCheckBox* m_check_Multitap;
public:
McdConfigPanel_Multitap( wxWindow* parent, int port=0 );
virtual ~McdConfigPanel_Multitap() throw() { }
void Apply();
protected:
void OnMultitapChecked( wxCommandEvent& evt );
void AppStatusEvent_OnSettingsApplied();
};
};

View File

@ -15,7 +15,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "ConfigurationPanels.h" #include "ConfigurationPanels.h"
#include "MemoryCardListView.h" #include "MemoryCardPanels.h"
#include "Dialogs/ConfigurationDialog.h" #include "Dialogs/ConfigurationDialog.h"
@ -81,7 +81,7 @@ MemoryCardListView::MemoryCardListView( wxWindow* parent )
Connect( wxEVT_COMMAND_LIST_BEGIN_DRAG, wxListEventHandler(MemoryCardListView::OnListDrag)); Connect( wxEVT_COMMAND_LIST_BEGIN_DRAG, wxListEventHandler(MemoryCardListView::OnListDrag));
} }
void Panels::MemoryCardListView::OnListDrag(wxListEvent& evt) void MemoryCardListView::OnListDrag(wxListEvent& evt)
{ {
evt.Skip(); evt.Skip();
@ -94,7 +94,7 @@ void Panels::MemoryCardListView::OnListDrag(wxListEvent& evt)
} }
// return the text for the given column of the given item // return the text for the given column of the given item
wxString Panels::MemoryCardListView::OnGetItemText(long item, long column) const wxString MemoryCardListView::OnGetItemText(long item, long column) const
{ {
if( m_KnownCards == NULL ) return _parent::OnGetItemText(item, column); if( m_KnownCards == NULL ) return _parent::OnGetItemText(item, column);
@ -123,19 +123,19 @@ wxString Panels::MemoryCardListView::OnGetItemText(long item, long column) const
// return the icon for the given item. In report view, OnGetItemImage will // return the icon for the given item. In report view, OnGetItemImage will
// only be called for the first column. See OnGetItemColumnImage for // only be called for the first column. See OnGetItemColumnImage for
// details. // details.
int Panels::MemoryCardListView::OnGetItemImage(long item) const int MemoryCardListView::OnGetItemImage(long item) const
{ {
return _parent::OnGetItemImage( item ); return _parent::OnGetItemImage( item );
} }
// return the icon for the given item and column. // return the icon for the given item and column.
int Panels::MemoryCardListView::OnGetItemColumnImage(long item, long column) const int MemoryCardListView::OnGetItemColumnImage(long item, long column) const
{ {
return _parent::OnGetItemColumnImage( item, column ); return _parent::OnGetItemColumnImage( item, column );
} }
// return the attribute for the item (may return NULL if none) // return the attribute for the item (may return NULL if none)
wxListItemAttr* Panels::MemoryCardListView::OnGetItemAttr(long item) const wxListItemAttr* MemoryCardListView::OnGetItemAttr(long item) const
{ {
wxListItemAttr* retval = _parent::OnGetItemAttr(item); wxListItemAttr* retval = _parent::OnGetItemAttr(item);
//const McdListItem& it( (*m_KnownCards)[item] ); //const McdListItem& it( (*m_KnownCards)[item] );
@ -155,34 +155,58 @@ MemoryCardInfoPanel::MemoryCardInfoPanel( wxWindow* parent, uint port, uint slot
SetMinSize( wxSize(128, 48) ); SetMinSize( wxSize(128, 48) );
Connect( wxEVT_PAINT, wxPaintEventHandler(MemoryCardInfoPanel::paintEvent) ); Connect( wxEVT_PAINT, wxPaintEventHandler(MemoryCardInfoPanel::paintEvent) );
}
AppStatusEvent_OnSettingsApplied(); static void DrawTextCentered( wxDC& dc, const wxString msg )
{
int tWidth, tHeight;
dc.GetTextExtent( msg, &tWidth, &tHeight );
dc.DrawText( msg, (dc.GetSize().GetWidth() - tWidth) / 2, 0 );
} }
void MemoryCardInfoPanel::paintEvent(wxPaintEvent & evt) void MemoryCardInfoPanel::paintEvent(wxPaintEvent & evt)
{ {
// Collect Info and Format Strings
wxString fname( m_filename.GetFullPath() );
if( fname.IsEmpty() ) fname = _("No Card (empty)");
// Create DC and plot some text (and images!)
wxPaintDC dc( this ); wxPaintDC dc( this );
wxFont normal( dc.GetFont() );
wxFont bold( normal );
normal.SetWeight( wxNORMAL );
bold.SetWeight( wxBOLD );
wxFont woot( dc.GetFont() ); dc.SetFont( bold );
woot.SetWeight( wxBOLD ); DrawTextCentered( dc, fname );
dc.SetFont( woot );
wxString msg;
msg = _("No Card (empty)");
int tWidth, tHeight;
dc.GetTextExtent( msg, &tWidth, &tHeight );
dc.DrawText( msg, (dc.GetSize().GetWidth() - tWidth) / 2, 0 );
//dc.DrawCircle( dc.GetSize().GetWidth()/2, 24, dc.GetSize().GetWidth()/4 ); //dc.DrawCircle( dc.GetSize().GetWidth()/2, 24, dc.GetSize().GetWidth()/4 );
} }
void MemoryCardInfoPanel::Apply() void MemoryCardInfoPanel::Apply()
{ {
if( m_filename.IsDir() )
{
throw Exception::CannotApplySettings( this,
wxLt("Cannot use or create memorycard: the filename is an existing directory."),
true
);
}
if( m_filename.FileExists() )
{
// TODO : Prompt user to create non-existing files. For now we just creat them implicitly.
}
g_Conf->Mcd[m_port][m_slot].Filename = m_filename;
} }
void MemoryCardInfoPanel::AppStatusEvent_OnSettingsApplied() void MemoryCardInfoPanel::AppStatusEvent_OnSettingsApplied()
{ {
m_filename = g_Conf->Mcd[m_port][m_slot].Filename;
Refresh();
} }
@ -194,73 +218,20 @@ Panels::MemoryCardsPanel::MemoryCardsPanel( wxWindow* parent )
{ {
m_panel_AllKnownCards = new MemoryCardListPanel( this ); m_panel_AllKnownCards = new MemoryCardListPanel( this );
m_idealWidth -= 48;
m_check_Ejection = new pxCheckBox( this,
_("Auto-eject memorycards when loading savestates"),
pxE( ".Dialog:Memorycards:EnableEjection",
L"Avoids memorycard corruption by forcing games to re-index card contents after "
L"loading from savestates. May not be compatible with all games (Guitar Hero)."
)
);
m_idealWidth += 48;
wxPanelWithHelpers* columns[2];
for( uint port=0; port<2; ++port ) for( uint port=0; port<2; ++port )
{ {
columns[port] = new wxPanelWithHelpers( this, wxVERTICAL );
columns[port]->SetIdealWidth( (columns[port]->GetIdealWidth()-12) / 2 );
/*m_check_Multitap[port] = new pxCheckBox( columns[port], wxsFormat(_("Enable Multitap on Port %u"), port+1) );
m_check_Multitap[port]->SetClientData( (void*) port );
m_check_Multitap[port]->SetFont( wxFont( m_check_Multitap[port]->GetFont().GetPointSize()+1, wxFONTFAMILY_MODERN, wxNORMAL, wxNORMAL, false, L"Lucida Console" ) );*/
for( uint slot=0; slot<1; ++slot ) for( uint slot=0; slot<1; ++slot )
{ {
m_panel_cardinfo[port][slot] = new MemoryCardInfoPanel( columns[port], port, slot ); m_panel_cardinfo[port][slot] = new MemoryCardInfoPanel( this, port, slot );
} }
//Connect( m_check_Multitap[port]->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(MemoryCardsPanel::OnMultitapChecked));
} }
// ------------------------------------ // ------------------------------------
// Sizer / Layout Section // Sizer / Layout Section
// ------------------------------------ // ------------------------------------
wxFlexGridSizer* s_table = new wxFlexGridSizer( 2 );
s_table->AddGrowableCol( 0 );
s_table->AddGrowableCol( 1 );
for( uint port=0; port<2; ++port )
{
wxStaticBoxSizer& portSizer( *new wxStaticBoxSizer( wxVERTICAL, columns[port], wxsFormat(_("Port %u"), port+1) ) );
*columns[port] += portSizer | SubGroup();
portSizer += m_panel_cardinfo[port][0] | SubGroup();
//portSizer += new wxStaticLine( columns[port] ) | pxExpand.Border( wxTOP, StdPadding );
//portSizer += m_check_Multitap[port] | pxCenter.Border( wxBOTTOM, StdPadding );
/*for( uint slot=1; slot<4; ++slot )
{
//portSizer += new wxStaticText( columns[port], wxID_ANY, wxsFormat(_("Slot #%u"), slot+1) ); // | pxCenter;
wxStaticBoxSizer& staticbox( *new wxStaticBoxSizer( wxVERTICAL, columns[port] ) );
staticbox += m_panel_cardinfo[port][slot] | pxExpand;
portSizer += staticbox | SubGroup();
}*/
*s_table += columns[port] | StdExpand();
}
wxBoxSizer& s_checks( *new wxBoxSizer( wxVERTICAL ) );
s_checks += m_check_Ejection;
*this += s_table | pxExpand;
*this += m_panel_AllKnownCards | StdExpand(); *this += m_panel_AllKnownCards | StdExpand();
*this += s_checks | StdExpand();
AppStatusEvent_OnSettingsApplied();
} }
void Panels::MemoryCardsPanel::OnMultitapChecked( wxCommandEvent& evt ) void Panels::MemoryCardsPanel::OnMultitapChecked( wxCommandEvent& evt )

View File

@ -14,16 +14,19 @@
*/ */
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "App.h"
#include "AppSaveStates.h"
#include "Plugins.h"
#include "Utilities/ScopedPtr.h"
#include "ConfigurationPanels.h"
#include "Dialogs/ModalPopups.h"
#include <wx/dynlib.h> #include <wx/dynlib.h>
#include <wx/dir.h> #include <wx/dir.h>
#include "App.h"
#include "AppSaveStates.h"
#include "Plugins.h"
#include "ConfigurationPanels.h"
#include "Dialogs/ModalPopups.h"
#include "Utilities/ThreadingDialogs.h"
// Allows us to force-disable threading for debugging/troubleshooting // Allows us to force-disable threading for debugging/troubleshooting
static const bool DisableThreading = static const bool DisableThreading =
#ifdef __LINUX__ #ifdef __LINUX__
@ -36,20 +39,20 @@ using namespace pxSizerFlags;
using namespace Threading; using namespace Threading;
BEGIN_DECLARE_EVENT_TYPES() BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE(pxEVT_EnumeratedNext, -1) DECLARE_EVENT_TYPE(pxEvt_EnumeratedNext, -1)
DECLARE_EVENT_TYPE(pxEvt_EnumerationFinished, -1) DECLARE_EVENT_TYPE(pxEvt_EnumerationFinished, -1)
DECLARE_EVENT_TYPE(pxEVT_ShowStatusBar, -1) DECLARE_EVENT_TYPE(pxEVT_ShowStatusBar, -1)
DECLARE_EVENT_TYPE(pxEvt_SysExecEventComplete, -1)
END_DECLARE_EVENT_TYPES() END_DECLARE_EVENT_TYPES()
DEFINE_EVENT_TYPE(pxEVT_EnumeratedNext) DEFINE_EVENT_TYPE(pxEvt_EnumeratedNext);
DEFINE_EVENT_TYPE(pxEvt_EnumerationFinished); DEFINE_EVENT_TYPE(pxEvt_EnumerationFinished);
DEFINE_EVENT_TYPE(pxEVT_ShowStatusBar); DEFINE_EVENT_TYPE(pxEVT_ShowStatusBar);
DEFINE_EVENT_TYPE(pxEvt_SysExecEventComplete)
typedef s32 (CALLBACK* TestFnptr)(); typedef s32 (CALLBACK* TestFnptr)();
typedef void (CALLBACK* ConfigureFnptr)(); typedef void (CALLBACK* ConfigureFnptr)();
static const wxString failed_separator( L"-------- Unsupported Plugins --------" );
namespace Exception namespace Exception
{ {
class NotEnumerablePlugin : public BadStream class NotEnumerablePlugin : public BadStream
@ -138,28 +141,20 @@ public:
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// ApplyPluginsDialog // ApplyPluginsDialog
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class ApplyPluginsDialog : public wxDialogWithHelpers class ApplyPluginsDialog : public WaitForTaskDialog
{ {
DECLARE_DYNAMIC_CLASS_NO_COPY(ApplyPluginsDialog) DECLARE_DYNAMIC_CLASS_NO_COPY(ApplyPluginsDialog)
typedef wxDialogWithHelpers _parent; typedef wxDialogWithHelpers _parent;
protected: protected:
BaseApplicableConfigPanel* m_panel; BaseApplicableConfigPanel* m_panel;
ScopedPtr<BaseException> m_Exception;
SynchronousActionState m_sync;
public: public:
ApplyPluginsDialog( BaseApplicableConfigPanel* panel=NULL ); ApplyPluginsDialog( BaseApplicableConfigPanel* panel=NULL );
virtual ~ApplyPluginsDialog() throw() {} virtual ~ApplyPluginsDialog() throw() {}
virtual void RethrowException();
virtual int ShowModal();
BaseApplicableConfigPanel* GetApplicableConfigPanel() const { return m_panel; } BaseApplicableConfigPanel* GetApplicableConfigPanel() const { return m_panel; }
protected:
void OnSysExecComplete( wxCommandEvent& evt );
}; };
@ -184,7 +179,7 @@ public:
virtual ApplyOverValidStateEvent *Clone() const { return new ApplyOverValidStateEvent(*this); } virtual ApplyOverValidStateEvent *Clone() const { return new ApplyOverValidStateEvent(*this); }
protected: protected:
void _DoInvoke(); void InvokeEvent();
}; };
@ -193,10 +188,14 @@ protected:
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class SysExecEvent_ApplyPlugins : public SysExecEvent class SysExecEvent_ApplyPlugins : public SysExecEvent
{ {
typedef SysExecEvent _parent;
protected: protected:
ApplyPluginsDialog* m_dialog; ApplyPluginsDialog* m_dialog;
public: public:
wxString GetEventName() const { return L"PluginSelectorPanel::ApplyPlugins"; }
virtual ~SysExecEvent_ApplyPlugins() throw() {} virtual ~SysExecEvent_ApplyPlugins() throw() {}
SysExecEvent_ApplyPlugins* Clone() const { return new SysExecEvent_ApplyPlugins( *this ); } SysExecEvent_ApplyPlugins* Clone() const { return new SysExecEvent_ApplyPlugins( *this ); }
@ -207,19 +206,27 @@ public:
} }
protected: protected:
void _DoInvoke(); void InvokeEvent();
void CleanupEvent();
void PostFinishToDialog();
}; };
IMPLEMENT_DYNAMIC_CLASS(ApplyPluginsDialog, wxDialogWithHelpers) // --------------------------------------------------------------------------------------
// ApplyPluginsDialog Implementations
// --------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(ApplyPluginsDialog, WaitForTaskDialog)
ApplyPluginsDialog::ApplyPluginsDialog( BaseApplicableConfigPanel* panel ) ApplyPluginsDialog::ApplyPluginsDialog( BaseApplicableConfigPanel* panel )
: wxDialogWithHelpers( NULL, _("Applying settings..."), wxVERTICAL ) : WaitForTaskDialog( _("Applying settings...") )
{ {
Connect( pxEvt_SysExecEventComplete, wxCommandEventHandler(ApplyPluginsDialog::OnSysExecComplete) );
GetSysExecutorThread().PostEvent( new SysExecEvent_ApplyPlugins( this, m_sync ) ); GetSysExecutorThread().PostEvent( new SysExecEvent_ApplyPlugins( this, m_sync ) );
} }
void ApplyOverValidStateEvent::_DoInvoke() // --------------------------------------------------------------------------------------
// ApplyOverValidStateEvent Implementations
// --------------------------------------------------------------------------------------
void ApplyOverValidStateEvent::InvokeEvent()
{ {
wxDialogWithHelpers dialog( m_owner, _("Shutdown PS2 virtual machine?"), wxVERTICAL ); wxDialogWithHelpers dialog( m_owner, _("Shutdown PS2 virtual machine?"), wxVERTICAL );
@ -237,7 +244,10 @@ void ApplyOverValidStateEvent::_DoInvoke()
throw Exception::CannotApplySettings( m_owner->GetApplicableConfigPanel(), "Cannot apply settings: canceled by user because plugins changed while the emulation state was active.", false ); throw Exception::CannotApplySettings( m_owner->GetApplicableConfigPanel(), "Cannot apply settings: canceled by user because plugins changed while the emulation state was active.", false );
} }
void SysExecEvent_ApplyPlugins::_DoInvoke() // --------------------------------------------------------------------------------------
// SysExecEvent_ApplyPlugins Implementations
// --------------------------------------------------------------------------------------
void SysExecEvent_ApplyPlugins::InvokeEvent()
{ {
ScopedCoreThreadPause paused_core; ScopedCoreThreadPause paused_core;
@ -261,36 +271,26 @@ void SysExecEvent_ApplyPlugins::_DoInvoke()
LoadPluginsImmediate(); LoadPluginsImmediate();
CoreThread.RecoverState(); CoreThread.RecoverState();
wxCommandEvent tevt( pxEvt_SysExecEventComplete ); PostFinishToDialog();
m_dialog->GetEventHandler()->AddPendingEvent( tevt );
closed_core.AllowResume(); closed_core.AllowResume();
paused_core.AllowResume(); paused_core.AllowResume();
} }
void SysExecEvent_ApplyPlugins::PostFinishToDialog()
void ApplyPluginsDialog::OnSysExecComplete( wxCommandEvent& evt )
{ {
evt.Skip(); if( !m_dialog ) return;
m_sync.WaitForResult(); wxCommandEvent tevt( pxEvt_ThreadedTaskComplete );
EndModal( wxID_OK ); m_dialog->GetEventHandler()->AddPendingEvent( tevt );
m_dialog = NULL;
} }
void ApplyPluginsDialog::RethrowException() void SysExecEvent_ApplyPlugins::CleanupEvent()
{ {
if( m_Exception ) m_Exception->Rethrow(); PostFinishToDialog();
_parent::CleanupEvent();
} }
int ApplyPluginsDialog::ShowModal()
{
int result = _parent::ShowModal();
RethrowException();
return result;
}
static const wxString failed_separator( L"-------- Unsupported Plugins --------" );
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// PluginSelectorPanel::StatusPanel implementations // PluginSelectorPanel::StatusPanel implementations
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -420,7 +420,7 @@ Panels::PluginSelectorPanel::PluginSelectorPanel( wxWindow* parent, int idealWid
//s_main.Add( refresh ); //s_main.Add( refresh );
//Connect( refresh->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PluginSelectorPanel::OnRefresh ) ); //Connect( refresh->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PluginSelectorPanel::OnRefresh ) );
Connect( pxEVT_EnumeratedNext, wxCommandEventHandler( PluginSelectorPanel::OnProgress ) ); Connect( pxEvt_EnumeratedNext, wxCommandEventHandler( PluginSelectorPanel::OnProgress ) );
Connect( pxEvt_EnumerationFinished, wxCommandEventHandler( PluginSelectorPanel::OnEnumComplete ) ); Connect( pxEvt_EnumerationFinished, wxCommandEventHandler( PluginSelectorPanel::OnEnumComplete ) );
Connect( pxEVT_ShowStatusBar, wxCommandEventHandler( PluginSelectorPanel::OnShowStatusBar ) ); Connect( pxEVT_ShowStatusBar, wxCommandEventHandler( PluginSelectorPanel::OnShowStatusBar ) );
Connect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( PluginSelectorPanel::OnPluginSelected ) ); Connect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( PluginSelectorPanel::OnPluginSelected ) );
@ -494,24 +494,9 @@ void Panels::PluginSelectorPanel::Apply()
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Plugin names are not up-to-date -- RELOAD! // Plugin names are not up-to-date -- RELOAD!
ApplyPluginsDialog applyDlg( this );
wxBoxSizer& paddedMsg( *new wxBoxSizer( wxHORIZONTAL ) );
paddedMsg += 24;
paddedMsg += applyDlg.Heading(_("Applying Settings..."));
paddedMsg += 24;
applyDlg += 12;
applyDlg += paddedMsg;
applyDlg += 12;
applyDlg += new wxButton( &applyDlg, wxID_CANCEL ) | pxCenter;
applyDlg += 6;
//applyDlg.GetEventHandler()->AddPendingEvent( );
try try
{ {
if( wxID_CANCEL == applyDlg.ShowModal() ) if( wxID_CANCEL == ApplyPluginsDialog( this ).ShowModal() )
throw Exception::CannotApplySettings( this, "User canceled plugin load process.", false ); throw Exception::CannotApplySettings( this, "User canceled plugin load process.", false );
} }
catch( Exception::PluginError& ex ) catch( Exception::PluginError& ex )
@ -794,7 +779,7 @@ void Panels::PluginSelectorPanel::EnumThread::DoNextPlugin( int curidx )
Console.Warning( ex.FormatDiagnosticMessage() ); Console.Warning( ex.FormatDiagnosticMessage() );
} }
wxCommandEvent yay( pxEVT_EnumeratedNext ); wxCommandEvent yay( pxEvt_EnumeratedNext );
yay.SetClientData( this ); yay.SetClientData( this );
yay.SetExtraLong( curidx ); yay.SetExtraLong( curidx );
m_master.GetEventHandler()->AddPendingEvent( yay ); m_master.GetEventHandler()->AddPendingEvent( yay );

View File

@ -40,16 +40,54 @@ bool States_isSlotUsed(int num)
return wxFileExists( SaveStateBase::GetFilename( num ) ); return wxFileExists( SaveStateBase::GetFilename( num ) );
} }
static volatile u32 IsSavingOrLoading = false;
class SysExecEvent_ClearSavingLoadingFlag : public SysExecEvent
{
public:
wxString GetEventName() const { return L"ClearSavingLoadingFlag"; }
virtual ~SysExecEvent_ClearSavingLoadingFlag() throw() { }
SysExecEvent_ClearSavingLoadingFlag()
{
}
SysExecEvent_ClearSavingLoadingFlag* Clone() const { return new SysExecEvent_ClearSavingLoadingFlag(); }
protected:
void InvokeEvent()
{
AtomicExchange(IsSavingOrLoading, false);
}
};
void States_FreezeCurrentSlot() void States_FreezeCurrentSlot()
{ {
if( AtomicExchange(IsSavingOrLoading, true) )
{
Console.WriteLn( "Load or save action is already pending." );
return;
}
GSchangeSaveState( StatesC, SaveStateBase::GetFilename( StatesC ).ToUTF8() ); GSchangeSaveState( StatesC, SaveStateBase::GetFilename( StatesC ).ToUTF8() );
StateCopy_SaveToSlot( StatesC ); StateCopy_SaveToSlot( StatesC );
GetSysExecutorThread().PostIdleEvent( SysExecEvent_ClearSavingLoadingFlag() );
} }
void States_DefrostCurrentSlot() void States_DefrostCurrentSlot()
{ {
if( AtomicExchange(IsSavingOrLoading, true) )
{
Console.WriteLn( "Load or save action is already pending." );
return;
}
GSchangeSaveState( StatesC, SaveStateBase::GetFilename( StatesC ).ToUTF8() ); GSchangeSaveState( StatesC, SaveStateBase::GetFilename( StatesC ).ToUTF8() );
StateCopy_LoadFromSlot( StatesC ); StateCopy_LoadFromSlot( StatesC );
GetSysExecutorThread().PostIdleEvent( SysExecEvent_ClearSavingLoadingFlag() );
//SysStatus( wxsFormat( _("Loaded State (slot %d)"), StatesC ) ); //SysStatus( wxsFormat( _("Loaded State (slot %d)"), StatesC ) );
} }

View File

@ -68,6 +68,8 @@ static void SaveStateFile_ReadHeader( IStreamReader& thr )
// //
class gzipReader : public IStreamReader class gzipReader : public IStreamReader
{ {
DeclareNoncopyableObject(gzipReader);
protected: protected:
wxString m_filename; wxString m_filename;
gzFile m_gzfp; gzFile m_gzfp;
@ -100,6 +102,9 @@ public:
} }
}; };
static bool IsSavingOrLoading = false;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// SysExecEvent_DownloadState // SysExecEvent_DownloadState
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -111,6 +116,8 @@ protected:
VmStateBuffer* m_dest_buffer; VmStateBuffer* m_dest_buffer;
public: public:
wxString GetEventName() const { return L"VM_Download"; }
virtual ~SysExecEvent_DownloadState() throw() {} virtual ~SysExecEvent_DownloadState() throw() {}
SysExecEvent_DownloadState* Clone() const { return new SysExecEvent_DownloadState( *this ); } SysExecEvent_DownloadState* Clone() const { return new SysExecEvent_DownloadState( *this ); }
SysExecEvent_DownloadState( VmStateBuffer* dest=&state_buffer ) SysExecEvent_DownloadState( VmStateBuffer* dest=&state_buffer )
@ -118,10 +125,11 @@ public:
m_dest_buffer = dest; m_dest_buffer = dest;
} }
bool IsCriticalEvent() const { return true; }
bool AllowCancelOnExit() const { return false; } bool AllowCancelOnExit() const { return false; }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
ScopedCoreThreadPause paused_core; ScopedCoreThreadPause paused_core;
@ -130,10 +138,61 @@ protected:
memSavingState(m_dest_buffer).FreezeAll(); memSavingState(m_dest_buffer).FreezeAll();
UI_EnableStateActions();
paused_core.AllowResume(); paused_core.AllowResume();
} }
}; };
// It's bad mojo to have savestates trying to read and write from the same file at the
// same time. To prevent that we use this mutex lock, which is used by both the
// CompressThread and the UnzipFromDisk events. (note that CompressThread locks the
// mutex during OnStartInThread, which ensures that the ZipToDisk event blocks; preventing
// the SysExecutor's Idle Event from re-enabing savestates and slots.)
//
static Mutex mtx_CompressToDisk;
// --------------------------------------------------------------------------------------
// CompressThread_VmState
// --------------------------------------------------------------------------------------
class VmStateZipThread : public CompressThread_gzip
{
typedef CompressThread_gzip _parent;
protected:
ScopedLock m_lock_Compress;
public:
VmStateZipThread( const wxString& file, VmStateBuffer* srcdata )
: _parent( file, srcdata, SaveStateFile_WriteHeader )
{
m_lock_Compress.Assign(mtx_CompressToDisk);
}
VmStateZipThread( const wxString& file, ScopedPtr<VmStateBuffer>& srcdata )
: _parent( file, srcdata, SaveStateFile_WriteHeader )
{
m_lock_Compress.Assign(mtx_CompressToDisk);
}
virtual ~VmStateZipThread() throw()
{
}
protected:
void OnStartInThread()
{
_parent::OnStartInThread();
m_lock_Compress.Acquire();
}
void OnCleanupInThread()
{
m_lock_Compress.Release();
_parent::OnCleanupInThread();
}
};
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// SysExecEvent_ZipToDisk // SysExecEvent_ZipToDisk
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -144,7 +203,7 @@ protected:
wxString m_filename; wxString m_filename;
public: public:
wxString GetEventName() const { return L"SysState_ZipToDisk"; } wxString GetEventName() const { return L"VM_ZipToDisk"; }
virtual ~SysExecEvent_ZipToDisk() throw() virtual ~SysExecEvent_ZipToDisk() throw()
{ {
@ -153,7 +212,6 @@ public:
SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); } SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); }
// Yep, gcc doesn't like >> again.
SysExecEvent_ZipToDisk( ScopedPtr<VmStateBuffer>& src, const wxString& filename ) SysExecEvent_ZipToDisk( ScopedPtr<VmStateBuffer>& src, const wxString& filename )
: m_filename( filename ) : m_filename( filename )
{ {
@ -170,9 +228,9 @@ public:
bool AllowCancelOnExit() const { return false; } bool AllowCancelOnExit() const { return false; }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
(new CompressThread_gzip( m_filename, m_src_buffer, SaveStateFile_WriteHeader ))->Start(); (new VmStateZipThread( m_filename, m_src_buffer ))->Start();
m_src_buffer = NULL; m_src_buffer = NULL;
} }
}; };
@ -188,25 +246,27 @@ class SysExecEvent_UnzipFromDisk : public SysExecEvent
{ {
protected: protected:
wxString m_filename; wxString m_filename;
gzipReader m_gzreader;
public: public:
wxString GetEventName() const { return L"SysState_UnzipFromDisk"; } wxString GetEventName() const { return L"VM_UnzipFromDisk"; }
virtual ~SysExecEvent_UnzipFromDisk() throw() {} virtual ~SysExecEvent_UnzipFromDisk() throw() {}
SysExecEvent_UnzipFromDisk* Clone() const { return new SysExecEvent_UnzipFromDisk( *this ); } SysExecEvent_UnzipFromDisk* Clone() const { return new SysExecEvent_UnzipFromDisk( *this ); }
SysExecEvent_UnzipFromDisk( const wxString& filename ) SysExecEvent_UnzipFromDisk( const wxString& filename )
: m_filename( filename ) : m_filename( filename )
, m_gzreader( filename )
{ {
SaveStateFile_ReadHeader( m_gzreader );
} }
wxString GetStreamName() const { return m_filename; } wxString GetStreamName() const { return m_filename; }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
ScopedLock lock( mtx_CompressToDisk );
gzipReader m_gzreader(m_filename );
SaveStateFile_ReadHeader( m_gzreader );
// We use direct Suspend/Resume control here, since it's desirable that emulation // We use direct Suspend/Resume control here, since it's desirable that emulation
// *ALWAYS* start execution after the new savestate is loaded. // *ALWAYS* start execution after the new savestate is loaded.
@ -263,6 +323,8 @@ void StateCopy_FreezeToMem()
void StateCopy_SaveToFile( const wxString& file ) void StateCopy_SaveToFile( const wxString& file )
{ {
UI_DisableStateActions();
ScopedPtr<VmStateBuffer> zipbuf(new VmStateBuffer( L"Zippable Savestate" )); ScopedPtr<VmStateBuffer> zipbuf(new VmStateBuffer( L"Zippable Savestate" ));
GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState( zipbuf )); GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState( zipbuf ));
GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk( zipbuf, file )); GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk( zipbuf, file ));

View File

@ -70,18 +70,34 @@ void UI_EnableSysShutdown()
void UI_DisableSysActions() void UI_DisableSysActions()
{ {
if( wxGetApp().PostMethodMyself( &UI_DisableSysShutdown ) ) return; if( wxGetApp().PostMethodMyself( &UI_DisableSysActions ) ) return;
sMainFrame.EnableMenuItem( MenuId_Sys_Restart, false ); sMainFrame.EnableMenuItem( MenuId_Sys_Restart, false );
sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, false ); sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, false );
_SaveLoadStuff( false );
} }
void UI_EnableSysActions() void UI_EnableSysActions()
{ {
if( wxGetApp().PostMethodMyself( &UI_EnableSysShutdown ) ) return; if( wxGetApp().PostMethodMyself( &UI_EnableSysActions ) ) return;
sMainFrame.EnableMenuItem( MenuId_Sys_Restart, true ); sMainFrame.EnableMenuItem( MenuId_Sys_Restart, true );
sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, true ); sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, true );
_SaveLoadStuff( true );
}
void UI_DisableStateActions()
{
if( wxGetApp().PostMethodMyself( &UI_DisableStateActions ) ) return;
_SaveLoadStuff( false );
}
void UI_EnableStateActions()
{
if( wxGetApp().PostMethodMyself( &UI_EnableStateActions ) ) return;
_SaveLoadStuff( true );
} }

View File

@ -23,9 +23,25 @@
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// SysExecEvent // SysExecEvent
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class SysExecEvent // Base class for all pxEvtHandler processable events.
: public IActionInvocation //
, public ICloneable // Rules for deriving:
// * Override InvokeEvent(), *NOT* _DoInvokeEvent(). _DoInvokeEvent() performs setup and
// wraps exceptions for transport to the invoking context/thread, and then itself calls
// InvokeEvent() to perform the derived class implementation.
//
// * Derived classes must implement their own versions of an empty constructor and
// Clone(), or else the class will fail to be copied to the event handler's thread
// context correctly.
//
// * This class is not abstract, and gives no error if the invocation method is not
// overridden: It can be used as a simple ping device against the event queue, Re-
// awaking the invoking thread as soon as the queue has caught up to and processed
// the event.
//
// * Avoid using virtual class inheritence. It's unreliable at best.
//
class SysExecEvent : public ICloneable
{ {
protected: protected:
SynchronousActionState* m_sync; SynchronousActionState* m_sync;
@ -63,11 +79,11 @@ public:
// data. // data.
virtual bool AllowCancelOnExit() const { return true; } virtual bool AllowCancelOnExit() const { return true; }
virtual void InvokeAction(); virtual void _DoInvokeEvent();
virtual void PostResult() const; virtual void PostResult() const;
virtual wxString GetEventName() const { return wxEmptyString; } virtual wxString GetEventName() const;
virtual wxString GetEventMessage() const { return wxEmptyString; } virtual wxString GetEventMessage() const;
virtual int GetResult() virtual int GetResult()
{ {
@ -80,28 +96,31 @@ public:
void SetException( const BaseException& ex ); void SetException( const BaseException& ex );
protected: protected:
virtual void _DoInvoke(); virtual void InvokeEvent();
virtual void CleanupEvent();
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// SysExecEvent_Method // SysExecEvent_MethodVoid
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class SysExecEvent_Method : public SysExecEvent class SysExecEvent_MethodVoid : public SysExecEvent
{ {
protected: protected:
FnType_Void* m_method; FnType_Void* m_method;
public: public:
virtual ~SysExecEvent_Method() throw() {} wxString GetEventName() const { return L"MethodVoid"; }
SysExecEvent_Method* Clone() const { return new SysExecEvent_Method( *this ); }
explicit SysExecEvent_Method( FnType_Void* method = NULL ) virtual ~SysExecEvent_MethodVoid() throw() {}
SysExecEvent_MethodVoid* Clone() const { return new SysExecEvent_MethodVoid( *this ); }
explicit SysExecEvent_MethodVoid( FnType_Void* method = NULL )
{ {
m_method = method; m_method = method;
} }
protected: protected:
void _DoInvoke() void InvokeEvent()
{ {
if( m_method ) m_method(); if( m_method ) m_method();
} }
@ -113,18 +132,39 @@ typedef std::list<SysExecEvent*> pxEvtList;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// pxEvtHandler // pxEvtHandler
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// wxWidgets Event Queue (wxEvtHandler) isn't thread-safe (uses static vars and checks/modifies wxApp globals // Purpose: To provide a safe environment for queuing tasks that must be executed in
// while processing), so it's useless to us. Have to roll our own. -_- // sequential order (in blocking fashion). Unlike the wxWidgets event handlers, instances
// of this handler can be stalled for extended periods of time without affecting the
// responsiveness of the GUI or frame updates of the DirectX output windows. This class
// is mostly intended to be used from the context of an ExecutorThread.
//
// Rationales:
// * Using the main event handler of wxWidgets is dangerous because it must call itself
// recursively when waiting on threaded events such as semaphore and mutexes. Thus,
// tasks such as suspending the VM would invoke the event queue while waiting,
// running events that expect the suspend to be complete while the suspend was still
// pending.
//
// * wxWidgets Event Queue (wxEvtHandler) isn't thread-safe and isn't even
// intended for use for anything other than wxWindow events (it uses static vars
// and checks/modifies wxApp globals while processing), so it's useless to us.
// Have to roll our own. -_-
// //
class pxEvtHandler class pxEvtHandler
{ {
protected: protected:
pxEvtList m_pendingEvents; pxEvtList m_pendingEvents;
pxEvtList m_idleEvents;
Threading::MutexRecursive m_mtx_pending; Threading::MutexRecursive m_mtx_pending;
Threading::Semaphore m_wakeup; Threading::Semaphore m_wakeup;
wxThreadIdType m_OwnerThreadId; wxThreadIdType m_OwnerThreadId;
volatile u32 m_Quitting; volatile u32 m_Quitting;
// Used for performance measuring the execution of individual events,
// and also for detecting deadlocks during message processing.
volatile u64 m_qpc_Start;
public: public:
pxEvtHandler(); pxEvtHandler();
virtual ~pxEvtHandler() throw() {} virtual ~pxEvtHandler() throw() {}
@ -134,27 +174,33 @@ public:
virtual void ShutdownQueue(); virtual void ShutdownQueue();
bool IsShuttingDown() const { return !!m_Quitting; } bool IsShuttingDown() const { return !!m_Quitting; }
void ProcessEvents( pxEvtList& list );
void ProcessPendingEvents(); void ProcessPendingEvents();
void ProcessIdleEvents();
void Idle();
void AddPendingEvent( SysExecEvent& evt ); void AddPendingEvent( SysExecEvent& evt );
void PostEvent( SysExecEvent* evt ); void PostEvent( SysExecEvent* evt );
void PostEvent( const SysExecEvent& evt ); void PostEvent( const SysExecEvent& evt );
void PostIdleEvent( SysExecEvent* evt );
void PostIdleEvent( const SysExecEvent& evt );
void ProcessEvent( SysExecEvent* evt ); void ProcessEvent( SysExecEvent* evt );
void ProcessEvent( SysExecEvent& evt ); void ProcessEvent( SysExecEvent& evt );
bool SelfProcessMethod( FnType_Void* method );
void Idle(); bool ProcessMethodSelf( FnType_Void* method );
void SetActiveThread(); void SetActiveThread();
protected: protected:
virtual void DoIdle() {} virtual void _DoIdle() {}
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// ExecutorThread // ExecutorThread
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Threaded wrapper class for implementing pxEvtHandler. Simply create the desired
// EvtHandler, start the thread, and enjoy queued event execution in fully blocking fashion.
//
class ExecutorThread : public Threading::PersistentThread class ExecutorThread : public Threading::PersistentThread
{ {
typedef Threading::PersistentThread _parent; typedef Threading::PersistentThread _parent;
@ -172,12 +218,15 @@ public:
void PostEvent( SysExecEvent* evt ); void PostEvent( SysExecEvent* evt );
void PostEvent( const SysExecEvent& evt ); void PostEvent( const SysExecEvent& evt );
void PostIdleEvent( SysExecEvent* evt );
void PostIdleEvent( const SysExecEvent& evt );
void ProcessEvent( SysExecEvent* evt ); void ProcessEvent( SysExecEvent* evt );
void ProcessEvent( SysExecEvent& evt ); void ProcessEvent( SysExecEvent& evt );
bool SelfProcessMethod( void (*evt)() ) bool ProcessMethodSelf( void (*evt)() )
{ {
return m_EvtHandler ? m_EvtHandler->SelfProcessMethod( evt ) : false; return m_EvtHandler ? m_EvtHandler->ProcessMethodSelf( evt ) : false;
} }
protected: protected:

View File

@ -2059,6 +2059,10 @@
RelativePath="..\..\gui\Dialogs\LogOptionsDialog.h" RelativePath="..\..\gui\Dialogs\LogOptionsDialog.h"
> >
</File> </File>
<File
RelativePath="..\..\gui\Dialogs\McdConfigDialog.cpp"
>
</File>
<File <File
RelativePath="..\..\gui\Dialogs\ModalPopups.h" RelativePath="..\..\gui\Dialogs\ModalPopups.h"
> >
@ -2610,7 +2614,7 @@
> >
</File> </File>
<File <File
RelativePath="..\..\gui\Panels\MemoryCardListView.h" RelativePath="..\..\gui\Panels\MemoryCardPanels.h"
> >
</File> </File>
<File <File