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

View File

@ -430,6 +430,10 @@
RelativePath="..\..\src\Utilities\Semaphore.cpp"
>
</File>
<File
RelativePath="..\..\src\Utilities\ThreadingDialogs.cpp"
>
</File>
<File
RelativePath="..\..\src\Utilities\ThreadingInternal.h"
>
@ -568,6 +572,10 @@
RelativePath="..\..\include\Utilities\Threading.h"
>
</File>
<File
RelativePath="..\..\include\Utilities\ThreadingDialogs.h"
>
</File>
<File
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
// 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 ) \
virtual ~classname() throw() {} \
virtual void Rethrow() const { throw *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_EXCEPTION_COPYTORS( classname ) \
\

View File

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

View File

@ -315,6 +315,7 @@ public:
void Init();
void AddOkCancel( wxSizer& sizer, bool hasApply=false );
void AddOkCancel( wxSizer* sizer=NULL, bool hasApply=false );
virtual void SmartCenterFit();
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
// and messages *if* the lock is performed from the main GUI thread.
//
// Exceptions:
// ThreadDeadlock - See description of ThreadDeadlock for details
//
void Threading::Mutex::Acquire()
{
#if wxUSE_GUI
@ -151,23 +148,22 @@ void Threading::Mutex::Acquire()
{
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
{
//ScopedBusyCursor hourglass( Cursor_KindaBusy );
while( !AcquireWithoutYield(def_yieldgui_interval) )
wxTheApp->Yield( true );
YieldToMain();
}
#else
pthread_mutex_lock( &m_mutex );
#endif
}
// Exceptions:
// ThreadDeadlock - See description of ThreadDeadlock for details
//
bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
{
#if wxUSE_GUI
@ -175,19 +171,19 @@ bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
{
return AcquireWithoutYield(timeout);
}
else if( _WaitGui_RecursionGuard( "Mutex::Acquire(timeout)" ) )
else if( _WaitGui_RecursionGuard( L"Mutex::TimedAcquire" ) )
{
ScopedBusyCursor hourglass( Cursor_ReallyBusy );
return AcquireWithoutYield( timeout );
}
else
{
ScopedBusyCursor hourglass( Cursor_KindaBusy );
//ScopedBusyCursor hourglass( Cursor_KindaBusy );
wxTimeSpan countdown( (timeout) );
do {
if( AcquireWithoutYield( def_yieldgui_interval ) ) break;
wxTheApp->Yield(true);
YieldToMain();
countdown -= def_yieldgui_interval;
} while( countdown.GetMilliseconds() > 0 );
@ -206,9 +202,6 @@ bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
//
// Implemented internally as a simple Acquire/Release pair.
//
// Exceptions:
// ThreadDeadlock - See description of ThreadDeadlock for details
//
void Threading::Mutex::Wait()
{
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
// and the mutex is still locked by another thread.
//
// Exceptions:
// ThreadDeadlock - See description of ThreadDeadlock for details
//
bool Threading::Mutex::Wait( const wxTimeSpan& timeout )
{
if( Acquire(timeout) )
@ -285,6 +275,16 @@ void Threading::ScopedLock::AssignAndLock( const Mutex* locker )
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.
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
// called from another thread, no message pumping is performed.
//
// Exceptions:
// ThreadDeadlock - See description of ThreadDeadlock for details
//
void Threading::Semaphore::Wait()
{
#if wxUSE_GUI
@ -89,16 +86,16 @@ void Threading::Semaphore::Wait()
{
sem_wait( &m_sema );
}
else if( _WaitGui_RecursionGuard( "Semaphore::Wait" ) )
else if( _WaitGui_RecursionGuard( L"Semaphore::Wait" ) )
{
ScopedBusyCursor hourglass( Cursor_ReallyBusy );
WaitWithoutYield();
sem_wait( &m_sema );
}
else
{
ScopedBusyCursor hourglass( Cursor_KindaBusy );
//ScopedBusyCursor hourglass( Cursor_KindaBusy );
while( !WaitWithoutYield( def_yieldgui_interval ) )
wxTheApp->Yield( true );
YieldToMain();
}
#else
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
// reached prior to timeout.
//
// Exceptions:
// ThreadDeadlock - See description of ThreadDeadlock for details
//
bool Threading::Semaphore::Wait( const wxTimeSpan& timeout )
{
#if wxUSE_GUI
@ -124,19 +118,19 @@ bool Threading::Semaphore::Wait( const wxTimeSpan& timeout )
{
return WaitWithoutYield( timeout );
}
else if( _WaitGui_RecursionGuard( "Semaphore::Wait(timeout)" ) )
else if( _WaitGui_RecursionGuard( L"Semaphore::TimedWait" ) )
{
ScopedBusyCursor hourglass( Cursor_ReallyBusy );
return WaitWithoutYield( timeout );
}
else
{
ScopedBusyCursor hourglass( Cursor_KindaBusy );
//ScopedBusyCursor hourglass( Cursor_KindaBusy );
wxTimeSpan countdown( (timeout) );
do {
if( WaitWithoutYield( def_yieldgui_interval ) ) break;
wxTheApp->Yield(true);
YieldToMain();
countdown -= def_yieldgui_interval;
} while( countdown.GetMilliseconds() > 0 );

View File

@ -125,8 +125,10 @@ void Threading::pxYield( int ms )
// (intended for internal use only)
// Returns true if the Wait is recursive, or false if the Wait is safe and should be
// handled via normal yielding methods.
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.
// 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.
@ -134,12 +136,11 @@ bool Threading::_WaitGui_RecursionGuard( const char* guardname )
static int __Guard = 0;
RecursionGuard guard( __Guard );
if( guard.IsReentrant() )
{
Console.WriteLn( "(Thread Log) Possible yield recursion detected in %s; performing blocking wait.", guardname );
return true;
}
return false;
//if( pxAssertDev( !guard.IsReentrant(), "Recursion during UI-bound threading wait object." ) ) return false;
if( !guard.IsReentrant() ) return false;
Console.WriteLn( "(Thread:%s) Yield recursion in %s; opening modal dialog.", pxGetCurrentThreadName().c_str(), name );
return true;
}
__forceinline void Threading::Timeslice()
@ -383,11 +384,26 @@ void Threading::PersistentThread::RethrowException() const
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
{
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
);
RethrowException();
@ -400,6 +416,13 @@ void Threading::PersistentThread::_selfRunningTest( const wxChar* name ) const
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
@ -666,7 +689,7 @@ void Threading::BaseTaskThread::WaitForResult()
{
if( m_detached || !m_running ) return;
if( m_TaskPending )
#ifdef wxUSE_GUI
#if wxUSE_GUI
m_post_TaskComplete.Wait();
#else
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 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 "wxAppWithHelpers.h"
#include "ThreadingInternal.h"
#include "PersistentThread.h"
DEFINE_EVENT_TYPE( pxEvt_DeleteObject );
@ -35,6 +36,47 @@ void BaseDeletableObject::DoDeletion()
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
// --------------------------------------------------------------------------------------
@ -181,7 +223,7 @@ public:
}
protected:
void _DoInvoke()
void InvokeEvent()
{
if( m_Method ) m_Method();
}
@ -197,7 +239,7 @@ pxExceptionEvent::pxExceptionEvent( const BaseException& ex )
m_except = ex.Clone();
}
void pxExceptionEvent::_DoInvoke()
void pxExceptionEvent::InvokeEvent()
{
ScopedPtr<BaseException> deleteMe( m_except );
if( deleteMe ) deleteMe->Rethrow();
@ -322,12 +364,12 @@ void wxAppWithHelpers::CleanUp()
_parent::CleanUp();
}
void pxInvokeActionEvent::InvokeAction()
void pxInvokeActionEvent::_DoInvokeEvent()
{
AffinityAssert_AllowFrom_MainUI();
try {
_DoInvoke();
InvokeEvent();
}
catch( BaseException& ex )
{
@ -387,10 +429,21 @@ void wxAppWithHelpers::IdleEventDispatcher( const char* action )
DbgCon.WriteLn( Color_Gray, "App IdleQueue (%s) -> %u events.", action, size );
std::vector<wxEvent*> postponed;
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 )
@ -464,7 +517,7 @@ void wxAppWithHelpers::ProcessAction( pxInvokeActionEvent& evt )
sync.WaitForResult();
}
else
evt.InvokeAction();
evt._DoInvokeEvent();
}
@ -508,7 +561,7 @@ bool wxAppWithHelpers::OnInit()
void wxAppWithHelpers::OnInvokeAction( pxInvokeActionEvent& evt )
{
evt.InvokeAction(); // wow this is easy!
evt._DoInvokeEvent(); // wow this is easy!
}
void wxAppWithHelpers::OnDeleteObject( wxCommandEvent& evt )

View File

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

View File

@ -377,6 +377,7 @@
<Unit filename="../gui/Dialogs/ImportSettingsDialog.cpp" />
<Unit filename="../gui/Dialogs/LogOptionsDialog.cpp" />
<Unit filename="../gui/Dialogs/LogOptionsDialog.h" />
<Unit filename="../gui/Dialogs/McdConfigDialog.cpp" />
<Unit filename="../gui/Dialogs/ModalPopups.h" />
<Unit filename="../gui/Dialogs/PickUserModeDialog.cpp" />
<Unit filename="../gui/Dialogs/StuckThreadDialog.cpp" />
@ -408,7 +409,7 @@
<Unit filename="../gui/Panels/LogOptionsPanels.cpp" />
<Unit filename="../gui/Panels/LogOptionsPanels.h" />
<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/MiscPanelStuff.cpp" />
<Unit filename="../gui/Panels/PathsPanel.cpp" />

View File

@ -86,7 +86,7 @@ namespace Exception
class PluginLoadError : public virtual PluginError, public virtual BadStream
{
public:
DEFINE_EXCEPTION_COPYTORS( PluginLoadError )
DEFINE_EXCEPTION_COPYTORS_COVARIANT( PluginLoadError )
PluginLoadError( PluginsEnum_t pid, const wxString& objname, const char* eng );
@ -103,7 +103,7 @@ namespace Exception
class PluginInitError : public virtual PluginError
{
public:
DEFINE_EXCEPTION_COPYTORS( PluginInitError )
DEFINE_EXCEPTION_COPYTORS_COVARIANT( PluginInitError )
explicit PluginInitError( PluginsEnum_t pid,
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
{
public:
DEFINE_EXCEPTION_COPYTORS( PluginOpenError )
DEFINE_EXCEPTION_COPYTORS_COVARIANT( PluginOpenError )
explicit PluginOpenError( PluginsEnum_t pid,
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
// 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 )
{
if( IsSelf() || !IsRunning() ) return;

View File

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

View File

@ -20,6 +20,17 @@
#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 )
: BaseCompressThread( file, srcdata, writeheader )
{
@ -35,6 +46,8 @@ CompressThread_gzip::CompressThread_gzip( const wxString& file, ScopedPtr< SafeA
CompressThread_gzip::~CompressThread_gzip() throw()
{
_parent::Cancel();
if( m_gzfp ) gzclose( m_gzfp );
}
@ -46,11 +59,21 @@ void CompressThread_gzip::Write( const void* data, size_t size )
void CompressThread_gzip::ExecuteTaskInThread()
{
// TODO : Add an API to PersistentThread for this! :) --air
//SetThreadPriority( THREAD_PRIORITY_BELOW_NORMAL );
if( !m_src_buffer ) return;
SetPendingSave();
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 );
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 )
throw Exception::BadStream( m_filename );
curidx += thisBlockSize;
Yield( 3 );
Yield( 2 );
} 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." );
}
void CompressThread_gzip::OnCleanupInThread()
{
_parent::OnCleanupInThread();
wxGetApp().DeleteThread( this );
}

View File

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

View File

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

View File

@ -344,6 +344,7 @@ wxString AppConfig::FullpathToMcd( uint port, uint slot ) const
AppConfig::AppConfig()
: MainGuiPosition( wxDefaultPosition )
, SysSettingsTabName( L"Cpu" )
, McdSettingsTabName( L"Standard" )
, AppSettingsTabName( L"GS Window" )
, DeskTheme( L"default" )
{
@ -353,7 +354,8 @@ AppConfig::AppConfig()
Toolbar_ImageSize = 24;
Toolbar_ShowLabels = true;
McdEnableNTFS = true;
McdCompressNTFS = true;
McdEnableEjection = true;
EnableSpeedHacks = false;
EnableGameFixes = false;
@ -423,6 +425,7 @@ void AppConfig::LoadSaveRootItems( IniInterface& ini )
IniEntry( MainGuiPosition );
IniEntry( SysSettingsTabName );
IniEntry( McdSettingsTabName );
IniEntry( AppSettingsTabName );
ini.EnumEntry( L"LanguageId", LanguageId, NULL, defaults.LanguageId );
IniEntry( RecentIsoCount );
@ -436,6 +439,9 @@ void AppConfig::LoadSaveRootItems( IniInterface& ini )
IniEntry( EnableSpeedHacks );
IniEntry( EnableGameFixes );
IniEntry( McdCompressNTFS );
IniEntry( McdEnableEjection );
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
// by it's UTF/ASCII name).
wxString SysSettingsTabName;
wxString McdSettingsTabName;
wxString AppSettingsTabName;
// Current language in use (correlates to a wxWidgets wxLANGUAGE specifier)
@ -204,8 +205,13 @@ public:
// Enables display of toolbar text labels.
bool Toolbar_ShowLabels;
// enables automatic ntfs compression of memory cards (Win32 only)
bool McdEnableNTFS;
#ifdef __WXMSW__
// 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.
// (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; }
protected:
void _DoInvoke()
void InvokeEvent()
{
sApp.DispatchEvent( m_evt );
}
@ -154,7 +154,7 @@ public:
}
protected:
void _DoInvoke()
void InvokeEvent()
{
if( m_method ) (CorePlugins.*m_method)();
}
@ -257,7 +257,7 @@ public:
}
protected:
void _DoInvoke()
void InvokeEvent()
{
CorePlugins.Load( m_folders );
}
@ -337,13 +337,15 @@ void ScopedCoreThreadClose::LoadPlugins()
class SysExecEvent_UnloadPlugins : public SysExecEvent
{
public:
wxString GetEventName() const { return L"UnloadPlugins"; }
virtual ~SysExecEvent_UnloadPlugins() throw() {}
SysExecEvent_UnloadPlugins* Clone() const { return new SysExecEvent_UnloadPlugins(*this); }
virtual bool AllowCancelOnExit() const { return false; }
virtual bool IsCriticalEvent() const { return true; }
void _DoInvoke()
void InvokeEvent()
{
CoreThread.Cancel();
CorePlugins.Unload();
@ -353,13 +355,15 @@ public:
class SysExecEvent_ShutdownPlugins : public SysExecEvent
{
public:
wxString GetEventName() const { return L"ShutdownPlugins"; }
virtual ~SysExecEvent_ShutdownPlugins() throw() {}
SysExecEvent_ShutdownPlugins* Clone() const { return new SysExecEvent_ShutdownPlugins(*this); }
virtual bool AllowCancelOnExit() const { return false; }
virtual bool IsCriticalEvent() const { return true; }
void _DoInvoke()
void InvokeEvent()
{
CoreThread.Cancel();
CorePlugins.Shutdown();

View File

@ -17,6 +17,8 @@
#include "App.h"
#include "AppSaveStates.h"
#include "Utilities/TlsVariable.inl"
#include "ps2/BiosTools.h"
#include "GS.h"
@ -78,7 +80,7 @@ public:
}
protected:
void _DoInvoke()
void InvokeEvent()
{
if( m_method ) (CoreThread.*m_method)();
}
@ -99,7 +101,7 @@ static void _Suspend()
void AppCoreThread::Suspend( bool isBlocking )
{
if( !GetSysExecutorThread().SelfProcessMethod( _Suspend ) )
if( !GetSysExecutorThread().ProcessMethodSelf( _Suspend ) )
_parent::Suspend(true);
}
@ -107,7 +109,13 @@ static int resume_tries = 0;
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( !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:
virtual ~SysExecEvent_FullStop() throw() {}
SysExecEvent_FullStop* Clone() const
wxString GetEventName() const { return L"CloseCoreThread"; }
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 ) { }
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 ) { }
protected:
void _DoInvoke()
void InvokeEvent()
{
ScopedCoreThreadClose 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:
virtual ~SysExecEvent_Pause() throw() {}
SysExecEvent_Pause* Clone() const
wxString GetEventName() const { return L"PauseCoreThread"; }
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 ) { }
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 ) { }
protected:
void _DoInvoke()
void InvokeEvent()
{
ScopedCoreThreadPause paused_core;
_post_and_wait(paused_core);
@ -384,8 +396,8 @@ protected:
// ScopedCoreThreadClose / ScopedCoreThreadPause
// --------------------------------------------------------------------------------------
static __threadlocal bool ScopedCore_IsPaused = false;
static __threadlocal bool ScopedCore_IsFullyClosed = false;
static DeclareTls(bool) ScopedCore_IsPaused = false;
static DeclareTls(bool) ScopedCore_IsFullyClosed = false;
BaseScopedCoreThread::BaseScopedCoreThread()
{
@ -440,7 +452,7 @@ ScopedCoreThreadClose::ScopedCoreThreadClose()
{
//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.RethrowException();
}
@ -470,7 +482,7 @@ ScopedCoreThreadPause::ScopedCoreThreadPause()
{
//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.RethrowException();
}

View File

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

View File

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

View File

@ -562,7 +562,7 @@ public:
protected:
// 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.
void DoIdle()
void _DoIdle()
{
UI_UpdateSysControls();
}

View File

@ -47,94 +47,106 @@ bool UseDefaultSettingsFolder = true;
ScopedPtr<AppConfig> g_Conf;
ConfigOverrides OverrideOptions;
class NamedDialogBoxEvent : public BaseMessageBoxEvent
template<typename DialogType>
int AppOpenModalDialog( wxWindow* parent=NULL )
{
typedef BaseMessageBoxEvent _parent;
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
if( wxWindow* window = wxFindWindowByName( DialogType::GetNameStatic() ) )
{
const wxString& dlgName( m_Content );
if( dlgName.IsEmpty() ) return wxID_CANCEL;
if( wxWindow* window = wxFindWindowByName( dlgName ) )
window->SetFocus();
if( wxDialog* dialog = wxDynamicCast( window, wxDialog ) )
{
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();
// It's legal to call ShowModal on a non-modal dialog, therefore making
// it modal in nature for the needs of whatever other thread of action wants
// to block against it:
if( !dialog->IsModal() )
{
int result = dialog->ShowModal();
dialog->Destroy();
return result;
}
int result = dialog->ShowModal();
dialog->Destroy();
return result;
}
}
else
{
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();
}
pxFailDev( "Can only show wxDialog class windows as modal!" );
return wxID_CANCEL;
}
};
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 );
else
return DialogType( parent ).ShowModal();
}
static bool HandlePluginError( Exception::PluginError& ex )
static bool HandlePluginError( Exception::BaseException& ex )
{
if( pxDialogExists( L"CoreSettings" ) ) return true;
bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() +
_("\n\nPress Ok to go to the Plugin Configuration Panel.")
);
if( result )
if( !pxDialogExists( L"CoreSettings" ) )
{
g_Conf->SysSettingsTabName = L"Plugins";
// fixme: Send a message to the panel to select the failed plugin.
if( IssueDialogAsModal( Dialogs::SysConfigDialog::GetNameStatic() ) == wxID_CANCEL )
return false;
if( !Msgbox::OkCancel( ex.FormatDisplayMessage() +
_("\n\nPress Ok to go to the Plugin Configuration Panel.")
) )
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.
@ -194,7 +206,7 @@ public:
}
protected:
void _DoInvoke()
void InvokeEvent()
{
if( m_Method ) (wxGetApp().*m_Method)();
}
@ -324,25 +336,6 @@ void Pcsx2App::LogicalVsync()
// 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 );
void Pcsx2App::OnEmuKeyDown( wxKeyEvent& evt )
@ -403,10 +396,8 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
if( dialog.ShowModal() == wxID_CANCEL )
Console.Warning( "User denied option to re-configure BIOS." );
if( IssueDialogAsModal( Dialogs::BiosSelectorDialog::GetNameStatic() ) != wxID_CANCEL )
{
if( AppOpenModalDialog<Dialogs::BiosSelectorDialog>() != wxID_CANCEL )
SysExecute();
}
else
Console.Warning( "User canceled BIOS configuration." );
}
@ -418,27 +409,20 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
StateCopy_Clear();
CoreThread.Resume();
}
// ----------------------------------------------------------------------------
catch( Exception::PluginInitError& ex )
{
CorePlugins.Shutdown();
ShutdownPlugins();
Console.Error( ex.FormatDiagnosticMessage() );
if( !HandlePluginError( 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.") );
}
AddIdleEvent( PluginInitErrorEvent(ex) );
}
catch( Exception::PluginError& ex )
{
CorePlugins.Close();
UnloadPlugins();
Console.Error( ex.FormatDiagnosticMessage() );
if( !HandlePluginError( ex ) )
{
Console.Error( L"User-canceled plugin configuration; Plugins not loaded!" );
Msgbox::Alert( _("Warning! System plugins have not been loaded. PCSX2 may be inoperable.") );
}
AddIdleEvent( PluginErrorEvent(ex) );
}
// ----------------------------------------------------------------------------
#if 0
@ -501,7 +485,7 @@ void Pcsx2App::StartPendingSave()
void Pcsx2App::ClearPendingSave()
{
if( PostAppMethodMyself(&Pcsx2App::StartPendingSave) ) return;
if( PostAppMethodMyself(&Pcsx2App::ClearPendingSave) ) return;
--m_PendingSaves;
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 )
{
// 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..." );
sApp.SetExitOnFrameDelete( false );
SetExitOnFrameDelete( false );
m_ScheduledTermination = true;
return;
}
@ -600,7 +589,7 @@ void AppApplySettings( const AppConfig* oldconf )
// Update the compression attribute on the Memcards folder.
// 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 );
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:
bool m_UseCDVDsrc;
@ -849,8 +838,8 @@ protected:
wxString m_elf_override;
public:
virtual ~SysExecuteEvent() throw() {}
SysExecuteEvent* Clone() const { return new SysExecuteEvent(*this); }
virtual ~SysExecEvent_Execute() throw() {}
SysExecEvent_Execute* Clone() const { return new SysExecEvent_Execute(*this); }
wxString GetEventName() const
{
@ -862,13 +851,13 @@ public:
return _("Executing PS2 Virtual Machine...");
}
SysExecuteEvent()
SysExecEvent_Execute()
: m_UseCDVDsrc(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_UseELFOverride(true)
, m_cdvdsrc_type(srctype)
@ -877,7 +866,7 @@ public:
}
protected:
void _DoInvoke()
void InvokeEvent()
{
wxGetApp().ProcessMethod( AppSaveSettings );
@ -905,7 +894,7 @@ protected:
// configured CDVD source device.
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
@ -913,7 +902,7 @@ void Pcsx2App::SysExecute()
// sources.
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:
void _DoInvoke()
void InvokeEvent()
{
StateCopy_Clear();
CoreThread.Shutdown();

View File

@ -151,7 +151,7 @@ ConsoleLogFrame::ColorArray::ColorArray( int fontsize ) :
Create( fontsize );
}
ConsoleLogFrame::ColorArray::~ColorArray()
ConsoleLogFrame::ColorArray::~ColorArray() throw()
{
Cleanup();
}
@ -351,7 +351,7 @@ void ConsoleLogFrame::Write( ConsoleColors color, const wxString& text )
{
pthread_testcancel();
ScopedLock lock( m_QueueLock );
ScopedLock lock( m_mtx_Queue );
if( m_QueueColorSection.GetLength() == 0 )
{
@ -378,10 +378,22 @@ void ConsoleLogFrame::Write( ConsoleColors color, const wxString& text )
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 );
evt.SetInt( 0 );
GetEventHandler()->AddPendingEvent( evt );
m_pendingFlushMsg = true;
if( wxThread::IsMain() )
GetEventHandler()->ProcessEvent( evt );
else
{
GetEventHandler()->AddPendingEvent( evt );
}
lock.Acquire();
}
++m_pendingFlushes;
@ -624,7 +636,7 @@ void ConsoleLogFrame::OnFlushLimiterTimer( wxTimerEvent& )
void ConsoleLogFrame::OnFlushEvent( wxCommandEvent& )
{
ScopedLock locker( m_QueueLock );
ScopedLock locker( m_mtx_Queue );
m_pendingFlushMsg = false;
// 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;
public:
virtual ~ColorArray();
virtual ~ColorArray() throw();
ColorArray( int fontsize=8 );
void Create( int fontsize );
@ -217,7 +217,7 @@ protected:
// Lock object for accessing or modifying the following three vars:
// m_QueueBuffer, m_QueueColorSelection, m_CurQueuePos
MutexRecursive m_QueueLock;
MutexRecursive m_mtx_Queue;
// Describes a series of colored text sections in the m_QueueBuffer.
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 )
: BaseApplicableDialog( parent, title, wxVERTICAL )
: _parent( parent, title, wxVERTICAL )
, m_listbook( *new wxListbook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_orient ) )
{
m_idealWidth = idealWidth;
@ -64,13 +64,7 @@ Dialogs::BaseConfigurationDialog::BaseConfigurationDialog( wxWindow* parent, con
m_listbook.SetImageList( &bookicons );
m_ApplyState.StartBook( &m_listbook );
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.") );
*this += m_listbook;
AddOkCancel( *GetSizer(), true );
*m_extraButtonSizer += screenshotButton;
*this += m_listbook | pxExpand.Border( wxLEFT | wxRIGHT, 2 );
Connect( wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BaseConfigurationDialog::OnOk_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( SLIDER_UPDATED );
ConnectSomethingChanged( DIRPICKER_CHANGED );
}
void Dialogs::BaseConfigurationDialog::AddOkCancel( wxSizer* sizer )
{
_parent::AddOkCancel( sizer, true );
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()
@ -112,7 +115,7 @@ void Dialogs::BaseConfigurationDialog::OnSomethingChanged( wxCommandEvent& evt )
evt.Skip();
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
{
typedef BaseApplicableDialog _parent;
protected:
wxListbook& m_listbook;
wxArrayString m_labels;
@ -53,10 +55,13 @@ namespace Dialogs
virtual ~BaseConfigurationDialog() throw();
BaseConfigurationDialog(wxWindow* parent, const wxString& title, wxImageList& bookicons, int idealWidth);
protected:
public:
void AddOkCancel( wxSizer* sizer=NULL );
template< typename T >
void AddPage( const char* label, int iconid );
protected:
void OnOk_Click( wxCommandEvent& evt );
void OnCancel_Click( wxCommandEvent& evt );
void OnApply_Click( wxCommandEvent& evt );
@ -72,8 +77,6 @@ namespace Dialogs
// --------------------------------------------------------------------------------------
class SysConfigDialog : public BaseConfigurationDialog
{
protected:
public:
virtual ~SysConfigDialog() throw() {}
SysConfigDialog(wxWindow* parent=NULL);
@ -83,6 +86,20 @@ namespace Dialogs
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
// --------------------------------------------------------------------------------------

View File

@ -21,6 +21,8 @@
using namespace pxSizerFlags;
extern wxString GetMsg_McdNtfsCompress();
wxFilePickerCtrl* CreateMemoryCardFilePicker( wxWindow* parent, uint portidx, uint slotidx, const wxString& filename=wxEmptyString )
{
return new wxFilePickerCtrl( parent, wxID_ANY, filename,
@ -47,10 +49,7 @@ Dialogs::CreateMemoryCardDialog::CreateMemoryCardDialog( wxWindow* parent, uint
#ifdef __WXMSW__
m_check_CompressNTFS = new pxCheckBox( this,
_("Use NTFS compression on this card"),
pxE( ".Dialog:Memorycards:NtfsCompress",
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."
)
GetMsg_McdNtfsCompress()
);
#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 "Panels/ConfigurationPanels.h"
#include <wx/filepicker.h>
//#include <wx/filepicker.h>
using namespace Panels;
@ -34,7 +34,6 @@ Dialogs::SysConfigDialog::SysConfigDialog(wxWindow* parent)
const AppImageIds::ConfigIds& cfgid( wxGetApp().GetImgId().Config );
AddPage<MemoryCardsPanel> ( wxLt("MemoryCards"), cfgid.MemoryCard );
AddPage<CpuPanelEE> ( wxLt("EE/IOP"), cfgid.Cpu );
AddPage<CpuPanelVU> ( wxLt("VUs"), cfgid.Cpu );
AddPage<VideoPanel> ( wxLt("GS"), cfgid.Video );
@ -44,8 +43,7 @@ Dialogs::SysConfigDialog::SysConfigDialog(wxWindow* parent)
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.
FindWindow( wxID_APPLY )->Disable();
AddOkCancel();
}
Dialogs::AppConfigDialog::AppConfigDialog(wxWindow* parent)
@ -60,6 +58,5 @@ Dialogs::AppConfigDialog::AppConfigDialog(wxWindow* parent)
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.
FindWindow( wxID_APPLY )->Disable();
AddOkCancel();
}

View File

@ -17,13 +17,38 @@
#include "App.h"
// This is called from InvokeAction after various affinity and state checks have verified the
// message as executable. Override this when possible. Only override InvokeAction if you
wxString SysExecEvent::GetEventName() const
{
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.
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 )
{
if( !ex ) return;
@ -55,14 +80,15 @@ void SysExecEvent::SetException( const BaseException& ex )
}
// This method calls _DoInvoke after performing some setup and affinity checks.
// Override _DoInvoke instead, which is the intended method of implementing derived class invocation.
void SysExecEvent::InvokeAction()
// This method calls _DoInvoke after performing some setup, exception handling, and
// affinity checks. For implementing behavior of your event, override _DoInvoke
// instead, which is the intended method of implementing derived class invocation.
void SysExecEvent::_DoInvokeEvent()
{
//pxAssumeDev( !IsBeingDeleted(), "Attempted to process a deleted SysExecutor event." );
AffinityAssert_AllowFrom_SysExecutor();
try {
_DoInvoke();
InvokeEvent();
}
catch( BaseException& ex )
{
@ -73,19 +99,32 @@ void SysExecEvent::InvokeAction()
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
{
if( m_sync ) m_sync->PostResult();
}
// --------------------------------------------------------------------------------------
// pxEvtHandler Implementations
// --------------------------------------------------------------------------------------
pxEvtHandler::pxEvtHandler()
{
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()
{
if( m_Quitting ) return;
@ -108,48 +147,73 @@ struct ScopedThreadCancelDisable
}
};
void pxEvtHandler::ProcessPendingEvents()
void pxEvtHandler::ProcessEvents( pxEvtList& list )
{
ScopedLock synclock( m_mtx_pending );
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() )
{
// Some messages can be blocking, so we should release the mutex lock
// to avoid having cases where the main thread deadlocks simply trying
// to add a message to the queue.
// Some messages can be blocking, so we should release the mutex lock while
// processing, to avoid having cases where the main thread deadlocks simply
// trying to add a message to the queue due to the basic mutex acquire needed.
m_qpc_Start = GetCPUTicks();
synclock.Release();
DevCon.WriteLn( L"(pxEvtHandler) Executing Event: %s [%s]", deleteMe->GetEventName().c_str(), deleteMe->AllowCancelOnExit() ? L"Cancelable" : L"Noncancelable" );
if( deleteMe->AllowCancelOnExit() )
deleteMe->InvokeAction();
deleteMe->_DoInvokeEvent();
else
{
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();
m_qpc_Start = 0; // lets the main thread know the message completed.
}
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();
}
}
}
// 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 )
{
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 )
{
if( !evt ) return;
@ -171,16 +235,33 @@ void pxEvtHandler::PostEvent( SysExecEvent* evt )
void pxEvtHandler::PostEvent( const SysExecEvent& evt )
{
PostEvent( evt.Clone() );
}
void pxEvtHandler::PostIdleEvent( SysExecEvent* evt )
{
if( !evt ) return;
if( m_Quitting )
{
evt.PostResult();
evt->PostResult();
return;
}
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();
}
else
m_idleEvents.push_back( evt );
}
void pxEvtHandler::PostIdleEvent( const SysExecEvent& evt )
{
PostIdleEvent( evt.Clone() );
}
void pxEvtHandler::ProcessEvent( SysExecEvent& evt )
@ -193,23 +274,7 @@ void pxEvtHandler::ProcessEvent( SysExecEvent& evt )
sync.WaitForResult();
}
else
evt.InvokeAction();
}
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;
evt._DoInvokeEvent();
}
void pxEvtHandler::ProcessEvent( SysExecEvent* evt )
@ -226,13 +291,38 @@ void pxEvtHandler::ProcessEvent( SysExecEvent* evt )
else
{
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()
{
DoIdle();
ProcessIdleEvents();
_DoIdle();
m_wakeup.WaitWithoutYield();
}
@ -244,6 +334,9 @@ void pxEvtHandler::SetActiveThread()
// --------------------------------------------------------------------------------------
// WaitingForThreadedTaskDialog
// --------------------------------------------------------------------------------------
// Note: currently unused (legacy code). May be utilized at a later date, so I'm leaving
// it in (for now!)
//
class WaitingForThreadedTaskDialog
: public wxDialogWithHelpers
{
@ -303,6 +396,7 @@ ExecutorThread::ExecutorThread( pxEvtHandler* evthandler )
m_EvtHandler = evthandler;
}
// Exposes the internal pxEvtHandler::ShutdownQueue API. See pxEvtHandler for details.
void ExecutorThread::ShutdownQueue()
{
if( !m_EvtHandler || m_EvtHandler->IsShuttingDown() ) return;
@ -310,6 +404,7 @@ void ExecutorThread::ShutdownQueue()
Block();
}
// Exposes the internal pxEvtHandler::PostEvent API. See pxEvtHandler for details.
void ExecutorThread::PostEvent( SysExecEvent* evt )
{
if( !pxAssert( m_EvtHandler ) ) return;
@ -322,6 +417,20 @@ void ExecutorThread::PostEvent( const SysExecEvent& 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 )
{
if( m_EvtHandler )
@ -329,7 +438,7 @@ void ExecutorThread::ProcessEvent( SysExecEvent* evt )
else
{
ScopedPtr<SysExecEvent> deleteMe( evt );
deleteMe->InvokeAction();
deleteMe->_DoInvokeEvent();
}
}
@ -338,7 +447,7 @@ void ExecutorThread::ProcessEvent( SysExecEvent& evt )
if( m_EvtHandler )
m_EvtHandler->ProcessEvent( evt );
else
evt.InvokeAction();
evt._DoInvokeEvent();
}
void ExecutorThread::OnStart()
@ -379,7 +488,7 @@ void ExecutorThread::OnCleanupInThread()
// 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
// 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 )
{
if( !SysExecutorThread.IsRunning() ) return;

View File

@ -155,7 +155,8 @@ void MainEmuFrame::ConnectMenus()
#define ConnectMenuRange( id_start, inc, 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_BIOS, Menu_SelectBios_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_AppSettings, _("App Settings") );
m_menuConfig.Append(MenuId_Config_McdSettings, _("&MemoryCards") );
m_menuConfig.AppendSeparator();

View File

@ -162,7 +162,8 @@ protected:
void OnFocus( wxFocusEvent& 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_SelectBios_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 );
}
void MainEmuFrame::Menu_McdSettings_Click(wxCommandEvent &event)
{
AppOpenDialog<McdConfigDialog>( this );
}
void MainEmuFrame::Menu_AppSettings_Click(wxCommandEvent &event)
{
AppOpenDialog<AppConfigDialog>( this );
@ -425,7 +430,7 @@ public:
wxString GetEventName() const { return L"ToggleSuspendResume"; }
protected:
void _DoInvoke()
void InvokeEvent()
{
if( CoreThread.IsOpen() )
CoreThread.Suspend();
@ -447,7 +452,7 @@ public:
}
protected:
void _DoInvoke()
void InvokeEvent()
{
sApp.SysShutdown();
sApp.SysExecute();

View File

@ -102,7 +102,7 @@ FileMemoryCard::FileMemoryCard()
// [TODO] : Add memcard size detection and report it to the console log.
// (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" ) )
{

View File

@ -64,7 +64,7 @@ BaseMessageBoxEvent::BaseMessageBoxEvent( const BaseMessageBoxEvent& event )
}
// Thread Safety: Must be called from the GUI thread ONLY.
void BaseMessageBoxEvent::_DoInvoke()
void BaseMessageBoxEvent::InvokeEvent()
{
int result = _DoDialog();
if( m_state ) m_state->PostResult( result );
@ -104,7 +104,7 @@ pxMessageBoxEvent::pxMessageBoxEvent( const pxMessageBoxEvent& event )
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 "ConfigurationPanels.h"
#include "MemoryCardListView.h"
#include "MemoryCardPanels.h"
#include <wx/filepicker.h>
#include <wx/ffile.h>
@ -81,7 +81,7 @@ static int EnumerateMemoryCards( McdList& dest, const wxArrayString& files )
// =====================================================================================================
// MemoryCardListPanel
// =====================================================================================================
MemoryCardListPanel::MemoryCardListPanel( wxWindow* parent )
Panels::MemoryCardListPanel::MemoryCardListPanel( wxWindow* parent )
: BaseSelectorPanel( parent )
{
m_FolderPicker = new DirPickerPanel( this, FolderId_MemoryCards,
@ -113,18 +113,16 @@ MemoryCardListPanel::MemoryCardListPanel( wxWindow* parent )
*this += m_listview | pxExpand;
*this += s_buttons | pxExpand;
m_listview->SetMinSize( wxSize( wxDefaultCoord, 120 ) );
AppStatusEvent_OnSettingsApplied();
m_listview->SetMinSize( wxSize( m_idealWidth, 120 ) );
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;
}
bool MemoryCardListPanel::ValidateEnumerationStatus()
bool Panels::MemoryCardListPanel::ValidateEnumerationStatus()
{
bool validated = true;
@ -163,7 +161,7 @@ bool MemoryCardListPanel::ValidateEnumerationStatus()
return validated;
}
void MemoryCardListPanel::DoRefresh()
void Panels::MemoryCardListPanel::DoRefresh()
{
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 "ConfigurationPanels.h"
#include "MemoryCardListView.h"
#include "MemoryCardPanels.h"
#include "Dialogs/ConfigurationDialog.h"
@ -81,7 +81,7 @@ MemoryCardListView::MemoryCardListView( wxWindow* parent )
Connect( wxEVT_COMMAND_LIST_BEGIN_DRAG, wxListEventHandler(MemoryCardListView::OnListDrag));
}
void Panels::MemoryCardListView::OnListDrag(wxListEvent& evt)
void MemoryCardListView::OnListDrag(wxListEvent& evt)
{
evt.Skip();
@ -94,7 +94,7 @@ void Panels::MemoryCardListView::OnListDrag(wxListEvent& evt)
}
// 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);
@ -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
// only be called for the first column. See OnGetItemColumnImage for
// details.
int Panels::MemoryCardListView::OnGetItemImage(long item) const
int MemoryCardListView::OnGetItemImage(long item) const
{
return _parent::OnGetItemImage( item );
}
// 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 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);
//const McdListItem& it( (*m_KnownCards)[item] );
@ -155,34 +155,58 @@ MemoryCardInfoPanel::MemoryCardInfoPanel( wxWindow* parent, uint port, uint slot
SetMinSize( wxSize(128, 48) );
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)
{
// 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 );
wxFont normal( dc.GetFont() );
wxFont bold( normal );
normal.SetWeight( wxNORMAL );
bold.SetWeight( wxBOLD );
wxFont woot( dc.GetFont() );
woot.SetWeight( wxBOLD );
dc.SetFont( woot );
dc.SetFont( bold );
DrawTextCentered( dc, fname );
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 );
}
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()
{
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_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 )
{
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 )
{
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
// ------------------------------------
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 += s_checks | StdExpand();
AppStatusEvent_OnSettingsApplied();
}
void Panels::MemoryCardsPanel::OnMultitapChecked( wxCommandEvent& evt )

View File

@ -14,16 +14,19 @@
*/
#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/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
static const bool DisableThreading =
#ifdef __LINUX__
@ -36,20 +39,20 @@ using namespace pxSizerFlags;
using namespace Threading;
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_ShowStatusBar, -1)
DECLARE_EVENT_TYPE(pxEvt_SysExecEventComplete, -1)
END_DECLARE_EVENT_TYPES()
DEFINE_EVENT_TYPE(pxEVT_EnumeratedNext)
DEFINE_EVENT_TYPE(pxEvt_EnumeratedNext);
DEFINE_EVENT_TYPE(pxEvt_EnumerationFinished);
DEFINE_EVENT_TYPE(pxEVT_ShowStatusBar);
DEFINE_EVENT_TYPE(pxEvt_SysExecEventComplete)
typedef s32 (CALLBACK* TestFnptr)();
typedef void (CALLBACK* ConfigureFnptr)();
static const wxString failed_separator( L"-------- Unsupported Plugins --------" );
namespace Exception
{
class NotEnumerablePlugin : public BadStream
@ -138,28 +141,20 @@ public:
// --------------------------------------------------------------------------------------
// ApplyPluginsDialog
// --------------------------------------------------------------------------------------
class ApplyPluginsDialog : public wxDialogWithHelpers
class ApplyPluginsDialog : public WaitForTaskDialog
{
DECLARE_DYNAMIC_CLASS_NO_COPY(ApplyPluginsDialog)
typedef wxDialogWithHelpers _parent;
protected:
BaseApplicableConfigPanel* m_panel;
ScopedPtr<BaseException> m_Exception;
SynchronousActionState m_sync;
BaseApplicableConfigPanel* m_panel;
public:
ApplyPluginsDialog( BaseApplicableConfigPanel* panel=NULL );
virtual ~ApplyPluginsDialog() throw() {}
virtual void RethrowException();
virtual int ShowModal();
BaseApplicableConfigPanel* GetApplicableConfigPanel() const { return m_panel; }
protected:
void OnSysExecComplete( wxCommandEvent& evt );
};
@ -184,7 +179,7 @@ public:
virtual ApplyOverValidStateEvent *Clone() const { return new ApplyOverValidStateEvent(*this); }
protected:
void _DoInvoke();
void InvokeEvent();
};
@ -193,10 +188,14 @@ protected:
// --------------------------------------------------------------------------------------
class SysExecEvent_ApplyPlugins : public SysExecEvent
{
typedef SysExecEvent _parent;
protected:
ApplyPluginsDialog* m_dialog;
public:
wxString GetEventName() const { return L"PluginSelectorPanel::ApplyPlugins"; }
virtual ~SysExecEvent_ApplyPlugins() throw() {}
SysExecEvent_ApplyPlugins* Clone() const { return new SysExecEvent_ApplyPlugins( *this ); }
@ -207,19 +206,27 @@ public:
}
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 )
: wxDialogWithHelpers( NULL, _("Applying settings..."), wxVERTICAL )
: WaitForTaskDialog( _("Applying settings...") )
{
Connect( pxEvt_SysExecEventComplete, wxCommandEventHandler(ApplyPluginsDialog::OnSysExecComplete) );
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 );
@ -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 );
}
void SysExecEvent_ApplyPlugins::_DoInvoke()
// --------------------------------------------------------------------------------------
// SysExecEvent_ApplyPlugins Implementations
// --------------------------------------------------------------------------------------
void SysExecEvent_ApplyPlugins::InvokeEvent()
{
ScopedCoreThreadPause paused_core;
@ -261,36 +271,26 @@ void SysExecEvent_ApplyPlugins::_DoInvoke()
LoadPluginsImmediate();
CoreThread.RecoverState();
wxCommandEvent tevt( pxEvt_SysExecEventComplete );
m_dialog->GetEventHandler()->AddPendingEvent( tevt );
PostFinishToDialog();
closed_core.AllowResume();
paused_core.AllowResume();
}
void ApplyPluginsDialog::OnSysExecComplete( wxCommandEvent& evt )
void SysExecEvent_ApplyPlugins::PostFinishToDialog()
{
evt.Skip();
m_sync.WaitForResult();
EndModal( wxID_OK );
if( !m_dialog ) return;
wxCommandEvent tevt( pxEvt_ThreadedTaskComplete );
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
// --------------------------------------------------------------------------------------
@ -420,7 +420,7 @@ Panels::PluginSelectorPanel::PluginSelectorPanel( wxWindow* parent, int idealWid
//s_main.Add( refresh );
//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_ShowStatusBar, wxCommandEventHandler( PluginSelectorPanel::OnShowStatusBar ) );
Connect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( PluginSelectorPanel::OnPluginSelected ) );
@ -494,24 +494,9 @@ void Panels::PluginSelectorPanel::Apply()
// ----------------------------------------------------------------------------
// 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
{
if( wxID_CANCEL == applyDlg.ShowModal() )
if( wxID_CANCEL == ApplyPluginsDialog( this ).ShowModal() )
throw Exception::CannotApplySettings( this, "User canceled plugin load process.", false );
}
catch( Exception::PluginError& ex )
@ -794,7 +779,7 @@ void Panels::PluginSelectorPanel::EnumThread::DoNextPlugin( int curidx )
Console.Warning( ex.FormatDiagnosticMessage() );
}
wxCommandEvent yay( pxEVT_EnumeratedNext );
wxCommandEvent yay( pxEvt_EnumeratedNext );
yay.SetClientData( this );
yay.SetExtraLong( curidx );
m_master.GetEventHandler()->AddPendingEvent( yay );

View File

@ -40,16 +40,54 @@ bool States_isSlotUsed(int 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()
{
if( AtomicExchange(IsSavingOrLoading, true) )
{
Console.WriteLn( "Load or save action is already pending." );
return;
}
GSchangeSaveState( StatesC, SaveStateBase::GetFilename( StatesC ).ToUTF8() );
StateCopy_SaveToSlot( StatesC );
GetSysExecutorThread().PostIdleEvent( SysExecEvent_ClearSavingLoadingFlag() );
}
void States_DefrostCurrentSlot()
{
if( AtomicExchange(IsSavingOrLoading, true) )
{
Console.WriteLn( "Load or save action is already pending." );
return;
}
GSchangeSaveState( StatesC, SaveStateBase::GetFilename( StatesC ).ToUTF8() );
StateCopy_LoadFromSlot( StatesC );
GetSysExecutorThread().PostIdleEvent( SysExecEvent_ClearSavingLoadingFlag() );
//SysStatus( wxsFormat( _("Loaded State (slot %d)"), StatesC ) );
}

View File

@ -68,6 +68,8 @@ static void SaveStateFile_ReadHeader( IStreamReader& thr )
//
class gzipReader : public IStreamReader
{
DeclareNoncopyableObject(gzipReader);
protected:
wxString m_filename;
gzFile m_gzfp;
@ -100,6 +102,9 @@ public:
}
};
static bool IsSavingOrLoading = false;
// --------------------------------------------------------------------------------------
// SysExecEvent_DownloadState
// --------------------------------------------------------------------------------------
@ -111,6 +116,8 @@ protected:
VmStateBuffer* m_dest_buffer;
public:
wxString GetEventName() const { return L"VM_Download"; }
virtual ~SysExecEvent_DownloadState() throw() {}
SysExecEvent_DownloadState* Clone() const { return new SysExecEvent_DownloadState( *this ); }
SysExecEvent_DownloadState( VmStateBuffer* dest=&state_buffer )
@ -118,10 +125,11 @@ public:
m_dest_buffer = dest;
}
bool IsCriticalEvent() const { return true; }
bool AllowCancelOnExit() const { return false; }
protected:
void _DoInvoke()
void InvokeEvent()
{
ScopedCoreThreadPause paused_core;
@ -130,10 +138,61 @@ protected:
memSavingState(m_dest_buffer).FreezeAll();
UI_EnableStateActions();
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
// --------------------------------------------------------------------------------------
@ -144,7 +203,7 @@ protected:
wxString m_filename;
public:
wxString GetEventName() const { return L"SysState_ZipToDisk"; }
wxString GetEventName() const { return L"VM_ZipToDisk"; }
virtual ~SysExecEvent_ZipToDisk() throw()
{
@ -153,7 +212,6 @@ public:
SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); }
// Yep, gcc doesn't like >> again.
SysExecEvent_ZipToDisk( ScopedPtr<VmStateBuffer>& src, const wxString& filename )
: m_filename( filename )
{
@ -170,9 +228,9 @@ public:
bool AllowCancelOnExit() const { return false; }
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;
}
};
@ -188,25 +246,27 @@ class SysExecEvent_UnzipFromDisk : public SysExecEvent
{
protected:
wxString m_filename;
gzipReader m_gzreader;
public:
wxString GetEventName() const { return L"SysState_UnzipFromDisk"; }
wxString GetEventName() const { return L"VM_UnzipFromDisk"; }
virtual ~SysExecEvent_UnzipFromDisk() throw() {}
SysExecEvent_UnzipFromDisk* Clone() const { return new SysExecEvent_UnzipFromDisk( *this ); }
SysExecEvent_UnzipFromDisk( const wxString& filename )
: m_filename( filename )
, m_gzreader( filename )
{
SaveStateFile_ReadHeader( m_gzreader );
}
wxString GetStreamName() const { return m_filename; }
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
// *ALWAYS* start execution after the new savestate is loaded.
@ -263,6 +323,8 @@ void StateCopy_FreezeToMem()
void StateCopy_SaveToFile( const wxString& file )
{
UI_DisableStateActions();
ScopedPtr<VmStateBuffer> zipbuf(new VmStateBuffer( L"Zippable Savestate" ));
GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState( zipbuf ));
GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk( zipbuf, file ));

View File

@ -70,18 +70,34 @@ void UI_EnableSysShutdown()
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_Shutdown, false );
_SaveLoadStuff( false );
}
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_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
// --------------------------------------------------------------------------------------
class SysExecEvent
: public IActionInvocation
, public ICloneable
// Base class for all pxEvtHandler processable events.
//
// 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:
SynchronousActionState* m_sync;
@ -63,11 +79,11 @@ public:
// data.
virtual bool AllowCancelOnExit() const { return true; }
virtual void InvokeAction();
virtual void _DoInvokeEvent();
virtual void PostResult() const;
virtual wxString GetEventName() const { return wxEmptyString; }
virtual wxString GetEventMessage() const { return wxEmptyString; }
virtual wxString GetEventName() const;
virtual wxString GetEventMessage() const;
virtual int GetResult()
{
@ -80,28 +96,31 @@ public:
void SetException( const BaseException& ex );
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:
FnType_Void* m_method;
public:
virtual ~SysExecEvent_Method() throw() {}
SysExecEvent_Method* Clone() const { return new SysExecEvent_Method( *this ); }
wxString GetEventName() const { return L"MethodVoid"; }
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;
}
protected:
void _DoInvoke()
void InvokeEvent()
{
if( m_method ) m_method();
}
@ -113,18 +132,39 @@ typedef std::list<SysExecEvent*> pxEvtList;
// --------------------------------------------------------------------------------------
// pxEvtHandler
// --------------------------------------------------------------------------------------
// wxWidgets Event Queue (wxEvtHandler) isn't thread-safe (uses static vars and checks/modifies wxApp globals
// while processing), so it's useless to us. Have to roll our own. -_-
// Purpose: To provide a safe environment for queuing tasks that must be executed in
// 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
{
protected:
pxEvtList m_pendingEvents;
pxEvtList m_idleEvents;
Threading::MutexRecursive m_mtx_pending;
Threading::Semaphore m_wakeup;
wxThreadIdType m_OwnerThreadId;
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:
pxEvtHandler();
virtual ~pxEvtHandler() throw() {}
@ -134,27 +174,33 @@ public:
virtual void ShutdownQueue();
bool IsShuttingDown() const { return !!m_Quitting; }
void ProcessEvents( pxEvtList& list );
void ProcessPendingEvents();
void ProcessIdleEvents();
void Idle();
void AddPendingEvent( SysExecEvent& evt );
void PostEvent( SysExecEvent* evt );
void PostEvent( const SysExecEvent& evt );
void PostIdleEvent( SysExecEvent* evt );
void PostIdleEvent( const SysExecEvent& evt );
void ProcessEvent( SysExecEvent* evt );
void ProcessEvent( SysExecEvent& evt );
bool SelfProcessMethod( FnType_Void* method );
void Idle();
bool ProcessMethodSelf( FnType_Void* method );
void SetActiveThread();
protected:
virtual void DoIdle() {}
virtual void _DoIdle() {}
};
// --------------------------------------------------------------------------------------
// 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
{
typedef Threading::PersistentThread _parent;
@ -172,12 +218,15 @@ public:
void PostEvent( SysExecEvent* evt );
void PostEvent( const SysExecEvent& evt );
void PostIdleEvent( SysExecEvent* evt );
void PostIdleEvent( const 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:

View File

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