mirror of https://github.com/PCSX2/pcsx2.git
Major UI renovations!
* Added versioning info to savestates! (along with some other bugfixes) * Simplified the Boot and System menus -- removed the old Skip BIOS hack and replaced it with the new BOOT2 injection method (which is considered hack-free at this time). * Removed lots of UI deadlock gotchas. * Some new confirmation dialogs and better error handling. * Implemented an exclusive SysExecutor thread, which serves the purpose of executing system/VM commands and events in uninterrupted order (including suspend, resume, savestates, etc.) * ... and probably broke linux! git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2911 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
c440cb563a
commit
5d37925617
|
@ -156,12 +156,14 @@
|
|||
<Unit filename="../../include/Utilities/Path.h" />
|
||||
<Unit filename="../../include/Utilities/PersistentThread.h" />
|
||||
<Unit filename="../../include/Utilities/RedtapeWindows.h" />
|
||||
<Unit filename="../../include/Utilities/RwMutex.h" />
|
||||
<Unit filename="../../include/Utilities/SafeArray.h" />
|
||||
<Unit filename="../../include/Utilities/ScopedPtr.h" />
|
||||
<Unit filename="../../include/Utilities/StringHelpers.h" />
|
||||
<Unit filename="../../include/Utilities/Threading.h" />
|
||||
<Unit filename="../../include/Utilities/lnx_memzero.h" />
|
||||
<Unit filename="../../include/Utilities/pxCheckBox.h" />
|
||||
<Unit filename="../../include/Utilities/pxEvents.h" />
|
||||
<Unit filename="../../include/Utilities/pxRadioPanel.h" />
|
||||
<Unit filename="../../include/Utilities/pxStaticText.h" />
|
||||
<Unit filename="../../include/Utilities/win_memzero.h" />
|
||||
|
@ -182,6 +184,7 @@
|
|||
<Unit filename="../../src/Utilities/PathUtils.cpp" />
|
||||
<Unit filename="../../src/Utilities/PrecompiledHeader.cpp" />
|
||||
<Unit filename="../../src/Utilities/PrecompiledHeader.h" />
|
||||
<Unit filename="../../src/Utilities/RwMutex.cpp" />
|
||||
<Unit filename="../../src/Utilities/Semaphore.cpp" />
|
||||
<Unit filename="../../src/Utilities/StringHelpers.cpp" />
|
||||
<Unit filename="../../src/Utilities/ThreadTools.cpp" />
|
||||
|
|
|
@ -207,10 +207,6 @@
|
|||
RelativePath="..\..\src\Utilities\Console.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\Utilities\EventSource.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\Utilities\Exceptions.cpp"
|
||||
>
|
||||
|
@ -426,6 +422,10 @@
|
|||
RelativePath="..\..\src\Utilities\Mutex.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\Utilities\RwMutex.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\Utilities\Semaphore.cpp"
|
||||
>
|
||||
|
@ -501,10 +501,6 @@
|
|||
RelativePath="..\..\include\Utilities\Path.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\Utilities\PersistentThread.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\Utilities\PrecompiledHeader.h"
|
||||
>
|
||||
|
@ -513,6 +509,10 @@
|
|||
RelativePath="..\..\include\Utilities\pxCheckBox.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\Utilities\pxEvents.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\Utilities\pxRadioPanel.h"
|
||||
>
|
||||
|
@ -556,6 +556,14 @@
|
|||
<Filter
|
||||
Name="Threading"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\include\Utilities\PersistentThread.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\Utilities\RwMutex.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\Utilities\Threading.h"
|
||||
>
|
||||
|
|
|
@ -154,8 +154,14 @@ public:
|
|||
}
|
||||
|
||||
virtual ~ConsoleIndentScope() throw()
|
||||
{
|
||||
if( m_amount != 0 ) Console.SetIndent( -m_amount );
|
||||
}
|
||||
|
||||
void EndScope()
|
||||
{
|
||||
Console.SetIndent( -m_amount );
|
||||
m_amount = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Threading.h"
|
||||
|
||||
using Threading::ScopedLock;
|
||||
|
||||
template< typename ListenerType >
|
||||
|
@ -77,7 +79,7 @@ __forceinline void EventSource<ListenerType>::_DispatchRaw( ListenerIterator ite
|
|||
Console.Error( L"Ignoring runtime error thrown from event listener: " + ex.FormatDiagnosticMessage() );
|
||||
}
|
||||
}
|
||||
catch( Exception::BaseException& ex )
|
||||
catch( BaseException& ex )
|
||||
{
|
||||
if( IsDevBuild )
|
||||
{
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
// friendly error log in their wake.
|
||||
//
|
||||
#define __DESTRUCTOR_CATCHALL( funcname ) \
|
||||
catch( Exception::BaseException& ex ) \
|
||||
catch( BaseException& ex ) \
|
||||
{ \
|
||||
Console.Error( "Unhandled BaseException in %s (ignored!):", funcname ); \
|
||||
Console.Error( ex.FormatDiagnosticMessage() ); \
|
||||
|
@ -155,6 +155,8 @@ namespace Exception
|
|||
bool IsSilent;
|
||||
public:
|
||||
DEFINE_RUNTIME_EXCEPTION( RuntimeError, wxLt("An unhandled runtime error has occurred, somewhere in the depths of Pcsx2's cluttered brain-matter.") )
|
||||
|
||||
RuntimeError( const std::runtime_error& ex, const wxString& prefix=wxEmptyString );
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -243,7 +245,7 @@ namespace Exception
|
|||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Streaming (file) Exceptions:
|
||||
// Stream / BadStream / CreateStream / FileNotFound / AccessDenied / EndOfStream
|
||||
// Stream / BadStream / CannotCreateStream / FileNotFound / AccessDenied / EndOfStream
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
#define DEFINE_STREAM_EXCEPTION( classname, defmsg ) \
|
||||
|
@ -308,22 +310,22 @@ namespace Exception
|
|||
|
||||
// A generic exception for odd-ball stream creation errors.
|
||||
//
|
||||
class CreateStream : public virtual Stream
|
||||
class CannotCreateStream : public virtual Stream
|
||||
{
|
||||
public:
|
||||
DEFINE_STREAM_EXCEPTION( CreateStream, wxLt("File could not be created or opened.") )
|
||||
DEFINE_STREAM_EXCEPTION( CannotCreateStream, wxLt("File could not be created or opened.") )
|
||||
};
|
||||
|
||||
// Exception thrown when an attempt to open a non-existent file is made.
|
||||
// (this exception can also mean file permissions are invalid)
|
||||
//
|
||||
class FileNotFound : public virtual CreateStream
|
||||
class FileNotFound : public virtual CannotCreateStream
|
||||
{
|
||||
public:
|
||||
DEFINE_STREAM_EXCEPTION( FileNotFound, wxLt("File not found.") )
|
||||
};
|
||||
|
||||
class AccessDenied : public virtual CreateStream
|
||||
class AccessDenied : public virtual CannotCreateStream
|
||||
{
|
||||
public:
|
||||
DEFINE_STREAM_EXCEPTION( AccessDenied, wxLt("Permission denied to file.") )
|
||||
|
@ -358,3 +360,5 @@ namespace Exception
|
|||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
using Exception::BaseException;
|
||||
|
|
|
@ -53,6 +53,9 @@ public:
|
|||
bool IsReentrant() const { return Counter > 1; }
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ICloneable / IActionInvocation / IDeletableObject
|
||||
// --------------------------------------------------------------------------------------
|
||||
class IActionInvocation
|
||||
{
|
||||
public:
|
||||
|
@ -60,8 +63,30 @@ public:
|
|||
virtual void InvokeAction()=0;
|
||||
};
|
||||
|
||||
class ICloneable
|
||||
{
|
||||
public:
|
||||
virtual ICloneable* Clone() const=0;
|
||||
};
|
||||
|
||||
class IDeletableObject
|
||||
{
|
||||
public:
|
||||
virtual ~IDeletableObject() throw() {}
|
||||
|
||||
virtual void DeleteSelf()=0;
|
||||
virtual bool IsBeingDeleted()=0;
|
||||
|
||||
protected:
|
||||
// This function is GUI implementation dependent! It's implemented by PCSX2's AppHost,
|
||||
// but if the SysCore is being linked to another front end, you'll need to implement this
|
||||
// yourself. Most GUIs have built in message pumps. If a platform lacks one then you'll
|
||||
// need to implement one yourself (yay?).
|
||||
virtual void DoDeletion()=0;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// IDeletableObject
|
||||
// BaseDeletableObject
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Oh the fruits and joys of multithreaded C++ coding conundrums! This class provides a way
|
||||
// to be deleted from arbitraty threads, or to delete themselves (which is considered unsafe
|
||||
|
@ -83,14 +108,14 @@ public:
|
|||
// (sigh). And, finally, it requires quite a bit of red tape to implement wxObjects because
|
||||
// of the wx-custom runtime type information. So I made my own.
|
||||
//
|
||||
class IDeletableObject
|
||||
class BaseDeletableObject : public virtual IDeletableObject
|
||||
{
|
||||
protected:
|
||||
volatile long m_IsBeingDeleted;
|
||||
|
||||
public:
|
||||
IDeletableObject();
|
||||
virtual ~IDeletableObject() throw();
|
||||
BaseDeletableObject();
|
||||
virtual ~BaseDeletableObject() throw();
|
||||
|
||||
void DeleteSelf();
|
||||
bool IsBeingDeleted() { return !!m_IsBeingDeleted; }
|
||||
|
|
|
@ -60,6 +60,7 @@ public:
|
|||
bool IsWritable() const { return IsDirWritable(); }
|
||||
bool IsReadable() const { return IsDirReadable(); }
|
||||
bool Exists() const { return DirExists(); }
|
||||
bool FileExists() const { return wxFileName::FileExists(); }
|
||||
bool IsOk() const { return wxFileName::IsOk(); }
|
||||
bool IsRelative() const { return wxFileName::IsRelative(); }
|
||||
bool IsAbsolute() const { return wxFileName::IsAbsolute(); }
|
||||
|
|
|
@ -123,14 +123,14 @@ namespace Threading
|
|||
Semaphore m_sem_event; // general wait event that's needed by most threads
|
||||
Semaphore m_sem_startup; // startup sync tool
|
||||
Mutex m_lock_InThread; // used for canceling and closing threads in a deadlock-safe manner
|
||||
MutexLockRecursive m_lock_start; // used to lock the Start() code from starting simultaneous threads accidentally.
|
||||
MutexRecursive m_lock_start; // used to lock the Start() code from starting simultaneous threads accidentally.
|
||||
|
||||
volatile long m_detached; // a boolean value which indicates if the m_thread handle is valid
|
||||
volatile long m_running; // set true by Start(), and set false by Cancel(), Block(), etc.
|
||||
|
||||
// exception handle, set non-NULL if the thread terminated with an exception
|
||||
// Use RethrowException() to re-throw the exception using its original exception type.
|
||||
ScopedPtr<Exception::BaseException> m_except;
|
||||
ScopedPtr<BaseException> m_except;
|
||||
|
||||
EventSource<EventListener_Thread> m_evtsrc_OnDelete;
|
||||
|
||||
|
@ -148,6 +148,7 @@ namespace Threading
|
|||
virtual bool Cancel( const wxTimeSpan& timeout );
|
||||
virtual bool Detach();
|
||||
virtual void Block();
|
||||
virtual bool Block( const wxTimeSpan& timeout );
|
||||
virtual void RethrowException() const;
|
||||
|
||||
void AddListener( EventListener_Thread& evt );
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/* 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"
|
||||
|
||||
namespace Threading
|
||||
{
|
||||
// --------------------------------------------------------------------------------------
|
||||
// RwMutex
|
||||
// --------------------------------------------------------------------------------------
|
||||
class RwMutex
|
||||
{
|
||||
DeclareNoncopyableObject(RwMutex);
|
||||
|
||||
protected:
|
||||
pthread_rwlock_t m_rwlock;
|
||||
|
||||
public:
|
||||
RwMutex();
|
||||
virtual ~RwMutex() throw();
|
||||
|
||||
virtual void AcquireRead();
|
||||
virtual void AcquireWrite();
|
||||
virtual bool TryAcquireRead();
|
||||
virtual bool TryAcquireWrite();
|
||||
|
||||
virtual void Release();
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// BaseScopedReadWriteLock
|
||||
// --------------------------------------------------------------------------------------
|
||||
class BaseScopedReadWriteLock
|
||||
{
|
||||
DeclareNoncopyableObject(BaseScopedReadWriteLock);
|
||||
|
||||
protected:
|
||||
RwMutex& m_lock;
|
||||
bool m_IsLocked;
|
||||
|
||||
public:
|
||||
BaseScopedReadWriteLock( RwMutex& locker )
|
||||
: m_lock( locker )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~BaseScopedReadWriteLock() throw();
|
||||
|
||||
void Release();
|
||||
bool IsLocked() const { return m_IsLocked; }
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ScopedReadLock / ScopedWriteLock
|
||||
// --------------------------------------------------------------------------------------
|
||||
class ScopedReadLock : public BaseScopedReadWriteLock
|
||||
{
|
||||
public:
|
||||
ScopedReadLock( RwMutex& locker );
|
||||
virtual ~ScopedReadLock() throw() {}
|
||||
|
||||
void Acquire();
|
||||
};
|
||||
|
||||
class ScopedWriteLock : public BaseScopedReadWriteLock
|
||||
{
|
||||
public:
|
||||
ScopedWriteLock( RwMutex& locker );
|
||||
virtual ~ScopedWriteLock() throw() {}
|
||||
|
||||
void Acquire();
|
||||
|
||||
protected:
|
||||
ScopedWriteLock( RwMutex& locker, bool isTryLock );
|
||||
};
|
||||
}
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
#undef Yield // release the burden of windows.h global namespace spam.
|
||||
|
||||
#define AffinityAssert_AllowFromMain() \
|
||||
#define AffinityAssert_AllowFrom_MainUI() \
|
||||
pxAssertMsg( wxThread::IsMain(), "Thread affinity violation: Call allowed from main thread only." )
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -48,7 +48,9 @@ class wxTimeSpan;
|
|||
namespace Threading
|
||||
{
|
||||
class PersistentThread;
|
||||
class RwMutex;
|
||||
|
||||
extern void pxTestCancel();
|
||||
extern PersistentThread* pxGetCurrentThread();
|
||||
extern wxString pxGetCurrentThreadName();
|
||||
extern u64 GetThreadCpuTime();
|
||||
|
@ -110,43 +112,6 @@ namespace Exception
|
|||
BaseException::InitBaseEx( msg_diag, msg_user );
|
||||
}
|
||||
};
|
||||
|
||||
#if wxUSE_GUI
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ThreadDeadlock Exception
|
||||
// --------------------------------------------------------------------------------------
|
||||
// This exception is thrown by Semaphore and Mutex Wait/Acquire functions if a blocking wait is
|
||||
// needed due to gui Yield recursion, and the timeout period for deadlocking (usually 3 seconds)
|
||||
// is reached before the lock becomes available. This exception cannot occur in the following
|
||||
// conditions:
|
||||
// * If the user-specified timeout is less than the deadlock timeout.
|
||||
// * If the method is run from a thread *other* than the MainGui thread.
|
||||
//
|
||||
class ThreadDeadlock : public virtual BaseThreadError
|
||||
{
|
||||
public:
|
||||
DEFINE_EXCEPTION_COPYTORS( ThreadDeadlock )
|
||||
|
||||
explicit ThreadDeadlock( Threading::PersistentThread* _thread=NULL, const char* msg="Blocking action timed out waiting for '%s' (potential thread deadlock)." )
|
||||
{
|
||||
m_thread = _thread;
|
||||
BaseException::InitBaseEx( msg );
|
||||
}
|
||||
|
||||
ThreadDeadlock( Threading::PersistentThread& _thread, const char* msg="Blocking action timed out waiting for '%s' (potential thread deadlock)." )
|
||||
{
|
||||
m_thread = &_thread;
|
||||
BaseException::InitBaseEx( msg );
|
||||
}
|
||||
|
||||
ThreadDeadlock( Threading::PersistentThread& _thread, const wxString& msg_diag, const wxString& msg_user )
|
||||
{
|
||||
m_thread = &_thread;
|
||||
BaseException::InitBaseEx( msg_diag, msg_user );
|
||||
}
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -298,17 +263,19 @@ namespace Threading
|
|||
|
||||
void Wait();
|
||||
bool Wait( const wxTimeSpan& timeout );
|
||||
void WaitWithoutYield();
|
||||
bool WaitWithoutYield( const wxTimeSpan& timeout );
|
||||
|
||||
protected:
|
||||
// empty constructor used by MutexLockRecursive
|
||||
Mutex( bool ) {}
|
||||
};
|
||||
|
||||
class MutexLockRecursive : public Mutex
|
||||
class MutexRecursive : public Mutex
|
||||
{
|
||||
public:
|
||||
MutexLockRecursive();
|
||||
virtual ~MutexLockRecursive() throw();
|
||||
MutexRecursive();
|
||||
virtual ~MutexRecursive() throw();
|
||||
virtual bool IsRecursive() const { return true; }
|
||||
};
|
||||
|
||||
|
@ -319,60 +286,46 @@ namespace Threading
|
|||
// generally clean) method of locking code inside a function or conditional block. The lock
|
||||
// will be automatically released on any return or exit from the function.
|
||||
//
|
||||
// Const qualification note:
|
||||
// ScopedLock takes const instances of the mutex, even though the mutex is modified
|
||||
// by locking and unlocking. Two rationales:
|
||||
//
|
||||
// 1) when designing classes with accessors (GetString, GetValue, etc) that need mutexes,
|
||||
// this class needs a const hack to allow those accessors to be const (which is typically
|
||||
// *very* important).
|
||||
//
|
||||
// 2) The state of the Mutex is guaranteed to be unchanged when the calling function or
|
||||
// scope exits, by any means. Only via manual calls to Release or Acquire does that
|
||||
// change, and typically those are only used in very special circumstances of their own.
|
||||
//
|
||||
class ScopedLock
|
||||
{
|
||||
DeclareNoncopyableObject(ScopedLock);
|
||||
|
||||
protected:
|
||||
Mutex& m_lock;
|
||||
Mutex* m_lock;
|
||||
bool m_IsLocked;
|
||||
|
||||
public:
|
||||
virtual ~ScopedLock() throw()
|
||||
{
|
||||
if( m_IsLocked )
|
||||
m_lock.Release();
|
||||
}
|
||||
|
||||
ScopedLock( Mutex& locker ) :
|
||||
m_lock( locker )
|
||||
{
|
||||
m_IsLocked = true;
|
||||
m_lock.Acquire();
|
||||
}
|
||||
|
||||
// Provides manual unlocking of a scoped lock prior to object destruction.
|
||||
void Release()
|
||||
{
|
||||
if( !m_IsLocked ) return;
|
||||
m_IsLocked = false;
|
||||
m_lock.Release();
|
||||
}
|
||||
|
||||
// provides manual locking of a scoped lock, to re-lock after a manual unlocking.
|
||||
void Acquire()
|
||||
{
|
||||
if( m_IsLocked ) return;
|
||||
m_lock.Acquire();
|
||||
m_IsLocked = true;
|
||||
}
|
||||
virtual ~ScopedLock() throw();
|
||||
explicit ScopedLock( const Mutex* locker=NULL );
|
||||
explicit ScopedLock( const Mutex& locker );
|
||||
void AssignAndLock( const Mutex& locker );
|
||||
void AssignAndLock( const Mutex* locker );
|
||||
void Release();
|
||||
void Acquire();
|
||||
|
||||
bool IsLocked() const { return m_IsLocked; }
|
||||
|
||||
protected:
|
||||
// Special constructor used by ScopedTryLock
|
||||
ScopedLock( Mutex& locker, bool isTryLock ) :
|
||||
m_lock( locker )
|
||||
{
|
||||
m_IsLocked = isTryLock ? m_lock.TryAcquire() : false;
|
||||
}
|
||||
|
||||
ScopedLock( const Mutex& locker, bool isTryLock );
|
||||
};
|
||||
|
||||
class ScopedTryLock : public ScopedLock
|
||||
{
|
||||
public:
|
||||
ScopedTryLock( Mutex& locker ) : ScopedLock( locker, true ) { }
|
||||
ScopedTryLock( const Mutex& locker ) : ScopedLock( locker, true ) { }
|
||||
virtual ~ScopedTryLock() throw() {}
|
||||
bool Failed() const { return !m_IsLocked; }
|
||||
};
|
||||
|
|
|
@ -19,109 +19,12 @@
|
|||
|
||||
#include "Threading.h"
|
||||
#include "wxGuiTools.h"
|
||||
#include "pxEvents.h"
|
||||
|
||||
using namespace Threading;
|
||||
|
||||
class pxPingEvent;
|
||||
class pxMessageBoxEvent;
|
||||
class pxSynchronousCommandEvent;
|
||||
|
||||
BEGIN_DECLARE_EVENT_TYPES()
|
||||
DECLARE_EVENT_TYPE( pxEvt_Ping, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEvt_IdleEventQueue, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEvt_MessageBox, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEvt_DeleteObject, -1 )
|
||||
//DECLARE_EVENT_TYPE( pxEvt_Assertion, -1 )
|
||||
END_DECLARE_EVENT_TYPES()
|
||||
|
||||
struct MsgboxEventResult
|
||||
{
|
||||
Semaphore WaitForMe;
|
||||
int result;
|
||||
|
||||
MsgboxEventResult()
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// MsgButtons
|
||||
// --------------------------------------------------------------------------------------
|
||||
class MsgButtons
|
||||
{
|
||||
protected:
|
||||
BITFIELD32()
|
||||
bool
|
||||
m_OK :1,
|
||||
m_Cancel :1,
|
||||
m_Yes :1,
|
||||
m_No :1,
|
||||
m_AllowToAll:1,
|
||||
m_Apply :1,
|
||||
m_Abort :1,
|
||||
m_Retry :1,
|
||||
m_Ignore :1,
|
||||
m_Reset :1,
|
||||
m_Close :1;
|
||||
BITFIELD_END
|
||||
|
||||
wxString m_CustomLabel;
|
||||
|
||||
public:
|
||||
MsgButtons() { bitset = 0; }
|
||||
|
||||
MsgButtons& OK() { m_OK = true; return *this; }
|
||||
MsgButtons& Cancel() { m_Cancel = true; return *this; }
|
||||
MsgButtons& Apply() { m_Apply = true; return *this; }
|
||||
MsgButtons& Yes() { m_Yes = true; return *this; }
|
||||
MsgButtons& No() { m_No = true; return *this; }
|
||||
MsgButtons& ToAll() { m_AllowToAll = true; return *this; }
|
||||
|
||||
MsgButtons& Abort() { m_Abort = true; return *this; }
|
||||
MsgButtons& Retry() { m_Retry = true; return *this; }
|
||||
MsgButtons& Ignore() { m_Ignore = true; return *this; }
|
||||
MsgButtons& Reset() { m_Reset = true; return *this; }
|
||||
MsgButtons& Close() { m_Close = true; return *this; }
|
||||
|
||||
MsgButtons& Custom( const wxString& label)
|
||||
{
|
||||
m_CustomLabel = label;
|
||||
return *this;
|
||||
}
|
||||
|
||||
MsgButtons& OKCancel() { m_OK = m_Cancel = true; return *this; }
|
||||
MsgButtons& YesNo() { m_Yes = m_No = true; return *this; }
|
||||
|
||||
bool HasOK() const { return m_OK; }
|
||||
bool HasCancel() const { return m_Cancel; }
|
||||
bool HasApply() const { return m_Apply; }
|
||||
bool HasYes() const { return m_Yes; }
|
||||
bool HasNo() const { return m_No; }
|
||||
bool AllowsToAll() const{ return m_AllowToAll; }
|
||||
|
||||
bool HasAbort() const { return m_Abort; }
|
||||
bool HasRetry() const { return m_Retry; }
|
||||
bool HasIgnore() const { return m_Ignore; }
|
||||
bool HasReset() const { return m_Reset; }
|
||||
bool HasClose() const { return m_Close; }
|
||||
|
||||
bool HasCustom() const { return !m_CustomLabel.IsEmpty(); }
|
||||
const wxString& GetCustomLabel() const { return m_CustomLabel; }
|
||||
|
||||
bool Allows( wxWindowID id ) const;
|
||||
void SetBestFocus( wxWindow* dialog ) const;
|
||||
void SetBestFocus( wxWindow& dialog ) const;
|
||||
|
||||
bool operator ==( const MsgButtons& right ) const
|
||||
{
|
||||
return OpEqu( bitset );
|
||||
}
|
||||
|
||||
bool operator !=( const MsgButtons& right ) const
|
||||
{
|
||||
return !OpEqu( bitset );
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ModalButtonPanel
|
||||
|
@ -138,151 +41,7 @@ public:
|
|||
virtual void OnActionButtonClicked( wxCommandEvent& evt );
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// BaseMessageBoxEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
class BaseMessageBoxEvent : public wxEvent
|
||||
{
|
||||
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(BaseMessageBoxEvent)
|
||||
|
||||
protected:
|
||||
MsgboxEventResult* m_Instdata;
|
||||
wxString m_Content;
|
||||
|
||||
public:
|
||||
virtual ~BaseMessageBoxEvent() throw() { }
|
||||
virtual BaseMessageBoxEvent *Clone() const { return new BaseMessageBoxEvent(*this); }
|
||||
|
||||
explicit BaseMessageBoxEvent( int msgtype=pxEvt_MessageBox, const wxString& content=wxEmptyString );
|
||||
BaseMessageBoxEvent( MsgboxEventResult& instdata, const wxString& content );
|
||||
BaseMessageBoxEvent( const wxString& content );
|
||||
BaseMessageBoxEvent( const BaseMessageBoxEvent& event );
|
||||
|
||||
BaseMessageBoxEvent& SetInstData( MsgboxEventResult& instdata );
|
||||
|
||||
virtual void IssueDialog();
|
||||
|
||||
protected:
|
||||
virtual int _DoDialog() const;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxMessageBoxEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
// This event type is used to transfer message boxes to the main UI thread, and return the
|
||||
// result of the box. It's the only way a message box can be issued from non-main threads
|
||||
// with complete safety in wx2.8.
|
||||
//
|
||||
// For simplicity sake this message box only supports two basic designs. The main design
|
||||
// is a generic message box with confirmation buttons of your choosing. Additionally you
|
||||
// can specify a "scrollableContent" text string, which is added into a read-only richtext
|
||||
// control similar to the console logs and such.
|
||||
//
|
||||
// Future consideration: If wxWidgets 3.0 has improved thread safety, then it should probably
|
||||
// be reasonable for it to work with a more flexable model where the dialog can be created
|
||||
// on a child thread, passed to the main thread, where ShowModal() is run (keeping the nested
|
||||
// message pumps on the main thread where they belong). But so far this is not possible,
|
||||
// because of various subtle issues in wx2.8 design.
|
||||
//
|
||||
class pxMessageBoxEvent : public BaseMessageBoxEvent
|
||||
{
|
||||
typedef BaseMessageBoxEvent _parent;
|
||||
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxMessageBoxEvent)
|
||||
|
||||
protected:
|
||||
wxString m_Title;
|
||||
MsgButtons m_Buttons;
|
||||
|
||||
public:
|
||||
virtual ~pxMessageBoxEvent() throw() { }
|
||||
virtual pxMessageBoxEvent *Clone() const { return new pxMessageBoxEvent(*this); }
|
||||
|
||||
explicit pxMessageBoxEvent( int msgtype=pxEvt_MessageBox );
|
||||
|
||||
pxMessageBoxEvent( MsgboxEventResult& instdata, const wxString& title, const wxString& content, const MsgButtons& buttons );
|
||||
pxMessageBoxEvent( const wxString& title, const wxString& content, const MsgButtons& buttons );
|
||||
pxMessageBoxEvent( const pxMessageBoxEvent& event );
|
||||
|
||||
pxMessageBoxEvent& SetInstData( MsgboxEventResult& instdata );
|
||||
|
||||
protected:
|
||||
virtual int _DoDialog() const;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxAssertionEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
class pxAssertionEvent : public BaseMessageBoxEvent
|
||||
{
|
||||
typedef BaseMessageBoxEvent _parent;
|
||||
DECLARE_DYNAMIC_CLASS_NO_ASSIGN( pxAssertionEvent )
|
||||
|
||||
protected:
|
||||
wxString m_Stacktrace;
|
||||
|
||||
public:
|
||||
virtual ~pxAssertionEvent() throw() { }
|
||||
virtual pxAssertionEvent *Clone() const { return new pxAssertionEvent(*this); }
|
||||
|
||||
pxAssertionEvent();
|
||||
pxAssertionEvent( MsgboxEventResult& instdata, const wxString& content, const wxString& trace );
|
||||
pxAssertionEvent( const wxString& content, const wxString& trace );
|
||||
pxAssertionEvent( const pxAssertionEvent& event );
|
||||
|
||||
pxAssertionEvent& SetInstData( MsgboxEventResult& instdata );
|
||||
pxAssertionEvent& SetStacktrace( const wxString& trace );
|
||||
|
||||
protected:
|
||||
virtual int _DoDialog() const;
|
||||
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxStuckThreadEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
class pxStuckThreadEvent : public BaseMessageBoxEvent
|
||||
{
|
||||
typedef BaseMessageBoxEvent _parent;
|
||||
DECLARE_DYNAMIC_CLASS_NO_ASSIGN( pxStuckThreadEvent )
|
||||
|
||||
protected:
|
||||
Threading::PersistentThread& m_Thread;
|
||||
|
||||
public:
|
||||
virtual ~pxStuckThreadEvent() throw() { }
|
||||
virtual pxStuckThreadEvent *Clone() const { return new pxStuckThreadEvent(*this); }
|
||||
|
||||
pxStuckThreadEvent();
|
||||
pxStuckThreadEvent( PersistentThread& thr );
|
||||
pxStuckThreadEvent( MsgboxEventResult& instdata, PersistentThread& thr );
|
||||
pxStuckThreadEvent( const pxStuckThreadEvent& src);
|
||||
|
||||
protected:
|
||||
virtual int _DoDialog() const;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxPingEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
class pxPingEvent : public wxEvent
|
||||
{
|
||||
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxPingEvent)
|
||||
|
||||
protected:
|
||||
Semaphore* m_PostBack;
|
||||
|
||||
public:
|
||||
virtual ~pxPingEvent() throw() { }
|
||||
virtual pxPingEvent *Clone() const { return new pxPingEvent(*this); }
|
||||
|
||||
explicit pxPingEvent( int msgtype, Semaphore* sema=NULL );
|
||||
explicit pxPingEvent( Semaphore* sema=NULL );
|
||||
pxPingEvent( const pxPingEvent& src );
|
||||
|
||||
Semaphore* GetSemaphore() { return m_PostBack; }
|
||||
};
|
||||
|
||||
typedef void FnType_VoidMethod();
|
||||
typedef void FnType_Void();
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// wxAppWithHelpers
|
||||
|
@ -290,15 +49,12 @@ typedef void FnType_VoidMethod();
|
|||
class wxAppWithHelpers : public wxApp
|
||||
{
|
||||
typedef wxApp _parent;
|
||||
|
||||
|
||||
DECLARE_DYNAMIC_CLASS(wxAppWithHelpers)
|
||||
|
||||
protected:
|
||||
std::vector<Semaphore*> m_PingWhenIdle;
|
||||
std::vector<IDeletableObject*> m_DeleteWhenIdle;
|
||||
std::vector<wxEvent*> m_IdleEventQueue;
|
||||
Threading::Mutex m_DeleteIdleLock;
|
||||
wxTimer m_PingTimer;
|
||||
Threading::Mutex m_IdleEventMutex;
|
||||
wxTimer m_IdleEventTimer;
|
||||
|
||||
public:
|
||||
|
@ -306,34 +62,59 @@ public:
|
|||
virtual ~wxAppWithHelpers() {}
|
||||
|
||||
void CleanUp();
|
||||
|
||||
void DeleteObject( IDeletableObject& obj );
|
||||
void DeleteObject( IDeletableObject* obj )
|
||||
|
||||
void DeleteObject( BaseDeletableObject& obj );
|
||||
void DeleteObject( BaseDeletableObject* obj )
|
||||
{
|
||||
if( obj == NULL ) return;
|
||||
DeleteObject( *obj );
|
||||
}
|
||||
|
||||
void DeleteThread( Threading::PersistentThread& obj );
|
||||
void DeleteThread( Threading::PersistentThread* obj )
|
||||
{
|
||||
if( obj == NULL ) return;
|
||||
DeleteThread( *obj );
|
||||
}
|
||||
|
||||
void PostCommand( void* clientData, int evtType, int intParam=0, long longParam=0, const wxString& stringParam=wxEmptyString );
|
||||
void PostCommand( int evtType, int intParam=0, long longParam=0, const wxString& stringParam=wxEmptyString );
|
||||
void PostMethod( FnType_VoidMethod* method );
|
||||
void PostMethod( FnType_Void* method );
|
||||
void PostIdleMethod( FnType_Void* method );
|
||||
void ProcessMethod( void (*method)() );
|
||||
|
||||
sptr ProcessCommand( void* clientData, int evtType, int intParam=0, long longParam=0, const wxString& stringParam=wxEmptyString );
|
||||
sptr ProcessCommand( int evtType, int intParam=0, long longParam=0, const wxString& stringParam=wxEmptyString );
|
||||
|
||||
void ProcessAction( pxInvokeActionEvent& evt );
|
||||
void PostAction( const pxInvokeActionEvent& evt );
|
||||
|
||||
bool PostMethodMyself( void (*method)() );
|
||||
|
||||
void Ping();
|
||||
bool OnInit();
|
||||
//int OnExit();
|
||||
|
||||
void AddIdleEvent( const wxEvent& evt );
|
||||
|
||||
void PostEvent( const wxEvent& evt );
|
||||
bool ProcessEvent( wxEvent& evt );
|
||||
bool ProcessEvent( wxEvent* evt );
|
||||
|
||||
bool ProcessEvent( pxInvokeActionEvent& evt );
|
||||
bool ProcessEvent( pxInvokeActionEvent* evt );
|
||||
|
||||
protected:
|
||||
void IdleEventDispatcher( const char* action );
|
||||
void PingDispatcher( const char* action );
|
||||
void DeletionDispatcher();
|
||||
|
||||
void OnIdleEvent( wxIdleEvent& evt );
|
||||
void OnPingEvent( pxPingEvent& evt );
|
||||
void OnAddEventToIdleQueue( wxEvent& evt );
|
||||
void OnPingTimeout( wxTimerEvent& evt );
|
||||
void OnStartIdleEventTimer( wxEvent& evt );
|
||||
void OnIdleEventTimeout( wxTimerEvent& evt );
|
||||
void OnMessageBox( BaseMessageBoxEvent& evt );
|
||||
void OnDeleteObject( wxCommandEvent& evt );
|
||||
void OnDeleteThread( wxCommandEvent& evt );
|
||||
void OnSynchronousCommand( pxSynchronousCommandEvent& evt );
|
||||
void OnInvokeAction( pxInvokeActionEvent& evt );
|
||||
|
||||
};
|
||||
|
||||
namespace Msgbox
|
||||
|
|
|
@ -291,6 +291,10 @@ namespace pxSizerFlags
|
|||
extern wxSizerFlags Checkbox();
|
||||
};
|
||||
|
||||
BEGIN_DECLARE_EVENT_TYPES()
|
||||
DECLARE_EVENT_TYPE( pxEvt_OnThreadCleanup, -1 );
|
||||
END_DECLARE_EVENT_TYPES()
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// wxDialogWithHelpers
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
|
|
@ -292,10 +292,11 @@ class FormatBuffer : public Mutex
|
|||
public:
|
||||
bool& clearbit;
|
||||
SafeArray<CharType> buffer;
|
||||
wxMBConvUTF8 ConvUTF8;
|
||||
|
||||
FormatBuffer( bool& bit_to_clear_on_destruction ) :
|
||||
clearbit( bit_to_clear_on_destruction )
|
||||
, buffer( 4096, wxsFormat( L"%s Format Buffer", (sizeof(CharType)==1) ? "Ascii" : "Unicode" ) )
|
||||
FormatBuffer( bool& bit_to_clear_on_destruction )
|
||||
: clearbit( bit_to_clear_on_destruction )
|
||||
, buffer( 4096, wxsFormat( L"%s Format Buffer", (sizeof(CharType)==1) ? "Ascii" : "Unicode" ) )
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -376,12 +377,20 @@ static wxString ascii_format_string(const char* fmt, va_list argptr)
|
|||
{
|
||||
if( ascii_buffer_is_deleted )
|
||||
{
|
||||
// This means that the program is shutting down and the C++ destructors are
|
||||
// running, randomly deallocating static variables from existence. We handle it
|
||||
// as gracefully as possible by allocating local vars to do our bidding (slow, but
|
||||
// ultimately necessary!)
|
||||
|
||||
SafeArray<char> localbuf( 4096, L"Temporary Ascii Formatting Buffer" );
|
||||
format_that_ascii_mess( localbuf, fmt, argptr );
|
||||
return fromUTF8( localbuf.GetPtr() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is normal operation. The static buffers are available for use, and we use
|
||||
// them for sake of efficiency (fewer heap allocs, for sure!)
|
||||
|
||||
ScopedLock locker( ascii_buffer );
|
||||
format_that_ascii_mess( ascii_buffer.buffer, fmt, argptr );
|
||||
return fromUTF8( ascii_buffer.buffer.GetPtr() );
|
||||
|
@ -391,6 +400,8 @@ static wxString ascii_format_string(const char* fmt, va_list argptr)
|
|||
|
||||
static wxString unicode_format_string(const wxChar* fmt, va_list argptr)
|
||||
{
|
||||
// See above for the explanation on the _is_deleted flags.
|
||||
|
||||
if( unicode_buffer_is_deleted )
|
||||
{
|
||||
SafeArray<wxChar> localbuf( 4096, L"Temporary Unicode Formatting Buffer" );
|
||||
|
|
|
@ -135,9 +135,9 @@ __forceinline void pxOnAssert( const DiagnosticOrigin& origin, const char* msg)
|
|||
// Exception Namespace Implementations (Format message handlers for general exceptions)
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
Exception::BaseException::~BaseException() throw() {}
|
||||
BaseException::~BaseException() throw() {}
|
||||
|
||||
void Exception::BaseException::InitBaseEx( const wxString& msg_eng, const wxString& msg_xlt )
|
||||
void BaseException::InitBaseEx( const wxString& msg_eng, const wxString& msg_xlt )
|
||||
{
|
||||
m_message_diag = msg_eng;
|
||||
m_message_user = msg_xlt.IsEmpty() ? msg_eng : msg_xlt;
|
||||
|
@ -155,7 +155,7 @@ void Exception::BaseException::InitBaseEx( const wxString& msg_eng, const wxStri
|
|||
|
||||
// given message is assumed to be a translation key, and will be stored in translated
|
||||
// and untranslated forms.
|
||||
void Exception::BaseException::InitBaseEx( const char* msg_eng )
|
||||
void BaseException::InitBaseEx( const char* msg_eng )
|
||||
{
|
||||
m_message_diag = GetEnglish( msg_eng );
|
||||
m_message_user = GetTranslation( msg_eng );
|
||||
|
@ -166,11 +166,22 @@ void Exception::BaseException::InitBaseEx( const char* msg_eng )
|
|||
#endif
|
||||
}
|
||||
|
||||
wxString Exception::BaseException::FormatDiagnosticMessage() const
|
||||
wxString BaseException::FormatDiagnosticMessage() const
|
||||
{
|
||||
return m_message_diag + L"\n\n" + m_stacktrace;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
Exception::RuntimeError::RuntimeError( const std::runtime_error& ex, const wxString& prefix )
|
||||
{
|
||||
const wxString msg( wxsFormat( L"%sSTL Runtime Error: %s",
|
||||
(prefix.IsEmpty() ? prefix : wxsFormat(L"(%s) ", prefix)),
|
||||
fromUTF8( ex.what() ).c_str()
|
||||
) );
|
||||
|
||||
BaseException::InitBaseEx( msg, msg );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
wxString Exception::CancelEvent::FormatDiagnosticMessage() const
|
||||
{
|
||||
|
|
|
@ -36,6 +36,8 @@ Threading::Mutex::Mutex()
|
|||
pthread_mutex_init( &m_mutex, NULL );
|
||||
}
|
||||
|
||||
static wxTimeSpan def_detach_timeout( 0, 0, 6, 0 );
|
||||
|
||||
void Threading::Mutex::Detach()
|
||||
{
|
||||
if( EBUSY != pthread_mutex_destroy(&m_mutex) ) return;
|
||||
|
@ -52,7 +54,7 @@ void Threading::Mutex::Detach()
|
|||
if( pxAssertDev( result != EBUSY, "Detachment of a recursively-locked mutex (self-locked!)." ) ) return;
|
||||
}
|
||||
|
||||
if( Wait(def_deadlock_timeout) )
|
||||
if( Wait(def_detach_timeout) )
|
||||
pthread_mutex_destroy( &m_mutex );
|
||||
else
|
||||
Console.Error( "(Thread Log) Mutex cleanup failed due to possible deadlock.");
|
||||
|
@ -65,7 +67,7 @@ Threading::Mutex::~Mutex() throw()
|
|||
} DESTRUCTOR_CATCHALL;
|
||||
}
|
||||
|
||||
Threading::MutexLockRecursive::MutexLockRecursive() : Mutex( false )
|
||||
Threading::MutexRecursive::MutexRecursive() : Mutex( false )
|
||||
{
|
||||
if( _InterlockedIncrement( &_attr_refcount ) == 1 )
|
||||
{
|
||||
|
@ -79,7 +81,7 @@ Threading::MutexLockRecursive::MutexLockRecursive() : Mutex( false )
|
|||
err = pthread_mutex_init( &m_mutex, &_attr_recursive );
|
||||
}
|
||||
|
||||
Threading::MutexLockRecursive::~MutexLockRecursive() throw()
|
||||
Threading::MutexRecursive::~MutexRecursive() throw()
|
||||
{
|
||||
if( _InterlockedDecrement( &_attr_refcount ) == 0 )
|
||||
pthread_mutexattr_destroy( &_attr_recursive );
|
||||
|
@ -100,7 +102,7 @@ void Threading::Mutex::Recreate()
|
|||
// unlocked.
|
||||
bool Threading::Mutex::RecreateIfLocked()
|
||||
{
|
||||
if( !Wait(def_deadlock_timeout) )
|
||||
if( !Wait(def_detach_timeout) )
|
||||
{
|
||||
Recreate();
|
||||
return true;
|
||||
|
@ -151,8 +153,7 @@ void Threading::Mutex::Acquire()
|
|||
}
|
||||
else if( _WaitGui_RecursionGuard( "Mutex::Acquire" ) )
|
||||
{
|
||||
if( !AcquireWithoutYield(def_deadlock_timeout) )
|
||||
throw Exception::ThreadDeadlock();
|
||||
AcquireWithoutYield();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -177,12 +178,6 @@ bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
|
|||
else if( _WaitGui_RecursionGuard( "Mutex::Acquire(timeout)" ) )
|
||||
{
|
||||
ScopedBusyCursor hourglass( Cursor_ReallyBusy );
|
||||
|
||||
if( timeout > def_deadlock_timeout )
|
||||
{
|
||||
if( AcquireWithoutYield(def_deadlock_timeout) ) return true;
|
||||
throw Exception::ThreadDeadlock();
|
||||
}
|
||||
return AcquireWithoutYield( timeout );
|
||||
}
|
||||
else
|
||||
|
@ -199,9 +194,6 @@ bool Threading::Mutex::Acquire( const wxTimeSpan& timeout )
|
|||
return countdown.GetMilliseconds() > 0;
|
||||
}
|
||||
|
||||
// Looks like a potential deadlock; throw an exception!
|
||||
throw Exception::ThreadDeadlock();
|
||||
|
||||
#else
|
||||
return AcquireWithoutYield();
|
||||
#endif
|
||||
|
@ -223,6 +215,12 @@ void Threading::Mutex::Wait()
|
|||
Release();
|
||||
}
|
||||
|
||||
void Threading::Mutex::WaitWithoutYield()
|
||||
{
|
||||
AcquireWithoutYield();
|
||||
Release();
|
||||
}
|
||||
|
||||
// Performs a wait on a locked mutex, or returns instantly if the mutex is unlocked.
|
||||
// (Implemented internally as a simple Acquire/Release pair.)
|
||||
//
|
||||
|
@ -243,3 +241,69 @@ bool Threading::Mutex::Wait( const wxTimeSpan& timeout )
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Threading::Mutex::WaitWithoutYield( const wxTimeSpan& timeout )
|
||||
{
|
||||
if( AcquireWithoutYield(timeout) )
|
||||
{
|
||||
Release();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ScopedLock Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
Threading::ScopedLock::~ScopedLock() throw()
|
||||
{
|
||||
if( m_IsLocked && m_lock )
|
||||
m_lock->Release();
|
||||
}
|
||||
|
||||
Threading::ScopedLock::ScopedLock( const Mutex* locker )
|
||||
{
|
||||
AssignAndLock( locker );
|
||||
}
|
||||
|
||||
Threading::ScopedLock::ScopedLock( const Mutex& locker )
|
||||
{
|
||||
AssignAndLock( locker );
|
||||
}
|
||||
|
||||
void Threading::ScopedLock::AssignAndLock( const Mutex& locker )
|
||||
{
|
||||
AssignAndLock( &locker );
|
||||
}
|
||||
|
||||
void Threading::ScopedLock::AssignAndLock( const Mutex* locker )
|
||||
{
|
||||
m_lock = const_cast<Mutex*>(locker);
|
||||
if( !m_lock ) return;
|
||||
|
||||
m_IsLocked = true;
|
||||
m_lock->Acquire();
|
||||
}
|
||||
|
||||
// Provides manual unlocking of a scoped lock prior to object destruction.
|
||||
void Threading::ScopedLock::Release()
|
||||
{
|
||||
if( !m_IsLocked ) return;
|
||||
m_IsLocked = false;
|
||||
if( m_lock ) m_lock->Release();
|
||||
}
|
||||
|
||||
// provides manual locking of a scoped lock, to re-lock after a manual unlocking.
|
||||
void Threading::ScopedLock::Acquire()
|
||||
{
|
||||
if( m_IsLocked || !m_lock ) return;
|
||||
m_lock->Acquire();
|
||||
m_IsLocked = true;
|
||||
}
|
||||
|
||||
Threading::ScopedLock::ScopedLock( const Mutex& locker, bool isTryLock )
|
||||
{
|
||||
m_lock = const_cast<Mutex*>(&locker);
|
||||
if( !m_lock ) return;
|
||||
m_IsLocked = isTryLock ? m_lock->TryAcquire() : false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/* 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 "RwMutex.h"
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// RwMutex
|
||||
// --------------------------------------------------------------------------------------
|
||||
Threading::RwMutex::RwMutex()
|
||||
{
|
||||
pthread_rwlock_init( &m_rwlock, NULL );
|
||||
}
|
||||
|
||||
Threading::RwMutex::~RwMutex() throw()
|
||||
{
|
||||
pthread_rwlock_destroy( &m_rwlock );
|
||||
}
|
||||
|
||||
void Threading::RwMutex::AcquireRead()
|
||||
{
|
||||
pthread_rwlock_rdlock( &m_rwlock );
|
||||
}
|
||||
|
||||
void Threading::RwMutex::AcquireWrite()
|
||||
{
|
||||
pthread_rwlock_wrlock( &m_rwlock );
|
||||
}
|
||||
|
||||
bool Threading::RwMutex::TryAcquireRead()
|
||||
{
|
||||
return pthread_rwlock_tryrdlock( &m_rwlock ) != EBUSY;
|
||||
}
|
||||
|
||||
bool Threading::RwMutex::TryAcquireWrite()
|
||||
{
|
||||
return pthread_rwlock_trywrlock( &m_rwlock ) != EBUSY;
|
||||
}
|
||||
|
||||
void Threading::RwMutex::Release()
|
||||
{
|
||||
pthread_rwlock_unlock( &m_rwlock );
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
//
|
||||
// --------------------------------------------------------------------------------------
|
||||
Threading::BaseScopedReadWriteLock::~BaseScopedReadWriteLock() throw()
|
||||
{
|
||||
if( m_IsLocked )
|
||||
m_lock.Release();
|
||||
}
|
||||
|
||||
// Provides manual unlocking of a scoped lock prior to object destruction.
|
||||
void Threading::BaseScopedReadWriteLock::Release()
|
||||
{
|
||||
if( !m_IsLocked ) return;
|
||||
m_IsLocked = false;
|
||||
m_lock.Release();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ScopedReadLock / ScopedWriteLock
|
||||
// --------------------------------------------------------------------------------------
|
||||
Threading::ScopedReadLock::ScopedReadLock( RwMutex& locker )
|
||||
: BaseScopedReadWriteLock( locker )
|
||||
{
|
||||
m_IsLocked = true;
|
||||
m_lock.AcquireRead();
|
||||
}
|
||||
|
||||
// provides manual locking of a scoped lock, to re-lock after a manual unlocking.
|
||||
void Threading::ScopedReadLock::Acquire()
|
||||
{
|
||||
if( m_IsLocked ) return;
|
||||
m_lock.AcquireRead();
|
||||
m_IsLocked = true;
|
||||
}
|
||||
|
||||
Threading::ScopedWriteLock::ScopedWriteLock( RwMutex& locker )
|
||||
: BaseScopedReadWriteLock( locker )
|
||||
{
|
||||
m_IsLocked = true;
|
||||
m_lock.AcquireWrite();
|
||||
}
|
||||
|
||||
// provides manual locking of a scoped lock, to re-lock after a manual unlocking.
|
||||
void Threading::ScopedWriteLock::Acquire()
|
||||
{
|
||||
if( m_IsLocked ) return;
|
||||
m_lock.AcquireWrite();
|
||||
m_IsLocked = true;
|
||||
}
|
||||
|
||||
// Special constructor used by ScopedTryLock
|
||||
Threading::ScopedWriteLock::ScopedWriteLock( RwMutex& locker, bool isTryLock )
|
||||
: BaseScopedReadWriteLock( locker )
|
||||
{
|
||||
//m_IsLocked = isTryLock ? m_lock.TryAcquireWrite() : false;
|
||||
}
|
|
@ -92,8 +92,7 @@ void Threading::Semaphore::Wait()
|
|||
else if( _WaitGui_RecursionGuard( "Semaphore::Wait" ) )
|
||||
{
|
||||
ScopedBusyCursor hourglass( Cursor_ReallyBusy );
|
||||
if( !WaitWithoutYield(def_yieldgui_interval) ) // default is 4 seconds
|
||||
throw Exception::ThreadDeadlock();
|
||||
WaitWithoutYield();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -128,11 +127,6 @@ bool Threading::Semaphore::Wait( const wxTimeSpan& timeout )
|
|||
else if( _WaitGui_RecursionGuard( "Semaphore::Wait(timeout)" ) )
|
||||
{
|
||||
ScopedBusyCursor hourglass( Cursor_ReallyBusy );
|
||||
if( timeout > def_deadlock_timeout )
|
||||
{
|
||||
if( WaitWithoutYield(def_deadlock_timeout) ) return true;
|
||||
throw Exception::ThreadDeadlock();
|
||||
}
|
||||
return WaitWithoutYield( timeout );
|
||||
}
|
||||
else
|
||||
|
@ -165,7 +159,8 @@ void Threading::Semaphore::WaitNoCancel()
|
|||
{
|
||||
int oldstate;
|
||||
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
|
||||
WaitWithoutYield();
|
||||
//WaitWithoutYield();
|
||||
Wait();
|
||||
pthread_setcancelstate( oldstate, NULL );
|
||||
}
|
||||
|
||||
|
@ -173,7 +168,8 @@ void Threading::Semaphore::WaitNoCancel( const wxTimeSpan& timeout )
|
|||
{
|
||||
int oldstate;
|
||||
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
|
||||
WaitWithoutYield( timeout );
|
||||
//WaitWithoutYield( timeout );
|
||||
Wait( timeout );
|
||||
pthread_setcancelstate( oldstate, NULL );
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,16 @@ const wxRect wxDefaultRect( wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, wxDe
|
|||
|
||||
__forceinline wxString fromUTF8( const char* src )
|
||||
{
|
||||
return wxString::FromUTF8( src );
|
||||
// IMPORTANT: We cannot use wxString::FromUTF8 because it *stupidly* relies on a C++ global instance of
|
||||
// wxMBConvUTF8(). C++ initializes and destroys these globals at random, so any object constructor or
|
||||
// destructor that attempts to do logging may crash the app (either during startup or during exit) unless
|
||||
// we use a LOCAL instance of wxMBConvUTF8(). --air
|
||||
|
||||
// Performance? No worries. wxMBConvUTF8() is virtually free. Initializing a stack copy of the class
|
||||
// is just as efficient as passing a pointer to a pre-instanced global. (which makes me wonder wh wxWidgets
|
||||
// uses the stupid globals in the first place!) --air
|
||||
|
||||
return wxString( src, wxMBConvUTF8() );
|
||||
}
|
||||
|
||||
__forceinline wxString fromAscii( const char* src )
|
||||
|
|
|
@ -33,15 +33,33 @@ template class EventSource< EventListener_Thread >;
|
|||
// to avoid gui deadlock).
|
||||
const wxTimeSpan Threading::def_yieldgui_interval( 0, 0, 0, 100 );
|
||||
|
||||
// three second interval for deadlock protection on waitgui.
|
||||
const wxTimeSpan Threading::def_deadlock_timeout( 0, 0, 3, 0 );
|
||||
class StaticMutex : public Mutex
|
||||
{
|
||||
protected:
|
||||
bool& m_DeletedFlag;
|
||||
|
||||
public:
|
||||
StaticMutex( bool& deletedFlag )
|
||||
: m_DeletedFlag( deletedFlag )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~StaticMutex() throw()
|
||||
{
|
||||
m_DeletedFlag = true;
|
||||
}
|
||||
};
|
||||
|
||||
static pthread_key_t curthread_key = NULL;
|
||||
static s32 total_key_count = 0;
|
||||
static Mutex total_key_lock;
|
||||
|
||||
static bool tkl_destructed = false;
|
||||
static StaticMutex total_key_lock( tkl_destructed );
|
||||
|
||||
static void make_curthread_key()
|
||||
{
|
||||
pxAssumeDev( !tkl_destructed, "total_key_lock is destroyed; program is shutting down; cannot create new thread key." );
|
||||
|
||||
ScopedLock lock( total_key_lock );
|
||||
if( total_key_count++ != 0 ) return;
|
||||
|
||||
|
@ -54,7 +72,10 @@ static void make_curthread_key()
|
|||
|
||||
static void unmake_curthread_key()
|
||||
{
|
||||
ScopedLock lock( total_key_lock );
|
||||
ScopedLock lock;
|
||||
if( !tkl_destructed )
|
||||
lock.AssignAndLock( total_key_lock );
|
||||
|
||||
if( --total_key_count > 0 ) return;
|
||||
|
||||
if( curthread_key != NULL )
|
||||
|
@ -63,6 +84,11 @@ static void unmake_curthread_key()
|
|||
curthread_key = NULL;
|
||||
}
|
||||
|
||||
void Threading::pxTestCancel()
|
||||
{
|
||||
pthread_testcancel();
|
||||
}
|
||||
|
||||
// Returns a handle to the current persistent thread. If the current thread does not belong
|
||||
// to the PersistentThread table, NULL is returned. Since the main/ui thread is not created
|
||||
// through PersistentThread it will also return NULL. Callers can use wxThread::IsMain() to
|
||||
|
@ -158,20 +184,6 @@ Threading::PersistentThread::~PersistentThread() throw()
|
|||
Threading::Sleep( 1 );
|
||||
Detach();
|
||||
}
|
||||
catch( Exception::ThreadDeadlock& ex )
|
||||
{
|
||||
// Windows allows for a thread to be terminated forcefully, but it's not really
|
||||
// a safe thing to do since typically threads are acquiring and releasing locks
|
||||
// and semaphores all the time. And terminating threads isn't really cross-platform
|
||||
// either so let's just not bother.
|
||||
|
||||
// Additionally since this is a destructor most of our derived class info is lost,
|
||||
// so we can't allow for customized deadlock handlers, least not in any useful
|
||||
// context. So let's just log the condition and move on.
|
||||
|
||||
Console.Error( L"(Thread Log) Thread destructor for '%s' timed out with error:\n\t",
|
||||
m_name.c_str(), ex.FormatDiagnosticMessage().c_str() );
|
||||
}
|
||||
DESTRUCTOR_CATCHALL
|
||||
}
|
||||
|
||||
|
@ -265,7 +277,6 @@ bool Threading::PersistentThread::Detach()
|
|||
|
||||
bool Threading::PersistentThread::_basecancel()
|
||||
{
|
||||
// Prevent simultaneous startup and cancel:
|
||||
if( !m_running ) return false;
|
||||
|
||||
if( m_detached )
|
||||
|
@ -339,6 +350,12 @@ void Threading::PersistentThread::Block()
|
|||
WaitOnSelf( m_lock_InThread );
|
||||
}
|
||||
|
||||
bool Threading::PersistentThread::Block( const wxTimeSpan& timeout )
|
||||
{
|
||||
AffinityAssert_DisallowFromSelf(pxDiagSpot);
|
||||
return WaitOnSelf( m_lock_InThread, timeout );
|
||||
}
|
||||
|
||||
bool Threading::PersistentThread::IsSelf() const
|
||||
{
|
||||
// Detached threads may have their pthread handles recycled as newer threads, causing
|
||||
|
@ -402,7 +419,7 @@ void Threading::PersistentThread::WaitOnSelf( Semaphore& sem ) const
|
|||
|
||||
while( true )
|
||||
{
|
||||
if( sem.Wait( wxTimeSpan(0, 0, 0, 333) ) ) return;
|
||||
if( sem.WaitWithoutYield( wxTimeSpan(0, 0, 0, 333) ) ) return;
|
||||
_selfRunningTest( L"semaphore" );
|
||||
}
|
||||
}
|
||||
|
@ -426,7 +443,7 @@ void Threading::PersistentThread::WaitOnSelf( Mutex& mutex ) const
|
|||
|
||||
while( true )
|
||||
{
|
||||
if( mutex.Wait( wxTimeSpan(0, 0, 0, 333) ) ) return;
|
||||
if( mutex.WaitWithoutYield( wxTimeSpan(0, 0, 0, 333) ) ) return;
|
||||
_selfRunningTest( L"mutex" );
|
||||
}
|
||||
}
|
||||
|
@ -442,7 +459,7 @@ bool Threading::PersistentThread::WaitOnSelf( Semaphore& sem, const wxTimeSpan&
|
|||
while( runningout.GetMilliseconds() > 0 )
|
||||
{
|
||||
const wxTimeSpan interval( (SelfWaitInterval < runningout) ? SelfWaitInterval : runningout );
|
||||
if( sem.Wait( interval ) ) return true;
|
||||
if( sem.WaitWithoutYield( interval ) ) return true;
|
||||
_selfRunningTest( L"semaphore" );
|
||||
runningout -= interval;
|
||||
}
|
||||
|
@ -458,7 +475,7 @@ bool Threading::PersistentThread::WaitOnSelf( Mutex& mutex, const wxTimeSpan& ti
|
|||
while( runningout.GetMilliseconds() > 0 )
|
||||
{
|
||||
const wxTimeSpan interval( (SelfWaitInterval < runningout) ? SelfWaitInterval : runningout );
|
||||
if( mutex.Wait( interval ) ) return true;
|
||||
if( mutex.WaitWithoutYield( interval ) ) return true;
|
||||
_selfRunningTest( L"mutex" );
|
||||
runningout -= interval;
|
||||
}
|
||||
|
@ -487,17 +504,7 @@ void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::*
|
|||
//
|
||||
catch( std::runtime_error& ex )
|
||||
{
|
||||
m_except = new Exception::RuntimeError(
|
||||
// Diagnostic message:
|
||||
wxsFormat( L"(thread: %s) STL Runtime Error: %s",
|
||||
GetName().c_str(), fromUTF8( ex.what() ).c_str()
|
||||
),
|
||||
|
||||
// User Message (not translated, std::exception doesn't have that kind of fancy!
|
||||
wxsFormat( L"A runtime error occurred in %s:\n\n%s (STL)",
|
||||
GetName().c_str(), fromUTF8( ex.what() ).c_str()
|
||||
)
|
||||
);
|
||||
m_except = new Exception::RuntimeError( ex, GetName().c_str() );
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -513,20 +520,20 @@ void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::*
|
|||
// the MSVC debugger (or by silent random annoying fail on debug-less linux).
|
||||
/*catch( std::logic_error& ex )
|
||||
{
|
||||
throw Exception::BaseException( wxsFormat( L"(thread: %s) STL Logic Error: %s",
|
||||
throw BaseException( wxsFormat( L"(thread: %s) STL Logic Error: %s",
|
||||
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
|
||||
);
|
||||
}
|
||||
catch( std::exception& ex )
|
||||
{
|
||||
throw Exception::BaseException( wxsFormat( L"(thread: %s) STL exception: %s",
|
||||
throw BaseException( wxsFormat( L"(thread: %s) STL exception: %s",
|
||||
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
|
||||
);
|
||||
}*/
|
||||
// ----------------------------------------------------------------------------
|
||||
// BaseException -- same deal as LogicErrors.
|
||||
//
|
||||
catch( Exception::BaseException& ex )
|
||||
catch( BaseException& ex )
|
||||
{
|
||||
m_except = ex.Clone();
|
||||
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
|
||||
|
@ -541,6 +548,10 @@ void Threading::PersistentThread::_ThreadCleanup()
|
|||
AffinityAssert_AllowFromSelf(pxDiagSpot);
|
||||
_try_virtual_invoke( &PersistentThread::OnCleanupInThread );
|
||||
m_lock_InThread.Release();
|
||||
|
||||
// Must set m_running LAST, as thread destructors depend on this value (it is used
|
||||
// to avoid destruction of the thread until all internal data use has stopped.
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
wxString Threading::PersistentThread::GetName() const
|
||||
|
@ -590,12 +601,10 @@ void Threading::PersistentThread::OnStart()
|
|||
m_sem_startup.Reset();
|
||||
}
|
||||
|
||||
// Extending classes that override this method shoul always call it last from their
|
||||
// Extending classes that override this method should always call it last from their
|
||||
// personal implementations.
|
||||
void Threading::PersistentThread::OnCleanupInThread()
|
||||
{
|
||||
m_running = false;
|
||||
|
||||
if( curthread_key != NULL )
|
||||
pthread_setspecific( curthread_key, NULL );
|
||||
|
||||
|
@ -605,6 +614,8 @@ void Threading::PersistentThread::OnCleanupInThread()
|
|||
|
||||
m_native_handle = NULL;
|
||||
m_native_id = 0;
|
||||
|
||||
m_evtsrc_OnDelete.Dispatch( 0 );
|
||||
}
|
||||
|
||||
// passed into pthread_create, and is used to dispatch the thread's object oriented
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
namespace Threading
|
||||
{
|
||||
extern const wxTimeSpan def_yieldgui_interval;
|
||||
extern const wxTimeSpan def_deadlock_timeout;
|
||||
|
||||
extern bool _WaitGui_RecursionGuard( const char* guardname );
|
||||
}
|
||||
|
|
|
@ -16,14 +16,19 @@
|
|||
#include "PrecompiledHeader.h"
|
||||
#include "wxAppWithHelpers.h"
|
||||
|
||||
DEFINE_EVENT_TYPE( pxEvt_Ping );
|
||||
DEFINE_EVENT_TYPE( pxEvt_IdleEventQueue );
|
||||
DEFINE_EVENT_TYPE( pxEvt_MessageBox );
|
||||
#include "PersistentThread.h"
|
||||
|
||||
DEFINE_EVENT_TYPE( pxEvt_DeleteObject );
|
||||
//DEFINE_EVENT_TYPE( pxEvt_Assertion );
|
||||
DEFINE_EVENT_TYPE( pxEvt_DeleteThread );
|
||||
DEFINE_EVENT_TYPE( pxEvt_StartIdleEventTimer );
|
||||
DEFINE_EVENT_TYPE( pxEvt_InvokeAction );
|
||||
DEFINE_EVENT_TYPE( pxEvt_SynchronousCommand );
|
||||
|
||||
|
||||
void IDeletableObject::DoDeletion()
|
||||
IMPLEMENT_DYNAMIC_CLASS( pxSimpleEvent, wxEvent )
|
||||
|
||||
|
||||
void BaseDeletableObject::DoDeletion()
|
||||
{
|
||||
wxAppWithHelpers* app = wxDynamicCast( wxApp::GetInstance(), wxAppWithHelpers );
|
||||
pxAssume( app != NULL );
|
||||
|
@ -31,27 +36,171 @@ void IDeletableObject::DoDeletion()
|
|||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxPingEvent Implementations
|
||||
// pxInvokeActionEvent Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS( pxPingEvent, wxEvent )
|
||||
IMPLEMENT_DYNAMIC_CLASS( pxInvokeActionEvent, wxEvent )
|
||||
|
||||
pxPingEvent::pxPingEvent( int msgtype, Semaphore* sema )
|
||||
pxInvokeActionEvent::pxInvokeActionEvent( SynchronousActionState* sema, int msgtype )
|
||||
: wxEvent( 0, msgtype )
|
||||
{
|
||||
m_PostBack = sema;
|
||||
m_state = sema;
|
||||
}
|
||||
|
||||
pxPingEvent::pxPingEvent( Semaphore* sema )
|
||||
: wxEvent( 0, pxEvt_Ping )
|
||||
pxInvokeActionEvent::pxInvokeActionEvent( SynchronousActionState& sema, int msgtype )
|
||||
: wxEvent( 0, msgtype )
|
||||
{
|
||||
m_PostBack = sema;
|
||||
m_state = &sema;
|
||||
}
|
||||
|
||||
pxPingEvent::pxPingEvent( const pxPingEvent& src )
|
||||
pxInvokeActionEvent::pxInvokeActionEvent( const pxInvokeActionEvent& src )
|
||||
: wxEvent( src )
|
||||
{
|
||||
m_PostBack = src.m_PostBack;
|
||||
m_state = src.m_state;
|
||||
}
|
||||
|
||||
void pxInvokeActionEvent::SetException( const BaseException& ex )
|
||||
{
|
||||
SetException( ex.Clone() );
|
||||
}
|
||||
|
||||
void pxInvokeActionEvent::SetException( BaseException* ex )
|
||||
{
|
||||
const wxString& prefix( wxsFormat(L"(%s) ", GetClassInfo()->GetClassName()) );
|
||||
ex->DiagMsg() = prefix + ex->DiagMsg();
|
||||
|
||||
if( !m_state )
|
||||
{
|
||||
ScopedPtr<BaseException> exptr( ex ); // auto-delete it after handling.
|
||||
ex->Rethrow();
|
||||
}
|
||||
|
||||
m_state->SetException( ex );
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxSynchronousCommandEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
IMPLEMENT_DYNAMIC_CLASS( pxSynchronousCommandEvent, wxCommandEvent )
|
||||
|
||||
pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, wxEventType commandType, int winid)
|
||||
: wxCommandEvent( pxEvt_SynchronousCommand, winid )
|
||||
{
|
||||
m_sync = sema;
|
||||
m_realEvent = commandType;
|
||||
}
|
||||
|
||||
pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, wxEventType commandType, int winid)
|
||||
: wxCommandEvent( pxEvt_SynchronousCommand )
|
||||
{
|
||||
m_sync = &sema;
|
||||
m_realEvent = commandType;
|
||||
}
|
||||
|
||||
pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, const wxCommandEvent& evt )
|
||||
: wxCommandEvent( evt )
|
||||
{
|
||||
m_sync = sema;
|
||||
m_realEvent = evt.GetEventType();
|
||||
SetEventType( pxEvt_SynchronousCommand );
|
||||
}
|
||||
|
||||
pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, const wxCommandEvent& evt )
|
||||
: wxCommandEvent( evt )
|
||||
{
|
||||
m_sync = &sema;
|
||||
m_realEvent = evt.GetEventType();
|
||||
SetEventType( pxEvt_SynchronousCommand );
|
||||
}
|
||||
|
||||
pxSynchronousCommandEvent::pxSynchronousCommandEvent( const pxSynchronousCommandEvent& src )
|
||||
: wxCommandEvent( src )
|
||||
{
|
||||
m_sync = src.m_sync;
|
||||
m_realEvent = src.m_realEvent;
|
||||
}
|
||||
|
||||
void pxSynchronousCommandEvent::SetException( const BaseException& ex )
|
||||
{
|
||||
if( !m_sync ) ex.Rethrow();
|
||||
m_sync->SetException( ex );
|
||||
}
|
||||
|
||||
void pxSynchronousCommandEvent::SetException( BaseException* ex )
|
||||
{
|
||||
if( !m_sync )
|
||||
{
|
||||
ScopedPtr<BaseException> exptr( ex ); // auto-delete it after handling.
|
||||
ex->Rethrow();
|
||||
}
|
||||
|
||||
m_sync->SetException( ex );
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxInvokeMethodEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Unlike pxPingEvent, the Semaphore belonging to this event is typically posted when the
|
||||
// invoked method is completed. If the method can be executed in non-blocking fashion then
|
||||
// it should leave the semaphore postback NULL.
|
||||
//
|
||||
class pxInvokeMethodEvent : public pxInvokeActionEvent
|
||||
{
|
||||
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxInvokeMethodEvent)
|
||||
|
||||
typedef pxInvokeActionEvent _parent;
|
||||
|
||||
protected:
|
||||
void (*m_Method)();
|
||||
|
||||
public:
|
||||
virtual ~pxInvokeMethodEvent() throw() { }
|
||||
virtual pxInvokeMethodEvent *Clone() const { return new pxInvokeMethodEvent(*this); }
|
||||
|
||||
explicit pxInvokeMethodEvent( void (*method)()=NULL, SynchronousActionState* sema=NULL )
|
||||
: pxInvokeActionEvent( sema )
|
||||
{
|
||||
m_Method = method;
|
||||
}
|
||||
|
||||
explicit pxInvokeMethodEvent( void (*method)(), SynchronousActionState& sema )
|
||||
: pxInvokeActionEvent( sema )
|
||||
{
|
||||
m_Method = method;
|
||||
}
|
||||
|
||||
pxInvokeMethodEvent( const pxInvokeMethodEvent& src )
|
||||
: pxInvokeActionEvent( src )
|
||||
{
|
||||
m_Method = src.m_Method;
|
||||
}
|
||||
|
||||
void SetMethod( void (*method)() )
|
||||
{
|
||||
m_Method = method;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
if( m_Method ) m_Method();
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS( pxInvokeMethodEvent, pxInvokeActionEvent )
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxExceptionEvent implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
pxExceptionEvent::pxExceptionEvent( const BaseException& ex )
|
||||
{
|
||||
m_except = ex.Clone();
|
||||
}
|
||||
|
||||
void pxExceptionEvent::_DoInvoke()
|
||||
{
|
||||
ScopedPtr<BaseException> deleteMe( m_except );
|
||||
if( deleteMe ) deleteMe->Rethrow();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -64,81 +213,192 @@ pxPingEvent::pxPingEvent( const pxPingEvent& src )
|
|||
//
|
||||
IMPLEMENT_DYNAMIC_CLASS( wxAppWithHelpers, wxApp )
|
||||
|
||||
|
||||
// Posts a method to the main thread; non-blocking. Post occurs even when called from the
|
||||
// main thread.
|
||||
void wxAppWithHelpers::PostMethod( FnType_Void* method )
|
||||
{
|
||||
PostEvent( pxInvokeMethodEvent( method ) );
|
||||
}
|
||||
|
||||
// Posts a method to the main thread; non-blocking. Post occurs even when called from the
|
||||
// main thread.
|
||||
void wxAppWithHelpers::PostIdleMethod( FnType_Void* method )
|
||||
{
|
||||
pxInvokeMethodEvent evt( method );
|
||||
AddIdleEvent( evt );
|
||||
}
|
||||
|
||||
bool wxAppWithHelpers::PostMethodMyself( void (*method)() )
|
||||
{
|
||||
if( wxThread::IsMain() ) return false;
|
||||
PostEvent( pxInvokeMethodEvent( method ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::ProcessMethod( void (*method)() )
|
||||
{
|
||||
if( wxThread::IsMain() )
|
||||
{
|
||||
method();
|
||||
return;
|
||||
}
|
||||
|
||||
SynchronousActionState sync;
|
||||
PostEvent( pxInvokeMethodEvent( method, sync ) );
|
||||
sync.WaitForResult();
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::PostEvent( const wxEvent& evt )
|
||||
{
|
||||
// Const Cast is OK!
|
||||
// Truth is, AddPendingEvent should be a const-qualified parameter, as
|
||||
// it makes an immediate clone copy of the event -- but wxWidgets
|
||||
// fails again in structured C/C++ design design. So I'm forcing it as such
|
||||
// here. -- air
|
||||
|
||||
_parent::AddPendingEvent( const_cast<wxEvent&>(evt) );
|
||||
}
|
||||
|
||||
bool wxAppWithHelpers::ProcessEvent( wxEvent& evt )
|
||||
{
|
||||
// Note: We can't do an automatic blocking post of the message here, because wxWidgets
|
||||
// isn't really designed for it (some events return data to the caller via the event
|
||||
// struct, and posting the event would require a temporary clone, where changes would
|
||||
// be lost).
|
||||
|
||||
AffinityAssert_AllowFrom_MainUI();
|
||||
return _parent::ProcessEvent( evt );
|
||||
}
|
||||
|
||||
bool wxAppWithHelpers::ProcessEvent( wxEvent* evt )
|
||||
{
|
||||
AffinityAssert_AllowFrom_MainUI();
|
||||
ScopedPtr<wxEvent> deleteMe( evt );
|
||||
return _parent::ProcessEvent( *deleteMe );
|
||||
}
|
||||
|
||||
bool wxAppWithHelpers::ProcessEvent( pxInvokeActionEvent& evt )
|
||||
{
|
||||
if( wxThread::IsMain() )
|
||||
return _parent::ProcessEvent( evt );
|
||||
else
|
||||
{
|
||||
SynchronousActionState sync;
|
||||
evt.SetSyncState( sync );
|
||||
AddPendingEvent( evt );
|
||||
sync.WaitForResult();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool wxAppWithHelpers::ProcessEvent( pxInvokeActionEvent* evt )
|
||||
{
|
||||
if( wxThread::IsMain() )
|
||||
{
|
||||
ScopedPtr<wxEvent> deleteMe( evt );
|
||||
return _parent::ProcessEvent( *deleteMe );
|
||||
}
|
||||
else
|
||||
{
|
||||
SynchronousActionState sync;
|
||||
evt->SetSyncState( sync );
|
||||
AddPendingEvent( *evt );
|
||||
sync.WaitForResult();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void wxAppWithHelpers::CleanUp()
|
||||
{
|
||||
DeletionDispatcher();
|
||||
// I'm pretty sure the message pump is dead by now, which means we need to run through
|
||||
// idle event list by hand and process the pending Deletion messages (all others can be
|
||||
// ignored -- it's only deletions we want handled, and really we *could* ignore those too
|
||||
// but I like to be tidy. -- air
|
||||
|
||||
//IdleEventDispatcher( "CleanUp" );
|
||||
//DeletionDispatcher();
|
||||
_parent::CleanUp();
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::OnPingEvent( pxPingEvent& evt )
|
||||
void pxInvokeActionEvent::InvokeAction()
|
||||
{
|
||||
// Ping events are dispatched during the idle event handler, which ensures
|
||||
// the ping is posted only after all other pending messages behind the ping
|
||||
// are also processed.
|
||||
AffinityAssert_AllowFrom_MainUI();
|
||||
|
||||
if( m_PingWhenIdle.size() == 0 ) m_PingTimer.Start( 200, true );
|
||||
m_PingWhenIdle.push_back( evt.GetSemaphore() );
|
||||
try {
|
||||
_DoInvoke();
|
||||
}
|
||||
catch( BaseException& ex )
|
||||
{
|
||||
SetException( ex );
|
||||
}
|
||||
catch( std::runtime_error& ex )
|
||||
{
|
||||
SetException( new Exception::RuntimeError( ex ) );
|
||||
}
|
||||
|
||||
if( m_state ) m_state->PostResult();
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::OnAddEventToIdleQueue( wxEvent& evt )
|
||||
void wxAppWithHelpers::OnSynchronousCommand( pxSynchronousCommandEvent& evt )
|
||||
{
|
||||
if( m_IdleEventQueue.size() == 0 ) m_IdleEventTimer.Start( 100, true );
|
||||
AffinityAssert_AllowFrom_MainUI();
|
||||
|
||||
evt.SetEventType( evt.GetRealEventType() );
|
||||
|
||||
try {
|
||||
ProcessEvent( evt );
|
||||
}
|
||||
catch( BaseException& ex )
|
||||
{
|
||||
evt.SetException( ex );
|
||||
}
|
||||
catch( std::runtime_error& ex )
|
||||
{
|
||||
evt.SetException( new Exception::RuntimeError( ex, evt.GetClassInfo()->GetClassName() ) );
|
||||
}
|
||||
|
||||
if( Semaphore* sema = evt.GetSemaphore() ) sema->Post();
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::AddIdleEvent( const wxEvent& evt )
|
||||
{
|
||||
ScopedLock lock( m_IdleEventMutex );
|
||||
if( m_IdleEventQueue.size() == 0 )
|
||||
PostEvent( pxSimpleEvent( pxEvt_StartIdleEventTimer ) );
|
||||
|
||||
m_IdleEventQueue.push_back( evt.Clone() );
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::OnStartIdleEventTimer( wxEvent& evt )
|
||||
{
|
||||
ScopedLock lock( m_IdleEventMutex );
|
||||
if( m_IdleEventQueue.size() != 0 )
|
||||
m_IdleEventTimer.Start( 100, true );
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::IdleEventDispatcher( const char* action )
|
||||
{
|
||||
ScopedLock lock( m_IdleEventMutex );
|
||||
|
||||
size_t size = m_IdleEventQueue.size();
|
||||
if( size == 0 ) return;
|
||||
|
||||
DbgCon.WriteLn( Color_Gray, "App IdleQueue (%s) -> %u events.", action, size );
|
||||
|
||||
for( size_t i=0; i<size; ++i )
|
||||
{
|
||||
ProcessEvent( *m_IdleEventQueue[i] );
|
||||
}
|
||||
|
||||
m_IdleEventQueue.clear();
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::PingDispatcher( const char* action )
|
||||
{
|
||||
size_t size = m_PingWhenIdle.size();
|
||||
if( size == 0 ) return;
|
||||
|
||||
DbgCon.WriteLn( Color_Gray, "App Event Ping (%s) -> %u listeners.", action, size );
|
||||
|
||||
for( size_t i=0; i<size; ++i )
|
||||
{
|
||||
if( Semaphore* sema = m_PingWhenIdle[i] ) sema->Post();
|
||||
}
|
||||
|
||||
m_PingWhenIdle.clear();
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::DeletionDispatcher()
|
||||
{
|
||||
ScopedLock lock( m_DeleteIdleLock );
|
||||
|
||||
size_t size = m_DeleteWhenIdle.size();
|
||||
if( size == 0 ) return;
|
||||
|
||||
DbgCon.WriteLn( Color_Gray, "App Idle Delete -> %u objects.", size );
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::OnIdleEvent( wxIdleEvent& evt )
|
||||
{
|
||||
m_PingTimer.Stop();
|
||||
m_IdleEventTimer.Stop();
|
||||
PingDispatcher( "Idle" );
|
||||
IdleEventDispatcher( "Idle" );
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::OnPingTimeout( wxTimerEvent& evt )
|
||||
{
|
||||
PingDispatcher( "Timeout" );
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::OnIdleEventTimeout( wxTimerEvent& evt )
|
||||
{
|
||||
IdleEventDispatcher( "Timeout" );
|
||||
|
@ -148,10 +408,10 @@ void wxAppWithHelpers::Ping()
|
|||
{
|
||||
DbgCon.WriteLn( Color_Gray, L"App Event Ping Requested from %s thread.", pxGetCurrentThreadName().c_str() );
|
||||
|
||||
Semaphore sema;
|
||||
pxPingEvent evt( &sema );
|
||||
AddPendingEvent( evt );
|
||||
sema.WaitNoCancel();
|
||||
SynchronousActionState sync;
|
||||
pxInvokeActionEvent evt( sync );
|
||||
AddIdleEvent( evt );
|
||||
sync.WaitForResult();
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::PostCommand( void* clientData, int evtType, int intParam, long longParam, const wxString& stringParam )
|
||||
|
@ -169,52 +429,106 @@ void wxAppWithHelpers::PostCommand( int evtType, int intParam, long longParam, c
|
|||
PostCommand( NULL, evtType, intParam, longParam, stringParam );
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::DeleteObject( IDeletableObject& obj )
|
||||
sptr wxAppWithHelpers::ProcessCommand( void* clientData, int evtType, int intParam, long longParam, const wxString& stringParam )
|
||||
{
|
||||
pxAssume( obj.IsBeingDeleted() );
|
||||
ScopedLock lock( m_DeleteIdleLock );
|
||||
m_DeleteWhenIdle.push_back( &obj );
|
||||
SynchronousActionState sync;
|
||||
pxSynchronousCommandEvent evt( sync, evtType );
|
||||
|
||||
evt.SetClientData( clientData );
|
||||
evt.SetInt( intParam );
|
||||
evt.SetExtraLong( longParam );
|
||||
evt.SetString( stringParam );
|
||||
AddPendingEvent( evt );
|
||||
sync.WaitForResult();
|
||||
|
||||
return sync.return_value;
|
||||
}
|
||||
|
||||
typedef void (wxEvtHandler::*BaseMessageBoxEventFunction)(BaseMessageBoxEvent&);
|
||||
typedef void (wxEvtHandler::*pxPingEventFunction)(pxPingEvent&);
|
||||
sptr wxAppWithHelpers::ProcessCommand( int evtType, int intParam, long longParam, const wxString& stringParam )
|
||||
{
|
||||
return ProcessCommand( NULL, evtType, intParam, longParam, stringParam );
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::PostAction( const pxInvokeActionEvent& evt )
|
||||
{
|
||||
PostEvent( evt );
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::ProcessAction( pxInvokeActionEvent& evt )
|
||||
{
|
||||
if( !wxThread::IsMain() )
|
||||
{
|
||||
SynchronousActionState sync;
|
||||
evt.SetSyncState( sync );
|
||||
AddPendingEvent( evt );
|
||||
sync.WaitForResult();
|
||||
}
|
||||
else
|
||||
evt.InvokeAction();
|
||||
}
|
||||
|
||||
|
||||
void wxAppWithHelpers::DeleteObject( BaseDeletableObject& obj )
|
||||
{
|
||||
pxAssume( !obj.IsBeingDeleted() );
|
||||
wxCommandEvent evt( pxEvt_DeleteObject );
|
||||
evt.SetClientData( (void*)&obj );
|
||||
AddIdleEvent( evt );
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::DeleteThread( PersistentThread& obj )
|
||||
{
|
||||
//pxAssume( obj.IsBeingDeleted() );
|
||||
wxCommandEvent evt( pxEvt_DeleteThread );
|
||||
evt.SetClientData( (void*)&obj );
|
||||
AddIdleEvent( evt );
|
||||
}
|
||||
|
||||
typedef void (wxEvtHandler::*pxInvokeActionEventFunction)(pxInvokeActionEvent&);
|
||||
|
||||
bool wxAppWithHelpers::OnInit()
|
||||
{
|
||||
#define pxMessageBoxEventThing(func) \
|
||||
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(BaseMessageBoxEventFunction, &func )
|
||||
#define pxActionEventHandler(func) \
|
||||
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxInvokeActionEventFunction, &func )
|
||||
|
||||
#define pxPingEventHandler(func) \
|
||||
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxPingEventFunction, &func )
|
||||
Connect( pxEvt_SynchronousCommand, pxSynchronousEventHandler (wxAppWithHelpers::OnSynchronousCommand) );
|
||||
Connect( pxEvt_InvokeAction, pxActionEventHandler (wxAppWithHelpers::OnInvokeAction) );
|
||||
|
||||
Connect( pxEvt_StartIdleEventTimer, wxEventHandler (wxAppWithHelpers::OnStartIdleEventTimer) );
|
||||
|
||||
Connect( pxEvt_MessageBox, pxMessageBoxEventThing (wxAppWithHelpers::OnMessageBox) );
|
||||
//Connect( pxEvt_Assertion, pxMessageBoxEventThing (wxAppWithHelpers::OnMessageBox) );
|
||||
Connect( pxEvt_Ping, pxPingEventHandler (wxAppWithHelpers::OnPingEvent) );
|
||||
Connect( pxEvt_IdleEventQueue, wxEventHandler (wxAppWithHelpers::OnAddEventToIdleQueue) );
|
||||
Connect( pxEvt_DeleteObject, wxCommandEventHandler (wxAppWithHelpers::OnDeleteObject) );
|
||||
Connect( wxEVT_IDLE, wxIdleEventHandler (wxAppWithHelpers::OnIdleEvent) );
|
||||
Connect( pxEvt_DeleteObject, wxCommandEventHandler (wxAppWithHelpers::OnDeleteObject) );
|
||||
Connect( pxEvt_DeleteThread, wxCommandEventHandler (wxAppWithHelpers::OnDeleteThread) );
|
||||
|
||||
Connect( wxEVT_IDLE, wxIdleEventHandler (wxAppWithHelpers::OnIdleEvent) );
|
||||
|
||||
Connect( m_PingTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler(wxAppWithHelpers::OnPingTimeout) );
|
||||
Connect( m_IdleEventTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler(wxAppWithHelpers::OnIdleEventTimeout) );
|
||||
|
||||
return _parent::OnInit();
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::OnMessageBox( BaseMessageBoxEvent& evt )
|
||||
void wxAppWithHelpers::OnInvokeAction( pxInvokeActionEvent& evt )
|
||||
{
|
||||
evt.IssueDialog();
|
||||
evt.InvokeAction(); // wow this is easy!
|
||||
}
|
||||
|
||||
void wxAppWithHelpers::OnDeleteObject( wxCommandEvent& evt )
|
||||
{
|
||||
if( evt.GetClientData() == NULL ) return;
|
||||
delete (IDeletableObject*)evt.GetClientData();
|
||||
delete (BaseDeletableObject*)evt.GetClientData();
|
||||
}
|
||||
|
||||
// Threads have their own deletion handler that propagates exceptions thrown by the thread to the UI.
|
||||
// (thus we have a fairly automatic threaded exception system!)
|
||||
void wxAppWithHelpers::OnDeleteThread( wxCommandEvent& evt )
|
||||
{
|
||||
ScopedPtr<PersistentThread> thr( (PersistentThread*)evt.GetClientData() );
|
||||
if( !thr ) return;
|
||||
|
||||
thr->RethrowException();
|
||||
}
|
||||
|
||||
wxAppWithHelpers::wxAppWithHelpers()
|
||||
: m_PingTimer( this )
|
||||
, m_IdleEventTimer( this )
|
||||
: m_IdleEventTimer( this )
|
||||
{
|
||||
#ifdef __WXMSW__
|
||||
// This variable assignment ensures that MSVC links in the TLS setup stubs even in
|
||||
|
|
|
@ -25,37 +25,39 @@
|
|||
|
||||
using namespace pxSizerFlags;
|
||||
|
||||
DEFINE_EVENT_TYPE( pxEvt_OnThreadCleanup );
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// IDeletableObject Implementation
|
||||
// BaseDeletableObject Implementation
|
||||
// --------------------------------------------------------------------------------------
|
||||
// This code probably deserves a better home. It's general purpose non-GUI code (the single
|
||||
// wxApp/Gui dependency is in wxGuiTools.cpp for now).
|
||||
//
|
||||
bool IDeletableObject::MarkForDeletion()
|
||||
bool BaseDeletableObject::MarkForDeletion()
|
||||
{
|
||||
return !_InterlockedExchange( &m_IsBeingDeleted, true );
|
||||
}
|
||||
|
||||
void IDeletableObject::DeleteSelf()
|
||||
void BaseDeletableObject::DeleteSelf()
|
||||
{
|
||||
if( MarkForDeletion() )
|
||||
DoDeletion();
|
||||
}
|
||||
|
||||
IDeletableObject::IDeletableObject()
|
||||
BaseDeletableObject::BaseDeletableObject()
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
// Bleh, this fails because _CrtIsValidHeapPointer calls HeapValidate on the
|
||||
// pointer, but the pointer is a virtual base class, so it's not a valid block. >_<
|
||||
//pxAssertDev( _CrtIsValidHeapPointer( this ), "IDeletableObject types cannot be created on the stack or as temporaries!" );
|
||||
//pxAssertDev( _CrtIsValidHeapPointer( this ), "BaseDeletableObject types cannot be created on the stack or as temporaries!" );
|
||||
#endif
|
||||
|
||||
m_IsBeingDeleted = false;
|
||||
}
|
||||
|
||||
IDeletableObject::~IDeletableObject() throw()
|
||||
BaseDeletableObject::~BaseDeletableObject() throw()
|
||||
{
|
||||
AffinityAssert_AllowFromMain();
|
||||
AffinityAssert_AllowFrom_MainUI();
|
||||
}
|
||||
|
||||
|
||||
|
@ -334,7 +336,6 @@ pxStaticHeading* wxPanelWithHelpers::Heading( const wxString& label )
|
|||
return new pxStaticHeading( this, label );
|
||||
}
|
||||
|
||||
|
||||
wxPanelWithHelpers::wxPanelWithHelpers( wxWindow* parent, wxOrientation orient, const wxString& staticBoxLabel )
|
||||
: wxPanel( parent )
|
||||
{
|
||||
|
|
|
@ -12,6 +12,6 @@
|
|||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="w32pthreads.v3.lib w32pthreads_lib.lib"
|
||||
AdditionalDependencies="w32pthreads_lib.lib"
|
||||
/>
|
||||
</VisualStudioPropertySheet>
|
||||
|
|
|
@ -93,7 +93,7 @@ FILE *_cdvdOpenMechaVer()
|
|||
if (fd == NULL)
|
||||
{
|
||||
Console.Error( "MEC File Creation failed!" );
|
||||
throw Exception::CreateStream( file );
|
||||
throw Exception::CannotCreateStream( file );
|
||||
//Msgbox::Alert( "_cdvdOpenMechaVer: Error creating %s", file);
|
||||
//exit(1);
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ FILE *_cdvdOpenNVM()
|
|||
if (fd == NULL)
|
||||
{
|
||||
Console.Error( "NVM File Creation failed!" );
|
||||
throw Exception::CreateStream( file );
|
||||
throw Exception::CannotCreateStream( file );
|
||||
}
|
||||
for (int i=0; i<1024; i++) fputc(0, fd);
|
||||
}
|
||||
|
@ -305,7 +305,7 @@ s32 cdvdWriteConfig(const u8* config)
|
|||
}
|
||||
}
|
||||
|
||||
static MutexLockRecursive Mutex_NewDiskCB;
|
||||
static MutexRecursive Mutex_NewDiskCB;
|
||||
|
||||
// Sets ElfCRC to the CRC of the game bound to the CDVD plugin.
|
||||
static __forceinline ElfObject *loadElf( const wxString filename )
|
||||
|
@ -566,11 +566,11 @@ static void cdvdDetectDisk()
|
|||
|
||||
void cdvdNewDiskCB()
|
||||
{
|
||||
if( !Mutex_NewDiskCB.TryAcquire() ) return;
|
||||
ScopedTryLock lock( Mutex_NewDiskCB );
|
||||
if( lock.Failed() ) return;
|
||||
|
||||
DoCDVDresetDiskTypeCache();
|
||||
|
||||
try { cdvdDetectDisk(); }
|
||||
catch(...) { Mutex_NewDiskCB.Release(); } // ensure mutex gets unlocked.
|
||||
cdvdDetectDisk();
|
||||
}
|
||||
|
||||
static void mechaDecryptBytes( u32 madr, int size )
|
||||
|
|
|
@ -288,7 +288,7 @@ CDVD_SourceType CDVDsys_GetSourceType()
|
|||
|
||||
void CDVDsys_ChangeSource( CDVD_SourceType type )
|
||||
{
|
||||
GetPluginManager().Close( PluginId_CDVD );
|
||||
GetCorePlugins().Close( PluginId_CDVD );
|
||||
|
||||
switch( m_CurrentSourceType = type )
|
||||
{
|
||||
|
|
|
@ -94,7 +94,7 @@ IsoDirectory::IsoDirectory(SectorSource& r)
|
|||
}
|
||||
|
||||
if( !isValid )
|
||||
throw Exception::BadStream( "IsoFS", "Root directory not found on ISO image." );
|
||||
throw Exception::FileNotFound( "IsoFS", "Root directory not found on ISO image." );
|
||||
|
||||
DevCon.WriteLn( L"(IsoFS) Filesystem is " + FStype_ToString() );
|
||||
Init( rootDirEntry );
|
||||
|
|
|
@ -484,8 +484,8 @@ struct Pcsx2Config
|
|||
CdvdDumpBlocks :1, // enables cdvd block dumping
|
||||
EnablePatches :1, // enables patch detection and application
|
||||
|
||||
// when enabled performs bios stub execution, skipping full sony bios + splash screens
|
||||
SkipBiosSplash :1,
|
||||
// when enabled uses BOOT2 injection, skipping sony bios splashes
|
||||
UseBOOT2Injection :1,
|
||||
|
||||
// enables simulated ejection of memory cards when loading savestates
|
||||
McdEnableEjection :1,
|
||||
|
|
|
@ -222,8 +222,7 @@ static void vSyncInfoCalc( vSyncTimingInfo* info, Fixed100 framesPerSecond, u32
|
|||
|
||||
u32 UpdateVSyncRate()
|
||||
{
|
||||
XMMRegisters::Freeze();
|
||||
MMXRegisters::Freeze();
|
||||
Registers::Freeze();
|
||||
|
||||
// Notice: (and I probably repeat this elsewhere, but it's worth repeating)
|
||||
// The PS2's vsync timer is an *independent* crystal that is fixed to either 59.94 (NTSC)
|
||||
|
@ -234,8 +233,8 @@ u32 UpdateVSyncRate()
|
|||
// 1/5 and 4/5 ratios).
|
||||
|
||||
Fixed100 framerate;
|
||||
u32 scanlines;
|
||||
bool isCustom;
|
||||
u32 scanlines;
|
||||
bool isCustom;
|
||||
|
||||
if( gsRegionMode == Region_PAL )
|
||||
{
|
||||
|
@ -278,8 +277,7 @@ u32 UpdateVSyncRate()
|
|||
|
||||
m_iStart = GetCPUTicks();
|
||||
|
||||
XMMRegisters::Thaw();
|
||||
MMXRegisters::Thaw();
|
||||
Registers::Thaw();
|
||||
|
||||
return (u32)m_iTicks;
|
||||
}
|
||||
|
|
|
@ -118,8 +118,8 @@ void LoadGSState(const wxString& file)
|
|||
|
||||
RunGSState( f );
|
||||
|
||||
g_plugins->Close( PluginId_GS );
|
||||
g_plugins->Close( PluginId_PAD );
|
||||
GetCorePlugins().Close( PluginId_GS );
|
||||
GetCorePlugins().Close( PluginId_PAD );
|
||||
}
|
||||
|
||||
struct GSStatePacket
|
||||
|
|
|
@ -287,7 +287,6 @@
|
|||
<Unit filename="../R5900OpcodeImpl.cpp" />
|
||||
<Unit filename="../R5900OpcodeTables.cpp" />
|
||||
<Unit filename="../R5900OpcodeTables.h" />
|
||||
<Unit filename="../RecoverySystem.cpp" />
|
||||
<Unit filename="../SPR.cpp" />
|
||||
<Unit filename="../SPR.h" />
|
||||
<Unit filename="../SamplProf.h" />
|
||||
|
@ -338,6 +337,9 @@
|
|||
<Unit filename="../Vif_Unpack.cpp" />
|
||||
<Unit filename="../Vif_Unpack.h" />
|
||||
<Unit filename="../Vif_Unpack.inl" />
|
||||
<Unit filename="../ZipTools/ThreadedZipTools.h" />
|
||||
<Unit filename="../ZipTools/thread_gzip.cpp" />
|
||||
<Unit filename="../ZipTools/thread_lzma.cpp" />
|
||||
<Unit filename="../gui/AdvancedDialog.cpp" />
|
||||
<Unit filename="../gui/AdvancedDialog.h" />
|
||||
<Unit filename="../gui/App.h" />
|
||||
|
@ -345,6 +347,8 @@
|
|||
<Unit filename="../gui/AppConfig.cpp" />
|
||||
<Unit filename="../gui/AppConfig.h" />
|
||||
<Unit filename="../gui/AppCoreThread.cpp" />
|
||||
<Unit filename="../gui/AppCorePlugins.cpp" />
|
||||
<Unit filename="../gui/AppCoreThread.h" />
|
||||
<Unit filename="../gui/AppEventListeners.h" />
|
||||
<Unit filename="../gui/AppEventSources.cpp" />
|
||||
<Unit filename="../gui/AppForwardDefs.h" />
|
||||
|
@ -376,6 +380,7 @@
|
|||
<Unit filename="../gui/Dialogs/PickUserModeDialog.cpp" />
|
||||
<Unit filename="../gui/Dialogs/StuckThreadDialog.cpp" />
|
||||
<Unit filename="../gui/Dialogs/SysConfigDialog.cpp" />
|
||||
<Unit filename="../gui/ExecutorThread.h" />
|
||||
<Unit filename="../gui/FrameForGS.cpp" />
|
||||
<Unit filename="../gui/GlobalCommands.cpp" />
|
||||
<Unit filename="../gui/IniInterface.cpp" />
|
||||
|
@ -408,7 +413,6 @@
|
|||
<Unit filename="../gui/Panels/PluginSelectorPanel.cpp" />
|
||||
<Unit filename="../gui/Panels/SpeedhacksPanel.cpp" />
|
||||
<Unit filename="../gui/Panels/VideoPanel.cpp" />
|
||||
<Unit filename="../gui/Plugins.cpp" />
|
||||
<Unit filename="../gui/RecentIsoList.cpp" />
|
||||
<Unit filename="../gui/RecentIsoList.h" />
|
||||
<Unit filename="../gui/Resources/AppIcon16.h" />
|
||||
|
@ -491,9 +495,13 @@
|
|||
</Unit>
|
||||
<Unit filename="../gui/Resources/EmbeddedImage.h" />
|
||||
<Unit filename="../gui/Saveslots.cpp" />
|
||||
<Unit filename="../gui/SysState.cpp" />
|
||||
<Unit filename="../gui/UpdateUI.cpp" />
|
||||
<Unit filename="../gui/i18n.cpp" />
|
||||
<Unit filename="../gui/i18n.h" />
|
||||
<Unit filename="../gui/gsFrame.h" />
|
||||
<Unit filename="../gui/pxLogTextCtrl.cpp" />
|
||||
<Unit filename="../gui/pxEvtThread.h" />
|
||||
<Unit filename="../pcsx2hostfs.cpp" />
|
||||
<Unit filename="../ps2/BiosTools.cpp" />
|
||||
<Unit filename="../ps2/BiosTools.h" />
|
||||
|
|
|
@ -399,9 +399,9 @@ void SysMtgsThread::ExecuteTaskInThread()
|
|||
{
|
||||
MTGS_FreezeData* data = (MTGS_FreezeData*)(*(uptr*)&tag.data[1]);
|
||||
int mode = tag.data[0];
|
||||
data->retval = GetPluginManager().DoFreeze( PluginId_GS, mode, data->fdata );
|
||||
break;
|
||||
data->retval = GetCorePlugins().DoFreeze( PluginId_GS, mode, data->fdata );
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_RINGTYPE_RECORD:
|
||||
{
|
||||
|
@ -488,8 +488,7 @@ void SysMtgsThread::ClosePlugin()
|
|||
{
|
||||
if( !m_PluginOpened ) return;
|
||||
m_PluginOpened = false;
|
||||
if( g_plugins != NULL )
|
||||
g_plugins->m_info[PluginId_GS].CommonBindings.Close();
|
||||
GetCorePlugins().Close( PluginId_GS );
|
||||
}
|
||||
|
||||
void SysMtgsThread::OnSuspendInThread()
|
||||
|
@ -998,7 +997,7 @@ void SysMtgsThread::WaitForOpen()
|
|||
|
||||
void SysMtgsThread::Freeze( int mode, MTGS_FreezeData& data )
|
||||
{
|
||||
GetPluginManager().Open( PluginId_GS );
|
||||
GetCorePlugins().Open( PluginId_GS );
|
||||
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
|
||||
Resume();
|
||||
WaitGS();
|
||||
|
|
|
@ -42,7 +42,6 @@ namespace PathDefs
|
|||
// complete pathnames are returned by these functions
|
||||
// For 99% of all code, you should use these.
|
||||
|
||||
extern wxDirName GetDocuments();
|
||||
extern wxDirName GetSnapshots();
|
||||
extern wxDirName GetBios();
|
||||
extern wxDirName GetThemes();
|
||||
|
@ -51,7 +50,6 @@ namespace PathDefs
|
|||
extern wxDirName GetMemoryCards();
|
||||
extern wxDirName GetSettings();
|
||||
extern wxDirName GetLogs();
|
||||
extern wxDirName GetThemes();
|
||||
|
||||
extern wxDirName Get( FoldersEnum_t folderidx );
|
||||
|
||||
|
|
|
@ -275,7 +275,7 @@ void Pcsx2Config::LoadSave( IniInterface& ini )
|
|||
IniBitBool( EnablePatches );
|
||||
IniBitBool( ConsoleToStdio );
|
||||
|
||||
IniBitBool( SkipBiosSplash );
|
||||
IniBitBool( UseBOOT2Injection );
|
||||
|
||||
IniBitBool( McdEnableEjection );
|
||||
IniBitBool( MultitapPort0_Enabled );
|
||||
|
|
|
@ -708,88 +708,193 @@ static void PS2E_CALLBACK pcsx2_OSD_WriteLn( int icon, const char* msg )
|
|||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// Plugin Manager Implementation
|
||||
// PluginStatus_t Implementations
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] )
|
||||
PluginManager::PluginStatus_t::PluginStatus_t( PluginsEnum_t _pid, const wxString& srcfile )
|
||||
: Filename( srcfile )
|
||||
{
|
||||
pid = _pid;
|
||||
|
||||
IsInitialized = false;
|
||||
IsOpened = false;
|
||||
|
||||
if( Filename.IsEmpty() )
|
||||
throw Exception::PluginInitError( pid, "Empty plugin filename." );
|
||||
|
||||
if( !wxFile::Exists( Filename ) )
|
||||
throw Exception::PluginLoadError( pid, srcfile,
|
||||
wxLt("The configured %s plugin file was not found")
|
||||
);
|
||||
|
||||
if( !Lib.Load( Filename ) )
|
||||
throw Exception::PluginLoadError( pid, Filename,
|
||||
wxLt("The configured %s plugin file is not a valid dynamic library")
|
||||
);
|
||||
|
||||
|
||||
// Try to enumerate the new v2.0 plugin interface first.
|
||||
// If that fails, fall back on the old style interface.
|
||||
|
||||
//m_libs[i].GetSymbol( L"PS2E_InitAPI" ); // on the TODO list!
|
||||
|
||||
|
||||
// 2.0 API Failed; Enumerate the Old Stuff! -->
|
||||
|
||||
_PS2EgetLibName GetLibName = (_PS2EgetLibName) Lib.GetSymbol( L"PS2EgetLibName" );
|
||||
_PS2EgetLibVersion2 GetLibVersion2 = (_PS2EgetLibVersion2) Lib.GetSymbol( L"PS2EgetLibVersion2" );
|
||||
_PS2EsetEmuVersion SetEmuVersion = (_PS2EsetEmuVersion) Lib.GetSymbol( L"PS2EsetEmuVersion" );
|
||||
|
||||
if( GetLibName == NULL || GetLibVersion2 == NULL )
|
||||
throw Exception::PluginLoadError( pid, Filename,
|
||||
L"\nMethod binding failure on GetLibName or GetLibVersion2.\n",
|
||||
_( "Configured plugin is not a PCSX2 plugin, or is for an older unsupported version of PCSX2." )
|
||||
);
|
||||
|
||||
if( SetEmuVersion != NULL )
|
||||
SetEmuVersion( "PCSX2", (0ul << 24) | (9ul<<16) | (7ul<<8) | 0 );
|
||||
|
||||
Name = fromUTF8( GetLibName() );
|
||||
int version = GetLibVersion2( tbl_PluginInfo[pid].typemask );
|
||||
Version.Printf( L"%d.%d.%d", (version>>8)&0xff, version&0xff, (version>>24)&0xff );
|
||||
|
||||
|
||||
// Bind Required Functions
|
||||
// (generate critical error if binding fails)
|
||||
|
||||
BindCommon( pid );
|
||||
BindRequired( pid );
|
||||
BindOptional( pid );
|
||||
|
||||
// Run Plugin's Functionality Test.
|
||||
// A lot of plugins don't bother to implement this function and return 0 (success)
|
||||
// regardless, but some do so let's go ahead and check it. I mean, we're supposed to. :)
|
||||
|
||||
int testres = CommonBindings.Test();
|
||||
if( testres != 0 )
|
||||
throw Exception::PluginLoadError( pid, Filename,
|
||||
wxsFormat( L"Plugin Test failure, return code: %d", testres ),
|
||||
_( "The plugin reports that your hardware or software/drivers are not supported." )
|
||||
);
|
||||
}
|
||||
|
||||
void PluginManager::PluginStatus_t::BindCommon( PluginsEnum_t pid )
|
||||
{
|
||||
const LegacyApi_CommonMethod* current = s_MethMessCommon;
|
||||
VoidMethod** target = (VoidMethod**)&CommonBindings;
|
||||
|
||||
wxDoNotLogInThisScope please;
|
||||
|
||||
Console.WriteLn( Color_StrongBlue, "Loading plugins..." );
|
||||
|
||||
const PluginInfo* pi = tbl_PluginInfo; do
|
||||
while( current->MethodName != NULL )
|
||||
{
|
||||
ConsoleIndentScope indent;
|
||||
const PluginsEnum_t pid = pi->id;
|
||||
*target = (VoidMethod*)Lib.GetSymbol( current->GetMethodName( pid ) );
|
||||
|
||||
Console.WriteLn( L"Binding %s\t: %s ", tbl_PluginInfo[pid].GetShortname().c_str(), folders[pid].c_str() );
|
||||
if( *target == NULL )
|
||||
*target = current->Fallback;
|
||||
|
||||
if( folders[pid].IsEmpty() )
|
||||
throw Exception::PluginInitError( pi->id, "Empty plugin filename." );
|
||||
|
||||
m_info[pid].Filename = folders[pid];
|
||||
|
||||
if( !wxFile::Exists( folders[pid] ) )
|
||||
throw Exception::PluginLoadError( pid, folders[pid],
|
||||
wxLt("The configured %s plugin file was not found")
|
||||
);
|
||||
|
||||
if( !m_info[pid].Lib.Load( folders[pid] ) )
|
||||
throw Exception::PluginLoadError( pid, folders[pid],
|
||||
wxLt("The configured %s plugin file is not a valid dynamic library")
|
||||
);
|
||||
|
||||
// Try to enumerate the new v2.0 plugin interface first.
|
||||
// If that fails, fall back on the old style interface.
|
||||
|
||||
//m_libs[i].GetSymbol( L"PS2E_InitAPI" );
|
||||
|
||||
// Fetch plugin name and version information
|
||||
|
||||
_PS2EgetLibName GetLibName = (_PS2EgetLibName) m_info[pid].Lib.GetSymbol( L"PS2EgetLibName" );
|
||||
_PS2EgetLibVersion2 GetLibVersion2 = (_PS2EgetLibVersion2) m_info[pid].Lib.GetSymbol( L"PS2EgetLibVersion2" );
|
||||
_PS2EsetEmuVersion SetEmuVersion = (_PS2EsetEmuVersion) m_info[pid].Lib.GetSymbol( L"PS2EsetEmuVersion" );
|
||||
|
||||
if( GetLibName == NULL || GetLibVersion2 == NULL )
|
||||
throw Exception::PluginLoadError( pid, m_info[pid].Filename,
|
||||
L"\nMethod binding failure on GetLibName or GetLibVersion2.\n",
|
||||
if( *target == NULL )
|
||||
{
|
||||
throw Exception::PluginLoadError( pid, Filename,
|
||||
wxsFormat( L"\nMethod binding failure on: %s\n", current->GetMethodName( pid ).c_str() ),
|
||||
_( "Configured plugin is not a PCSX2 plugin, or is for an older unsupported version of PCSX2." )
|
||||
);
|
||||
}
|
||||
|
||||
if( SetEmuVersion != NULL )
|
||||
SetEmuVersion( "PCSX2", (0ul << 24) | (9ul<<16) | (7ul<<8) | 0 );
|
||||
target++;
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
m_info[pid].Name = fromUTF8( GetLibName() );
|
||||
int version = GetLibVersion2( tbl_PluginInfo[pid].typemask );
|
||||
m_info[pid].Version.Printf( L"%d.%d.%d", (version>>8)&0xff, version&0xff, (version>>24)&0xff );
|
||||
void PluginManager::PluginStatus_t::BindRequired( PluginsEnum_t pid )
|
||||
{
|
||||
const LegacyApi_ReqMethod* current = s_MethMessReq[pid];
|
||||
const wxDynamicLibrary& lib = Lib;
|
||||
|
||||
// Bind Required Functions
|
||||
// (generate critical error if binding fails)
|
||||
wxDoNotLogInThisScope please;
|
||||
|
||||
BindCommon( pid );
|
||||
BindRequired( pid );
|
||||
BindOptional( pid );
|
||||
while( current->MethodName != NULL )
|
||||
{
|
||||
*(current->Dest) = (VoidMethod*)lib.GetSymbol( current->GetMethodName() );
|
||||
|
||||
// Run Plugin's Functionality Test.
|
||||
// A lot of plugins don't bother to implement this function and return 0 (success)
|
||||
// regardless, but some do so let's go ahead and check it. I mean, we're supposed to. :)
|
||||
if( *(current->Dest) == NULL )
|
||||
*(current->Dest) = current->Fallback;
|
||||
|
||||
int testres = m_info[pi->id].CommonBindings.Test();
|
||||
if( testres != 0 )
|
||||
throw Exception::PluginLoadError( pid, m_info[pid].Filename,
|
||||
wxsFormat( L"Plugin Test failure, return code: %d", testres ),
|
||||
_( "The plugin reports that your hardware or software/drivers are not supported." )
|
||||
if( *(current->Dest) == NULL )
|
||||
{
|
||||
throw Exception::PluginLoadError( pid, Filename,
|
||||
wxsFormat( L"\nMethod binding failure on: %s\n", current->GetMethodName().c_str() ),
|
||||
_( "Configured plugin is not a valid PCSX2 plugin, or is for an older unsupported version of PCSX2." )
|
||||
);
|
||||
}
|
||||
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::PluginStatus_t::BindOptional( PluginsEnum_t pid )
|
||||
{
|
||||
const LegacyApi_OptMethod* current = s_MethMessOpt[pid];
|
||||
const wxDynamicLibrary& lib = Lib;
|
||||
|
||||
wxDoNotLogInThisScope please;
|
||||
|
||||
while( current->MethodName != NULL )
|
||||
{
|
||||
*(current->Dest) = (VoidMethod*)lib.GetSymbol( current->GetMethodName() );
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================================
|
||||
// PluginManager Implementations
|
||||
// =====================================================================================
|
||||
|
||||
PluginManager::PluginManager()
|
||||
{
|
||||
}
|
||||
|
||||
PluginManager::~PluginManager() throw()
|
||||
{
|
||||
try
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
DESTRUCTOR_CATCHALL
|
||||
|
||||
// All library unloading done automatically by wx.
|
||||
}
|
||||
|
||||
void PluginManager::Load( PluginsEnum_t pid, const wxString& srcfile )
|
||||
{
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
pxAssume( (uint)pid < PluginId_Count );
|
||||
Console.WriteLn( L"Binding %s\t: %s ", tbl_PluginInfo[pid].GetShortname().c_str(), srcfile.c_str() );
|
||||
m_info[pid] = new PluginStatus_t( pid, srcfile );
|
||||
}
|
||||
|
||||
void PluginManager::Load( const wxString (&folders)[PluginId_Count] )
|
||||
{
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
|
||||
if( !NeedsLoad() ) return;
|
||||
|
||||
wxDoNotLogInThisScope please;
|
||||
|
||||
Console.WriteLn( Color_StrongBlue, "Loading plugins..." );
|
||||
|
||||
ConsoleIndentScope indent;
|
||||
const PluginInfo* pi = tbl_PluginInfo; do
|
||||
{
|
||||
Load( pi->id, folders[pi->id] );
|
||||
pxYield( 2 );
|
||||
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
indent.EndScope();
|
||||
|
||||
CDVDapi_Plugin.newDiskCB( cdvdNewDiskCB );
|
||||
|
||||
// Hack for PAD's stupid parameter passed on Init
|
||||
PADinit = (_PADinit)m_info[PluginId_PAD].CommonBindings.Init;
|
||||
m_info[PluginId_PAD].CommonBindings.Init = _hack_PADinit;
|
||||
PADinit = (_PADinit)m_info[PluginId_PAD]->CommonBindings.Init;
|
||||
m_info[PluginId_PAD]->CommonBindings.Init = _hack_PADinit;
|
||||
|
||||
Console.WriteLn( Color_StrongBlue, "Plugins loaded successfully.\n" );
|
||||
|
||||
|
@ -822,90 +927,34 @@ PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] )
|
|||
throw Exception::PluginLoadError( PluginId_Mcd, wxEmptyString, "Internal Memorycard Plugin failed to load." );
|
||||
}
|
||||
|
||||
g_plugins = this;
|
||||
|
||||
SendSettingsFolder();
|
||||
}
|
||||
|
||||
PluginManager::~PluginManager() throw()
|
||||
void PluginManager::Unload(PluginsEnum_t pid)
|
||||
{
|
||||
try
|
||||
{
|
||||
Close();
|
||||
Shutdown();
|
||||
}
|
||||
DESTRUCTOR_CATCHALL
|
||||
// All library unloading done automatically.
|
||||
|
||||
if( g_plugins == this )
|
||||
g_plugins = NULL;
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
pxAssume( (uint)pid < PluginId_Count );
|
||||
m_info[pid].Delete();
|
||||
}
|
||||
|
||||
void PluginManager::BindCommon( PluginsEnum_t pid )
|
||||
void PluginManager::Unload()
|
||||
{
|
||||
const LegacyApi_CommonMethod* current = s_MethMessCommon;
|
||||
VoidMethod** target = (VoidMethod**)&m_info[pid].CommonBindings;
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
|
||||
wxDoNotLogInThisScope please;
|
||||
if( NeedsShutdown() )
|
||||
Console.Warning( "(SysCorePlugins) Warning: Unloading plugins prior to shutdown!" );
|
||||
|
||||
while( current->MethodName != NULL )
|
||||
{
|
||||
*target = (VoidMethod*)m_info[pid].Lib.GetSymbol( current->GetMethodName( pid ) );
|
||||
//Shutdown();
|
||||
|
||||
if( *target == NULL )
|
||||
*target = current->Fallback;
|
||||
if( !NeedsUnload() ) return;
|
||||
|
||||
if( *target == NULL )
|
||||
{
|
||||
throw Exception::PluginLoadError( pid, m_info[pid].Filename,
|
||||
wxsFormat( L"\nMethod binding failure on: %s\n", current->GetMethodName( pid ).c_str() ),
|
||||
_( "Configured plugin is not a PCSX2 plugin, or is for an older unsupported version of PCSX2." )
|
||||
);
|
||||
}
|
||||
DbgCon.WriteLn( Color_StrongBlue, "Unloading plugins..." );
|
||||
|
||||
target++;
|
||||
current++;
|
||||
}
|
||||
}
|
||||
for( int i=PluginId_Count-1; i>=0; --i )
|
||||
Unload( tbl_PluginInfo[i].id );
|
||||
|
||||
void PluginManager::BindRequired( PluginsEnum_t pid )
|
||||
{
|
||||
const LegacyApi_ReqMethod* current = s_MethMessReq[pid];
|
||||
const wxDynamicLibrary& lib = m_info[pid].Lib;
|
||||
DbgCon.WriteLn( Color_StrongBlue, "Plugins unloaded successfully." );
|
||||
|
||||
wxDoNotLogInThisScope please;
|
||||
|
||||
while( current->MethodName != NULL )
|
||||
{
|
||||
*(current->Dest) = (VoidMethod*)lib.GetSymbol( current->GetMethodName() );
|
||||
|
||||
if( *(current->Dest) == NULL )
|
||||
*(current->Dest) = current->Fallback;
|
||||
|
||||
if( *(current->Dest) == NULL )
|
||||
{
|
||||
throw Exception::PluginLoadError( pid, m_info[pid].Filename,
|
||||
wxsFormat( L"\nMethod binding failure on: %s\n", current->GetMethodName().c_str() ),
|
||||
_( "Configured plugin is not a valid PCSX2 plugin, or is for an older unsupported version of PCSX2." )
|
||||
);
|
||||
}
|
||||
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::BindOptional( PluginsEnum_t pid )
|
||||
{
|
||||
const LegacyApi_OptMethod* current = s_MethMessOpt[pid];
|
||||
const wxDynamicLibrary& lib = m_info[pid].Lib;
|
||||
|
||||
wxDoNotLogInThisScope please;
|
||||
|
||||
while( current->MethodName != NULL )
|
||||
{
|
||||
*(current->Dest) = (VoidMethod*)lib.GetSymbol( current->GetMethodName() );
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
// Exceptions:
|
||||
|
@ -978,7 +1027,8 @@ bool PluginManager::OpenPlugin_FW()
|
|||
|
||||
void PluginManager::Open( PluginsEnum_t pid )
|
||||
{
|
||||
if( m_info[pid].IsOpened ) return;
|
||||
pxAssume( (uint)pid < PluginId_Count );
|
||||
if( IsOpen(pid) ) return;
|
||||
|
||||
Console.Indent().WriteLn( "Opening %s", tbl_PluginInfo[pid].shortname );
|
||||
|
||||
|
@ -999,29 +1049,14 @@ void PluginManager::Open( PluginsEnum_t pid )
|
|||
if( !result )
|
||||
throw Exception::PluginOpenError( pid );
|
||||
|
||||
m_info[pid].IsOpened = true;
|
||||
}
|
||||
|
||||
bool PluginManager::NeedsOpen() const
|
||||
{
|
||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
if( !m_info[pi->id].IsOpened ) break;
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
return pi->shortname != NULL;
|
||||
}
|
||||
|
||||
bool PluginManager::NeedsClose() const
|
||||
{
|
||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
if( m_info[pi->id].IsOpened ) break;
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
return pi->shortname != NULL;
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
if( m_info[pid] ) m_info[pid]->IsOpened = true;
|
||||
}
|
||||
|
||||
void PluginManager::Open()
|
||||
{
|
||||
Init();
|
||||
|
||||
if( !NeedsOpen() ) return; // Spam stopper: returns before writing any logs. >_<
|
||||
|
||||
Console.WriteLn( Color_StrongBlue, "Opening plugins..." );
|
||||
|
@ -1041,12 +1076,23 @@ void PluginManager::Open()
|
|||
Console.WriteLn( Color_StrongBlue, "Plugins opened successfully." );
|
||||
}
|
||||
|
||||
void PluginManager::_generalclose( PluginsEnum_t pid )
|
||||
{
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
if( m_info[pid] ) m_info[pid]->CommonBindings.Close();
|
||||
}
|
||||
|
||||
void PluginManager::ClosePlugin_GS()
|
||||
{
|
||||
// force-close PAD before GS, because the PAD depends on the GS window.
|
||||
// old-skool: force-close PAD before GS, because the PAD depends on the GS window.
|
||||
|
||||
Close( PluginId_PAD );
|
||||
GetMTGS().Suspend();
|
||||
if( GetMTGS().IsSelf() )
|
||||
_generalclose( PluginId_GS );
|
||||
else
|
||||
{
|
||||
if( !GSopen2 ) Close( PluginId_PAD );
|
||||
GetMTGS().Suspend();
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::ClosePlugin_CDVD()
|
||||
|
@ -1056,33 +1102,35 @@ void PluginManager::ClosePlugin_CDVD()
|
|||
|
||||
void PluginManager::ClosePlugin_PAD()
|
||||
{
|
||||
m_info[PluginId_PAD].CommonBindings.Close();
|
||||
_generalclose( PluginId_PAD );
|
||||
}
|
||||
|
||||
void PluginManager::ClosePlugin_SPU2()
|
||||
{
|
||||
m_info[PluginId_SPU2].CommonBindings.Close();
|
||||
_generalclose( PluginId_SPU2 );
|
||||
}
|
||||
|
||||
void PluginManager::ClosePlugin_DEV9()
|
||||
{
|
||||
m_info[PluginId_DEV9].CommonBindings.Close();
|
||||
_generalclose( PluginId_DEV9 );
|
||||
}
|
||||
|
||||
void PluginManager::ClosePlugin_USB()
|
||||
{
|
||||
m_info[PluginId_USB].CommonBindings.Close();
|
||||
_generalclose( PluginId_USB );
|
||||
}
|
||||
|
||||
void PluginManager::ClosePlugin_FW()
|
||||
{
|
||||
m_info[PluginId_FW].CommonBindings.Close();
|
||||
_generalclose( PluginId_FW );
|
||||
}
|
||||
|
||||
|
||||
void PluginManager::Close( PluginsEnum_t pid )
|
||||
{
|
||||
if( !m_info[pid].IsOpened ) return;
|
||||
pxAssume( (uint)pid < PluginId_Count );
|
||||
|
||||
if( !IsOpen(pid) ) return;
|
||||
Console.Indent().WriteLn( "Closing %s", tbl_PluginInfo[pid].shortname );
|
||||
|
||||
switch( pid )
|
||||
|
@ -1094,11 +1142,12 @@ void PluginManager::Close( PluginsEnum_t pid )
|
|||
case PluginId_USB: ClosePlugin_USB(); break;
|
||||
case PluginId_FW: ClosePlugin_FW(); break;
|
||||
case PluginId_DEV9: ClosePlugin_DEV9(); break;
|
||||
|
||||
|
||||
jNO_DEFAULT;
|
||||
}
|
||||
|
||||
m_info[pid].IsOpened = false;
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
if( m_info[pid] ) m_info[pid]->IsOpened = false;
|
||||
}
|
||||
|
||||
void PluginManager::Close()
|
||||
|
@ -1126,22 +1175,26 @@ void PluginManager::Close()
|
|||
//
|
||||
void PluginManager::Init()
|
||||
{
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
|
||||
if( !NeedsInit() ) return;
|
||||
|
||||
bool printlog = false;
|
||||
const PluginInfo* pi = tbl_PluginInfo; do
|
||||
{
|
||||
const PluginsEnum_t pid = pi->id;
|
||||
|
||||
if( m_info[pid].IsInitialized ) continue;
|
||||
if( !m_info[pid] || m_info[pid]->IsInitialized ) continue;
|
||||
if( !printlog )
|
||||
{
|
||||
Console.WriteLn( Color_StrongBlue, "Initializing plugins..." );
|
||||
printlog = true;
|
||||
}
|
||||
Console.Indent().WriteLn( "Init %s", tbl_PluginInfo[pid].shortname );
|
||||
if( 0 != m_info[pid].CommonBindings.Init() )
|
||||
if( 0 != m_info[pid]->CommonBindings.Init() )
|
||||
throw Exception::PluginInitError( pid );
|
||||
|
||||
m_info[pid].IsInitialized = true;
|
||||
m_info[pid]->IsInitialized = true;
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
if( SysPlugins.Mcd == NULL )
|
||||
|
@ -1166,9 +1219,13 @@ void PluginManager::Init()
|
|||
//
|
||||
void PluginManager::Shutdown()
|
||||
{
|
||||
GetMTGS().Cancel(); // cancel it for speedier shutdown!
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
if( !NeedsShutdown() ) return;
|
||||
|
||||
Close();
|
||||
pxAssumeDev( !NeedsClose(), "Cannot shut down plugins prior to Close()" );
|
||||
|
||||
GetMTGS().Cancel(); // cancel it for speedier shutdown!
|
||||
|
||||
DbgCon.WriteLn( Color_StrongGreen, "Shutting down plugins..." );
|
||||
|
||||
// Shutdown plugins in reverse order (probably doesn't matter...
|
||||
|
@ -1177,10 +1234,10 @@ void PluginManager::Shutdown()
|
|||
for( int i=PluginId_Count-1; i>=0; --i )
|
||||
{
|
||||
const PluginsEnum_t pid = tbl_PluginInfo[i].id;
|
||||
if( !m_info[pid].IsInitialized ) continue;
|
||||
if( !m_info[pid] || !m_info[pid]->IsInitialized ) continue;
|
||||
DevCon.Indent().WriteLn( "Shutdown %s", tbl_PluginInfo[pid].shortname );
|
||||
m_info[pid].IsInitialized = false;
|
||||
m_info[pid].CommonBindings.Shutdown();
|
||||
m_info[pid]->IsInitialized = false;
|
||||
m_info[pid]->CommonBindings.Shutdown();
|
||||
}
|
||||
|
||||
// More memorycard hacks!!
|
||||
|
@ -1208,7 +1265,8 @@ bool PluginManager::DoFreeze( PluginsEnum_t pid, int mode, freezeData* data )
|
|||
}
|
||||
else
|
||||
{
|
||||
return m_info[pid].CommonBindings.Freeze( mode, data ) != -1;
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
return !m_info[pid] || m_info[pid]->CommonBindings.Freeze( mode, data ) != -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1219,6 +1277,9 @@ bool PluginManager::DoFreeze( PluginsEnum_t pid, int mode, freezeData* data )
|
|||
//
|
||||
void PluginManager::Freeze( PluginsEnum_t pid, SaveStateBase& state )
|
||||
{
|
||||
// No locking leeded -- DoFreeze locks as needed, and this avoids MTGS deadlock.
|
||||
//ScopedLock lock( m_mtx_PluginStatus );
|
||||
|
||||
Console.Indent().WriteLn( "%s %s", state.IsSaving() ? "Saving" : "Loading",
|
||||
tbl_PluginInfo[pid].shortname );
|
||||
|
||||
|
@ -1265,14 +1326,16 @@ void PluginManager::Freeze( PluginsEnum_t pid, SaveStateBase& state )
|
|||
|
||||
bool PluginManager::KeyEvent( const keyEvent& evt )
|
||||
{
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
|
||||
// [TODO] : The plan here is to give plugins "first chance" handling of keys.
|
||||
// Handling order will be fixed (GS, SPU2, PAD, etc), and the first plugin to
|
||||
// pick up the key and return "true" (for handled) will cause the loop to break.
|
||||
// The current version of PS2E doesn't support it yet, though.
|
||||
|
||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
if( pi->id != PluginId_PAD )
|
||||
m_info[pi->id].CommonBindings.KeyEvent( const_cast<keyEvent*>(&evt) );
|
||||
if( pi->id != PluginId_PAD && m_info[pi->id] )
|
||||
m_info[pi->id]->CommonBindings.KeyEvent( const_cast<keyEvent*>(&evt) );
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
return false;
|
||||
|
@ -1280,23 +1343,26 @@ bool PluginManager::KeyEvent( const keyEvent& evt )
|
|||
|
||||
void PluginManager::SendSettingsFolder()
|
||||
{
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
if( m_SettingsFolder.IsEmpty() ) return;
|
||||
|
||||
wxCharBuffer utf8buffer( m_SettingsFolder.ToUTF8() );
|
||||
|
||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
m_info[pi->id].CommonBindings.SetSettingsDir( utf8buffer );
|
||||
if( m_info[pi->id] ) m_info[pi->id]->CommonBindings.SetSettingsDir( utf8buffer );
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
}
|
||||
|
||||
void PluginManager::SetSettingsFolder( const wxString& folder )
|
||||
{
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
|
||||
wxString fixedfolder( folder );
|
||||
if( !fixedfolder.IsEmpty() && (fixedfolder[fixedfolder.length()-1] != wxFileName::GetPathSeparator() ) )
|
||||
{
|
||||
fixedfolder += wxFileName::GetPathSeparator();
|
||||
}
|
||||
|
||||
|
||||
if( m_SettingsFolder == fixedfolder ) return;
|
||||
m_SettingsFolder = fixedfolder;
|
||||
SendSettingsFolder();
|
||||
|
@ -1304,26 +1370,126 @@ void PluginManager::SetSettingsFolder( const wxString& folder )
|
|||
|
||||
void PluginManager::Configure( PluginsEnum_t pid )
|
||||
{
|
||||
m_info[pid].CommonBindings.Configure();
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
if( m_info[pid] ) m_info[pid]->CommonBindings.Configure();
|
||||
}
|
||||
|
||||
PluginManager* PluginManager_Create( const wxChar* (&folders)[PluginId_Count] )
|
||||
bool PluginManager::AreLoaded() const
|
||||
{
|
||||
wxString passins[PluginId_Count];
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
for( int i=0; i<PluginId_Count; ++i )
|
||||
{
|
||||
if( !m_info[i] ) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PluginManager::AreAnyLoaded() const
|
||||
{
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
for( int i=0; i<PluginId_Count; ++i )
|
||||
{
|
||||
if( m_info[i] ) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PluginManager::AreAnyInitialized() const
|
||||
{
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
passins[pi->id] = folders[pi->id];
|
||||
if( IsInitialized(pi->id) ) return true;
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
return new PluginManager( passins );
|
||||
return false;
|
||||
}
|
||||
|
||||
static PluginManagerBase s_pluginman_placebo;
|
||||
|
||||
// retrieves a handle to the current plugin manager. Plugin manager is assumed to be valid,
|
||||
// and debug-level assertions are performed on the validity of the handle.
|
||||
PluginManagerBase& GetPluginManager()
|
||||
bool PluginManager::IsOpen( PluginsEnum_t pid ) const
|
||||
{
|
||||
if( g_plugins == NULL ) return s_pluginman_placebo;
|
||||
return *g_plugins;
|
||||
pxAssume( (uint)pid < PluginId_Count );
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
return m_info[pid] && m_info[pid]->IsOpened;
|
||||
}
|
||||
|
||||
bool PluginManager::IsInitialized( PluginsEnum_t pid ) const
|
||||
{
|
||||
pxAssume( (uint)pid < PluginId_Count );
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
return m_info[pid] && m_info[pid]->IsInitialized;
|
||||
}
|
||||
|
||||
bool PluginManager::IsLoaded( PluginsEnum_t pid ) const
|
||||
{
|
||||
pxAssume( (uint)pid < PluginId_Count );
|
||||
return !!m_info[pid];
|
||||
}
|
||||
|
||||
bool PluginManager::NeedsLoad() const
|
||||
{
|
||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
if( !IsLoaded(pi->id) ) return true;
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PluginManager::NeedsUnload() const
|
||||
{
|
||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
if( IsLoaded(pi->id) ) return true;
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PluginManager::NeedsInit() const
|
||||
{
|
||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
if( !IsInitialized(pi->id) ) return true;
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PluginManager::NeedsShutdown() const
|
||||
{
|
||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
if( IsInitialized(pi->id) ) return true;
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PluginManager::NeedsOpen() const
|
||||
{
|
||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
if( !IsOpen(pi->id) ) return true;
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PluginManager::NeedsClose() const
|
||||
{
|
||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
if( IsOpen(pi->id) ) return true;
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const wxString PluginManager::GetName( PluginsEnum_t pid ) const
|
||||
{
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
pxAssume( (uint)pid < PluginId_Count );
|
||||
return m_info[pid] ? m_info[pid]->Name : _("Unloaded Plugin");
|
||||
}
|
||||
|
||||
const wxString PluginManager::GetVersion( PluginsEnum_t pid ) const
|
||||
{
|
||||
ScopedLock lock( m_mtx_PluginStatus );
|
||||
pxAssume( (uint)pid < PluginId_Count );
|
||||
return m_info[pid] ? m_info[pid]->Version : _("0.0");
|
||||
}
|
||||
|
|
116
pcsx2/Plugins.h
116
pcsx2/Plugins.h
|
@ -21,6 +21,8 @@
|
|||
#include "PS2Edefs.h"
|
||||
#include "PluginCallbacks.h"
|
||||
|
||||
#include "Utilities/Threading.h"
|
||||
|
||||
#include <wx/dynlib.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -125,7 +127,7 @@ namespace Exception
|
|||
PluginId = pid;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// This exception is thrown when a plugin returns an error while trying to save itself.
|
||||
// Typically this should be a very rare occurance since a plugin typically shoudn't
|
||||
// be doing memory allocations or file access during state saving.
|
||||
|
@ -222,57 +224,20 @@ public:
|
|||
|
||||
extern SysPluginBindings SysPlugins;
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// PluginManagerBase Class
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Provides a basic placebo "do-nothing" interface for plugin management. This is used
|
||||
// to avoid NULL pointer exceptions/segfaults when referencing the plugin manager global
|
||||
// handle.
|
||||
//
|
||||
// Note: The Init and Freeze methods of this class will cause debug assertions, but Close
|
||||
// methods fail silently, on the premise that Close and Shutdown are typically run from
|
||||
// exception handlers or cleanup code, and null pointers should be silently ignored in
|
||||
// favor of continuing cleanup.
|
||||
//
|
||||
class PluginManagerBase
|
||||
{
|
||||
DeclareNoncopyableObject( PluginManagerBase );
|
||||
|
||||
public:
|
||||
PluginManagerBase() {}
|
||||
virtual ~PluginManagerBase() {}
|
||||
|
||||
virtual void Init() { pxFail( "Null PluginManager!" ); }
|
||||
virtual void Shutdown() {}
|
||||
virtual void Open() { }
|
||||
virtual void Open( PluginsEnum_t pid ) { pxFail( "Null PluginManager!" ); }
|
||||
virtual void Close( PluginsEnum_t pid ) {}
|
||||
virtual void Close() {}
|
||||
|
||||
virtual bool IsOpen( PluginsEnum_t pid ) const { return false; }
|
||||
|
||||
virtual void Freeze( PluginsEnum_t pid, SaveStateBase& state ) { pxFail( "Null PluginManager!" ); }
|
||||
virtual bool DoFreeze( PluginsEnum_t pid, int mode, freezeData* data )
|
||||
{
|
||||
pxFail( "Null PluginManager!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool KeyEvent( const keyEvent& evt ) { return false; }
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// PluginManager Class
|
||||
// --------------------------------------------------------------------------------------
|
||||
//
|
||||
class PluginManager : public PluginManagerBase
|
||||
class PluginManager
|
||||
{
|
||||
DeclareNoncopyableObject( PluginManager );
|
||||
|
||||
protected:
|
||||
struct PluginStatus_t
|
||||
class PluginStatus_t
|
||||
{
|
||||
public:
|
||||
PluginsEnum_t pid;
|
||||
|
||||
bool IsInitialized;
|
||||
bool IsOpened;
|
||||
|
||||
|
@ -283,23 +248,44 @@ protected:
|
|||
LegacyPluginAPI_Common CommonBindings;
|
||||
wxDynamicLibrary Lib;
|
||||
|
||||
public:
|
||||
PluginStatus_t()
|
||||
{
|
||||
IsInitialized = false;
|
||||
IsOpened = false;
|
||||
}
|
||||
|
||||
PluginStatus_t( PluginsEnum_t _pid, const wxString& srcfile );
|
||||
virtual ~PluginStatus_t() throw() { }
|
||||
|
||||
protected:
|
||||
void BindCommon( PluginsEnum_t pid );
|
||||
void BindRequired( PluginsEnum_t pid );
|
||||
void BindOptional( PluginsEnum_t pid );
|
||||
};
|
||||
|
||||
const PS2E_LibraryAPI* m_mcdPlugin;
|
||||
wxString m_SettingsFolder;
|
||||
const PS2E_LibraryAPI* m_mcdPlugin;
|
||||
wxString m_SettingsFolder;
|
||||
Threading::MutexRecursive m_mtx_PluginStatus;
|
||||
|
||||
public: // hack until we unsuck plugins...
|
||||
PluginStatus_t m_info[PluginId_Count];
|
||||
ScopedPtr<PluginStatus_t> m_info[PluginId_Count];
|
||||
|
||||
public:
|
||||
PluginManager( const wxString (&folders)[PluginId_Count] );
|
||||
PluginManager();
|
||||
virtual ~PluginManager() throw();
|
||||
|
||||
void Load( PluginsEnum_t pid, const wxString& srcfile );
|
||||
void Load( const wxString (&folders)[PluginId_Count] );
|
||||
void Unload();
|
||||
void Unload( PluginsEnum_t pid );
|
||||
|
||||
bool AreLoaded() const;
|
||||
bool AreAnyLoaded() const;
|
||||
bool AreAnyInitialized() const;
|
||||
|
||||
Threading::Mutex& GetMutex() { return m_mtx_PluginStatus; }
|
||||
|
||||
virtual void Init();
|
||||
virtual void Shutdown();
|
||||
virtual void Open();
|
||||
|
@ -307,11 +293,10 @@ public:
|
|||
virtual void Close( PluginsEnum_t pid );
|
||||
virtual void Close();
|
||||
|
||||
virtual bool IsOpen( PluginsEnum_t pid ) const { return m_info[pid].IsOpened; }
|
||||
|
||||
virtual bool NeedsClose() const;
|
||||
virtual bool NeedsOpen() const;
|
||||
|
||||
virtual bool IsOpen( PluginsEnum_t pid ) const;
|
||||
virtual bool IsInitialized( PluginsEnum_t pid ) const;
|
||||
virtual bool IsLoaded( PluginsEnum_t pid ) const;
|
||||
|
||||
virtual void Freeze( PluginsEnum_t pid, SaveStateBase& state );
|
||||
virtual bool DoFreeze( PluginsEnum_t pid, int mode, freezeData* data );
|
||||
|
||||
|
@ -320,15 +305,18 @@ public:
|
|||
virtual void SetSettingsFolder( const wxString& folder );
|
||||
virtual void SendSettingsFolder();
|
||||
|
||||
const wxString& GetName( PluginsEnum_t pid ) const { return m_info[pid].Name; }
|
||||
const wxString& GetVersion( PluginsEnum_t pid ) const { return m_info[pid].Version; }
|
||||
|
||||
friend PluginManager* PluginManager_Create( const wxChar* (&folders)[PluginId_Count] );
|
||||
const wxString GetName( PluginsEnum_t pid ) const;
|
||||
const wxString GetVersion( PluginsEnum_t pid ) const;
|
||||
|
||||
protected:
|
||||
void BindCommon( PluginsEnum_t pid );
|
||||
void BindRequired( PluginsEnum_t pid );
|
||||
void BindOptional( PluginsEnum_t pid );
|
||||
virtual bool NeedsClose() const;
|
||||
virtual bool NeedsOpen() const;
|
||||
|
||||
virtual bool NeedsShutdown() const;
|
||||
virtual bool NeedsInit() const;
|
||||
|
||||
virtual bool NeedsLoad() const;
|
||||
virtual bool NeedsUnload() const;
|
||||
|
||||
virtual bool OpenPlugin_GS();
|
||||
virtual bool OpenPlugin_CDVD();
|
||||
|
@ -337,6 +325,8 @@ protected:
|
|||
virtual bool OpenPlugin_DEV9();
|
||||
virtual bool OpenPlugin_USB();
|
||||
virtual bool OpenPlugin_FW();
|
||||
|
||||
void _generalclose( PluginsEnum_t pid );
|
||||
|
||||
virtual void ClosePlugin_GS();
|
||||
virtual void ClosePlugin_CDVD();
|
||||
|
@ -350,11 +340,13 @@ protected:
|
|||
};
|
||||
|
||||
extern const PluginInfo tbl_PluginInfo[];
|
||||
extern PluginManager* g_plugins;
|
||||
|
||||
extern PluginManager* PluginManager_Create( const wxChar* (&folders)[PluginId_Count] );
|
||||
// GetPluginManager() is a required external implementation. This function is *NOT*
|
||||
// provided by the PCSX2 core library. It provides an interface for the linking User
|
||||
// Interface apps or DLLs to reference their own instance of PluginManager (also allowing
|
||||
// them to extend the class and override virtual methods).
|
||||
|
||||
extern PluginManagerBase& GetPluginManager();
|
||||
extern PluginManager& GetCorePlugins();
|
||||
|
||||
// Hack to expose internal MemoryCard plugin:
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ void cpuReset()
|
|||
psxReset();
|
||||
|
||||
g_GameStarted = false;
|
||||
g_SkipBiosHack = EmuConfig.SkipBiosSplash;
|
||||
g_SkipBiosHack = EmuConfig.UseBOOT2Injection;
|
||||
|
||||
ElfCRC = 0;
|
||||
DiscID = L"";
|
||||
|
|
|
@ -1,598 +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 te License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* 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 "zlib/zlib.h"
|
||||
|
||||
#include "App.h"
|
||||
#include "HostGui.h"
|
||||
#include "AppSaveStates.h"
|
||||
#include "Utilities/EventSource.inl"
|
||||
|
||||
class _BaseStateThread;
|
||||
|
||||
template class EventSource<IEventListener_SaveStateThread>;
|
||||
static EventSource<IEventListener_SaveStateThread> m_evtsrc_SaveState;
|
||||
|
||||
// Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
|
||||
static SafeArray<u8> state_buffer;
|
||||
|
||||
static _BaseStateThread* current_state_thread = NULL;
|
||||
|
||||
// Simple lock boolean for the state buffer being in use by a thread.
|
||||
static NonblockingMutex state_buffer_lock;
|
||||
|
||||
// This boolean tracks if a savestate is actively saving. When a state is saving we
|
||||
// typically delay program termination to allow the state time to finish its work.
|
||||
//static bool state_is_saving = false;
|
||||
|
||||
// This boolean is to keep the system from resuming emulation until the current state has completely
|
||||
// uploaded or downloaded itself. It is only modified from the main thread, and should only be read
|
||||
// from the main thread.
|
||||
int sys_resume_lock = 0;
|
||||
|
||||
static FnType_OnThreadComplete* Callback_FreezeFinished = NULL;
|
||||
|
||||
static bool StateCopy_ForceClear()
|
||||
{
|
||||
sys_resume_lock = 0;
|
||||
state_buffer_lock.Release();
|
||||
state_buffer.Dispose();
|
||||
}
|
||||
|
||||
class _BaseStateThread : public PersistentThread,
|
||||
public virtual EventListener_AppStatus,
|
||||
public virtual IDeletableObject
|
||||
{
|
||||
typedef PersistentThread _parent;
|
||||
|
||||
protected:
|
||||
bool m_isStarted;
|
||||
|
||||
// Holds the pause/suspend state of the emulator when the state load/stave chain of action is started,
|
||||
// so that the proper state can be restored automatically on completion.
|
||||
bool m_resume_when_done;
|
||||
|
||||
public:
|
||||
virtual ~_BaseStateThread() throw()
|
||||
{
|
||||
if( !m_isStarted ) return;
|
||||
|
||||
// Assertion fails because C++ changes the 'this' pointer to the base class since
|
||||
// derived classes have been deallocated at this point the destructor!
|
||||
|
||||
//pxAssumeDev( current_state_thread == this, wxCharNull );
|
||||
current_state_thread = NULL;
|
||||
state_buffer_lock.Release(); // just in case;
|
||||
}
|
||||
|
||||
virtual bool IsFreezing() const=0;
|
||||
|
||||
protected:
|
||||
_BaseStateThread( const char* name, FnType_OnThreadComplete* onFinished )
|
||||
{
|
||||
Callback_FreezeFinished = onFinished;
|
||||
m_name = L"StateThread::" + fromUTF8(name);
|
||||
m_isStarted = false;
|
||||
m_resume_when_done = false;
|
||||
}
|
||||
|
||||
void OnStart()
|
||||
{
|
||||
if( !state_buffer_lock.TryAcquire() )
|
||||
throw Exception::CancelEvent( m_name + L"request ignored: state copy buffer is already locked!" );
|
||||
|
||||
_parent::OnStart();
|
||||
|
||||
current_state_thread = this;
|
||||
m_isStarted = true;
|
||||
}
|
||||
|
||||
void SendFinishEvent( int type )
|
||||
{
|
||||
wxGetApp().PostCommand( this, pxEvt_FreezeThreadFinished, type, m_resume_when_done );
|
||||
}
|
||||
|
||||
void AppStatusEvent_OnExit()
|
||||
{
|
||||
Cancel();
|
||||
wxGetApp().DeleteObject( this );
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// StateThread_Freeze
|
||||
// --------------------------------------------------------------------------------------
|
||||
class StateThread_Freeze : public _BaseStateThread
|
||||
{
|
||||
typedef _BaseStateThread _parent;
|
||||
|
||||
public:
|
||||
StateThread_Freeze( FnType_OnThreadComplete* onFinished ) : _BaseStateThread( "Freeze", onFinished )
|
||||
{
|
||||
if( !SysHasValidState() )
|
||||
throw Exception::RuntimeError( L"Cannot complete state freeze request; the virtual machine state is reset.", _("You'll need to start a new virtual machine before you can save its state.") );
|
||||
}
|
||||
|
||||
bool IsFreezing() const { return true; }
|
||||
|
||||
protected:
|
||||
void OnStart()
|
||||
{
|
||||
try
|
||||
{
|
||||
++sys_resume_lock;
|
||||
m_resume_when_done = CoreThread.Pause();
|
||||
|
||||
_parent::OnStart();
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
wxGetApp().DeleteObject( this );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void ExecuteTaskInThread()
|
||||
{
|
||||
memSavingState( state_buffer ).FreezeAll();
|
||||
}
|
||||
|
||||
void OnCleanupInThread()
|
||||
{
|
||||
SendFinishEvent( SaveStateAction_CreateFinished );
|
||||
_parent::OnCleanupInThread();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static const char SavestateIdentString[] = "PCSX2 Savestate";
|
||||
static const uint SavestateIdentLen = sizeof(SavestateIdentString);
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// StateThread_ZipToDisk
|
||||
// --------------------------------------------------------------------------------------
|
||||
class StateThread_ZipToDisk : public _BaseStateThread
|
||||
{
|
||||
typedef _BaseStateThread _parent;
|
||||
|
||||
protected:
|
||||
const wxString m_filename;
|
||||
gzFile m_gzfp;
|
||||
|
||||
public:
|
||||
StateThread_ZipToDisk( FnType_OnThreadComplete* onFinished, bool resume_done, const wxString& file )
|
||||
: _BaseStateThread( "ZipToDisk", onFinished )
|
||||
, m_filename( file )
|
||||
{
|
||||
m_gzfp = NULL;
|
||||
m_resume_when_done = resume_done;
|
||||
}
|
||||
|
||||
virtual ~StateThread_ZipToDisk() throw()
|
||||
{
|
||||
if( m_gzfp != NULL ) gzclose( m_gzfp );
|
||||
}
|
||||
|
||||
bool IsFreezing() const { return true; }
|
||||
|
||||
protected:
|
||||
void OnStart()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_gzfp = gzopen( m_filename.ToUTF8(), "wb" );
|
||||
if( m_gzfp == NULL )
|
||||
throw Exception::CreateStream( m_filename, "Cannot create savestate file for writing." );
|
||||
|
||||
_parent::OnStart();
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
wxGetApp().DeleteObject( this );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void ExecuteTaskInThread()
|
||||
{
|
||||
Yield( 3 );
|
||||
|
||||
static const int BlockSize = 0x20000;
|
||||
int curidx = 0;
|
||||
|
||||
gzsetparams(m_gzfp, Z_BEST_SPEED, Z_FILTERED); // Best speed at good compression
|
||||
gzbuffer(m_gzfp, 0x100000); // 1mb buffer size for less file fragments
|
||||
|
||||
gzwrite(m_gzfp, SavestateIdentString, sizeof( SavestateIdentString ));
|
||||
gzwrite(m_gzfp, &g_SaveVersion, sizeof( g_SaveVersion ));
|
||||
|
||||
do
|
||||
{
|
||||
int thisBlockSize = std::min( BlockSize, state_buffer.GetSizeInBytes() - curidx );
|
||||
if( gzwrite( m_gzfp, state_buffer.GetPtr(curidx), thisBlockSize ) < thisBlockSize )
|
||||
throw Exception::BadStream( m_filename );
|
||||
curidx += thisBlockSize;
|
||||
Yield( 1 );
|
||||
} while( curidx < state_buffer.GetSizeInBytes() );
|
||||
|
||||
Console.WriteLn( "State saved to disk without error." );
|
||||
}
|
||||
|
||||
void OnCleanupInThread()
|
||||
{
|
||||
SendFinishEvent( SaveStateAction_ZipToDiskFinished );
|
||||
_parent::OnCleanupInThread();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// StateThread_UnzipFromDisk
|
||||
// --------------------------------------------------------------------------------------
|
||||
class StateThread_UnzipFromDisk : public _BaseStateThread
|
||||
{
|
||||
typedef _BaseStateThread _parent;
|
||||
|
||||
protected:
|
||||
const wxString m_filename;
|
||||
gzFile m_gzfp;
|
||||
|
||||
// set true only once the whole file has finished loading. IF the thread is canceled or
|
||||
// an error occurs, this will remain false.
|
||||
bool m_finished;
|
||||
|
||||
public:
|
||||
StateThread_UnzipFromDisk( FnType_OnThreadComplete* onFinished, bool resume_done, const wxString& file )
|
||||
: _BaseStateThread( "UnzipFromDisk", onFinished )
|
||||
, m_filename( file )
|
||||
{
|
||||
m_gzfp = NULL;
|
||||
m_finished = false;
|
||||
m_resume_when_done = resume_done;
|
||||
}
|
||||
|
||||
virtual ~StateThread_UnzipFromDisk() throw()
|
||||
{
|
||||
if( m_gzfp != NULL ) gzclose( m_gzfp );
|
||||
}
|
||||
|
||||
bool IsFreezing() const { return false; }
|
||||
|
||||
protected:
|
||||
void OnStart()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_gzfp = gzopen( m_filename.ToUTF8(), "rb" );
|
||||
if( m_gzfp == NULL )
|
||||
throw Exception::CreateStream( m_filename, "Cannot open savestate file for reading." );
|
||||
|
||||
char ident[SavestateIdentLen] = {0};
|
||||
|
||||
int result = gzread(m_gzfp, ident, SavestateIdentLen);
|
||||
if( result == -1 )
|
||||
throw Exception::SaveStateLoadError( m_filename, "Unable to read any data from the gzip archive." );
|
||||
|
||||
if( result < SavestateIdentLen )
|
||||
throw Exception::SaveStateLoadError( m_filename );
|
||||
|
||||
if( strcmp(SavestateIdentString, ident) )
|
||||
throw Exception::SaveStateLoadError( m_filename,
|
||||
wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ),
|
||||
_("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.")
|
||||
);
|
||||
|
||||
u32 savever;
|
||||
gzread(m_gzfp, &savever, sizeof(g_SaveVersion));
|
||||
|
||||
if( (savever >> 16) != (g_SaveVersion >> 16) )
|
||||
throw Exception::SaveStateLoadError( m_filename,
|
||||
wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ),
|
||||
_("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.")
|
||||
);
|
||||
|
||||
if( savever > g_SaveVersion )
|
||||
throw Exception::SaveStateLoadError( m_filename,
|
||||
wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ),
|
||||
_("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.")
|
||||
);
|
||||
|
||||
_parent::OnStart();
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
wxGetApp().DeleteObject( this );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void ExecuteTaskInThread()
|
||||
{
|
||||
// fixme: should start initially with the file size, and then grow from there.
|
||||
|
||||
static const int BlockSize = 0x100000;
|
||||
state_buffer.MakeRoomFor( 0x800000 ); // start with an 8 meg buffer to avoid frequent reallocation.
|
||||
gzbuffer(m_gzfp, 0x100000); // 1mb buffer for zlib internal operations
|
||||
int curidx = 0;
|
||||
do
|
||||
{
|
||||
state_buffer.MakeRoomFor( curidx+BlockSize );
|
||||
gzread( m_gzfp, state_buffer.GetPtr(curidx), BlockSize );
|
||||
curidx += BlockSize;
|
||||
TestCancel();
|
||||
} while( !gzeof(m_gzfp) );
|
||||
|
||||
m_finished = true;
|
||||
}
|
||||
|
||||
void OnCleanupInThread()
|
||||
{
|
||||
SendFinishEvent( SaveStateAction_UnzipFromDiskFinished );
|
||||
_parent::OnCleanupInThread();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt )
|
||||
{
|
||||
// clear the OnFreezeFinished to NULL now, in case of error.
|
||||
// (but only actually run it if no errors occur)
|
||||
FnType_OnThreadComplete* fn_tmp = Callback_FreezeFinished;
|
||||
Callback_FreezeFinished = NULL;
|
||||
|
||||
{
|
||||
ScopedPtr<PersistentThread> thr( (PersistentThread*)evt.GetClientData() );
|
||||
if( !pxAssertDev( thr != NULL, "NULL thread handle on freeze finished?" ) ) return;
|
||||
|
||||
current_state_thread = NULL;
|
||||
state_buffer_lock.Release();
|
||||
--sys_resume_lock;
|
||||
|
||||
m_evtsrc_SaveState.Dispatch( (SaveStateActionType)evt.GetInt() );
|
||||
|
||||
thr->RethrowException();
|
||||
}
|
||||
|
||||
if( fn_tmp != NULL ) fn_tmp( evt );
|
||||
}
|
||||
|
||||
static void OnFinished_Resume( const wxCommandEvent& evt )
|
||||
{
|
||||
CoreThread.RecoverState();
|
||||
if( evt.GetExtraLong() ) CoreThread.Resume();
|
||||
}
|
||||
|
||||
static wxString zip_dest_filename;
|
||||
|
||||
static void OnFinished_ZipToDisk( const wxCommandEvent& evt )
|
||||
{
|
||||
if( !pxAssertDev( evt.GetInt() == SaveStateAction_CreateFinished, "Unexpected StateThreadAction value, aborting save." ) ) return;
|
||||
|
||||
if( zip_dest_filename.IsEmpty() )
|
||||
{
|
||||
Console.Warning( "Cannot save state to disk: empty filename specified." );
|
||||
return;
|
||||
}
|
||||
|
||||
// Phase 2: Record to disk!!
|
||||
(new StateThread_ZipToDisk( NULL, !!evt.GetExtraLong(), zip_dest_filename ))->Start();
|
||||
|
||||
CoreThread.Resume();
|
||||
}
|
||||
|
||||
class InvokeAction_WhenSaveComplete :
|
||||
public IEventListener_SaveStateThread,
|
||||
public IDeletableObject
|
||||
{
|
||||
protected:
|
||||
IActionInvocation* m_action;
|
||||
|
||||
public:
|
||||
InvokeAction_WhenSaveComplete( IActionInvocation* action )
|
||||
{
|
||||
m_action = action;
|
||||
}
|
||||
|
||||
virtual ~InvokeAction_WhenSaveComplete() throw() {}
|
||||
|
||||
void SaveStateAction_OnZipToDiskFinished()
|
||||
{
|
||||
if( m_action )
|
||||
{
|
||||
m_action->InvokeAction();
|
||||
safe_delete( m_action );
|
||||
}
|
||||
wxGetApp().DeleteObject( this );
|
||||
}
|
||||
};
|
||||
|
||||
class InvokeAction_WhenStateCopyComplete : public InvokeAction_WhenSaveComplete
|
||||
{
|
||||
public:
|
||||
InvokeAction_WhenStateCopyComplete( IActionInvocation* action )
|
||||
: InvokeAction_WhenSaveComplete( action )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~InvokeAction_WhenStateCopyComplete() throw() {}
|
||||
|
||||
void SaveStateAction_OnCreateFinished()
|
||||
{
|
||||
SaveStateAction_OnZipToDiskFinished();
|
||||
}
|
||||
};
|
||||
|
||||
// =====================================================================================================
|
||||
// StateCopy Public Interface
|
||||
// =====================================================================================================
|
||||
|
||||
bool StateCopy_InvokeOnSaveComplete( IActionInvocation* sst )
|
||||
{
|
||||
AffinityAssert_AllowFromMain();
|
||||
|
||||
if( current_state_thread == NULL || !current_state_thread->IsFreezing() )
|
||||
{
|
||||
delete sst;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_evtsrc_SaveState.Add( new InvokeAction_WhenSaveComplete( sst ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StateCopy_InvokeOnCopyComplete( IActionInvocation* sst )
|
||||
{
|
||||
AffinityAssert_AllowFromMain();
|
||||
|
||||
if( current_state_thread == NULL || !current_state_thread->IsFreezing() )
|
||||
{
|
||||
delete sst;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_evtsrc_SaveState.Add( new InvokeAction_WhenStateCopyComplete( sst ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
void StateCopy_SaveToFile( const wxString& file )
|
||||
{
|
||||
if( state_buffer_lock.IsLocked() ) return;
|
||||
zip_dest_filename = file;
|
||||
(new StateThread_Freeze( OnFinished_ZipToDisk ))->Start();
|
||||
Console.WriteLn( Color_StrongGreen, L"Saving savestate to file: %s", zip_dest_filename.c_str() );
|
||||
}
|
||||
|
||||
void StateCopy_LoadFromFile( const wxString& file )
|
||||
{
|
||||
if( state_buffer_lock.IsLocked() ) return;
|
||||
bool resume_when_done = CoreThread.Pause();
|
||||
(new StateThread_UnzipFromDisk( OnFinished_Resume, resume_when_done, file ))->Start();
|
||||
}
|
||||
|
||||
// Saves recovery state info to the given saveslot, or saves the active emulation state
|
||||
// (if one exists) and no recovery data was found. This is needed because when a recovery
|
||||
// state is made, the emulation state is usually reset so the only persisting state is
|
||||
// the one in the memory save. :)
|
||||
void StateCopy_SaveToSlot( uint num )
|
||||
{
|
||||
if( state_buffer_lock.IsLocked() ) return;
|
||||
|
||||
zip_dest_filename = SaveStateBase::GetFilename( num );
|
||||
(new StateThread_Freeze( OnFinished_ZipToDisk ))->Start();
|
||||
Console.WriteLn( Color_StrongGreen, "Saving savestate to slot %d...", num );
|
||||
Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", zip_dest_filename.c_str() );
|
||||
}
|
||||
|
||||
void StateCopy_LoadFromSlot( uint slot )
|
||||
{
|
||||
if( state_buffer_lock.IsLocked() ) return;
|
||||
wxString file( SaveStateBase::GetFilename( slot ) );
|
||||
|
||||
if( !wxFileExists( file ) )
|
||||
{
|
||||
Console.Warning( "Savestate slot %d is empty.", slot );
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLn( Color_StrongGreen, "Loading savestate from slot %d...", slot );
|
||||
Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() );
|
||||
|
||||
bool resume_when_done = CoreThread.Pause();
|
||||
(new StateThread_UnzipFromDisk( OnFinished_Resume, resume_when_done, file ))->Start();
|
||||
}
|
||||
|
||||
bool StateCopy_IsValid()
|
||||
{
|
||||
return !state_buffer.IsDisposed();
|
||||
}
|
||||
|
||||
const SafeArray<u8>* StateCopy_GetBuffer()
|
||||
{
|
||||
if( state_buffer_lock.IsLocked() || state_buffer.IsDisposed() ) return NULL;
|
||||
return &state_buffer;
|
||||
}
|
||||
|
||||
void StateCopy_FreezeToMem()
|
||||
{
|
||||
if( state_buffer_lock.IsLocked() ) return;
|
||||
(new StateThread_Freeze( OnFinished_Resume ))->Start();
|
||||
}
|
||||
|
||||
class Acquire_And_Block
|
||||
{
|
||||
protected:
|
||||
bool m_DisposeWhenFinished;
|
||||
bool m_Acquired;
|
||||
|
||||
public:
|
||||
Acquire_And_Block( bool dispose )
|
||||
{
|
||||
m_DisposeWhenFinished = dispose;
|
||||
m_Acquired = false;
|
||||
|
||||
/*
|
||||
// If the state buffer is locked and we're being called from the main thread then we need
|
||||
// to cancel the current action. This is needed because state_buffer_lock is only updated
|
||||
// from events handled on the main thread.
|
||||
|
||||
if( wxThread::IsMain() )
|
||||
throw Exception::CancelEvent( "Blocking ThawFromMem canceled due to existing state buffer lock." );
|
||||
else*/
|
||||
|
||||
while ( !state_buffer_lock.TryAcquire() )
|
||||
{
|
||||
pxAssume( current_state_thread != NULL );
|
||||
current_state_thread->Block();
|
||||
wxGetApp().ProcessPendingEvents(); // Trying this for now, may or may not work due to recursive pitfalls (see above)
|
||||
};
|
||||
|
||||
m_Acquired = true;
|
||||
}
|
||||
|
||||
virtual ~Acquire_And_Block() throw()
|
||||
{
|
||||
if( m_DisposeWhenFinished )
|
||||
state_buffer.Dispose();
|
||||
|
||||
if( m_Acquired )
|
||||
state_buffer_lock.Release();
|
||||
}
|
||||
};
|
||||
|
||||
void StateCopy_FreezeToMem_Blocking()
|
||||
{
|
||||
Acquire_And_Block blocker( false );
|
||||
memSavingState( state_buffer ).FreezeAll();
|
||||
}
|
||||
|
||||
// Copies the saved state into the active VM, and automatically free's the saved state data.
|
||||
void StateCopy_ThawFromMem_Blocking()
|
||||
{
|
||||
Acquire_And_Block blocker( true );
|
||||
memLoadingState( state_buffer ).FreezeAll();
|
||||
}
|
||||
|
||||
void StateCopy_Clear()
|
||||
{
|
||||
if( state_buffer_lock.IsLocked() ) return;
|
||||
state_buffer.Dispose();
|
||||
}
|
||||
|
||||
bool StateCopy_IsBusy()
|
||||
{
|
||||
return state_buffer_lock.IsLocked();
|
||||
}
|
|
@ -47,8 +47,18 @@ wxString SaveStateBase::GetFilename( int slot )
|
|||
}
|
||||
|
||||
SaveStateBase::SaveStateBase( SafeArray<u8>& memblock )
|
||||
: m_memory( memblock )
|
||||
{
|
||||
Init( &memblock );
|
||||
}
|
||||
|
||||
SaveStateBase::SaveStateBase( SafeArray<u8>* memblock )
|
||||
{
|
||||
Init( memblock );
|
||||
}
|
||||
|
||||
void SaveStateBase::Init( SafeArray<u8>* memblock )
|
||||
{
|
||||
m_memory = memblock;
|
||||
m_version = g_SaveVersion;
|
||||
m_idx = 0;
|
||||
m_sectid = FreezeId_Unknown;
|
||||
|
@ -58,12 +68,14 @@ SaveStateBase::SaveStateBase( SafeArray<u8>& memblock )
|
|||
|
||||
void SaveStateBase::PrepBlock( int size )
|
||||
{
|
||||
pxAssumeDev( m_memory, "Savestate memory/buffer pointer is null!" );
|
||||
|
||||
const int end = m_idx+size;
|
||||
if( IsSaving() )
|
||||
m_memory.MakeRoomFor( end );
|
||||
m_memory->MakeRoomFor( end );
|
||||
else
|
||||
{
|
||||
if( m_memory.GetSizeInBytes() < end )
|
||||
if( m_memory->GetSizeInBytes() < end )
|
||||
throw Exception::SaveStateLoadError();
|
||||
}
|
||||
}
|
||||
|
@ -203,7 +215,7 @@ void SaveStateBase::WritebackSectionLength( int seekpos, int sectlen, const wxCh
|
|||
if( IsSaving() )
|
||||
{
|
||||
// write back the section length...
|
||||
*((u32*)m_memory.GetPtr(seekpos-4)) = realsectsize;
|
||||
*((u32*)m_memory->GetPtr(seekpos-4)) = realsectsize;
|
||||
}
|
||||
else // IsLoading!!
|
||||
{
|
||||
|
@ -304,7 +316,7 @@ bool SaveStateBase::FreezeSection( int seek_section )
|
|||
if( isSeeking )
|
||||
m_idx += sectlen;
|
||||
else
|
||||
g_plugins->Freeze( (PluginsEnum_t)m_pid, *this );
|
||||
GetCorePlugins().Freeze( (PluginsEnum_t)m_pid, *this );
|
||||
|
||||
WritebackSectionLength( seekpos, sectlen, L"Plugins" );
|
||||
|
||||
|
@ -364,19 +376,26 @@ memSavingState::memSavingState( SafeArray<u8>& save_to )
|
|||
{
|
||||
}
|
||||
|
||||
memSavingState::memSavingState( SafeArray<u8>* save_to )
|
||||
: SaveStateBase( save_to )
|
||||
{
|
||||
}
|
||||
|
||||
// Saving of state data
|
||||
void memSavingState::FreezeMem( void* data, int size )
|
||||
{
|
||||
m_memory.MakeRoomFor( m_idx+size );
|
||||
memcpy_fast( m_memory.GetPtr(m_idx), data, size );
|
||||
m_memory->MakeRoomFor( m_idx+size );
|
||||
memcpy_fast( m_memory->GetPtr(m_idx), data, size );
|
||||
m_idx += size;
|
||||
}
|
||||
|
||||
void memSavingState::FreezeAll()
|
||||
{
|
||||
pxAssumeDev( m_memory, "Savestate memory/buffer pointer is null!" );
|
||||
|
||||
// 90% of all savestates fit in under 45 megs (and require more than 43 megs, so might as well...)
|
||||
m_memory.ChunkSize = ReallocThreshold;
|
||||
m_memory.MakeRoomFor( MemoryBaseAllocSize );
|
||||
m_memory->ChunkSize = ReallocThreshold;
|
||||
m_memory->MakeRoomFor( MemoryBaseAllocSize );
|
||||
|
||||
_parent::FreezeAll();
|
||||
}
|
||||
|
@ -386,12 +405,17 @@ memLoadingState::memLoadingState( const SafeArray<u8>& load_from )
|
|||
{
|
||||
}
|
||||
|
||||
memLoadingState::memLoadingState( const SafeArray<u8>* load_from )
|
||||
: SaveStateBase( const_cast<SafeArray<u8>*>(load_from) )
|
||||
{
|
||||
}
|
||||
|
||||
memLoadingState::~memLoadingState() throw() { }
|
||||
|
||||
// Loading of state data
|
||||
void memLoadingState::FreezeMem( void* data, int size )
|
||||
{
|
||||
const u8* const src = m_memory.GetPtr(m_idx);
|
||||
const u8* const src = m_memory->GetPtr(m_idx);
|
||||
m_idx += size;
|
||||
memcpy_fast( data, src, size );
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ namespace Exception
|
|||
class SaveStateBase
|
||||
{
|
||||
protected:
|
||||
SafeArray<u8>& m_memory;
|
||||
SafeArray<u8>* m_memory;
|
||||
char m_tagspace[32];
|
||||
|
||||
u32 m_version; // version of the savestate being loaded.
|
||||
|
@ -123,6 +123,7 @@ protected:
|
|||
|
||||
public:
|
||||
SaveStateBase( SafeArray<u8>& memblock );
|
||||
SaveStateBase( SafeArray<u8>* memblock );
|
||||
virtual ~SaveStateBase() { }
|
||||
|
||||
static wxString GetFilename( int slot );
|
||||
|
@ -159,7 +160,7 @@ public:
|
|||
|
||||
u8* GetBlockPtr()
|
||||
{
|
||||
return &m_memory[m_idx];
|
||||
return m_memory->GetPtr(m_idx);
|
||||
}
|
||||
|
||||
void CommitBlock( int size )
|
||||
|
@ -190,6 +191,7 @@ public:
|
|||
void gsFreeze();
|
||||
|
||||
protected:
|
||||
void Init( SafeArray<u8>* memblock );
|
||||
|
||||
// Load/Save functions for the various components of our glorious emulator!
|
||||
|
||||
|
@ -233,6 +235,7 @@ protected:
|
|||
public:
|
||||
virtual ~memSavingState() throw() { }
|
||||
memSavingState( SafeArray<u8>& save_to );
|
||||
memSavingState( SafeArray<u8>* save_to );
|
||||
|
||||
// Saving of state data to a memory buffer
|
||||
void FreezeMem( void* data, int size );
|
||||
|
@ -245,13 +248,15 @@ class memLoadingState : public SaveStateBase
|
|||
{
|
||||
public:
|
||||
virtual ~memLoadingState() throw();
|
||||
|
||||
memLoadingState( const SafeArray<u8>& load_from );
|
||||
memLoadingState( const SafeArray<u8>* load_from );
|
||||
|
||||
// Loading of state data from a memory buffer...
|
||||
void FreezeMem( void* data, int size );
|
||||
bool SeekToSection( PluginsEnum_t pid );
|
||||
|
||||
bool IsSaving() const { return false; }
|
||||
bool IsFinished() const { return m_idx >= m_memory.GetSizeInBytes(); }
|
||||
bool IsFinished() const { return m_idx >= m_memory->GetSizeInBytes(); }
|
||||
};
|
||||
|
||||
|
|
|
@ -64,21 +64,21 @@ const Pcsx2Config EmuConfig;
|
|||
Pcsx2Config::GSOptions& SetGSConfig()
|
||||
{
|
||||
//DbgCon.WriteLn( "Direct modification of EmuConfig.GS detected" );
|
||||
AffinityAssert_AllowFromMain();
|
||||
AffinityAssert_AllowFrom_MainUI();
|
||||
return const_cast<Pcsx2Config::GSOptions&>(EmuConfig.GS);
|
||||
}
|
||||
|
||||
ConsoleLogFilters& SetConsoleConfig()
|
||||
{
|
||||
//DbgCon.WriteLn( "Direct modification of EmuConfig.Log detected" );
|
||||
AffinityAssert_AllowFromMain();
|
||||
AffinityAssert_AllowFrom_MainUI();
|
||||
return const_cast<ConsoleLogFilters&>(EmuConfig.Log);
|
||||
}
|
||||
|
||||
TraceLogFilters& SetTraceConfig()
|
||||
{
|
||||
//DbgCon.WriteLn( "Direct modification of EmuConfig.TraceLog detected" );
|
||||
AffinityAssert_AllowFromMain();
|
||||
AffinityAssert_AllowFrom_MainUI();
|
||||
return const_cast<TraceLogFilters&>(EmuConfig.Trace);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,25 +57,27 @@ void SysCoreThread::Cancel( bool isBlocking )
|
|||
{
|
||||
m_CoreCancelDamnit = true;
|
||||
_parent::Cancel();
|
||||
ReleaseResumeLock();
|
||||
}
|
||||
|
||||
bool SysCoreThread::Cancel( const wxTimeSpan& span )
|
||||
{
|
||||
m_CoreCancelDamnit = true;
|
||||
if( _parent::Cancel( span ) )
|
||||
{
|
||||
ReleaseResumeLock();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SysCoreThread::OnStart()
|
||||
{
|
||||
m_CoreCancelDamnit = false;
|
||||
_parent::OnStart();
|
||||
}
|
||||
|
||||
void SysCoreThread::Start()
|
||||
{
|
||||
if( g_plugins == NULL ) return;
|
||||
g_plugins->Init();
|
||||
m_CoreCancelDamnit = false; // belongs in OnStart actually, but I'm tired :P
|
||||
if( !GetCorePlugins().AreLoaded() ) return;
|
||||
GetCorePlugins().Init();
|
||||
_parent::Start();
|
||||
}
|
||||
|
||||
|
@ -101,8 +103,8 @@ void SysCoreThread::OnResumeReady()
|
|||
// resumed manually).
|
||||
void SysCoreThread::RecoverState()
|
||||
{
|
||||
Pause();
|
||||
m_resetVirtualMachine = true;
|
||||
pxAssumeDev( IsPaused(), "Unsafe use of RecoverState function; Corethread is not paused/closed." );
|
||||
m_resetRecompilers = true;
|
||||
m_hasValidState = false;
|
||||
}
|
||||
|
||||
|
@ -122,50 +124,6 @@ void SysCoreThread::SetElfOverride( const wxString& elf )
|
|||
m_elf_override = elf;
|
||||
}
|
||||
|
||||
ScopedCoreThreadSuspend::ScopedCoreThreadSuspend()
|
||||
{
|
||||
m_ResumeWhenDone = GetCoreThread().Suspend();
|
||||
}
|
||||
|
||||
ScopedCoreThreadSuspend::~ScopedCoreThreadSuspend() throw()
|
||||
{
|
||||
if( m_ResumeWhenDone )
|
||||
{
|
||||
Console.WriteLn( Color_Gray, "Scoped CoreThread suspend was not allowed to resume." );
|
||||
}
|
||||
}
|
||||
|
||||
// Resumes CoreThread execution, but *only* if it was in a running state when this object
|
||||
// was instanized. Subsequent calls to Resume() will be ignored.
|
||||
void ScopedCoreThreadSuspend::Resume()
|
||||
{
|
||||
if( m_ResumeWhenDone )
|
||||
GetCoreThread().Resume();
|
||||
m_ResumeWhenDone = false;
|
||||
}
|
||||
|
||||
ScopedCoreThreadPause::ScopedCoreThreadPause()
|
||||
{
|
||||
m_ResumeWhenDone = GetCoreThread().Pause();
|
||||
}
|
||||
|
||||
ScopedCoreThreadPause::~ScopedCoreThreadPause() throw()
|
||||
{
|
||||
if( m_ResumeWhenDone )
|
||||
{
|
||||
Console.WriteLn( Color_Gray, "Scoped CoreThread pause was not allowed to resume." );
|
||||
}
|
||||
}
|
||||
|
||||
// Resumes CoreThread execution, but *only* if it was in a running state when this object
|
||||
// was instanized. Subsequent calls to Resume() will be ignored.
|
||||
void ScopedCoreThreadPause::Resume()
|
||||
{
|
||||
if( m_ResumeWhenDone )
|
||||
GetCoreThread().Resume();
|
||||
m_ResumeWhenDone = false;
|
||||
}
|
||||
|
||||
|
||||
// Applies a full suite of new settings, which will automatically facilitate the necessary
|
||||
// resets of the core and components (including plugins, if needed). The scope of resetting
|
||||
|
@ -175,27 +133,14 @@ void SysCoreThread::ApplySettings( const Pcsx2Config& src )
|
|||
{
|
||||
if( src == EmuConfig ) return;
|
||||
|
||||
ScopedCoreThreadPause sys_paused;
|
||||
|
||||
if( !pxAssertDev( IsPaused(), "CoreThread is not paused; settings cannot be applied." ) ) return;
|
||||
|
||||
m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Recompiler != EmuConfig.Recompiler ) ||
|
||||
( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
|
||||
m_resetProfilers = ( src.Profiler != EmuConfig.Profiler );
|
||||
m_resetVsyncTimers = ( src.GS != EmuConfig.GS );
|
||||
|
||||
const_cast<Pcsx2Config&>(EmuConfig) = src;
|
||||
sys_paused.Resume();
|
||||
}
|
||||
|
||||
void SysCoreThread::ChangeCdvdSource( CDVD_SourceType type )
|
||||
{
|
||||
if( type == CDVDsys_GetSourceType() ) return;
|
||||
|
||||
// Fast change of the CDVD source only -- a Pause will suffice.
|
||||
|
||||
bool resumeWhenDone = Pause();
|
||||
GetPluginManager().Close( PluginId_CDVD );
|
||||
CDVDsys_ChangeSource( type );
|
||||
if( resumeWhenDone ) Resume();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -306,6 +251,13 @@ void SysCoreThread::StateCheckInThread()
|
|||
_reset_stuff_as_needed(); // kinda redundant but could catch unexpected threaded state changes...
|
||||
}
|
||||
|
||||
// Allows an override point and solves an SEH "exception-type boundary" problem (can't mix
|
||||
// SEH and C++ exceptions in the same function).
|
||||
void SysCoreThread::DoCpuExecute()
|
||||
{
|
||||
Cpu->Execute();
|
||||
}
|
||||
|
||||
void SysCoreThread::ExecuteTaskInThread()
|
||||
{
|
||||
Threading::EnableHiresScheduler();
|
||||
|
@ -317,21 +269,19 @@ void SysCoreThread::ExecuteTaskInThread()
|
|||
PCSX2_PAGEFAULT_PROTECT {
|
||||
do {
|
||||
StateCheckInThread();
|
||||
Cpu->Execute();
|
||||
DoCpuExecute();
|
||||
} while( true );
|
||||
} PCSX2_PAGEFAULT_EXCEPT;
|
||||
}
|
||||
|
||||
void SysCoreThread::OnSuspendInThread()
|
||||
{
|
||||
if( g_plugins != NULL )
|
||||
g_plugins->Close();
|
||||
GetCorePlugins().Close();
|
||||
}
|
||||
|
||||
void SysCoreThread::OnResumeInThread( bool isSuspended )
|
||||
{
|
||||
if( g_plugins != NULL )
|
||||
g_plugins->Open();
|
||||
GetCorePlugins().Open();
|
||||
|
||||
CpuInitializeMess();
|
||||
}
|
||||
|
@ -346,8 +296,7 @@ void SysCoreThread::OnCleanupInThread()
|
|||
|
||||
Threading::DisableHiresScheduler();
|
||||
|
||||
if( g_plugins != NULL )
|
||||
g_plugins->Close();
|
||||
GetCorePlugins().Close();
|
||||
|
||||
tls_coreThread = NULL;
|
||||
_parent::OnCleanupInThread();
|
||||
|
|
|
@ -51,34 +51,15 @@ void SysThreadBase::OnStart()
|
|||
{
|
||||
if( !pxAssertDev( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return;
|
||||
|
||||
m_ResumeEvent.Reset();
|
||||
m_sem_Resume.Reset();
|
||||
m_sem_ChangingExecMode.Reset();
|
||||
|
||||
FrankenMutex( m_ExecModeMutex );
|
||||
FrankenMutex( m_RunningLock );
|
||||
|
||||
_parent::OnStart();
|
||||
}
|
||||
|
||||
// (overridable) Timeout period before a thread is considered potentially
|
||||
// deadlocked. SysThreadBase default is 4 seconds.
|
||||
//
|
||||
wxTimeSpan SysThreadBase::GetDeadlockTimeout() const
|
||||
{
|
||||
return wxTimeSpan( 0, 0, 4, 0 );
|
||||
}
|
||||
|
||||
void SysThreadBase::DoThreadDeadlocked()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SysThreadBase::ThrowDeadlockException()
|
||||
{
|
||||
throw Exception::ThreadDeadlock( *this,
|
||||
wxsFormat(L"Unhandled deadlock while suspending thread '%s'", m_name.c_str()),
|
||||
wxsFormat(L"'%s' thread is not responding to suspend requests. It may be deadlocked or just running *really* slow.", m_name.c_str())
|
||||
);
|
||||
}
|
||||
|
||||
// Suspends emulation and closes the emulation state (including plugins) at the next PS2 vsync,
|
||||
// and returns control to the calling thread; or does nothing if the core is already suspended.
|
||||
//
|
||||
|
@ -95,21 +76,19 @@ void SysThreadBase::ThrowDeadlockException()
|
|||
// Exceptions:
|
||||
// CancelEvent - thrown if the thread is already in a Paused or Closing state. Because
|
||||
// actions that pause emulation typically rely on plugins remaining loaded/active,
|
||||
// Suspension must cansel itself forcefully or risk crashing whatever other action is
|
||||
// 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().
|
||||
//
|
||||
bool SysThreadBase::Suspend( bool isBlocking )
|
||||
void SysThreadBase::Suspend( bool isBlocking )
|
||||
{
|
||||
if( IsSelf() || !IsRunning() ) return false;
|
||||
if( IsSelf() || !IsRunning() ) return;
|
||||
|
||||
// shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
|
||||
// from Resume or OnResumeReady code.
|
||||
if( m_ExecMode == ExecMode_Closed ) return false;
|
||||
|
||||
bool retval = false;
|
||||
if( m_ExecMode == ExecMode_Closed ) return;
|
||||
|
||||
{
|
||||
ScopedLock locker( m_ExecModeMutex );
|
||||
|
@ -117,16 +96,20 @@ bool SysThreadBase::Suspend( bool isBlocking )
|
|||
switch( m_ExecMode )
|
||||
{
|
||||
// Check again -- status could have changed since above.
|
||||
case ExecMode_Closed: return false;
|
||||
case ExecMode_Closed: return;
|
||||
|
||||
case ExecMode_Pausing:
|
||||
case ExecMode_Paused:
|
||||
if( !isBlocking ) return retval;
|
||||
throw Exception::CancelEvent( "Another thread is pausing the VM state." );
|
||||
|
||||
if( !isBlocking )
|
||||
throw Exception::CancelEvent( "Cannot suspend in non-blocking fashion: Another thread is pausing the VM state." );
|
||||
|
||||
m_ExecMode = ExecMode_Closing;
|
||||
m_sem_Resume.Post();
|
||||
m_sem_ChangingExecMode.Wait();
|
||||
break;
|
||||
|
||||
case ExecMode_Opened:
|
||||
m_ExecMode = ExecMode_Closing;
|
||||
retval = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -135,40 +118,29 @@ bool SysThreadBase::Suspend( bool isBlocking )
|
|||
}
|
||||
|
||||
if( isBlocking )
|
||||
{
|
||||
if( !m_RunningLock.Wait( GetDeadlockTimeout() ) )
|
||||
{
|
||||
DoThreadDeadlocked();
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
m_RunningLock.Wait();
|
||||
}
|
||||
|
||||
// Returns:
|
||||
// The previous suspension state; true if the thread was running or false if it was
|
||||
// closed, not running, or paused.
|
||||
//
|
||||
bool SysThreadBase::Pause()
|
||||
void SysThreadBase::Pause()
|
||||
{
|
||||
if( IsSelf() || !IsRunning() ) return false;
|
||||
if( IsSelf() || !IsRunning() ) return;
|
||||
|
||||
// shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
|
||||
// from Resume or OnResumeReady code.
|
||||
if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return false;
|
||||
|
||||
bool retval = false;
|
||||
if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return;
|
||||
|
||||
{
|
||||
ScopedLock locker( m_ExecModeMutex );
|
||||
|
||||
// Check again -- status could have changed since above.
|
||||
if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return false;
|
||||
if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return;
|
||||
|
||||
if( m_ExecMode == ExecMode_Opened )
|
||||
{
|
||||
m_ExecMode = ExecMode_Pausing;
|
||||
retval = true;
|
||||
}
|
||||
|
||||
pxAssumeDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
|
||||
|
||||
|
@ -176,8 +148,6 @@ bool SysThreadBase::Pause()
|
|||
}
|
||||
|
||||
m_RunningLock.Wait();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Resumes the core execution state, or does nothing is the core is already running. If
|
||||
|
@ -199,9 +169,6 @@ void SysThreadBase::Resume()
|
|||
if( IsSelf() ) return;
|
||||
if( m_ExecMode == ExecMode_Opened ) return;
|
||||
|
||||
ScopedNonblockingLock resprotect( m_ResumeProtection );
|
||||
if( resprotect.Failed() ) return;
|
||||
|
||||
ScopedLock locker( m_ExecModeMutex );
|
||||
|
||||
// Implementation Note:
|
||||
|
@ -231,7 +198,7 @@ void SysThreadBase::Resume()
|
|||
m_RunningLock.Wait();
|
||||
if( !m_running ) return;
|
||||
if( (m_ExecMode != ExecMode_Closed) && (m_ExecMode != ExecMode_Paused) ) return;
|
||||
if( g_plugins == NULL ) return;
|
||||
if( !GetCorePlugins().AreLoaded() ) return;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -240,7 +207,7 @@ void SysThreadBase::Resume()
|
|||
|
||||
OnResumeReady();
|
||||
m_ExecMode = ExecMode_Opened;
|
||||
m_ResumeEvent.Post();
|
||||
m_sem_Resume.Post();
|
||||
}
|
||||
|
||||
|
||||
|
@ -296,11 +263,17 @@ void SysThreadBase::StateCheckInThread()
|
|||
|
||||
case ExecMode_Paused:
|
||||
while( m_ExecMode == ExecMode_Paused )
|
||||
m_ResumeEvent.WaitWithoutYield();
|
||||
|
||||
m_sem_Resume.WaitWithoutYield();
|
||||
|
||||
m_RunningLock.Acquire();
|
||||
OnResumeInThread( false );
|
||||
break;
|
||||
if( m_ExecMode != ExecMode_Closing )
|
||||
{
|
||||
OnResumeInThread( false );
|
||||
break;
|
||||
}
|
||||
m_sem_ChangingExecMode.Post();
|
||||
|
||||
// fallthrough if we're switching to closing state...
|
||||
|
||||
// -------------------------------------
|
||||
case ExecMode_Closing:
|
||||
|
@ -313,7 +286,7 @@ void SysThreadBase::StateCheckInThread()
|
|||
|
||||
case ExecMode_Closed:
|
||||
while( m_ExecMode == ExecMode_Closed )
|
||||
m_ResumeEvent.WaitWithoutYield();
|
||||
m_sem_Resume.WaitWithoutYield();
|
||||
|
||||
m_RunningLock.Acquire();
|
||||
OnResumeInThread( true );
|
||||
|
|
|
@ -16,60 +16,48 @@
|
|||
#pragma once
|
||||
|
||||
#include "Utilities/PersistentThread.h"
|
||||
#include "Utilities/RwMutex.h"
|
||||
#include "x86emitter/tools.h"
|
||||
|
||||
#include "CDVD/CDVDaccess.h"
|
||||
|
||||
using namespace Threading;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ISysThread
|
||||
// --------------------------------------------------------------------------------------
|
||||
class ISysThread : public virtual IThread
|
||||
{
|
||||
public:
|
||||
ISysThread() {}
|
||||
virtual ~ISysThread() throw() {}
|
||||
|
||||
virtual bool Suspend( bool isBlocking = true ) { return false; }
|
||||
virtual bool Pause() { return false; }
|
||||
virtual void Resume() {}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysThreadBase
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
class SysThreadBase : public PersistentThread, public virtual ISysThread
|
||||
class SysThreadBase : public PersistentThread
|
||||
{
|
||||
typedef PersistentThread _parent;
|
||||
|
||||
public:
|
||||
// Important: The order of these enumerations matters. All "not-open" statuses must
|
||||
// be listed before ExecMode_Closed, since there are "optimized" tests that rely on the
|
||||
// assumption that "ExecMode <= ExecMode_Closed" equates to a closed thread status.
|
||||
// Important: The order of these enumerations matters! Optimized tests are used for both
|
||||
// Closed and Paused states.
|
||||
enum ExecutionMode
|
||||
{
|
||||
// Thread has not been created yet. Typically this is the same as IsRunning()
|
||||
// returning FALSE.
|
||||
ExecMode_NoThreadYet,
|
||||
|
||||
// Close signal has been sent to the thread, but the thread's response is still
|
||||
// pending (thread is busy/running).
|
||||
ExecMode_Closing,
|
||||
|
||||
// Thread is safely paused, with plugins in a "closed" state, and waiting for a
|
||||
// resume/open signal.
|
||||
ExecMode_Closed,
|
||||
|
||||
// Thread is safely paused, with plugins in an "open" state, and waiting for a
|
||||
// resume/open signal.
|
||||
ExecMode_Paused,
|
||||
|
||||
// Thread is active and running, with pluigns in an "open" state.
|
||||
ExecMode_Opened,
|
||||
|
||||
// Close signal has been sent to the thread, but the thread's response is still
|
||||
// pending (thread is busy/running).
|
||||
ExecMode_Closing,
|
||||
|
||||
// Pause signal has been sent to the thread, but the thread's response is still
|
||||
// pending (thread is busy/running).
|
||||
ExecMode_Pausing,
|
||||
|
||||
// Thread is safely paused, with plugins in an "open" state, and waiting for a
|
||||
// resume/open signal.
|
||||
ExecMode_Paused,
|
||||
};
|
||||
|
||||
protected:
|
||||
|
@ -77,20 +65,19 @@ protected:
|
|||
|
||||
// This lock is used to avoid simultaneous requests to Suspend/Resume/Pause from
|
||||
// contending threads.
|
||||
MutexLockRecursive m_ExecModeMutex;
|
||||
MutexRecursive m_ExecModeMutex;
|
||||
|
||||
// Used to wake up the thread from sleeping when it's in a suspended state.
|
||||
Semaphore m_ResumeEvent;
|
||||
Semaphore m_sem_Resume;
|
||||
|
||||
// Used to synchronize inline changes from paused to suspended status.
|
||||
Semaphore m_sem_ChangingExecMode;
|
||||
|
||||
// Locked whenever the thread is not in a suspended state (either closed or paused).
|
||||
// Issue a Wait against this mutex for performing actions that require the thread
|
||||
// to be suspended.
|
||||
Mutex m_RunningLock;
|
||||
|
||||
// Protects the thread from re-entrant resume requests while dependent resources are
|
||||
// being constructed.
|
||||
NonblockingMutex m_ResumeProtection;
|
||||
|
||||
Mutex m_RunningLock;
|
||||
|
||||
public:
|
||||
explicit SysThreadBase();
|
||||
virtual ~SysThreadBase() throw();
|
||||
|
@ -102,32 +89,27 @@ public:
|
|||
// first.
|
||||
bool IsOpen() const
|
||||
{
|
||||
return m_ExecMode > ExecMode_Closed;
|
||||
return IsRunning() && (m_ExecMode > ExecMode_Closed);
|
||||
}
|
||||
|
||||
bool IsClosed() const { return !IsOpen(); }
|
||||
|
||||
bool IsPaused() const { return !IsRunning() || (m_ExecMode <= ExecMode_Paused); }
|
||||
|
||||
bool HasPendingStateChangeRequest() const
|
||||
{
|
||||
ExecutionMode mode = m_ExecMode;
|
||||
return (mode == ExecMode_Closing) || (mode == ExecMode_Pausing);
|
||||
}
|
||||
|
||||
bool IsClosed() const { return !IsOpen(); }
|
||||
|
||||
ExecutionMode GetExecutionMode() const { return m_ExecMode; }
|
||||
Mutex& ExecutionModeMutex() { return m_ExecModeMutex; }
|
||||
|
||||
virtual bool Suspend( bool isBlocking = true );
|
||||
virtual void Suspend( bool isBlocking = true );
|
||||
virtual void Resume();
|
||||
virtual bool Pause();
|
||||
|
||||
virtual bool AcquireResumeLock() { return m_ResumeProtection.TryAcquire(); }
|
||||
virtual void ReleaseResumeLock() { m_ResumeProtection.Release(); }
|
||||
|
||||
virtual wxTimeSpan GetDeadlockTimeout() const;
|
||||
virtual void ThrowDeadlockException();
|
||||
|
||||
virtual void Pause();
|
||||
|
||||
protected:
|
||||
virtual void DoThreadDeadlocked();
|
||||
virtual void OnStart();
|
||||
|
||||
// This function is called by Resume immediately prior to releasing the suspension of
|
||||
|
@ -146,14 +128,14 @@ protected:
|
|||
// handles invocation by the following guidelines: Called *in thread* from StateCheckInThread()
|
||||
// prior to suspending the thread (ie, when Suspend() has been called on a separate
|
||||
// thread, requesting this thread suspend itself temporarily). After this is called,
|
||||
// the thread enters a waiting state on the m_ResumeEvent semaphore.
|
||||
// the thread enters a waiting state on the m_sem_Resume semaphore.
|
||||
virtual void OnSuspendInThread()=0;
|
||||
|
||||
// Extending classes should implement this, but should not call it. The parent class
|
||||
// handles invocation by the following guidelines: Called *in thread* from StateCheckInThread()
|
||||
// prior to pausing the thread (ie, when Pause() has been called on a separate thread,
|
||||
// requesting this thread pause itself temporarily). After this is called, the thread
|
||||
// enters a waiting state on the m_ResumeEvent semaphore.
|
||||
// enters a waiting state on the m_sem_Resume semaphore.
|
||||
virtual void OnPauseInThread()=0;
|
||||
|
||||
// Extending classes should implement this, but should not call it. The parent class
|
||||
|
@ -165,6 +147,7 @@ protected:
|
|||
virtual void OnResumeInThread( bool isSuspended )=0;
|
||||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysCoreThread class
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -173,6 +156,8 @@ class SysCoreThread : public SysThreadBase
|
|||
typedef SysThreadBase _parent;
|
||||
|
||||
protected:
|
||||
s32 m_CloseTemporary;
|
||||
|
||||
bool m_resetRecompilers;
|
||||
bool m_resetProfilers;
|
||||
bool m_resetVsyncTimers;
|
||||
|
@ -183,7 +168,7 @@ protected:
|
|||
bool m_CoreCancelDamnit;
|
||||
|
||||
wxString m_elf_override;
|
||||
|
||||
|
||||
SSE_MXCSR m_mxcsr_saved;
|
||||
|
||||
public:
|
||||
|
@ -199,7 +184,7 @@ public:
|
|||
virtual void RecoverState();
|
||||
virtual void Cancel( bool isBlocking=true );
|
||||
virtual bool Cancel( const wxTimeSpan& timeout );
|
||||
|
||||
|
||||
bool HasValidState()
|
||||
{
|
||||
return m_hasValidState;
|
||||
|
@ -209,55 +194,52 @@ public:
|
|||
virtual void StateCheckInThread();
|
||||
virtual void VsyncInThread();
|
||||
virtual void PostVsyncToUI()=0;
|
||||
|
||||
|
||||
virtual const wxString& GetElfOverride() const { return m_elf_override; }
|
||||
virtual void SetElfOverride( const wxString& elf );
|
||||
virtual void ChangeCdvdSource( CDVD_SourceType type );
|
||||
|
||||
|
||||
protected:
|
||||
void _reset_stuff_as_needed();
|
||||
|
||||
virtual void CpuInitializeMess();
|
||||
virtual void Start();
|
||||
virtual void OnStart();
|
||||
virtual void OnSuspendInThread();
|
||||
virtual void OnPauseInThread() {}
|
||||
virtual void OnResumeInThread( bool IsSuspended );
|
||||
virtual void OnCleanupInThread();
|
||||
virtual void ExecuteTaskInThread();
|
||||
virtual void DoCpuReset();
|
||||
|
||||
virtual void DoCpuExecute();
|
||||
|
||||
void _StateCheckThrows();
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ScopedCoreThreadSuspend
|
||||
// --------------------------------------------------------------------------------------
|
||||
// This class behaves a bit differently from other scoped classes due to the "standard"
|
||||
// assumption that we actually do *not* want to resume CoreThread operations when an
|
||||
// exception occurs. Because of this, the destructor of this class does *not* unroll the
|
||||
// suspend operation. Instead you must manually instruct the class to resume using a call
|
||||
// to the provisioned Resume() method.
|
||||
//
|
||||
// If the class leaves scope without having been resumed, a log is written to the console.
|
||||
// This can be useful for troubleshooting, and also allows the log a second line of info
|
||||
// indicating the status of CoreThread execution at the time of the exception.
|
||||
//
|
||||
struct ScopedCoreThreadSuspend
|
||||
{
|
||||
bool m_ResumeWhenDone;
|
||||
|
||||
ScopedCoreThreadSuspend();
|
||||
virtual ~ScopedCoreThreadSuspend() throw();
|
||||
virtual void Resume();
|
||||
struct SysStateUnlockedParams
|
||||
{
|
||||
SysStateUnlockedParams() {}
|
||||
};
|
||||
|
||||
struct ScopedCoreThreadPause
|
||||
// --------------------------------------------------------------------------------------
|
||||
// IEventListener_SaveStateThread
|
||||
// --------------------------------------------------------------------------------------
|
||||
class IEventListener_SysState : public IEventDispatcher<SysStateUnlockedParams>
|
||||
{
|
||||
bool m_ResumeWhenDone;
|
||||
public:
|
||||
typedef SysStateUnlockedParams EvtParams;
|
||||
|
||||
ScopedCoreThreadPause();
|
||||
virtual ~ScopedCoreThreadPause() throw();
|
||||
virtual void Resume();
|
||||
public:
|
||||
IEventListener_SysState() {}
|
||||
virtual ~IEventListener_SysState() throw() {}
|
||||
|
||||
virtual void DispatchEvent( const SysStateUnlockedParams& status )
|
||||
{
|
||||
SysStateAction_OnUnlocked();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void SysStateAction_OnUnlocked();
|
||||
};
|
||||
|
||||
// GetCoreThread() is a required external implementation. This function is *NOT*
|
||||
|
@ -266,5 +248,3 @@ struct ScopedCoreThreadPause
|
|||
// them to extend the class and override virtual methods).
|
||||
//
|
||||
extern SysCoreThread& GetCoreThread();
|
||||
|
||||
extern int sys_resume_lock;
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
-------------------
|
||||
ZipTools (folder)
|
||||
-------------------
|
||||
|
||||
Contains C++ interfaces for zipping to/from various formats
|
||||
(primarily gzip and 7zip).
|
||||
|
||||
Notice: This folder is intended to be moved to a utility folder
|
||||
outside the main PCSX2 folders at a later date.
|
|
@ -0,0 +1,103 @@
|
|||
/* 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 "Utilities/PersistentThread.h"
|
||||
//#include "zlib/zlib.h"
|
||||
|
||||
using namespace Threading;
|
||||
|
||||
class IStreamWriter
|
||||
{
|
||||
public:
|
||||
virtual ~IStreamWriter() throw() {}
|
||||
|
||||
virtual void Write( const void* data, size_t size )=0;
|
||||
virtual wxString GetStreamName() const=0;
|
||||
|
||||
template< typename T >
|
||||
void Write( const T& data )
|
||||
{
|
||||
Write( &data, sizeof(data) );
|
||||
}
|
||||
};
|
||||
|
||||
class IStreamReader
|
||||
{
|
||||
public:
|
||||
virtual ~IStreamReader() throw() {}
|
||||
|
||||
virtual void Read( void* dest, size_t size )=0;
|
||||
virtual wxString GetStreamName() const=0;
|
||||
|
||||
template< typename T >
|
||||
void Read( T& dest )
|
||||
{
|
||||
Read( &dest, sizeof(dest) );
|
||||
}
|
||||
};
|
||||
|
||||
typedef void FnType_WriteCompressedHeader( IStreamWriter& thr );
|
||||
typedef void FnType_ReadCompressedHeader( IStreamReader& thr );
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// BaseCompressThread
|
||||
// --------------------------------------------------------------------------------------
|
||||
class BaseCompressThread
|
||||
: public PersistentThread
|
||||
, public IStreamWriter
|
||||
{
|
||||
typedef PersistentThread _parent;
|
||||
|
||||
protected:
|
||||
FnType_WriteCompressedHeader* m_WriteHeaderInThread;
|
||||
|
||||
const wxString m_filename;
|
||||
ScopedPtr< SafeArray< u8 > > m_src_buffer;
|
||||
|
||||
BaseCompressThread( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeHeader=NULL)
|
||||
: m_filename( file )
|
||||
, m_src_buffer( srcdata )
|
||||
{
|
||||
m_WriteHeaderInThread = writeHeader;
|
||||
}
|
||||
|
||||
virtual ~BaseCompressThread() throw() {}
|
||||
|
||||
public:
|
||||
wxString GetStreamName() const { return m_filename; }
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// CompressThread_gzip
|
||||
// --------------------------------------------------------------------------------------
|
||||
class CompressThread_gzip : public BaseCompressThread
|
||||
{
|
||||
typedef BaseCompressThread _parent;
|
||||
|
||||
protected:
|
||||
gzFile m_gzfp;
|
||||
|
||||
public:
|
||||
CompressThread_gzip( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeHeader=NULL );
|
||||
CompressThread_gzip( const wxString& file, ScopedPtr<SafeArray<u8>>& srcdata, FnType_WriteCompressedHeader* writeHeader=NULL );
|
||||
virtual ~CompressThread_gzip() throw();
|
||||
|
||||
protected:
|
||||
void Write( const void* data, size_t size );
|
||||
void ExecuteTaskInThread();
|
||||
void OnCleanupInThread();
|
||||
};
|
|
@ -0,0 +1,79 @@
|
|||
/* 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 te License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "App.h"
|
||||
#include "SaveState.h"
|
||||
#include "ThreadedZipTools.h"
|
||||
|
||||
|
||||
CompressThread_gzip::CompressThread_gzip( const wxString& file, SafeArray<u8>* srcdata, FnType_WriteCompressedHeader* writeheader )
|
||||
: BaseCompressThread( file, srcdata, writeheader )
|
||||
{
|
||||
m_gzfp = NULL;
|
||||
}
|
||||
|
||||
CompressThread_gzip::CompressThread_gzip( const wxString& file, ScopedPtr<SafeArray<u8>>& srcdata, FnType_WriteCompressedHeader* writeheader )
|
||||
: BaseCompressThread( file, srcdata.DetachPtr(), writeheader )
|
||||
{
|
||||
m_gzfp = NULL;
|
||||
}
|
||||
|
||||
CompressThread_gzip::~CompressThread_gzip() throw()
|
||||
{
|
||||
if( m_gzfp ) gzclose( m_gzfp );
|
||||
}
|
||||
|
||||
void CompressThread_gzip::Write( const void* data, size_t size )
|
||||
{
|
||||
if( gzwrite( m_gzfp, data, size ) == 0 )
|
||||
throw Exception::BadStream( m_filename, "Write to zip file failed." );
|
||||
}
|
||||
|
||||
void CompressThread_gzip::ExecuteTaskInThread()
|
||||
{
|
||||
if( !m_src_buffer ) return;
|
||||
|
||||
Yield( 3 );
|
||||
|
||||
if( !(m_gzfp = gzopen(m_filename.ToUTF8(), "wb")) )
|
||||
throw Exception::CannotCreateStream( m_filename );
|
||||
|
||||
gzsetparams(m_gzfp, Z_BEST_SPEED, Z_FILTERED); // Best speed at good compression
|
||||
gzbuffer(m_gzfp, 0x100000); // 1mb buffer size for less file fragments (Windows/NTFS)
|
||||
|
||||
if( m_WriteHeaderInThread )
|
||||
m_WriteHeaderInThread( *this );
|
||||
|
||||
static const int BlockSize = 0x64000;
|
||||
int curidx = 0;
|
||||
|
||||
do {
|
||||
int thisBlockSize = std::min( BlockSize, m_src_buffer->GetSizeInBytes() - curidx );
|
||||
if( gzwrite( m_gzfp, m_src_buffer->GetPtr(curidx), thisBlockSize ) < thisBlockSize )
|
||||
throw Exception::BadStream( m_filename );
|
||||
curidx += thisBlockSize;
|
||||
Yield( 3 );
|
||||
} while( curidx < m_src_buffer->GetSizeInBytes() );
|
||||
|
||||
Console.WriteLn( "(gzipThread) Data saved to disk without error." );
|
||||
}
|
||||
|
||||
void CompressThread_gzip::OnCleanupInThread()
|
||||
{
|
||||
wxGetApp().DeleteThread( this );
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
/* 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 te License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* 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"
|
||||
|
179
pcsx2/gui/App.h
179
pcsx2/gui/App.h
|
@ -21,7 +21,10 @@
|
|||
#include <wx/imaglist.h>
|
||||
#include <wx/apptrait.h>
|
||||
|
||||
#include "pxEventThread.h"
|
||||
|
||||
#include "AppCommon.h"
|
||||
#include "AppCoreThread.h"
|
||||
#include "RecentIsoList.h"
|
||||
|
||||
#include "System.h"
|
||||
|
@ -32,22 +35,12 @@
|
|||
class Pcsx2App;
|
||||
|
||||
typedef void FnType_OnThreadComplete(const wxCommandEvent& evt);
|
||||
typedef void (Pcsx2App::*FnPtr_AppMethod)();
|
||||
typedef void (Pcsx2App::*FnPtr_Pcsx2App)();
|
||||
|
||||
BEGIN_DECLARE_EVENT_TYPES()
|
||||
/*DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEVT_OpenGsPanel, -1 )*/
|
||||
|
||||
DECLARE_EVENT_TYPE( pxEvt_FreezeThreadFinished, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEvt_CoreThreadStatus, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEvt_LoadPluginsComplete, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEvt_PluginStatus, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEvt_SysExecute, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEvt_InvokeMethod, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEvt_LogicalVsync, -1 )
|
||||
|
||||
DECLARE_EVENT_TYPE( pxEvt_OpenModalDialog, -1 )
|
||||
//DECLARE_EVENT_TYPE( pxEvt_StuckThread, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEvt_ThreadTaskTimeout_SysExec, -1 )
|
||||
END_DECLARE_EVENT_TYPES()
|
||||
|
||||
// This is used when the GS plugin is handling its own window. Messages from the PAD
|
||||
|
@ -91,15 +84,14 @@ enum MenuIdentifiers
|
|||
MenuId_Boot_Iso, // Opens submenu with Iso browser, and recent isos.
|
||||
MenuId_IsoSelector, // Contains a submenu of selectable "favorite" isos
|
||||
MenuId_IsoBrowse, // Open dialog, runs selected iso.
|
||||
MenuId_Boot_CDVD, // opens a submenu filled by CDVD plugin (usually list of drives)
|
||||
MenuId_Boot_CDVD,
|
||||
MenuId_Boot_CDVD2,
|
||||
MenuId_Boot_ELF,
|
||||
MenuId_Boot_Recent, // Menu populated with recent source bootings
|
||||
MenuId_SkipBiosToggle, // enables the Bios Skip speedhack
|
||||
|
||||
|
||||
MenuId_Sys_SuspendResume, // suspends/resumes active emulation, retains plugin states
|
||||
MenuId_Sys_Close, // Closes the emulator (states are preserved)
|
||||
MenuId_Sys_Reset, // Issues a complete VM reset (wipes preserved states)
|
||||
MenuId_Sys_Restart, // Issues a complete VM reset (wipes preserved states)
|
||||
MenuId_Sys_Shutdown, // Closes virtual machine, shuts down plugins, wipes states.
|
||||
MenuId_Sys_LoadStates, // Opens load states submenu
|
||||
MenuId_Sys_SaveStates, // Opens save states submenu
|
||||
|
@ -284,7 +276,7 @@ struct AppImageIds
|
|||
{
|
||||
Paths = Plugins =
|
||||
Speedhacks = Gamefixes =
|
||||
Video = Cpu =
|
||||
Video = Cpu =
|
||||
MemoryCard = -1;
|
||||
}
|
||||
} Config;
|
||||
|
@ -384,7 +376,7 @@ protected:
|
|||
public:
|
||||
void AddListener( IEventListener_Plugins& listener )
|
||||
{
|
||||
m_evtsrc_CorePluginStatus.Add( listener );
|
||||
m_evtsrc_CorePluginStatus.Add( listener );
|
||||
}
|
||||
|
||||
void AddListener( IEventListener_CoreThread& listener )
|
||||
|
@ -399,7 +391,7 @@ public:
|
|||
|
||||
void RemoveListener( IEventListener_Plugins& listener )
|
||||
{
|
||||
m_evtsrc_CorePluginStatus.Remove( listener );
|
||||
m_evtsrc_CorePluginStatus.Remove( listener );
|
||||
}
|
||||
|
||||
void RemoveListener( IEventListener_CoreThread& listener )
|
||||
|
@ -414,7 +406,7 @@ public:
|
|||
|
||||
void AddListener( IEventListener_Plugins* listener )
|
||||
{
|
||||
m_evtsrc_CorePluginStatus.Add( listener );
|
||||
m_evtsrc_CorePluginStatus.Add( listener );
|
||||
}
|
||||
|
||||
void AddListener( IEventListener_CoreThread* listener )
|
||||
|
@ -429,7 +421,7 @@ public:
|
|||
|
||||
void RemoveListener( IEventListener_Plugins* listener )
|
||||
{
|
||||
m_evtsrc_CorePluginStatus.Remove( listener );
|
||||
m_evtsrc_CorePluginStatus.Remove( listener );
|
||||
}
|
||||
|
||||
void RemoveListener( IEventListener_CoreThread* listener )
|
||||
|
@ -441,27 +433,17 @@ public:
|
|||
{
|
||||
m_evtsrc_AppStatus.Remove( listener );
|
||||
}
|
||||
|
||||
void DispatchEvent( PluginEventType evt )
|
||||
{
|
||||
if( !AffinityAssert_AllowFromMain() ) return;
|
||||
m_evtsrc_CorePluginStatus.Dispatch( evt );
|
||||
}
|
||||
|
||||
void DispatchEvent( AppEventType evt )
|
||||
{
|
||||
if( !AffinityAssert_AllowFromMain() ) return;
|
||||
m_evtsrc_AppStatus.Dispatch( AppEventInfo( evt ) );
|
||||
}
|
||||
|
||||
void DispatchEvent( IniInterface& ini )
|
||||
{
|
||||
if( !AffinityAssert_AllowFromMain() ) return;
|
||||
m_evtsrc_AppStatus.Dispatch( AppSettingsEventInfo( ini ) );
|
||||
}
|
||||
|
||||
void DispatchEvent( PluginEventType evt );
|
||||
void DispatchEvent( AppEventType evt );
|
||||
void DispatchEvent( CoreThreadStatus evt );
|
||||
void DispatchEvent( IniInterface& ini );
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
protected:
|
||||
int m_PendingSaves;
|
||||
bool m_ScheduledTermination;
|
||||
|
||||
public:
|
||||
FramerateManager FpsManager;
|
||||
CommandDictionary GlobalCommands;
|
||||
|
@ -474,9 +456,13 @@ protected:
|
|||
ScopedPtr<RecentIsoList> m_RecentIsoList;
|
||||
ScopedPtr<pxAppResources> m_Resources;
|
||||
|
||||
// Executor Thread for complex VM/System tasks. This thread is used to execute such tasks
|
||||
// in parallel to the main message pump, to allow the main pump to run without fear of
|
||||
// blocked threads stalling the GUI.
|
||||
|
||||
public:
|
||||
ExecutorThread SysExecutorThread;
|
||||
ScopedPtr<SysCoreAllocations> m_CoreAllocs;
|
||||
ScopedPtr<PluginManager> m_CorePlugins;
|
||||
|
||||
protected:
|
||||
wxWindowID m_id_MainFrame;
|
||||
|
@ -489,17 +475,13 @@ public:
|
|||
Pcsx2App();
|
||||
virtual ~Pcsx2App();
|
||||
|
||||
void PostPluginStatus( PluginEventType pevt );
|
||||
void PostMenuAction( MenuIdentifiers menu_id ) const;
|
||||
int IssueDialogAsModal( const wxString& dlgName );
|
||||
void PostMethod( FnPtr_AppMethod method );
|
||||
void PostIdleMethod( FnPtr_AppMethod method );
|
||||
int DoStuckThread( PersistentThread& stuck_thread );
|
||||
void PostAppMethod( FnPtr_Pcsx2App method );
|
||||
void PostIdleAppMethod( FnPtr_Pcsx2App method );
|
||||
|
||||
void SysExecute();
|
||||
void SysExecute( CDVD_SourceType cdvdsrc, const wxString& elf_override=wxEmptyString );
|
||||
void SysReset();
|
||||
void ReloadPlugins();
|
||||
void SysShutdown();
|
||||
void LogicalVsync();
|
||||
|
||||
GSFrame& GetGsFrame() const;
|
||||
|
@ -528,6 +510,8 @@ public:
|
|||
void WipeUserModeSettings();
|
||||
void ReadUserModeSettings();
|
||||
|
||||
void StartPendingSave();
|
||||
void ClearPendingSave();
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// App-wide Resources
|
||||
|
@ -576,8 +560,8 @@ public:
|
|||
void OnProgramLogClosed( wxWindowID id );
|
||||
|
||||
protected:
|
||||
bool InvokeMethodOnMainThread( FnPtr_AppMethod method );
|
||||
bool PostMethodToMainThread( FnPtr_AppMethod method );
|
||||
bool InvokeOnMainThread( FnPtr_Pcsx2App method );
|
||||
bool PostAppMethodMyself( FnPtr_Pcsx2App method );
|
||||
|
||||
void AllocateCoreStuffs();
|
||||
void InitDefaultGlobalAccelerators();
|
||||
|
@ -586,23 +570,13 @@ protected:
|
|||
void CleanupOnExit();
|
||||
void OpenWizardConsole();
|
||||
void PadKeyDispatch( const keyEvent& ev );
|
||||
void CancelLoadingPlugins();
|
||||
|
||||
|
||||
void HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent& event) const;
|
||||
void HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent& event);
|
||||
|
||||
void OnSysExecute( wxCommandEvent& evt );
|
||||
void OnLoadPluginsComplete( wxCommandEvent& evt );
|
||||
void OnPluginStatus( wxCommandEvent& evt );
|
||||
void OnCoreThreadStatus( wxCommandEvent& evt );
|
||||
void OnFreezeThreadFinished( wxCommandEvent& evt );
|
||||
|
||||
void OnOpenModalDialog( wxCommandEvent& evt );
|
||||
void OnOpenDialog_StuckThread( wxCommandEvent& evt );
|
||||
|
||||
void OnEmuKeyDown( wxKeyEvent& evt );
|
||||
|
||||
void OnInvokeMethod( pxInvokeAppMethodEvent& evt );
|
||||
void OnSysExecutorTaskTimeout( wxTimerEvent& evt );
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Override wx default exception handling behavior
|
||||
|
@ -620,40 +594,6 @@ protected:
|
|||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// AppCoreThread class
|
||||
// --------------------------------------------------------------------------------------
|
||||
class AppCoreThread : public SysCoreThread
|
||||
{
|
||||
typedef SysCoreThread _parent;
|
||||
|
||||
public:
|
||||
AppCoreThread();
|
||||
virtual ~AppCoreThread() throw();
|
||||
|
||||
virtual bool Suspend( bool isBlocking=true );
|
||||
virtual void Resume();
|
||||
virtual void Reset();
|
||||
virtual void Cancel( bool isBlocking=true );
|
||||
virtual void StateCheckInThread();
|
||||
virtual void ApplySettings( const Pcsx2Config& src );
|
||||
virtual void ChangeCdvdSource( CDVD_SourceType type );
|
||||
|
||||
protected:
|
||||
virtual void OnResumeReady();
|
||||
virtual void OnResumeInThread( bool IsSuspended );
|
||||
virtual void OnSuspendInThread();
|
||||
virtual void OnCleanupInThread();
|
||||
//virtual void VsyncInThread();
|
||||
virtual void PostVsyncToUI();
|
||||
virtual void ExecuteTaskInThread();
|
||||
virtual void DoCpuReset();
|
||||
|
||||
virtual void DoThreadDeadlocked();
|
||||
|
||||
virtual void CpuInitializeMess();
|
||||
};
|
||||
|
||||
DECLARE_APP(Pcsx2App)
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -686,9 +626,6 @@ DECLARE_APP(Pcsx2App)
|
|||
#define sMainFrame \
|
||||
if( MainEmuFrame* __frame_ = GetMainFramePtr() ) (*__frame_)
|
||||
|
||||
#define sGSFrame \
|
||||
if( GSFrame* __gsframe_ = wxGetApp().GetGsFramePtr() ) (*__gsframe_)
|
||||
|
||||
// Use this within the scope of a wxWindow (wxDialog or wxFrame). If the window has a valid menu
|
||||
// bar, the command will run, otherwise it will be silently ignored. :)
|
||||
#define sMenuBar \
|
||||
|
@ -706,30 +643,6 @@ void AppOpenDialog( wxWindow* parent )
|
|||
(new DialogType( parent ))->Show();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SaveSinglePluginHelper
|
||||
// --------------------------------------------------------------------------------------
|
||||
// A scoped convenience class for closing a single plugin and saving its state to memory.
|
||||
// Emulation is suspended as needed, and is restored when the object leaves scope. Within
|
||||
// the scope of the object, code is free to call plugin re-configurations or even unload
|
||||
// a plugin entirely and re-load a different plugin in its place.
|
||||
//
|
||||
class SaveSinglePluginHelper
|
||||
{
|
||||
protected:
|
||||
SafeArray<u8> m_plugstore;
|
||||
const SafeArray<u8>* m_whereitsat;
|
||||
|
||||
bool m_resume;
|
||||
bool m_validstate;
|
||||
PluginsEnum_t m_pid;
|
||||
|
||||
public:
|
||||
SaveSinglePluginHelper( PluginsEnum_t pid );
|
||||
virtual ~SaveSinglePluginHelper() throw();
|
||||
};
|
||||
|
||||
|
||||
extern pxDoAssertFnType AppDoAssert;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -737,9 +650,10 @@ extern pxDoAssertFnType AppDoAssert;
|
|||
// --------------------------------------------------------------------------------------
|
||||
|
||||
extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest );
|
||||
extern void LoadPluginsPassive( FnType_OnThreadComplete* onComplete );
|
||||
extern void LoadPluginsPassive();
|
||||
extern void LoadPluginsImmediate();
|
||||
extern void UnloadPlugins();
|
||||
extern void ShutdownPlugins();
|
||||
|
||||
extern void AppLoadSettings();
|
||||
extern void AppSaveSettings();
|
||||
|
@ -755,5 +669,22 @@ extern MainEmuFrame* GetMainFramePtr();
|
|||
|
||||
extern __aligned16 AppCoreThread CoreThread;
|
||||
extern __aligned16 SysMtgsThread mtgsThread;
|
||||
extern __aligned16 AppPluginManager CorePlugins;
|
||||
|
||||
|
||||
extern void UI_UpdateSysControls();
|
||||
|
||||
extern void UI_DisableSysActions();
|
||||
extern void UI_EnableSysActions();
|
||||
|
||||
extern void UI_DisableSysReset();
|
||||
extern void UI_DisableSysShutdown();
|
||||
|
||||
|
||||
#define AffinityAssert_AllowFrom_SysExecutor() \
|
||||
pxAssertMsg( wxGetApp().SysExecutorThread.IsSelf(), "Thread affinity violation: Call allowed from SysExecutor thread only." )
|
||||
|
||||
#define AffinityAssert_DisallowFrom_SysExecutor() \
|
||||
pxAssertMsg( !wxGetApp().SysExecutorThread.IsSelf(), "Thread affinity violation: Call is *not* allowed from SysExecutor thread." )
|
||||
|
||||
extern ExecutorThread& GetSysExecutorThread();
|
||||
|
|
|
@ -93,9 +93,9 @@ namespace PathDefs
|
|||
|
||||
// Fetches the path location for user-consumable documents -- stuff users are likely to want to
|
||||
// share with other programs: screenshots, memory cards, and savestates.
|
||||
wxDirName GetDocuments()
|
||||
wxDirName GetDocuments( DocsModeType mode )
|
||||
{
|
||||
switch( DocsFolderMode )
|
||||
switch( mode )
|
||||
{
|
||||
case DocsFolder_User: return (wxDirName)Path::Combine( wxStandardPaths::Get().GetDocumentsDir(), wxGetApp().GetAppName() );
|
||||
case DocsFolder_CWD: return (wxDirName)wxGetCwd();
|
||||
|
@ -106,6 +106,11 @@ namespace PathDefs
|
|||
|
||||
return wxDirName();
|
||||
}
|
||||
|
||||
wxDirName GetDocuments()
|
||||
{
|
||||
return GetDocuments( DocsFolderMode );
|
||||
}
|
||||
|
||||
wxDirName GetSnapshots()
|
||||
{
|
||||
|
|
|
@ -23,15 +23,25 @@ enum DocsModeType
|
|||
{
|
||||
// uses /home/user or /cwd for the program data
|
||||
DocsFolder_User,
|
||||
|
||||
|
||||
// uses the current working directory for program data
|
||||
DocsFolder_CWD,
|
||||
|
||||
|
||||
// uses a custom location for program data
|
||||
DocsFolder_Custom,
|
||||
};
|
||||
|
||||
extern DocsModeType DocsFolderMode; //
|
||||
namespace PathDefs
|
||||
{
|
||||
// complete pathnames are returned by these functions
|
||||
// For 99% of all code, you should use these.
|
||||
|
||||
extern wxDirName GetDocuments();
|
||||
extern wxDirName GetDocuments( DocsModeType mode );
|
||||
extern wxDirName GetThemes();
|
||||
}
|
||||
|
||||
extern DocsModeType DocsFolderMode; //
|
||||
extern wxDirName SettingsFolder; // dictates where the settings folder comes from, *if* UseDefaultSettingsFolder is FALSE.
|
||||
extern wxDirName CustomDocumentsFolder; // allows the specification of a custom home folder for PCSX2 documents files.
|
||||
extern bool UseDefaultSettingsFolder; // when TRUE, pcsx2 derives the settings folder from the UseAdminMode
|
||||
|
@ -48,7 +58,7 @@ enum AspectRatioType
|
|||
};
|
||||
|
||||
// =====================================================================================================
|
||||
// Pcsx2 Application Configuration.
|
||||
// Pcsx2 Application Configuration.
|
||||
// =====================================================================================================
|
||||
|
||||
class AppConfig
|
||||
|
@ -161,7 +171,7 @@ public:
|
|||
Fixed100 SlomoScalar;
|
||||
|
||||
FramerateOptions();
|
||||
|
||||
|
||||
void LoadSave( IniInterface& conf );
|
||||
void SanityCheck();
|
||||
};
|
||||
|
@ -212,7 +222,7 @@ public:
|
|||
FilenameOptions BaseFilenames;
|
||||
GSWindowOptions GSWindow;
|
||||
FramerateOptions Framerate;
|
||||
|
||||
|
||||
// PCSX2-core emulation options, which are passed to the emu core prior to initiating
|
||||
// an emulation session. Note these are the options saved into the GUI ini file and
|
||||
// which are shown as options in the gui preferences, but *not* necessarily the options
|
||||
|
@ -227,7 +237,7 @@ public:
|
|||
wxString FullpathTo( PluginsEnum_t pluginId ) const;
|
||||
|
||||
bool FullpathMatchTest( PluginsEnum_t pluginId, const wxString& cmpto ) const;
|
||||
|
||||
|
||||
void LoadSaveUserMode( IniInterface& ini, const wxString& cwdhash );
|
||||
|
||||
void LoadSave( IniInterface& ini );
|
||||
|
|
|
@ -0,0 +1,419 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "App.h"
|
||||
#include "AppSaveStates.h"
|
||||
#include "GSFrame.h"
|
||||
|
||||
#include <wx/dir.h>
|
||||
#include <wx/file.h>
|
||||
|
||||
#include "Plugins.h"
|
||||
#include "GS.h"
|
||||
#include "HostGui.h"
|
||||
#include "AppConfig.h"
|
||||
|
||||
using namespace Threading;
|
||||
|
||||
// The GS plugin needs to be opened to save/load the state during plugin configuration, but
|
||||
// the window shouldn't. This blocks it. :)
|
||||
static bool s_DisableGsWindow = false;
|
||||
|
||||
__aligned16 AppPluginManager CorePlugins;
|
||||
|
||||
PluginManager& GetCorePlugins()
|
||||
{
|
||||
return CorePlugins;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// CorePluginsEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
class CorePluginsEvent : public pxInvokeActionEvent
|
||||
{
|
||||
typedef pxInvokeActionEvent _parent;
|
||||
|
||||
protected:
|
||||
PluginEventType m_evt;
|
||||
|
||||
public:
|
||||
virtual ~CorePluginsEvent() throw() {}
|
||||
CorePluginsEvent* Clone() const { return new CorePluginsEvent( *this ); }
|
||||
|
||||
explicit CorePluginsEvent( PluginEventType evt, SynchronousActionState* sema=NULL )
|
||||
: pxInvokeActionEvent( sema )
|
||||
{
|
||||
m_evt = evt;
|
||||
}
|
||||
|
||||
explicit CorePluginsEvent( PluginEventType evt, SynchronousActionState& sema )
|
||||
: pxInvokeActionEvent( sema )
|
||||
{
|
||||
m_evt = evt;
|
||||
}
|
||||
|
||||
CorePluginsEvent( const CorePluginsEvent& src )
|
||||
: pxInvokeActionEvent( src )
|
||||
{
|
||||
m_evt = src.m_evt;
|
||||
}
|
||||
|
||||
void SetEventType( PluginEventType evt ) { m_evt = evt; }
|
||||
PluginEventType GetEventType() { return m_evt; }
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
sApp.DispatchEvent( m_evt );
|
||||
}
|
||||
};
|
||||
|
||||
static void PostPluginStatus( PluginEventType pevt )
|
||||
{
|
||||
sApp.PostAction( CorePluginsEvent( pevt ) );
|
||||
}
|
||||
|
||||
static void ConvertPluginFilenames( wxString (&passins)[PluginId_Count] )
|
||||
{
|
||||
const PluginInfo* pi = tbl_PluginInfo; do
|
||||
{
|
||||
passins[pi->id] = OverrideOptions.Filenames[pi->id].GetFullPath();
|
||||
|
||||
if( passins[pi->id].IsEmpty() || !wxFileExists( passins[pi->id] ) )
|
||||
passins[pi->id] = g_Conf->FullpathTo( pi->id );
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// AppPluginManager
|
||||
// --------------------------------------------------------------------------------------
|
||||
AppPluginManager::AppPluginManager()
|
||||
{
|
||||
}
|
||||
|
||||
AppPluginManager::~AppPluginManager() throw()
|
||||
{
|
||||
}
|
||||
|
||||
void AppPluginManager::Load( const wxString (&folders)[PluginId_Count] )
|
||||
{
|
||||
if( !pxAssert(!AreLoaded()) ) return;
|
||||
|
||||
SetSettingsFolder( GetSettingsFolder().ToString() );
|
||||
_parent::Load( folders );
|
||||
PostPluginStatus( CorePlugins_Loaded );
|
||||
}
|
||||
|
||||
void AppPluginManager::Unload()
|
||||
{
|
||||
_parent::Unload();
|
||||
PostPluginStatus( CorePlugins_Unloaded );
|
||||
}
|
||||
|
||||
void AppPluginManager::Init()
|
||||
{
|
||||
SetSettingsFolder( GetSettingsFolder().ToString() );
|
||||
_parent::Init();
|
||||
PostPluginStatus( CorePlugins_Init );
|
||||
}
|
||||
|
||||
void AppPluginManager::Shutdown()
|
||||
{
|
||||
_parent::Shutdown();
|
||||
PostPluginStatus( CorePlugins_Shutdown );
|
||||
}
|
||||
|
||||
typedef void (AppPluginManager::*FnPtr_AppPluginManager)();
|
||||
|
||||
class SysExecEvent_AppPluginManager : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
FnPtr_AppPluginManager m_method;
|
||||
|
||||
public:
|
||||
virtual ~SysExecEvent_AppPluginManager() throw() {}
|
||||
SysExecEvent_AppPluginManager* Clone() const { return new SysExecEvent_AppPluginManager( *this ); }
|
||||
|
||||
SysExecEvent_AppPluginManager( FnPtr_AppPluginManager method )
|
||||
{
|
||||
m_method = method;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
if( m_method ) (CorePlugins.*m_method)();
|
||||
}
|
||||
};
|
||||
|
||||
void AppPluginManager::Close()
|
||||
{
|
||||
AffinityAssert_AllowFrom_CoreThread();
|
||||
|
||||
/*if( !GetSysExecutorThread().IsSelf() )
|
||||
{
|
||||
GetSysExecutorThread().ProcessEvent( new SysExecEvent_AppPluginManager( &AppPluginManager::Close ) );
|
||||
return;
|
||||
}*/
|
||||
|
||||
if( !NeedsClose() ) return;
|
||||
|
||||
PostPluginStatus( CorePlugins_Closing );
|
||||
_parent::Close();
|
||||
PostPluginStatus( CorePlugins_Closed );
|
||||
}
|
||||
|
||||
void AppPluginManager::Open()
|
||||
{
|
||||
AffinityAssert_AllowFrom_CoreThread();
|
||||
|
||||
/*if( !GetSysExecutorThread().IsSelf() )
|
||||
{
|
||||
GetSysExecutorThread().ProcessEvent( new SysExecEvent_AppPluginManager( &AppPluginManager::Open ) );
|
||||
return;
|
||||
}*/
|
||||
|
||||
SetSettingsFolder( GetSettingsFolder().ToString() );
|
||||
|
||||
if( !NeedsOpen() ) return;
|
||||
|
||||
PostPluginStatus( CorePlugins_Opening );
|
||||
_parent::Open();
|
||||
PostPluginStatus( CorePlugins_Opened );
|
||||
}
|
||||
|
||||
// Yay, this plugin is guaranteed to always be opened first and closed last.
|
||||
bool AppPluginManager::OpenPlugin_GS()
|
||||
{
|
||||
if( GSopen2 && !s_DisableGsWindow )
|
||||
{
|
||||
sApp.OpenGsPanel();
|
||||
}
|
||||
|
||||
bool retval = _parent::OpenPlugin_GS();
|
||||
|
||||
if( g_LimiterMode == Limit_Turbo )
|
||||
GSsetVsync( false );
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int _guard = 0;
|
||||
|
||||
// Yay, this plugin is guaranteed to always be opened first and closed last.
|
||||
void AppPluginManager::ClosePlugin_GS()
|
||||
{
|
||||
/*if( GSopen2 == NULL || CloseViewportWithPlugins )
|
||||
{
|
||||
// All other plugins must be closed before the GS, because they all rely on
|
||||
// the GS window handle being valid. The recursion guard will protect this
|
||||
// function from being called a million times. ;)
|
||||
|
||||
RecursionGuard mess( _guard );
|
||||
if( !mess.IsReentrant() ) Close();
|
||||
}*/
|
||||
|
||||
_parent::ClosePlugin_GS();
|
||||
if( GetMTGS().IsSelf() && GSopen2 && CloseViewportWithPlugins ) sApp.CloseGsPanel();
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// LoadCorePluginsEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
class LoadCorePluginsEvent : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
wxString m_folders[PluginId_Count];
|
||||
|
||||
public:
|
||||
LoadCorePluginsEvent()
|
||||
{
|
||||
ConvertPluginFilenames( m_folders );
|
||||
}
|
||||
|
||||
wxString GetEventName() const
|
||||
{
|
||||
return L"LoadCorePlugins";
|
||||
}
|
||||
|
||||
wxString GetEventMessage() const
|
||||
{
|
||||
return _("Loading PS2 system plugins...");
|
||||
}
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
CorePlugins.Load( m_folders );
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Public API / Interface
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest )
|
||||
{
|
||||
ScopedPtr<wxArrayString> placebo;
|
||||
wxArrayString* realdest = dest;
|
||||
if( realdest == NULL )
|
||||
placebo = realdest = new wxArrayString(); // placebo is our /dev/null -- gets deleted when done
|
||||
|
||||
#ifdef __WXMSW__
|
||||
// Windows pretty well has a strict "must end in .dll" rule.
|
||||
wxString pattern( L"*%s" );
|
||||
#else
|
||||
// Other platforms seem to like to version their libs after the .so extension:
|
||||
// blah.so.3.1.fail?
|
||||
wxString pattern( L"*%s*" );
|
||||
#endif
|
||||
|
||||
return searchpath.Exists() ?
|
||||
wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( pattern, wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0;
|
||||
}
|
||||
|
||||
// Posts a message to the App to reload plugins. Plugins are loaded via a background thread
|
||||
// which is started on a pending event, so don't expect them to be ready "right now."
|
||||
// If plugins are already loaded, onComplete is invoked, and the function returns with no
|
||||
// other actions performed.
|
||||
void LoadPluginsPassive()
|
||||
{
|
||||
AffinityAssert_AllowFrom_MainUI();
|
||||
|
||||
// Plugins already loaded?
|
||||
if( !CorePlugins.AreLoaded() )
|
||||
{
|
||||
wxGetApp().SysExecutorThread.PostEvent( new LoadCorePluginsEvent() );
|
||||
}
|
||||
}
|
||||
|
||||
static void _LoadPluginsImmediate()
|
||||
{
|
||||
if( CorePlugins.AreLoaded() ) return;
|
||||
|
||||
wxString passins[PluginId_Count];
|
||||
ConvertPluginFilenames( passins );
|
||||
CorePlugins.Load( passins );
|
||||
}
|
||||
|
||||
void LoadPluginsImmediate()
|
||||
{
|
||||
AffinityAssert_AllowFrom_SysExecutor();
|
||||
_LoadPluginsImmediate();
|
||||
}
|
||||
|
||||
// Performs a blocking load of plugins. If the emulation thread is active, it is shut down
|
||||
// automatically to prevent race conditions (it depends on plugins).
|
||||
//
|
||||
// Exceptions regarding plugin failures will propagate out of this function, so be prepared
|
||||
// to handle them.
|
||||
//
|
||||
// Note that this is not recommended for most situations, but coding improper passive loads
|
||||
// is probably worse, so if in doubt use this and air will fix it up for you later. :)
|
||||
//
|
||||
void ScopedCoreThreadClose::LoadPlugins()
|
||||
{
|
||||
DbgCon.WriteLn("(ScopedCoreThreadClose) Loading plugins!");
|
||||
_LoadPluginsImmediate();
|
||||
}
|
||||
|
||||
|
||||
class SysExecEvent_UnloadPlugins : public SysExecEvent
|
||||
{
|
||||
public:
|
||||
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()
|
||||
{
|
||||
CoreThread.Cancel();
|
||||
CorePlugins.Unload();
|
||||
}
|
||||
};
|
||||
|
||||
class SysExecEvent_ShutdownPlugins : public SysExecEvent
|
||||
{
|
||||
public:
|
||||
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()
|
||||
{
|
||||
CoreThread.Cancel();
|
||||
CorePlugins.Shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
void UnloadPlugins()
|
||||
{
|
||||
GetSysExecutorThread().PostEvent( new SysExecEvent_UnloadPlugins() );
|
||||
}
|
||||
|
||||
void ShutdownPlugins()
|
||||
{
|
||||
GetSysExecutorThread().PostEvent( new SysExecEvent_ShutdownPlugins() );
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SaveSinglePluginHelper (Implementations)
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
SaveSinglePluginHelper::SaveSinglePluginHelper( PluginsEnum_t pid )
|
||||
: m_plugstore( L"PluginConf Savestate" )
|
||||
{
|
||||
s_DisableGsWindow = true;
|
||||
|
||||
m_pid = pid;
|
||||
m_validstate = SysHasValidState();
|
||||
|
||||
_LoadPluginsImmediate();
|
||||
if( !CorePlugins.AreLoaded() ) return;
|
||||
|
||||
if( !m_validstate ) return;
|
||||
Console.WriteLn( Color_Green, L"Suspending single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
|
||||
|
||||
memSavingState save( m_plugstore );
|
||||
GetCorePlugins().Freeze( m_pid, save );
|
||||
GetCorePlugins().Close( pid );
|
||||
}
|
||||
|
||||
SaveSinglePluginHelper::~SaveSinglePluginHelper() throw()
|
||||
{
|
||||
try
|
||||
{
|
||||
if( m_validstate )
|
||||
{
|
||||
Console.WriteLn( Color_Green, L"Recovering single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
|
||||
memLoadingState load( m_plugstore );
|
||||
//if( m_plugstore.IsDisposed() ) load.SeekToSection( m_pid );
|
||||
GetCorePlugins().Freeze( m_pid, load );
|
||||
GetCorePlugins().Close( m_pid );
|
||||
}
|
||||
|
||||
s_DisableGsWindow = false;
|
||||
}
|
||||
DESTRUCTOR_CATCHALL;
|
||||
|
||||
s_DisableGsWindow = false;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/* 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"
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// AppPluginManager
|
||||
// --------------------------------------------------------------------------------------
|
||||
// This extension of PluginManager provides event listener sources for plugins -- loading,
|
||||
// unloading, open, close, shutdown, etc.
|
||||
//
|
||||
// FIXME : Should this be made part of the PCSX2 core emulation? (integrated into PluginManager)
|
||||
// I'm undecided on if it makes sense more in that context or in this one (interface).
|
||||
//
|
||||
class AppPluginManager : public PluginManager
|
||||
{
|
||||
typedef PluginManager _parent;
|
||||
|
||||
public:
|
||||
AppPluginManager();
|
||||
virtual ~AppPluginManager() throw();
|
||||
|
||||
void Load( const wxString (&folders)[PluginId_Count] );
|
||||
void Unload();
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
void Close();
|
||||
void Open();
|
||||
|
||||
protected:
|
||||
bool OpenPlugin_GS();
|
||||
void ClosePlugin_GS();
|
||||
};
|
|
@ -14,78 +14,104 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "MainFrame.h"
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "App.h"
|
||||
#include "AppSaveStates.h"
|
||||
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "GS.h"
|
||||
|
||||
__aligned16 SysMtgsThread mtgsThread;
|
||||
__aligned16 AppCoreThread CoreThread;
|
||||
|
||||
static void PostCoreStatus( CoreThreadStatus pevt )
|
||||
{
|
||||
sApp.PostAction( CoreThreadStatusEvent( pevt ) );
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// AppCoreThread Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
AppCoreThread::AppCoreThread() : SysCoreThread()
|
||||
{
|
||||
}
|
||||
|
||||
AppCoreThread::~AppCoreThread() throw()
|
||||
{
|
||||
AppCoreThread::Cancel();
|
||||
_parent::Cancel(); // use parent's, skips thread affinity check.
|
||||
}
|
||||
|
||||
void AppCoreThread::Cancel( bool isBlocking )
|
||||
{
|
||||
if( !_parent::Cancel( wxTimeSpan( 0, 0, 2, 0 ) ) )
|
||||
{
|
||||
// Possible deadlock!
|
||||
throw Exception::ThreadDeadlock( this );
|
||||
}
|
||||
AffinityAssert_AllowFrom_SysExecutor();
|
||||
_parent::Cancel( wxTimeSpan(0, 0, 2, 0) );
|
||||
}
|
||||
|
||||
void AppCoreThread::Reset()
|
||||
void AppCoreThread::Shutdown()
|
||||
{
|
||||
ScopedBusyCursor::SetDefault( Cursor_KindaBusy );
|
||||
AffinityAssert_AllowFrom_SysExecutor();
|
||||
_parent::Reset();
|
||||
CorePlugins.Shutdown();
|
||||
}
|
||||
|
||||
void AppCoreThread::DoThreadDeadlocked()
|
||||
ExecutorThread& GetSysExecutorThread()
|
||||
{
|
||||
wxGetApp().DoStuckThread( *this );
|
||||
return wxGetApp().SysExecutorThread;
|
||||
}
|
||||
|
||||
bool AppCoreThread::Suspend( bool isBlocking )
|
||||
typedef void (AppCoreThread::*FnPtr_CoreThreadMethod)();
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent_InvokeCoreThreadMethod
|
||||
// --------------------------------------------------------------------------------------
|
||||
class SysExecEvent_InvokeCoreThreadMethod : public SysExecEvent
|
||||
{
|
||||
ScopedBusyCursor::SetDefault( Cursor_KindaBusy );
|
||||
protected:
|
||||
FnPtr_CoreThreadMethod m_method;
|
||||
|
||||
bool retval = _parent::Suspend( false );
|
||||
|
||||
if( !retval || isBlocking )
|
||||
ScopedBusyCursor::SetDefault( Cursor_NotBusy );
|
||||
|
||||
if( g_Conf->GSWindow.CloseOnEsc )
|
||||
public:
|
||||
virtual ~SysExecEvent_InvokeCoreThreadMethod() throw() {}
|
||||
SysExecEvent_InvokeCoreThreadMethod* Clone() const { return new SysExecEvent_InvokeCoreThreadMethod(*this); }
|
||||
|
||||
SysExecEvent_InvokeCoreThreadMethod( FnPtr_CoreThreadMethod method )
|
||||
{
|
||||
sGSFrame.Hide();
|
||||
m_method = method;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
if( m_method ) (CoreThread.*m_method)();
|
||||
}
|
||||
};
|
||||
|
||||
return retval;
|
||||
bool ProcessingMethodViaThread( FnPtr_CoreThreadMethod method )
|
||||
{
|
||||
if( GetSysExecutorThread().IsSelf() ) return false;
|
||||
SysExecEvent_InvokeCoreThreadMethod evt( method );
|
||||
GetSysExecutorThread().ProcessEvent( evt );
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _Suspend()
|
||||
{
|
||||
GetCoreThread().Suspend(true);
|
||||
}
|
||||
|
||||
void AppCoreThread::Suspend( bool isBlocking )
|
||||
{
|
||||
if( !GetSysExecutorThread().SelfProcessMethod( _Suspend ) )
|
||||
_parent::Suspend(true);
|
||||
}
|
||||
|
||||
static int resume_tries = 0;
|
||||
|
||||
void AppCoreThread::Resume()
|
||||
{
|
||||
// Thread control (suspend / resume) should only be performed from the main/gui thread.
|
||||
if( !AffinityAssert_AllowFromMain() ) return;
|
||||
if( m_ExecMode == ExecMode_Opened ) return;
|
||||
if( m_ResumeProtection.IsLocked() ) return;
|
||||
if( !AffinityAssert_AllowFrom_SysExecutor() ) return;
|
||||
if( m_ExecMode == ExecMode_Opened || (m_CloseTemporary > 0) ) return;
|
||||
|
||||
if( !pxAssert( g_plugins != NULL ) ) return;
|
||||
if( !pxAssert( CorePlugins.AreLoaded() ) ) return;
|
||||
|
||||
if( sys_resume_lock > 0 )
|
||||
{
|
||||
Console.WriteLn( "SysResume: State is locked, ignoring Resume request!" );
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedBusyCursor::SetDefault( Cursor_KindaBusy );
|
||||
_parent::Resume();
|
||||
|
||||
if( m_ExecMode != ExecMode_Opened )
|
||||
|
@ -93,7 +119,7 @@ void AppCoreThread::Resume()
|
|||
// Resume failed for some reason, so update GUI statuses and post a message to
|
||||
// try again on the resume.
|
||||
|
||||
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Suspended );
|
||||
PostCoreStatus( CoreThread_Suspended );
|
||||
|
||||
if( (m_ExecMode != ExecMode_Closing) || (m_ExecMode != ExecMode_Pausing) )
|
||||
{
|
||||
|
@ -109,29 +135,30 @@ void AppCoreThread::Resume()
|
|||
resume_tries = 0;
|
||||
}
|
||||
|
||||
void AppCoreThread::ChangeCdvdSource( CDVD_SourceType type )
|
||||
void AppCoreThread::ChangeCdvdSource()
|
||||
{
|
||||
g_Conf->CdvdSource = type;
|
||||
_parent::ChangeCdvdSource( type );
|
||||
sMainFrame.UpdateIsoSrcSelection();
|
||||
if( !GetSysExecutorThread().IsSelf() )
|
||||
{
|
||||
GetSysExecutorThread().PostEvent( new SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::ChangeCdvdSource) );
|
||||
return;
|
||||
}
|
||||
|
||||
CDVD_SourceType cdvdsrc( g_Conf->CdvdSource );
|
||||
if( cdvdsrc == CDVDsys_GetSourceType() ) return;
|
||||
|
||||
// Fast change of the CDVD source only -- a Pause will suffice.
|
||||
|
||||
ScopedCoreThreadPause paused_core;
|
||||
GetCorePlugins().Close( PluginId_CDVD );
|
||||
CDVDsys_ChangeSource( cdvdsrc );
|
||||
paused_core.AllowResume();
|
||||
|
||||
// TODO: Add a listener for CDVDsource changes? Or should we bother?
|
||||
}
|
||||
|
||||
void AppCoreThread::DoCpuReset()
|
||||
{
|
||||
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Reset );
|
||||
_parent::DoCpuReset();
|
||||
}
|
||||
|
||||
void AppCoreThread::OnResumeReady()
|
||||
{
|
||||
ApplySettings( g_Conf->EmuOptions );
|
||||
|
||||
if( !wxFile::Exists( g_Conf->CurrentIso ) )
|
||||
g_Conf->CurrentIso.Clear();
|
||||
|
||||
sApp.GetRecentIsoManager().Add( g_Conf->CurrentIso );
|
||||
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
|
||||
|
||||
AppSaveSettings();
|
||||
|
@ -139,45 +166,8 @@ void AppCoreThread::OnResumeReady()
|
|||
_parent::OnResumeReady();
|
||||
}
|
||||
|
||||
void AppCoreThread::OnResumeInThread( bool isSuspended )
|
||||
{
|
||||
_parent::OnResumeInThread( isSuspended );
|
||||
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Resumed );
|
||||
}
|
||||
|
||||
void AppCoreThread::OnSuspendInThread()
|
||||
{
|
||||
_parent::OnSuspendInThread();
|
||||
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Suspended );
|
||||
}
|
||||
|
||||
// Called whenever the thread has terminated, for either regular or irregular reasons.
|
||||
// Typically the thread handles all its own errors, so there's no need to have error
|
||||
// handling here. However it's a good idea to update the status of the GUI to reflect
|
||||
// the new (lack of) thread status, so this posts a message to the App to do so.
|
||||
void AppCoreThread::OnCleanupInThread()
|
||||
{
|
||||
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Stopped );
|
||||
_parent::OnCleanupInThread();
|
||||
}
|
||||
|
||||
void AppCoreThread::PostVsyncToUI()
|
||||
{
|
||||
wxGetApp().LogicalVsync();
|
||||
}
|
||||
|
||||
void AppCoreThread::StateCheckInThread()
|
||||
{
|
||||
_parent::StateCheckInThread();
|
||||
}
|
||||
|
||||
// To simplify settings application rules and re-entry conditions, the main App's implementation
|
||||
// of ApplySettings requires that the caller manually ensure that the thread has been properly
|
||||
// suspended. If the thread has not been suspended, this call will fail *silently*.
|
||||
void AppCoreThread::ApplySettings( const Pcsx2Config& src )
|
||||
{
|
||||
//if( m_ExecMode != ExecMode_Closed ) return;
|
||||
|
||||
Pcsx2Config fixup( src );
|
||||
if( !g_Conf->EnableSpeedHacks )
|
||||
fixup.Speedhacks = Pcsx2Config::SpeedhackOptions();
|
||||
|
@ -192,9 +182,63 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src )
|
|||
RecursionGuard guard( localc );
|
||||
if( guard.IsReentrant() ) return;
|
||||
if( fixup == EmuConfig ) return;
|
||||
_parent::ApplySettings( fixup );
|
||||
|
||||
if( m_ExecMode >= ExecMode_Opened )
|
||||
{
|
||||
ScopedCoreThreadPause paused_core;
|
||||
_parent::ApplySettings( fixup );
|
||||
paused_core.AllowResume();
|
||||
}
|
||||
else
|
||||
{
|
||||
_parent::ApplySettings( fixup );
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// AppCoreThread *Worker* Implementations
|
||||
// (Called from the context of this thread only)
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
void AppCoreThread::DoCpuReset()
|
||||
{
|
||||
PostCoreStatus( CoreThread_Reset );
|
||||
_parent::DoCpuReset();
|
||||
}
|
||||
|
||||
void AppCoreThread::OnResumeInThread( bool isSuspended )
|
||||
{
|
||||
_parent::OnResumeInThread( isSuspended );
|
||||
PostCoreStatus( CoreThread_Resumed );
|
||||
}
|
||||
|
||||
void AppCoreThread::OnSuspendInThread()
|
||||
{
|
||||
_parent::OnSuspendInThread();
|
||||
PostCoreStatus( CoreThread_Suspended );
|
||||
}
|
||||
|
||||
// Called whenever the thread has terminated, for either regular or irregular reasons.
|
||||
// Typically the thread handles all its own errors, so there's no need to have error
|
||||
// handling here. However it's a good idea to update the status of the GUI to reflect
|
||||
// the new (lack of) thread status, so this posts a message to the App to do so.
|
||||
void AppCoreThread::OnCleanupInThread()
|
||||
{
|
||||
PostCoreStatus( CoreThread_Stopped );
|
||||
_parent::OnCleanupInThread();
|
||||
}
|
||||
|
||||
void AppCoreThread::PostVsyncToUI()
|
||||
{
|
||||
wxGetApp().LogicalVsync();
|
||||
}
|
||||
|
||||
void AppCoreThread::StateCheckInThread()
|
||||
{
|
||||
_parent::StateCheckInThread();
|
||||
}
|
||||
|
||||
// Thread Affinity: This function is called from the SysCoreThread. :)
|
||||
void AppCoreThread::CpuInitializeMess()
|
||||
{
|
||||
if( m_hasValidState ) return;
|
||||
|
@ -205,33 +249,278 @@ void AppCoreThread::CpuInitializeMess()
|
|||
// in order to ensure the plugins are in the proper (loaded/opened) state.
|
||||
|
||||
SysClearExecutionCache();
|
||||
StateCopy_ThawFromMem_Blocking();
|
||||
memLoadingState( StateCopy_GetBuffer() ).FreezeAll();
|
||||
StateCopy_Clear();
|
||||
|
||||
m_hasValidState = true;
|
||||
m_resetVirtualMachine = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_parent::CpuInitializeMess();
|
||||
}
|
||||
|
||||
|
||||
void AppCoreThread::ExecuteTaskInThread()
|
||||
{
|
||||
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Started );
|
||||
PostCoreStatus( CoreThread_Started );
|
||||
_parent::ExecuteTaskInThread();
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/*catch( Exception::PluginError& ex )
|
||||
{
|
||||
if( g_plugins != NULL ) g_plugins->Close();
|
||||
Console.Error( ex.FormatDiagnosticMessage() );
|
||||
Msgbox::Alert( ex.FormatDisplayMessage(), _("Plugin Open Error") );
|
||||
|
||||
if( HandlePluginError( ex ) )
|
||||
{
|
||||
// fixme: automatically re-try emu startup here...
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
FullStop_BlockingResume
|
||||
, FullStop_NonblockingResume
|
||||
, FullStop_SkipResume
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent_FullStop
|
||||
// --------------------------------------------------------------------------------------
|
||||
class SysExecEvent_FullStop : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
SynchronousActionState* m_resume;
|
||||
Threading::Mutex* m_mtx_resume;
|
||||
|
||||
public:
|
||||
virtual ~SysExecEvent_FullStop() throw() {}
|
||||
SysExecEvent_FullStop* Clone() const
|
||||
{
|
||||
return new SysExecEvent_FullStop( *this );
|
||||
}
|
||||
|
||||
SysExecEvent_FullStop( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL )
|
||||
: SysExecEvent( sync )
|
||||
{
|
||||
m_resume = resume_sync;
|
||||
m_mtx_resume = mtx_resume;
|
||||
}
|
||||
|
||||
SysExecEvent_FullStop( SynchronousActionState& sync, SynchronousActionState& resume_sync, Threading::Mutex& mtx_resume )
|
||||
: SysExecEvent( sync )
|
||||
{
|
||||
m_resume = &resume_sync;
|
||||
m_mtx_resume = &mtx_resume;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
ScopedCoreThreadClose closed_core;
|
||||
PostResult();
|
||||
|
||||
if( m_resume )
|
||||
{
|
||||
ScopedLock lock( m_mtx_resume );
|
||||
|
||||
// If the sender of the message requests a non-blocking resume, then we need
|
||||
// to deallocate the m_sync object, since the sender will likely leave scope and
|
||||
// invalidate it.
|
||||
switch( m_resume->WaitForResult() )
|
||||
{
|
||||
case FullStop_SkipResume: return;
|
||||
|
||||
case FullStop_BlockingResume:
|
||||
if( m_sync ) m_sync->ClearResult();
|
||||
break;
|
||||
|
||||
case FullStop_NonblockingResume:
|
||||
m_sync = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closed_core.AllowResume();
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent_FullStop
|
||||
// --------------------------------------------------------------------------------------
|
||||
class SysExecEvent_Pause : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
SynchronousActionState* m_resume;
|
||||
Threading::Mutex* m_mtx_resume;
|
||||
|
||||
public:
|
||||
virtual ~SysExecEvent_Pause() throw() {}
|
||||
SysExecEvent_Pause* Clone() const
|
||||
{
|
||||
return new SysExecEvent_Pause( *this );
|
||||
}
|
||||
|
||||
SysExecEvent_Pause( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL )
|
||||
: SysExecEvent( sync )
|
||||
{
|
||||
m_resume = resume_sync;
|
||||
m_mtx_resume = mtx_resume;
|
||||
}
|
||||
|
||||
SysExecEvent_Pause( SynchronousActionState& sync, SynchronousActionState& resume_sync, Threading::Mutex& mtx_resume )
|
||||
: SysExecEvent( sync )
|
||||
{
|
||||
m_resume = &resume_sync;
|
||||
m_mtx_resume = &mtx_resume;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
ScopedCoreThreadPause paused_core;
|
||||
PostResult();
|
||||
|
||||
if( m_resume )
|
||||
{
|
||||
ScopedLock lock( m_mtx_resume );
|
||||
|
||||
// If the sender of the message requests a non-blocking resume, then we need
|
||||
// to deallocate the m_sync object, since the sender will likely leave scope and
|
||||
// invalidate it.
|
||||
switch( m_resume->WaitForResult() )
|
||||
{
|
||||
case FullStop_SkipResume: return;
|
||||
|
||||
case FullStop_BlockingResume:
|
||||
if( m_sync ) m_sync->ClearResult();
|
||||
break;
|
||||
|
||||
case FullStop_NonblockingResume:
|
||||
m_sync = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
paused_core.AllowResume();
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ScopedCoreThreadClose / ScopedCoreThreadPause
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
static __threadlocal bool ScopedCore_IsPaused = false;
|
||||
static __threadlocal bool ScopedCore_IsFullyClosed = false;
|
||||
|
||||
BaseScopedCoreThread::BaseScopedCoreThread()
|
||||
{
|
||||
//AffinityAssert_AllowFrom_MainUI();
|
||||
|
||||
m_allowResume = false;
|
||||
m_alreadyStopped = false;
|
||||
m_alreadyScoped = false;
|
||||
}
|
||||
|
||||
BaseScopedCoreThread::~BaseScopedCoreThread() throw()
|
||||
{
|
||||
}
|
||||
|
||||
// Allows the object to resume execution upon object destruction. Typically called as the last thing
|
||||
// in the object's scope. Any code prior to this call that causes exceptions will not resume the emulator,
|
||||
// which is *typically* the intended behavior when errors occur.
|
||||
void BaseScopedCoreThread::AllowResume()
|
||||
{
|
||||
m_allowResume = true;
|
||||
}
|
||||
|
||||
void BaseScopedCoreThread::DisallowResume()
|
||||
{
|
||||
m_allowResume = false;
|
||||
}
|
||||
|
||||
void BaseScopedCoreThread::DoResume()
|
||||
{
|
||||
if( m_alreadyStopped ) return;
|
||||
if( !GetSysExecutorThread().IsSelf() )
|
||||
{
|
||||
//DbgCon.WriteLn("(ScopedCoreThreadPause) Threaded Scope Created!");
|
||||
m_sync_resume.PostResult( m_allowResume ? FullStop_NonblockingResume : FullStop_SkipResume );
|
||||
m_mtx_resume.Wait();
|
||||
}
|
||||
else
|
||||
CoreThread.Resume();
|
||||
}
|
||||
|
||||
|
||||
ScopedCoreThreadClose::ScopedCoreThreadClose()
|
||||
{
|
||||
if( ScopedCore_IsFullyClosed )
|
||||
{
|
||||
// tracks if we're already in scope or not.
|
||||
m_alreadyScoped = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if( !GetSysExecutorThread().IsSelf() )
|
||||
{
|
||||
//DbgCon.WriteLn("(ScopedCoreThreadClose) Threaded Scope Created!");
|
||||
|
||||
GetSysExecutorThread().PostEvent( SysExecEvent_FullStop(m_sync, m_sync_resume, m_mtx_resume) );
|
||||
m_sync.WaitForResult();
|
||||
m_sync.RethrowException();
|
||||
}
|
||||
else if( !(m_alreadyStopped = CoreThread.IsClosed()) )
|
||||
CoreThread.Suspend();
|
||||
|
||||
ScopedCore_IsFullyClosed = true;
|
||||
}
|
||||
|
||||
ScopedCoreThreadClose::~ScopedCoreThreadClose() throw()
|
||||
{
|
||||
if( m_alreadyScoped ) return;
|
||||
_parent::DoResume();
|
||||
ScopedCore_IsFullyClosed = false;
|
||||
}
|
||||
|
||||
ScopedCoreThreadPause::ScopedCoreThreadPause()
|
||||
{
|
||||
if( ScopedCore_IsFullyClosed || ScopedCore_IsPaused )
|
||||
{
|
||||
// tracks if we're already in scope or not.
|
||||
m_alreadyScoped = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if( !GetSysExecutorThread().IsSelf() )
|
||||
{
|
||||
//DbgCon.WriteLn("(ScopedCoreThreadPause) Threaded Scope Created!");
|
||||
|
||||
GetSysExecutorThread().PostEvent( SysExecEvent_Pause(m_sync, m_sync_resume, m_mtx_resume) );
|
||||
m_sync.WaitForResult();
|
||||
m_sync.RethrowException();
|
||||
}
|
||||
else if( !(m_alreadyStopped = CoreThread.IsPaused()) )
|
||||
CoreThread.Pause();
|
||||
|
||||
ScopedCore_IsPaused = true;
|
||||
}
|
||||
|
||||
ScopedCoreThreadPause::~ScopedCoreThreadPause() throw()
|
||||
{
|
||||
if( m_alreadyScoped ) return;
|
||||
_parent::DoResume();
|
||||
ScopedCore_IsPaused = false;
|
||||
}
|
||||
|
||||
ScopedCoreThreadPopup::ScopedCoreThreadPopup()
|
||||
{
|
||||
// The old style GUI (without GSopen2) must use a full close of the CoreThread, in order to
|
||||
// ensure that the GS window isn't blocking the popup, and to avoid crashes if the GS window
|
||||
// is maximized or fullscreen.
|
||||
|
||||
if( !GSopen2 )
|
||||
m_scoped_core = new ScopedCoreThreadClose();
|
||||
else
|
||||
m_scoped_core = new ScopedCoreThreadPause();
|
||||
};
|
||||
|
||||
void ScopedCoreThreadPopup::AllowResume()
|
||||
{
|
||||
if( m_scoped_core ) m_scoped_core->AllowResume();
|
||||
}
|
||||
|
||||
void ScopedCoreThreadPopup::DisallowResume()
|
||||
{
|
||||
if( m_scoped_core ) m_scoped_core->DisallowResume();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/* 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 "SysThreads.h"
|
||||
#include "AppCommon.h"
|
||||
#include "AppCorePlugins.h"
|
||||
|
||||
#define AffinityAssert_AllowFrom_CoreThread() \
|
||||
pxAssertMsg( GetCoreThread().IsSelf(), "Thread affinity violation: Call allowed from SysCoreThread only." )
|
||||
|
||||
#define AffinityAssert_DisallowFrom_CoreThread() \
|
||||
pxAssertMsg( !GetCoreThread().IsSelf(), "Thread affinity violation: Call is *not* allowed from SysCoreThread." )
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// AppCoreThread class
|
||||
// --------------------------------------------------------------------------------------
|
||||
class AppCoreThread : public SysCoreThread
|
||||
{
|
||||
typedef SysCoreThread _parent;
|
||||
|
||||
public:
|
||||
AppCoreThread();
|
||||
virtual ~AppCoreThread() throw();
|
||||
|
||||
virtual void Suspend( bool isBlocking=false );
|
||||
virtual void Resume();
|
||||
virtual void Shutdown();
|
||||
virtual void Cancel( bool isBlocking=true );
|
||||
virtual void StateCheckInThread();
|
||||
virtual void ApplySettings( const Pcsx2Config& src );
|
||||
virtual void ChangeCdvdSource();
|
||||
|
||||
protected:
|
||||
virtual void OnResumeReady();
|
||||
virtual void OnResumeInThread( bool IsSuspended );
|
||||
virtual void OnSuspendInThread();
|
||||
virtual void OnCleanupInThread();
|
||||
virtual void PostVsyncToUI();
|
||||
virtual void ExecuteTaskInThread();
|
||||
virtual void DoCpuReset();
|
||||
virtual void CpuInitializeMess();
|
||||
|
||||
};
|
||||
|
||||
class IScopedCoreThread
|
||||
{
|
||||
protected:
|
||||
IScopedCoreThread() {}
|
||||
|
||||
public:
|
||||
virtual ~IScopedCoreThread() throw() {};
|
||||
virtual void AllowResume()=0;
|
||||
virtual void DisallowResume()=0;
|
||||
};
|
||||
|
||||
|
||||
class BaseScopedCoreThread : public IScopedCoreThread
|
||||
{
|
||||
DeclareNoncopyableObject( BaseScopedCoreThread );
|
||||
|
||||
protected:
|
||||
bool m_allowResume;
|
||||
bool m_alreadyStopped;
|
||||
bool m_alreadyScoped;
|
||||
SynchronousActionState m_sync;
|
||||
SynchronousActionState m_sync_resume;
|
||||
Threading::Mutex m_mtx_resume;
|
||||
|
||||
BaseScopedCoreThread();
|
||||
|
||||
public:
|
||||
virtual ~BaseScopedCoreThread() throw()=0;
|
||||
virtual void AllowResume();
|
||||
virtual void DisallowResume();
|
||||
|
||||
protected:
|
||||
void DoResume();
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ScopedCoreThreadClose / ScopedCoreThreadPause / ScopedCoreThreadPopup
|
||||
// --------------------------------------------------------------------------------------
|
||||
// This class behaves a bit differently from other scoped classes due to the "standard"
|
||||
// assumption that we actually do *not* want to resume CoreThread operations when an
|
||||
// exception occurs. Because of this, the destructor of this class does *not* unroll the
|
||||
// suspend operation. Instead you must manually instruct the class to resume using a call
|
||||
// to the provisioned AllowResume() method.
|
||||
//
|
||||
// If the class leaves scope without having been resumed, a log is written to the console.
|
||||
// This can be useful for troubleshooting, and also allows the log a second line of info
|
||||
// indicating the status of CoreThread execution at the time of the exception.
|
||||
//
|
||||
// ScopedCoreThreadPopup is intended for use where message boxes are popped up to the user.
|
||||
// The old style GUI (without GSopen2) must use a full close of the CoreThread, in order to
|
||||
// ensure that the GS window isn't blocking the popup, and to avoid crashes if the GS window
|
||||
// is maximized or fullscreen.
|
||||
//
|
||||
class ScopedCoreThreadClose : public BaseScopedCoreThread
|
||||
{
|
||||
typedef BaseScopedCoreThread _parent;
|
||||
|
||||
public:
|
||||
ScopedCoreThreadClose();
|
||||
virtual ~ScopedCoreThreadClose() throw();
|
||||
|
||||
void LoadPlugins();
|
||||
};
|
||||
|
||||
struct ScopedCoreThreadPause : public BaseScopedCoreThread
|
||||
{
|
||||
typedef BaseScopedCoreThread _parent;
|
||||
|
||||
public:
|
||||
ScopedCoreThreadPause();
|
||||
virtual ~ScopedCoreThreadPause() throw();
|
||||
};
|
||||
|
||||
struct ScopedCoreThreadPopup : public IScopedCoreThread
|
||||
{
|
||||
protected:
|
||||
ScopedPtr<BaseScopedCoreThread> m_scoped_core;
|
||||
|
||||
public:
|
||||
ScopedCoreThreadPopup();
|
||||
virtual ~ScopedCoreThreadPopup() throw() {}
|
||||
|
||||
virtual void AllowResume();
|
||||
virtual void DisallowResume();
|
||||
};
|
|
@ -16,6 +16,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "Utilities/EventSource.h"
|
||||
#include "Utilities/pxEvents.h"
|
||||
|
||||
enum CoreThreadStatus
|
||||
{
|
||||
|
@ -242,3 +243,28 @@ protected:
|
|||
virtual void AppStatusEvent_OnSettingsApplied() { Owner.AppStatusEvent_OnSettingsApplied(); }
|
||||
virtual void AppStatusEvent_OnExit() { Owner.AppStatusEvent_OnExit(); }
|
||||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// CoreThreadStatusEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
class CoreThreadStatusEvent : public pxInvokeActionEvent
|
||||
{
|
||||
typedef pxInvokeActionEvent _parent;
|
||||
|
||||
protected:
|
||||
CoreThreadStatus m_evt;
|
||||
|
||||
public:
|
||||
virtual ~CoreThreadStatusEvent() throw() {}
|
||||
CoreThreadStatusEvent* Clone() const { return new CoreThreadStatusEvent( *this ); }
|
||||
|
||||
explicit CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousActionState* sema=NULL );
|
||||
explicit CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousActionState& sema );
|
||||
|
||||
void SetEventType( CoreThreadStatus evt ) { m_evt = evt; }
|
||||
CoreThreadStatus GetEventType() { return m_evt; }
|
||||
|
||||
protected:
|
||||
void _DoInvoke();
|
||||
};
|
||||
|
|
|
@ -42,12 +42,14 @@ void IEventListener_CoreThread::DispatchEvent( const CoreThreadStatus& status )
|
|||
{
|
||||
switch( status )
|
||||
{
|
||||
case CoreThread_Indeterminate: break;
|
||||
|
||||
case CoreThread_Started: CoreThread_OnStarted(); break;
|
||||
case CoreThread_Resumed: CoreThread_OnResumed(); break;
|
||||
case CoreThread_Suspended: CoreThread_OnSuspended(); break;
|
||||
case CoreThread_Reset: CoreThread_OnReset(); break;
|
||||
case CoreThread_Stopped: CoreThread_OnStopped(); break;
|
||||
|
||||
|
||||
jNO_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +76,7 @@ void IEventListener_Plugins::DispatchEvent( const PluginEventType& pevt )
|
|||
case CorePlugins_Closed: CorePlugins_OnClosed(); break;
|
||||
case CorePlugins_Shutdown: CorePlugins_OnShutdown(); break;
|
||||
case CorePlugins_Unloaded: CorePlugins_OnUnloaded(); break;
|
||||
|
||||
|
||||
jNO_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
@ -102,3 +104,71 @@ void IEventListener_AppStatus::DispatchEvent( const AppEventInfo& evtinfo )
|
|||
case AppStatus_Exiting: AppStatusEvent_OnExit(); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Pcsx2App::DispatchEvent( PluginEventType evt )
|
||||
{
|
||||
if( !AffinityAssert_AllowFrom_MainUI() ) return;
|
||||
m_evtsrc_CorePluginStatus.Dispatch( evt );
|
||||
}
|
||||
|
||||
void Pcsx2App::DispatchEvent( AppEventType evt )
|
||||
{
|
||||
if( !AffinityAssert_AllowFrom_MainUI() ) return;
|
||||
m_evtsrc_AppStatus.Dispatch( AppEventInfo( evt ) );
|
||||
}
|
||||
|
||||
void Pcsx2App::DispatchEvent( CoreThreadStatus evt )
|
||||
{
|
||||
switch( evt )
|
||||
{
|
||||
case CoreThread_Started:
|
||||
case CoreThread_Reset:
|
||||
case CoreThread_Stopped:
|
||||
FpsManager.Reset();
|
||||
break;
|
||||
|
||||
case CoreThread_Resumed:
|
||||
case CoreThread_Suspended:
|
||||
FpsManager.Resume();
|
||||
break;
|
||||
}
|
||||
|
||||
// Clear the sticky key statuses, because hell knows what'll change while the PAD
|
||||
// plugin is suspended.
|
||||
|
||||
m_kevt.m_shiftDown = false;
|
||||
m_kevt.m_controlDown = false;
|
||||
m_kevt.m_altDown = false;
|
||||
|
||||
m_evtsrc_CoreThreadStatus.Dispatch( evt );
|
||||
ScopedBusyCursor::SetDefault( Cursor_NotBusy );
|
||||
CoreThread.RethrowException();
|
||||
}
|
||||
|
||||
void Pcsx2App::DispatchEvent( IniInterface& ini )
|
||||
{
|
||||
if( !AffinityAssert_AllowFrom_MainUI() ) return;
|
||||
m_evtsrc_AppStatus.Dispatch( AppSettingsEventInfo( ini ) );
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// CoreThreadStatusEvent Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
CoreThreadStatusEvent::CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousActionState* sema )
|
||||
: pxInvokeActionEvent( sema )
|
||||
{
|
||||
m_evt = evt;
|
||||
}
|
||||
|
||||
CoreThreadStatusEvent::CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousActionState& sema )
|
||||
: pxInvokeActionEvent( sema )
|
||||
{
|
||||
m_evt = evt;
|
||||
}
|
||||
|
||||
void CoreThreadStatusEvent::_DoInvoke()
|
||||
{
|
||||
sApp.DispatchEvent( m_evt );
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
//
|
||||
// Purpose:
|
||||
// This header file is meant to be a dependency-free include that provides a relatively
|
||||
// full compliment of forward defines for PCSX2/App and wxwidgets types. When
|
||||
// full compliment of forward defines for PCSX2/App and wxwidgets types. When
|
||||
// forward defined in this way, these types can be used by method and class definitions
|
||||
// as either pointers or handles without running into complicated header file
|
||||
// as either pointers or handles without running into complicated header file
|
||||
// inter-dependence.
|
||||
//
|
||||
|
||||
|
@ -30,7 +30,7 @@ class GSFrame;
|
|||
class ConsoleLogFrame;
|
||||
class PipeRedirectionBase;
|
||||
class AppCoreThread;
|
||||
class pxInvokeAppMethodEvent;
|
||||
class Pcsx2AppMethodEvent;
|
||||
class IniInterface;
|
||||
|
||||
// wxWidgets forward declarations
|
||||
|
|
|
@ -53,15 +53,6 @@ static void CpuCheckSSE2()
|
|||
g_Conf->EmuOptions.Cpu.Recompiler.EnableVU1 = false;
|
||||
}
|
||||
|
||||
|
||||
void Pcsx2App::OpenWizardConsole()
|
||||
{
|
||||
if( !IsDebugBuild ) return;
|
||||
g_Conf->ProgLogBox.Visible = true;
|
||||
m_id_ProgramLogBox = (new ConsoleLogFrame( NULL, L"PCSX2 Program Log", g_Conf->ProgLogBox ))->GetId();
|
||||
EnableAllLogging();
|
||||
}
|
||||
|
||||
void Pcsx2App::WipeUserModeSettings()
|
||||
{
|
||||
wxDirName usrlocaldir( wxStandardPaths::Get().GetUserLocalDataDir() );
|
||||
|
@ -116,14 +107,14 @@ void Pcsx2App::ReadUserModeSettings()
|
|||
L"It will likely crash on all games, devour your young, and make you an object of shame and disgrace among your family and friends. "
|
||||
L"Do not report any bugs with this version if you received this popup. \n\nYou have been warned. ", wxALIGN_CENTER
|
||||
);
|
||||
|
||||
|
||||
hackedVersion += new wxButton( &hackedVersion, wxID_OK ) | pxSizerFlags::StdCenter();
|
||||
hackedVersion.ShowModal();
|
||||
}
|
||||
|
||||
bool hasGroup = conf_usermode->HasGroup( groupname );
|
||||
bool forceWiz = m_ForceWizard || !hasGroup;
|
||||
|
||||
|
||||
if( !forceWiz )
|
||||
{
|
||||
conf_usermode->SetPath( groupname );
|
||||
|
@ -141,18 +132,17 @@ void Pcsx2App::ReadUserModeSettings()
|
|||
|
||||
preAlpha.SetSizer( new wxBoxSizer( wxVERTICAL ) );
|
||||
preAlpha += new pxStaticText( &preAlpha,
|
||||
L"NOTICE!! This is a *PRE-ALPHA* developer build of PCSX2 0.9.7. We are in the middle of major rewrites of the "
|
||||
L"NOTICE!! This is a *PRE-ALPHA* developer build of PCSX2 0.9.7. We are in the middle of major rewrites of the "
|
||||
L"user interface, and many parts of the program have *NOT* been implemented yet. Options will be missing. "
|
||||
L"Some things may crash or hang without warning. Other things will seem plainly stupid and the product of incompetent "
|
||||
L"programmers. This is normal. We're working on it.\n\nYou have been warned!", wxALIGN_CENTER
|
||||
);
|
||||
|
||||
|
||||
preAlpha += new wxButton( &preAlpha, wxID_OK ) | pxSizerFlags::StdCenter();
|
||||
preAlpha.ShowModal();
|
||||
}
|
||||
|
||||
|
||||
// first time startup, so give the user the choice of user mode:
|
||||
OpenWizardConsole();
|
||||
FirstTimeWizard wiz( NULL );
|
||||
if( !wiz.RunWizard( wiz.GetUsermodePage() ) )
|
||||
throw Exception::StartupAborted( L"Startup aborted: User canceled FirstTime Wizard." );
|
||||
|
@ -178,7 +168,6 @@ void Pcsx2App::ReadUserModeSettings()
|
|||
// user wiped their pcsx2.ini -- needs a reconfiguration via wizard!
|
||||
// (we skip the first page since it's a usermode.ini thing)
|
||||
|
||||
OpenWizardConsole();
|
||||
FirstTimeWizard wiz( NULL );
|
||||
if( !wiz.RunWizard( wiz.GetPostUsermodePage() ) )
|
||||
throw Exception::StartupAborted( L"Startup aborted: User canceled Configuration Wizard." );
|
||||
|
@ -190,11 +179,10 @@ void Pcsx2App::ReadUserModeSettings()
|
|||
AppSaveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// force a reset here to unload plugins loaded by the wizard. If we don't do this
|
||||
// the recompilers might fail to allocate the memory they need to function.
|
||||
SysReset();
|
||||
sys_resume_lock = 0;
|
||||
|
||||
// force unload plugins loaded by the wizard. If we don't do this the recompilers might
|
||||
// fail to allocate the memory they need to function.
|
||||
UnloadPlugins();
|
||||
}
|
||||
|
||||
void Pcsx2App::DetectCpuAndUserMode()
|
||||
|
@ -211,9 +199,9 @@ void Pcsx2App::DetectCpuAndUserMode()
|
|||
ReadUserModeSettings();
|
||||
AppConfig_OnChangedSettingsFolder();
|
||||
|
||||
PostMethod( &Pcsx2App::OpenMainFrame );
|
||||
PostMethod( &Pcsx2App::OpenConsoleLog );
|
||||
PostMethod( &Pcsx2App::AllocateCoreStuffs );
|
||||
PostAppMethod( &Pcsx2App::OpenMainFrame );
|
||||
PostAppMethod( &Pcsx2App::OpenConsoleLog );
|
||||
PostAppMethod( &Pcsx2App::AllocateCoreStuffs );
|
||||
}
|
||||
|
||||
void Pcsx2App::OpenMainFrame()
|
||||
|
@ -222,14 +210,13 @@ void Pcsx2App::OpenMainFrame()
|
|||
|
||||
MainEmuFrame* mainFrame = new MainEmuFrame( NULL, L"PCSX2" );
|
||||
m_id_MainFrame = mainFrame->GetId();
|
||||
mainFrame->PushEventHandler( &GetRecentIsoManager() );
|
||||
|
||||
if( wxWindow* deleteme = GetProgramLog() )
|
||||
{
|
||||
deleteme->Destroy();
|
||||
g_Conf->ProgLogBox.Visible = true;
|
||||
m_id_ProgramLogBox = wxID_ANY;
|
||||
PostIdleMethod( &Pcsx2App::OpenConsoleLog );
|
||||
PostIdleAppMethod( &Pcsx2App::OpenConsoleLog );
|
||||
}
|
||||
|
||||
SetTopWindow( mainFrame ); // not really needed...
|
||||
|
@ -259,7 +246,7 @@ void Pcsx2App::AllocateCoreStuffs()
|
|||
// HadSomeFailures only returns 'true' if an *enabled* cpu type fails to init. If
|
||||
// the user already has all interps configured, for example, then no point in
|
||||
// popping up this dialog.
|
||||
|
||||
|
||||
wxDialogWithHelpers exconf( NULL, _("PCSX2 Recompiler Error(s)"), wxVERTICAL );
|
||||
|
||||
exconf += 12;
|
||||
|
@ -273,7 +260,7 @@ void Pcsx2App::AllocateCoreStuffs()
|
|||
);
|
||||
|
||||
exconf += scrollableTextArea | pxSizerFlags::StdExpand();
|
||||
|
||||
|
||||
if( !m_CoreAllocs->IsRecAvailable_EE() )
|
||||
{
|
||||
scrollableTextArea->AppendText( L"* R5900 (EE)\n\n" );
|
||||
|
@ -333,7 +320,7 @@ void Pcsx2App::AllocateCoreStuffs()
|
|||
}
|
||||
}
|
||||
|
||||
LoadPluginsPassive( NULL );
|
||||
LoadPluginsPassive();
|
||||
}
|
||||
|
||||
|
||||
|
@ -414,15 +401,13 @@ bool Pcsx2App::OnCmdLineParsed( wxCmdLineParser& parser )
|
|||
return true;
|
||||
}
|
||||
|
||||
typedef void (wxEvtHandler::*pxInvokeMethodEventFunction)(pxInvokeAppMethodEvent&);
|
||||
typedef void (wxEvtHandler::*pxInvokeAppMethodEventFunction)(Pcsx2AppMethodEvent&);
|
||||
typedef void (wxEvtHandler::*pxStuckThreadEventHandler)(pxMessageBoxEvent&);
|
||||
|
||||
bool Pcsx2App::OnInit()
|
||||
{
|
||||
#define pxMethodEventHandler(func) \
|
||||
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxInvokeMethodEventFunction, &func )
|
||||
|
||||
Connect( pxEvt_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
|
||||
#define pxAppMethodEventHandler(func) \
|
||||
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxInvokeAppMethodEventFunction, &func )
|
||||
|
||||
pxDoAssert = AppDoAssert;
|
||||
|
||||
|
@ -436,14 +421,7 @@ bool Pcsx2App::OnInit()
|
|||
m_StderrRedirHandle = NewPipeRedir(stderr);
|
||||
wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() );
|
||||
|
||||
Connect( pxEvt_FreezeThreadFinished, wxCommandEventHandler (Pcsx2App::OnFreezeThreadFinished) );
|
||||
Connect( pxEvt_CoreThreadStatus, wxCommandEventHandler (Pcsx2App::OnCoreThreadStatus) );
|
||||
Connect( pxEvt_LoadPluginsComplete, wxCommandEventHandler (Pcsx2App::OnLoadPluginsComplete) );
|
||||
Connect( pxEvt_PluginStatus, wxCommandEventHandler (Pcsx2App::OnPluginStatus) );
|
||||
Connect( pxEvt_SysExecute, wxCommandEventHandler (Pcsx2App::OnSysExecute) );
|
||||
Connect( pxEvt_InvokeMethod, pxMethodEventHandler (Pcsx2App::OnInvokeMethod) );
|
||||
|
||||
Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) );
|
||||
Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler (Pcsx2App::OnEmuKeyDown) );
|
||||
|
||||
// User/Admin Mode Dual Setup:
|
||||
// PCSX2 now supports two fundamental modes of operation. The default is Classic mode,
|
||||
|
@ -467,6 +445,7 @@ bool Pcsx2App::OnInit()
|
|||
pxDwm_Load();
|
||||
#endif
|
||||
|
||||
SysExecutorThread.Start();
|
||||
DetectCpuAndUserMode();
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -502,25 +481,17 @@ bool Pcsx2App::OnInit()
|
|||
// OnExit() must use CleanupOnExit instead.
|
||||
void Pcsx2App::CleanupRestartable()
|
||||
{
|
||||
AffinityAssert_AllowFromMain();
|
||||
AffinityAssert_AllowFrom_MainUI();
|
||||
|
||||
// app is shutting down, so don't let the system resume for anything. (sometimes
|
||||
// there are pending Resume messages in the queue from previous user actions, and
|
||||
// this will block them from executing).
|
||||
sys_resume_lock += 10;
|
||||
ShutdownPlugins();
|
||||
SysExecutorThread.ShutdownQueue();
|
||||
|
||||
PingDispatcher( "Cleanup" );
|
||||
DeletionDispatcher();
|
||||
|
||||
CoreThread.Cancel();
|
||||
|
||||
if( m_CorePlugins )
|
||||
m_CorePlugins->Shutdown();
|
||||
//PingDispatcher( "Cleanup" );
|
||||
//DeletionDispatcher();
|
||||
IdleEventDispatcher( "Cleanup" );
|
||||
|
||||
if( g_Conf )
|
||||
AppSaveSettings();
|
||||
|
||||
sMainFrame.RemoveEventHandler( &GetRecentIsoManager() );
|
||||
}
|
||||
|
||||
// This cleanup handler can be called from OnExit (it doesn't need a running message pump),
|
||||
|
@ -529,14 +500,13 @@ void Pcsx2App::CleanupRestartable()
|
|||
// to be friendly to the OnExit scenario (no message pump).
|
||||
void Pcsx2App::CleanupOnExit()
|
||||
{
|
||||
AffinityAssert_AllowFromMain();
|
||||
AffinityAssert_AllowFrom_MainUI();
|
||||
|
||||
try
|
||||
{
|
||||
CleanupRestartable();
|
||||
CleanupResources();
|
||||
}
|
||||
catch( Exception::ThreadDeadlock& ) { throw; }
|
||||
catch( Exception::CancelEvent& ) { throw; }
|
||||
catch( Exception::RuntimeError& ex )
|
||||
{
|
||||
|
@ -551,13 +521,13 @@ void Pcsx2App::CleanupOnExit()
|
|||
#ifdef __WXMSW__
|
||||
pxDwm_Unload();
|
||||
#endif
|
||||
|
||||
|
||||
// Notice: deleting the plugin manager (unloading plugins) here causes Lilypad to crash,
|
||||
// likely due to some pending message in the queue that references lilypad procs.
|
||||
// We don't need to unload plugins anyway tho -- shutdown is plenty safe enough for
|
||||
// closing out all the windows. So just leave it be and let the plugins get unloaded
|
||||
// during the wxApp destructor. -- air
|
||||
|
||||
|
||||
// FIXME: performing a wxYield() here may fix that problem. -- air
|
||||
|
||||
pxDoAssert = pxAssertImpl_LogIt;
|
||||
|
@ -581,9 +551,30 @@ int Pcsx2App::OnExit()
|
|||
return wxApp::OnExit();
|
||||
}
|
||||
|
||||
|
||||
Pcsx2App::Pcsx2App()
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysEventHandler
|
||||
// --------------------------------------------------------------------------------------
|
||||
class SysEvtHandler : public pxEvtHandler
|
||||
{
|
||||
public:
|
||||
wxString GetEvtHandlerName() const { return L"SysExecutor"; }
|
||||
|
||||
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()
|
||||
{
|
||||
UI_UpdateSysControls();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Pcsx2App::Pcsx2App()
|
||||
: SysExecutorThread( new SysEvtHandler() )
|
||||
{
|
||||
m_PendingSaves = 0;
|
||||
m_ScheduledTermination = false;
|
||||
|
||||
m_id_MainFrame = wxID_ANY;
|
||||
m_id_GsFrame = wxID_ANY;
|
||||
m_id_ProgramLogBox = wxID_ANY;
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include "PrecompiledHeader.h"
|
||||
#include "IniInterface.h"
|
||||
#include "MainFrame.h"
|
||||
#include "GSFrame.h"
|
||||
|
||||
#include "Plugins.h"
|
||||
#include "AppSaveStates.h"
|
||||
#include "ps2/BiosTools.h"
|
||||
|
@ -32,15 +34,10 @@
|
|||
|
||||
IMPLEMENT_APP(Pcsx2App)
|
||||
|
||||
DEFINE_EVENT_TYPE( pxEvt_FreezeThreadFinished );
|
||||
DEFINE_EVENT_TYPE( pxEvt_CoreThreadStatus );
|
||||
DEFINE_EVENT_TYPE( pxEvt_LoadPluginsComplete );
|
||||
DEFINE_EVENT_TYPE( pxEvt_PluginStatus );
|
||||
DEFINE_EVENT_TYPE( pxEvt_SysExecute );
|
||||
DEFINE_EVENT_TYPE( pxEvt_InvokeMethod );
|
||||
DEFINE_EVENT_TYPE( pxEvt_LogicalVsync );
|
||||
|
||||
DEFINE_EVENT_TYPE( pxEvt_OpenModalDialog );
|
||||
DEFINE_EVENT_TYPE( pxEvt_ThreadTaskTimeout_SysExec );
|
||||
|
||||
DocsModeType DocsFolderMode = DocsFolder_User;
|
||||
wxDirName SettingsFolder;
|
||||
|
@ -50,6 +47,77 @@ bool UseDefaultSettingsFolder = true;
|
|||
ScopedPtr<AppConfig> g_Conf;
|
||||
ConfigOverrides OverrideOptions;
|
||||
|
||||
class NamedDialogBoxEvent : public BaseMessageBoxEvent
|
||||
{
|
||||
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
|
||||
{
|
||||
const wxString& dlgName( m_Content );
|
||||
|
||||
if( dlgName.IsEmpty() ) return wxID_CANCEL;
|
||||
|
||||
if( wxWindow* window = wxFindWindowByName( dlgName ) )
|
||||
{
|
||||
if( wxDialog* dialog = wxDynamicCast( window, wxDialog ) )
|
||||
{
|
||||
window->SetFocus();
|
||||
|
||||
// It's legal to call ShowModal on a non-modal dialog, therefore making
|
||||
// it modal in nature for the needs of whatever other thread of action wants
|
||||
// to block against it:
|
||||
|
||||
if( !dialog->IsModal() )
|
||||
{
|
||||
int result = dialog->ShowModal();
|
||||
dialog->Destroy();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using namespace Dialogs;
|
||||
|
||||
if( dlgName == SysConfigDialog::GetNameStatic() )
|
||||
return SysConfigDialog().ShowModal();
|
||||
if( dlgName == AppConfigDialog::GetNameStatic() )
|
||||
return AppConfigDialog().ShowModal();
|
||||
if( dlgName == BiosSelectorDialog::GetNameStatic() )
|
||||
return BiosSelectorDialog().ShowModal();
|
||||
if( dlgName == LogOptionsDialog::GetNameStatic() )
|
||||
return LogOptionsDialog().ShowModal();
|
||||
if( dlgName == AboutBoxDialog::GetNameStatic() )
|
||||
return AboutBoxDialog().ShowModal();
|
||||
}
|
||||
|
||||
return wxID_CANCEL;
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS( NamedDialogBoxEvent, BaseMessageBoxEvent );
|
||||
|
||||
// Opens the specified standard dialog as a modal dialog, or forces the an existing
|
||||
// instance of the dialog (ie, it's already open) to be modal. This is needed for
|
||||
// items which are
|
||||
static int IssueDialogAsModal( const wxString& dlgName )
|
||||
{
|
||||
BaseMessageBoxEvent tevt( dlgName );
|
||||
return Msgbox::ShowModal( tevt );
|
||||
}
|
||||
|
||||
static bool HandlePluginError( Exception::PluginError& ex )
|
||||
{
|
||||
if( pxDialogExists( L"CoreSettings" ) ) return true;
|
||||
|
@ -63,7 +131,7 @@ static bool HandlePluginError( Exception::PluginError& ex )
|
|||
g_Conf->SysSettingsTabName = L"Plugins";
|
||||
|
||||
// fixme: Send a message to the panel to select the failed plugin.
|
||||
if( wxGetApp().IssueDialogAsModal( Dialogs::SysConfigDialog::GetNameStatic() ) == wxID_CANCEL )
|
||||
if( IssueDialogAsModal( Dialogs::SysConfigDialog::GetNameStatic() ) == wxID_CANCEL )
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
|
@ -84,66 +152,56 @@ void Pcsx2App::PostMenuAction( MenuIdentifiers menu_id ) const
|
|||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxInvokeAppMethodEvent
|
||||
// Pcsx2AppMethodEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Unlike pxPingEvent, the Semaphore belonging to this event is typically posted when the
|
||||
// invoked method is completed. If the method can be executed in non-blocking fashion then
|
||||
// it should leave the semaphore postback NULL.
|
||||
//
|
||||
class pxInvokeAppMethodEvent : public pxPingEvent
|
||||
class Pcsx2AppMethodEvent : public pxInvokeActionEvent
|
||||
{
|
||||
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxInvokeAppMethodEvent)
|
||||
typedef pxInvokeActionEvent _parent;
|
||||
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(Pcsx2AppMethodEvent)
|
||||
|
||||
protected:
|
||||
FnPtr_AppMethod m_Method;
|
||||
FnPtr_Pcsx2App m_Method;
|
||||
|
||||
public:
|
||||
virtual ~pxInvokeAppMethodEvent() throw() { }
|
||||
virtual pxInvokeAppMethodEvent *Clone() const { return new pxInvokeAppMethodEvent(*this); }
|
||||
virtual ~Pcsx2AppMethodEvent() throw() { }
|
||||
virtual Pcsx2AppMethodEvent *Clone() const { return new Pcsx2AppMethodEvent(*this); }
|
||||
|
||||
explicit pxInvokeAppMethodEvent( int msgtype, FnPtr_AppMethod method=NULL, Semaphore* sema=NULL )
|
||||
: pxPingEvent( msgtype, sema )
|
||||
explicit Pcsx2AppMethodEvent( FnPtr_Pcsx2App method=NULL, SynchronousActionState* sema=NULL )
|
||||
: pxInvokeActionEvent( sema )
|
||||
{
|
||||
m_Method = method;
|
||||
}
|
||||
|
||||
explicit pxInvokeAppMethodEvent( FnPtr_AppMethod method=NULL, Semaphore* sema=NULL )
|
||||
: pxPingEvent( pxEvt_InvokeMethod, sema )
|
||||
explicit Pcsx2AppMethodEvent( FnPtr_Pcsx2App method, SynchronousActionState& sema )
|
||||
: pxInvokeActionEvent( sema )
|
||||
{
|
||||
m_Method = method;
|
||||
}
|
||||
|
||||
explicit pxInvokeAppMethodEvent( FnPtr_AppMethod method, Semaphore& sema )
|
||||
: pxPingEvent( pxEvt_InvokeMethod, &sema )
|
||||
{
|
||||
m_Method = method;
|
||||
}
|
||||
|
||||
pxInvokeAppMethodEvent( const pxInvokeAppMethodEvent& src )
|
||||
: pxPingEvent( src )
|
||||
|
||||
Pcsx2AppMethodEvent( const Pcsx2AppMethodEvent& src )
|
||||
: pxInvokeActionEvent( src )
|
||||
{
|
||||
m_Method = src.m_Method;
|
||||
}
|
||||
|
||||
void Invoke() const
|
||||
{
|
||||
if( m_Method ) (wxGetApp().*m_Method)();
|
||||
if( m_PostBack ) m_PostBack->Post();
|
||||
}
|
||||
|
||||
void SetMethod( FnPtr_AppMethod method )
|
||||
|
||||
void SetMethod( FnPtr_Pcsx2App method )
|
||||
{
|
||||
m_Method = method;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
if( m_Method ) (wxGetApp().*m_Method)();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS( pxInvokeAppMethodEvent, pxPingEvent )
|
||||
|
||||
void Pcsx2App::OnInvokeMethod( pxInvokeAppMethodEvent& evt )
|
||||
{
|
||||
evt.Invoke(); // wow this is easy!
|
||||
}
|
||||
IMPLEMENT_DYNAMIC_CLASS( Pcsx2AppMethodEvent, pxInvokeActionEvent )
|
||||
|
||||
#ifdef __WXGTK__
|
||||
extern int TranslateGDKtoWXK( u32 keysym );
|
||||
|
@ -206,7 +264,7 @@ void FramerateManager::Reset()
|
|||
Resume();
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
void FramerateManager::Resume()
|
||||
{
|
||||
}
|
||||
|
@ -218,7 +276,7 @@ void FramerateManager::DoFrame()
|
|||
m_fpsqueue_writepos = (m_fpsqueue_writepos + 1) % FramerateQueueDepth;
|
||||
m_fpsqueue[m_fpsqueue_writepos] = GetCPUTicks();
|
||||
|
||||
// intentionally leave 1 on the counter here, since ultimately we want to divide the
|
||||
// intentionally leave 1 on the counter here, since ultimately we want to divide the
|
||||
// final result (in GetFramerate() by QueueDepth-1.
|
||||
if( m_initpause > 1 ) --m_initpause;
|
||||
}
|
||||
|
@ -232,13 +290,13 @@ double FramerateManager::GetFramerate() const
|
|||
}
|
||||
|
||||
// LogicalVsync - Event received from the AppCoreThread (EEcore) for each vsync,
|
||||
// roughly 50/60 times a second when frame limiting is enabled, and up to 10,000
|
||||
// roughly 50/60 times a second when frame limiting is enabled, and up to 10,000
|
||||
// times a second if not (ok, not quite, but you get the idea... I hope.)
|
||||
void Pcsx2App::LogicalVsync()
|
||||
{
|
||||
if( PostMethodToMainThread( &Pcsx2App::LogicalVsync ) ) return;
|
||||
if( PostAppMethodMyself( &Pcsx2App::LogicalVsync ) ) return;
|
||||
|
||||
if( !SysHasValidState() || g_plugins == NULL ) return;
|
||||
if( !SysHasValidState() ) return;
|
||||
|
||||
// Update / Calculate framerate!
|
||||
|
||||
|
@ -249,78 +307,24 @@ void Pcsx2App::LogicalVsync()
|
|||
if( (PADupdate != NULL) && (GSopen2 != NULL) && (wxGetApp().GetGsFramePtr() != NULL) )
|
||||
PADupdate(0);
|
||||
|
||||
const keyEvent* ev = PADkeyEvent();
|
||||
|
||||
if( (ev != NULL) && (ev->key != 0) )
|
||||
while( const keyEvent* ev = PADkeyEvent() )
|
||||
{
|
||||
if( ev->key == 0 ) break;
|
||||
|
||||
// Give plugins first try to handle keys. If none of them handles the key, it will
|
||||
// be passed to the main user interface.
|
||||
|
||||
if( !g_plugins->KeyEvent( *ev ) )
|
||||
if( !GetCorePlugins().KeyEvent( *ev ) )
|
||||
PadKeyDispatch( *ev );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Pcsx2App Event Handlers
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Invoked by the AppCoreThread when it's internal status has changed.
|
||||
// evt.GetInt() reflects the status at the time the message was sent, which may differ
|
||||
// from the actual status. Typically listeners bound to this will want to use direct
|
||||
// polling of the CoreThread rather than the belated status.
|
||||
void Pcsx2App::OnCoreThreadStatus( wxCommandEvent& evt )
|
||||
{
|
||||
CoreThreadStatus status = (CoreThreadStatus)evt.GetInt();
|
||||
|
||||
switch( status )
|
||||
{
|
||||
case CoreThread_Started:
|
||||
case CoreThread_Reset:
|
||||
case CoreThread_Stopped:
|
||||
FpsManager.Reset();
|
||||
break;
|
||||
|
||||
case CoreThread_Resumed:
|
||||
case CoreThread_Suspended:
|
||||
FpsManager.Resume();
|
||||
break;
|
||||
}
|
||||
|
||||
// Clear the sticky key statuses, because hell knows what'll change while the PAD
|
||||
// plugin is suspended.
|
||||
|
||||
m_kevt.m_shiftDown = false;
|
||||
m_kevt.m_controlDown = false;
|
||||
m_kevt.m_altDown = false;
|
||||
|
||||
m_evtsrc_CoreThreadStatus.Dispatch( status );
|
||||
ScopedBusyCursor::SetDefault( Cursor_NotBusy );
|
||||
CoreThread.RethrowException();
|
||||
}
|
||||
|
||||
void Pcsx2App::OnOpenModalDialog( wxCommandEvent& evt )
|
||||
{
|
||||
pxAssertDev( !evt.GetString().IsEmpty(), wxNullChar );
|
||||
|
||||
MsgboxEventResult* evtres = (MsgboxEventResult*)evt.GetClientData();
|
||||
|
||||
wxWindowID result = IssueDialogAsModal( evt.GetString() );
|
||||
|
||||
if( evtres != NULL )
|
||||
{
|
||||
evtres->result = result;
|
||||
evtres->WaitForMe.Post();
|
||||
}
|
||||
}
|
||||
|
||||
void Pcsx2App::OnOpenDialog_StuckThread( wxCommandEvent& evt )
|
||||
{
|
||||
if( !pxAssert( evt.GetClientData() != NULL ) ) return;
|
||||
DoStuckThread( *(PersistentThread*)evt.GetClientData() );
|
||||
}
|
||||
|
||||
int Pcsx2App::DoStuckThread( PersistentThread& stuck_thread )
|
||||
/*int Pcsx2App::DoStuckThread( PersistentThread& stuck_thread )
|
||||
{
|
||||
if( !wxThread::IsMain() )
|
||||
{
|
||||
|
@ -336,59 +340,8 @@ int Pcsx2App::DoStuckThread( PersistentThread& stuck_thread )
|
|||
|
||||
pxStuckThreadEvent evt( stuck_thread );
|
||||
return Msgbox::ShowModal( evt );
|
||||
}
|
||||
}*/
|
||||
|
||||
// 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
|
||||
int Pcsx2App::IssueDialogAsModal( const wxString& dlgName )
|
||||
{
|
||||
if( dlgName.IsEmpty() ) return wxID_CANCEL;
|
||||
|
||||
if( !wxThread::IsMain() )
|
||||
{
|
||||
MsgboxEventResult result;
|
||||
PostCommand( &result, pxEvt_OpenModalDialog, 0, 0, dlgName );
|
||||
result.WaitForMe.WaitNoCancel();
|
||||
return result.result;
|
||||
}
|
||||
|
||||
if( wxWindow* window = wxFindWindowByName( dlgName ) )
|
||||
{
|
||||
if( wxDialog* dialog = wxDynamicCast( window, wxDialog ) )
|
||||
{
|
||||
window->SetFocus();
|
||||
|
||||
// It's legal to call ShowModal on a non-modal dialog, therefore making
|
||||
// it modal in nature for the needs of whatever other thread of action wants
|
||||
// to block against it:
|
||||
|
||||
if( !dialog->IsModal() )
|
||||
{
|
||||
int result = dialog->ShowModal();
|
||||
dialog->Destroy();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using namespace Dialogs;
|
||||
|
||||
if( dlgName == SysConfigDialog::GetNameStatic() )
|
||||
return SysConfigDialog().ShowModal();
|
||||
if( dlgName == AppConfigDialog::GetNameStatic() )
|
||||
return AppConfigDialog().ShowModal();
|
||||
if( dlgName == BiosSelectorDialog::GetNameStatic() )
|
||||
return BiosSelectorDialog().ShowModal();
|
||||
if( dlgName == LogOptionsDialog::GetNameStatic() )
|
||||
return LogOptionsDialog().ShowModal();
|
||||
if( dlgName == AboutBoxDialog::GetNameStatic() )
|
||||
return AboutBoxDialog().ShowModal();
|
||||
}
|
||||
|
||||
return wxID_CANCEL;
|
||||
}
|
||||
|
||||
HashTools::HashMap<int, const GlobalCommandDescriptor*> GlobalAccels( 0, 0xffffffff );
|
||||
|
||||
|
@ -446,7 +399,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
|
|||
wxDialogWithHelpers dialog( NULL, _("PS2 BIOS Error"), wxVERTICAL );
|
||||
dialog += dialog.Heading( ex.FormatDisplayMessage() + BIOS_GetMsg_Required() + _("\nPress Ok to go to the BIOS Configuration Panel.") );
|
||||
dialog += new ModalButtonPanel( &dialog, MsgButtons().OKCancel() );
|
||||
|
||||
|
||||
if( dialog.ShowModal() == wxID_CANCEL )
|
||||
Console.Warning( "User denied option to re-configure BIOS." );
|
||||
|
||||
|
@ -466,7 +419,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
|
|||
}
|
||||
catch( Exception::PluginInitError& ex )
|
||||
{
|
||||
if( m_CorePlugins ) m_CorePlugins->Shutdown();
|
||||
CorePlugins.Shutdown();
|
||||
|
||||
Console.Error( ex.FormatDiagnosticMessage() );
|
||||
if( !HandlePluginError( ex ) )
|
||||
|
@ -477,7 +430,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
|
|||
}
|
||||
catch( Exception::PluginError& ex )
|
||||
{
|
||||
if( m_CorePlugins ) m_CorePlugins->Close();
|
||||
CorePlugins.Close();
|
||||
|
||||
Console.Error( ex.FormatDiagnosticMessage() );
|
||||
if( !HandlePluginError( ex ) )
|
||||
|
@ -487,15 +440,16 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
|
|||
}
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
#if 0
|
||||
catch( Exception::ThreadDeadlock& ex )
|
||||
{
|
||||
// [TODO] Bind a listener to the CoreThread status, and automatically close the dialog
|
||||
// if the thread starts responding while we're waiting (not hard in fact, but I'm getting
|
||||
// a little tired, so maybe later!) --air
|
||||
|
||||
|
||||
Console.Warning( ex.FormatDiagnosticMessage() );
|
||||
wxDialogWithHelpers dialog( NULL, _("PCSX2 Unresponsive Thread"), wxVERTICAL );
|
||||
|
||||
|
||||
dialog += dialog.Heading( ex.FormatDisplayMessage() + L"\n\n" +
|
||||
pxE( ".Popup Error:Thread Deadlock Actions",
|
||||
L"'Ignore' to continue waiting for the thread to respond.\n"
|
||||
|
@ -505,7 +459,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
|
|||
);
|
||||
|
||||
int result = pxIssueConfirmation( dialog, MsgButtons().Ignore().Cancel().Custom( _("Terminate") ) );
|
||||
|
||||
|
||||
if( result == pxID_CUSTOM )
|
||||
{
|
||||
// fastest way to kill the process! (works in Linux and win32, thanks to windows having very
|
||||
|
@ -522,6 +476,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
|
|||
|
||||
// Ignore does nothing...
|
||||
}
|
||||
#endif
|
||||
// ----------------------------------------------------------------------------
|
||||
catch( Exception::CancelEvent& ex )
|
||||
{
|
||||
|
@ -537,6 +492,27 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
|
|||
}
|
||||
}
|
||||
|
||||
void Pcsx2App::StartPendingSave()
|
||||
{
|
||||
if( PostAppMethodMyself(&Pcsx2App::StartPendingSave) ) return;
|
||||
++m_PendingSaves;
|
||||
}
|
||||
|
||||
void Pcsx2App::ClearPendingSave()
|
||||
{
|
||||
if( PostAppMethodMyself(&Pcsx2App::StartPendingSave) ) return;
|
||||
|
||||
--m_PendingSaves;
|
||||
pxAssumeDev( m_PendingSaves >= 0, "Pending saves count mismatch (pending count is less than 0)" );
|
||||
|
||||
if( (m_PendingSaves == 0) && m_ScheduledTermination )
|
||||
{
|
||||
Console.WriteLn( "App: All pending saves completed; exiting!" );
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Common exit handler which can be called from any event (though really it should
|
||||
// be called only from CloseWindow handlers since that's the more appropriate way
|
||||
// to handle cancelable window closures)
|
||||
|
@ -545,10 +521,18 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
|
|||
// the glorious user, whomever (s)he-it might be.
|
||||
void Pcsx2App::PrepForExit()
|
||||
{
|
||||
CancelLoadingPlugins();
|
||||
SysExecutorThread.ShutdownQueue();
|
||||
|
||||
//SysExecutorThread.Cancel();
|
||||
DispatchEvent( AppStatus_Exiting );
|
||||
|
||||
if( m_PendingSaves != 0 )
|
||||
{
|
||||
Console.WriteLn( "App: Saves are pending; exit postponed..." );
|
||||
sApp.SetExitOnFrameDelete( false );
|
||||
return;
|
||||
}
|
||||
|
||||
// This should be called by OnExit(), but sometimes wxWidgets fails to call OnExit(), so
|
||||
// do it here just in case (no harm anyway -- OnExit is the next logical step after
|
||||
// CloseWindow returns true from the TopLevel window).
|
||||
|
@ -577,10 +561,11 @@ GSFrame& Pcsx2App::GetGsFrame() const
|
|||
return *gsFrame;
|
||||
}
|
||||
|
||||
|
||||
void AppApplySettings( const AppConfig* oldconf )
|
||||
{
|
||||
AffinityAssert_AllowFromMain();
|
||||
AffinityAssert_AllowFrom_MainUI();
|
||||
|
||||
ScopedCoreThreadClose suspend_core;
|
||||
|
||||
g_Conf->Folders.ApplyDefaults();
|
||||
|
||||
|
@ -593,18 +578,8 @@ void AppApplySettings( const AppConfig* oldconf )
|
|||
|
||||
g_Conf->EmuOptions.BiosFilename = g_Conf->FullpathToBios();
|
||||
|
||||
ScopedCoreThreadSuspend suspend_core;
|
||||
|
||||
if( g_plugins != NULL )
|
||||
g_plugins->SetSettingsFolder( GetSettingsFolder().ToString() );
|
||||
|
||||
RelocateLogfile();
|
||||
|
||||
// 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 );
|
||||
|
||||
if( (oldconf == NULL) || (oldconf->LanguageId != g_Conf->LanguageId) )
|
||||
{
|
||||
wxDoNotLogInThisScope please;
|
||||
|
@ -616,11 +591,27 @@ void AppApplySettings( const AppConfig* oldconf )
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
CorePlugins.SetSettingsFolder( GetSettingsFolder().ToString() );
|
||||
|
||||
// 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 );
|
||||
sApp.DispatchEvent( AppStatus_SettingsApplied );
|
||||
suspend_core.Resume();
|
||||
|
||||
suspend_core.AllowResume();
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxDudConfig
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Used to handle config actions prior to the creation of the ini file (for example, the
|
||||
// first time wizard). Attempts to save ini settings are simply ignored through this
|
||||
// class, which allows us to give the user a way to set everything up in the wizard, apply
|
||||
// settings as usual, and only *save* something once the whole wizard is complete.
|
||||
//
|
||||
class pxDudConfig : public wxConfigBase
|
||||
{
|
||||
protected:
|
||||
|
@ -691,16 +682,25 @@ AppIniLoader::AppIniLoader()
|
|||
|
||||
void AppLoadSettings()
|
||||
{
|
||||
if( !AffinityAssert_AllowFromMain() ) return;
|
||||
if( wxGetApp().PostMethodMyself(AppLoadSettings) ) return;
|
||||
|
||||
AppIniLoader loader;
|
||||
g_Conf->LoadSave( loader );
|
||||
|
||||
if( !wxFile::Exists( g_Conf->CurrentIso ) )
|
||||
g_Conf->CurrentIso.Clear();
|
||||
|
||||
sApp.DispatchEvent( loader );
|
||||
}
|
||||
|
||||
void AppSaveSettings()
|
||||
{
|
||||
if( !AffinityAssert_AllowFromMain() ) return;
|
||||
if( wxGetApp().PostMethodMyself(AppSaveSettings) ) return;
|
||||
|
||||
if( !wxFile::Exists( g_Conf->CurrentIso ) )
|
||||
g_Conf->CurrentIso.Clear();
|
||||
|
||||
sApp.GetRecentIsoManager().Add( g_Conf->CurrentIso );
|
||||
|
||||
AppIniSaver saver;
|
||||
g_Conf->LoadSave( saver );
|
||||
|
@ -709,7 +709,7 @@ void AppSaveSettings()
|
|||
|
||||
// Invokes the specified Pcsx2App method, or posts the method to the main thread if the calling
|
||||
// thread is not Main. Action is blocking. For non-blocking method execution, use
|
||||
// PostMethodToMainThread.
|
||||
// PostAppMethodMyself.
|
||||
//
|
||||
// This function works something like setjmp/longjmp, in that the return value indicates if the
|
||||
// function actually executed the specified method or not.
|
||||
|
@ -718,21 +718,20 @@ void AppSaveSettings()
|
|||
// FALSE if the method was not posted to the main thread (meaning this IS the main thread!)
|
||||
// TRUE if the method was posted.
|
||||
//
|
||||
bool Pcsx2App::InvokeMethodOnMainThread( FnPtr_AppMethod method )
|
||||
bool Pcsx2App::InvokeOnMainThread( FnPtr_Pcsx2App method )
|
||||
{
|
||||
if( wxThread::IsMain() ) return false;
|
||||
|
||||
Semaphore sem;
|
||||
pxInvokeAppMethodEvent evt( method, sem );
|
||||
AddPendingEvent( evt );
|
||||
sem.Wait();
|
||||
SynchronousActionState sync;
|
||||
PostEvent( Pcsx2AppMethodEvent( method, sync ) );
|
||||
sync.WaitForResult();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Invokes the specified Pcsx2App method, or posts the method to the main thread if the calling
|
||||
// thread is not Main. Action is non-blocking. For blocking method execution, use
|
||||
// InvokeMethodOnMainThread.
|
||||
// InvokeOnMainThread.
|
||||
//
|
||||
// This function works something like setjmp/longjmp, in that the return value indicates if the
|
||||
// function actually executed the specified method or not.
|
||||
|
@ -741,33 +740,31 @@ bool Pcsx2App::InvokeMethodOnMainThread( FnPtr_AppMethod method )
|
|||
// FALSE if the method was not posted to the main thread (meaning this IS the main thread!)
|
||||
// TRUE if the method was posted.
|
||||
//
|
||||
bool Pcsx2App::PostMethodToMainThread( FnPtr_AppMethod method )
|
||||
bool Pcsx2App::PostAppMethodMyself( FnPtr_Pcsx2App method )
|
||||
{
|
||||
if( wxThread::IsMain() ) return false;
|
||||
pxInvokeAppMethodEvent evt( method );
|
||||
AddPendingEvent( evt );
|
||||
PostEvent( Pcsx2AppMethodEvent( method ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
// Posts a method to the main thread; non-blocking. Post occurs even when called from the
|
||||
// main thread.
|
||||
void Pcsx2App::PostMethod( FnPtr_AppMethod method )
|
||||
void Pcsx2App::PostAppMethod( FnPtr_Pcsx2App method )
|
||||
{
|
||||
pxInvokeAppMethodEvent evt( method );
|
||||
AddPendingEvent( evt );
|
||||
PostEvent( Pcsx2AppMethodEvent( method ) );
|
||||
}
|
||||
|
||||
// Posts a method to the main thread; non-blocking. Post occurs even when called from the
|
||||
// main thread.
|
||||
void Pcsx2App::PostIdleMethod( FnPtr_AppMethod method )
|
||||
void Pcsx2App::PostIdleAppMethod( FnPtr_Pcsx2App method )
|
||||
{
|
||||
pxInvokeAppMethodEvent evt( method );
|
||||
OnAddEventToIdleQueue( evt );
|
||||
Pcsx2AppMethodEvent evt( method );
|
||||
AddIdleEvent( evt );
|
||||
}
|
||||
|
||||
void Pcsx2App::OpenGsPanel()
|
||||
{
|
||||
if( InvokeMethodOnMainThread( &Pcsx2App::OpenGsPanel ) ) return;
|
||||
if( InvokeOnMainThread( &Pcsx2App::OpenGsPanel ) ) return;
|
||||
|
||||
GSFrame* gsFrame = GetGsFramePtr();
|
||||
if( gsFrame == NULL )
|
||||
|
@ -786,7 +783,7 @@ void Pcsx2App::OpenGsPanel()
|
|||
// Doing an immediate hide/show didn't work. So now I'm trying a resize. Because
|
||||
// wxWidgets is "clever" (grr!) it optimizes out just force-setting the same size
|
||||
// over again, so instead I resize it to size-1 and then back to the original size.
|
||||
|
||||
|
||||
const wxSize oldsize( gsFrame->GetSize() );
|
||||
wxSize newsize( oldsize );
|
||||
newsize.DecBy(1);
|
||||
|
@ -794,8 +791,8 @@ void Pcsx2App::OpenGsPanel()
|
|||
gsFrame->SetSize( newsize );
|
||||
gsFrame->SetSize( oldsize );
|
||||
}
|
||||
|
||||
pxAssumeDev( !GetPluginManager().IsOpen( PluginId_GS ), "GS Plugin must be closed prior to opening a new Gs Panel!" );
|
||||
|
||||
pxAssumeDev( !GetCorePlugins().IsOpen( PluginId_GS ), "GS Plugin must be closed prior to opening a new Gs Panel!" );
|
||||
|
||||
gsFrame->Show();
|
||||
pDsp = (uptr)gsFrame->GetViewport()->GetHandle();
|
||||
|
@ -806,7 +803,7 @@ void Pcsx2App::OpenGsPanel()
|
|||
|
||||
void Pcsx2App::CloseGsPanel()
|
||||
{
|
||||
if( InvokeMethodOnMainThread( &Pcsx2App::CloseGsPanel ) ) return;
|
||||
if( InvokeOnMainThread( &Pcsx2App::CloseGsPanel ) ) return;
|
||||
|
||||
GSFrame* gsFrame = GetGsFramePtr();
|
||||
if( (gsFrame != NULL) && CloseViewportWithPlugins )
|
||||
|
@ -838,45 +835,77 @@ void Pcsx2App::OnMainFrameClosed( wxWindowID id )
|
|||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Sys/Core API and Shortcuts (for wxGetApp())
|
||||
// SysExecuteEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
static int _sysexec_cdvdsrc_type = -1;
|
||||
static wxString _sysexec_elf_override;
|
||||
|
||||
static void _sendmsg_SysExecute()
|
||||
class SysExecuteEvent : public SysExecEvent
|
||||
{
|
||||
if( !CoreThread.AcquireResumeLock() )
|
||||
protected:
|
||||
bool m_UseCDVDsrc;
|
||||
CDVD_SourceType m_cdvdsrc_type;
|
||||
wxString m_elf_override;
|
||||
|
||||
public:
|
||||
virtual ~SysExecuteEvent() throw() {}
|
||||
SysExecuteEvent* Clone() const { return new SysExecuteEvent(*this); }
|
||||
|
||||
wxString GetEventName() const
|
||||
{
|
||||
DbgCon.WriteLn( "(SysExecute) another resume lock or message is already pending; no message posted." );
|
||||
return;
|
||||
return L"SysExecute";
|
||||
}
|
||||
|
||||
AppSaveSettings();
|
||||
wxGetApp().PostCommand( pxEvt_SysExecute, _sysexec_cdvdsrc_type );
|
||||
}
|
||||
wxString GetEventMessage() const
|
||||
{
|
||||
return _("Executing PS2 Virtual Machine...");
|
||||
}
|
||||
|
||||
SysExecuteEvent()
|
||||
{
|
||||
m_UseCDVDsrc = false;
|
||||
}
|
||||
|
||||
static void OnSysExecuteAfterPlugins( const wxCommandEvent& loadevt )
|
||||
{
|
||||
if( (wxTheApp == NULL) || !((Pcsx2App*)wxTheApp)->m_CorePlugins ) return;
|
||||
_sendmsg_SysExecute();
|
||||
}
|
||||
SysExecuteEvent( const wxString& elf_override )
|
||||
: m_elf_override( elf_override )
|
||||
{
|
||||
m_UseCDVDsrc = false;
|
||||
}
|
||||
|
||||
SysExecuteEvent( CDVD_SourceType srctype, const wxString& elf_override )
|
||||
: m_elf_override( elf_override )
|
||||
{
|
||||
m_cdvdsrc_type = srctype;
|
||||
m_UseCDVDsrc = true;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
wxGetApp().ProcessMethod( AppSaveSettings );
|
||||
|
||||
// if something unloaded plugins since this messages was queued then it's best to ignore
|
||||
// it, because apparently too much stuff is going on and the emulation states are wonky.
|
||||
if( !CorePlugins.AreLoaded() ) return;
|
||||
|
||||
DbgCon.WriteLn( Color_Gray, "(SysExecute) received." );
|
||||
|
||||
if( m_UseCDVDsrc ) CoreThread.Shutdown(); else CoreThread.Suspend();
|
||||
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
|
||||
if( m_UseCDVDsrc )
|
||||
CDVDsys_ChangeSource( m_cdvdsrc_type );
|
||||
else if( CDVD == NULL )
|
||||
CDVDsys_ChangeSource( CDVDsrc_NoDisc );
|
||||
|
||||
if( !CoreThread.HasValidState() )
|
||||
CoreThread.SetElfOverride( m_elf_override );
|
||||
|
||||
CoreThread.Resume();
|
||||
}
|
||||
};
|
||||
|
||||
// Executes the emulator using a saved/existing virtual machine state and currently
|
||||
// configured CDVD source device.
|
||||
void Pcsx2App::SysExecute()
|
||||
{
|
||||
_sysexec_cdvdsrc_type = -1;
|
||||
_sysexec_elf_override = CoreThread.GetElfOverride();
|
||||
|
||||
if( !m_CorePlugins )
|
||||
{
|
||||
LoadPluginsPassive( OnSysExecuteAfterPlugins );
|
||||
return;
|
||||
}
|
||||
|
||||
DbgCon.WriteLn( Color_Gray, "(SysExecute) Queuing request to re-execute existing VM state." );
|
||||
_sendmsg_SysExecute();
|
||||
SysExecutorThread.PostEvent( new SysExecuteEvent(CoreThread.GetElfOverride()) );
|
||||
}
|
||||
|
||||
// Executes the specified cdvd source and optional elf file. This command performs a
|
||||
|
@ -884,59 +913,38 @@ void Pcsx2App::SysExecute()
|
|||
// sources.
|
||||
void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc, const wxString& elf_override )
|
||||
{
|
||||
_sysexec_cdvdsrc_type = (int)cdvdsrc;
|
||||
_sysexec_elf_override = elf_override;
|
||||
|
||||
if( !m_CorePlugins )
|
||||
{
|
||||
LoadPluginsPassive( OnSysExecuteAfterPlugins );
|
||||
return;
|
||||
}
|
||||
|
||||
DbgCon.WriteLn( Color_Gray, "(SysExecute) Queuing request for new VM state." );
|
||||
_sendmsg_SysExecute();
|
||||
SysExecutorThread.PostEvent( new SysExecuteEvent(cdvdsrc, CoreThread.GetElfOverride()) );
|
||||
}
|
||||
|
||||
// This message performs actual system execution (as dictated by SysExecute variants).
|
||||
// It is implemented as a message handler so that it can be triggered in response to
|
||||
// the completion of other dependent activities, namely loading plugins.
|
||||
void Pcsx2App::OnSysExecute( wxCommandEvent& evt )
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent_Shutdown
|
||||
// --------------------------------------------------------------------------------------
|
||||
class SysExecEvent_Shutdown : public SysExecEvent
|
||||
{
|
||||
CoreThread.ReleaseResumeLock();
|
||||
|
||||
if( sys_resume_lock > 0 )
|
||||
public:
|
||||
wxString GetEventName() const
|
||||
{
|
||||
Console.WriteLn( "SysExecute: State is locked, ignoring Execute request!" );
|
||||
return;
|
||||
return L"SysShutdown";
|
||||
}
|
||||
|
||||
wxString GetEventMessage() const
|
||||
{
|
||||
return _("Resetting PS2 virtual machine...");
|
||||
}
|
||||
|
||||
// if something unloaded plugins since this messages was queued then it's best to ignore
|
||||
// it, because apparently too much stuff is going on and the emulation states are wonky.
|
||||
if( !m_CorePlugins ) return;
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
StateCopy_Clear();
|
||||
CoreThread.Shutdown();
|
||||
}
|
||||
|
||||
DbgCon.WriteLn( Color_Gray, "(MainThread) SysExecute received." );
|
||||
|
||||
if( evt.GetInt() != -1 ) CoreThread.Reset(); else CoreThread.Suspend();
|
||||
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
|
||||
if( evt.GetInt() != -1 )
|
||||
CDVDsys_ChangeSource( (CDVD_SourceType)evt.GetInt() );
|
||||
else if( CDVD == NULL )
|
||||
CDVDsys_ChangeSource( CDVDsrc_NoDisc );
|
||||
|
||||
if( !CoreThread.HasValidState() )
|
||||
CoreThread.SetElfOverride( _sysexec_elf_override );
|
||||
|
||||
CoreThread.Resume();
|
||||
}
|
||||
};
|
||||
|
||||
// Full system reset stops the core thread and unloads all core plugins *completely*.
|
||||
void Pcsx2App::SysReset()
|
||||
void Pcsx2App::SysShutdown()
|
||||
{
|
||||
StateCopy_Clear();
|
||||
CoreThread.Reset();
|
||||
CoreThread.Cancel();
|
||||
CoreThread.ReleaseResumeLock();
|
||||
m_CorePlugins = NULL;
|
||||
SysExecutorThread.PostEvent( new SysExecEvent_Shutdown() );
|
||||
}
|
||||
|
||||
// Returns true if there is a "valid" virtual machine state from the user's perspective. This
|
||||
|
@ -1002,39 +1010,3 @@ SysCoreAllocations& GetSysCoreAlloc()
|
|||
{
|
||||
return *wxGetApp().m_CoreAllocs;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxStuckThreadEvent Implementation
|
||||
// --------------------------------------------------------------------------------------
|
||||
IMPLEMENT_DYNAMIC_CLASS( pxStuckThreadEvent, BaseMessageBoxEvent )
|
||||
|
||||
pxStuckThreadEvent::pxStuckThreadEvent()
|
||||
: BaseMessageBoxEvent()
|
||||
, m_Thread( *(PersistentThread*)NULL )
|
||||
{
|
||||
}
|
||||
|
||||
pxStuckThreadEvent::pxStuckThreadEvent( MsgboxEventResult& instdata, PersistentThread& thr )
|
||||
: BaseMessageBoxEvent( instdata, wxEmptyString )
|
||||
, m_Thread( thr )
|
||||
{
|
||||
}
|
||||
|
||||
pxStuckThreadEvent::pxStuckThreadEvent( PersistentThread& thr )
|
||||
: BaseMessageBoxEvent()
|
||||
, m_Thread( thr )
|
||||
{
|
||||
}
|
||||
|
||||
pxStuckThreadEvent::pxStuckThreadEvent( const pxStuckThreadEvent& src )
|
||||
: BaseMessageBoxEvent( src )
|
||||
, m_Thread( src.m_Thread )
|
||||
{
|
||||
}
|
||||
|
||||
int pxStuckThreadEvent::_DoDialog() const
|
||||
{
|
||||
return 1;
|
||||
//return Dialogs::StuckThreadDialog( m_Thread ).ShowModal();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,63 +15,39 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "App.h"
|
||||
#include "SaveState.h"
|
||||
|
||||
enum SaveStateActionType
|
||||
{
|
||||
SaveStateAction_CreateFinished,
|
||||
SaveStateAction_RestoreFinished,
|
||||
SaveStateAction_ZipToDiskFinished,
|
||||
SaveStateAction_UnzipFromDiskFinished,
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// IEventListener_SaveStateThread
|
||||
// SaveSinglePluginHelper
|
||||
// --------------------------------------------------------------------------------------
|
||||
class IEventListener_SaveStateThread : public IEventDispatcher<SaveStateActionType>
|
||||
// A scoped convenience class for closing a single plugin and saving its state to memory.
|
||||
// Emulation is suspended as needed, and is restored when the object leaves scope. Within
|
||||
// the scope of the object, code is free to call plugin re-configurations or even unload
|
||||
// a plugin entirely and re-load a different plugin in its place.
|
||||
//
|
||||
class SaveSinglePluginHelper
|
||||
{
|
||||
public:
|
||||
typedef SaveStateActionType EvtParams;
|
||||
|
||||
public:
|
||||
IEventListener_SaveStateThread() {}
|
||||
virtual ~IEventListener_SaveStateThread() throw() {}
|
||||
|
||||
virtual void DispatchEvent( const SaveStateActionType& status )
|
||||
{
|
||||
switch( status )
|
||||
{
|
||||
case SaveStateAction_CreateFinished: SaveStateAction_OnCreateFinished(); break;
|
||||
case SaveStateAction_RestoreFinished: SaveStateAction_OnRestoreFinished(); break;
|
||||
case SaveStateAction_ZipToDiskFinished: SaveStateAction_OnZipToDiskFinished(); break;
|
||||
case SaveStateAction_UnzipFromDiskFinished: SaveStateAction_OnUnzipFromDiskFinished(); break;
|
||||
|
||||
jNO_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void SaveStateAction_OnCreateFinished() {}
|
||||
virtual void SaveStateAction_OnRestoreFinished() {}
|
||||
virtual void SaveStateAction_OnZipToDiskFinished() {}
|
||||
virtual void SaveStateAction_OnUnzipFromDiskFinished() {}
|
||||
SafeArray<u8> m_plugstore;
|
||||
bool m_validstate;
|
||||
PluginsEnum_t m_pid;
|
||||
|
||||
ScopedCoreThreadPause m_scoped_pause;
|
||||
|
||||
public:
|
||||
SaveSinglePluginHelper( PluginsEnum_t pid );
|
||||
virtual ~SaveSinglePluginHelper() throw();
|
||||
};
|
||||
|
||||
|
||||
extern bool StateCopy_InvokeOnSaveComplete( IActionInvocation* sst );
|
||||
extern bool StateCopy_InvokeOnCopyComplete( IActionInvocation* sst );
|
||||
extern SafeArray<u8>& StateCopy_GetBuffer();
|
||||
extern bool StateCopy_IsValid();
|
||||
|
||||
extern void StateCopy_FreezeToMem();
|
||||
extern void StateCopy_FreezeToMem_Blocking();
|
||||
extern void StateCopy_ThawFromMem_Blocking();
|
||||
|
||||
extern void StateCopy_SaveToFile( const wxString& file );
|
||||
extern void StateCopy_LoadFromFile( const wxString& file );
|
||||
extern void StateCopy_SaveToSlot( uint num );
|
||||
extern void StateCopy_LoadFromSlot( uint slot );
|
||||
extern void StateCopy_Clear();
|
||||
extern bool StateCopy_IsBusy();
|
||||
|
||||
|
||||
extern const SafeArray<u8>* StateCopy_GetBuffer();
|
||||
|
|
|
@ -179,5 +179,7 @@ public:
|
|||
);
|
||||
|
||||
virtual ~ApplicableWizardPage() throw() { m_ApplyState.DoCleanup(); }
|
||||
|
||||
virtual bool PrepForApply();
|
||||
};
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ protected:
|
|||
int m_win32_LinesPerPage;
|
||||
int m_win32_LinesPerScroll;
|
||||
#endif
|
||||
bool m_IsPaused;
|
||||
ScopedPtr<ScopedCoreThreadPause> m_IsPaused;
|
||||
bool m_FreezeWrites;
|
||||
|
||||
public:
|
||||
|
@ -217,7 +217,7 @@ protected:
|
|||
|
||||
// Lock object for accessing or modifying the following three vars:
|
||||
// m_QueueBuffer, m_QueueColorSelection, m_CurQueuePos
|
||||
MutexLockRecursive m_QueueLock;
|
||||
MutexRecursive m_QueueLock;
|
||||
|
||||
// Describes a series of colored text sections in the m_QueueBuffer.
|
||||
SafeList<ColorSection> m_QueueColorSection;
|
||||
|
|
|
@ -32,6 +32,15 @@ ApplicableWizardPage::ApplicableWizardPage( wxWizard* parent, wxWizardPage* prev
|
|||
{
|
||||
}
|
||||
|
||||
// This is a hack feature substitute for prioritized apply events. This callback is issued prior
|
||||
// to the apply chain being run, allowing a panel to do some pre-apply prepwork. PAnels implementing
|
||||
// this function should not modify the g_conf state.
|
||||
bool ApplicableWizardPage::PrepForApply()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
Panels::SettingsDirPickerPanel::SettingsDirPickerPanel( wxWindow* parent ) :
|
||||
DirPickerPanel( parent, FolderId_Settings, _("Settings"), _("Select a folder for PCSX2 settings") )
|
||||
|
@ -63,7 +72,7 @@ FirstTimeWizard::UsermodePage::UsermodePage( wxWizard* parent ) :
|
|||
|
||||
m_dirpick_settings = new SettingsDirPickerPanel( &panel );
|
||||
m_panel_LangSel = new LanguageSelectionPanel( &panel );
|
||||
m_panel_UserSel = new UsermodeSelectionPanel( &panel );
|
||||
m_panel_UserSel = new DocsFolderPickerPanel( &panel );
|
||||
|
||||
panel += panel.Heading(_("PCSX2 is starting from a new or unknown folder and needs to be configured."));
|
||||
|
||||
|
@ -93,6 +102,37 @@ void FirstTimeWizard::UsermodePage::OnCustomDirChanged( wxCommandEvent& evt )
|
|||
OnUsermodeChanged( evt );
|
||||
}
|
||||
|
||||
bool FirstTimeWizard::UsermodePage::PrepForApply()
|
||||
{
|
||||
wxDirName path( PathDefs::GetDocuments(m_panel_UserSel->GetDocsMode()) );
|
||||
|
||||
if( path.FileExists() )
|
||||
{
|
||||
// FIXME: There's already a file by the same name.. not sure what we should do here.
|
||||
throw Exception::BadStream( path.ToString(),
|
||||
L"Targeted documents folder is already occupied by a file.",
|
||||
pxE( "Error:DocsFolderFileConflict",
|
||||
L"PCSX2 cannot create a documents folder in the requested location. "
|
||||
L"The path name matches an existing file. Delete the file or change the documents location, "
|
||||
L"and then try again."
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if( !path.Exists() )
|
||||
{
|
||||
wxDialogWithHelpers dialog( NULL, _("Create folder?"), wxVERTICAL );
|
||||
dialog += dialog.Heading( _("PCSX2 will create the following folder for documents. You can change this setting later, at any time.") );
|
||||
dialog += 12;
|
||||
dialog += dialog.Heading( path.ToString() );
|
||||
|
||||
if( wxID_CANCEL == pxIssueConfirmation( dialog, MsgButtons().Custom(_("Create")).Cancel(), L"CreateNewFolder" ) )
|
||||
return false;
|
||||
}
|
||||
path.Mkdir();
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
FirstTimeWizard::FirstTimeWizard( wxWindow* parent )
|
||||
: wxWizard( parent, wxID_ANY, _("PCSX2 First Time Configuration") )
|
||||
|
@ -141,6 +181,18 @@ FirstTimeWizard::~FirstTimeWizard() throw()
|
|||
|
||||
}
|
||||
|
||||
static void _OpenConsole()
|
||||
{
|
||||
g_Conf->ProgLogBox.Visible = true;
|
||||
wxGetApp().OpenConsoleLog();
|
||||
}
|
||||
|
||||
int FirstTimeWizard::ShowModal()
|
||||
{
|
||||
if( IsDebugBuild ) wxGetApp().PostIdleMethod( _OpenConsole );
|
||||
return _parent::ShowModal();
|
||||
}
|
||||
|
||||
void FirstTimeWizard::OnDoubleClicked( wxCommandEvent& evt )
|
||||
{
|
||||
wxWindow* forwardButton = FindWindow( wxID_FORWARD );
|
||||
|
@ -166,7 +218,7 @@ void FirstTimeWizard::OnPageChanging( wxWizardEvent& evt )
|
|||
{
|
||||
if( ApplicableWizardPage* page = wxDynamicCast( GetCurrentPage(), ApplicableWizardPage ) )
|
||||
{
|
||||
if( !page->GetApplyState().ApplyAll() )
|
||||
if( !page->PrepForApply() || !page->GetApplyState().ApplyAll() )
|
||||
{
|
||||
evt.Veto();
|
||||
return;
|
||||
|
|
|
@ -26,16 +26,20 @@ static const wxWindowID pxID_CUSTOM = wxID_LOWEST - 1;
|
|||
|
||||
class FirstTimeWizard : public wxWizard
|
||||
{
|
||||
typedef wxWizard _parent;
|
||||
|
||||
protected:
|
||||
class UsermodePage : public ApplicableWizardPage
|
||||
{
|
||||
protected:
|
||||
Panels::DirPickerPanel* m_dirpick_settings;
|
||||
Panels::LanguageSelectionPanel* m_panel_LangSel;
|
||||
Panels::UsermodeSelectionPanel* m_panel_UserSel;
|
||||
Panels::DocsFolderPickerPanel* m_panel_UserSel;
|
||||
|
||||
public:
|
||||
UsermodePage( wxWizard* parent );
|
||||
virtual ~UsermodePage() throw() { }
|
||||
bool PrepForApply();
|
||||
|
||||
protected:
|
||||
void OnUsermodeChanged( wxCommandEvent& evt );
|
||||
|
@ -61,6 +65,8 @@ public:
|
|||
{
|
||||
m_panel_PluginSel.OnShown();
|
||||
}
|
||||
|
||||
int ShowModal();
|
||||
|
||||
protected:
|
||||
virtual void OnPageChanging( wxWizardEvent& evt );
|
||||
|
@ -88,7 +94,7 @@ namespace Dialogs
|
|||
class PickUserModeDialog : public BaseApplicableDialog
|
||||
{
|
||||
protected:
|
||||
Panels::UsermodeSelectionPanel* m_panel_usersel;
|
||||
Panels::DocsFolderPickerPanel* m_panel_usersel;
|
||||
Panels::LanguageSelectionPanel* m_panel_langsel;
|
||||
|
||||
public:
|
||||
|
|
|
@ -23,7 +23,7 @@ using namespace Panels;
|
|||
Dialogs::PickUserModeDialog::PickUserModeDialog( wxWindow* parent )
|
||||
: BaseApplicableDialog( parent, _("PCSX2 First Time configuration"), wxVERTICAL )
|
||||
{
|
||||
m_panel_usersel = new UsermodeSelectionPanel( this, false );
|
||||
m_panel_usersel = new DocsFolderPickerPanel( this, false );
|
||||
m_panel_langsel = new LanguageSelectionPanel( this );
|
||||
|
||||
*this += new pxStaticHeading( this, _("PCSX2 is starting from a new or unknown folder and needs to be configured.") );
|
||||
|
|
|
@ -0,0 +1,391 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "App.h"
|
||||
|
||||
|
||||
// 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
|
||||
// need some kind of additional low-level ability.
|
||||
void SysExecEvent::_DoInvoke()
|
||||
{
|
||||
}
|
||||
|
||||
void SysExecEvent::SetException( BaseException* ex )
|
||||
{
|
||||
if( !ex ) return;
|
||||
|
||||
const wxString& prefix( wxsFormat(L"(%s) ", GetEventName()) );
|
||||
ex->DiagMsg() = prefix + ex->DiagMsg();
|
||||
//ex->UserMsg() = prefix + ex->UserMsg();
|
||||
|
||||
if( m_sync )
|
||||
{
|
||||
// Throws the exception inline with the message handler (this makes the exception
|
||||
// look like it was thrown quite naturally).
|
||||
m_sync->SetException( ex );
|
||||
}
|
||||
else
|
||||
{
|
||||
// transport the exception to the main thread, since the message is fully
|
||||
// asynchronous. Message is sent as a non-blocking action since proper handling
|
||||
// of user errors on async messages is *usually* to log/ignore it (hah), or to
|
||||
// suspend emulation and issue a dialog box to the user.
|
||||
|
||||
wxGetApp().PostEvent( pxExceptionEvent( ex ) );
|
||||
}
|
||||
}
|
||||
|
||||
void SysExecEvent::SetException( const BaseException& ex )
|
||||
{
|
||||
SetException( ex.Clone() );
|
||||
}
|
||||
|
||||
|
||||
// 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()
|
||||
{
|
||||
//pxAssumeDev( !IsBeingDeleted(), "Attempted to process a deleted SysExecutor event." );
|
||||
AffinityAssert_AllowFrom_SysExecutor();
|
||||
try {
|
||||
_DoInvoke();
|
||||
}
|
||||
catch( BaseException& ex )
|
||||
{
|
||||
SetException( ex );
|
||||
}
|
||||
catch( std::runtime_error& ex )
|
||||
{
|
||||
SetException( new Exception::RuntimeError(ex) );
|
||||
}
|
||||
|
||||
PostResult();
|
||||
}
|
||||
|
||||
void SysExecEvent::PostResult() const
|
||||
{
|
||||
if( m_sync ) m_sync->PostResult();
|
||||
}
|
||||
|
||||
pxEvtHandler::pxEvtHandler()
|
||||
{
|
||||
AtomicExchange( m_Quitting, false );
|
||||
}
|
||||
|
||||
void pxEvtHandler::ShutdownQueue()
|
||||
{
|
||||
if( m_Quitting ) return;
|
||||
AtomicExchange( m_Quitting, true );
|
||||
m_wakeup.Post();
|
||||
}
|
||||
|
||||
struct ScopedThreadCancelDisable
|
||||
{
|
||||
ScopedThreadCancelDisable()
|
||||
{
|
||||
int oldstate;
|
||||
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
|
||||
}
|
||||
|
||||
~ScopedThreadCancelDisable() throw()
|
||||
{
|
||||
int oldstate;
|
||||
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
|
||||
}
|
||||
};
|
||||
|
||||
void pxEvtHandler::ProcessPendingEvents()
|
||||
{
|
||||
ScopedLock synclock( m_mtx_pending );
|
||||
|
||||
pxEvtList::iterator node;
|
||||
while( node = m_pendingEvents.begin(), node != m_pendingEvents.end() )
|
||||
{
|
||||
ScopedPtr<SysExecEvent> deleteMe(wx_static_cast(SysExecEvent *, *node));
|
||||
|
||||
m_pendingEvents.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.
|
||||
|
||||
synclock.Release();
|
||||
|
||||
if( deleteMe->AllowCancelOnExit() )
|
||||
deleteMe->InvokeAction();
|
||||
else
|
||||
{
|
||||
ScopedThreadCancelDisable thr_cancel_scope;
|
||||
deleteMe->InvokeAction();
|
||||
}
|
||||
|
||||
synclock.Acquire();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn( L"(pxEvtHandler:Skipping Event) %s", deleteMe->GetEventName().c_str() );
|
||||
deleteMe->PostResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This method is provided for wxWidgets API conformance.
|
||||
void pxEvtHandler::AddPendingEvent( SysExecEvent& evt )
|
||||
{
|
||||
PostEvent( evt );
|
||||
}
|
||||
|
||||
void pxEvtHandler::PostEvent( SysExecEvent* evt )
|
||||
{
|
||||
if( !evt ) return;
|
||||
|
||||
if( m_Quitting )
|
||||
{
|
||||
evt->PostResult();
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedLock synclock( m_mtx_pending );
|
||||
|
||||
DbgCon.WriteLn( L"(%s) Posting event: %s (queue count=%d)", GetEventHandlerName().c_str(), evt->GetEventName().c_str(), m_pendingEvents.size() );
|
||||
|
||||
m_pendingEvents.push_back( evt );
|
||||
if( m_pendingEvents.size() == 1)
|
||||
m_wakeup.Post();
|
||||
}
|
||||
|
||||
void pxEvtHandler::PostEvent( const SysExecEvent& evt )
|
||||
{
|
||||
if( m_Quitting )
|
||||
{
|
||||
evt.PostResult();
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedLock synclock( m_mtx_pending );
|
||||
m_pendingEvents.push_back( evt.Clone() );
|
||||
if( m_pendingEvents.size() == 1)
|
||||
m_wakeup.Post();
|
||||
}
|
||||
|
||||
void pxEvtHandler::ProcessEvent( SysExecEvent& evt )
|
||||
{
|
||||
if( wxThread::GetCurrentId() != m_OwnerThreadId )
|
||||
{
|
||||
SynchronousActionState sync;
|
||||
evt.SetSyncState( sync );
|
||||
PostEvent( 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;
|
||||
}
|
||||
|
||||
void pxEvtHandler::ProcessEvent( SysExecEvent* evt )
|
||||
{
|
||||
if( !evt ) return;
|
||||
|
||||
if( wxThread::GetCurrentId() != m_OwnerThreadId )
|
||||
{
|
||||
SynchronousActionState sync;
|
||||
evt->SetSyncState( sync );
|
||||
PostEvent( evt );
|
||||
sync.WaitForResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
ScopedPtr<SysExecEvent> deleteMe( evt );
|
||||
deleteMe->InvokeAction();
|
||||
}
|
||||
}
|
||||
|
||||
void pxEvtHandler::Idle()
|
||||
{
|
||||
DoIdle();
|
||||
m_wakeup.WaitWithoutYield();
|
||||
}
|
||||
|
||||
void pxEvtHandler::SetActiveThread()
|
||||
{
|
||||
m_OwnerThreadId = wxThread::GetCurrentId();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// WaitingForThreadedTaskDialog
|
||||
// --------------------------------------------------------------------------------------
|
||||
class WaitingForThreadedTaskDialog
|
||||
: public wxDialogWithHelpers
|
||||
{
|
||||
private:
|
||||
typedef wxDialogWithHelpers _parent;
|
||||
|
||||
protected:
|
||||
PersistentThread* m_thread;
|
||||
|
||||
public:
|
||||
WaitingForThreadedTaskDialog( PersistentThread* thr, wxWindow* parent, const wxString& title, const wxString& content );
|
||||
virtual ~WaitingForThreadedTaskDialog() throw() {}
|
||||
|
||||
protected:
|
||||
void OnCancel_Clicked( wxCommandEvent& evt );
|
||||
void OnTerminateApp_Clicked( wxCommandEvent& evt );
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// WaitingForThreadedTaskDialog Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
WaitingForThreadedTaskDialog::WaitingForThreadedTaskDialog( PersistentThread* thr, wxWindow* parent, const wxString& title, const wxString& content )
|
||||
: wxDialogWithHelpers( parent, title, wxVERTICAL )
|
||||
{
|
||||
m_thread = thr;
|
||||
m_idealWidth = 500;
|
||||
|
||||
*this += Heading( content );
|
||||
*this += 15;
|
||||
*this += Heading( _("Press Cancel to attempt to cancel the action.") );
|
||||
*this += Heading( _("Press Terminate to kill PCSX2 immediately.") );
|
||||
|
||||
*this += new wxButton( this, wxID_CANCEL );
|
||||
*this += new wxButton( this, wxID_ANY, _("Terminate App") );
|
||||
}
|
||||
|
||||
void WaitingForThreadedTaskDialog::OnCancel_Clicked( wxCommandEvent& evt )
|
||||
{
|
||||
evt.Skip();
|
||||
if( !m_thread ) return;
|
||||
m_thread->Cancel( false );
|
||||
|
||||
if( wxWindow* cancel = FindWindowById( wxID_CANCEL ) ) cancel->Disable();
|
||||
}
|
||||
|
||||
void WaitingForThreadedTaskDialog::OnTerminateApp_Clicked( wxCommandEvent& evt )
|
||||
{
|
||||
// (note: SIGTERM is a "handled" kill that performs shutdown stuff, which typically just crashes anyway)
|
||||
wxKill( wxGetProcessId(), wxSIGKILL );
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ExecutorThread Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
ExecutorThread::ExecutorThread( pxEvtHandler* evthandler )
|
||||
{
|
||||
m_EvtHandler = evthandler;
|
||||
}
|
||||
|
||||
void ExecutorThread::ShutdownQueue()
|
||||
{
|
||||
if( !m_EvtHandler || m_EvtHandler->IsShuttingDown() ) return;
|
||||
m_EvtHandler->ShutdownQueue();
|
||||
Block();
|
||||
}
|
||||
|
||||
void ExecutorThread::PostEvent( SysExecEvent* evt )
|
||||
{
|
||||
if( !pxAssert( m_EvtHandler ) ) return;
|
||||
m_EvtHandler->PostEvent( evt );
|
||||
}
|
||||
|
||||
void ExecutorThread::PostEvent( const SysExecEvent& evt )
|
||||
{
|
||||
if( !pxAssert( m_EvtHandler ) ) return;
|
||||
m_EvtHandler->PostEvent( evt );
|
||||
}
|
||||
|
||||
void ExecutorThread::ProcessEvent( SysExecEvent* evt )
|
||||
{
|
||||
if( m_EvtHandler )
|
||||
m_EvtHandler->ProcessEvent( evt );
|
||||
else
|
||||
{
|
||||
ScopedPtr<SysExecEvent> deleteMe( evt );
|
||||
deleteMe->InvokeAction();
|
||||
}
|
||||
}
|
||||
|
||||
void ExecutorThread::ProcessEvent( SysExecEvent& evt )
|
||||
{
|
||||
if( m_EvtHandler )
|
||||
m_EvtHandler->ProcessEvent( evt );
|
||||
else
|
||||
evt.InvokeAction();
|
||||
}
|
||||
|
||||
void ExecutorThread::OnStart()
|
||||
{
|
||||
//if( !m_ExecutorTimer )
|
||||
// m_ExecutorTimer = new wxTimer( wxTheApp, pxEvt_ThreadTaskTimeout_SysExec );
|
||||
|
||||
m_name = L"SysExecutor";
|
||||
_parent::OnStart();
|
||||
}
|
||||
|
||||
void ExecutorThread::ExecuteTaskInThread()
|
||||
{
|
||||
if( !pxAssertDev( m_EvtHandler, "Gimme a damn Event Handler first, object whore." ) ) return;
|
||||
|
||||
m_EvtHandler->SetActiveThread();
|
||||
|
||||
while( true )
|
||||
{
|
||||
if( !pxAssertDev( m_EvtHandler, "Event handler has been deallocated during SysExecutor thread execution." ) ) return;
|
||||
|
||||
m_EvtHandler->Idle();
|
||||
m_EvtHandler->ProcessPendingEvents();
|
||||
if( m_EvtHandler->IsShuttingDown() ) break;
|
||||
}
|
||||
}
|
||||
|
||||
void ExecutorThread::OnCleanupInThread()
|
||||
{
|
||||
//wxGetApp().PostCommand( m_task, pxEvt_ThreadTaskComplete );
|
||||
_parent::OnCleanupInThread();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Threaded Event Handlers (Pcsx2App)
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
// 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.
|
||||
void Pcsx2App::OnSysExecutorTaskTimeout( wxTimerEvent& evt )
|
||||
{
|
||||
if( !SysExecutorThread.IsRunning() ) return;
|
||||
|
||||
//BaseThreadedInvocation* task = SysExecutorThread.GetTask();
|
||||
//if( !task ) return;
|
||||
|
||||
//task->ShowModalStatus();
|
||||
}
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "MainFrame.h"
|
||||
#include "GSFrame.h"
|
||||
|
||||
#include "GS.h"
|
||||
#include "MSWstuff.h"
|
||||
|
||||
|
@ -295,6 +297,8 @@ void GSFrame::CoreThread_OnSuspended()
|
|||
// Could stop the timer outright here, tho no harm in having an occasional
|
||||
// update here or there, just in case some state info changes while emu is suspended.
|
||||
m_timer_UpdateTitle.Start( TitleBarUpdateMs );
|
||||
|
||||
if( g_Conf->GSWindow.CloseOnEsc ) Hide();
|
||||
}
|
||||
|
||||
// overrides base Show behavior.
|
||||
|
@ -331,7 +335,7 @@ void GSFrame::AppStatusEvent_OnSettingsApplied()
|
|||
{
|
||||
if( IsBeingDeleted() ) return;
|
||||
ShowFullScreen( g_Conf->GSWindow.DefaultToFullscreen );
|
||||
Show( !g_Conf->GSWindow.CloseOnEsc || ((g_plugins==NULL) || !SysHasValidState()) );
|
||||
Show( !g_Conf->GSWindow.CloseOnEsc || !CorePlugins.IsOpen(PluginId_GS) || !SysHasValidState() );
|
||||
|
||||
if( wxStaticText* label = GetLabel_OutputDisabled() )
|
||||
label->Show( !EmuConfig.GS.DisableOutput );
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
|
||||
/* 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 "App.h"
|
||||
#include "CpuUsageProvider.h"
|
||||
|
||||
|
||||
enum LimiterModeType
|
||||
{
|
||||
Limit_Nominal,
|
||||
Limit_Turbo,
|
||||
Limit_Slomo,
|
||||
};
|
||||
|
||||
extern LimiterModeType g_LimiterMode;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// GSPanel
|
||||
// --------------------------------------------------------------------------------------
|
||||
class GSPanel : public wxWindow, public EventListener_AppStatus
|
||||
{
|
||||
typedef wxWindow _parent;
|
||||
|
||||
protected:
|
||||
AcceleratorDictionary m_Accels;
|
||||
wxTimer m_HideMouseTimer;
|
||||
bool m_CursorShown;
|
||||
bool m_HasFocus;
|
||||
|
||||
public:
|
||||
GSPanel( wxWindow* parent );
|
||||
virtual ~GSPanel() throw();
|
||||
|
||||
void DoResize();
|
||||
void DoShowMouse();
|
||||
|
||||
protected:
|
||||
void AppStatusEvent_OnSettingsApplied();
|
||||
|
||||
#ifdef __WXMSW__
|
||||
virtual WXLRESULT MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam);
|
||||
#endif
|
||||
|
||||
void InitDefaultAccelerators();
|
||||
|
||||
void OnCloseWindow( wxCloseEvent& evt );
|
||||
void OnResize(wxSizeEvent& event);
|
||||
void OnShowMouse( wxMouseEvent& evt );
|
||||
void OnHideMouseTimeout( wxTimerEvent& evt );
|
||||
void OnKeyDown( wxKeyEvent& evt );
|
||||
void OnFocus( wxFocusEvent& evt );
|
||||
void OnFocusLost( wxFocusEvent& evt );
|
||||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// GSFrame
|
||||
// --------------------------------------------------------------------------------------
|
||||
class GSFrame : public wxFrame,
|
||||
public EventListener_AppStatus,
|
||||
public EventListener_CoreThread
|
||||
{
|
||||
typedef wxFrame _parent;
|
||||
|
||||
protected:
|
||||
wxTimer m_timer_UpdateTitle;
|
||||
wxWindowID m_id_gspanel;
|
||||
wxWindowID m_id_OutputDisabled;
|
||||
wxStaticText* m_label_Disabled;
|
||||
wxStatusBar* m_statusbar;
|
||||
|
||||
CpuUsageProvider m_CpuUsage;
|
||||
|
||||
public:
|
||||
GSFrame(wxWindow* parent, const wxString& title);
|
||||
virtual ~GSFrame() throw();
|
||||
|
||||
GSPanel* GetViewport();
|
||||
void SetFocus();
|
||||
bool Show( bool shown=true );
|
||||
wxStaticText* GetLabel_OutputDisabled() const;
|
||||
|
||||
protected:
|
||||
void OnCloseWindow( wxCloseEvent& evt );
|
||||
void OnMove( wxMoveEvent& evt );
|
||||
void OnResize( wxSizeEvent& evt );
|
||||
void OnActivate( wxActivateEvent& evt );
|
||||
void OnUpdateTitle( wxTimerEvent& evt );
|
||||
|
||||
void AppStatusEvent_OnSettingsApplied();
|
||||
void CoreThread_OnResumed();
|
||||
void CoreThread_OnSuspended();
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// s* macros! ['s' stands for 'shortcut']
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Use these for "silent fail" invocation of PCSX2 Application-related constructs. If the
|
||||
// construct (albeit wxApp, MainFrame, CoreThread, etc) is null, the requested method will
|
||||
// not be invoked, and an optional "else" clause can be affixed for handling the end case.
|
||||
//
|
||||
// See App.h (sApp) for more details.
|
||||
//
|
||||
#define sGSFrame \
|
||||
if( GSFrame* __gsframe_ = wxGetApp().GetGsFramePtr() ) (*__gsframe_)
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "MainFrame.h"
|
||||
#include "GSFrame.h"
|
||||
|
||||
#include "HostGui.h"
|
||||
#include "AppSaveStates.h"
|
||||
#include "GS.h"
|
||||
|
@ -91,7 +93,7 @@ namespace Implementations
|
|||
g_Conf->EmuOptions.GS.LimitScalar = g_Conf->Framerate.TurboScalar;
|
||||
Console.WriteLn("(FrameLimiter) Turbo ENABLED." );
|
||||
}
|
||||
pauser.Resume();
|
||||
pauser.AllowResume();
|
||||
}
|
||||
|
||||
void Framelimiter_SlomoToggle()
|
||||
|
@ -117,7 +119,7 @@ namespace Implementations
|
|||
Console.WriteLn("(FrameLimiter) SlowMotion ENABLED." );
|
||||
g_Conf->EmuOptions.GS.FrameLimitEnable = true;
|
||||
}
|
||||
pauser.Resume();
|
||||
pauser.AllowResume();
|
||||
}
|
||||
|
||||
void Framelimiter_MasterToggle()
|
||||
|
@ -126,7 +128,7 @@ namespace Implementations
|
|||
g_Conf->EmuOptions.GS.FrameLimitEnable = !g_Conf->EmuOptions.GS.FrameLimitEnable;
|
||||
GSsetVsync( g_Conf->EmuOptions.GS.FrameLimitEnable && g_Conf->EmuOptions.GS.VsyncEnable );
|
||||
Console.WriteLn("(FrameLimiter) %s.", g_Conf->EmuOptions.GS.FrameLimitEnable ? "ENABLED" : "DISABLED" );
|
||||
pauser.Resume();
|
||||
pauser.AllowResume();
|
||||
}
|
||||
|
||||
void Sys_Suspend()
|
||||
|
@ -142,13 +144,11 @@ namespace Implementations
|
|||
|
||||
void Sys_TakeSnapshot()
|
||||
{
|
||||
GSmakeSnapshot( g_Conf->Folders.Snapshots.ToAscii().data() );
|
||||
GSmakeSnapshot( g_Conf->Folders.Snapshots.ToAscii() );
|
||||
}
|
||||
|
||||
void Sys_RenderToggle()
|
||||
{
|
||||
if( g_plugins == NULL ) return;
|
||||
|
||||
SaveSinglePluginHelper helper( PluginId_GS );
|
||||
renderswitch = !renderswitch;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "App.h"
|
||||
#include "MainFrame.h"
|
||||
#include "IsoDropTarget.h"
|
||||
|
||||
#include "Dialogs/ModalPopups.h"
|
||||
|
@ -34,7 +35,7 @@ wxString GetMsg_ConfirmSysReset()
|
|||
|
||||
bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames)
|
||||
{
|
||||
ScopedCoreThreadSuspend stopped_core;
|
||||
ScopedCoreThreadPopup stopped_core;
|
||||
|
||||
if( filenames.GetCount() > 1 )
|
||||
{
|
||||
|
@ -53,7 +54,7 @@ bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filen
|
|||
wxFileInputStream filechk( filenames[0] );
|
||||
|
||||
if( !filechk.IsOk() )
|
||||
throw Exception::CreateStream( filenames[0] );
|
||||
throw Exception::CannotCreateStream( filenames[0] );
|
||||
|
||||
u8 ident[16];
|
||||
filechk.Read( ident, 16 );
|
||||
|
@ -82,8 +83,9 @@ bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filen
|
|||
{
|
||||
sApp.SysExecute( g_Conf->CdvdSource, g_Conf->CurrentELF );
|
||||
}
|
||||
else
|
||||
stopped_core.AllowResume();
|
||||
|
||||
stopped_core.Resume();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -97,45 +99,18 @@ bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filen
|
|||
// hack ;)
|
||||
|
||||
isoFile iso;
|
||||
memzero( iso );
|
||||
iso.handle = _openfile( filenames[0].ToUTF8(), O_RDONLY);
|
||||
memzero(iso);
|
||||
iso.handle = _openfile(filenames[0].ToUTF8(), O_RDONLY);
|
||||
|
||||
if( iso.handle == NULL )
|
||||
throw Exception::CreateStream( filenames[0] );
|
||||
throw Exception::CannotCreateStream( filenames[0] );
|
||||
|
||||
if (isoDetect(&iso))
|
||||
{
|
||||
Console.WriteLn( L"(Drag&Drop) Found valid ISO file type!" );
|
||||
|
||||
wxWindowID result = wxID_RESET;
|
||||
|
||||
if( SysHasValidState() )
|
||||
{
|
||||
wxDialogWithHelpers dialog( m_WindowBound, _("Confirm PS2 Reset"), wxVERTICAL );
|
||||
|
||||
dialog += dialog.Heading(_("You have dropped the following ISO image into PCSX2:\n\n") +
|
||||
filenames[0] + L"\n\n" +
|
||||
_("Do you want to swap discs or boot the new image (via system reset)?")
|
||||
);
|
||||
|
||||
result = pxIssueConfirmation( dialog, MsgButtons().Reset().Cancel().Custom(_("Swap Disc")), L"DragDrop:BootIso" );
|
||||
}
|
||||
|
||||
if( result != wxID_CANCEL )
|
||||
{
|
||||
SysUpdateIsoSrcFile( filenames[0] );
|
||||
if( result != wxID_RESET )
|
||||
{
|
||||
CoreThread.ChangeCdvdSource( CDVDsrc_Iso );
|
||||
}
|
||||
else
|
||||
{
|
||||
sApp.SysExecute( CDVDsrc_Iso );
|
||||
}
|
||||
}
|
||||
SwapOrReset_Iso(m_WindowBound, stopped_core, filenames[0], _("You have dropped the following ISO image into PCSX2:\n\n"));
|
||||
}
|
||||
|
||||
_closefile( iso.handle );
|
||||
stopped_core.Resume();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -34,3 +34,7 @@ public:
|
|||
|
||||
virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames);
|
||||
};
|
||||
|
||||
|
||||
extern wxString GetMsg_ConfirmSysReset();
|
||||
extern wxWindowID SwapOrReset_Iso( wxWindow* owner, IScopedCoreThread& core_control, const wxString& isoFilename, const wxString& descpart1 );
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "MainFrame.h"
|
||||
#include "GSFrame.h"
|
||||
|
||||
#include "MSWstuff.h"
|
||||
#include <wx/listbook.h>
|
||||
#include <wx/listctrl.h>
|
||||
|
|
|
@ -92,15 +92,16 @@ void MainEmuFrame::OnCloseWindow(wxCloseEvent& evt)
|
|||
|
||||
//evt.Veto( true );
|
||||
|
||||
if( StateCopy_InvokeOnSaveComplete( new InvokeAction_MenuCommand( MenuId_Exit ) ) ) return;
|
||||
}
|
||||
|
||||
m_menuCDVD.Remove( MenuId_IsoSelector );
|
||||
//m_menuCDVD.Delete( MenuId_IsoSelector );
|
||||
|
||||
wxGetApp().PrepForExit();
|
||||
sApp.OnMainFrameClosed( GetId() );
|
||||
|
||||
if( m_menubar.FindItem(MenuId_IsoSelector) )
|
||||
m_menuCDVD.Remove(MenuId_IsoSelector);
|
||||
|
||||
RemoveEventHandler( &wxGetApp().GetRecentIsoManager() );
|
||||
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
|
@ -169,14 +170,14 @@ void MainEmuFrame::ConnectMenus()
|
|||
ConnectMenu( MenuId_PluginBase_Settings + (i*PluginMenuId_Interval), Menu_ConfigPlugin_Click);
|
||||
|
||||
ConnectMenu( MenuId_Boot_CDVD, Menu_BootCdvd_Click );
|
||||
ConnectMenu( MenuId_Boot_CDVD2, Menu_BootCdvd2_Click );
|
||||
ConnectMenu( MenuId_Boot_ELF, Menu_OpenELF_Click );
|
||||
ConnectMenu( MenuId_IsoBrowse, Menu_IsoBrowse_Click );
|
||||
ConnectMenu( MenuId_SkipBiosToggle, Menu_SkipBiosToggle_Click );
|
||||
ConnectMenu( MenuId_EnablePatches, Menu_EnablePatches_Click );
|
||||
ConnectMenu( MenuId_Exit, Menu_Exit_Click );
|
||||
|
||||
ConnectMenu( MenuId_Sys_SuspendResume, Menu_SuspendResume_Click );
|
||||
ConnectMenu( MenuId_Sys_Reset, Menu_SysReset_Click );
|
||||
ConnectMenu( MenuId_Sys_Restart, Menu_SysReset_Click );
|
||||
ConnectMenu( MenuId_Sys_Shutdown, Menu_SysShutdown_Click );
|
||||
|
||||
ConnectMenu( MenuId_State_LoadOther, Menu_LoadStateOther_Click );
|
||||
|
@ -229,14 +230,12 @@ void MainEmuFrame::DispatchEvent( const PluginEventType& plugin_evt )
|
|||
}
|
||||
else if( plugin_evt == CorePlugins_Loaded )
|
||||
{
|
||||
if( !pxAssertDev( g_plugins!=NULL, wxNullChar ) ) return;
|
||||
|
||||
for( int i=0; i<PluginId_Count; ++i )
|
||||
m_PluginMenuPacks[i].OnLoaded();
|
||||
|
||||
// bleh this makes the menu too cluttered. --air
|
||||
//m_menuCDVD.SetLabel( MenuId_Src_Plugin, wxsFormat( L"%s (%s)", _("Plugin"),
|
||||
// g_plugins->GetName( PluginId_CDVD ).c_str() ) );
|
||||
// GetCorePlugins().GetName( PluginId_CDVD ).c_str() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,9 +303,9 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
|
|||
// Initial menubar setup. This needs to be done first so that the menu bar's visible size
|
||||
// can be factored into the window size (which ends up being background+status+menus)
|
||||
|
||||
m_menubar.Append( &m_menuBoot, _("&Boot") );
|
||||
m_menubar.Append( &m_menuCDVD, _("CD&VD") );
|
||||
//m_menubar.Append( &m_menuBoot, _("&Boot") );
|
||||
m_menubar.Append( &m_menuSys, _("&System") );
|
||||
m_menubar.Append( &m_menuCDVD, _("CD&VD") );
|
||||
m_menubar.Append( &m_menuConfig, _("&Config") );
|
||||
m_menubar.Append( &m_menuMisc, _("&Misc") );
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
|
@ -364,37 +363,18 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
|
|||
InitLogBoxPosition( g_Conf->ProgLogBox );
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Some of the items in the System menu are configured by the UpdateCoreStatus() method.
|
||||
|
||||
m_menuSys.Append(MenuId_Boot_CDVD, _("Initializing..."));
|
||||
|
||||
m_menuBoot.Append(MenuId_Boot_CDVD, _("Run CDVD"),
|
||||
_("For booting DVD discs or Isos, depending on the configured CDVD source."));
|
||||
m_menuSys.Append(MenuId_Boot_CDVD2, _("Initializing..."));
|
||||
|
||||
m_menuBoot.Append(MenuId_Boot_ELF, _("Run ELF File..."),
|
||||
_("For running raw binaries directly"));
|
||||
|
||||
m_menuBoot.AppendSeparator();
|
||||
m_menuBoot.Append(MenuId_Exit, _("Exit"),
|
||||
_("Closing PCSX2 may be hazardous to your health"));
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
wxMenu& isoRecents( wxGetApp().GetRecentIsoMenu() );
|
||||
|
||||
//m_menuCDVD.AppendSeparator();
|
||||
m_menuCDVD.Append( MenuId_IsoSelector, _("Iso Selector"), &isoRecents );
|
||||
m_menuCDVD.Append( GetPluginMenuId_Settings(PluginId_CDVD), _("Plugin Menu"), m_PluginMenuPacks[PluginId_CDVD] );
|
||||
|
||||
m_menuCDVD.AppendSeparator();
|
||||
m_menuCDVD.Append( MenuId_Src_Iso, _("Iso"), _("Makes the specified ISO image the CDVD source."), wxITEM_RADIO );
|
||||
m_menuCDVD.Append( MenuId_Src_Plugin, _("Plugin"), _("Uses an external plugin as the CDVD source."), wxITEM_RADIO );
|
||||
m_menuCDVD.Append( MenuId_Src_NoDisc, _("No disc"), _("Use this to boot into your virtual PS2's BIOS configuration."), wxITEM_RADIO );
|
||||
|
||||
m_menuCDVD.AppendSeparator();
|
||||
m_menuCDVD.Append( MenuId_SkipBiosToggle,_("Enable Skip BIOS Hack"),
|
||||
_("Skips PS2 splash screens when booting from Iso or CDVD media"), wxITEM_CHECK );
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
m_menuSys.Append(MenuId_Sys_SuspendResume, _("Suspend") )->Enable( SysHasValidState() );
|
||||
m_menuSys.Append(MenuId_Boot_ELF, _("Run ELF..."),
|
||||
_("For running raw PS2 binaries directly"));
|
||||
|
||||
m_menuSys.AppendSeparator();
|
||||
m_menuSys.Append(MenuId_Sys_SuspendResume, _("Initializing..."));
|
||||
m_menuSys.AppendSeparator();
|
||||
|
||||
//m_menuSys.Append(MenuId_Sys_Close, _("Close"),
|
||||
// _("Stops emulation and closes the GS window."));
|
||||
|
@ -408,12 +388,30 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
|
|||
wxEmptyString, wxITEM_CHECK);
|
||||
|
||||
m_menuSys.AppendSeparator();
|
||||
m_menuSys.Append(MenuId_Sys_Reset, _("Reset"),
|
||||
_("Resets the VM state and re-runs current CDVD image."));
|
||||
|
||||
m_menuSys.Append(MenuId_Sys_Shutdown, _("Shutdown"),
|
||||
_("Wipes all internal VM states and shuts down plugins."));
|
||||
|
||||
m_menuSys.Append(MenuId_Exit, _("Exit"),
|
||||
_("Closing PCSX2 may be hazardous to your health"));
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
wxMenu& isoRecents( wxGetApp().GetRecentIsoMenu() );
|
||||
|
||||
//m_menuCDVD.AppendSeparator();
|
||||
m_menuCDVD.Append( MenuId_IsoSelector, _("Iso Selector"), &isoRecents );
|
||||
m_menuCDVD.Append( GetPluginMenuId_Settings(PluginId_CDVD), _("Plugin Menu"), m_PluginMenuPacks[PluginId_CDVD] );
|
||||
|
||||
m_menuCDVD.AppendSeparator();
|
||||
m_menuCDVD.Append( MenuId_Src_Iso, _("Iso"), _("Makes the specified ISO image the CDVD source."), wxITEM_RADIO );
|
||||
m_menuCDVD.Append( MenuId_Src_Plugin, _("Plugin"), _("Uses an external plugin as the CDVD source."), wxITEM_RADIO );
|
||||
m_menuCDVD.Append( MenuId_Src_NoDisc, _("No disc"), _("Use this to boot into your virtual PS2's BIOS configuration."), wxITEM_RADIO );
|
||||
|
||||
//m_menuCDVD.AppendSeparator();
|
||||
//m_menuCDVD.Append( MenuId_SkipBiosToggle,_("Enable BOOT2 injection"),
|
||||
// _("Skips PS2 splash screens when booting from Iso or DVD media"), wxITEM_CHECK );
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
m_menuConfig.Append(MenuId_Config_SysSettings, _("Emulation &Settings") );
|
||||
|
@ -478,7 +476,9 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
|
|||
|
||||
Connect( wxEVT_ACTIVATE, wxActivateEventHandler (MainEmuFrame::OnActivate) );
|
||||
|
||||
PushEventHandler( &wxGetApp().GetRecentIsoManager() );
|
||||
SetDropTarget( new IsoDropTarget( this ) );
|
||||
ApplyCoreStatus();
|
||||
}
|
||||
|
||||
MainEmuFrame::~MainEmuFrame() throw()
|
||||
|
@ -486,7 +486,7 @@ MainEmuFrame::~MainEmuFrame() throw()
|
|||
if( m_RestartEmuOnDelete )
|
||||
{
|
||||
sApp.SetExitOnFrameDelete( false );
|
||||
sApp.PostMethod( &Pcsx2App::DetectCpuAndUserMode );
|
||||
sApp.PostAppMethod( &Pcsx2App::DetectCpuAndUserMode );
|
||||
sApp.WipeUserModeSettings();
|
||||
}
|
||||
}
|
||||
|
@ -520,38 +520,71 @@ void MainEmuFrame::ApplyCoreStatus()
|
|||
{
|
||||
wxMenuBar& menubar( *GetMenuBar() );
|
||||
|
||||
wxMenuItem& susres( *menubar.FindItem( MenuId_Sys_SuspendResume ) );
|
||||
if( !pxAssertMsg( &susres!=NULL, "Suspend/Resume Menubar Item is NULL!" ) ) return;
|
||||
wxMenuItem& susres (*menubar.FindItem( MenuId_Sys_SuspendResume ));
|
||||
wxMenuItem& cdvd (*menubar.FindItem( MenuId_Boot_CDVD ));
|
||||
wxMenuItem& cdvd2 (*menubar.FindItem( MenuId_Boot_CDVD2 ));
|
||||
|
||||
if( !pxAssertMsg( (&susres) && (&cdvd) && (&cdvd2), "Unexpected NULL Menubar Item!" ) ) return;
|
||||
|
||||
wxMenuItem* restart = menubar.FindItem( MenuId_Sys_Restart );
|
||||
|
||||
if( SysHasValidState() )
|
||||
{
|
||||
susres.Enable();
|
||||
if( CoreThread.IsOpen() )
|
||||
{
|
||||
susres.SetHelp( _("Safely pauses emulation and preserves the PS2 state.") );
|
||||
susres.SetText( _("Suspend") );
|
||||
susres.SetText(_("Suspend"));
|
||||
susres.SetHelp(_("Safely pauses emulation and preserves the PS2 state."));
|
||||
}
|
||||
else
|
||||
{
|
||||
susres.SetHelp( _("Resumes the suspended emulation state.") );
|
||||
susres.SetText( _("Resume") );
|
||||
susres.SetText(_("Resume"));
|
||||
susres.SetHelp(_("Resumes the suspended emulation state."));
|
||||
}
|
||||
|
||||
if( restart )
|
||||
{
|
||||
restart->SetText(_("Restart"));
|
||||
restart->SetHelp(_("Simulates hardware reset of the PS2 virtual machine."));
|
||||
}
|
||||
|
||||
cdvd.SetText(_("Reboot CDVD (full)"));
|
||||
cdvd.SetHelp(_("Hard reset of the active VM."));
|
||||
|
||||
cdvd2.SetText(_("Reboot CDVD (fast)"));
|
||||
cdvd2.SetHelp(_("Reboot using BOOT2 injection (skips splash screens)"));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
susres.Enable( false );
|
||||
susres.SetText(_("Suspend/Resume"));
|
||||
susres.SetHelp( _("No emulation state is active; cannot suspend or resume.") );
|
||||
|
||||
if( restart )
|
||||
{
|
||||
restart->Enable( false );
|
||||
restart->SetHelp( _("No emulation state is active; boot something first.") );
|
||||
}
|
||||
|
||||
cdvd.SetText(_("Boot CDVD (full)"));
|
||||
cdvd.SetHelp(_("Boot the VM using the current DVD or Iso source media"));
|
||||
|
||||
cdvd2.SetText(_("Boot CDVD (fast)"));
|
||||
cdvd2.SetHelp(_("Use BOOT2 injection to skip PS2 startup and splash screens"));
|
||||
|
||||
// Old style...
|
||||
//restart.SetHelp(_("Starts execution of the PS2 virtual machine."));
|
||||
//restart.SetText(_("Start"));
|
||||
}
|
||||
|
||||
menubar.Enable( MenuId_Sys_Reset, true );
|
||||
menubar.Enable( MenuId_Sys_Shutdown, SysHasValidState() || (g_plugins!=NULL) );
|
||||
menubar.Enable( MenuId_Sys_Shutdown, SysHasValidState() || CorePlugins.AreAnyInitialized() );
|
||||
}
|
||||
|
||||
void MainEmuFrame::ApplySettings()
|
||||
{
|
||||
wxMenuBar& menubar( *GetMenuBar() );
|
||||
|
||||
menubar.Check( MenuId_SkipBiosToggle, g_Conf->EmuOptions.SkipBiosSplash );
|
||||
menubar.Check( MenuId_EnablePatches, g_Conf->EmuOptions.EnablePatches );
|
||||
menubar.Check( MenuId_CDVD_Info, g_Conf->EmuOptions.CdvdVerboseReads );
|
||||
#ifdef __LINUX__
|
||||
|
@ -615,8 +648,9 @@ void PerPluginMenuInfo::OnUnloaded()
|
|||
|
||||
void PerPluginMenuInfo::OnLoaded()
|
||||
{
|
||||
if( !CorePlugins.IsLoaded(PluginId) ) return;
|
||||
MyMenu.SetLabel( GetPluginMenuId_Name(PluginId),
|
||||
g_plugins->GetName( PluginId ) + L" " + g_plugins->GetVersion( PluginId )
|
||||
CorePlugins.GetName( PluginId ) + L" " + CorePlugins.GetVersion( PluginId )
|
||||
);
|
||||
MyMenu.Enable( GetPluginMenuId_Settings(PluginId), true );
|
||||
}
|
||||
|
|
|
@ -15,99 +15,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/docview.h>
|
||||
|
||||
#include "App.h"
|
||||
#include "AppSaveStates.h"
|
||||
#include "CpuUsageProvider.h"
|
||||
|
||||
enum LimiterModeType
|
||||
{
|
||||
Limit_Nominal,
|
||||
Limit_Turbo,
|
||||
Limit_Slomo,
|
||||
};
|
||||
|
||||
extern LimiterModeType g_LimiterMode;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// GSPanel
|
||||
// --------------------------------------------------------------------------------------
|
||||
class GSPanel : public wxWindow, public EventListener_AppStatus
|
||||
{
|
||||
typedef wxWindow _parent;
|
||||
|
||||
protected:
|
||||
AcceleratorDictionary m_Accels;
|
||||
wxTimer m_HideMouseTimer;
|
||||
bool m_CursorShown;
|
||||
bool m_HasFocus;
|
||||
|
||||
public:
|
||||
GSPanel( wxWindow* parent );
|
||||
virtual ~GSPanel() throw();
|
||||
|
||||
void DoResize();
|
||||
void DoShowMouse();
|
||||
|
||||
protected:
|
||||
void AppStatusEvent_OnSettingsApplied();
|
||||
|
||||
#ifdef __WXMSW__
|
||||
virtual WXLRESULT MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam);
|
||||
#endif
|
||||
|
||||
void InitDefaultAccelerators();
|
||||
|
||||
void OnCloseWindow( wxCloseEvent& evt );
|
||||
void OnResize(wxSizeEvent& event);
|
||||
void OnShowMouse( wxMouseEvent& evt );
|
||||
void OnHideMouseTimeout( wxTimerEvent& evt );
|
||||
void OnKeyDown( wxKeyEvent& evt );
|
||||
void OnFocus( wxFocusEvent& evt );
|
||||
void OnFocusLost( wxFocusEvent& evt );
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// GSFrame
|
||||
// --------------------------------------------------------------------------------------
|
||||
class GSFrame : public wxFrame,
|
||||
public EventListener_AppStatus,
|
||||
public EventListener_CoreThread
|
||||
{
|
||||
typedef wxFrame _parent;
|
||||
|
||||
protected:
|
||||
wxTimer m_timer_UpdateTitle;
|
||||
wxWindowID m_id_gspanel;
|
||||
wxWindowID m_id_OutputDisabled;
|
||||
wxStaticText* m_label_Disabled;
|
||||
wxStatusBar* m_statusbar;
|
||||
|
||||
CpuUsageProvider m_CpuUsage;
|
||||
|
||||
public:
|
||||
GSFrame(wxWindow* parent, const wxString& title);
|
||||
virtual ~GSFrame() throw();
|
||||
|
||||
GSPanel* GetViewport();
|
||||
void SetFocus();
|
||||
bool Show( bool shown=true );
|
||||
wxStaticText* GetLabel_OutputDisabled() const;
|
||||
|
||||
protected:
|
||||
void OnCloseWindow( wxCloseEvent& evt );
|
||||
void OnMove( wxMoveEvent& evt );
|
||||
void OnResize( wxSizeEvent& evt );
|
||||
void OnActivate( wxActivateEvent& evt );
|
||||
void OnUpdateTitle( wxTimerEvent& evt );
|
||||
|
||||
void AppStatusEvent_OnSettingsApplied();
|
||||
void CoreThread_OnResumed();
|
||||
void CoreThread_OnSuspended();
|
||||
};
|
||||
#include <wx/image.h>
|
||||
#include <wx/docview.h>
|
||||
|
||||
struct PluginMenuAddition
|
||||
{
|
||||
|
@ -162,18 +74,25 @@ public:
|
|||
operator const wxMenu*() const { return &MyMenu; }
|
||||
};
|
||||
|
||||
class InvokeAction_MenuCommand : public IActionInvocation
|
||||
// --------------------------------------------------------------------------------------
|
||||
// InvokeMenuCommand_OnSysStateUnlocked
|
||||
// --------------------------------------------------------------------------------------
|
||||
class InvokeMenuCommand_OnSysStateUnlocked
|
||||
: public IEventListener_SysState
|
||||
, public BaseDeletableObject
|
||||
{
|
||||
protected:
|
||||
MenuIdentifiers m_menu_cmd;
|
||||
|
||||
public:
|
||||
InvokeAction_MenuCommand( MenuIdentifiers menu_command )
|
||||
InvokeMenuCommand_OnSysStateUnlocked( MenuIdentifiers menu_command )
|
||||
{
|
||||
m_menu_cmd = menu_command;
|
||||
}
|
||||
|
||||
virtual ~InvokeMenuCommand_OnSysStateUnlocked() throw() {}
|
||||
|
||||
virtual void InvokeAction()
|
||||
virtual void SaveStateAction_OnCreateFinished()
|
||||
{
|
||||
wxGetApp().PostMenuAction( m_menu_cmd );
|
||||
}
|
||||
|
@ -188,10 +107,6 @@ class MainEmuFrame : public wxFrame,
|
|||
public EventListener_AppStatus
|
||||
{
|
||||
protected:
|
||||
// EventListenerHelper_Plugins<MainEmuFrame> m_listener_plugins;
|
||||
// EventListenerHelper_CoreThread<MainEmuFrame> m_listener_corethread;
|
||||
// EventListenerHelper_AppStatus<MainEmuFrame> m_listener_appstatus;
|
||||
|
||||
bool m_RestartEmuOnDelete;
|
||||
|
||||
wxStatusBar& m_statusbar;
|
||||
|
@ -248,10 +163,10 @@ protected:
|
|||
void Menu_ResetAllSettings_Click(wxCommandEvent &event);
|
||||
|
||||
void Menu_IsoBrowse_Click(wxCommandEvent &event);
|
||||
void Menu_SkipBiosToggle_Click(wxCommandEvent &event);
|
||||
void Menu_EnablePatches_Click(wxCommandEvent &event);
|
||||
|
||||
void Menu_BootCdvd_Click(wxCommandEvent &event);
|
||||
void Menu_BootCdvd2_Click(wxCommandEvent &event);
|
||||
void Menu_OpenELF_Click(wxCommandEvent &event);
|
||||
void Menu_CdvdSource_Click(wxCommandEvent &event);
|
||||
void Menu_LoadStates_Click(wxCommandEvent &event);
|
||||
|
@ -277,6 +192,7 @@ protected:
|
|||
void Menu_PrintCDVD_Info(wxCommandEvent &event);
|
||||
void Menu_ShowAboutBox(wxCommandEvent &event);
|
||||
|
||||
void _DoBootCdvd();
|
||||
bool _DoSelectIsoBrowser( wxString& dest );
|
||||
bool _DoSelectELFBrowser();
|
||||
|
||||
|
|
|
@ -25,11 +25,10 @@
|
|||
#include "Dialogs/LogOptionsDialog.h"
|
||||
|
||||
#include "IniInterface.h"
|
||||
#include "IsoDropTarget.h"
|
||||
|
||||
using namespace Dialogs;
|
||||
|
||||
extern wxString GetMsg_ConfirmSysReset();
|
||||
|
||||
void MainEmuFrame::SaveEmuOptions()
|
||||
{
|
||||
if (wxConfigBase* conf = GetAppConfig())
|
||||
|
@ -57,7 +56,6 @@ void MainEmuFrame::Menu_SelectBios_Click(wxCommandEvent &event)
|
|||
|
||||
static void WipeSettings()
|
||||
{
|
||||
UnloadPlugins();
|
||||
wxGetApp().CleanupRestartable();
|
||||
wxGetApp().CleanupResources();
|
||||
|
||||
|
@ -80,53 +78,31 @@ void MainEmuFrame::RemoveCdvdMenu()
|
|||
m_menuCDVD.Remove( item );
|
||||
}
|
||||
|
||||
|
||||
class RestartEverything_WhenCoreThreadStops : public EventListener_CoreThread,
|
||||
public virtual IDeletableObject
|
||||
{
|
||||
public:
|
||||
RestartEverything_WhenCoreThreadStops() {}
|
||||
virtual ~RestartEverything_WhenCoreThreadStops() throw() {}
|
||||
|
||||
protected:
|
||||
virtual void CoreThread_OnStopped()
|
||||
{
|
||||
wxGetApp().DeleteObject( this );
|
||||
WipeSettings();
|
||||
}
|
||||
};
|
||||
|
||||
class CancelCoreThread_WhenSaveStateDone :
|
||||
public EventListener_CoreThread,
|
||||
public IDeletableObject
|
||||
{
|
||||
public:
|
||||
virtual ~CancelCoreThread_WhenSaveStateDone() throw() {}
|
||||
|
||||
void CoreThread_OnResumed()
|
||||
{
|
||||
wxGetApp().DeleteObject( this );
|
||||
CoreThread.Cancel();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void MainEmuFrame::Menu_ResetAllSettings_Click(wxCommandEvent &event)
|
||||
{
|
||||
if( IsBeingDeleted() || m_RestartEmuOnDelete ) return;
|
||||
|
||||
ScopedCoreThreadSuspend suspender;
|
||||
if( !Msgbox::OkCancel(
|
||||
pxE( ".Popup Warning:DeleteSettings",
|
||||
L"WARNING!! This option will delete *ALL* settings for PCSX2 and force PCSX2 to restart, losing any current emulation progress. Are you absolutely sure?"
|
||||
L"\n\n(note: settings for plugins are unaffected)"
|
||||
),
|
||||
_("Reset all settings?") ) )
|
||||
{
|
||||
suspender.Resume();
|
||||
return;
|
||||
ScopedCoreThreadPopup suspender;
|
||||
if( !Msgbox::OkCancel(
|
||||
pxE( ".Popup Warning:DeleteSettings",
|
||||
L"This command clears PCSX2 settings and allows you to re-run the First-Time Wizard. You will need to "
|
||||
L"manually restart PCSX2 after this operation.\n\n"
|
||||
L"WARNING!! Click OK to delete *ALL* settings for PCSX2 and force PCSX2 to shudown, losing any current emulation progress. Are you absolutely sure?"
|
||||
L"\n\n(note: settings for plugins are unaffected)"
|
||||
),
|
||||
_("Reset all settings?") ) )
|
||||
{
|
||||
suspender.AllowResume();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Old 'auto-restart' method; avoids shutting down the PCSX2 process, but tends to
|
||||
// be bug prone in odd ways (namely QueryPerformanceFrequency hangs for a number of
|
||||
// seconds .. wtf?)
|
||||
|
||||
/*
|
||||
m_RestartEmuOnDelete = true;
|
||||
Destroy();
|
||||
|
||||
|
@ -145,21 +121,93 @@ void MainEmuFrame::Menu_ResetAllSettings_Click(wxCommandEvent &event)
|
|||
{
|
||||
WipeSettings();
|
||||
}
|
||||
*/
|
||||
|
||||
WipeSettings();
|
||||
wxGetApp().PostMenuAction( MenuId_Exit );
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_CdvdSource_Click( wxCommandEvent &event )
|
||||
// Return values:
|
||||
// wxID_CANCEL - User canceled the action outright.
|
||||
// wxID_RESET - User wants to reset the emu in addition to swap discs
|
||||
// (anything else) - Standard swap, no reset. (hotswap!)
|
||||
wxWindowID SwapOrReset_Iso( wxWindow* owner, IScopedCoreThread& core_control, const wxString& isoFilename, const wxString& descpart1 )
|
||||
{
|
||||
CDVD_SourceType newSource = CDVDsrc_NoDisc;
|
||||
wxWindowID result = wxID_RESET;
|
||||
|
||||
switch( event.GetId() )
|
||||
if( SysHasValidState() )
|
||||
{
|
||||
case MenuId_Src_Iso: newSource = CDVDsrc_Iso; break;
|
||||
case MenuId_Src_Plugin: newSource = CDVDsrc_Plugin; break;
|
||||
case MenuId_Src_NoDisc: newSource = CDVDsrc_NoDisc; break;
|
||||
core_control.DisallowResume();
|
||||
wxDialogWithHelpers dialog( owner, _("Confirm ISO image change"), wxVERTICAL );
|
||||
|
||||
jNO_DEFAULT
|
||||
dialog += dialog.Heading(descpart1 +
|
||||
isoFilename + L"\n\n" +
|
||||
_("Do you want to swap discs or boot the new image (via system reset)?")
|
||||
);
|
||||
|
||||
result = pxIssueConfirmation( dialog, MsgButtons().Reset().Cancel().Custom(_("Swap Disc")), L"DragDrop:BootSwapIso" );
|
||||
}
|
||||
CoreThread.ChangeCdvdSource( newSource );
|
||||
|
||||
if( result != wxID_CANCEL )
|
||||
{
|
||||
SysUpdateIsoSrcFile( isoFilename );
|
||||
if( result != wxID_RESET )
|
||||
{
|
||||
Console.Indent().WriteLn( "HotSwapping to new ISO src image!" );
|
||||
g_Conf->CdvdSource = CDVDsrc_Iso;
|
||||
sMainFrame.UpdateIsoSrcSelection();
|
||||
CoreThread.ChangeCdvdSource();
|
||||
core_control.AllowResume();
|
||||
}
|
||||
else
|
||||
{
|
||||
core_control.DisallowResume();
|
||||
sApp.SysExecute( CDVDsrc_Iso );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
wxWindowID SwapOrReset_CdvdSrc( wxWindow* owner, CDVD_SourceType newsrc )
|
||||
{
|
||||
if(newsrc == g_Conf->CdvdSource) return wxID_CANCEL;
|
||||
wxWindowID result = wxID_CANCEL;
|
||||
ScopedCoreThreadPopup core;
|
||||
|
||||
if( SysHasValidState() )
|
||||
{
|
||||
wxDialogWithHelpers dialog( owner, _("Confirm CDVD source change"), wxVERTICAL );
|
||||
|
||||
wxString changeMsg;
|
||||
changeMsg.Printf(_("You've selected to switch the CDVD source from %s to %s."),
|
||||
CDVD_SourceLabels[g_Conf->CdvdSource], CDVD_SourceLabels[newsrc] );
|
||||
|
||||
dialog += dialog.Heading(changeMsg + L"\n\n" +
|
||||
_("Do you want to swap discs or boot the new image (system reset)?")
|
||||
);
|
||||
|
||||
result = pxIssueConfirmation( dialog, MsgButtons().Reset().Cancel().Custom(_("Swap Disc")), L"DragDrop:BootSwapIso" );
|
||||
}
|
||||
|
||||
if( result != wxID_CANCEL )
|
||||
{
|
||||
if( result != wxID_RESET )
|
||||
{
|
||||
Console.Indent().WriteLn( L"(CdvdSource) HotSwapping CDVD source types from %s to %s.", CDVD_SourceLabels[g_Conf->CdvdSource], CDVD_SourceLabels[newsrc] );
|
||||
g_Conf->CdvdSource = newsrc;
|
||||
CoreThread.ChangeCdvdSource();
|
||||
core.AllowResume();
|
||||
}
|
||||
else
|
||||
{
|
||||
core.DisallowResume();
|
||||
g_Conf->CdvdSource = newsrc;
|
||||
sApp.SysExecute( newsrc );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns FALSE if the user canceled the action.
|
||||
|
@ -203,9 +251,9 @@ bool MainEmuFrame::_DoSelectELFBrowser()
|
|||
return false;
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
|
||||
void MainEmuFrame::_DoBootCdvd()
|
||||
{
|
||||
ScopedCoreThreadSuspend core;
|
||||
ScopedCoreThreadPause paused_core;
|
||||
|
||||
if( g_Conf->CdvdSource == CDVDsrc_Iso )
|
||||
{
|
||||
|
@ -232,7 +280,7 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
|
|||
wxString result;
|
||||
if( !_DoSelectIsoBrowser( result ) )
|
||||
{
|
||||
core.Resume();
|
||||
paused_core.AllowResume();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -248,30 +296,57 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
|
|||
|
||||
if( !confirmed )
|
||||
{
|
||||
core.Resume();
|
||||
paused_core.AllowResume();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sApp.SysReset();
|
||||
sApp.SysExecute( g_Conf->CdvdSource );
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_CdvdSource_Click( wxCommandEvent &event )
|
||||
{
|
||||
CDVD_SourceType newsrc = CDVDsrc_NoDisc;
|
||||
|
||||
switch( event.GetId() )
|
||||
{
|
||||
case MenuId_Src_Iso: newsrc = CDVDsrc_Iso; break;
|
||||
case MenuId_Src_Plugin: newsrc = CDVDsrc_Plugin; break;
|
||||
case MenuId_Src_NoDisc: newsrc = CDVDsrc_NoDisc; break;
|
||||
jNO_DEFAULT
|
||||
}
|
||||
|
||||
SwapOrReset_CdvdSrc(this, newsrc);
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
|
||||
{
|
||||
g_Conf->EmuOptions.UseBOOT2Injection = false;
|
||||
_DoBootCdvd();
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_BootCdvd2_Click( wxCommandEvent &event )
|
||||
{
|
||||
g_Conf->EmuOptions.UseBOOT2Injection = true;
|
||||
_DoBootCdvd();
|
||||
}
|
||||
|
||||
static wxString GetMsg_IsoImageChanged()
|
||||
{
|
||||
return _("You have selected the following ISO image into PCSX2:\n\n");
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_IsoBrowse_Click( wxCommandEvent &event )
|
||||
{
|
||||
ScopedCoreThreadSuspend core;
|
||||
wxString result;
|
||||
ScopedCoreThreadPopup core;
|
||||
wxString isofile;
|
||||
|
||||
if( _DoSelectIsoBrowser( result ) )
|
||||
if( !_DoSelectIsoBrowser(isofile) ||
|
||||
(wxID_CANCEL == SwapOrReset_Iso(this, core, isofile, GetMsg_IsoImageChanged())) )
|
||||
{
|
||||
// This command does an on-the-fly change of CD media without automatic reset.
|
||||
// (useful for disc swapping)
|
||||
|
||||
SysUpdateIsoSrcFile( result );
|
||||
AppSaveSettings();
|
||||
core.AllowResume();
|
||||
return;
|
||||
}
|
||||
|
||||
core.Resume();
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_MultitapToggle_Click( wxCommandEvent& )
|
||||
|
@ -284,12 +359,6 @@ void MainEmuFrame::Menu_MultitapToggle_Click( wxCommandEvent& )
|
|||
//evt.Skip();
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_SkipBiosToggle_Click( wxCommandEvent& )
|
||||
{
|
||||
g_Conf->EmuOptions.SkipBiosSplash = GetMenuBar()->IsChecked( MenuId_SkipBiosToggle );
|
||||
SaveEmuOptions();
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_EnablePatches_Click( wxCommandEvent& )
|
||||
{
|
||||
g_Conf->EmuOptions.EnablePatches = GetMenuBar()->IsChecked( MenuId_EnablePatches );
|
||||
|
@ -298,13 +367,13 @@ void MainEmuFrame::Menu_EnablePatches_Click( wxCommandEvent& )
|
|||
|
||||
void MainEmuFrame::Menu_OpenELF_Click(wxCommandEvent&)
|
||||
{
|
||||
bool resume = CoreThread.Suspend();
|
||||
ScopedCoreThreadClose stopped_core;
|
||||
if( _DoSelectELFBrowser() )
|
||||
{
|
||||
sApp.SysExecute( g_Conf->CdvdSource, g_Conf->CurrentELF );
|
||||
}
|
||||
|
||||
if( resume ) CoreThread.Resume();
|
||||
stopped_core.AllowResume();
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_LoadStates_Click(wxCommandEvent &event)
|
||||
|
@ -334,6 +403,43 @@ void MainEmuFrame::Menu_Exit_Click(wxCommandEvent &event)
|
|||
Close();
|
||||
}
|
||||
|
||||
class SysExecEvent_ToggleSuspend : public SysExecEvent
|
||||
{
|
||||
public:
|
||||
virtual ~SysExecEvent_ToggleSuspend() throw() {}
|
||||
|
||||
wxString GetEventName() const { return L"ToggleSuspendResume"; }
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
if( CoreThread.IsOpen() )
|
||||
CoreThread.Suspend();
|
||||
else
|
||||
CoreThread.Resume();
|
||||
}
|
||||
};
|
||||
|
||||
class SysExecEvent_Restart : public SysExecEvent
|
||||
{
|
||||
public:
|
||||
virtual ~SysExecEvent_Restart() throw() {}
|
||||
|
||||
wxString GetEventName() const { return L"SysRestart"; }
|
||||
|
||||
wxString GetEventMessage() const
|
||||
{
|
||||
return _("Restarting PS2 Virtual Machine...");
|
||||
}
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
sApp.SysShutdown();
|
||||
sApp.SysExecute();
|
||||
}
|
||||
};
|
||||
|
||||
void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event)
|
||||
{
|
||||
if( !SysHasValidState() ) return;
|
||||
|
@ -343,29 +449,21 @@ void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event)
|
|||
// engaged successfully).
|
||||
|
||||
GetMenuBar()->Enable( MenuId_Sys_SuspendResume, false );
|
||||
|
||||
if( !CoreThread.Suspend() )
|
||||
{
|
||||
sApp.SysExecute();
|
||||
}
|
||||
GetSysExecutorThread().PostEvent( new SysExecEvent_ToggleSuspend() );
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_SysReset_Click(wxCommandEvent &event)
|
||||
{
|
||||
if( StateCopy_InvokeOnCopyComplete( new InvokeAction_MenuCommand(MenuId_Sys_Reset) ) ) return;
|
||||
|
||||
sApp.SysReset();
|
||||
sApp.SysExecute();
|
||||
//GetMenuBar()->Enable( MenuId_Sys_Reset, true );
|
||||
UI_DisableSysReset();
|
||||
GetSysExecutorThread().PostEvent( new SysExecEvent_Restart() );
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_SysShutdown_Click(wxCommandEvent &event)
|
||||
{
|
||||
if( !SysHasValidState() && g_plugins == NULL ) return;
|
||||
if( StateCopy_InvokeOnCopyComplete( new InvokeAction_MenuCommand(MenuId_Sys_Shutdown) ) ) return;
|
||||
if( !SysHasValidState() && !CorePlugins.AreAnyInitialized() ) return;
|
||||
|
||||
sApp.SysReset();
|
||||
GetMenuBar()->Enable( MenuId_Sys_Shutdown, false );
|
||||
UI_DisableSysShutdown();
|
||||
sApp.SysShutdown();
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event)
|
||||
|
@ -379,12 +477,13 @@ void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event)
|
|||
|
||||
if( !pxAssertDev( (eventId >= 0) || (pid < PluginId_Count), "Invalid plugin identifier passed to ConfigPlugin event handler." ) ) return;
|
||||
|
||||
LoadPluginsImmediate();
|
||||
if( g_plugins == NULL ) return;
|
||||
// This could probably just load a single plugin as needed now, but for design safety
|
||||
// I'm leaving it force-load everything until the individual plugin management is
|
||||
// better tested.
|
||||
|
||||
wxWindowDisabler disabler;
|
||||
SaveSinglePluginHelper helper( pid );
|
||||
g_plugins->Configure( pid );
|
||||
GetCorePlugins().Configure( pid );
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_Debug_Open_Click(wxCommandEvent &event)
|
||||
|
|
|
@ -26,7 +26,7 @@ using namespace pxSizerFlags;
|
|||
//
|
||||
static int pxMessageDialog( const wxString& caption, const wxString& content, const MsgButtons& buttons )
|
||||
{
|
||||
if( !AffinityAssert_AllowFromMain() ) return wxID_CANCEL;
|
||||
if( !AffinityAssert_AllowFrom_MainUI() ) return wxID_CANCEL;
|
||||
|
||||
// fixme: If the emulator is currently active and is running in exclusive mode (forced
|
||||
// fullscreen), then we need to either:
|
||||
|
@ -43,54 +43,31 @@ static int pxMessageDialog( const wxString& caption, const wxString& content, co
|
|||
// --------------------------------------------------------------------------------------
|
||||
// BaseMessageBoxEvent Implementation
|
||||
// --------------------------------------------------------------------------------------
|
||||
IMPLEMENT_DYNAMIC_CLASS( BaseMessageBoxEvent, wxEvent )
|
||||
IMPLEMENT_DYNAMIC_CLASS( BaseMessageBoxEvent, pxInvokeActionEvent )
|
||||
|
||||
BaseMessageBoxEvent::BaseMessageBoxEvent( int msgtype, const wxString& content )
|
||||
: wxEvent( 0, msgtype )
|
||||
, m_Content( content )
|
||||
BaseMessageBoxEvent::BaseMessageBoxEvent( const wxString& content, SynchronousActionState& instdata )
|
||||
: m_Content( content )
|
||||
{
|
||||
m_Instdata = NULL;
|
||||
m_state = &instdata;
|
||||
}
|
||||
|
||||
BaseMessageBoxEvent::BaseMessageBoxEvent( MsgboxEventResult& instdata, const wxString& content )
|
||||
: wxEvent( 0, pxEvt_MessageBox )
|
||||
, m_Instdata( &instdata )
|
||||
, m_Content( content )
|
||||
{
|
||||
}
|
||||
|
||||
BaseMessageBoxEvent::BaseMessageBoxEvent( const wxString& content )
|
||||
: wxEvent( 0, pxEvt_MessageBox )
|
||||
, m_Instdata( NULL )
|
||||
, m_Content( content )
|
||||
BaseMessageBoxEvent::BaseMessageBoxEvent( const wxString& content, SynchronousActionState* instdata )
|
||||
: m_Content( content )
|
||||
{
|
||||
m_state = instdata;
|
||||
}
|
||||
|
||||
BaseMessageBoxEvent::BaseMessageBoxEvent( const BaseMessageBoxEvent& event )
|
||||
: wxEvent( event )
|
||||
, m_Instdata( event.m_Instdata )
|
||||
, m_Content( event.m_Content )
|
||||
: m_Content( event.m_Content )
|
||||
{
|
||||
}
|
||||
|
||||
BaseMessageBoxEvent& BaseMessageBoxEvent::SetInstData( MsgboxEventResult& instdata )
|
||||
{
|
||||
m_Instdata = &instdata;
|
||||
return *this;
|
||||
m_state = event.m_state;
|
||||
}
|
||||
|
||||
// Thread Safety: Must be called from the GUI thread ONLY.
|
||||
void BaseMessageBoxEvent::IssueDialog()
|
||||
void BaseMessageBoxEvent::_DoInvoke()
|
||||
{
|
||||
AffinityAssert_AllowFromMain();
|
||||
|
||||
int result = _DoDialog();
|
||||
|
||||
if( m_Instdata != NULL )
|
||||
{
|
||||
m_Instdata->result = result;
|
||||
m_Instdata->WaitForMe.Post();
|
||||
}
|
||||
if( m_state ) m_state->PostResult( result );
|
||||
}
|
||||
|
||||
int BaseMessageBoxEvent::_DoDialog() const
|
||||
|
@ -104,20 +81,15 @@ int BaseMessageBoxEvent::_DoDialog() const
|
|||
// --------------------------------------------------------------------------------------
|
||||
IMPLEMENT_DYNAMIC_CLASS( pxMessageBoxEvent, BaseMessageBoxEvent )
|
||||
|
||||
pxMessageBoxEvent::pxMessageBoxEvent( int msgtype )
|
||||
: BaseMessageBoxEvent( msgtype )
|
||||
{
|
||||
}
|
||||
|
||||
pxMessageBoxEvent::pxMessageBoxEvent( MsgboxEventResult& instdata, const wxString& title, const wxString& content, const MsgButtons& buttons )
|
||||
: BaseMessageBoxEvent( instdata, content )
|
||||
pxMessageBoxEvent::pxMessageBoxEvent( const wxString& title, const wxString& content, const MsgButtons& buttons, SynchronousActionState& instdata )
|
||||
: BaseMessageBoxEvent( content, instdata )
|
||||
, m_Title( title )
|
||||
, m_Buttons( buttons )
|
||||
{
|
||||
}
|
||||
|
||||
pxMessageBoxEvent::pxMessageBoxEvent( const wxString& title, const wxString& content, const MsgButtons& buttons )
|
||||
: BaseMessageBoxEvent( content )
|
||||
pxMessageBoxEvent::pxMessageBoxEvent( const wxString& title, const wxString& content, const MsgButtons& buttons, SynchronousActionState* instdata )
|
||||
: BaseMessageBoxEvent( content, instdata )
|
||||
, m_Title( title )
|
||||
, m_Buttons( buttons )
|
||||
{
|
||||
|
@ -130,12 +102,6 @@ pxMessageBoxEvent::pxMessageBoxEvent( const pxMessageBoxEvent& event )
|
|||
{
|
||||
}
|
||||
|
||||
pxMessageBoxEvent& pxMessageBoxEvent::SetInstData( MsgboxEventResult& instdata )
|
||||
{
|
||||
_parent::SetInstData( instdata );
|
||||
return *this;
|
||||
}
|
||||
|
||||
int pxMessageBoxEvent::_DoDialog() const
|
||||
{
|
||||
return pxMessageDialog( m_Content, m_Title, m_Buttons );
|
||||
|
@ -146,19 +112,14 @@ int pxMessageBoxEvent::_DoDialog() const
|
|||
// --------------------------------------------------------------------------------------
|
||||
IMPLEMENT_DYNAMIC_CLASS( pxAssertionEvent, BaseMessageBoxEvent )
|
||||
|
||||
pxAssertionEvent::pxAssertionEvent()
|
||||
: BaseMessageBoxEvent( )
|
||||
{
|
||||
}
|
||||
|
||||
pxAssertionEvent::pxAssertionEvent( MsgboxEventResult& instdata, const wxString& content, const wxString& trace )
|
||||
: BaseMessageBoxEvent( instdata, content )
|
||||
pxAssertionEvent::pxAssertionEvent( const wxString& content, const wxString& trace, SynchronousActionState& instdata )
|
||||
: BaseMessageBoxEvent( content, instdata )
|
||||
, m_Stacktrace( trace )
|
||||
{
|
||||
}
|
||||
|
||||
pxAssertionEvent::pxAssertionEvent( const wxString& content, const wxString& trace )
|
||||
: BaseMessageBoxEvent( content )
|
||||
pxAssertionEvent::pxAssertionEvent( const wxString& content, const wxString& trace, SynchronousActionState* instdata )
|
||||
: BaseMessageBoxEvent( content, instdata )
|
||||
, m_Stacktrace( trace )
|
||||
{
|
||||
}
|
||||
|
@ -169,12 +130,6 @@ pxAssertionEvent::pxAssertionEvent( const pxAssertionEvent& event )
|
|||
{
|
||||
}
|
||||
|
||||
pxAssertionEvent& pxAssertionEvent::SetInstData( MsgboxEventResult& instdata )
|
||||
{
|
||||
_parent::SetInstData( instdata );
|
||||
return *this;
|
||||
}
|
||||
|
||||
pxAssertionEvent& pxAssertionEvent::SetStacktrace( const wxString& trace )
|
||||
{
|
||||
m_Stacktrace = trace;
|
||||
|
@ -190,33 +145,27 @@ namespace Msgbox
|
|||
{
|
||||
int ShowModal( BaseMessageBoxEvent& evt )
|
||||
{
|
||||
MsgboxEventResult instdat;
|
||||
evt.SetInstData( instdat );
|
||||
SynchronousActionState instdat;
|
||||
evt.SetSyncState( instdat );
|
||||
|
||||
if( wxThread::IsMain() )
|
||||
{
|
||||
// main thread can handle the message immediately.
|
||||
wxGetApp().ProcessEvent( evt );
|
||||
return instdat.return_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not on main thread, must post the message there for handling instead:
|
||||
wxGetApp().AddPendingEvent( evt );
|
||||
instdat.WaitForMe.WaitNoCancel(); // Important! disable cancellation since we're using local stack vars.
|
||||
return instdat.WaitForResult(); // Important! disable cancellation since we're using local stack vars.
|
||||
}
|
||||
return instdat.result;
|
||||
}
|
||||
|
||||
static int ShowModal( const wxString& title, const wxString& content, const MsgButtons& buttons )
|
||||
{
|
||||
// must pass the message to the main gui thread, and then stall this thread, to avoid
|
||||
// threaded chaos where our thread keeps running while the popup is awaiting input.
|
||||
|
||||
MsgboxEventResult instdat;
|
||||
pxMessageBoxEvent tevt( instdat, title, content, buttons );
|
||||
wxGetApp().AddPendingEvent( tevt );
|
||||
instdat.WaitForMe.WaitNoCancel(); // Important! disable cancellation since we're using local stack vars.
|
||||
return instdat.result;
|
||||
pxMessageBoxEvent tevt( title, content, buttons );
|
||||
return ShowModal( tevt );
|
||||
}
|
||||
|
||||
// Pops up an alert Dialog Box with a singular "OK" button.
|
||||
|
@ -225,10 +174,7 @@ namespace Msgbox
|
|||
{
|
||||
MsgButtons buttons( MsgButtons().OK() );
|
||||
|
||||
if( wxThread::IsMain() )
|
||||
pxMessageDialog( caption, text, buttons );
|
||||
else
|
||||
ShowModal( caption, text, buttons );
|
||||
ShowModal( caption, text, buttons );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ namespace Panels
|
|||
//
|
||||
class DirPickerPanel : public BaseApplicableConfigPanel
|
||||
{
|
||||
typedef BaseApplicableConfigPanel _parent;
|
||||
|
||||
protected:
|
||||
FoldersEnum_t m_FolderId;
|
||||
wxDirPickerCtrl* m_pickerCtrl;
|
||||
|
@ -49,9 +51,6 @@ namespace Panels
|
|||
DirPickerPanel( wxWindow* parent, FoldersEnum_t folderid, const wxString& dialogLabel );
|
||||
virtual ~DirPickerPanel() throw() { }
|
||||
|
||||
void Apply();
|
||||
void AppStatusEvent_OnSettingsApplied();
|
||||
|
||||
void Reset();
|
||||
wxDirName GetPath() const;
|
||||
void SetPath( const wxString& src );
|
||||
|
@ -62,6 +61,12 @@ namespace Panels
|
|||
wxWindowID GetId() const;
|
||||
wxWindowID GetPanelId() const { return m_windowId; }
|
||||
|
||||
// Overrides!
|
||||
|
||||
void Apply();
|
||||
void AppStatusEvent_OnSettingsApplied();
|
||||
bool Enable( bool enable=true );
|
||||
|
||||
protected:
|
||||
void Init( FoldersEnum_t folderid, const wxString& dialogLabel, bool isCompact );
|
||||
|
||||
|
@ -71,20 +76,22 @@ namespace Panels
|
|||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// UsermodeSelectionPanel / LanguageSelectionPanel
|
||||
// DocsFolderPickerPanel / LanguageSelectionPanel
|
||||
// --------------------------------------------------------------------------------------
|
||||
class UsermodeSelectionPanel : public BaseApplicableConfigPanel
|
||||
class DocsFolderPickerPanel : public BaseApplicableConfigPanel
|
||||
{
|
||||
protected:
|
||||
pxRadioPanel* m_radio_UserMode;
|
||||
DirPickerPanel* m_dirpicker_custom;
|
||||
|
||||
public:
|
||||
virtual ~UsermodeSelectionPanel() throw() { }
|
||||
UsermodeSelectionPanel( wxWindow* parent, bool isFirstTime = true );
|
||||
virtual ~DocsFolderPickerPanel() throw() { }
|
||||
DocsFolderPickerPanel( wxWindow* parent, bool isFirstTime = true );
|
||||
|
||||
void Apply();
|
||||
void AppStatusEvent_OnSettingsApplied();
|
||||
|
||||
DocsModeType GetDocsMode() const;
|
||||
wxWindowID GetDirPickerId() const { return m_dirpicker_custom ? m_dirpicker_custom->GetId() : 0; }
|
||||
|
||||
protected:
|
||||
|
|
|
@ -70,7 +70,7 @@ void Panels::DirPickerPanel::Explore_Click( wxCommandEvent &evt )
|
|||
);
|
||||
|
||||
if( result == wxID_CANCEL ) return;
|
||||
wxMkdir( path );
|
||||
wxDirName(path).Mkdir();
|
||||
}
|
||||
|
||||
pxExplore( path );
|
||||
|
@ -108,8 +108,8 @@ void Panels::DirPickerPanel::Init( FoldersEnum_t folderid, const wxString& dialo
|
|||
// The default path is invalid... What should we do here? hmm..
|
||||
}
|
||||
|
||||
if( !wxDir::Exists( normalized ) )
|
||||
wxMkdir( normalized );
|
||||
//if( !wxDir::Exists( normalized ) )
|
||||
// wxMkdir( normalized );
|
||||
|
||||
if( !isCompact )
|
||||
{
|
||||
|
@ -191,11 +191,22 @@ void Panels::DirPickerPanel::Reset()
|
|||
|
||||
if( m_pickerCtrl )
|
||||
{
|
||||
m_pickerCtrl->Enable( m_checkCtrl ? !isDefault : true );
|
||||
// Important! The dirpicker panel stuff, due to however it's put together
|
||||
// needs to check the enable status of this panel before setting the child
|
||||
// panel's enable status.
|
||||
|
||||
m_pickerCtrl->Enable( IsEnabled() ? ( m_checkCtrl ? !isDefault : true ) : false );
|
||||
m_pickerCtrl->SetPath( GetNormalizedConfigFolder( m_FolderId ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool Panels::DirPickerPanel::Enable( bool enable )
|
||||
{
|
||||
m_pickerCtrl->Enable( enable ? (!m_checkCtrl || m_checkCtrl->GetValue()) : false );
|
||||
return _parent::Enable( enable );
|
||||
}
|
||||
|
||||
|
||||
void Panels::DirPickerPanel::AppStatusEvent_OnSettingsApplied()
|
||||
{
|
||||
Reset();
|
||||
|
@ -204,6 +215,21 @@ void Panels::DirPickerPanel::AppStatusEvent_OnSettingsApplied()
|
|||
void Panels::DirPickerPanel::Apply()
|
||||
{
|
||||
if( !m_pickerCtrl ) return;
|
||||
|
||||
const wxString path( m_pickerCtrl->GetPath() );
|
||||
|
||||
if( !wxDir::Exists( path ) )
|
||||
{
|
||||
wxDialogWithHelpers dialog( NULL, _("Create folder?"), wxVERTICAL );
|
||||
dialog += dialog.Heading( _("A configured folder does not exist. Should PCSX2 to create it?") );
|
||||
dialog += 12;
|
||||
dialog += dialog.Heading( path );
|
||||
|
||||
if( wxID_CANCEL == pxIssueConfirmation( dialog, MsgButtons().Custom(_("Create")).Cancel(), L"CreateNewFolder" ) )
|
||||
throw Exception::CannotApplySettings( this );
|
||||
}
|
||||
|
||||
wxDirName(path).Mkdir();
|
||||
g_Conf->Folders.Set( m_FolderId, m_pickerCtrl->GetPath(), m_checkCtrl ? m_checkCtrl->GetValue() : false );
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* 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.
|
||||
|
@ -27,9 +27,9 @@ using namespace Dialogs;
|
|||
using namespace pxSizerFlags;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// UsermodeSelectionPanel
|
||||
// DocsFolderPickerPanel
|
||||
// --------------------------------------------------------------------------------------
|
||||
Panels::UsermodeSelectionPanel::UsermodeSelectionPanel( wxWindow* parent, bool isFirstTime )
|
||||
Panels::DocsFolderPickerPanel::DocsFolderPickerPanel( wxWindow* parent, bool isFirstTime )
|
||||
: BaseApplicableConfigPanel( parent, wxVERTICAL, _("Usermode Selection") )
|
||||
{
|
||||
const wxString usermodeExplained( pxE( ".Panels:Usermode:Explained",
|
||||
|
@ -44,7 +44,7 @@ Panels::UsermodeSelectionPanel::UsermodeSelectionPanel( wxWindow* parent, bool i
|
|||
L"This option only affects Standard Paths which are set to use the installation default value."
|
||||
) );
|
||||
|
||||
const RadioPanelItem UsermodeOptions[] =
|
||||
const RadioPanelItem UsermodeOptions[] =
|
||||
{
|
||||
RadioPanelItem(
|
||||
_("User Documents (recommended)"),
|
||||
|
@ -67,7 +67,7 @@ Panels::UsermodeSelectionPanel::UsermodeSelectionPanel( wxWindow* parent, bool i
|
|||
m_radio_UserMode = new pxRadioPanel( this, UsermodeOptions );
|
||||
m_radio_UserMode->SetPaddingHoriz( m_radio_UserMode->GetPaddingVert() + 4 );
|
||||
m_radio_UserMode->Realize();
|
||||
|
||||
|
||||
m_dirpicker_custom = new DirPickerPanel( this, FolderId_Documents, _("Select a document root for PCSX2") );
|
||||
|
||||
*this += Text( isFirstTime ? usermodeExplained : usermodeWarning );
|
||||
|
@ -75,32 +75,34 @@ Panels::UsermodeSelectionPanel::UsermodeSelectionPanel( wxWindow* parent, bool i
|
|||
*this += m_dirpicker_custom | pxExpand.Border( wxLEFT, StdPadding + m_radio_UserMode->GetIndentation() );
|
||||
*this += 4;
|
||||
|
||||
AppStatusEvent_OnSettingsApplied();
|
||||
|
||||
Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler(UsermodeSelectionPanel::OnRadioChanged) );
|
||||
Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler(DocsFolderPickerPanel::OnRadioChanged) );
|
||||
}
|
||||
|
||||
void Panels::UsermodeSelectionPanel::Apply()
|
||||
DocsModeType Panels::DocsFolderPickerPanel::GetDocsMode() const
|
||||
{
|
||||
return (DocsModeType) m_radio_UserMode->GetSelection();
|
||||
}
|
||||
|
||||
void Panels::DocsFolderPickerPanel::Apply()
|
||||
{
|
||||
DocsFolderMode = (DocsModeType) m_radio_UserMode->GetSelection();
|
||||
CustomDocumentsFolder = m_dirpicker_custom->GetPath();
|
||||
}
|
||||
|
||||
void Panels::UsermodeSelectionPanel::AppStatusEvent_OnSettingsApplied()
|
||||
void Panels::DocsFolderPickerPanel::AppStatusEvent_OnSettingsApplied()
|
||||
{
|
||||
if( m_radio_UserMode ) m_radio_UserMode->SetSelection( DocsFolderMode );
|
||||
|
||||
if( m_dirpicker_custom ) m_dirpicker_custom->Enable( DocsFolderMode == DocsFolder_Custom );
|
||||
}
|
||||
|
||||
void Panels::UsermodeSelectionPanel::OnRadioChanged( wxCommandEvent& evt )
|
||||
void Panels::DocsFolderPickerPanel::OnRadioChanged( wxCommandEvent& evt )
|
||||
{
|
||||
evt.Skip();
|
||||
|
||||
if( !m_radio_UserMode ) return;
|
||||
|
||||
if( m_dirpicker_custom )
|
||||
m_dirpicker_custom->Enable( m_radio_UserMode->GetSelection() == (int)DocsFolder_Custom );
|
||||
m_dirpicker_custom->Enable( m_radio_UserMode->GetSelection() == (int)DocsFolder_Custom );
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
|
|
@ -39,11 +39,13 @@ BEGIN_DECLARE_EVENT_TYPES()
|
|||
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_EnumerationFinished);
|
||||
DEFINE_EVENT_TYPE(pxEVT_ShowStatusBar);
|
||||
DEFINE_EVENT_TYPE(pxEvt_SysExecEventComplete)
|
||||
|
||||
typedef s32 (CALLBACK* TestFnptr)();
|
||||
typedef void (CALLBACK* ConfigureFnptr)();
|
||||
|
@ -60,7 +62,6 @@ namespace Exception
|
|||
// --------------------------------------------------------------------------------------
|
||||
// PluginEnumerator class
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
class PluginEnumerator
|
||||
{
|
||||
protected:
|
||||
|
@ -134,10 +135,162 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ApplyPluginsDialog
|
||||
// --------------------------------------------------------------------------------------
|
||||
class ApplyPluginsDialog : public wxDialogWithHelpers
|
||||
{
|
||||
DECLARE_DYNAMIC_CLASS_NO_COPY(ApplyPluginsDialog)
|
||||
|
||||
typedef wxDialogWithHelpers _parent;
|
||||
|
||||
protected:
|
||||
BaseApplicableConfigPanel* m_panel;
|
||||
ScopedPtr<BaseException> m_Exception;
|
||||
SynchronousActionState m_sync;
|
||||
|
||||
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 );
|
||||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ApplyOverValidStateEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
class ApplyOverValidStateEvent : public pxInvokeActionEvent
|
||||
{
|
||||
//DeclareNoncopyableObject( ApplyOverValidStateEvent );
|
||||
typedef pxInvokeActionEvent _parent;
|
||||
|
||||
protected:
|
||||
ApplyPluginsDialog* m_owner;
|
||||
|
||||
public:
|
||||
ApplyOverValidStateEvent( ApplyPluginsDialog* owner=NULL )
|
||||
{
|
||||
m_owner = owner;
|
||||
}
|
||||
|
||||
virtual ~ApplyOverValidStateEvent() throw() { }
|
||||
virtual ApplyOverValidStateEvent *Clone() const { return new ApplyOverValidStateEvent(*this); }
|
||||
|
||||
protected:
|
||||
void _DoInvoke();
|
||||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent_ApplyPlugins
|
||||
// --------------------------------------------------------------------------------------
|
||||
class SysExecEvent_ApplyPlugins : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
ApplyPluginsDialog* m_dialog;
|
||||
|
||||
public:
|
||||
virtual ~SysExecEvent_ApplyPlugins() throw() {}
|
||||
SysExecEvent_ApplyPlugins* Clone() const { return new SysExecEvent_ApplyPlugins( *this ); }
|
||||
|
||||
SysExecEvent_ApplyPlugins( ApplyPluginsDialog* parent, SynchronousActionState& sync )
|
||||
: SysExecEvent( &sync )
|
||||
{
|
||||
m_dialog = parent;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _DoInvoke();
|
||||
};
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(ApplyPluginsDialog, wxDialogWithHelpers)
|
||||
|
||||
ApplyPluginsDialog::ApplyPluginsDialog( BaseApplicableConfigPanel* panel )
|
||||
: wxDialogWithHelpers( NULL, _("Applying settings..."), wxVERTICAL )
|
||||
{
|
||||
Connect( pxEvt_SysExecEventComplete, wxCommandEventHandler(ApplyPluginsDialog::OnSysExecComplete) );
|
||||
GetSysExecutorThread().PostEvent( new SysExecEvent_ApplyPlugins( this, m_sync ) );
|
||||
}
|
||||
|
||||
void ApplyOverValidStateEvent::_DoInvoke()
|
||||
{
|
||||
wxDialogWithHelpers dialog( m_owner, _("Shutdown PS2 virtual machine?"), wxVERTICAL );
|
||||
|
||||
dialog += dialog.Heading( pxE( ".Popup:PluginSelector:ConfirmShutdown",
|
||||
L"Warning! Changing plugins requires a complete shutdown and reset of the PS2 virtual machine. "
|
||||
L"PCSX2 will attempt to save and restore the state, but if the newly selected plugins are "
|
||||
L"incompatible the recovery may fail, and current progress will be lost."
|
||||
L"\n\n"
|
||||
L"Are you sure you want to apply settings now?"
|
||||
) );
|
||||
|
||||
int result = pxIssueConfirmation( dialog, MsgButtons().OK().Cancel(), L"PluginSelector:ConfirmShutdown" );
|
||||
|
||||
if( result == wxID_CANCEL )
|
||||
throw Exception::CannotApplySettings( m_owner->GetApplicableConfigPanel(), "Cannot apply settings: canceled by user because plugins changed while the emulation state was active.", false );
|
||||
}
|
||||
|
||||
void SysExecEvent_ApplyPlugins::_DoInvoke()
|
||||
{
|
||||
ScopedCoreThreadPause paused_core;
|
||||
|
||||
if( SysHasValidState() )
|
||||
{
|
||||
paused_core.AllowResume();
|
||||
wxGetApp().ProcessEvent( ApplyOverValidStateEvent( m_dialog ) );
|
||||
paused_core.DisallowResume();
|
||||
|
||||
// FIXME : We only actually have to save plugins here, except the recovery code
|
||||
// in SysCoreThread isn't quite set up yet to handle that (I think...) --air
|
||||
|
||||
memSavingState( StateCopy_GetBuffer() ).FreezeAll();
|
||||
}
|
||||
|
||||
ScopedCoreThreadClose closed_core;
|
||||
|
||||
CorePlugins.Shutdown();
|
||||
CorePlugins.Unload();
|
||||
LoadPluginsImmediate();
|
||||
|
||||
wxCommandEvent tevt( pxEvt_SysExecEventComplete );
|
||||
m_dialog->GetEventHandler()->AddPendingEvent( tevt );
|
||||
|
||||
closed_core.AllowResume();
|
||||
paused_core.AllowResume();
|
||||
}
|
||||
|
||||
|
||||
void ApplyPluginsDialog::OnSysExecComplete( wxCommandEvent& evt )
|
||||
{
|
||||
evt.Skip();
|
||||
m_sync.WaitForResult();
|
||||
EndModal( wxID_OK );
|
||||
}
|
||||
|
||||
void ApplyPluginsDialog::RethrowException()
|
||||
{
|
||||
if( m_Exception ) m_Exception->Rethrow();
|
||||
}
|
||||
|
||||
int ApplyPluginsDialog::ShowModal()
|
||||
{
|
||||
int result = _parent::ShowModal();
|
||||
RethrowException();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static const wxString failed_separator( L"-------- Unsupported Plugins --------" );
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// PluginSelectorPanel implementations
|
||||
// PluginSelectorPanel::StatusPanel implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
Panels::PluginSelectorPanel::StatusPanel::StatusPanel( wxWindow* parent )
|
||||
|
@ -176,9 +329,9 @@ void Panels::PluginSelectorPanel::StatusPanel::Reset()
|
|||
// Id for all Configure buttons (any non-negative arbitrary integer will do)
|
||||
static const int ButtonId_Configure = 51;
|
||||
|
||||
// =====================================================================================================
|
||||
// PluginSelectorPanel::ComboBoxPanel
|
||||
// =====================================================================================================
|
||||
// --------------------------------------------------------------------------------------
|
||||
// PluginSelectorPanel::ComboBoxPanel implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
Panels::PluginSelectorPanel::ComboBoxPanel::ComboBoxPanel( PluginSelectorPanel* parent )
|
||||
: wxPanelWithHelpers( parent, wxVERTICAL )
|
||||
, m_FolderPicker( *new DirPickerPanel( this, FolderId_Plugins,
|
||||
|
@ -224,9 +377,6 @@ void Panels::PluginSelectorPanel::ComboBoxPanel::Reset()
|
|||
}
|
||||
}
|
||||
|
||||
// =====================================================================================================
|
||||
// PluginSelectorPanel
|
||||
// =====================================================================================================
|
||||
void Panels::PluginSelectorPanel::DispatchEvent( const PluginEventType& evt )
|
||||
{
|
||||
if( (evt != CorePlugins_Loaded) && (evt != CorePlugins_Unloaded) ) return; // everything else we don't care about
|
||||
|
@ -337,64 +487,47 @@ void Panels::PluginSelectorPanel::Apply()
|
|||
break;
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
bool isSuspended = false;
|
||||
if( pi->shortname == NULL ) return; // no plugins changed? nothing left to do!
|
||||
|
||||
if( pi->shortname != NULL )
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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( CoreThread.IsRunning() )
|
||||
{
|
||||
// [TODO] : Post notice that this shuts down existing emulation, and may not safely recover.
|
||||
wxDialogWithHelpers dialog( this, _("Shutdown PS2 virtual machine?"), wxVERTICAL );
|
||||
|
||||
dialog += dialog.Heading( pxE( ".Popup:PluginSelector:ConfirmShutdown",
|
||||
L"Warning! Changing plugins requires a complete shutdown and reset of the PS2 virtual machine. "
|
||||
L"PCSX2 will attempt to save and restore the state, but if the newly selected plugins are "
|
||||
L"incompatible the recovery may fail, and current progress will be lost."
|
||||
L"\n\n"
|
||||
L"Are you sure you want to apply settings now?"
|
||||
) );
|
||||
|
||||
int result = pxIssueConfirmation( dialog, MsgButtons().OK().Cancel(), L"PluginSelector:ConfirmShutdown" );
|
||||
|
||||
if( result == wxID_CANCEL )
|
||||
throw Exception::CannotApplySettings( this, "Cannot apply settings: canceled by user because plugins changed while the emulation state was active.", false );
|
||||
|
||||
// FIXME : We only actually have to save plugins here, except the recovery code
|
||||
// in SysCoreThread isn't quite set up yet to handle that (I think...) --air
|
||||
|
||||
isSuspended = CoreThread.Suspend();
|
||||
StateCopy_FreezeToMem_Blocking();
|
||||
}
|
||||
|
||||
// Don't use SysShutdown, it clears the StateCopy.
|
||||
CoreThread.Cancel();
|
||||
wxGetApp().m_CorePlugins = NULL;
|
||||
if( wxID_CANCEL == applyDlg.ShowModal() )
|
||||
throw Exception::CannotApplySettings( this, "User canceled plugin load process.", false );
|
||||
}
|
||||
|
||||
if( !wxGetApp().m_CorePlugins )
|
||||
catch( Exception::PluginError& ex )
|
||||
{
|
||||
try {
|
||||
LoadPluginsImmediate();
|
||||
}
|
||||
catch( Exception::PluginError& ex )
|
||||
{
|
||||
// Rethrow PluginLoadErrors as a failure to Apply...
|
||||
// Rethrow PluginLoadErrors as a failure to Apply...
|
||||
|
||||
wxString plugname( tbl_PluginInfo[ex.PluginId].GetShortname() );
|
||||
wxString plugname( tbl_PluginInfo[ex.PluginId].GetShortname() );
|
||||
|
||||
throw Exception::CannotApplySettings( this,
|
||||
// Diagnostic
|
||||
ex.FormatDiagnosticMessage(),
|
||||
throw Exception::CannotApplySettings( this,
|
||||
// Diagnostic
|
||||
ex.FormatDiagnosticMessage(),
|
||||
|
||||
// Translated
|
||||
wxsFormat( _("The selected %s plugin failed to load.\n\nReason: %s\n\n"),
|
||||
plugname.c_str(), ex.FormatDisplayMessage().c_str()
|
||||
) + GetApplyFailedMsg()
|
||||
);
|
||||
}
|
||||
// Translated
|
||||
wxsFormat( _("The selected %s plugin failed to load.\n\nReason: %s\n\n"),
|
||||
plugname.c_str(), ex.FormatDisplayMessage().c_str()
|
||||
) + GetApplyFailedMsg()
|
||||
);
|
||||
}
|
||||
|
||||
if( isSuspended ) CoreThread.Resume();
|
||||
}
|
||||
|
||||
void Panels::PluginSelectorPanel::CancelRefresh()
|
||||
|
@ -480,9 +613,9 @@ void Panels::PluginSelectorPanel::OnPluginSelected( wxCommandEvent& evt )
|
|||
// (a) plugins aren't even loaded yet.
|
||||
// (b) current selection matches exactly the currently configured/loaded plugin.
|
||||
|
||||
bool isSame = (g_plugins==NULL) || g_Conf->FullpathMatchTest( pi->id, (*m_FileList)[(int)box.GetClientData(box.GetSelection())] );
|
||||
bool isSame = (!CorePlugins.AreLoaded()) || g_Conf->FullpathMatchTest( pi->id, (*m_FileList)[(int)box.GetClientData(box.GetSelection())] );
|
||||
m_ComponentBoxes->GetConfigButton( pi->id ).Enable( isSame );
|
||||
|
||||
|
||||
if( !isSame ) evt.Skip(); // enabled Apply button! :D
|
||||
return;
|
||||
}
|
||||
|
@ -504,7 +637,7 @@ void Panels::PluginSelectorPanel::OnConfigure_Clicked( wxCommandEvent& evt )
|
|||
|
||||
const wxString filename( (*m_FileList)[(int)m_ComponentBoxes->Get(pid).GetClientData(sel)] );
|
||||
|
||||
if( g_plugins != NULL && !g_Conf->FullpathMatchTest( pid, filename ) )
|
||||
if( CorePlugins.AreLoaded() && !g_Conf->FullpathMatchTest( pid, filename ) )
|
||||
{
|
||||
Console.Warning( "(PluginSelector) Plugin name mismatch, configuration request ignored." );
|
||||
return;
|
||||
|
@ -544,7 +677,7 @@ void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt )
|
|||
else if( m_ComponentBoxes->Get(pid).GetSelection() == wxNOT_FOUND )
|
||||
{
|
||||
m_ComponentBoxes->Get(pid).SetSelection( 0 );
|
||||
m_ComponentBoxes->GetConfigButton(pid).Enable( g_plugins == NULL );
|
||||
m_ComponentBoxes->GetConfigButton(pid).Enable( CorePlugins.AreLoaded() );
|
||||
}
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
|
|
|
@ -1,396 +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/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "App.h"
|
||||
#include "MainFrame.h"
|
||||
|
||||
#include <wx/dir.h>
|
||||
#include <wx/file.h>
|
||||
|
||||
#include "Plugins.h"
|
||||
#include "GS.h"
|
||||
#include "HostGui.h"
|
||||
#include "AppConfig.h"
|
||||
|
||||
using namespace Threading;
|
||||
|
||||
static FnType_OnThreadComplete* Callback_PluginsLoadComplete = NULL;
|
||||
|
||||
// The GS plugin needs to be opened to save/load the state during plugin configuration, but
|
||||
// the window shouldn't. This blocks it. :)
|
||||
static bool s_DisableGsWindow = false;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// AppPluginManager
|
||||
// --------------------------------------------------------------------------------------
|
||||
// This extension of PluginManager provides event listener sources for plugins -- loading,
|
||||
// unloading, open, close, shutdown, etc.
|
||||
//
|
||||
// FIXME : Should this be made part of the PCSX2 core emulation? (integrated into PluginManager)
|
||||
// I'm undecided on if it makes sense more in that context or in this one (interface).
|
||||
//
|
||||
class AppPluginManager : public PluginManager
|
||||
{
|
||||
typedef PluginManager _parent;
|
||||
|
||||
public:
|
||||
AppPluginManager( const wxString (&folders)[PluginId_Count] )
|
||||
: PluginManager( folders )
|
||||
{
|
||||
SetSettingsFolder( GetSettingsFolder().ToString() );
|
||||
wxGetApp().PostPluginStatus( CorePlugins_Loaded );
|
||||
}
|
||||
|
||||
virtual ~AppPluginManager() throw()
|
||||
{
|
||||
sApp.PostPluginStatus( CorePlugins_Unloaded );
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
SetSettingsFolder( GetSettingsFolder().ToString() );
|
||||
_parent::Init();
|
||||
sApp.PostPluginStatus( CorePlugins_Init );
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
_parent::Shutdown();
|
||||
sApp.PostPluginStatus( CorePlugins_Shutdown );
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
if( !NeedsClose() ) return;
|
||||
|
||||
sApp.PostPluginStatus( CorePlugins_Closing );
|
||||
_parent::Close();
|
||||
sApp.PostPluginStatus( CorePlugins_Closed );
|
||||
}
|
||||
|
||||
void Open()
|
||||
{
|
||||
SetSettingsFolder( GetSettingsFolder().ToString() );
|
||||
|
||||
if( !NeedsOpen() ) return;
|
||||
|
||||
sApp.PostPluginStatus( CorePlugins_Opening );
|
||||
_parent::Open();
|
||||
sApp.PostPluginStatus( CorePlugins_Opened );
|
||||
}
|
||||
|
||||
// Yay, this plugin is guaranteed to always be opened first and closed last.
|
||||
bool OpenPlugin_GS()
|
||||
{
|
||||
if( GSopen2 != NULL && !s_DisableGsWindow )
|
||||
{
|
||||
sApp.OpenGsPanel();
|
||||
}
|
||||
|
||||
bool retval = _parent::OpenPlugin_GS();
|
||||
|
||||
if( g_LimiterMode == Limit_Turbo )
|
||||
GSsetVsync( false );
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Yay, this plugin is guaranteed to always be opened first and closed last.
|
||||
void ClosePlugin_GS()
|
||||
{
|
||||
if( GSopen2 == NULL || CloseViewportWithPlugins )
|
||||
{
|
||||
// All other plugins must be closed before the GS, because they all rely on
|
||||
// the GS window handle being valid. The recursion guard will protect this
|
||||
// function from being called a million times. ;)
|
||||
|
||||
static int _guard;
|
||||
RecursionGuard mess( _guard );
|
||||
if( !mess.IsReentrant() ) Close();
|
||||
}
|
||||
|
||||
_parent::ClosePlugin_GS();
|
||||
sApp.CloseGsPanel();
|
||||
}
|
||||
|
||||
|
||||
/*void Open( PluginsEnum_t pid )
|
||||
{
|
||||
_parent::Open( pid );
|
||||
}
|
||||
|
||||
void Close( PluginsEnum_t pid )
|
||||
{
|
||||
_parent::Close( pid );
|
||||
}*/
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// LoadPluginsTask
|
||||
// --------------------------------------------------------------------------------------
|
||||
// On completion the thread sends a pxEvt_LoadPluginsComplete message, which contains a
|
||||
// handle to this thread object. If the load is successful, the Result var is set to
|
||||
// non-NULL. If NULL, an error occurred and the thread loads the exception into either
|
||||
// Ex_PluginError or Ex_RuntimeError.
|
||||
//
|
||||
class LoadPluginsTask : public PersistentThread
|
||||
{
|
||||
typedef PersistentThread _parent;
|
||||
|
||||
public:
|
||||
PluginManager* Result;
|
||||
|
||||
protected:
|
||||
wxString m_folders[PluginId_Count];
|
||||
ScopedBusyCursor m_hourglass;
|
||||
public:
|
||||
LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) : Result( NULL ), m_hourglass( Cursor_KindaBusy )
|
||||
{
|
||||
for(int i=0; i<PluginId_Count; ++i )
|
||||
m_folders[i] = folders[i];
|
||||
}
|
||||
|
||||
virtual ~LoadPluginsTask() throw();
|
||||
|
||||
protected:
|
||||
void OnCleanupInThread();
|
||||
void ExecuteTaskInThread();
|
||||
};
|
||||
|
||||
LoadPluginsTask::~LoadPluginsTask() throw()
|
||||
{
|
||||
PersistentThread::Cancel();
|
||||
}
|
||||
|
||||
void LoadPluginsTask::ExecuteTaskInThread()
|
||||
{
|
||||
wxGetApp().Ping();
|
||||
|
||||
// This is for testing of the error handler... uncomment for fun?
|
||||
//throw Exception::PluginError( PluginId_PAD, "This one is for testing the error handler!" );
|
||||
|
||||
Result = new AppPluginManager( m_folders );
|
||||
}
|
||||
|
||||
void LoadPluginsTask::OnCleanupInThread()
|
||||
{
|
||||
wxGetApp().PostCommand( this, pxEvt_LoadPluginsComplete );
|
||||
_parent::OnCleanupInThread();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SaveSinglePluginHelper (Implementations)
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
SaveSinglePluginHelper::SaveSinglePluginHelper( PluginsEnum_t pid )
|
||||
: m_plugstore( L"PluginConf Savestate" )
|
||||
{
|
||||
s_DisableGsWindow = true;
|
||||
|
||||
m_whereitsat = NULL;
|
||||
m_resume = false;
|
||||
m_pid = pid;
|
||||
m_validstate = SysHasValidState();
|
||||
|
||||
if( !m_validstate ) return;
|
||||
Console.WriteLn( Color_Green, L"Suspending single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
|
||||
|
||||
m_resume = CoreThread.Pause();
|
||||
m_whereitsat = StateCopy_GetBuffer();
|
||||
|
||||
if( m_whereitsat == NULL )
|
||||
{
|
||||
m_whereitsat = &m_plugstore;
|
||||
memSavingState save( m_plugstore );
|
||||
g_plugins->Freeze( m_pid, save );
|
||||
}
|
||||
|
||||
g_plugins->Close( pid );
|
||||
}
|
||||
|
||||
SaveSinglePluginHelper::~SaveSinglePluginHelper() throw()
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
if( m_validstate )
|
||||
{
|
||||
Console.WriteLn( Color_Green, L"Recovering single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
|
||||
memLoadingState load( *m_whereitsat );
|
||||
if( m_plugstore.IsDisposed() ) load.SeekToSection( m_pid );
|
||||
g_plugins->Freeze( m_pid, load );
|
||||
g_plugins->Close( m_pid );
|
||||
}
|
||||
|
||||
s_DisableGsWindow = false;
|
||||
if( m_resume ) CoreThread.Resume();
|
||||
}
|
||||
DESTRUCTOR_CATCHALL;
|
||||
|
||||
s_DisableGsWindow = false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest )
|
||||
{
|
||||
ScopedPtr<wxArrayString> placebo;
|
||||
wxArrayString* realdest = dest;
|
||||
if( realdest == NULL )
|
||||
placebo = realdest = new wxArrayString(); // placebo is our /dev/null -- gets deleted when done
|
||||
|
||||
#ifdef __WXMSW__
|
||||
// Windows pretty well has a strict "must end in .dll" rule.
|
||||
wxString pattern( L"*%s" );
|
||||
#else
|
||||
// Other platforms seem to like to version their libs after the .so extension:
|
||||
// blah.so.3.1.fail?
|
||||
wxString pattern( L"*%s*" );
|
||||
#endif
|
||||
|
||||
return searchpath.Exists() ?
|
||||
wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( pattern, wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0;
|
||||
}
|
||||
|
||||
void ConvertPluginFilenames( wxString (&passins)[PluginId_Count] )
|
||||
{
|
||||
const PluginInfo* pi = tbl_PluginInfo; do
|
||||
{
|
||||
passins[pi->id] = OverrideOptions.Filenames[pi->id].GetFullPath();
|
||||
|
||||
if( passins[pi->id].IsEmpty() || !wxFileExists( passins[pi->id] ) )
|
||||
passins[pi->id] = g_Conf->FullpathTo( pi->id );
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
}
|
||||
|
||||
// boolean lock modified from the main thread only...
|
||||
static LoadPluginsTask* plugin_load_lock = NULL;
|
||||
|
||||
void Pcsx2App::ReloadPlugins()
|
||||
{
|
||||
if( InvokeMethodOnMainThread( &Pcsx2App::ReloadPlugins ) ) return;
|
||||
|
||||
if( plugin_load_lock ) return;
|
||||
UnloadPlugins();
|
||||
|
||||
wxString passins[PluginId_Count];
|
||||
|
||||
const PluginInfo* pi = tbl_PluginInfo; do
|
||||
{
|
||||
passins[pi->id] = OverrideOptions.Filenames[pi->id].GetFullPath();
|
||||
|
||||
if( passins[pi->id].IsEmpty() || !wxFileExists( passins[pi->id] ) )
|
||||
passins[pi->id] = g_Conf->FullpathTo( pi->id );
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
plugin_load_lock = new LoadPluginsTask(passins);
|
||||
plugin_load_lock->Start();
|
||||
|
||||
// ... and when it finishes it posts up a OnLoadPluginsComplete(). Bye. :)
|
||||
}
|
||||
|
||||
// Note: If the ClientData parameter of wxCommandEvt is NULL, this message simply dispatches
|
||||
// the plugged in listeners.
|
||||
void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt )
|
||||
{
|
||||
FnType_OnThreadComplete* fn_tmp = Callback_PluginsLoadComplete;
|
||||
|
||||
if( LoadPluginsTask* pluginthread = (LoadPluginsTask*)evt.GetClientData() )
|
||||
{
|
||||
// scoped ptr ensures the thread object is cleaned up even on exception:
|
||||
ScopedPtr<LoadPluginsTask> killTask( pluginthread );
|
||||
|
||||
pxAssume( plugin_load_lock == pluginthread );
|
||||
plugin_load_lock = NULL;
|
||||
|
||||
if( !pxAssertDev( !m_CorePlugins, "LoadPlugins thread just finished, but CorePlugins state != NULL (odd!)." ) )
|
||||
m_CorePlugins = NULL;
|
||||
|
||||
killTask->RethrowException();
|
||||
m_CorePlugins = killTask->Result;
|
||||
}
|
||||
|
||||
if( fn_tmp != NULL ) fn_tmp( evt );
|
||||
}
|
||||
|
||||
void Pcsx2App::CancelLoadingPlugins()
|
||||
{
|
||||
if( plugin_load_lock )
|
||||
plugin_load_lock->Cancel();
|
||||
}
|
||||
|
||||
void Pcsx2App::PostPluginStatus( PluginEventType pevt )
|
||||
{
|
||||
if( !wxThread::IsMain() )
|
||||
{
|
||||
PostCommand( pxEvt_PluginStatus, pevt );
|
||||
}
|
||||
else
|
||||
{
|
||||
sApp.DispatchEvent( pevt );
|
||||
}
|
||||
}
|
||||
|
||||
void Pcsx2App::OnPluginStatus( wxCommandEvent& evt )
|
||||
{
|
||||
PostPluginStatus( (PluginEventType)evt.GetInt() );
|
||||
}
|
||||
|
||||
// Posts a message to the App to reload plugins. Plugins are loaded via a background thread
|
||||
// which is started on a pending event, so don't expect them to be ready "right now."
|
||||
// If plugins are already loaded, onComplete is invoked, and the function returns with no
|
||||
// other actions performed.
|
||||
void LoadPluginsPassive( FnType_OnThreadComplete* onComplete )
|
||||
{
|
||||
// Plugins already loaded?
|
||||
if( wxGetApp().m_CorePlugins )
|
||||
{
|
||||
if( onComplete ) onComplete( wxCommandEvent( pxEvt_LoadPluginsComplete ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if( onComplete )
|
||||
Callback_PluginsLoadComplete = onComplete;
|
||||
|
||||
wxGetApp().ReloadPlugins();
|
||||
}
|
||||
|
||||
// Performs a blocking load of plugins. If the emulation thread is active, it is shut down
|
||||
// automatically to prevent race conditions (it depends on plugins).
|
||||
//
|
||||
// Exceptions regarding plugin failures will propagate out of this function, so be prepared
|
||||
// to handle them.
|
||||
//
|
||||
// Note that this is not recommended for most situations, but coding improper passive loads
|
||||
// is probably worse, so if in doubt use this and air will fix it up for you later. :)
|
||||
//
|
||||
void LoadPluginsImmediate()
|
||||
{
|
||||
if( g_plugins != NULL ) return;
|
||||
|
||||
CoreThread.Cancel();
|
||||
|
||||
wxString passins[PluginId_Count];
|
||||
ConvertPluginFilenames( passins );
|
||||
wxGetApp().m_CorePlugins = new AppPluginManager( passins );
|
||||
}
|
||||
|
||||
void UnloadPlugins()
|
||||
{
|
||||
CoreThread.Cancel();
|
||||
sApp.m_CorePlugins = NULL;
|
||||
}
|
|
@ -46,7 +46,7 @@ void RecentIsoManager::OnChangedSelection( wxCommandEvent& evt )
|
|||
{
|
||||
if( (m_Items[i].ItemPtr != NULL) && (m_Items[i].ItemPtr->GetId() == evt.GetId()) ) break;
|
||||
}
|
||||
|
||||
|
||||
if( i >= m_Items.size() )
|
||||
{
|
||||
evt.Skip();
|
||||
|
@ -55,9 +55,11 @@ void RecentIsoManager::OnChangedSelection( wxCommandEvent& evt )
|
|||
|
||||
m_cursel = i;
|
||||
|
||||
bool resume = CoreThread.Suspend();
|
||||
// TODO: Dialog asking for hotswap or reset!!!!
|
||||
|
||||
ScopedCoreThreadClose stopped_core;
|
||||
SysUpdateIsoSrcFile( m_Items[i].Filename );
|
||||
if( resume ) CoreThread.Resume();
|
||||
stopped_core.AllowResume();
|
||||
}
|
||||
|
||||
void RecentIsoManager::RemoveAllFromMenu()
|
||||
|
@ -72,7 +74,7 @@ void RecentIsoManager::RemoveAllFromMenu()
|
|||
m_Menu->Destroy( curitem.ItemPtr );
|
||||
curitem.ItemPtr = NULL;
|
||||
}
|
||||
|
||||
|
||||
if( m_Separator != NULL )
|
||||
{
|
||||
m_Menu->Destroy( m_Separator );
|
||||
|
@ -93,7 +95,7 @@ void RecentIsoManager::Repopulate()
|
|||
if( cnt <= 0 ) return;
|
||||
|
||||
m_Separator = m_Menu->AppendSeparator();
|
||||
|
||||
|
||||
for( int i=0; i<cnt; ++i )
|
||||
InsertIntoMenu( i );
|
||||
}
|
||||
|
@ -172,7 +174,7 @@ void RecentIsoManager::AppStatusEvent_OnSettingsLoadSave( const AppSettingsEvent
|
|||
else
|
||||
{
|
||||
RemoveAllFromMenu();
|
||||
|
||||
|
||||
m_MaxLength = g_Conf->RecentIsoCount;
|
||||
IniScopedGroup groupie( ini, L"RecentIso" );
|
||||
for( uint i=0; i<m_MaxLength; ++i )
|
||||
|
|
|
@ -0,0 +1,310 @@
|
|||
/* 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 te License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "App.h"
|
||||
|
||||
#include "SysThreads.h"
|
||||
#include "SaveState.h"
|
||||
|
||||
#include "ZipTools\ThreadedZipTools.h"
|
||||
|
||||
// Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
|
||||
static SafeArray<u8> state_buffer( L"Public Savestate Buffer" );
|
||||
|
||||
static const char SavestateIdentString[] = "PCSX2 Savestate";
|
||||
static const uint SavestateIdentLen = sizeof(SavestateIdentString);
|
||||
|
||||
static void SaveStateFile_WriteHeader( IStreamWriter& thr )
|
||||
{
|
||||
thr.Write( SavestateIdentString );
|
||||
thr.Write( g_SaveVersion );
|
||||
}
|
||||
|
||||
static void SaveStateFile_ReadHeader( IStreamReader& thr )
|
||||
{
|
||||
char ident[SavestateIdentLen] = {0};
|
||||
|
||||
thr.Read( ident );
|
||||
|
||||
if( strcmp(SavestateIdentString, ident) )
|
||||
throw Exception::SaveStateLoadError( thr.GetStreamName(),
|
||||
wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ),
|
||||
_("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.")
|
||||
);
|
||||
|
||||
u32 savever;
|
||||
thr.Read( savever );
|
||||
|
||||
if( (savever >> 16) != (g_SaveVersion >> 16) )
|
||||
throw Exception::SaveStateLoadError( thr.GetStreamName(),
|
||||
wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ),
|
||||
_("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.")
|
||||
);
|
||||
|
||||
if( savever > g_SaveVersion )
|
||||
throw Exception::SaveStateLoadError( thr.GetStreamName(),
|
||||
wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ),
|
||||
_("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.")
|
||||
);
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// gzipReader
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Interface for reading data from a gzip stream.
|
||||
//
|
||||
class gzipReader : public IStreamReader
|
||||
{
|
||||
protected:
|
||||
wxString m_filename;
|
||||
gzFile m_gzfp;
|
||||
|
||||
public:
|
||||
gzipReader( const wxString& filename )
|
||||
: m_filename( filename )
|
||||
{
|
||||
if( NULL == (m_gzfp = gzopen( m_filename.ToUTF8(), "rb" )) )
|
||||
throw Exception::CannotCreateStream( m_filename, "Cannot open file for reading." );
|
||||
|
||||
gzbuffer(m_gzfp, 0x100000); // 1mb buffer for zlib internal operations
|
||||
}
|
||||
|
||||
virtual ~gzipReader() throw ()
|
||||
{
|
||||
if( m_gzfp ) gzclose( m_gzfp );
|
||||
}
|
||||
|
||||
wxString GetStreamName() const { return m_filename; }
|
||||
|
||||
void Read( void* dest, size_t size )
|
||||
{
|
||||
int result = gzread( m_gzfp, dest, size );
|
||||
if( result == -1)
|
||||
throw Exception::BadStream( m_filename, "Data read failed: Invalid or corrupted gzip archive." );
|
||||
|
||||
if( (size_t)result < size )
|
||||
throw Exception::EndOfStream( m_filename );
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent_DownloadState
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Pauses core emulation and downloads the savestate into the state_buffer.
|
||||
//
|
||||
class SysExecEvent_DownloadState : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
SafeArray<u8>* m_dest_buffer;
|
||||
|
||||
public:
|
||||
virtual ~SysExecEvent_DownloadState() throw() {}
|
||||
SysExecEvent_DownloadState* Clone() const { return new SysExecEvent_DownloadState( *this ); }
|
||||
SysExecEvent_DownloadState( SafeArray<u8>* dest=&state_buffer )
|
||||
{
|
||||
m_dest_buffer = dest;
|
||||
}
|
||||
|
||||
bool AllowCancelOnExit() const { return false; }
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
ScopedCoreThreadPause paused_core;
|
||||
|
||||
if( !SysHasValidState() )
|
||||
throw Exception::RuntimeError( L"Cannot complete state freeze request; the virtual machine state is reset.", _("You'll need to start a new virtual machine before you can save its state.") );
|
||||
|
||||
memSavingState(m_dest_buffer).FreezeAll();
|
||||
|
||||
paused_core.AllowResume();
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent_ZipToDisk
|
||||
// --------------------------------------------------------------------------------------
|
||||
class SysExecEvent_ZipToDisk : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
SafeArray<u8>* m_src_buffer;
|
||||
wxString m_filename;
|
||||
|
||||
public:
|
||||
wxString GetEventName() const { return L"SysState_ZipToDisk"; }
|
||||
|
||||
virtual ~SysExecEvent_ZipToDisk() throw()
|
||||
{
|
||||
delete m_src_buffer;
|
||||
}
|
||||
|
||||
SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); }
|
||||
|
||||
SysExecEvent_ZipToDisk( ScopedPtr<SafeArray<u8>>& src, const wxString& filename )
|
||||
: m_filename( filename )
|
||||
{
|
||||
m_src_buffer = src.DetachPtr();
|
||||
}
|
||||
|
||||
SysExecEvent_ZipToDisk( SafeArray<u8>* src, const wxString& filename )
|
||||
: m_filename( filename )
|
||||
{
|
||||
m_src_buffer = src;
|
||||
}
|
||||
|
||||
bool IsCriticalEvent() const { return true; }
|
||||
bool AllowCancelOnExit() const { return false; }
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
(new CompressThread_gzip( m_filename, m_src_buffer, SaveStateFile_WriteHeader ))->Start();
|
||||
m_src_buffer = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent_UnzipFromDisk
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Note: Unzipping always goes directly into the state_buffer, and is always a blocking
|
||||
// action on the SysExecutor thread (the system cannot execute other commands while states
|
||||
// are unzipping or uplading into the system).
|
||||
//
|
||||
class SysExecEvent_UnzipFromDisk : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
wxString m_filename;
|
||||
gzipReader m_gzreader;
|
||||
|
||||
public:
|
||||
wxString GetEventName() const { return L"SysState_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()
|
||||
{
|
||||
// We use direct Suspend/Resume control here, since it's desirable that emulation
|
||||
// *ALWAYS* start execution after the new savestate is loaded.
|
||||
|
||||
GetCoreThread().Pause();
|
||||
|
||||
// fixme: should start initially with the file size, and then grow from there.
|
||||
|
||||
static const int BlockSize = 0x100000;
|
||||
state_buffer.MakeRoomFor( 0x800000 ); // start with an 8 meg buffer to avoid frequent reallocation.
|
||||
int curidx = 0;
|
||||
|
||||
try {
|
||||
while(true) {
|
||||
state_buffer.MakeRoomFor( curidx+BlockSize );
|
||||
m_gzreader.Read( state_buffer.GetPtr(curidx), BlockSize );
|
||||
curidx += BlockSize;
|
||||
Threading::pxTestCancel();
|
||||
}
|
||||
}
|
||||
catch( Exception::EndOfStream& )
|
||||
{
|
||||
// This exception actually means success! Any others we let get sent
|
||||
// to the main event handler/thread for handling.
|
||||
}
|
||||
|
||||
// Optional shutdown of plugins when loading states? I'm not implementing it yet because some
|
||||
// things, like the SPU2-recovery trick, rely on not resetting the plugins prior to loading
|
||||
// the new savestate data.
|
||||
|
||||
//if( ShutdownOnStateLoad ) GetCoreThread().Cancel();
|
||||
GetCoreThread().RecoverState();
|
||||
GetCoreThread().Resume(); // force resume regardless of emulation state earlier.
|
||||
}
|
||||
};
|
||||
|
||||
// =====================================================================================================
|
||||
// StateCopy Public Interface
|
||||
// =====================================================================================================
|
||||
|
||||
SafeArray<u8>& StateCopy_GetBuffer()
|
||||
{
|
||||
return state_buffer;
|
||||
}
|
||||
|
||||
bool StateCopy_IsValid()
|
||||
{
|
||||
return !state_buffer.IsDisposed();
|
||||
}
|
||||
|
||||
void StateCopy_FreezeToMem()
|
||||
{
|
||||
GetSysExecutorThread().PostEvent( new SysExecEvent_DownloadState() );
|
||||
}
|
||||
|
||||
void StateCopy_SaveToFile( const wxString& file )
|
||||
{
|
||||
ScopedPtr<SafeArray<u8>> zipbuf(new SafeArray<u8>( L"Zippable Savestate" ));
|
||||
GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState( zipbuf ));
|
||||
GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk( zipbuf, file ));
|
||||
}
|
||||
|
||||
void StateCopy_LoadFromFile( const wxString& file )
|
||||
{
|
||||
UI_DisableSysActions();
|
||||
GetSysExecutorThread().PostEvent(new SysExecEvent_UnzipFromDisk( file ));
|
||||
}
|
||||
|
||||
// Saves recovery state info to the given saveslot, or saves the active emulation state
|
||||
// (if one exists) and no recovery data was found. This is needed because when a recovery
|
||||
// state is made, the emulation state is usually reset so the only persisting state is
|
||||
// the one in the memory save. :)
|
||||
void StateCopy_SaveToSlot( uint num )
|
||||
{
|
||||
const wxString file( SaveStateBase::GetFilename( num ) );
|
||||
|
||||
Console.WriteLn( Color_StrongGreen, "Saving savestate to slot %d...", num );
|
||||
Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() );
|
||||
|
||||
StateCopy_SaveToFile( file );
|
||||
}
|
||||
|
||||
void StateCopy_LoadFromSlot( uint slot )
|
||||
{
|
||||
wxString file( SaveStateBase::GetFilename( slot ) );
|
||||
|
||||
if( !wxFileExists( file ) )
|
||||
{
|
||||
Console.Warning( "Savestate slot %d is empty.", slot );
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLn( Color_StrongGreen, "Loading savestate from slot %d...", slot );
|
||||
Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() );
|
||||
|
||||
StateCopy_LoadFromFile( file );
|
||||
}
|
||||
|
||||
void StateCopy_Clear()
|
||||
{
|
||||
state_buffer.Dispose();
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/* 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 "Mainframe.h"
|
||||
#include "GSFrame.h"
|
||||
|
||||
|
||||
static void _SaveLoadStuff( bool enabled )
|
||||
{
|
||||
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_LoadStates, enabled );
|
||||
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_SaveStates, enabled );
|
||||
}
|
||||
|
||||
// Updates the enable/disable status of all System related controls: menus, toolbars,
|
||||
// etc. Typically called by SysEvtHandler whenever the message pump becomes idle.
|
||||
void UI_UpdateSysControls()
|
||||
{
|
||||
if( wxGetApp().PostMethodMyself( &UI_UpdateSysControls ) ) return;
|
||||
|
||||
sApp.PostAction( CoreThreadStatusEvent( CoreThread_Indeterminate ) );
|
||||
|
||||
_SaveLoadStuff( true );
|
||||
}
|
||||
|
||||
|
||||
void UI_DisableSysReset()
|
||||
{
|
||||
if( wxGetApp().PostMethodMyself( UI_DisableSysReset ) ) return;
|
||||
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Restart, false );
|
||||
|
||||
_SaveLoadStuff( false );
|
||||
}
|
||||
|
||||
void UI_DisableSysShutdown()
|
||||
{
|
||||
if( wxGetApp().PostMethodMyself( &UI_DisableSysShutdown ) ) return;
|
||||
|
||||
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Restart, false );
|
||||
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Shutdown, false );
|
||||
}
|
||||
|
||||
void UI_EnableSysShutdown()
|
||||
{
|
||||
if( wxGetApp().PostMethodMyself( &UI_EnableSysShutdown ) ) return;
|
||||
|
||||
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Restart, true );
|
||||
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Shutdown, true );
|
||||
}
|
||||
|
||||
|
||||
void UI_DisableSysActions()
|
||||
{
|
||||
if( wxGetApp().PostMethodMyself( &UI_DisableSysShutdown ) ) return;
|
||||
|
||||
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Restart, false );
|
||||
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Shutdown, false );
|
||||
}
|
||||
|
||||
void UI_EnableSysActions()
|
||||
{
|
||||
if( wxGetApp().PostMethodMyself( &UI_EnableSysShutdown ) ) return;
|
||||
|
||||
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Restart, true );
|
||||
sMainFrame.GetMenuBar()->Enable( MenuId_Sys_Shutdown, true );
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
/* 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 "Utilities/PersistentThread.h"
|
||||
#include "Utilities/pxEvents.h"
|
||||
|
||||
// TODO!! Make this system a bit more generic, and then move it to the Utilities library.
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
class SysExecEvent
|
||||
: public IActionInvocation
|
||||
, public ICloneable
|
||||
{
|
||||
protected:
|
||||
SynchronousActionState* m_sync;
|
||||
|
||||
public:
|
||||
virtual ~SysExecEvent() throw() {}
|
||||
SysExecEvent* Clone() const { return new SysExecEvent( *this ); }
|
||||
|
||||
SysExecEvent( SynchronousActionState* sync=NULL )
|
||||
{
|
||||
m_sync = sync;
|
||||
}
|
||||
|
||||
SysExecEvent( SynchronousActionState& sync )
|
||||
{
|
||||
m_sync = &sync;
|
||||
}
|
||||
|
||||
const SynchronousActionState* GetSyncState() const { return m_sync; }
|
||||
SynchronousActionState* GetSyncState() { return m_sync; }
|
||||
|
||||
void SetSyncState( SynchronousActionState* obj ) { m_sync = obj; }
|
||||
void SetSyncState( SynchronousActionState& obj ) { m_sync = &obj; }
|
||||
|
||||
// Tells the Event Handler whether or not this event can be skipped when the system
|
||||
// is being quit or reset. Typically set this to true for events which shut down the
|
||||
// system, since program crashes can occur if the program tries to exit while threads
|
||||
// are running.
|
||||
virtual bool IsCriticalEvent() const { return false; }
|
||||
|
||||
// Tells the Event Handler whether or not this event can be canceled. Typically events
|
||||
// should not prohibit cancellation, since it expedites program termination and helps
|
||||
// avoid permanent deadlock. Some actions like saving states and shutdown procedures
|
||||
// should not allow cancellation since they could result in program crashes or corrupted
|
||||
// data.
|
||||
virtual bool AllowCancelOnExit() const { return true; }
|
||||
|
||||
virtual void InvokeAction();
|
||||
virtual void PostResult() const;
|
||||
|
||||
virtual wxString GetEventName() const { return wxEmptyString; }
|
||||
virtual wxString GetEventMessage() const { return wxEmptyString; }
|
||||
|
||||
virtual int GetResult()
|
||||
{
|
||||
if( !pxAssertDev( m_sync != NULL, "SysEvent: Expecting return value, but no sync object provided." ) ) return 0;
|
||||
return m_sync->return_value;
|
||||
}
|
||||
|
||||
virtual void SetException( BaseException* ex );
|
||||
|
||||
void SetException( const BaseException& ex );
|
||||
|
||||
protected:
|
||||
virtual void _DoInvoke();
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent_Method
|
||||
// --------------------------------------------------------------------------------------
|
||||
class SysExecEvent_Method : public SysExecEvent
|
||||
{
|
||||
protected:
|
||||
FnType_Void* m_method;
|
||||
|
||||
public:
|
||||
virtual ~SysExecEvent_Method() throw() {}
|
||||
SysExecEvent_Method* Clone() const { return new SysExecEvent_Method( *this ); }
|
||||
|
||||
explicit SysExecEvent_Method( FnType_Void* method = NULL )
|
||||
{
|
||||
m_method = method;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _DoInvoke()
|
||||
{
|
||||
if( m_method ) m_method();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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. -_-
|
||||
//
|
||||
class pxEvtHandler
|
||||
{
|
||||
protected:
|
||||
pxEvtList m_pendingEvents;
|
||||
Threading::MutexRecursive m_mtx_pending;
|
||||
Threading::Semaphore m_wakeup;
|
||||
wxThreadIdType m_OwnerThreadId;
|
||||
volatile u32 m_Quitting;
|
||||
|
||||
public:
|
||||
pxEvtHandler();
|
||||
virtual ~pxEvtHandler() throw() {}
|
||||
|
||||
virtual wxString GetEventHandlerName() const { return L"pxEvtHandler"; }
|
||||
|
||||
virtual void ShutdownQueue();
|
||||
bool IsShuttingDown() const { return !!m_Quitting; }
|
||||
|
||||
void ProcessPendingEvents();
|
||||
void AddPendingEvent( SysExecEvent& evt );
|
||||
void PostEvent( SysExecEvent* evt );
|
||||
void PostEvent( const SysExecEvent& evt );
|
||||
|
||||
void ProcessEvent( SysExecEvent* evt );
|
||||
void ProcessEvent( SysExecEvent& evt );
|
||||
|
||||
bool SelfProcessMethod( FnType_Void* method );
|
||||
|
||||
void Idle();
|
||||
|
||||
void SetActiveThread();
|
||||
|
||||
protected:
|
||||
virtual void DoIdle() {}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ExecutorThread
|
||||
// --------------------------------------------------------------------------------------
|
||||
class ExecutorThread : public Threading::PersistentThread
|
||||
{
|
||||
typedef Threading::PersistentThread _parent;
|
||||
|
||||
protected:
|
||||
ScopedPtr<wxTimer> m_ExecutorTimer;
|
||||
ScopedPtr<pxEvtHandler> m_EvtHandler;
|
||||
|
||||
public:
|
||||
ExecutorThread( pxEvtHandler* evtandler = NULL );
|
||||
virtual ~ExecutorThread() throw() { }
|
||||
|
||||
virtual void ShutdownQueue();
|
||||
|
||||
void PostEvent( SysExecEvent* evt );
|
||||
void PostEvent( const SysExecEvent& evt );
|
||||
|
||||
void ProcessEvent( SysExecEvent* evt );
|
||||
void ProcessEvent( SysExecEvent& evt );
|
||||
|
||||
bool SelfProcessMethod( void (*evt)() )
|
||||
{
|
||||
return m_EvtHandler ? m_EvtHandler->SelfProcessMethod( evt ) : false;
|
||||
}
|
||||
|
||||
protected:
|
||||
void OnStart();
|
||||
void ExecuteTaskInThread();
|
||||
void OnCleanupInThread();
|
||||
};
|
||||
|
|
@ -83,7 +83,7 @@ void pxLogTextCtrl::OnThumbTrack(wxScrollWinEvent& evt)
|
|||
//Console.Warning( "Thumb Tracking!!!" );
|
||||
m_FreezeWrites = true;
|
||||
if( !m_IsPaused )
|
||||
m_IsPaused = CoreThread.Pause();
|
||||
m_IsPaused = new ScopedCoreThreadPause();
|
||||
|
||||
evt.Skip();
|
||||
}
|
||||
|
@ -94,8 +94,8 @@ void pxLogTextCtrl::OnThumbRelease(wxScrollWinEvent& evt)
|
|||
m_FreezeWrites = false;
|
||||
if( m_IsPaused )
|
||||
{
|
||||
CoreThread.Resume();
|
||||
m_IsPaused = false;
|
||||
m_IsPaused->AllowResume();
|
||||
m_IsPaused.Delete();
|
||||
}
|
||||
evt.Skip();
|
||||
}
|
||||
|
|
|
@ -1880,6 +1880,10 @@
|
|||
RelativePath="..\..\gui\AppConfig.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\AppCorePlugins.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\AppCoreThread.cpp"
|
||||
>
|
||||
|
@ -1940,6 +1944,10 @@
|
|||
RelativePath="..\..\gui\CpuUsageProviderMSW.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\ExecutorThread.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\FrameForGS.cpp"
|
||||
>
|
||||
|
@ -1980,10 +1988,6 @@
|
|||
RelativePath="..\..\gui\MSWstuff.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\Plugins.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\pxLogTextCtrl.cpp"
|
||||
>
|
||||
|
@ -1993,11 +1997,7 @@
|
|||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\RecoverySystem.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\Saveslots.cpp"
|
||||
RelativePath="..\..\gui\UpdateUI.cpp"
|
||||
>
|
||||
</File>
|
||||
<Filter
|
||||
|
@ -2653,6 +2653,14 @@
|
|||
RelativePath="..\..\gui\AppConfig.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\AppCorePlugins.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\AppCoreThread.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\AppEventListeners.h"
|
||||
>
|
||||
|
@ -2681,6 +2689,10 @@
|
|||
RelativePath="..\..\gui\Resources\EmbeddedImage.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\GSFrame.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\HostGui.h"
|
||||
>
|
||||
|
@ -2697,6 +2709,10 @@
|
|||
RelativePath="..\..\gui\MainFrame.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\pxEventThread.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\RecentIsoList.h"
|
||||
>
|
||||
|
@ -2822,6 +2838,38 @@
|
|||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="VM States"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\gui\Saveslots.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\SysState.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="ZipTools"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\ZipTools\FolderDesc.txt"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\ZipTools\thread_gzip.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\ZipTools\thread_lzma.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\ZipTools\ThreadedZipTools.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
|
|
|
@ -32,10 +32,10 @@ void StreamException_ThrowFromErrno( const wxString& streamname, errno_t errcode
|
|||
throw Exception::AccessDenied( streamname );
|
||||
|
||||
case EMFILE: // Too many open files!
|
||||
throw Exception::CreateStream( streamname, "Too many open files" ); // File handle allocation failure
|
||||
throw Exception::CannotCreateStream( streamname, "Too many open files" ); // File handle allocation failure
|
||||
|
||||
case EEXIST:
|
||||
throw Exception::CreateStream( streamname, "File already exists" );
|
||||
throw Exception::CannotCreateStream( streamname, "File already exists" );
|
||||
|
||||
case ENOENT: // File not found!
|
||||
throw Exception::FileNotFound( streamname );
|
||||
|
@ -70,7 +70,7 @@ void StreamException_ThrowLastError( const wxString& streamname, HANDLE result )
|
|||
throw Exception::FileNotFound( streamname );
|
||||
|
||||
case ERROR_TOO_MANY_OPEN_FILES:
|
||||
throw Exception::CreateStream( streamname, "Too many open files" );
|
||||
throw Exception::CannotCreateStream( streamname, "Too many open files" );
|
||||
|
||||
case ERROR_ACCESS_DENIED:
|
||||
throw Exception::AccessDenied( streamname );
|
||||
|
|
|
@ -226,7 +226,7 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream )
|
|||
Cleanup();
|
||||
throw Exception::RuntimeError( ex.FormatDiagnosticMessage(), ex.FormatDisplayMessage() );
|
||||
}
|
||||
catch( Exception::BaseException& ex )
|
||||
catch( BaseException& ex )
|
||||
{
|
||||
Cleanup();
|
||||
ex.DiagMsg() = (wxString)((stdstream==stdout) ? L"STDOUT" : L"STDERR") + L" Redirection Init failed: " + ex.DiagMsg();
|
||||
|
|
Loading…
Reference in New Issue