mirror of https://github.com/PCSX2/pcsx2.git
User Interface:
* Fixed and added better Emulation/System menu updating. Suspend/Resume is more consistent, and Reset grays itself out after being used. * Entering plugin configurations auto-suspends the emulator. * Changing plugins in the Configuration PAnel takes effect now without a restart. * Added preliminary support for an ExtensibleConfirmation Dialog box (contains a sizer you can add content to, and also has an optional "[x] Do not show this again" checkbox). Bugfixes: * Added some mutex protection to cdvdNewDiskCB; "just in case." * Resolved several recursion and deadlock scenarios when (very!) rapidly suspending, resuming, and resetting the emu. Developments / Code Cleanups: * Renamed SysCoreThread ExecutionModes: Suspend/Resume are now Opened/Closed (which more accurately reflects the fact they opena nd close the plugins, and helps avoid ambiguity with the "Paused" state). * Added Exception::ThreadTimedOut, which is now thrown from Semaphore::Wait when recursive wxApp::Yield() calls are detected, and a deadlock occurs (basically cancels the current action which, most of the time, allows for full recovery). * Major Threading namespace cleanups, documentations, etc. * Removed wxScopedArray (scopedarray.h) and replaced it with a better implemeneted ScopedArray class. * Removed toUTF8 class, which I only added a couple weeks ago because I didn't realize wxCharBuffer had an implicit typecast to (char*). * Implemented more Source/Listener events for Pcsx2App. CoreThread events are sourced properly now, and added SettingsApplied and SettingsLoadSave Sources. git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2010 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
adf4b7ae50
commit
1f2daa6459
|
@ -294,6 +294,12 @@ namespace Exception
|
|||
DEFINE_RUNTIME_EXCEPTION( ThreadCreationError, wxLt("Thread could not be created.") );
|
||||
};
|
||||
|
||||
class ThreadTimedOut : public virtual RuntimeError
|
||||
{
|
||||
public:
|
||||
DEFINE_RUNTIME_EXCEPTION( ThreadTimedOut, "Blocking action timed out due to potential deadlock." );
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Streaming (file) Exceptions:
|
||||
// Stream / BadStream / CreateStream / FileNotFound / AccessDenied / EndOfStream
|
||||
|
|
|
@ -308,11 +308,11 @@ public:
|
|||
if( m_ptr == NULL )
|
||||
{
|
||||
throw Exception::OutOfMemory(
|
||||
// English Diagonstic message:
|
||||
// English Diagnostic message:
|
||||
wxsFormat(
|
||||
L"Out-of-memory on SafeList block re-allocation.\n"
|
||||
L"Old size: %d bytes, New size: %d bytes",
|
||||
m_allocsize, newalloc
|
||||
L"Name: %s, Old size: %d bytes, New size: %d bytes",
|
||||
Name, m_allocsize, newalloc
|
||||
),
|
||||
|
||||
wxsFormat( _("Out of memory, trying to allocate %d bytes."), newalloc )
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <wx/scopedarray.h>
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ScopedPtr
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -19,7 +17,7 @@ public:
|
|||
|
||||
wxEXPLICIT ScopedPtr(T * ptr = NULL) : m_ptr(ptr) { }
|
||||
|
||||
~ScopedPtr()
|
||||
~ScopedPtr() throw()
|
||||
{ Delete(); }
|
||||
|
||||
ScopedPtr& Reassign(T * ptr = NULL)
|
||||
|
@ -32,7 +30,7 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
ScopedPtr& Delete()
|
||||
ScopedPtr& Delete() throw()
|
||||
{
|
||||
// Thread-safe deletion: Set the pointer to NULL first, and then issue
|
||||
// the deletion. This allows pending Application messages that might be
|
||||
|
@ -118,6 +116,125 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ScopedArray - same as ScopedPtr but uses delete[], and has operator[]
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
template< typename T >
|
||||
class ScopedArray
|
||||
{
|
||||
DeclareNoncopyableObject(ScopedArray);
|
||||
|
||||
protected:
|
||||
T* m_array;
|
||||
uint m_valid_range;
|
||||
public:
|
||||
typedef T element_type;
|
||||
|
||||
wxEXPLICIT ScopedArray(T * ptr = NULL) :
|
||||
m_array(ptr)
|
||||
, m_valid_range( 0xffffffff )
|
||||
{
|
||||
}
|
||||
|
||||
wxEXPLICIT ScopedArray( int size ) :
|
||||
m_array( pxAssertDev( size >= 0, "Invalid negative size specified." ) ? new T[size] : NULL )
|
||||
, m_valid_range( (uint)size )
|
||||
{
|
||||
}
|
||||
|
||||
// For breaking the 2gb barrier, lets provision this:
|
||||
wxEXPLICIT ScopedArray( s64 size ) :
|
||||
m_array( pxAssertDev( size >= 0 && (size < UINT_MAX), "Invalid negative size specified to ScopedArray." ) ? new T[size] : NULL )
|
||||
, m_valid_range( (uint)size )
|
||||
{
|
||||
}
|
||||
|
||||
~ScopedArray() throw()
|
||||
{ Delete(); }
|
||||
|
||||
ScopedArray& Reassign(T * ptr = NULL)
|
||||
{
|
||||
if( ptr != m_array )
|
||||
{
|
||||
Delete();
|
||||
m_array = ptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ScopedArray& Delete() throw()
|
||||
{
|
||||
// Thread-safe deletion: Set the pointer to NULL first, and then issue
|
||||
// the deletion. This allows pending Application messages that might be
|
||||
// dependent on the current object to nullify their actions.
|
||||
|
||||
T* deleteme = m_array;
|
||||
m_array = NULL;
|
||||
delete[] deleteme;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Removes the pointer from scoped management, but does not delete!
|
||||
T *DetachPtr()
|
||||
{
|
||||
T *ptr = m_array;
|
||||
m_array = NULL;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Returns the managed pointer. Can return NULL as a valid result if the ScopedPtr
|
||||
// has no object in management.
|
||||
T* GetPtr() const
|
||||
{
|
||||
return m_array;
|
||||
}
|
||||
|
||||
void SwapPtr(ScopedArray& other)
|
||||
{
|
||||
T * const tmp = other.m_array;
|
||||
other.m_array = m_array;
|
||||
m_array = tmp;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ScopedPtr Operators
|
||||
// ----------------------------------------------------------------------------
|
||||
// I've decided to use the ATL's approach to pointer validity tests, opposed to
|
||||
// the wx/boost approach (which uses some bizarre member method pointer crap, and can't
|
||||
// allow the T* implicit casting.
|
||||
|
||||
bool operator!() const throw()
|
||||
{
|
||||
return m_array == NULL;
|
||||
}
|
||||
|
||||
// Equality
|
||||
bool operator==(T* pT) const throw()
|
||||
{
|
||||
return m_array == pT;
|
||||
}
|
||||
|
||||
// Inequality
|
||||
bool operator!=(T* pT) const throw()
|
||||
{
|
||||
return !operator==(pT);
|
||||
}
|
||||
|
||||
// Convenient assignment operator. ScopedPtr = NULL will issue an automatic deletion
|
||||
// of the managed pointer.
|
||||
ScopedArray& operator=( T* src )
|
||||
{
|
||||
return Reassign( src );
|
||||
}
|
||||
|
||||
T& operator[]( uint idx ) const
|
||||
{
|
||||
pxAssertDev( idx < m_valid_range, "Array index out of bounds on ScopedArray." );
|
||||
return m_array[idx];
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxObjPtr -- fancified version of wxScopedPtr
|
||||
|
|
|
@ -23,38 +23,6 @@
|
|||
|
||||
extern void px_fputs( FILE* fp, const char* src );
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// toUTF8 - shortcut for str.ToUTF8().data()
|
||||
// --------------------------------------------------------------------------------------
|
||||
class toUTF8
|
||||
{
|
||||
DeclareNoncopyableObject( toUTF8 );
|
||||
|
||||
protected:
|
||||
wxCharBuffer m_charbuffer;
|
||||
|
||||
public:
|
||||
toUTF8( const wxString& str ) : m_charbuffer( str.ToUTF8().data() ) { }
|
||||
virtual ~toUTF8() throw() {}
|
||||
|
||||
operator const char*() { return m_charbuffer.data(); }
|
||||
};
|
||||
|
||||
// This class provided for completeness sake. You probably should use toUTF8 instead.
|
||||
class toAscii
|
||||
{
|
||||
DeclareNoncopyableObject( toAscii );
|
||||
|
||||
protected:
|
||||
wxCharBuffer m_charbuffer;
|
||||
|
||||
public:
|
||||
toAscii( const wxString& str ) : m_charbuffer( str.ToAscii().data() ) { }
|
||||
virtual ~toAscii() throw() {}
|
||||
|
||||
operator const char*() { return m_charbuffer.data(); }
|
||||
};
|
||||
|
||||
extern wxString fromUTF8( const char* src );
|
||||
extern wxString fromAscii( const char* src );
|
||||
|
||||
|
|
|
@ -25,10 +25,30 @@
|
|||
#undef Yield // release th burden of windows.h global namespace spam.
|
||||
class wxTimeSpan;
|
||||
|
||||
#define AllowFromMainThreadOnly() \
|
||||
pxAssertMsg( wxThread::IsMain(), "Thread affinity violation: Call allowed from main thread only." )
|
||||
|
||||
namespace Threading
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Define some useful object handles - wait events, mutexes.
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Platform Specific External APIs
|
||||
// --------------------------------------------------------------------------------------
|
||||
// The following set of documented functions have Linux/Win32 specific implementations,
|
||||
// which are found in WinThreads.cpp and LnxThreads.cpp
|
||||
|
||||
|
||||
// Returns the number of available logical CPUs (cores plus hyperthreaded cpus)
|
||||
extern void CountLogicalCores( int LogicalCoresPerPhysicalCPU, int PhysicalCoresPerPhysicalCPU );
|
||||
|
||||
// Releases a timeslice to other threads.
|
||||
extern void Timeslice();
|
||||
|
||||
// For use in spin/wait loops.
|
||||
extern void SpinWait();
|
||||
|
||||
// sleeps the current thread for the given number of milliseconds.
|
||||
extern void Sleep( int ms );
|
||||
|
||||
|
||||
// pthread Cond is an evil api that is not suited for Pcsx2 needs.
|
||||
// Let's not use it. Use mutexes and semaphores instead to create waits. (Air)
|
||||
|
@ -39,7 +59,7 @@ namespace Threading
|
|||
pthread_mutex_t mutex;
|
||||
|
||||
WaitEvent();
|
||||
~WaitEvent();
|
||||
~WaitEvent() throw();
|
||||
|
||||
void Set();
|
||||
void Wait();
|
||||
|
@ -64,7 +84,7 @@ namespace Threading
|
|||
|
||||
public:
|
||||
NonblockingMutex() : val( false ) {}
|
||||
virtual ~NonblockingMutex() throw() {};
|
||||
virtual ~NonblockingMutex() throw() {}
|
||||
|
||||
bool TryLock() throw()
|
||||
{
|
||||
|
@ -78,25 +98,31 @@ namespace Threading
|
|||
{ val = false; }
|
||||
};
|
||||
|
||||
struct Semaphore
|
||||
class Semaphore
|
||||
{
|
||||
sem_t sema;
|
||||
protected:
|
||||
sem_t m_sema;
|
||||
|
||||
public:
|
||||
Semaphore();
|
||||
~Semaphore();
|
||||
virtual ~Semaphore() throw();
|
||||
|
||||
void Reset();
|
||||
void Post();
|
||||
void Post( int multiple );
|
||||
|
||||
#if wxUSE_GUI
|
||||
void WaitGui();
|
||||
bool WaitGui( const wxTimeSpan& timeout );
|
||||
#endif
|
||||
void Wait();
|
||||
bool Wait( const wxTimeSpan& timeout );
|
||||
void WaitRaw();
|
||||
bool WaitRaw( const wxTimeSpan& timeout );
|
||||
void WaitNoCancel();
|
||||
int Count();
|
||||
|
||||
#if wxUSE_GUI
|
||||
void Wait();
|
||||
bool Wait( const wxTimeSpan& timeout );
|
||||
|
||||
protected:
|
||||
bool _WaitGui_RecursionGuard();
|
||||
#endif
|
||||
};
|
||||
|
||||
class MutexLock
|
||||
|
@ -124,19 +150,6 @@ namespace Threading
|
|||
virtual ~MutexLockRecursive() throw();
|
||||
};
|
||||
|
||||
|
||||
// Returns the number of available logical CPUs (cores plus hyperthreaded cpus)
|
||||
extern void CountLogicalCores( int LogicalCoresPerPhysicalCPU, int PhysicalCoresPerPhysicalCPU );
|
||||
|
||||
// Releases a timeslice to other threads.
|
||||
extern void Timeslice();
|
||||
|
||||
// For use in spin/wait loops.
|
||||
extern void SpinWait();
|
||||
|
||||
// sleeps the current thread for the given number of milliseconds.
|
||||
extern void Sleep( int ms );
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// IThread - Interface for the public access to PersistentThread.
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -311,6 +324,25 @@ namespace Threading
|
|||
m_lock.Lock();
|
||||
m_IsLocked = true;
|
||||
}
|
||||
|
||||
bool IsLocked() const { return m_IsLocked; }
|
||||
|
||||
protected:
|
||||
// Special constructor used by ScopedTryLock
|
||||
ScopedLock( MutexLock& locker, bool isTryLock ) :
|
||||
m_lock( locker )
|
||||
, m_IsLocked( isTryLock ? m_lock.TryLock() : false )
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class ScopedTryLock : public ScopedLock
|
||||
{
|
||||
public:
|
||||
ScopedTryLock( MutexLock& locker ) : ScopedLock( locker, true ) { }
|
||||
virtual ~ScopedTryLock() throw() {}
|
||||
bool Failed() const { return !m_IsLocked; }
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -7,11 +7,9 @@ This folder contains various classes borrowed from wxWidgets 2.9.x / 3.0.
|
|||
/Common/include is a PCSX2 project default include search path, and with the /wx
|
||||
folder prefix, these files can be included the same way as other wxWidgets includes:
|
||||
|
||||
#include <wx/scopedptr.h>
|
||||
#include <wx/something.h>
|
||||
|
||||
If/when PCSX2 upgrades to wx2.9/3.0 these files will be removed and the wxWidgets
|
||||
distribution files will automatically be used instead.
|
||||
|
||||
|
||||
NOTE: Removed wxScopedPtr in favor of our own implementation, which uses a more
|
||||
sensible API naming convention and also features better operator assignment.
|
||||
NOTE: Folder is currently empty, by design. Nothing from 2.9/3.0 is useful anymore (for now).
|
|
@ -1,63 +0,0 @@
|
|||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: wx/scopedarray.h
|
||||
// Purpose: scoped smart pointer class
|
||||
// Author: Vadim Zeitlin
|
||||
// Created: 2009-02-03
|
||||
// RCS-ID: $Id: scopedarray.h 58634 2009-02-03 12:01:46Z VZ $
|
||||
// Copyright: (c) Jesse Lovelace and original Boost authors (see below)
|
||||
// (c) 2009 Vadim Zeitlin
|
||||
// Licence: wxWindows licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
#include "wx/defs.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxScopedArray: A scoped array, same as a wxScopedPtr but uses delete[]
|
||||
// instead of delete.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
template <class T>
|
||||
class wxScopedArray
|
||||
{
|
||||
public:
|
||||
typedef T element_type;
|
||||
|
||||
wxEXPLICIT wxScopedArray(T * array = NULL) : m_array(array) { }
|
||||
|
||||
~wxScopedArray() { delete [] m_array; }
|
||||
|
||||
// test for pointer validity: defining conversion to unspecified_bool_type
|
||||
// and not more obvious bool to avoid implicit conversions to integer types
|
||||
typedef T *(wxScopedArray<T>::*unspecified_bool_type)() const;
|
||||
operator unspecified_bool_type() const
|
||||
{
|
||||
return m_array ? &wxScopedArray<T>::get : NULL;
|
||||
}
|
||||
|
||||
void reset(T *array = NULL)
|
||||
{
|
||||
if ( array != m_array )
|
||||
{
|
||||
delete [] m_array;
|
||||
m_array = array;
|
||||
}
|
||||
}
|
||||
|
||||
T& operator[](size_t n) const { return m_array[n]; }
|
||||
|
||||
T *get() const { return m_array; }
|
||||
|
||||
void swap(wxScopedArray &other)
|
||||
{
|
||||
T * const tmp = other.m_array;
|
||||
other.m_array = m_array;
|
||||
m_array = tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
T *m_array;
|
||||
|
||||
wxScopedArray(const wxScopedArray<T>&);
|
||||
wxScopedArray& operator=(const wxScopedArray<T>&);
|
||||
};
|
|
@ -146,7 +146,7 @@ void ConsoleBuffer_Clear()
|
|||
void ConsoleBuffer_FlushToFile( FILE *fp )
|
||||
{
|
||||
if( fp == NULL || m_buffer.IsEmpty() ) return;
|
||||
px_fputs( fp, toUTF8(m_buffer) );
|
||||
px_fputs( fp, m_buffer.ToUTF8() );
|
||||
m_buffer.Clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -45,11 +45,6 @@ namespace Threading
|
|||
}
|
||||
}
|
||||
|
||||
__forceinline void Timeslice()
|
||||
{
|
||||
usleep(500);
|
||||
}
|
||||
|
||||
__forceinline void Sleep( int ms )
|
||||
{
|
||||
usleep( 1000*ms );
|
||||
|
|
|
@ -31,50 +31,61 @@
|
|||
#include <wx/datetime.h>
|
||||
#include <wx/thread.h>
|
||||
|
||||
|
||||
using namespace Threading;
|
||||
|
||||
namespace Threading
|
||||
{
|
||||
static const wxTimeSpan ts_msec_250( 0, 0, 0, 250 );
|
||||
// 100ms interval for waitgui (issued from blocking semaphore waits on the main thread,
|
||||
// to avoid gui deadlock).
|
||||
static const wxTimeSpan ts_waitgui_interval( 0, 0, 0, 100 );
|
||||
|
||||
void PersistentThread::_pt_callback_cleanup( void* handle )
|
||||
{
|
||||
// Four second interval for deadlock protection on waitgui.
|
||||
static const wxTimeSpan ts_waitgui_deadlock( 0, 0, 4, 0 );
|
||||
|
||||
static long _attr_refcount = 0;
|
||||
static pthread_mutexattr_t _attr_recursive;
|
||||
}
|
||||
|
||||
__forceinline void Threading::Timeslice()
|
||||
{
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
void Threading::PersistentThread::_pt_callback_cleanup( void* handle )
|
||||
{
|
||||
((PersistentThread*)handle)->_ThreadCleanup();
|
||||
}
|
||||
}
|
||||
|
||||
PersistentThread::PersistentThread() :
|
||||
Threading::PersistentThread::PersistentThread() :
|
||||
m_name( L"PersistentThread" )
|
||||
, m_thread()
|
||||
, m_sem_event()
|
||||
, m_sem_finished()
|
||||
, m_lock_start()
|
||||
, m_detached( true ) // start out with m_thread in detached/invalid state
|
||||
, m_running( false )
|
||||
{
|
||||
}
|
||||
, m_thread()
|
||||
, m_sem_event()
|
||||
, m_sem_finished()
|
||||
, m_lock_start()
|
||||
, m_detached( true ) // start out with m_thread in detached/invalid state
|
||||
, m_running( false )
|
||||
{
|
||||
}
|
||||
|
||||
// This destructor performs basic "last chance" cleanup, which is a blocking join
|
||||
// against the thread. Extending classes should almost always implement their own
|
||||
// thread closure process, since any PersistentThread will, by design, not terminate
|
||||
// unless it has been properly canceled.
|
||||
//
|
||||
// Thread safetly: This class must not be deleted from its own thread. That would be
|
||||
// like marrying your sister, and then cheating on her with your daughter.
|
||||
PersistentThread::~PersistentThread() throw()
|
||||
{
|
||||
// This destructor performs basic "last chance" cleanup, which is a blocking join
|
||||
// against the thread. Extending classes should almost always implement their own
|
||||
// thread closure process, since any PersistentThread will, by design, not terminate
|
||||
// unless it has been properly canceled.
|
||||
//
|
||||
// Thread safety: This class must not be deleted from its own thread. That would be
|
||||
// like marrying your sister, and then cheating on her with your daughter.
|
||||
Threading::PersistentThread::~PersistentThread() throw()
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLn( L"Thread Log: Executing destructor for " + m_name );
|
||||
DevCon.WriteLn( L"(Thread Log) Executing destructor for " + m_name );
|
||||
|
||||
if( m_running )
|
||||
{
|
||||
Console.WriteLn( L"\tWaiting for running thread to end...");
|
||||
#if wxUSE_GUI
|
||||
m_sem_finished.WaitGui();
|
||||
#else
|
||||
DevCon.WriteLn( L"\tWaiting for running thread to end...");
|
||||
#if wxUSE_GUI
|
||||
m_sem_finished.Wait();
|
||||
#endif
|
||||
#else
|
||||
m_sem_finished.WaitRaw();
|
||||
#endif
|
||||
// Need to lock here so that the thread can finish shutting down before
|
||||
// it gets destroyed, otherwise th mutex handle would become invalid.
|
||||
ScopedLock locker( m_lock_start );
|
||||
|
@ -82,17 +93,31 @@ namespace Threading
|
|||
Threading::Sleep( 1 );
|
||||
Detach();
|
||||
}
|
||||
DESTRUCTOR_CATCHALL
|
||||
}
|
||||
|
||||
// Main entry point for starting or e-starting a persistent thread. This function performs necessary
|
||||
// locks and checks for avoiding race conditions, and then calls OnStart() immeediately before
|
||||
// the actual thread creation. Extending classes should generally not override Start(), and should
|
||||
// instead override DoPrepStart instead.
|
||||
//
|
||||
// This function should not be called from the owner thread.
|
||||
void PersistentThread::Start()
|
||||
catch( Exception::ThreadTimedOut& 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( wxsFormat(L"\tThread destructor for '%s' timed out with error:\n\t",
|
||||
m_name.c_str(), ex.FormatDiagnosticMessage().c_str() ) );
|
||||
}
|
||||
DESTRUCTOR_CATCHALL
|
||||
}
|
||||
|
||||
// Main entry point for starting or e-starting a persistent thread. This function performs necessary
|
||||
// locks and checks for avoiding race conditions, and then calls OnStart() immeediately before
|
||||
// the actual thread creation. Extending classes should generally not override Start(), and should
|
||||
// instead override DoPrepStart instead.
|
||||
//
|
||||
// This function should not be called from the owner thread.
|
||||
void Threading::PersistentThread::Start()
|
||||
{
|
||||
ScopedLock startlock( m_lock_start ); // Prevents sudden parallel startup
|
||||
if( m_running ) return;
|
||||
|
||||
|
@ -105,40 +130,40 @@ namespace Threading
|
|||
throw Exception::ThreadCreationError();
|
||||
|
||||
m_detached = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns: TRUE if the detachment was performed, or FALSE if the thread was
|
||||
// already detached or isn't running at all.
|
||||
// This function should not be called from the owner thread.
|
||||
bool PersistentThread::Detach()
|
||||
{
|
||||
// Returns: TRUE if the detachment was performed, or FALSE if the thread was
|
||||
// already detached or isn't running at all.
|
||||
// This function should not be called from the owner thread.
|
||||
bool Threading::PersistentThread::Detach()
|
||||
{
|
||||
pxAssertMsg( !IsSelf(), "Thread affinity error." ); // not allowed from our own thread.
|
||||
|
||||
if( _InterlockedExchange( &m_detached, true ) ) return false;
|
||||
pthread_detach( m_thread );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Remarks:
|
||||
// Provision of non-blocking Cancel() is probably academic, since destroying a PersistentThread
|
||||
// object performs a blocking Cancel regardless of if you explicitly do a non-blocking Cancel()
|
||||
// prior, since the ExecuteTaskInThread() method requires a valid object state. If you really need
|
||||
// fire-and-forget behavior on threads, use pthreads directly for now.
|
||||
//
|
||||
// This function should not be called from the owner thread.
|
||||
//
|
||||
// Parameters:
|
||||
// isBlocking - indicates if the Cancel action should block for thread completion or not.
|
||||
//
|
||||
void PersistentThread::Cancel( bool isBlocking )
|
||||
{
|
||||
// Remarks:
|
||||
// Provision of non-blocking Cancel() is probably academic, since destroying a PersistentThread
|
||||
// object performs a blocking Cancel regardless of if you explicitly do a non-blocking Cancel()
|
||||
// prior, since the ExecuteTaskInThread() method requires a valid object state. If you really need
|
||||
// fire-and-forget behavior on threads, use pthreads directly for now.
|
||||
//
|
||||
// This function should not be called from the owner thread.
|
||||
//
|
||||
// Parameters:
|
||||
// isBlocking - indicates if the Cancel action should block for thread completion or not.
|
||||
//
|
||||
void Threading::PersistentThread::Cancel( bool isBlocking )
|
||||
{
|
||||
pxAssertMsg( !IsSelf(), "Thread affinity error." );
|
||||
|
||||
if( !m_running ) return;
|
||||
|
||||
if( m_detached )
|
||||
{
|
||||
Console.Notice( "Threading Warning: Attempted to cancel detached thread; Ignoring..." );
|
||||
Console.Notice( "(Thread Warning) Ignoring attempted cancelation of detached thread." );
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -147,61 +172,61 @@ namespace Threading
|
|||
if( isBlocking )
|
||||
{
|
||||
#if wxUSE_GUI
|
||||
m_sem_finished.WaitGui();
|
||||
#else
|
||||
m_sem_finished.Wait();
|
||||
#else
|
||||
m_sem_finished.WaitRaw();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Blocks execution of the calling thread until this thread completes its task. The
|
||||
// caller should make sure to signal the thread to exit, or else blocking may deadlock the
|
||||
// calling thread. Classes which extend PersistentThread should override this method
|
||||
// and signal any necessary thread exit variables prior to blocking.
|
||||
//
|
||||
// Returns the return code of the thread.
|
||||
// This method is roughly the equivalent of pthread_join().
|
||||
//
|
||||
void PersistentThread::Block()
|
||||
{
|
||||
// Blocks execution of the calling thread until this thread completes its task. The
|
||||
// caller should make sure to signal the thread to exit, or else blocking may deadlock the
|
||||
// calling thread. Classes which extend PersistentThread should override this method
|
||||
// and signal any necessary thread exit variables prior to blocking.
|
||||
//
|
||||
// Returns the return code of the thread.
|
||||
// This method is roughly the equivalent of pthread_join().
|
||||
//
|
||||
void Threading::PersistentThread::Block()
|
||||
{
|
||||
pxAssertDev( !IsSelf(), "Thread deadlock detected; Block() should never be called by the owner thread." );
|
||||
|
||||
if( m_running )
|
||||
#if wxUSE_GUI
|
||||
m_sem_finished.WaitGui();
|
||||
#else
|
||||
m_sem_finished.Wait();
|
||||
#else
|
||||
m_sem_finished.WaitRaw();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool PersistentThread::IsSelf() const
|
||||
{
|
||||
bool Threading::PersistentThread::IsSelf() const
|
||||
{
|
||||
return pthread_self() == m_thread;
|
||||
}
|
||||
}
|
||||
|
||||
bool PersistentThread::IsRunning() const
|
||||
{
|
||||
bool Threading::PersistentThread::IsRunning() const
|
||||
{
|
||||
return !!m_running;
|
||||
}
|
||||
}
|
||||
|
||||
// Throws an exception if the thread encountered one. Uses the BaseException's Rethrow() method,
|
||||
// which ensures the exception type remains consistent. Debuggable stacktraces will be lost, since
|
||||
// the thread will have allowed itself to terminate properly.
|
||||
void PersistentThread::RethrowException() const
|
||||
{
|
||||
// Throws an exception if the thread encountered one. Uses the BaseException's Rethrow() method,
|
||||
// which ensures the exception type remains consistent. Debuggable stacktraces will be lost, since
|
||||
// the thread will have allowed itself to terminate properly.
|
||||
void Threading::PersistentThread::RethrowException() const
|
||||
{
|
||||
if( !m_except ) return;
|
||||
m_except->Rethrow();
|
||||
}
|
||||
}
|
||||
|
||||
void PersistentThread::TestCancel()
|
||||
{
|
||||
void Threading::PersistentThread::TestCancel()
|
||||
{
|
||||
pxAssert( IsSelf() );
|
||||
pthread_testcancel();
|
||||
}
|
||||
}
|
||||
|
||||
// Executes the virtual member method
|
||||
void PersistentThread::_try_virtual_invoke( void (PersistentThread::*method)() )
|
||||
{
|
||||
// Executes the virtual member method
|
||||
void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::*method)() )
|
||||
{
|
||||
try {
|
||||
(this->*method)();
|
||||
}
|
||||
|
@ -230,11 +255,12 @@ namespace Threading
|
|||
m_except = ex.Clone();
|
||||
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
|
||||
}
|
||||
|
||||
#ifndef PCSX2_DEVBUILD
|
||||
// ----------------------------------------------------------------------------
|
||||
// Should we let logic errors propagate, or swallow them and let the thread manager
|
||||
// handle them? Hmm..
|
||||
/*catch( std::logic_error& ex )
|
||||
// Allow logic errors to propagate out of the thread in release builds, so that they might be
|
||||
// handled in non-fatal ways. On Devbuilds let them loose, so that they produce debug stack
|
||||
// traces and such.
|
||||
catch( std::logic_error& ex )
|
||||
{
|
||||
throw Exception::LogicError( wxsFormat( L"(thread: %s) STL Logic Error: %s\n\t%s",
|
||||
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
|
||||
|
@ -244,12 +270,12 @@ namespace Threading
|
|||
{
|
||||
m_except = ex.Clone();
|
||||
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
|
||||
}*/
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// BaseException / std::exception -- same deal. Allow propagation or no?
|
||||
// BaseException / std::exception -- same deal as LogicErrors.
|
||||
//
|
||||
/*catch( std::exception& ex )
|
||||
catch( std::exception& ex )
|
||||
{
|
||||
throw Exception::BaseException( wxsFormat( L"(thread: %s) STL exception: %s\n\t%s",
|
||||
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
|
||||
|
@ -259,13 +285,14 @@ namespace Threading
|
|||
{
|
||||
m_except = ex.Clone();
|
||||
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// invoked internally when canceling or exiting the thread. Extending classes should implement
|
||||
// OnCleanupInThread() to extend cleanup functionality.
|
||||
void PersistentThread::_ThreadCleanup()
|
||||
{
|
||||
// invoked internally when canceling or exiting the thread. Extending classes should implement
|
||||
// OnCleanupInThread() to extend cleanup functionality.
|
||||
void Threading::PersistentThread::_ThreadCleanup()
|
||||
{
|
||||
pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks.
|
||||
|
||||
// Typically thread cleanup needs to lock against thread startup, since both
|
||||
|
@ -277,27 +304,27 @@ namespace Threading
|
|||
|
||||
m_running = false;
|
||||
m_sem_finished.Post();
|
||||
}
|
||||
}
|
||||
|
||||
wxString PersistentThread::GetName() const
|
||||
{
|
||||
wxString Threading::PersistentThread::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
}
|
||||
|
||||
void PersistentThread::_internal_execute()
|
||||
{
|
||||
void Threading::PersistentThread::_internal_execute()
|
||||
{
|
||||
m_running = true;
|
||||
_DoSetThreadName( m_name );
|
||||
_try_virtual_invoke( &PersistentThread::ExecuteTaskInThread );
|
||||
}
|
||||
}
|
||||
|
||||
void PersistentThread::OnStart() {}
|
||||
void PersistentThread::OnCleanupInThread() {}
|
||||
void Threading::PersistentThread::OnStart() {}
|
||||
void Threading::PersistentThread::OnCleanupInThread() {}
|
||||
|
||||
// passed into pthread_create, and is used to dispatch the thread's object oriented
|
||||
// callback function
|
||||
void* PersistentThread::_internal_callback( void* itsme )
|
||||
{
|
||||
// passed into pthread_create, and is used to dispatch the thread's object oriented
|
||||
// callback function
|
||||
void* Threading::PersistentThread::_internal_callback( void* itsme )
|
||||
{
|
||||
jASSUME( itsme != NULL );
|
||||
PersistentThread& owner = *((PersistentThread*)itsme);
|
||||
|
||||
|
@ -305,20 +332,20 @@ namespace Threading
|
|||
owner._internal_execute();
|
||||
pthread_cleanup_pop( true );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void PersistentThread::_DoSetThreadName( const wxString& name )
|
||||
{
|
||||
_DoSetThreadName( toUTF8(name) );
|
||||
}
|
||||
void Threading::PersistentThread::_DoSetThreadName( const wxString& name )
|
||||
{
|
||||
_DoSetThreadName( name.ToUTF8() );
|
||||
}
|
||||
|
||||
void PersistentThread::_DoSetThreadName( __unused const char* name )
|
||||
{
|
||||
void Threading::PersistentThread::_DoSetThreadName( __unused const char* name )
|
||||
{
|
||||
pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks.
|
||||
|
||||
// This feature needs Windows headers and MSVC's SEH support:
|
||||
|
||||
#if defined(_WINDOWS_) && defined (_MSC_VER)
|
||||
#if defined(_WINDOWS_) && defined (_MSC_VER)
|
||||
|
||||
// This code sample was borrowed form some obscure MSDN article.
|
||||
// In a rare bout of sanity, it's an actual Micrsoft-published hack
|
||||
|
@ -349,54 +376,54 @@ namespace Threading
|
|||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// BaseTaskThread Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
// Tells the thread to exit and then waits for thread termination.
|
||||
void BaseTaskThread::Block()
|
||||
{
|
||||
// Tells the thread to exit and then waits for thread termination.
|
||||
void Threading::BaseTaskThread::Block()
|
||||
{
|
||||
if( !IsRunning() ) return;
|
||||
m_Done = true;
|
||||
m_sem_event.Post();
|
||||
PersistentThread::Block();
|
||||
}
|
||||
}
|
||||
|
||||
// Initiates the new task. This should be called after your own StartTask has
|
||||
// initialized internal variables / preparations for task execution.
|
||||
void BaseTaskThread::PostTask()
|
||||
{
|
||||
// Initiates the new task. This should be called after your own StartTask has
|
||||
// initialized internal variables / preparations for task execution.
|
||||
void Threading::BaseTaskThread::PostTask()
|
||||
{
|
||||
pxAssert( !m_detached );
|
||||
|
||||
ScopedLock locker( m_lock_TaskComplete );
|
||||
m_TaskPending = true;
|
||||
m_post_TaskComplete.Reset();
|
||||
m_sem_event.Post();
|
||||
}
|
||||
}
|
||||
|
||||
// Blocks current thread execution pending the completion of the parallel task.
|
||||
void BaseTaskThread::WaitForResult()
|
||||
{
|
||||
// Blocks current thread execution pending the completion of the parallel task.
|
||||
void Threading::BaseTaskThread::WaitForResult()
|
||||
{
|
||||
if( m_detached || !m_running ) return;
|
||||
if( m_TaskPending )
|
||||
#ifdef wxUSE_GUI
|
||||
m_post_TaskComplete.WaitGui();
|
||||
#else
|
||||
m_post_TaskComplete.Wait();
|
||||
#else
|
||||
m_post_TaskComplete.WaitRaw();
|
||||
#endif
|
||||
|
||||
m_post_TaskComplete.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void BaseTaskThread::ExecuteTaskInThread()
|
||||
{
|
||||
void Threading::BaseTaskThread::ExecuteTaskInThread()
|
||||
{
|
||||
while( !m_Done )
|
||||
{
|
||||
// Wait for a job -- or get a pthread_cancel. I'm easy.
|
||||
m_sem_event.Wait();
|
||||
m_sem_event.WaitRaw();
|
||||
|
||||
Task();
|
||||
m_lock_TaskComplete.Lock();
|
||||
|
@ -406,7 +433,7 @@ namespace Threading
|
|||
};
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pthread Cond is an evil api that is not suited for Pcsx2 needs.
|
||||
|
@ -414,174 +441,225 @@ namespace Threading
|
|||
// --------------------------------------------------------------------------------------
|
||||
|
||||
#if 0
|
||||
WaitEvent::WaitEvent()
|
||||
{
|
||||
Threading::WaitEvent::WaitEvent()
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
err = pthread_cond_init(&cond, NULL);
|
||||
err = pthread_mutex_init(&mutex, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
WaitEvent::~WaitEvent()
|
||||
{
|
||||
Threading::WaitEvent::~WaitEvent() throw()
|
||||
{
|
||||
pthread_cond_destroy( &cond );
|
||||
pthread_mutex_destroy( &mutex );
|
||||
}
|
||||
}
|
||||
|
||||
void WaitEvent::Set()
|
||||
{
|
||||
void Threading::WaitEvent::Set()
|
||||
{
|
||||
pthread_mutex_lock( &mutex );
|
||||
pthread_cond_signal( &cond );
|
||||
pthread_mutex_unlock( &mutex );
|
||||
}
|
||||
}
|
||||
|
||||
void WaitEvent::Wait()
|
||||
{
|
||||
void Threading::WaitEvent::Wait()
|
||||
{
|
||||
pthread_mutex_lock( &mutex );
|
||||
pthread_cond_wait( &cond, &mutex );
|
||||
pthread_mutex_unlock( &mutex );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Semaphore Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
Semaphore::Semaphore()
|
||||
{
|
||||
sem_init( &sema, false, 0 );
|
||||
}
|
||||
Threading::Semaphore::Semaphore()
|
||||
{
|
||||
sem_init( &m_sema, false, 0 );
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore()
|
||||
{
|
||||
sem_destroy( &sema );
|
||||
}
|
||||
Threading::Semaphore::~Semaphore() throw()
|
||||
{
|
||||
sem_destroy( &m_sema );
|
||||
}
|
||||
|
||||
void Semaphore::Reset()
|
||||
{
|
||||
sem_destroy( &sema );
|
||||
sem_init( &sema, false, 0 );
|
||||
}
|
||||
void Threading::Semaphore::Reset()
|
||||
{
|
||||
sem_destroy( &m_sema );
|
||||
sem_init( &m_sema, false, 0 );
|
||||
}
|
||||
|
||||
void Semaphore::Post()
|
||||
{
|
||||
sem_post( &sema );
|
||||
}
|
||||
void Threading::Semaphore::Post()
|
||||
{
|
||||
sem_post( &m_sema );
|
||||
}
|
||||
|
||||
// Valid on Win32 builds only!! Attempts to use it on Linux will result in unresolved
|
||||
// external linker errors.
|
||||
void Semaphore::Post( int multiple )
|
||||
{
|
||||
void Threading::Semaphore::Post( int multiple )
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
sem_post_multiple( &sema, multiple );
|
||||
sem_post_multiple( &m_sema, multiple );
|
||||
#else
|
||||
// Only w32pthreads has the post_multiple, but it's easy enough to fake:
|
||||
while( multiple > 0 )
|
||||
{
|
||||
multiple--;
|
||||
sem_post( &sema );
|
||||
sem_post( &m_sema );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if wxUSE_GUI
|
||||
// This is a wxApp-safe implementation of Wait, which makes sure and executes the App's
|
||||
// pending messages *if* the Wait is performed on the Main/GUI thread. If the Wait is
|
||||
// called from another thread, no message pumping is performed.
|
||||
void Semaphore::WaitGui()
|
||||
// (intended for internal use only)
|
||||
// Returns true if the Wait is recursive, or false if the Wait is safe and should be
|
||||
// handled via normal yielding methods.
|
||||
bool Threading::Semaphore::_WaitGui_RecursionGuard()
|
||||
{
|
||||
// In order to avoid deadlock we need to make sure we cut some time to handle
|
||||
// messages. But this can result in recursive yield calls, which would crash
|
||||
// the app. Protect against them here and, if recursion is detected, perform
|
||||
// a standard blocking wait.
|
||||
|
||||
static int __Guard = 0;
|
||||
RecursionGuard guard( __Guard );
|
||||
|
||||
if( guard.Counter > 4 )
|
||||
{
|
||||
Console.WriteLn( "(Thread Log) Possible yield recursion detected in Semaphore::Wait; performing blocking wait." );
|
||||
//while( wxTheApp->Pending() ) wxTheApp->Dispatch(); // ensures console gets updated.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is a wxApp-safe implementation of Wait, which makes sure and executes the App's
|
||||
// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that
|
||||
// user input continues to be handled and that windoes continue to repaint. If the Wait is
|
||||
// called from another thread, no message pumping is performed.
|
||||
//
|
||||
// Exceptions:
|
||||
// ThreadTimedOut - thrown if a blocking wait was needed due to recursion and the default
|
||||
// timeout period (usually 4 seconds) was reached, indicating likely deadlock. If
|
||||
// the method is run from a thread *other* than the MainGui thread, this exception
|
||||
// cannot occur.
|
||||
//
|
||||
void Threading::Semaphore::Wait()
|
||||
{
|
||||
if( !wxThread::IsMain() || (wxTheApp == NULL) )
|
||||
Wait();
|
||||
{
|
||||
WaitRaw();
|
||||
}
|
||||
else if( _WaitGui_RecursionGuard() )
|
||||
{
|
||||
if( !WaitRaw(ts_waitgui_deadlock) ) // default is 4 seconds
|
||||
throw Exception::ThreadTimedOut();
|
||||
}
|
||||
else
|
||||
{
|
||||
// In order to avoid deadlock we need to make sure we cut some time
|
||||
// to handle messages.
|
||||
|
||||
do {
|
||||
wxTheApp->Yield();
|
||||
} while( !Wait( ts_msec_250 ) );
|
||||
}
|
||||
wxTheApp->Yield( true );
|
||||
} while( !WaitRaw( ts_waitgui_interval ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool Semaphore::WaitGui( const wxTimeSpan& timeout )
|
||||
{
|
||||
// This is a wxApp-safe implementation of Wait, which makes sure and executes the App's
|
||||
// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that
|
||||
// user input continues to be handled and that windows continue to repaint. If the Wait is
|
||||
// called from another thread, no message pumping is performed.
|
||||
//
|
||||
// Returns:
|
||||
// false if the wait timed out before the semaphore was signaled, or true if the signal was
|
||||
// reached prior to timeout.
|
||||
//
|
||||
// Exceptions:
|
||||
// ThreadTimedOut - thrown if a blocking wait was needed due to recursion and the default
|
||||
// timeout period (usually 4 seconds) was reached, indicating likely deadlock. If the
|
||||
// user-specified timeout is less than four seconds, this exception cannot occur. If
|
||||
// the method is run from a thread *other* than the MainGui thread, this exception
|
||||
// cannot occur.
|
||||
//
|
||||
bool Threading::Semaphore::Wait( const wxTimeSpan& timeout )
|
||||
{
|
||||
if( !wxThread::IsMain() || (wxTheApp == NULL) )
|
||||
{
|
||||
return Wait( timeout );
|
||||
return WaitRaw( timeout );
|
||||
}
|
||||
else if( _WaitGui_RecursionGuard() )
|
||||
{
|
||||
if( timeout > ts_waitgui_deadlock )
|
||||
{
|
||||
if( WaitRaw(ts_waitgui_deadlock) ) return true;
|
||||
throw Exception::ThreadTimedOut();
|
||||
}
|
||||
return WaitRaw( timeout );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxTimeSpan countdown( (timeout) );
|
||||
|
||||
// In order to avoid deadlock we need to make sure we cut some time
|
||||
// to handle messages.
|
||||
|
||||
do {
|
||||
wxTheApp->Yield();
|
||||
if( Wait( ts_msec_250 ) ) break;
|
||||
countdown -= ts_msec_250;
|
||||
if( WaitRaw( ts_waitgui_interval ) ) break;
|
||||
countdown -= ts_waitgui_interval;
|
||||
} while( countdown.GetMilliseconds() > 0 );
|
||||
|
||||
return countdown.GetMilliseconds() > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Semaphore::Wait()
|
||||
{
|
||||
sem_wait( &sema );
|
||||
}
|
||||
void Threading::Semaphore::WaitRaw()
|
||||
{
|
||||
sem_wait( &m_sema );
|
||||
}
|
||||
|
||||
bool Semaphore::Wait( const wxTimeSpan& timeout )
|
||||
{
|
||||
bool Threading::Semaphore::WaitRaw( const wxTimeSpan& timeout )
|
||||
{
|
||||
wxDateTime megafail( wxDateTime::UNow() + timeout );
|
||||
const timespec fail = { megafail.GetTicks(), megafail.GetMillisecond() * 1000000 };
|
||||
return sem_timedwait( &sema, &fail ) != -1;
|
||||
}
|
||||
return sem_timedwait( &m_sema, &fail ) != -1;
|
||||
}
|
||||
|
||||
// Performs an uncancellable wait on a semaphore; restoring the thread's previous cancel state
|
||||
// after the wait has completed. Useful for situations where the semaphore itself is stored on
|
||||
// the stack and passed to another thread via GUI message or such, avoiding complications where
|
||||
// the thread might be canceled and the stack value becomes invalid.
|
||||
//
|
||||
// Performance note: this function has quite a bit more overhead compared to Semaphore::Wait(), so
|
||||
// consider manually specifying the thread as uncancellable and using Wait() instead if you need
|
||||
// to do a lot of no-cancel waits in a tight loop worker thread, for example.
|
||||
void Semaphore::WaitNoCancel()
|
||||
{
|
||||
// Performs an uncancellable wait on a semaphore; restoring the thread's previous cancel state
|
||||
// after the wait has completed. Useful for situations where the semaphore itself is stored on
|
||||
// the stack and passed to another thread via GUI message or such, avoiding complications where
|
||||
// the thread might be canceled and the stack value becomes invalid.
|
||||
//
|
||||
// Performance note: this function has quite a bit more overhead compared to Semaphore::WaitRaw(), so
|
||||
// consider manually specifying the thread as uncancellable and using WaitRaw() instead if you need
|
||||
// to do a lot of no-cancel waits in a tight loop worker thread, for example.
|
||||
void Threading::Semaphore::WaitNoCancel()
|
||||
{
|
||||
int oldstate;
|
||||
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
|
||||
Wait();
|
||||
WaitRaw();
|
||||
pthread_setcancelstate( oldstate, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
int Semaphore::Count()
|
||||
{
|
||||
int Threading::Semaphore::Count()
|
||||
{
|
||||
int retval;
|
||||
sem_getvalue( &sema, &retval );
|
||||
sem_getvalue( &m_sema, &retval );
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// MutexLock Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
MutexLock::MutexLock()
|
||||
{
|
||||
Threading::MutexLock::MutexLock()
|
||||
{
|
||||
int err = 0;
|
||||
err = pthread_mutex_init( &mutex, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
MutexLock::~MutexLock() throw()
|
||||
{
|
||||
Threading::MutexLock::~MutexLock() throw()
|
||||
{
|
||||
pthread_mutex_destroy( &mutex );
|
||||
}
|
||||
}
|
||||
|
||||
static long _attr_refcount = 0;
|
||||
static pthread_mutexattr_t _attr_recursive;
|
||||
|
||||
MutexLockRecursive::MutexLockRecursive() : MutexLock( false )
|
||||
{
|
||||
Threading::MutexLockRecursive::MutexLockRecursive() : MutexLock( false )
|
||||
{
|
||||
if( _InterlockedIncrement( &_attr_refcount ) == 1 )
|
||||
{
|
||||
if( 0 != pthread_mutexattr_init( &_attr_recursive ) )
|
||||
|
@ -592,81 +670,80 @@ namespace Threading
|
|||
|
||||
int err = 0;
|
||||
err = pthread_mutex_init( &mutex, &_attr_recursive );
|
||||
}
|
||||
}
|
||||
|
||||
MutexLockRecursive::~MutexLockRecursive() throw()
|
||||
{
|
||||
Threading::MutexLockRecursive::~MutexLockRecursive() throw()
|
||||
{
|
||||
if( _InterlockedDecrement( &_attr_refcount ) == 0 )
|
||||
pthread_mutexattr_destroy( &_attr_recursive );
|
||||
}
|
||||
}
|
||||
|
||||
void MutexLock::Lock()
|
||||
{
|
||||
void Threading::MutexLock::Lock()
|
||||
{
|
||||
pthread_mutex_lock( &mutex );
|
||||
}
|
||||
}
|
||||
|
||||
void MutexLock::Unlock()
|
||||
{
|
||||
void Threading::MutexLock::Unlock()
|
||||
{
|
||||
pthread_mutex_unlock( &mutex );
|
||||
}
|
||||
}
|
||||
|
||||
bool MutexLock::TryLock()
|
||||
{
|
||||
bool Threading::MutexLock::TryLock()
|
||||
{
|
||||
return EBUSY != pthread_mutex_trylock( &mutex );
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// InterlockedExchanges / AtomicExchanges (PCSX2's Helper versions)
|
||||
// --------------------------------------------------------------------------------------
|
||||
// define some overloads for InterlockedExchanges for commonly used types, like u32 and s32.
|
||||
|
||||
__forceinline void AtomicExchange( volatile u32& Target, u32 value )
|
||||
{
|
||||
__forceinline void Threading::AtomicExchange( volatile u32& Target, u32 value )
|
||||
{
|
||||
_InterlockedExchange( (volatile long*)&Target, value );
|
||||
}
|
||||
|
||||
__forceinline void AtomicExchangeAdd( volatile u32& Target, u32 value )
|
||||
{
|
||||
_InterlockedExchangeAdd( (volatile long*)&Target, value );
|
||||
}
|
||||
|
||||
__forceinline void AtomicIncrement( volatile u32& Target )
|
||||
{
|
||||
_InterlockedExchangeAdd( (volatile long*)&Target, 1 );
|
||||
}
|
||||
|
||||
__forceinline void AtomicDecrement( volatile u32& Target )
|
||||
{
|
||||
_InterlockedExchangeAdd( (volatile long*)&Target, -1 );
|
||||
}
|
||||
|
||||
__forceinline void AtomicExchange( volatile s32& Target, s32 value )
|
||||
{
|
||||
_InterlockedExchange( (volatile long*)&Target, value );
|
||||
}
|
||||
|
||||
__forceinline void AtomicExchangeAdd( s32& Target, u32 value )
|
||||
{
|
||||
_InterlockedExchangeAdd( (volatile long*)&Target, value );
|
||||
}
|
||||
|
||||
__forceinline void AtomicIncrement( volatile s32& Target )
|
||||
{
|
||||
_InterlockedExchangeAdd( (volatile long*)&Target, 1 );
|
||||
}
|
||||
|
||||
__forceinline void AtomicDecrement( volatile s32& Target )
|
||||
{
|
||||
_InterlockedExchangeAdd( (volatile long*)&Target, -1 );
|
||||
}
|
||||
|
||||
__forceinline void _AtomicExchangePointer( const void ** target, const void* value )
|
||||
{
|
||||
_InterlockedExchange( (volatile long*)target, (long)value );
|
||||
}
|
||||
|
||||
__forceinline void _AtomicCompareExchangePointer( const void ** target, const void* value, const void* comparand )
|
||||
{
|
||||
_InterlockedCompareExchange( (volatile long*)target, (long)value, (long)comparand );
|
||||
}
|
||||
}
|
||||
|
||||
__forceinline void Threading::AtomicExchangeAdd( volatile u32& Target, u32 value )
|
||||
{
|
||||
_InterlockedExchangeAdd( (volatile long*)&Target, value );
|
||||
}
|
||||
|
||||
__forceinline void Threading::AtomicIncrement( volatile u32& Target )
|
||||
{
|
||||
_InterlockedExchangeAdd( (volatile long*)&Target, 1 );
|
||||
}
|
||||
|
||||
__forceinline void Threading::AtomicDecrement( volatile u32& Target )
|
||||
{
|
||||
_InterlockedExchangeAdd( (volatile long*)&Target, -1 );
|
||||
}
|
||||
|
||||
__forceinline void Threading::AtomicExchange( volatile s32& Target, s32 value )
|
||||
{
|
||||
_InterlockedExchange( (volatile long*)&Target, value );
|
||||
}
|
||||
|
||||
__forceinline void Threading::AtomicExchangeAdd( volatile s32& Target, u32 value )
|
||||
{
|
||||
_InterlockedExchangeAdd( (volatile long*)&Target, value );
|
||||
}
|
||||
|
||||
__forceinline void Threading::AtomicIncrement( volatile s32& Target )
|
||||
{
|
||||
_InterlockedExchangeAdd( (volatile long*)&Target, 1 );
|
||||
}
|
||||
|
||||
__forceinline void Threading::AtomicDecrement( volatile s32& Target )
|
||||
{
|
||||
_InterlockedExchangeAdd( (volatile long*)&Target, -1 );
|
||||
}
|
||||
|
||||
__forceinline void Threading::_AtomicExchangePointer( const void ** target, const void* value )
|
||||
{
|
||||
_InterlockedExchange( (volatile long*)target, (long)value );
|
||||
}
|
||||
|
||||
__forceinline void Threading::_AtomicCompareExchangePointer( const void ** target, const void* value, const void* comparand )
|
||||
{
|
||||
_InterlockedCompareExchange( (volatile long*)target, (long)value, (long)comparand );
|
||||
}
|
||||
|
|
|
@ -51,11 +51,6 @@ namespace Threading
|
|||
//ptw32_smp_system = ( x86caps.LogicalCores > 1 ) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
__forceinline void Timeslice()
|
||||
{
|
||||
::Sleep(0);
|
||||
}
|
||||
|
||||
__forceinline void Sleep( int ms )
|
||||
{
|
||||
::Sleep( ms );
|
||||
|
|
|
@ -81,11 +81,11 @@ FILE *_cdvdOpenMechaVer()
|
|||
const wxCharBuffer file( mecfile.GetFullPath().ToUTF8() );
|
||||
|
||||
// if file doesnt exist, create empty one
|
||||
fd = fopen(file.data(), "r+b");
|
||||
fd = fopen(file, "r+b");
|
||||
if (fd == NULL)
|
||||
{
|
||||
Console.Notice("MEC File Not Found , Creating Blank File");
|
||||
fd = fopen(file.data(), "wb");
|
||||
fd = fopen(file, "wb");
|
||||
if (fd == NULL)
|
||||
{
|
||||
Console.Error( "\tMEC File Creation failed!" );
|
||||
|
@ -121,11 +121,11 @@ FILE *_cdvdOpenNVM()
|
|||
const wxCharBuffer file( nvmfile.GetFullPath().ToUTF8() );
|
||||
|
||||
// if file doesn't exist, create empty one
|
||||
fd = fopen(file.data(), "r+b");
|
||||
fd = fopen(file, "r+b");
|
||||
if (fd == NULL)
|
||||
{
|
||||
Console.Notice("NVM File Not Found , Creating Blank File");
|
||||
fd = fopen(file.data(), "wb");
|
||||
fd = fopen(file, "wb");
|
||||
if (fd == NULL)
|
||||
{
|
||||
Console.Error( "\tNVM File Creation failed!" );
|
||||
|
@ -301,11 +301,15 @@ s32 cdvdWriteConfig(const u8* config)
|
|||
}
|
||||
}
|
||||
|
||||
void reloadElfInfo(const char* str)
|
||||
static MutexLockRecursive Mutex_NewDiskCB;
|
||||
|
||||
static void reloadElfInfo(const char* str)
|
||||
{
|
||||
// Now's a good time to reload the ELF info...
|
||||
if (ElfCRC == 0)
|
||||
{
|
||||
ScopedLock locker( Mutex_NewDiskCB );
|
||||
|
||||
ElfCRC = loadElfCRC( str );
|
||||
ElfApplyPatches();
|
||||
mtgsThread.SendGameCRC( ElfCRC );
|
||||
|
@ -504,7 +508,7 @@ void SaveStateBase::cdvdFreeze()
|
|||
}
|
||||
}
|
||||
|
||||
void cdvdDetectDisk()
|
||||
static void cdvdDetectDisk()
|
||||
{
|
||||
cdvd.Type = DoCDVDdetectDiskType();
|
||||
|
||||
|
@ -516,12 +520,14 @@ void cdvdDetectDisk()
|
|||
|
||||
void cdvdNewDiskCB()
|
||||
{
|
||||
if( !Mutex_NewDiskCB.TryLock() ) return;
|
||||
DoCDVDresetDiskTypeCache();
|
||||
|
||||
cdvdDetectDisk();
|
||||
try { cdvdDetectDisk(); }
|
||||
catch(...) { Mutex_NewDiskCB.Unlock(); } // ensure mutex gets unlocked.
|
||||
}
|
||||
|
||||
void mechaDecryptBytes( u32 madr, int size )
|
||||
static void mechaDecryptBytes( u32 madr, int size )
|
||||
{
|
||||
int shiftAmount = (cdvd.decSet>>4) & 7;
|
||||
int doXor = (cdvd.decSet) & 1;
|
||||
|
|
|
@ -137,7 +137,6 @@ extern void cdvdActionInterrupt();
|
|||
extern void cdvdReadInterrupt();
|
||||
|
||||
// We really should not have a function with the exact same name as a callback except for case!
|
||||
extern void cdvdDetectDisk();
|
||||
extern void cdvdNewDiskCB();
|
||||
extern u8 cdvdRead(u8 key);
|
||||
extern void cdvdWrite(u8 key, u8 rt);
|
||||
|
|
|
@ -292,15 +292,17 @@ void CDVDsys_ChangeSource( CDVD_SourceType type )
|
|||
|
||||
jNO_DEFAULT;
|
||||
}
|
||||
|
||||
CDVD->newDiskCB( cdvdNewDiskCB );
|
||||
}
|
||||
|
||||
bool DoCDVDopen()
|
||||
{
|
||||
CheckNullCDVD();
|
||||
|
||||
int ret = CDVD->open( m_SourceFilename[m_CurrentSourceType].IsEmpty() ? NULL : m_SourceFilename[m_CurrentSourceType].ToUTF8().data() );
|
||||
// the new disk callback is set on Init also, but just in case the plugin clears it for
|
||||
// some reason on close, we re-send here:
|
||||
CDVD->newDiskCB( cdvdNewDiskCB );
|
||||
|
||||
int ret = CDVD->open( !m_SourceFilename[m_CurrentSourceType].IsEmpty() ? m_SourceFilename[m_CurrentSourceType].ToUTF8() : (char*)NULL );
|
||||
if( ret == -1 ) return false;
|
||||
|
||||
int cdtype = DoCDVDdetectDiskType();
|
||||
|
|
|
@ -439,7 +439,7 @@ __forceinline void rcntUpdate_vSync()
|
|||
{
|
||||
eeRecIsReset = false;
|
||||
mtgsThread.RethrowException();
|
||||
SysCoreThread::Get().StateCheck();
|
||||
SysCoreThread::Get().StateCheckInThread();
|
||||
if( eeRecIsReset )
|
||||
{
|
||||
eeRecIsReset = false;
|
||||
|
|
|
@ -527,7 +527,7 @@ void gsDynamicSkipEnable()
|
|||
{
|
||||
if( !m_StrictSkipping ) return;
|
||||
|
||||
mtgsWaitGS();
|
||||
mtgsThread.WaitGS();
|
||||
m_iSlowStart = GetCPUTicks();
|
||||
frameLimitReset();
|
||||
}
|
||||
|
|
|
@ -82,9 +82,9 @@ struct MTGS_FreezeData
|
|||
s32 retval; // value returned from the call, valid only after an mtgsWaitGS()
|
||||
};
|
||||
|
||||
class mtgsThreadObject : public SysSuspendableThread
|
||||
class mtgsThreadObject : public SysThreadBase
|
||||
{
|
||||
typedef SysSuspendableThread _parent;
|
||||
typedef SysThreadBase _parent;
|
||||
|
||||
protected:
|
||||
// note: when g_pGSRingPos == g_pGSWritePos, the fifo is empty
|
||||
|
@ -170,8 +170,6 @@ protected:
|
|||
|
||||
extern __aligned16 mtgsThreadObject mtgsThread;
|
||||
|
||||
void mtgsWaitGS();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Generalized GS Functions and Stuff
|
||||
|
||||
|
|
|
@ -111,7 +111,6 @@
|
|||
<Unit filename="../../common/include/Pcsx2Types.h" />
|
||||
<Unit filename="../../common/include/PluginCallbacks.h" />
|
||||
<Unit filename="../../common/include/wx/folderdesc.txt" />
|
||||
<Unit filename="../../common/include/wx/scopedarray.h" />
|
||||
<Unit filename="../CDVD/CDVD.cpp" />
|
||||
<Unit filename="../CDVD/CDVD.h" />
|
||||
<Unit filename="../CDVD/CDVD_internal.h" />
|
||||
|
@ -277,6 +276,7 @@
|
|||
<Unit filename="../gui/Dialogs/AboutBoxDialog.cpp" />
|
||||
<Unit filename="../gui/Dialogs/ConfigurationDialog.cpp" />
|
||||
<Unit filename="../gui/Dialogs/ConfigurationDialog.h" />
|
||||
<Unit filename="../gui/Dialogs/ConfirmationDialogs.cpp" />
|
||||
<Unit filename="../gui/Dialogs/FirstTimeWizard.cpp" />
|
||||
<Unit filename="../gui/Dialogs/ImportSettingsDialog.cpp" />
|
||||
<Unit filename="../gui/Dialogs/LogOptionsDialog.cpp" />
|
||||
|
|
|
@ -88,7 +88,7 @@ std::list<uint> ringposStack;
|
|||
#endif
|
||||
|
||||
mtgsThreadObject::mtgsThreadObject() :
|
||||
SysSuspendableThread()
|
||||
SysThreadBase()
|
||||
, m_RingPos( 0 )
|
||||
, m_WritePos( 0 )
|
||||
|
||||
|
@ -244,8 +244,8 @@ void mtgsThreadObject::ExecuteTaskInThread()
|
|||
pthread_cleanup_push( _clean_close_gs, this );
|
||||
while( true )
|
||||
{
|
||||
m_sem_event.Wait(); // ... because this does a cancel test itself..
|
||||
StateCheck( false ); // false disables cancel test here!
|
||||
m_sem_event.WaitRaw(); // ... because this does a cancel test itself..
|
||||
StateCheckInThread( false ); // false disables cancel test here!
|
||||
|
||||
m_RingBufferIsBusy = true;
|
||||
|
||||
|
@ -282,7 +282,7 @@ void mtgsThreadObject::ExecuteTaskInThread()
|
|||
m_lock_RingRestart.Lock();
|
||||
m_lock_RingRestart.Unlock();
|
||||
|
||||
StateCheck( false ); // disable cancel since the above locks are cancelable already
|
||||
StateCheckInThread( false ); // disable cancel since the above locks are cancelable already
|
||||
continue;
|
||||
|
||||
case GS_RINGTYPE_P1:
|
||||
|
@ -435,7 +435,8 @@ void mtgsThreadObject::WaitGS()
|
|||
{
|
||||
pxAssertDev( !IsSelf(), "This method is only allowed from threads *not* named MTGS." );
|
||||
|
||||
if( !pxAssertDev( m_ExecMode == ExecMode_Running, "MTGS Warning! WaitGS issued on a suspended/paused thread." ) ) return;
|
||||
if( m_ExecMode == ExecMode_NoThreadYet || !IsRunning() ) return;
|
||||
if( !pxAssertDev( IsOpen(), "MTGS Warning! WaitGS issued on a closed thread." ) ) return;
|
||||
|
||||
// FIXME : Use semaphores instead of spinwaits.
|
||||
SetEvent();
|
||||
|
@ -790,7 +791,7 @@ void mtgsThreadObject::SendGameCRC( u32 crc )
|
|||
void mtgsThreadObject::WaitForOpen()
|
||||
{
|
||||
if( !gsIsOpened )
|
||||
m_sem_OpenDone.WaitGui();
|
||||
m_sem_OpenDone.Wait();
|
||||
m_sem_OpenDone.Reset();
|
||||
}
|
||||
|
||||
|
@ -806,13 +807,5 @@ void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
|
|||
else
|
||||
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
|
||||
|
||||
mtgsWaitGS();
|
||||
}
|
||||
|
||||
// Waits for the GS to empty out the entire ring buffer contents.
|
||||
// Used primarily for plugin startup/shutdown.
|
||||
void mtgsWaitGS()
|
||||
{
|
||||
mtgsThread.WaitGS();
|
||||
}
|
||||
|
||||
|
|
|
@ -668,7 +668,7 @@ PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] )
|
|||
{
|
||||
const PluginsEnum_t pid = pi->id;
|
||||
|
||||
Console.WriteLn( "\tBinding %s\t: %s ", tbl_PluginInfo[pid].shortname, folders[pid].ToUTF8().data() );
|
||||
Console.WriteLn( "\tBinding %s\t: %s ", tbl_PluginInfo[pid].shortname, folders[pid].ToUTF8() );
|
||||
|
||||
if( folders[pid].IsEmpty() )
|
||||
throw Exception::InvalidArgument( "Empty plugin filename." );
|
||||
|
@ -701,6 +701,8 @@ PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] )
|
|||
// (leave pointer null and do not generate error)
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
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;
|
||||
|
@ -1003,7 +1005,7 @@ void PluginManager::Init()
|
|||
//
|
||||
void PluginManager::Shutdown()
|
||||
{
|
||||
mtgsThread.Cancel(); // speedier shutdown!
|
||||
mtgsThread.Cancel(); // cancel it for speedier shutdown!
|
||||
|
||||
Close();
|
||||
DbgCon.Status( "Shutting down plugins..." );
|
||||
|
|
|
@ -58,8 +58,8 @@ R5900Exception::BaseExcept::~BaseExcept() throw (){}
|
|||
|
||||
void cpuReset()
|
||||
{
|
||||
if( mtgsThread.IsExecMode_Running() )
|
||||
mtgsWaitGS(); // GS better be done processing before we reset the EE, just in case.
|
||||
if( mtgsThread.IsOpen() )
|
||||
mtgsThread.WaitGS(); // GS better be done processing before we reset the EE, just in case.
|
||||
|
||||
cpuIsInitialized = true;
|
||||
|
||||
|
|
|
@ -29,12 +29,12 @@ static NonblockingMutex state_buffer_lock;
|
|||
|
||||
// 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
|
||||
// form the main thread.
|
||||
bool sys_resume_lock = false;
|
||||
// from the main thread.
|
||||
int sys_resume_lock = 0;
|
||||
|
||||
static FnType_OnThreadComplete* Callback_FreezeFinished = NULL;
|
||||
|
||||
static void StateThread_OnAppStatus( void* thr, const enum AppStatusEvent& stat )
|
||||
static void StateThread_OnAppStatus( void* thr, const enum AppEventType& stat )
|
||||
{
|
||||
if( (thr == NULL) || (stat != AppStatus_Exiting) ) return;
|
||||
((PersistentThread*)thr)->Cancel();
|
||||
|
@ -54,7 +54,7 @@ class _BaseStateThread : public PersistentThread
|
|||
typedef PersistentThread _parent;
|
||||
|
||||
protected:
|
||||
EventListenerBinding<AppStatusEvent> m_bind_OnExit;
|
||||
EventListenerBinding<AppEventType> m_bind_OnExit;
|
||||
|
||||
public:
|
||||
virtual ~_BaseStateThread() throw()
|
||||
|
@ -64,7 +64,7 @@ public:
|
|||
|
||||
protected:
|
||||
_BaseStateThread( const char* name, FnType_OnThreadComplete* onFinished ) :
|
||||
m_bind_OnExit( wxGetApp().Source_AppStatus(), EventListener<AppStatusEvent>( this, StateThread_OnAppStatus ) )
|
||||
m_bind_OnExit( wxGetApp().Source_AppStatus(), EventListener<AppEventType>( this, StateThread_OnAppStatus ) )
|
||||
{
|
||||
Callback_FreezeFinished = onFinished;
|
||||
m_name = L"StateThread::" + fromUTF8(name);
|
||||
|
@ -105,7 +105,7 @@ protected:
|
|||
{
|
||||
_parent::OnStart();
|
||||
|
||||
sys_resume_lock = true;
|
||||
++sys_resume_lock;
|
||||
CoreThread.Pause();
|
||||
}
|
||||
|
||||
|
@ -142,7 +142,7 @@ protected:
|
|||
throw Exception::RuntimeError( "ThawState request made, but no valid state exists!" );
|
||||
}
|
||||
|
||||
sys_resume_lock = true;
|
||||
++sys_resume_lock;
|
||||
CoreThread.Pause();
|
||||
}
|
||||
|
||||
|
@ -186,7 +186,7 @@ protected:
|
|||
void OnStart()
|
||||
{
|
||||
_parent::OnStart();
|
||||
m_gzfp = gzopen( toUTF8(m_filename), "wb" );
|
||||
m_gzfp = gzopen( m_filename.ToUTF8(), "wb" );
|
||||
if( m_gzfp == NULL )
|
||||
throw Exception::CreateStream( m_filename, "Cannot create savestate file for writing." );
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ protected:
|
|||
{
|
||||
_parent::OnStart();
|
||||
|
||||
m_gzfp = gzopen( toUTF8(m_filename), "rb" );
|
||||
m_gzfp = gzopen( m_filename.ToUTF8(), "rb" );
|
||||
if( m_gzfp == NULL )
|
||||
throw Exception::CreateStream( m_filename, "Cannot open savestate file for reading." );
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ protected:
|
|||
|
||||
void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt )
|
||||
{
|
||||
// clear the OnFreezeFinsihed to NULL now, in case of error.
|
||||
// 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;
|
||||
|
@ -293,7 +293,7 @@ void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt )
|
|||
ScopedPtr<PersistentThread> thr( (PersistentThread*)evt.GetClientData() );
|
||||
if( !pxAssertDev( thr != NULL, "NULL thread handle on freeze finished?" ) ) return;
|
||||
state_buffer_lock.Release();
|
||||
sys_resume_lock = false;
|
||||
--sys_resume_lock;
|
||||
thr->RethrowException();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,32 +28,32 @@
|
|||
static __threadlocal SysCoreThread* tls_coreThread = NULL;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysSuspendableThread *External Thread* Implementations
|
||||
// SysThreadBase *External Thread* Implementations
|
||||
// (Called form outside the context of this thread)
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
SysSuspendableThread::SysSuspendableThread() :
|
||||
SysThreadBase::SysThreadBase() :
|
||||
m_ExecMode( ExecMode_NoThreadYet )
|
||||
, m_lock_ExecMode()
|
||||
, m_ExecModeMutex()
|
||||
, m_ResumeEvent()
|
||||
, m_SuspendEvent()
|
||||
, m_ResumeProtection( false )
|
||||
, m_resume_guard( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
SysSuspendableThread::~SysSuspendableThread() throw()
|
||||
SysThreadBase::~SysThreadBase() throw()
|
||||
{
|
||||
}
|
||||
|
||||
void SysSuspendableThread::Start()
|
||||
void SysThreadBase::Start()
|
||||
{
|
||||
_parent::Start();
|
||||
m_ExecMode = ExecMode_Suspending;
|
||||
m_ExecMode = ExecMode_Closing;
|
||||
m_sem_event.Post();
|
||||
}
|
||||
|
||||
|
||||
void SysSuspendableThread::OnStart()
|
||||
void SysThreadBase::OnStart()
|
||||
{
|
||||
if( !pxAssertDev( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return;
|
||||
|
||||
|
@ -63,9 +63,8 @@ void SysSuspendableThread::OnStart()
|
|||
_parent::OnStart();
|
||||
}
|
||||
|
||||
// Pauses the emulation state at the next PS2 vsync, and returns control to the calling
|
||||
// thread; or does nothing if the core is already suspended. Calling this thread from the
|
||||
// Core thread will result in deadlock.
|
||||
// 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.
|
||||
//
|
||||
// Parameters:
|
||||
// isNonblocking - if set to true then the function will not block for emulation suspension.
|
||||
|
@ -78,61 +77,78 @@ void SysSuspendableThread::OnStart()
|
|||
// suspended.
|
||||
//
|
||||
// Exceptions:
|
||||
// CancelEvent - thrown if the thread is already in a Paused 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 in progress.
|
||||
// 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
|
||||
// in progress.
|
||||
//
|
||||
bool SysSuspendableThread::Suspend( bool isBlocking )
|
||||
bool SysThreadBase::Suspend( bool isBlocking )
|
||||
{
|
||||
if( IsSelf() || !IsRunning() ) return false;
|
||||
|
||||
// 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;
|
||||
|
||||
{
|
||||
ScopedLock locker( m_lock_ExecMode );
|
||||
ScopedLock locker( m_ExecModeMutex );
|
||||
|
||||
if( m_ExecMode == ExecMode_Suspended )
|
||||
return false;
|
||||
// Check again -- status could have changed since above.
|
||||
if( m_ExecMode == ExecMode_Closed ) return false;
|
||||
|
||||
if( m_ExecMode == ExecMode_Pausing || m_ExecMode == ExecMode_Paused )
|
||||
throw Exception::CancelEvent( "Another thread is pausing the VM state." );
|
||||
|
||||
if( m_ExecMode == ExecMode_Running )
|
||||
if( m_ExecMode == ExecMode_Opened )
|
||||
{
|
||||
m_ExecMode = ExecMode_Suspending;
|
||||
m_ExecMode = ExecMode_Closing;
|
||||
retval = true;
|
||||
}
|
||||
|
||||
pxAssertDev( m_ExecMode == ExecMode_Suspending, "ExecMode should be nothing other than Suspending..." );
|
||||
}
|
||||
pxAssertDev( m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing..." );
|
||||
m_SuspendEvent.Reset();
|
||||
m_sem_event.Post();
|
||||
if( isBlocking ) m_SuspendEvent.WaitGui();
|
||||
}
|
||||
|
||||
if( isBlocking ) m_SuspendEvent.Wait();
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool SysSuspendableThread::Pause()
|
||||
// Returns:
|
||||
// The previous suspension state; true if the thread was running or false if it was
|
||||
// closed, not running, or paused.
|
||||
//
|
||||
bool SysThreadBase::Pause()
|
||||
{
|
||||
if( IsSelf() || !IsRunning() ) return false;
|
||||
|
||||
// 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;
|
||||
|
||||
{
|
||||
ScopedLock locker( m_lock_ExecMode );
|
||||
ScopedLock locker( m_ExecModeMutex );
|
||||
|
||||
if( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Paused) ) return false;
|
||||
// Check again -- status could have changed since above.
|
||||
if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return false;
|
||||
|
||||
if( m_ExecMode == ExecMode_Running )
|
||||
if( m_ExecMode == ExecMode_Opened )
|
||||
{
|
||||
m_ExecMode = ExecMode_Pausing;
|
||||
retval = true;
|
||||
}
|
||||
|
||||
pxAssertDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
|
||||
}
|
||||
|
||||
m_SuspendEvent.Reset();
|
||||
m_sem_event.Post();
|
||||
m_SuspendEvent.WaitGui();
|
||||
}
|
||||
|
||||
m_SuspendEvent.Wait();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -141,72 +157,92 @@ bool SysSuspendableThread::Pause()
|
|||
// settings were changed, resets will be performed as needed and emulation state resumed from
|
||||
// memory savestates.
|
||||
//
|
||||
// Note that this is considered a non-blocking action. Most times the state is safely resumed
|
||||
// on return, but in the case of re-entrant or nested message handling the function may return
|
||||
// before the thread has resumed. If you need explicit behavior tied to the completion of the
|
||||
// Resume, you'll need to bind callbacks to either OnResumeReady or OnResumeInThread.
|
||||
//
|
||||
// Exceptions (can occur on first call only):
|
||||
// PluginInitError - thrown if a plugin fails init (init is performed on the current thread
|
||||
// on the first time the thread is resumed from it's initial idle state)
|
||||
// ThreadCreationError - Insufficient system resources to create thread.
|
||||
//
|
||||
void SysSuspendableThread::Resume()
|
||||
void SysThreadBase::Resume()
|
||||
{
|
||||
if( IsSelf() ) return;
|
||||
if( m_ExecMode == ExecMode_Opened ) return;
|
||||
if( !pxAssert( g_plugins != NULL ) ) return;
|
||||
|
||||
{
|
||||
ScopedLock locker( m_lock_ExecMode );
|
||||
ScopedLock locker( m_ExecModeMutex );
|
||||
|
||||
// Recursion guard is needed because of the non-blocking Wait if the state
|
||||
// is Suspending/Closing. Processed events could recurse into Resume, and we'll
|
||||
// want to silently ignore them.
|
||||
|
||||
RecursionGuard guard( m_resume_guard );
|
||||
if( guard.IsReentrant() ) return;
|
||||
|
||||
switch( m_ExecMode )
|
||||
{
|
||||
case ExecMode_Running: return;
|
||||
case ExecMode_Opened: return;
|
||||
|
||||
case ExecMode_NoThreadYet:
|
||||
Start();
|
||||
m_ExecMode = ExecMode_Suspending;
|
||||
m_ExecMode = ExecMode_Closing;
|
||||
// fall through...
|
||||
|
||||
case ExecMode_Suspending:
|
||||
case ExecMode_Closing:
|
||||
case ExecMode_Pausing:
|
||||
// we need to make sure and wait for the emuThread to enter a fully suspended
|
||||
// state before continuing...
|
||||
|
||||
locker.Unlock(); // no deadlocks please, thanks. :)
|
||||
m_SuspendEvent.WaitGui();
|
||||
++sys_resume_lock;
|
||||
m_SuspendEvent.Wait();
|
||||
--sys_resume_lock;
|
||||
locker.Lock();
|
||||
|
||||
// The entire state coming out of a Wait is indeterminate because of user input
|
||||
// and pending messages being handled. If something doesn't feel right, we should
|
||||
// abort.
|
||||
|
||||
if( (m_ExecMode != ExecMode_Closed) && (m_ExecMode != ExecMode_Paused) ) return;
|
||||
if( g_plugins == NULL ) return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pxAssertDev( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Paused),
|
||||
"SysSuspendableThread is not in a suspended/paused state? wtf!" );
|
||||
pxAssertDev( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused),
|
||||
"SysThreadBase is not in a closed/paused state? wtf!" );
|
||||
|
||||
m_ResumeProtection = true;
|
||||
OnResumeReady();
|
||||
m_ResumeProtection = false;
|
||||
m_ExecMode = ExecMode_Running;
|
||||
m_ExecMode = ExecMode_Opened;
|
||||
m_ResumeEvent.Post();
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SysSuspendableThread *Worker* Implementations
|
||||
// SysThreadBase *Worker* Implementations
|
||||
// (Called from the context of this thread only)
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
void SysSuspendableThread::OnCleanupInThread()
|
||||
void SysThreadBase::OnCleanupInThread()
|
||||
{
|
||||
ScopedLock locker( m_lock_ExecMode );
|
||||
ScopedLock locker( m_ExecModeMutex );
|
||||
m_ExecMode = ExecMode_NoThreadYet;
|
||||
_parent::OnCleanupInThread();
|
||||
}
|
||||
|
||||
void SysSuspendableThread::StateCheck( bool isCancelable )
|
||||
void SysThreadBase::StateCheckInThread( bool isCancelable )
|
||||
{
|
||||
// Shortcut for the common case, to avoid unnecessary Mutex locks:
|
||||
if( m_ExecMode == ExecMode_Running )
|
||||
if( m_ExecMode == ExecMode_Opened )
|
||||
{
|
||||
if( isCancelable ) TestCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// Oh, seems we need a full lock, because something special is happening!
|
||||
ScopedLock locker( m_lock_ExecMode );
|
||||
ScopedLock locker( m_ExecModeMutex );
|
||||
|
||||
switch( m_ExecMode )
|
||||
{
|
||||
|
@ -219,7 +255,7 @@ void SysSuspendableThread::StateCheck( bool isCancelable )
|
|||
break;
|
||||
#endif
|
||||
|
||||
case ExecMode_Running:
|
||||
case ExecMode_Opened:
|
||||
// Yup, need this a second time. Variable state could have changed while we
|
||||
// were trying to acquire the lock above.
|
||||
if( isCancelable )
|
||||
|
@ -236,26 +272,26 @@ void SysSuspendableThread::StateCheck( bool isCancelable )
|
|||
// fallthrough...
|
||||
|
||||
case ExecMode_Paused:
|
||||
m_lock_ExecMode.Unlock();
|
||||
m_ExecModeMutex.Unlock();
|
||||
while( m_ExecMode == ExecMode_Paused )
|
||||
m_ResumeEvent.WaitGui();
|
||||
m_ResumeEvent.WaitRaw();
|
||||
|
||||
OnResumeInThread( false );
|
||||
break;
|
||||
|
||||
// -------------------------------------
|
||||
case ExecMode_Suspending:
|
||||
case ExecMode_Closing:
|
||||
{
|
||||
OnSuspendInThread();
|
||||
m_ExecMode = ExecMode_Suspended;
|
||||
m_ExecMode = ExecMode_Closed;
|
||||
m_SuspendEvent.Post();
|
||||
}
|
||||
// fallthrough...
|
||||
|
||||
case ExecMode_Suspended:
|
||||
m_lock_ExecMode.Unlock();
|
||||
while( m_ExecMode == ExecMode_Suspended )
|
||||
m_ResumeEvent.WaitGui();
|
||||
case ExecMode_Closed:
|
||||
m_ExecModeMutex.Unlock();
|
||||
while( m_ExecMode == ExecMode_Closed )
|
||||
m_ResumeEvent.WaitRaw();
|
||||
|
||||
OnResumeInThread( true );
|
||||
break;
|
||||
|
@ -330,8 +366,7 @@ void SysCoreThread::ApplySettings( const Pcsx2Config& src )
|
|||
{
|
||||
if( src == EmuConfig ) return;
|
||||
|
||||
// Suspend only if ResumeProtection is false:
|
||||
const bool resumeWhenDone = !m_ResumeProtection && Suspend();
|
||||
const bool resumeWhenDone = Suspend();
|
||||
|
||||
m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
|
||||
m_resetProfilers = (src.Profiler != EmuConfig.Profiler );
|
||||
|
@ -416,8 +451,8 @@ void SysCoreThread::ExecuteTaskInThread()
|
|||
{
|
||||
tls_coreThread = this;
|
||||
|
||||
m_sem_event.Wait();
|
||||
StateCheck();
|
||||
m_sem_event.WaitRaw();
|
||||
StateCheckInThread();
|
||||
CpuExecute();
|
||||
}
|
||||
|
||||
|
|
|
@ -31,39 +31,71 @@ public:
|
|||
};
|
||||
|
||||
|
||||
class SysSuspendableThread : public PersistentThread, public virtual ISysThread
|
||||
class SysThreadBase : public PersistentThread, public virtual ISysThread
|
||||
{
|
||||
typedef PersistentThread _parent;
|
||||
|
||||
protected:
|
||||
// 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.
|
||||
enum ExecutionMode
|
||||
{
|
||||
// Thread has not been created yet. Typically this is the same as IsRunning()
|
||||
// returning FALSE.
|
||||
ExecMode_NoThreadYet,
|
||||
ExecMode_Running,
|
||||
ExecMode_Suspending,
|
||||
ExecMode_Suspended,
|
||||
|
||||
// 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 active and running, with pluigns in an "open" state.
|
||||
ExecMode_Opened,
|
||||
|
||||
// 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,
|
||||
};
|
||||
|
||||
volatile ExecutionMode m_ExecMode;
|
||||
MutexLock m_lock_ExecMode;
|
||||
MutexLock m_ExecModeMutex;
|
||||
|
||||
Semaphore m_ResumeEvent;
|
||||
Semaphore m_SuspendEvent;
|
||||
bool m_ResumeProtection;
|
||||
int m_resume_guard;
|
||||
|
||||
public:
|
||||
explicit SysSuspendableThread();
|
||||
virtual ~SysSuspendableThread() throw();
|
||||
explicit SysThreadBase();
|
||||
virtual ~SysThreadBase() throw();
|
||||
|
||||
bool IsExecMode_Running() const { return m_ExecMode == ExecMode_Running; }
|
||||
// Thread safety for IsOpen / IsClosed: The execution mode can change at any time on
|
||||
// any thread, so the actual status may have already changed by the time this function
|
||||
// returns its result. Typically this isn't of major concern. However if you need
|
||||
// more assured execution mode status, issue a lock against the ExecutionModeMutex()
|
||||
// first.
|
||||
bool IsOpen() const
|
||||
{
|
||||
return m_ExecMode > ExecMode_Closed;
|
||||
}
|
||||
|
||||
bool IsClosed() const { return !IsOpen(); }
|
||||
|
||||
ExecutionMode GetExecutionMode() const { return m_ExecMode; }
|
||||
MutexLock& ExecutionModeMutex() { return m_ExecModeMutex; }
|
||||
|
||||
virtual bool Suspend( bool isBlocking = true );
|
||||
virtual void Resume();
|
||||
virtual bool Pause();
|
||||
|
||||
virtual void StateCheck( bool isCancelable = true );
|
||||
virtual void StateCheckInThread( bool isCancelable = true );
|
||||
virtual void OnCleanupInThread();
|
||||
|
||||
// This function is called by Resume immediately prior to releasing the suspension of
|
||||
|
@ -79,21 +111,21 @@ protected:
|
|||
virtual void Start();
|
||||
|
||||
// Extending classes should implement this, but should not call it. The parent class
|
||||
// handles invocation by the following guidelines: Called *in thread* from StateCheck()
|
||||
// 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.
|
||||
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 StateCheck()
|
||||
// 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.
|
||||
virtual void OnPauseInThread()=0;
|
||||
|
||||
// Extending classes should implement this, but should not call it. The parent class
|
||||
// handles invocation by the following guidelines: Called from StateCheck() after the
|
||||
// handles invocation by the following guidelines: Called from StateCheckInThread() after the
|
||||
// thread has been suspended and then subsequently resumed.
|
||||
// Parameter:
|
||||
// isSuspended - set to TRUE if the thread is returning from a suspended state, or
|
||||
|
@ -104,9 +136,9 @@ protected:
|
|||
// --------------------------------------------------------------------------------------
|
||||
// EECoreThread class
|
||||
// --------------------------------------------------------------------------------------
|
||||
class SysCoreThread : public SysSuspendableThread
|
||||
class SysCoreThread : public SysThreadBase
|
||||
{
|
||||
typedef SysSuspendableThread _parent;
|
||||
typedef SysThreadBase _parent;
|
||||
|
||||
protected:
|
||||
bool m_resetRecompilers;
|
||||
|
@ -141,3 +173,5 @@ protected:
|
|||
virtual void OnCleanupInThread();
|
||||
virtual void ExecuteTaskInThread();
|
||||
};
|
||||
|
||||
extern int sys_resume_lock;
|
||||
|
|
|
@ -728,7 +728,7 @@ void vif1TransferFromMemory()
|
|||
{
|
||||
if (size > 1)
|
||||
{
|
||||
mtgsWaitGS();
|
||||
mtgsThread.WaitGS();
|
||||
GSreadFIFO((u64*)&PS2MEM_HW[0x5000]);
|
||||
}
|
||||
pMem[0] = psHu64(VIF1_FIFO);
|
||||
|
@ -738,7 +738,7 @@ void vif1TransferFromMemory()
|
|||
}
|
||||
else
|
||||
{
|
||||
mtgsWaitGS();
|
||||
mtgsThread.WaitGS();
|
||||
GSreadFIFO2(pMem, vif1ch->qwc);
|
||||
|
||||
// set incase read
|
||||
|
|
|
@ -22,8 +22,9 @@
|
|||
#include <wx/apptrait.h>
|
||||
|
||||
#include "Utilities/Listeners.h"
|
||||
#include "IniInterface.h"
|
||||
|
||||
class IniInterface;
|
||||
//class IniInterface;
|
||||
class MainEmuFrame;
|
||||
class GSFrame;
|
||||
class ConsoleLogFrame;
|
||||
|
@ -40,9 +41,6 @@ class AppCoreThread;
|
|||
|
||||
typedef void FnType_OnThreadComplete(const wxCommandEvent& evt);
|
||||
|
||||
#define AllowFromMainThreadOnly() \
|
||||
pxAssertMsg( wxThread::IsMain(), "Thread affinity violation: Call allowed from main thread only." )
|
||||
|
||||
BEGIN_DECLARE_EVENT_TYPES()
|
||||
DECLARE_EVENT_TYPE( pxEVT_SemaphorePing, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEVT_OpenModalDialog, -1 )
|
||||
|
@ -159,7 +157,7 @@ enum DialogIdentifiers
|
|||
DialogId_About,
|
||||
};
|
||||
|
||||
enum AppStatusEvent
|
||||
enum AppEventType
|
||||
{
|
||||
// Maybe this will be expanded upon later..?
|
||||
AppStatus_Exiting
|
||||
|
@ -309,6 +307,23 @@ struct MsgboxEventResult
|
|||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// AppIniSaver / AppIniLoader
|
||||
// --------------------------------------------------------------------------------------
|
||||
class AppIniSaver : public IniSaver
|
||||
{
|
||||
public:
|
||||
AppIniSaver();
|
||||
virtual ~AppIniSaver() {}
|
||||
};
|
||||
|
||||
class AppIniLoader : public IniLoader
|
||||
{
|
||||
public:
|
||||
AppIniLoader();
|
||||
virtual ~AppIniLoader() {}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Pcsx2App - main wxApp class
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -390,7 +405,7 @@ public:
|
|||
ConsoleLogFrame* GetProgramLog();
|
||||
void ProgramLog_CountMsg();
|
||||
void ProgramLog_PostEvent( wxEvent& evt );
|
||||
void EnableConsoleLogging() const;
|
||||
void EnableAllLogging() const;
|
||||
void DisableWindowLogging() const;
|
||||
void DisableDiskLogging() const;
|
||||
void OnProgramLogClosed();
|
||||
|
@ -402,12 +417,16 @@ public:
|
|||
protected:
|
||||
CmdEvt_Source m_evtsrc_CorePluginStatus;
|
||||
CmdEvt_Source m_evtsrc_CoreThreadStatus;
|
||||
EventSource<AppStatusEvent> m_evtsrc_AppStatus;
|
||||
EventSource<int> m_evtsrc_SettingsApplied;
|
||||
EventSource<IniInterface> m_evtsrc_SettingsLoadSave;
|
||||
EventSource<AppEventType> m_evtsrc_AppStatus;
|
||||
|
||||
public:
|
||||
CmdEvt_Source& Source_CoreThreadStatus() { return m_evtsrc_CoreThreadStatus; }
|
||||
CmdEvt_Source& Source_CorePluginStatus() { return m_evtsrc_CorePluginStatus; }
|
||||
EventSource<AppStatusEvent>& Source_AppStatus() { return m_evtsrc_AppStatus; }
|
||||
EventSource<int>& Source_SettingsApplied() { return m_evtsrc_SettingsApplied; }
|
||||
EventSource<IniInterface>& Source_SettingsLoadSave() { return m_evtsrc_SettingsLoadSave; }
|
||||
EventSource<AppEventType>& Source_AppStatus() { return m_evtsrc_AppStatus; }
|
||||
|
||||
protected:
|
||||
void InitDefaultGlobalAccelerators();
|
||||
|
@ -453,6 +472,7 @@ protected:
|
|||
|
||||
enum CoreThreadStatus
|
||||
{
|
||||
CoreStatus_Indeterminate,
|
||||
CoreStatus_Resumed,
|
||||
CoreStatus_Suspended,
|
||||
CoreStatus_Stopped,
|
||||
|
@ -471,11 +491,14 @@ public:
|
|||
|
||||
virtual bool Suspend( bool isBlocking=true );
|
||||
virtual void Resume();
|
||||
virtual void StateCheck( bool isCancelable=true );
|
||||
virtual void StateCheckInThread( bool isCancelable=true );
|
||||
virtual void ApplySettings( const Pcsx2Config& src );
|
||||
|
||||
protected:
|
||||
virtual void OnResumeReady();
|
||||
|
||||
virtual void OnResumeInThread( bool IsSuspended );
|
||||
virtual void OnSuspendInThread();
|
||||
virtual void OnCleanupInThread();
|
||||
virtual void ExecuteTaskInThread();
|
||||
};
|
||||
|
@ -514,8 +537,6 @@ DECLARE_APP(Pcsx2App)
|
|||
// External App-related Globals and Shortcuts
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
extern bool sys_resume_lock;
|
||||
|
||||
extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest );
|
||||
extern void LoadPluginsPassive( FnType_OnThreadComplete* onComplete );
|
||||
extern void LoadPluginsImmediate();
|
||||
|
@ -523,7 +544,7 @@ extern void UnloadPlugins();
|
|||
|
||||
extern void AppLoadSettings();
|
||||
extern void AppSaveSettings();
|
||||
extern void AppApplySettings( const AppConfig* oldconf=NULL );
|
||||
extern void AppApplySettings( const AppConfig* oldconf=NULL, bool saveOnSuccess=false );
|
||||
|
||||
extern bool SysHasValidState();
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ void Pcsx2App::OnAssertFailure( const wxChar *file, int line, const wxChar *func
|
|||
|
||||
// make life easier for people using VC++ IDE by using this format, which allows double-click
|
||||
// response times from the Output window...
|
||||
dbgmsg.Printf( L"%s(%d) : assertion failed%s%s: %s", file, line,
|
||||
dbgmsg.Printf( L"%s(%d) : assertion failed%s%s: %s\n", file, line,
|
||||
(func==NULL) ? wxEmptyString : L" in ",
|
||||
(func==NULL) ? wxEmptyString : func,
|
||||
message.c_str()
|
||||
|
|
|
@ -375,8 +375,7 @@ void AppConfig::LoadSaveMemcards( IniInterface& ini )
|
|||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void AppConfig::LoadSave( IniInterface& ini )
|
||||
void AppConfig::LoadSaveRootItems( IniInterface& ini )
|
||||
{
|
||||
AppConfig defaults;
|
||||
|
||||
|
@ -392,7 +391,12 @@ void AppConfig::LoadSave( IniInterface& ini )
|
|||
IniEntry( CurrentIso );
|
||||
|
||||
ini.EnumEntry( L"CdvdSource", CdvdSource, CDVD_SourceLabels, defaults.CdvdSource );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void AppConfig::LoadSave( IniInterface& ini )
|
||||
{
|
||||
LoadSaveRootItems( ini );
|
||||
LoadSaveMemcards( ini );
|
||||
|
||||
// Process various sub-components:
|
||||
|
@ -508,6 +512,30 @@ wxFileConfig* OpenFileConfig( const wxString& filename )
|
|||
return new wxFileConfig( wxEmptyString, wxEmptyString, filename, wxEmptyString, wxCONFIG_USE_RELATIVE_PATH );
|
||||
}
|
||||
|
||||
void RelocateLogfile()
|
||||
{
|
||||
g_Conf->Folders.Logs.Mkdir();
|
||||
|
||||
wxString newlogname( Path::Combine( g_Conf->Folders.Logs.ToString(), L"emuLog.txt" ) );
|
||||
|
||||
if( (emuLog != NULL) && (emuLogName != newlogname) )
|
||||
{
|
||||
Console.Status( "\nRelocating Logfile...\n\tFrom: %s\n\tTo : %s\n", emuLogName.c_str(), newlogname.c_str() );
|
||||
wxGetApp().DisableDiskLogging();
|
||||
|
||||
fclose( emuLog );
|
||||
emuLog = NULL;
|
||||
}
|
||||
|
||||
if( emuLog == NULL )
|
||||
{
|
||||
emuLogName = newlogname;
|
||||
emuLog = fopen( emuLogName.ToUTF8(), "wb" );
|
||||
}
|
||||
|
||||
wxGetApp().EnableAllLogging();
|
||||
}
|
||||
|
||||
// Parameters:
|
||||
// overwrite - this option forces the current settings to overwrite any existing settings that might
|
||||
// be saved to the configured ini/settings folder.
|
||||
|
@ -517,35 +545,32 @@ void AppConfig_OnChangedSettingsFolder( bool overwrite )
|
|||
PathDefs::GetDocuments().Mkdir();
|
||||
PathDefs::GetSettings().Mkdir();
|
||||
|
||||
// Allow wx to use our config, and enforces auto-cleanup as well
|
||||
if( overwrite )
|
||||
{
|
||||
if( !wxRemoveFile( GetSettingsFilename() ) )
|
||||
throw Exception::AccessDenied( "Failed to overwrite settings; permission to file was denied." );
|
||||
}
|
||||
|
||||
//if( GetAppConfig() != NULL )
|
||||
// wxGetApp().Source_SettingsChanged().Dispatch( SettingsEvt_IniClosing );
|
||||
|
||||
// Bind into wxConfigBase to allow wx to use our config internally, and delete whatever
|
||||
// comes out (cleans up prev config, if one).
|
||||
delete wxConfigBase::Set( OpenFileConfig( GetSettingsFilename() ) );
|
||||
wxConfigBase::Get()->SetRecordDefaults();
|
||||
GetAppConfig()->SetRecordDefaults();
|
||||
|
||||
//wxGetApp().Source_SettingsChanged().Dispatch( SettingsEvt_IniOpening );
|
||||
|
||||
if( !overwrite )
|
||||
AppLoadSettings();
|
||||
|
||||
AppApplySettings();
|
||||
sMainFrame.ReloadRecentLists();
|
||||
|
||||
g_Conf->Folders.Logs.Mkdir();
|
||||
|
||||
wxString newlogname( Path::Combine( g_Conf->Folders.Logs.ToString(), L"emuLog.txt" ) );
|
||||
|
||||
wxGetApp().DisableDiskLogging();
|
||||
|
||||
if( emuLog != NULL )
|
||||
{
|
||||
if( emuLogName != newlogname )
|
||||
{
|
||||
fclose( emuLog );
|
||||
emuLog = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if( emuLog == NULL )
|
||||
{
|
||||
emuLogName = newlogname;
|
||||
emuLog = fopen( toUTF8(emuLogName), "wb" );
|
||||
}
|
||||
wxGetApp().EnableConsoleLogging();
|
||||
}
|
||||
|
||||
// Returns the current application configuration file. This is preferred over using
|
||||
// wxConfigBase::GetAppConfig(), since it defaults to *not* creating a config file
|
||||
// automatically (which is typically highly undesired behavior in our system)
|
||||
wxConfigBase* GetAppConfig()
|
||||
{
|
||||
return wxConfigBase::Get( false );
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
#include "CDVD/CDVDaccess.h"
|
||||
|
||||
class IniInterface;
|
||||
class wxConfigBase;
|
||||
class wxFileConfig;
|
||||
|
||||
extern bool UseAdminMode; // dictates if the program uses /home/user or /cwd for the program data
|
||||
|
@ -164,6 +165,7 @@ public:
|
|||
void LoadSaveUserMode( IniInterface& ini, const wxString& cwdhash );
|
||||
|
||||
void LoadSave( IniInterface& ini );
|
||||
void LoadSaveRootItems( IniInterface& ini );
|
||||
void LoadSaveMemcards( IniInterface& ini );
|
||||
};
|
||||
|
||||
|
@ -176,6 +178,8 @@ struct ConfigOverrides
|
|||
extern ConfigOverrides OverrideOptions;
|
||||
|
||||
extern wxFileConfig* OpenFileConfig( const wxString& filename );
|
||||
extern void RelocateLogfile();
|
||||
extern void AppConfig_OnChangedSettingsFolder( bool overwrite = false );
|
||||
extern wxConfigBase* GetAppConfig();
|
||||
|
||||
extern ScopedPtr<AppConfig> g_Conf;
|
||||
|
|
|
@ -31,9 +31,9 @@ bool AppCoreThread::Suspend( bool isBlocking )
|
|||
{
|
||||
bool retval = _parent::Suspend( isBlocking );
|
||||
|
||||
wxCommandEvent evt( pxEVT_CoreThreadStatus );
|
||||
/*wxCommandEvent evt( pxEVT_CoreThreadStatus );
|
||||
evt.SetInt( CoreStatus_Suspended );
|
||||
wxGetApp().AddPendingEvent( evt );
|
||||
wxGetApp().AddPendingEvent( evt );*/
|
||||
|
||||
// Clear the sticky key statuses, because hell knows what'll change while the PAD
|
||||
// plugin is suspended.
|
||||
|
@ -50,12 +50,24 @@ void AppCoreThread::Resume()
|
|||
// Thread control (suspend / resume) should only be performed from the main/gui thread.
|
||||
if( !AllowFromMainThreadOnly() ) return;
|
||||
|
||||
if( sys_resume_lock )
|
||||
if( sys_resume_lock > 0 )
|
||||
{
|
||||
Console.WriteLn( "SysResume: State is locked, ignoring Resume request!" );
|
||||
return;
|
||||
}
|
||||
_parent::Resume();
|
||||
|
||||
if( m_ExecMode != ExecMode_Opened )
|
||||
{
|
||||
// Resume failed for some reason, so update GUI statuses and post a message to
|
||||
// try again on the resume.
|
||||
|
||||
wxCommandEvent evt( pxEVT_CoreThreadStatus );
|
||||
evt.SetInt( CoreStatus_Suspended );
|
||||
wxGetApp().AddPendingEvent( evt );
|
||||
|
||||
sApp.SysExecute();
|
||||
}
|
||||
}
|
||||
|
||||
void AppCoreThread::OnResumeReady()
|
||||
|
@ -65,11 +77,25 @@ void AppCoreThread::OnResumeReady()
|
|||
if( GSopen2 != NULL )
|
||||
wxGetApp().OpenGsFrame();
|
||||
|
||||
_parent::OnResumeReady();
|
||||
}
|
||||
|
||||
void AppCoreThread::OnResumeInThread( bool isSuspended )
|
||||
{
|
||||
_parent::OnResumeInThread( isSuspended );
|
||||
|
||||
wxCommandEvent evt( pxEVT_CoreThreadStatus );
|
||||
evt.SetInt( CoreStatus_Resumed );
|
||||
wxGetApp().AddPendingEvent( evt );
|
||||
}
|
||||
|
||||
_parent::OnResumeReady();
|
||||
void AppCoreThread::OnSuspendInThread()
|
||||
{
|
||||
_parent::OnSuspendInThread();
|
||||
|
||||
wxCommandEvent evt( pxEVT_CoreThreadStatus );
|
||||
evt.SetInt( CoreStatus_Suspended );
|
||||
wxGetApp().AddPendingEvent( evt );
|
||||
}
|
||||
|
||||
// Called whenever the thread has terminated, for either regular or irregular reasons.
|
||||
|
@ -88,9 +114,9 @@ void AppCoreThread::OnCleanupInThread()
|
|||
extern int TranslateGDKtoWXK( u32 keysym );
|
||||
#endif
|
||||
|
||||
void AppCoreThread::StateCheck( bool isCancelable )
|
||||
void AppCoreThread::StateCheckInThread( bool isCancelable )
|
||||
{
|
||||
_parent::StateCheck( isCancelable );
|
||||
_parent::StateCheckInThread( isCancelable );
|
||||
if( !pxAssert(g_plugins!=NULL) ) return;
|
||||
|
||||
const keyEvent* ev = PADkeyEvent();
|
||||
|
@ -124,7 +150,7 @@ void AppCoreThread::StateCheck( bool isCancelable )
|
|||
// suspended. If the thread has mot been suspended, this call will fail *silently*.
|
||||
void AppCoreThread::ApplySettings( const Pcsx2Config& src )
|
||||
{
|
||||
if( m_ExecMode != ExecMode_Suspended ) return;
|
||||
if( m_ExecMode != ExecMode_Closed ) return;
|
||||
if( src == EmuConfig ) return;
|
||||
|
||||
// Re-entry guard protects against cases where code wants to manually set core settings
|
||||
|
|
|
@ -118,6 +118,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();
|
||||
}
|
||||
|
||||
void Pcsx2App::OnInitCmdLine( wxCmdLineParser& parser )
|
||||
|
@ -203,7 +207,7 @@ typedef void (wxEvtHandler::*pxMessageBoxEventFunction)(pxMessageBoxEvent&);
|
|||
bool Pcsx2App::OnInit()
|
||||
{
|
||||
g_Conf = new AppConfig();
|
||||
EnableConsoleLogging();
|
||||
EnableAllLogging();
|
||||
|
||||
wxInitAllImageHandlers();
|
||||
if( !wxApp::OnInit() ) return false;
|
||||
|
@ -260,7 +264,7 @@ bool Pcsx2App::OnInit()
|
|||
}
|
||||
|
||||
m_ProgramLogBox = new ConsoleLogFrame( m_MainFrame, L"PCSX2 Program Log", g_Conf->ProgLogBox );
|
||||
EnableConsoleLogging();
|
||||
EnableAllLogging();
|
||||
|
||||
SetTopWindow( m_MainFrame ); // not really needed...
|
||||
SetExitOnFrameDelete( true ); // but being explicit doesn't hurt...
|
||||
|
@ -316,6 +320,7 @@ bool Pcsx2App::OnInit()
|
|||
catch( Exception::StartupAborted& ex )
|
||||
{
|
||||
Console.Notice( ex.FormatDiagnosticMessage() );
|
||||
CleanupMess();
|
||||
return false;
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -347,6 +352,9 @@ void Pcsx2App::CleanupMess()
|
|||
// during the wxApp destructor. -- air
|
||||
|
||||
// FIXME: performing a wxYield() here may fix that problem. -- air
|
||||
|
||||
while( wxGetLocale() != NULL )
|
||||
delete wxGetLocale();
|
||||
}
|
||||
|
||||
Pcsx2App::Pcsx2App() :
|
||||
|
@ -369,6 +377,9 @@ Pcsx2App::~Pcsx2App()
|
|||
// to happen here in the destructor.
|
||||
|
||||
CleanupMess();
|
||||
|
||||
delete wxConfigBase::Set( NULL );
|
||||
|
||||
Console_SetActiveHandler( ConsoleWriter_Null );
|
||||
|
||||
if( emuLog != NULL )
|
||||
|
@ -377,6 +388,8 @@ Pcsx2App::~Pcsx2App()
|
|||
emuLog = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
// Using the MSVCRT to track memory leaks:
|
||||
// ------------------------------------------------------------------------------------------
|
||||
|
@ -403,6 +416,6 @@ struct CrtDebugBreak
|
|||
}
|
||||
};
|
||||
|
||||
//CrtDebugBreak breakAt( 157 );
|
||||
//CrtDebugBreak breakAt( 20603 );
|
||||
|
||||
#endif
|
|
@ -293,9 +293,6 @@ int Pcsx2App::OnExit()
|
|||
if( g_Conf )
|
||||
AppSaveSettings();
|
||||
|
||||
while( wxGetLocale() != NULL )
|
||||
delete wxGetLocale();
|
||||
|
||||
return wxApp::OnExit();
|
||||
}
|
||||
|
||||
|
@ -311,7 +308,7 @@ MainEmuFrame& Pcsx2App::GetMainFrame() const
|
|||
return *m_MainFrame;
|
||||
}
|
||||
|
||||
void AppApplySettings( const AppConfig* oldconf )
|
||||
void AppApplySettings( const AppConfig* oldconf, bool saveOnSuccess )
|
||||
{
|
||||
AllowFromMainThreadOnly();
|
||||
|
||||
|
@ -324,6 +321,8 @@ void AppApplySettings( const AppConfig* oldconf )
|
|||
|
||||
g_Conf->EmuOptions.BiosFilename = g_Conf->FullpathToBios();
|
||||
|
||||
RelocateLogfile();
|
||||
|
||||
bool resume = CoreThread.Suspend();
|
||||
|
||||
// Update the compression attribute on the Memcards folder.
|
||||
|
@ -343,34 +342,44 @@ void AppApplySettings( const AppConfig* oldconf )
|
|||
}
|
||||
}
|
||||
|
||||
sApp.Source_SettingsApplied().Dispatch( 0 );
|
||||
CoreThread.ApplySettings( g_Conf->EmuOptions );
|
||||
|
||||
if( resume )
|
||||
CoreThread.Resume();
|
||||
|
||||
if( saveOnSuccess )
|
||||
AppSaveSettings();
|
||||
}
|
||||
|
||||
static wxFileConfig _dud_config;
|
||||
|
||||
AppIniSaver::AppIniSaver() :
|
||||
IniSaver( (GetAppConfig() != NULL) ? *GetAppConfig() : _dud_config )
|
||||
{
|
||||
}
|
||||
|
||||
AppIniLoader::AppIniLoader() :
|
||||
IniLoader( (GetAppConfig() != NULL) ? *GetAppConfig() : _dud_config )
|
||||
{
|
||||
}
|
||||
|
||||
void AppLoadSettings()
|
||||
{
|
||||
wxConfigBase* conf = wxConfigBase::Get( false );
|
||||
if( NULL == conf ) return;
|
||||
if( !AllowFromMainThreadOnly() ) return;
|
||||
|
||||
IniLoader loader( *conf );
|
||||
AppIniLoader loader;
|
||||
g_Conf->LoadSave( loader );
|
||||
|
||||
if( HasMainFrame() )
|
||||
GetMainFrame().LoadRecentIsoList( *conf );
|
||||
wxGetApp().Source_SettingsLoadSave().Dispatch( loader );
|
||||
}
|
||||
|
||||
void AppSaveSettings()
|
||||
{
|
||||
wxConfigBase* conf = wxConfigBase::Get( false );
|
||||
if( NULL == conf ) return;
|
||||
if( !AllowFromMainThreadOnly() ) return;
|
||||
|
||||
IniSaver saver( *conf );
|
||||
AppIniSaver saver;
|
||||
g_Conf->LoadSave( saver );
|
||||
|
||||
if( HasMainFrame() )
|
||||
GetMainFrame().SaveRecentIsoList( *conf );
|
||||
wxGetApp().Source_SettingsLoadSave().Dispatch( saver );
|
||||
}
|
||||
|
||||
void Pcsx2App::OpenGsFrame()
|
||||
|
@ -413,28 +422,30 @@ void Pcsx2App::OnMainFrameClosed()
|
|||
|
||||
static int _sysexec_cdvdsrc_type = -1;
|
||||
|
||||
static void OnSysExecuteAfterPlugins( const wxCommandEvent& loadevt )
|
||||
static void _sendmsg_SysExecute()
|
||||
{
|
||||
if( !wxGetApp().m_CorePlugins ) return;
|
||||
|
||||
wxCommandEvent execevt( pxEVT_SysExecute );
|
||||
execevt.SetInt( _sysexec_cdvdsrc_type );
|
||||
wxGetApp().AddPendingEvent( execevt );
|
||||
}
|
||||
|
||||
static void OnSysExecuteAfterPlugins( const wxCommandEvent& loadevt )
|
||||
{
|
||||
if( (wxTheApp == NULL) || !((Pcsx2App*)wxTheApp)->m_CorePlugins ) return;
|
||||
_sendmsg_SysExecute();
|
||||
}
|
||||
|
||||
// Executes the emulator using a saved/existing virtual machine state and currently
|
||||
// configured CDVD source device.
|
||||
void Pcsx2App::SysExecute()
|
||||
{
|
||||
_sysexec_cdvdsrc_type = -1;
|
||||
if( !m_CorePlugins )
|
||||
{
|
||||
LoadPluginsPassive( OnSysExecuteAfterPlugins );
|
||||
return;
|
||||
}
|
||||
|
||||
wxCommandEvent evt( pxEVT_SysExecute );
|
||||
evt.SetInt( -1 );
|
||||
AddPendingEvent( evt );
|
||||
_sendmsg_SysExecute();
|
||||
}
|
||||
|
||||
// Executes the specified cdvd source and optional elf file. This command performs a
|
||||
|
@ -442,20 +453,18 @@ void Pcsx2App::SysExecute()
|
|||
// sources.
|
||||
void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc )
|
||||
{
|
||||
_sysexec_cdvdsrc_type = (int)cdvdsrc;
|
||||
if( !m_CorePlugins )
|
||||
{
|
||||
LoadPluginsPassive( OnSysExecuteAfterPlugins );
|
||||
return;
|
||||
}
|
||||
|
||||
wxCommandEvent evt( pxEVT_SysExecute );
|
||||
evt.SetInt( (int)cdvdsrc );
|
||||
AddPendingEvent( evt );
|
||||
_sendmsg_SysExecute();
|
||||
}
|
||||
|
||||
void Pcsx2App::OnSysExecute( wxCommandEvent& evt )
|
||||
{
|
||||
if( sys_resume_lock )
|
||||
if( sys_resume_lock > 0 )
|
||||
{
|
||||
Console.WriteLn( "SysExecute: State is locked, ignoring Execute request!" );
|
||||
return;
|
||||
|
@ -465,16 +474,18 @@ void Pcsx2App::OnSysExecute( wxCommandEvent& evt )
|
|||
// it, because apparently too much stuff is going on and the emulation states are wonky.
|
||||
if( !m_CorePlugins ) return;
|
||||
|
||||
if( evt.GetInt() != -1 ) SysReset(); else CoreThread.Suspend();
|
||||
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() );
|
||||
|
||||
CoreThread.Resume();
|
||||
}
|
||||
|
||||
// Full system reset stops the core thread and unloads all core plugins *completely*.
|
||||
void Pcsx2App::SysReset()
|
||||
{
|
||||
CoreThread.Reset();
|
||||
m_CorePlugins = NULL;
|
||||
}
|
||||
|
||||
// Returns true if there is a "valid" virtual machine state from the user's perspective. This
|
||||
|
|
|
@ -521,7 +521,7 @@ void ConsoleLogFrame::CountMessage()
|
|||
|
||||
wxCommandEvent evt( wxEVT_SemaphoreWait );
|
||||
GetEventHandler()->AddPendingEvent( evt );
|
||||
m_semaphore.Wait();
|
||||
m_semaphore.WaitRaw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -594,12 +594,12 @@ static void __concall ConsoleToFile_Newline()
|
|||
|
||||
static void __concall ConsoleToFile_DoWrite( const wxString& fmt )
|
||||
{
|
||||
_immediate_logger( toUTF8(fmt) );
|
||||
_immediate_logger( fmt.ToUTF8() );
|
||||
}
|
||||
|
||||
static void __concall ConsoleToFile_DoWriteLn( const wxString& fmt )
|
||||
{
|
||||
_immediate_logger( toUTF8(fmt) );
|
||||
_immediate_logger( fmt.ToUTF8() );
|
||||
ConsoleToFile_Newline();
|
||||
|
||||
if( emuLog != NULL )
|
||||
|
@ -702,7 +702,7 @@ static const IConsoleWriter ConsoleWriter_WindowAndFile =
|
|||
ConsoleToWindow_ClearColor,
|
||||
};
|
||||
|
||||
void Pcsx2App::EnableConsoleLogging() const
|
||||
void Pcsx2App::EnableAllLogging() const
|
||||
{
|
||||
if( emuLog )
|
||||
Console_SetActiveHandler( (m_ProgramLogBox!=NULL) ? (IConsoleWriter&)ConsoleWriter_WindowAndFile : (IConsoleWriter&)ConsoleWriter_File );
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
/* 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 "System.h"
|
||||
#include "App.h"
|
||||
|
||||
#include "ModalPopups.h"
|
||||
|
||||
using namespace wxHelpers;
|
||||
|
||||
bool ConfButtons::Allows( wxWindowID id ) const
|
||||
{
|
||||
switch( id )
|
||||
{
|
||||
case wxID_OK: return HasOK();
|
||||
case wxID_CANCEL: return HasCancel();
|
||||
case wxID_APPLY: return HasApply();
|
||||
case wxID_YES: return HasYes();
|
||||
case wxID_NO: return HasNo();
|
||||
case wxID_YESTOALL: return HasYes() && AllowsToAll();
|
||||
case wxID_NOTOALL: return HasNo() && AllowsToAll();
|
||||
|
||||
case wxID_ABORT: return HasAbort();
|
||||
case wxID_RETRY: return HasRetry();
|
||||
|
||||
// [TODO] : maybe add in an Ignore All?
|
||||
case wxID_IGNORE: return HasIgnore();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static wxString ResultToString( int result )
|
||||
{
|
||||
switch( result )
|
||||
{
|
||||
case wxID_OK: return L"ok";
|
||||
case wxID_CANCEL: return L"cancel";
|
||||
case wxID_APPLY: return L"apply";
|
||||
case wxID_YES: return L"yes";
|
||||
case wxID_NO: return L"no";
|
||||
case wxID_YESTOALL: return L"yestoall";
|
||||
case wxID_NOTOALL: return L"notoall";
|
||||
|
||||
case wxID_ABORT: return L"abort";
|
||||
case wxID_RETRY: return L"retry";
|
||||
|
||||
// [TODO] : maybe add in an Ignore All?
|
||||
case wxID_IGNORE: return L"ignore";
|
||||
}
|
||||
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
static wxWindowID ParseThatResult( const wxString& src, const ConfButtons& validTypes )
|
||||
{
|
||||
if( !pxAssert( !src.IsEmpty() ) ) return wxID_ANY;
|
||||
|
||||
static const wxWindowID retvals[] =
|
||||
{
|
||||
wxID_OK, wxID_CANCEL, wxID_APPLY,
|
||||
wxID_YES, wxID_NO,
|
||||
wxID_YESTOALL, wxID_NOTOALL,
|
||||
|
||||
wxID_ABORT, wxID_RETRY, wxID_IGNORE,
|
||||
};
|
||||
|
||||
for( int i=0; i<ArraySize( retvals ); ++i )
|
||||
{
|
||||
if( (validTypes.Allows( retvals[i] )) && (src == ResultToString(retvals[i])) )
|
||||
return retvals[i];
|
||||
}
|
||||
|
||||
return wxID_ANY;
|
||||
}
|
||||
|
||||
wxWindowID Dialogs::IssueConfirmation( wxWindow* parent, const wxString& disablerKey, const ConfButtons& type, const wxString& title, const wxString& msg )
|
||||
{
|
||||
wxConfigBase* cfg = GetAppConfig();
|
||||
|
||||
if( cfg != NULL )
|
||||
{
|
||||
cfg->SetPath( L"/PopupDisablers" );
|
||||
bool recdef = cfg->IsRecordingDefaults();
|
||||
cfg->SetRecordDefaults( false );
|
||||
|
||||
wxString result = cfg->Read( disablerKey, L"enabled" );
|
||||
|
||||
cfg->SetRecordDefaults( recdef );
|
||||
cfg->SetPath( L"/" );
|
||||
|
||||
result.LowerCase();
|
||||
wxArrayString split;
|
||||
SplitString( split, result, L"," );
|
||||
|
||||
// if only one param (no comma!?) then assume the entry is invalid and force a
|
||||
// re-display of the dialog.
|
||||
|
||||
if( split.Count() > 1 )
|
||||
{
|
||||
result = split[0];
|
||||
if( result == L"disabled" || result == L"off" || result == L"no" )
|
||||
{
|
||||
int result = ParseThatResult( split[1], type );
|
||||
if( result != wxID_ANY ) return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dialogs::ExtensibleConfirmation confirmDlg( parent, type, title, msg );
|
||||
|
||||
if( cfg == NULL ) return confirmDlg.ShowModal();
|
||||
|
||||
// Add an option that allows the user to disable this popup from showing again.
|
||||
// (and if the config hasn't been initialized yet, then assume the dialog as non-disablable)
|
||||
|
||||
wxBoxSizer& cboxPad = *new wxBoxSizer( wxHORIZONTAL );
|
||||
wxCheckBox& DisablerCtrl(
|
||||
AddCheckBoxTo( &confirmDlg, cboxPad, _("Do not show this dialog again."))
|
||||
);
|
||||
|
||||
confirmDlg.GetExtensibleSizer().Add( &cboxPad, wxSizerFlags().Centre() );
|
||||
|
||||
if( type != ConfButtons().OK() )
|
||||
DisablerCtrl.SetToolTip(_("Disables this popup and whatever response you select here will be automatically used from now on."));
|
||||
else
|
||||
DisablerCtrl.SetToolTip(_("The popup will not be shown again. This setting can be undone from the settings panels."));
|
||||
|
||||
confirmDlg.Fit();
|
||||
|
||||
int modalResult = confirmDlg.ShowModal();
|
||||
|
||||
wxString cfgResult = ResultToString( modalResult );
|
||||
if( DisablerCtrl.IsChecked() && !cfgResult.IsEmpty() )
|
||||
{
|
||||
cfg->SetPath( L"/PopupDisablers" );
|
||||
cfg->Write( disablerKey, L"disabled," + cfgResult );
|
||||
cfg->SetPath( L"/" );
|
||||
}
|
||||
return modalResult;
|
||||
}
|
||||
|
||||
Dialogs::ExtensibleConfirmation::ExtensibleConfirmation( wxWindow* parent, const ConfButtons& type, const wxString& title, const wxString& msg ) :
|
||||
wxDialogWithHelpers( parent, wxID_ANY, title, false )
|
||||
, m_ExtensibleSizer( *new wxBoxSizer( wxVERTICAL ) )
|
||||
, m_ButtonSizer( *new wxBoxSizer( wxHORIZONTAL ) )
|
||||
{
|
||||
wxBoxSizer& mainsizer( *new wxBoxSizer(wxVERTICAL) );
|
||||
|
||||
// Add the message padded some (StdCenter gives us a 5 pt padding). Helps emphasize it a bit.
|
||||
wxBoxSizer& msgPadSizer( *new wxBoxSizer(wxVERTICAL) );
|
||||
AddStaticText( msgPadSizer, msg, wxALIGN_CENTRE, 444 );
|
||||
mainsizer.Add( &msgPadSizer, SizerFlags::StdCenter() );
|
||||
|
||||
mainsizer.Add( &m_ExtensibleSizer, wxSizerFlags().Centre() );
|
||||
|
||||
// Populate the Button Sizer.
|
||||
// We prefer this over the built-in wxWidgets ButtonSizer stuff used for other types of
|
||||
// dialogs because we offer more button types, and we don't want the MSW default behavior
|
||||
// of right-justified buttons.
|
||||
|
||||
#ifdef __WXGTK__
|
||||
// GTK+ / Linux inverts OK/CANCEL order -- cancel / no first, OK / Yes later. >_<
|
||||
if( type.HasCancel() )
|
||||
AddActionButton( wxID_CANCEL );
|
||||
|
||||
if( type.HasNo() )
|
||||
{
|
||||
AddActionButton( wxID_NO );
|
||||
if( type.AllowsToAll() ) AddActionButton( wxID_NOTOALL );
|
||||
}
|
||||
if( type.HasOK() || type.HasYes() ) // Extra space between Affirm and Cancel Actions
|
||||
m_ButtonSizer.Add(0, 0, 1, wxEXPAND, 0);
|
||||
#endif
|
||||
|
||||
if( type.HasOK() )
|
||||
AddActionButton( wxID_OK );
|
||||
|
||||
if( type.HasYes() )
|
||||
{
|
||||
AddActionButton( wxID_YES );
|
||||
if( type.AllowsToAll() )
|
||||
AddActionButton( wxID_YESTOALL );
|
||||
}
|
||||
|
||||
#ifndef __WXGTK__
|
||||
if( type.HasNo() || type.HasCancel() ) // Extra space between Affirm and Cancel Actions
|
||||
m_ButtonSizer.Add(0, 0, 1, wxEXPAND, 0);
|
||||
|
||||
if( type.HasNo() )
|
||||
{
|
||||
AddActionButton( wxID_NO );
|
||||
if( type.AllowsToAll() )
|
||||
AddActionButton( wxID_NOTOALL );
|
||||
}
|
||||
|
||||
if( type.HasCancel() )
|
||||
AddActionButton( wxID_CANCEL );
|
||||
#endif
|
||||
|
||||
mainsizer.Add( &m_ButtonSizer, SizerFlags::StdCenter() );
|
||||
|
||||
SetSizerAndFit( &mainsizer, true );
|
||||
CenterOnScreen();
|
||||
}
|
||||
|
||||
void Dialogs::ExtensibleConfirmation::AddActionButton( wxWindowID id )
|
||||
{
|
||||
m_ButtonSizer.Add( new wxButton( this, id ), SizerFlags::StdButton() )->SetProportion( 6 );
|
||||
}
|
||||
|
|
@ -64,6 +64,60 @@ protected:
|
|||
virtual void OnDoubleClicked( wxCommandEvent& evt );
|
||||
};
|
||||
|
||||
class ConfButtons
|
||||
{
|
||||
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;
|
||||
BITFIELD_END
|
||||
|
||||
public:
|
||||
ConfButtons() : bitset( 0 ) { }
|
||||
|
||||
ConfButtons& OK() { m_OK = true; return *this; }
|
||||
ConfButtons& Cancel() { m_Cancel = true; return *this; }
|
||||
ConfButtons& Apply() { m_Apply = true; return *this; }
|
||||
ConfButtons& Yes() { m_Yes = true; return *this; }
|
||||
ConfButtons& No() { m_No = true; return *this; }
|
||||
ConfButtons& ToAll() { m_AllowToAll = true; return *this; }
|
||||
|
||||
ConfButtons& Abort() { m_Abort = true; return *this; }
|
||||
ConfButtons& Retry() { m_Retry = true; return *this; }
|
||||
ConfButtons& Ignore() { m_Ignore = 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 Allows( wxWindowID id ) const;
|
||||
|
||||
bool operator ==( const ConfButtons& right ) const
|
||||
{
|
||||
return OpEqu( bitset );
|
||||
}
|
||||
|
||||
bool operator !=( const ConfButtons& right ) const
|
||||
{
|
||||
return !OpEqu( bitset );
|
||||
}
|
||||
};
|
||||
|
||||
namespace Dialogs
|
||||
{
|
||||
class AboutBoxDialog: public wxDialogWithHelpers
|
||||
|
@ -77,6 +131,7 @@ namespace Dialogs
|
|||
wxStaticBitmap m_bitmap_dualshock;
|
||||
};
|
||||
|
||||
|
||||
class PickUserModeDialog : public wxDialogWithHelpers
|
||||
{
|
||||
protected:
|
||||
|
@ -103,5 +158,22 @@ namespace Dialogs
|
|||
void OnOverwrite_Click( wxCommandEvent& evt );
|
||||
};
|
||||
|
||||
|
||||
class ExtensibleConfirmation : public wxDialogWithHelpers
|
||||
{
|
||||
protected:
|
||||
wxBoxSizer& m_ExtensibleSizer;
|
||||
wxBoxSizer& m_ButtonSizer;
|
||||
public:
|
||||
ExtensibleConfirmation( wxWindow* parent, const ConfButtons& type, const wxString& title, const wxString& msg );
|
||||
virtual ~ExtensibleConfirmation() throw() {}
|
||||
|
||||
wxBoxSizer& GetExtensibleSizer() const { return m_ExtensibleSizer; }
|
||||
|
||||
protected:
|
||||
void AddActionButton( wxWindowID id );
|
||||
};
|
||||
|
||||
wxWindowID IssueConfirmation( wxWindow* parent, const wxString& disablerKey, const ConfButtons& type, const wxString& title, const wxString& msg );
|
||||
}
|
||||
|
||||
|
|
|
@ -89,19 +89,14 @@ void MainEmuFrame::UpdateIsoSrcFile()
|
|||
GetMenuBar()->SetLabel( MenuId_Src_Iso, label );
|
||||
}
|
||||
|
||||
void MainEmuFrame::LoadRecentIsoList( wxConfigBase& conf )
|
||||
void MainEmuFrame::LoadSaveRecentIsoList( IniInterface& conf )
|
||||
{
|
||||
if( m_RecentIsoList )
|
||||
m_RecentIsoList->Load( conf );
|
||||
if( conf.IsLoading() )
|
||||
m_RecentIsoList->Load( conf.GetConfig() );
|
||||
else
|
||||
m_RecentIsoList->Save( conf.GetConfig() );
|
||||
}
|
||||
|
||||
void MainEmuFrame::SaveRecentIsoList( wxConfigBase& conf )
|
||||
{
|
||||
if( m_RecentIsoList )
|
||||
m_RecentIsoList->Load( conf );
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Video / Audio / Pad "Extensible" Menus
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -260,13 +255,36 @@ void MainEmuFrame::InitLogBoxPosition( AppConfig::ConsoleLogOptions& conf )
|
|||
}
|
||||
}
|
||||
|
||||
static void OnCoreThreadStatusChanged( void* obj, const wxCommandEvent& evt )
|
||||
void MainEmuFrame::OnCoreThreadStatusChanged( void* obj, const wxCommandEvent& evt )
|
||||
{
|
||||
if( obj == NULL ) return;
|
||||
MainEmuFrame* mframe = (MainEmuFrame*)obj;
|
||||
mframe->ApplyCoreStatus();
|
||||
}
|
||||
|
||||
void MainEmuFrame::OnCorePluginStatusChanged( void* obj, const wxCommandEvent& evt )
|
||||
{
|
||||
if( obj == NULL ) return;
|
||||
MainEmuFrame* mframe = (MainEmuFrame*)obj;
|
||||
mframe->ApplyCoreStatus();
|
||||
}
|
||||
|
||||
void MainEmuFrame::OnSettingsApplied( void* obj, const int& evt )
|
||||
{
|
||||
if( obj == NULL ) return;
|
||||
MainEmuFrame* mframe = (MainEmuFrame*)obj;
|
||||
mframe->ApplySettings();
|
||||
}
|
||||
|
||||
void MainEmuFrame::OnSettingsLoadSave( void* obj, const IniInterface& evt )
|
||||
{
|
||||
if( obj == NULL ) return;
|
||||
MainEmuFrame* mframe = (MainEmuFrame*)obj;
|
||||
|
||||
// FIXME: Evil const cast hack!
|
||||
mframe->LoadSaveRecentIsoList( const_cast<IniInterface&>(evt) );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title):
|
||||
wxFrame(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER) ),
|
||||
|
@ -295,7 +313,10 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title):
|
|||
|
||||
m_MenuItem_Console( *new wxMenuItem( &m_menuMisc, MenuId_Console, L"Show Console", wxEmptyString, wxITEM_CHECK ) ),
|
||||
|
||||
m_Listener_CoreThreadStatus( wxGetApp().Source_CoreThreadStatus(), CmdEvt_Listener( this, OnCoreThreadStatusChanged ) )
|
||||
m_Listener_CoreThreadStatus( wxGetApp().Source_CoreThreadStatus(), CmdEvt_Listener( this, OnCoreThreadStatusChanged ) ),
|
||||
m_Listener_CorePluginStatus( wxGetApp().Source_CorePluginStatus(), CmdEvt_Listener( this, OnCorePluginStatusChanged ) ),
|
||||
m_Listener_SettingsApplied( wxGetApp().Source_SettingsApplied(), EventListener<int>( this, OnSettingsApplied ) ),
|
||||
m_Listener_SettingsLoadSave( wxGetApp().Source_SettingsLoadSave(), EventListener<IniInterface>( this, OnSettingsLoadSave ) )
|
||||
{
|
||||
// ------------------------------------------------------------------------
|
||||
// Initial menubar setup. This needs to be done first so that the menu bar's visible size
|
||||
|
@ -471,8 +492,8 @@ MainEmuFrame::~MainEmuFrame() throw()
|
|||
{
|
||||
try
|
||||
{
|
||||
if( m_RecentIsoList )
|
||||
m_RecentIsoList->Save( *wxConfigBase::Get( false ) );
|
||||
if( m_RecentIsoList && GetAppConfig() )
|
||||
m_RecentIsoList->Save( *GetAppConfig() );
|
||||
}
|
||||
DESTRUCTOR_CATCHALL
|
||||
}
|
||||
|
@ -485,7 +506,7 @@ void MainEmuFrame::ReloadRecentLists()
|
|||
// the recent file count has been changed, and it's a helluva lot easier than trying
|
||||
// to make a clone copy of this complex object. ;)
|
||||
|
||||
wxConfigBase* cfg = wxConfigBase::Get( false );
|
||||
wxConfigBase* cfg = GetAppConfig();
|
||||
pxAssert( cfg != NULL );
|
||||
|
||||
if( m_RecentIsoList )
|
||||
|
@ -495,6 +516,14 @@ void MainEmuFrame::ReloadRecentLists()
|
|||
cfg->Flush();
|
||||
}
|
||||
|
||||
void MainEmuFrame::ApplyCoreStatus()
|
||||
{
|
||||
GetMenuBar()->Enable( MenuId_Sys_SuspendResume, SysHasValidState() );
|
||||
GetMenuBar()->Enable( MenuId_Sys_Reset, SysHasValidState() || (g_plugins!=NULL) );
|
||||
|
||||
GetMenuBar()->SetLabel( MenuId_Sys_SuspendResume, CoreThread.IsOpen() ? _("Suspend") : _("Resume") );
|
||||
}
|
||||
|
||||
void MainEmuFrame::ApplySettings()
|
||||
{
|
||||
GetMenuBar()->Check( MenuId_SkipBiosToggle, g_Conf->EmuOptions.SkipBiosSplash );
|
||||
|
@ -502,10 +531,6 @@ void MainEmuFrame::ApplySettings()
|
|||
GetMenuBar()->Check( MenuId_Config_Multitap0Toggle, g_Conf->EmuOptions.MultitapPort0_Enabled );
|
||||
GetMenuBar()->Check( MenuId_Config_Multitap1Toggle, g_Conf->EmuOptions.MultitapPort1_Enabled );
|
||||
|
||||
GetMenuBar()->Enable( MenuId_Sys_SuspendResume, SysHasValidState() );
|
||||
|
||||
GetMenuBar()->SetLabel( MenuId_Sys_SuspendResume, CoreThread.IsExecMode_Running() ? _("Suspend") :_("Resume") );
|
||||
|
||||
if( m_RecentIsoList )
|
||||
{
|
||||
if( m_RecentIsoList->GetMaxFiles() != g_Conf->RecentFileCount )
|
||||
|
|
|
@ -65,6 +65,9 @@ protected:
|
|||
wxMenuItem& m_MenuItem_Console;
|
||||
|
||||
CmdEvt_ListenerBinding m_Listener_CoreThreadStatus;
|
||||
CmdEvt_ListenerBinding m_Listener_CorePluginStatus;
|
||||
EventListenerBinding<int> m_Listener_SettingsApplied;
|
||||
EventListenerBinding<IniInterface> m_Listener_SettingsLoadSave;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// MainEmuFrame Constructors and Member Methods
|
||||
|
@ -79,13 +82,18 @@ public:
|
|||
bool IsPaused() const { return GetMenuBar()->IsChecked( MenuId_Sys_SuspendResume ); }
|
||||
void UpdateIsoSrcFile();
|
||||
void UpdateIsoSrcSelection();
|
||||
void ApplySettings();
|
||||
void ReloadRecentLists();
|
||||
|
||||
void LoadRecentIsoList( wxConfigBase& conf );
|
||||
void SaveRecentIsoList( wxConfigBase& conf );
|
||||
|
||||
protected:
|
||||
static void OnCoreThreadStatusChanged( void* obj, const wxCommandEvent& evt );
|
||||
static void OnCorePluginStatusChanged( void* obj, const wxCommandEvent& evt );
|
||||
static void OnSettingsApplied( void* obj, const int& evt );
|
||||
static void MainEmuFrame::OnSettingsLoadSave( void* obj, const IniInterface& evt );
|
||||
|
||||
void LoadSaveRecentIsoList( IniInterface& conf );
|
||||
void ApplySettings();
|
||||
void ApplyCoreStatus();
|
||||
|
||||
void InitLogBoxPosition( AppConfig::ConsoleLogOptions& conf );
|
||||
|
||||
void OnCloseWindow(wxCloseEvent& evt);
|
||||
|
|
|
@ -27,18 +27,12 @@ using namespace Dialogs;
|
|||
|
||||
void MainEmuFrame::Menu_ConfigSettings_Click(wxCommandEvent &event)
|
||||
{
|
||||
if( Dialogs::ConfigurationDialog( this ).ShowModal() )
|
||||
{
|
||||
AppSaveSettings();
|
||||
}
|
||||
Dialogs::ConfigurationDialog( this ).ShowModal();
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_SelectBios_Click(wxCommandEvent &event)
|
||||
{
|
||||
if( Dialogs::BiosSelectorDialog( this ).ShowModal() )
|
||||
{
|
||||
AppSaveSettings();
|
||||
}
|
||||
Dialogs::BiosSelectorDialog( this ).ShowModal();
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_CdvdSource_Click( wxCommandEvent &event )
|
||||
|
@ -150,10 +144,11 @@ void MainEmuFrame::Menu_SkipBiosToggle_Click( wxCommandEvent &event )
|
|||
{
|
||||
g_Conf->EmuOptions.SkipBiosSplash = GetMenuBar()->IsChecked( MenuId_SkipBiosToggle );
|
||||
|
||||
wxConfigBase* conf = wxConfigBase::Get( false );
|
||||
if( NULL == conf ) return;
|
||||
if( wxConfigBase* conf = GetAppConfig() )
|
||||
{
|
||||
IniSaver saver( *conf );
|
||||
g_Conf->EmuOptions.LoadSave( saver );
|
||||
}
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_OpenELF_Click(wxCommandEvent &event)
|
||||
|
@ -191,8 +186,17 @@ void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event)
|
|||
{
|
||||
if( !SysHasValidState() ) return;
|
||||
|
||||
if( !CoreThread.Suspend() )
|
||||
CoreThread.Resume();
|
||||
// Note: We manually update the menu here, even though it'll be updated again
|
||||
// when the thread "officially" suspends or resumes (via listener callback), because
|
||||
// the callback is tied to the actual thread status
|
||||
|
||||
if( CoreThread.Suspend() )
|
||||
GetMenuBar()->SetLabel( MenuId_Sys_SuspendResume, _("Resume") );
|
||||
else
|
||||
{
|
||||
sApp.SysExecute();
|
||||
GetMenuBar()->SetLabel( MenuId_Sys_SuspendResume, _("Suspend") );
|
||||
}
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_SysReset_Click(wxCommandEvent &event)
|
||||
|
@ -204,6 +208,8 @@ void MainEmuFrame::Menu_SysReset_Click(wxCommandEvent &event)
|
|||
|
||||
if( resume )
|
||||
sApp.SysExecute();
|
||||
|
||||
GetMenuBar()->Enable( MenuId_Sys_Reset, resume );
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event)
|
||||
|
@ -229,9 +235,10 @@ void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event)
|
|||
LoadPluginsImmediate();
|
||||
if( g_plugins == NULL ) return;
|
||||
|
||||
bool resume = CoreThread.Suspend();
|
||||
wxWindowDisabler disabler;
|
||||
//ScopedWindowDisable disabler( this );
|
||||
g_plugins->Configure( pid );
|
||||
if( resume ) CoreThread.Resume();
|
||||
}
|
||||
|
||||
void MainEmuFrame::Menu_Debug_Open_Click(wxCommandEvent &event)
|
||||
|
|
|
@ -51,22 +51,27 @@ namespace Exception
|
|||
//
|
||||
class CannotApplySettings : public BaseException
|
||||
{
|
||||
public:
|
||||
bool IsVerbose;
|
||||
|
||||
protected:
|
||||
Panels::BaseApplicableConfigPanel* m_Panel;
|
||||
|
||||
public:
|
||||
DEFINE_EXCEPTION_COPYTORS( CannotApplySettings )
|
||||
|
||||
explicit CannotApplySettings( Panels::BaseApplicableConfigPanel* thispanel, const char* msg=wxLt("Cannot apply new settings, one of the settings is invalid.") )
|
||||
explicit CannotApplySettings( Panels::BaseApplicableConfigPanel* thispanel, const char* msg=wxLt("Cannot apply new settings, one of the settings is invalid."), bool isVerbose = true )
|
||||
{
|
||||
BaseException::InitBaseEx( msg );
|
||||
m_Panel = thispanel;
|
||||
IsVerbose = isVerbose;
|
||||
}
|
||||
|
||||
explicit CannotApplySettings( Panels::BaseApplicableConfigPanel* thispanel, const wxString& msg_eng, const wxString& msg_xlt )
|
||||
{
|
||||
BaseException::InitBaseEx( msg_eng, msg_xlt );
|
||||
m_Panel = thispanel;
|
||||
IsVerbose = true;
|
||||
}
|
||||
|
||||
Panels::BaseApplicableConfigPanel* GetPanel()
|
||||
|
|
|
@ -85,8 +85,7 @@ bool Panels::StaticApplyState::ApplyPage( int pageid, bool saveOnSuccess )
|
|||
|
||||
// Note: apply first, then save -- in case the apply fails.
|
||||
|
||||
AppApplySettings( &confcopy );
|
||||
if( saveOnSuccess ) AppSaveSettings();
|
||||
AppApplySettings( &confcopy, saveOnSuccess );
|
||||
}
|
||||
catch( Exception::CannotApplySettings& ex )
|
||||
{
|
||||
|
@ -95,10 +94,13 @@ bool Panels::StaticApplyState::ApplyPage( int pageid, bool saveOnSuccess )
|
|||
UseDefaultSettingsFolder = oldUseDefSet;
|
||||
*g_Conf = confcopy;
|
||||
|
||||
if( ex.IsVerbose )
|
||||
{
|
||||
wxMessageBox( ex.FormatDisplayMessage(), _("Cannot apply settings...") );
|
||||
|
||||
if( ex.GetPanel() != NULL )
|
||||
ex.GetPanel()->SetFocusToMe();
|
||||
}
|
||||
|
||||
retval = false;
|
||||
}
|
||||
|
@ -170,7 +172,7 @@ Panels::LanguageSelectionPanel::LanguageSelectionPanel( wxWindow& parent, int id
|
|||
|
||||
int size = m_langs.size();
|
||||
int cursel = 0;
|
||||
wxString* compiled = new wxString[size];
|
||||
ScopedArray<wxString> compiled( size ); //, L"Compiled Language Names" );
|
||||
wxString configLangName( wxLocale::GetLanguageName( wxLANGUAGE_DEFAULT ) );
|
||||
|
||||
for( int i=0; i<size; ++i )
|
||||
|
@ -181,7 +183,7 @@ Panels::LanguageSelectionPanel::LanguageSelectionPanel( wxWindow& parent, int id
|
|||
}
|
||||
|
||||
m_picker = new wxComboBox( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
|
||||
size, compiled, wxCB_READONLY | wxCB_SORT );
|
||||
size, compiled.GetPtr(), wxCB_READONLY | wxCB_SORT );
|
||||
m_picker->SetSelection( cursel );
|
||||
|
||||
wxBoxSizer& s_lang = *new wxBoxSizer( wxHORIZONTAL );
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "Plugins.h"
|
||||
#include "Utilities/ScopedPtr.h"
|
||||
#include "ConfigurationPanels.h"
|
||||
#include "Dialogs/ModalPopups.h"
|
||||
|
||||
#include <wx/dynlib.h>
|
||||
#include <wx/dir.h>
|
||||
|
@ -276,6 +277,8 @@ void Panels::PluginSelectorPanel::Apply()
|
|||
// user never entered plugins panel? Skip application since combo boxes are invalid/uninitialized.
|
||||
if( !m_FileList ) return;
|
||||
|
||||
AppConfig curconf( *g_Conf );
|
||||
|
||||
for( int i=0; i<NumPluginTypes; ++i )
|
||||
{
|
||||
int sel = m_ComponentBoxes.Get(i).GetSelection();
|
||||
|
@ -304,7 +307,7 @@ void Panels::PluginSelectorPanel::Apply()
|
|||
// the whole plugin system needs to be re-loaded.
|
||||
|
||||
const PluginInfo* pi = tbl_PluginInfo; do {
|
||||
if( g_Conf->FullpathTo( pi->id ) != g_Conf->FullpathTo( pi->id ) )
|
||||
if( g_Conf->FullpathTo( pi->id ) != curconf.FullpathTo( pi->id ) )
|
||||
break;
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
|
@ -313,6 +316,21 @@ void Panels::PluginSelectorPanel::Apply()
|
|||
if( CoreThread.IsRunning() )
|
||||
{
|
||||
// [TODO] : Post notice that this shuts down existing emulation, and may not safely recover.
|
||||
int result = Dialogs::IssueConfirmation( this, L"PluginSelector:ConfirmShutdown", ConfButtons().OK().Cancel(),
|
||||
|
||||
_("Shutdown PS2 virtual machine?"),
|
||||
|
||||
pxE( ".Popup:PluginSelector:ConfirmShutdown",
|
||||
L"Warning! Changing plugins requires a complete shutdown and reset of the PS2 virtual machine. "
|
||||
L"PCSX2 will attempt to save and restore the state, but if the newly selected plugins are "
|
||||
L"incompatible the recovery may fail, and current progress will be lost."
|
||||
L"\n\n"
|
||||
L"Are you sure you want to apply settings now?"
|
||||
)
|
||||
);
|
||||
|
||||
if( result == wxID_CANCEL )
|
||||
throw Exception::CannotApplySettings( this, "Cannot apply settings: canceled by user because plugins changed while the emulation state was active.", false );
|
||||
}
|
||||
|
||||
sApp.SysReset();
|
||||
|
@ -358,12 +376,17 @@ void Panels::PluginSelectorPanel::DoRefresh()
|
|||
return;
|
||||
}
|
||||
|
||||
// Disable all controls until enumeration is complete.
|
||||
// Disable all controls until enumeration is complete
|
||||
m_ComponentBoxes.Hide();
|
||||
|
||||
// (including next button if it's a Wizard)
|
||||
wxWindow* forwardButton = GetGrandParent()->FindWindow( wxID_FORWARD );
|
||||
if( forwardButton != NULL )
|
||||
forwardButton->Disable();
|
||||
|
||||
// Show status bar for plugin enumeration. Use a pending event so that
|
||||
// the window's size can get initialized properly before trying to custom-
|
||||
// fit the status panel to it.
|
||||
|
||||
m_ComponentBoxes.Hide();
|
||||
wxCommandEvent evt( pxEVT_ShowStatusBar );
|
||||
GetEventHandler()->AddPendingEvent( evt );
|
||||
|
||||
|
@ -415,8 +438,10 @@ void Panels::PluginSelectorPanel::OnConfigure_Clicked( wxCommandEvent& evt )
|
|||
wxDynamicLibrary dynlib( (*m_FileList)[(int)m_ComponentBoxes.Get(pid).GetClientData(sel)] );
|
||||
if( PluginConfigureFnptr configfunc = (PluginConfigureFnptr)dynlib.GetSymbol( tbl_PluginInfo[pid].GetShortname() + L"configure" ) )
|
||||
{
|
||||
bool resume = CoreThread.Suspend();
|
||||
wxWindowDisabler disabler;
|
||||
configfunc();
|
||||
if( resume ) CoreThread.Resume();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,6 +472,10 @@ void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt )
|
|||
m_ComponentBoxes.Show();
|
||||
m_StatusPanel.Hide();
|
||||
m_StatusPanel.Reset();
|
||||
|
||||
wxWindow* forwardButton = GetGrandParent()->FindWindow( wxID_FORWARD );
|
||||
if( forwardButton != NULL )
|
||||
forwardButton->Enable();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1816,10 +1816,6 @@
|
|||
RelativePath="..\..\gui\IniInterface.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\common\include\Utilities\Listeners.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\MainFrame.cpp"
|
||||
>
|
||||
|
@ -1871,6 +1867,10 @@
|
|||
RelativePath="..\..\gui\Dialogs\ConfigurationDialog.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\Dialogs\ConfirmationDialogs.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\Dialogs\FirstTimeWizard.cpp"
|
||||
>
|
||||
|
|
|
@ -123,7 +123,6 @@ EndProject
|
|||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wx", "wx", "{62BF822E-6A12-49A8-BE8C-C55A9BCA24DA}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
common\include\wx\folderdesc.txt = common\include\wx\folderdesc.txt
|
||||
common\include\wx\scopedptr.h = common\include\wx\scopedptr.h
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bin2cpp", "tools\bin2cpp\bin2c.vcproj", "{677B7D11-D5E1-40B3-88B1-9A4DF83D2213}"
|
||||
|
|
Loading…
Reference in New Issue