* Fixed thread classing so that detached pthreeads are handled safely, and so that virtual functions in C++ destructors are explicitly specified.
 * Added some helper functions to Pcsx2App for safely executing menu commands form any thread in the emu.
 * Fixed several minor bugs in settings / config handling.

git-svn-id: http://pcsx2.googlecode.com/svn/branches/wxgui@1730 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-09-03 11:59:05 +00:00
parent ee97290cd2
commit 64e43a9086
19 changed files with 308 additions and 197 deletions

View File

@ -109,8 +109,8 @@ namespace Threading
// derived class if your thread utilizes the post). // derived class if your thread utilizes the post).
// //
// Notes: // Notes:
// * Constructing threads as static vars isn't recommended since it can potentially con- // * Constructing threads as static global vars isn't recommended since it can potentially
// fuse w32pthreads, if the static initializers are executed out-of-order (C++ offers // confuse w32pthreads, if the static initializers are executed out-of-order (C++ offers
// no dependency options for ensuring correct static var initializations). Use heap // no dependency options for ensuring correct static var initializations). Use heap
// allocation to create thread objects instead. // allocation to create thread objects instead.
// //
@ -121,10 +121,11 @@ namespace Threading
protected: protected:
typedef int (*PlainJoeFP)(); typedef int (*PlainJoeFP)();
pthread_t m_thread; pthread_t m_thread;
Semaphore m_sem_event; // general wait event that's needed by most threads.
sptr m_returncode; // value returned from the thread on close. sptr m_returncode; // value returned from the thread on close.
bool m_running; volatile long m_detached; // a boolean value which indicates if the m_thread handle is valid
Semaphore m_sem_event; // general wait event that's needed by most threads. volatile long m_running; // set true by Start(), and set false by Cancel(), Block(), etc.
public: public:
virtual ~PersistentThread(); virtual ~PersistentThread();
@ -132,6 +133,7 @@ namespace Threading
virtual void Start(); virtual void Start();
virtual void Cancel( bool isBlocking = true ); virtual void Cancel( bool isBlocking = true );
virtual void Detach();
// Gets the return code of the thread. // Gets the return code of the thread.
// Throws std::logic_error if the thread has not terminated. // Throws std::logic_error if the thread has not terminated.
@ -142,6 +144,8 @@ namespace Threading
bool IsSelf() const; bool IsSelf() const;
virtual void DoThreadCleanup();
protected: protected:
// Used to dispatch the thread callback function. // Used to dispatch the thread callback function.
// (handles some thread cleanup on Win32, and is basically a typecast // (handles some thread cleanup on Win32, and is basically a typecast
@ -150,14 +154,6 @@ namespace Threading
// Implemented by derived class to handle threading actions! // Implemented by derived class to handle threading actions!
virtual sptr ExecuteTask()=0; virtual sptr ExecuteTask()=0;
// ----------------------------------------------------------------------------
// Static Methods (PersistentThread)
// ----------------------------------------------------------------------------
public:
// performs a test on the given thread handle, returning true if the thread exists
// or false if the thread is dead/done/never existed.
static bool Exists( pthread_t pid );
}; };
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////

View File

@ -29,22 +29,40 @@ using namespace Threading;
namespace Threading namespace Threading
{ {
static void _pt_callback_cleanup( void* handle )
{
((PersistentThread*)handle)->DoThreadCleanup();
}
PersistentThread::PersistentThread() : PersistentThread::PersistentThread() :
m_thread() m_thread()
, m_returncode( 0 )
, m_running( false )
, m_sem_event() , m_sem_event()
, m_returncode( 0 )
, m_detached( false )
, m_running( false )
{ {
} }
// Perform a blocking termination of the thread, to ensure full object cleanup. // This destructor performs basic "last chance" cleanup, which is a blocking
// join against non-detached threads. Detached threads are unhandled.
// Extending classes should always implement their own thread closure process.
// 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() PersistentThread::~PersistentThread()
{ {
Cancel( false ); wxASSERT( !IsSelf() ); // not allowed from our own thread.
if( !_InterlockedExchange( &m_detached, true ) )
{
pthread_join( m_thread, (void**)&m_returncode );
m_running = false;
}
} }
// This function should not be called from the owner thread.
void PersistentThread::Start() void PersistentThread::Start()
{ {
wxASSERT( !IsSelf() ); // not allowed from our own thread.
if( m_running ) return; if( m_running ) return;
if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 ) if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 )
@ -53,26 +71,43 @@ namespace Threading
m_running = true; m_running = true;
} }
// This function should not be called from the owner thread.
void PersistentThread::Detach()
{
wxASSERT( !IsSelf() ); // not allowed from our own thread.
if( _InterlockedExchange( &m_detached, true ) ) return;
pthread_detach( m_thread );
}
// Remarks: // Remarks:
// Provision of non-blocking Cancel() is probably academic, since destroying a PersistentThread // 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() // object performs a blocking Cancel regardless of if you explicitly do a non-blocking Cancel()
// prior, since the ExecuteTask() method requires a valid object state. If you really need // prior, since the ExecuteTask() method requires a valid object state. If you really need
// fire-and-forget behavior on threads, use pthreads directly for now. // fire-and-forget behavior on threads, use pthreads directly for now.
// (TODO : make a DetachedThread class?) //
// This function should not be called from the owner thread.
// //
// Parameters: // Parameters:
// isBlocking - indicates if the Cancel action should block for thread completion or not. // isBlocking - indicates if the Cancel action should block for thread completion or not.
// //
void PersistentThread::Cancel( bool isBlocking ) void PersistentThread::Cancel( bool isBlocking )
{ {
if( !m_running ) return; wxASSERT( !IsSelf() );
m_running = false; if( _InterlockedExchange( &m_detached, true ) )
{
if( m_running )
Console::Notice( "Threading Warning: Attempted to cancel detached thread; Ignoring..." );
return;
}
pthread_cancel( m_thread ); pthread_cancel( m_thread );
if( isBlocking ) if( isBlocking )
pthread_join( m_thread, (void**)&m_returncode ); pthread_join( m_thread, (void**)&m_returncode );
else else
pthread_detach( m_thread ); pthread_detach( m_thread );
m_running = false;
} }
// Blocks execution of the calling thread until this thread completes its task. The // Blocks execution of the calling thread until this thread completes its task. The
@ -85,11 +120,21 @@ namespace Threading
// //
sptr PersistentThread::Block() sptr PersistentThread::Block()
{ {
bool isOwner = (pthread_self() == m_thread); DevAssert( !IsSelf(), "Thread deadlock detected; Block() should never be called by the owner thread." );
DevAssert( !isOwner, "Thread deadlock detected; Block() should never be called by the owner thread." );
pthread_join( m_thread, (void**)&m_returncode ); if( _InterlockedExchange( &m_detached, true ) )
return m_returncode; {
// already detached: if we're still running then its an invalid operation
if( m_running )
throw Exception::InvalidOperation( "Blocking on detached threads requires manual semaphore implementation." );
return m_returncode;
}
else
{
pthread_join( m_thread, (void**)&m_returncode );
return m_returncode;
}
} }
bool PersistentThread::IsSelf() const bool PersistentThread::IsSelf() const
@ -97,15 +142,12 @@ namespace Threading
return pthread_self() == m_thread; return pthread_self() == m_thread;
} }
bool PersistentThread::Exists( pthread_t pid )
{
// passing 0 to pthread_kill is a NOP, and returns the status of the thread only.
return ( ESRCH != pthread_kill( pid, 0 ) );
}
bool PersistentThread::IsRunning() const bool PersistentThread::IsRunning() const
{ {
return ( m_running && (ESRCH != pthread_kill( m_thread, 0 )) ); if( !!m_detached )
return !!m_running;
else
return ( ESRCH != pthread_kill( m_thread, 0 ) );
} }
// Exceptions: // Exceptions:
@ -113,21 +155,28 @@ namespace Threading
// //
sptr PersistentThread::GetReturnCode() const sptr PersistentThread::GetReturnCode() const
{ {
if( !m_running )
throw Exception::InvalidOperation( "Thread.GetReturnCode : thread has not been started." );
if( IsRunning() ) if( IsRunning() )
throw Exception::InvalidOperation( "Thread.GetReturnCode : thread is still running." ); throw Exception::InvalidOperation( "Thread.GetReturnCode : thread is still running." );
return m_returncode; return m_returncode;
} }
// invoked when canceling or exiting the thread.
void PersistentThread::DoThreadCleanup()
{
wxASSERT( IsSelf() ); // only allowed from our own thread, thanks.
_InterlockedExchange( &m_running, false );
}
void* PersistentThread::_internal_callback( void* itsme ) void* PersistentThread::_internal_callback( void* itsme )
{ {
jASSUME( itsme != NULL ); jASSUME( itsme != NULL );
PersistentThread& owner = *((PersistentThread*)itsme); PersistentThread& owner = *((PersistentThread*)itsme);
pthread_cleanup_push( _pt_callback_cleanup, itsme );
owner.m_returncode = owner.ExecuteTask(); owner.m_returncode = owner.ExecuteTask();
pthread_cleanup_pop( true );
return (void*)owner.m_returncode; return (void*)owner.m_returncode;
} }

View File

@ -219,7 +219,7 @@ void mtgsThreadObject::Start()
mtgsThreadObject::~mtgsThreadObject() mtgsThreadObject::~mtgsThreadObject()
{ {
Cancel(); mtgsThreadObject::Cancel();
} }
void mtgsThreadObject::Cancel() void mtgsThreadObject::Cancel()

View File

@ -704,8 +704,9 @@ static bool OpenPlugin_GS()
return true; return true;
} }
if( mtgsThread->IsSelf() ) if( !mtgsThread->IsSelf() ) return true; // already opened?
return !GSopen( (void*)&pDsp, "PCSX2", renderswitch ? 2 : 1 );
return !GSopen( (void*)&pDsp, "PCSX2", renderswitch ? 2 : 1 );
// Note: rederswitch is us abusing the isMultiThread parameter for that so // Note: rederswitch is us abusing the isMultiThread parameter for that so
// we don't need a new callback // we don't need a new callback

View File

@ -110,6 +110,6 @@ namespace Msgbox
} }
BEGIN_DECLARE_EVENT_TYPES() BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEVT_MSGBOX, -1 ); DECLARE_EVENT_TYPE( pxEVT_MSGBOX, -1 )
DECLARE_EVENT_TYPE( pxEVT_CallStackBox, -1 ); DECLARE_EVENT_TYPE( pxEVT_CallStackBox, -1 )
END_DECLARE_EVENT_TYPES() END_DECLARE_EVENT_TYPES()

View File

@ -32,6 +32,89 @@
class IniInterface; class IniInterface;
// ------------------------------------------------------------------------
// All Menu Options for the Main Window! :D
// ------------------------------------------------------------------------
enum MenuIdentifiers
{
// Main Menu Section
Menu_Run = 1,
Menu_Config, // General config, plus non audio/video plugins.
Menu_Video, // Video options filled in by GS plugin
Menu_Audio, // audio options filled in by SPU2 plugin
Menu_Misc, // Misc options and help!
// Run SubSection
Menu_RunIso = 20, // Opens submenu with Iso browser, and recent isos.
Menu_IsoBrowse, // Open dialog, runs selected iso.
Menu_BootCDVD, // opens a submenu filled by CDVD plugin (usually list of drives)
Menu_RunWithoutDisc, // used to enter the bios (subs in cdvdnull)
Menu_RunELF,
Menu_SkipBiosToggle, // enables the Bios Skip speedhack
Menu_EnableSkipBios, // check marked menu that toggles Skip Bios boot feature.
Menu_PauseExec, // suspends/resumes active emulation
Menu_Reset, // Issues a complete reset.
Menu_States, // Opens states submenu
Menu_Run_Exit = wxID_EXIT,
Menu_State_Load = 40,
Menu_State_LoadOther,
Menu_State_Load01, // first of many load slots
Menu_State_Save = 60,
Menu_State_SaveOther,
Menu_State_Save01, // first of many save slots
// Config Subsection
Menu_Config_Settings = 100,
Menu_Config_BIOS,
Menu_Config_CDVD,
Menu_Config_DEV9,
Menu_Config_USB,
Menu_Config_FireWire,
Menu_Config_Patches,
// Video Subsection
// Top items are Pcsx2-controlled. GS plugin items are inserted beneath.
Menu_Video_Basics = 200, // includes frame timings and skippings settings
Menu_Video_Advanced, // inserted at the bottom of the menu
Menu_GS_Custom1 = 210, // start index for GS custom entries (valid up to 299)
Menu_GS_CustomMax = 299,
// Audio subsection
// Top items are Pcsx2-controlled. SPU2 plugin items are inserted beneath.
// [no items at this time]
Menu_Audio_Advanced = 300, // inserted at the bottom of the menu
Menu_Audio_Custom1 = 310,
Menu_Audio_CustomMax = 399,
// Controller subsection
// Top items are Pcsx2-controlled. Pad plugin items are inserted beneath.
// [no items at this time]
Menu_Pad_Advanced = 400,
Menu_Pad_Custom1 = 410,
Menu_Pad_CustomMax = 499,
// Miscellaneous Menu! (Misc)
Menu_About = wxID_ABOUT,
Menu_Website = 500, // Visit our awesome website!
Menu_Profiler, // Enable profiler
Menu_Console, // Enable console
Menu_Patches,
// Debug Subsection
Menu_Debug_Open = 600, // opens the debugger window / starts a debug session
Menu_Debug_MemoryDump,
Menu_Debug_Logging, // dialog for selection additional log options
Menu_Debug_Usermode,
Menu_Languages,
// Language Menu
// (Updated entirely dynamically, all values from range 1000 onward are reserved for languages)
Menu_Language_Start = 1000
};
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// //
struct AppImageIds struct AppImageIds
@ -97,7 +180,6 @@ class Pcsx2App : public wxApp
protected: protected:
MainEmuFrame* m_MainFrame; MainEmuFrame* m_MainFrame;
ConsoleLogFrame* m_ProgramLogBox; ConsoleLogFrame* m_ProgramLogBox;
ConsoleLogFrame* m_Ps2ConLogBox;
wxBitmap* m_Bitmap_Logo; wxBitmap* m_Bitmap_Logo;
wxImageList m_ConfigImages; wxImageList m_ConfigImages;
@ -135,16 +217,17 @@ public:
return *m_MainFrame; return *m_MainFrame;
} }
void PostMenuAction( MenuIdentifiers menu_id ) const;
void Ping() const;
// ----------------------------------------------------------------------------
// Console / Program Logging Helpers
// ----------------------------------------------------------------------------
ConsoleLogFrame* GetProgramLog() ConsoleLogFrame* GetProgramLog()
{ {
return m_ProgramLogBox; return m_ProgramLogBox;
} }
ConsoleLogFrame* GetConsoleLog()
{
return m_Ps2ConLogBox;
}
void CloseProgramLog() void CloseProgramLog()
{ {
m_ProgramLogBox->Close(); m_ProgramLogBox->Close();
@ -165,18 +248,13 @@ public:
m_ProgramLogBox->GetEventHandler()->AddPendingEvent( evt ); m_ProgramLogBox->GetEventHandler()->AddPendingEvent( evt );
} }
void ConsoleLog_PostEvent( wxEvent& evt )
{
if( m_Ps2ConLogBox == NULL ) return;
m_Ps2ConLogBox->GetEventHandler()->AddPendingEvent( evt );
}
//ConsoleLogFrame* GetConsoleFrame() const { return m_ProgramLogBox; } //ConsoleLogFrame* GetConsoleFrame() const { return m_ProgramLogBox; }
//void SetConsoleFrame( ConsoleLogFrame& frame ) { m_ProgramLogBox = &frame; } //void SetConsoleFrame( ConsoleLogFrame& frame ) { m_ProgramLogBox = &frame; }
protected: protected:
void ReadUserModeSettings(); void ReadUserModeSettings();
bool TryOpenConfigCwd(); bool TryOpenConfigCwd();
void OnSemaphorePing( wxCommandEvent& evt );
void OnMessageBox( pxMessageBoxEvent& evt ); void OnMessageBox( pxMessageBoxEvent& evt );
void CleanupMess(); void CleanupMess();
void OpenWizardConsole(); void OpenWizardConsole();

View File

@ -359,6 +359,7 @@ wxString AppConfig::FullpathToMcd( uint mcdidx ) const { return Path::Combine( F
AppConfig::AppConfig() : AppConfig::AppConfig() :
MainGuiPosition( wxDefaultPosition ) MainGuiPosition( wxDefaultPosition )
, SettingsTabName( L"Cpu" )
, LanguageId( wxLANGUAGE_DEFAULT ) , LanguageId( wxLANGUAGE_DEFAULT )
, RecentFileCount( 6 ) , RecentFileCount( 6 )
, DeskTheme( L"default" ) , DeskTheme( L"default" )
@ -418,6 +419,7 @@ void AppConfig::LoadSave( IniInterface& ini )
AppConfig defaults; AppConfig defaults;
IniEntry( MainGuiPosition ); IniEntry( MainGuiPosition );
IniEntry( SettingsTabName );
ini.EnumEntry( L"LanguageId", LanguageId, NULL, defaults.LanguageId ); ini.EnumEntry( L"LanguageId", LanguageId, NULL, defaults.LanguageId );
IniEntry( RecentFileCount ); IniEntry( RecentFileCount );
IniEntry( DeskTheme ); IniEntry( DeskTheme );

View File

@ -104,6 +104,10 @@ public:
public: public:
wxPoint MainGuiPosition; wxPoint MainGuiPosition;
// Because remembering the last used tab on the settings panel is cool (tab is remembered
// by it's UTF/ASCII name).
wxString SettingsTabName;
// Current language in use (correlates to a wxWidgets wxLANGUAGE specifier) // Current language in use (correlates to a wxWidgets wxLANGUAGE specifier)
wxLanguage LanguageId; wxLanguage LanguageId;

View File

@ -647,6 +647,9 @@ namespace Console
const wxString fmtline( wxString::FromAscii( fmt ) + L"\n" ); const wxString fmtline( wxString::FromAscii( fmt ) + L"\n" );
_immediate_logger( fmtline ); _immediate_logger( fmtline );
if( emuLog != NULL )
fflush( emuLog );
// Implementation note: I've duplicated Write+Newline behavior here to avoid polluting // Implementation note: I've duplicated Write+Newline behavior here to avoid polluting
// the message pump with lots of erroneous messages (Newlines can be bound into Write message). // the message pump with lots of erroneous messages (Newlines can be bound into Write message).
@ -664,6 +667,9 @@ namespace Console
const wxString fmtline( fmt + L"\n" ); const wxString fmtline( fmt + L"\n" );
_immediate_logger( fmtline ); _immediate_logger( fmtline );
if( emuLog != NULL )
fflush( emuLog );
// Implementation note: I've duplicated Write+Newline behavior here to avoid polluting // Implementation note: I've duplicated Write+Newline behavior here to avoid polluting
// the message pump with lots of erroneous messages (Newlines can be bound into Write message). // the message pump with lots of erroneous messages (Newlines can be bound into Write message).

View File

@ -43,13 +43,22 @@ using namespace Panels;
static const int s_orient = wxBK_LEFT; static const int s_orient = wxBK_LEFT;
#endif #endif
static const int IdealWidth = 500;
template< typename T >
void Dialogs::ConfigurationDialog::AddPage( const char* label, int iconid )
{
const wxString labelstr( wxString::FromUTF8( label ) );
const int curidx = m_labels.Add( labelstr );
g_ApplyState.SetCurrentPage( curidx );
m_listbook.AddPage( new T( m_listbook, IdealWidth ), wxGetTranslation( labelstr ),
( labelstr == g_Conf->SettingsTabName ), iconid );
}
Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent, int id ) : Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent, int id ) :
wxDialogWithHelpers( parent, id, _("PCSX2 Configuration"), true ) wxDialogWithHelpers( parent, id, _("PCSX2 Configuration"), true )
, m_listbook( *new wxListbook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_orient ) ) , m_listbook( *new wxListbook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_orient ) )
{ {
static const int IdealWidth = 500;
wxBoxSizer& mainSizer = *new wxBoxSizer( wxVERTICAL ); wxBoxSizer& mainSizer = *new wxBoxSizer( wxVERTICAL );
m_listbook.SetImageList( &wxGetApp().GetImgList_Config() ); m_listbook.SetImageList( &wxGetApp().GetImgList_Config() );
@ -57,23 +66,12 @@ Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent, int id ) :
g_ApplyState.StartBook( &m_listbook ); g_ApplyState.StartBook( &m_listbook );
g_ApplyState.SetCurrentPage( m_listbook.GetPageCount() ); AddPage<CpuPanel> ( wxLt("CPU"), cfgid.Cpu );
m_listbook.AddPage( new CpuPanel( m_listbook, IdealWidth ), _("CPU"), false, cfgid.Cpu ); AddPage<VideoPanel> ( wxLt("GS/Video"), cfgid.Video );
AddPage<SpeedHacksPanel> ( wxLt("Speedhacks"), cfgid.Speedhacks );
g_ApplyState.SetCurrentPage( m_listbook.GetPageCount() ); AddPage<GameFixesPanel> ( wxLt("Game Fixes"), cfgid.Gamefixes );
m_listbook.AddPage( new VideoPanel( m_listbook, IdealWidth ), _("GS/Video"), false, cfgid.Video ); AddPage<PluginSelectorPanel>( wxLt("Plugins"), cfgid.Plugins );
AddPage<StandardPathsPanel> ( wxLt("Folders"), cfgid.Paths );
g_ApplyState.SetCurrentPage( m_listbook.GetPageCount() );
m_listbook.AddPage( new SpeedHacksPanel( m_listbook, IdealWidth ), _("Speedhacks"), false, cfgid.Speedhacks );
g_ApplyState.SetCurrentPage( m_listbook.GetPageCount() );
m_listbook.AddPage( new GameFixesPanel( m_listbook, IdealWidth ), _("Game Fixes"), false, cfgid.Gamefixes );
g_ApplyState.SetCurrentPage( m_listbook.GetPageCount() );
m_listbook.AddPage( new PluginSelectorPanel( m_listbook, IdealWidth ), _("Plugins"), false, cfgid.Plugins );
g_ApplyState.SetCurrentPage( m_listbook.GetPageCount() );
m_listbook.AddPage( new StandardPathsPanel( m_listbook ), _("Folders"), false, cfgid.Paths );
mainSizer.Add( &m_listbook ); mainSizer.Add( &m_listbook );
AddOkCancel( mainSizer, true ); AddOkCancel( mainSizer, true );
@ -94,6 +92,7 @@ Dialogs::ConfigurationDialog::ConfigurationDialog( wxWindow* parent, int id ) :
#endif #endif
Connect( wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigurationDialog::OnOk_Click ) ); Connect( wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigurationDialog::OnOk_Click ) );
Connect( wxID_CANCEL, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigurationDialog::OnCancel_Click ) );
Connect( wxID_APPLY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigurationDialog::OnApply_Click ) ); Connect( wxID_APPLY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigurationDialog::OnApply_Click ) );
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -121,17 +120,30 @@ Dialogs::ConfigurationDialog::~ConfigurationDialog()
void Dialogs::ConfigurationDialog::OnOk_Click( wxCommandEvent& evt ) void Dialogs::ConfigurationDialog::OnOk_Click( wxCommandEvent& evt )
{ {
if( g_ApplyState.ApplyAll() ) if( g_ApplyState.ApplyAll( false ) )
{ {
FindWindow( wxID_APPLY )->Disable();
g_Conf->SettingsTabName = m_labels[m_listbook.GetSelection()];
g_Conf->Save();
Close(); Close();
evt.Skip(); evt.Skip();
} }
} }
void Dialogs::ConfigurationDialog::OnCancel_Click( wxCommandEvent& evt )
{
evt.Skip();
g_Conf->SettingsTabName = m_labels[m_listbook.GetSelection()];
}
void Dialogs::ConfigurationDialog::OnApply_Click( wxCommandEvent& evt ) void Dialogs::ConfigurationDialog::OnApply_Click( wxCommandEvent& evt )
{ {
FindWindow( wxID_APPLY )->Disable(); if( g_ApplyState.ApplyAll( false ) )
g_ApplyState.ApplyAll(); FindWindow( wxID_APPLY )->Disable();
g_Conf->SettingsTabName = m_labels[m_listbook.GetSelection()];
g_Conf->Save();
} }

View File

@ -32,13 +32,18 @@ namespace Dialogs
{ {
protected: protected:
wxListbook& m_listbook; wxListbook& m_listbook;
wxArrayString m_labels;
public: public:
virtual ~ConfigurationDialog(); virtual ~ConfigurationDialog();
ConfigurationDialog(wxWindow* parent, int id=wxID_ANY); ConfigurationDialog(wxWindow* parent, int id=wxID_ANY);
protected: protected:
template< typename T >
void AddPage( const char* label, int iconid );
void OnOk_Click( wxCommandEvent& evt ); void OnOk_Click( wxCommandEvent& evt );
void OnCancel_Click( wxCommandEvent& evt );
void OnApply_Click( wxCommandEvent& evt ); void OnApply_Click( wxCommandEvent& evt );
virtual void OnSomethingChanged( wxCommandEvent& evt ) virtual void OnSomethingChanged( wxCommandEvent& evt )

View File

@ -24,95 +24,12 @@
#include "App.h" #include "App.h"
//////////////////////////////////////////////////////////////////////////////////////////
//
class MainEmuFrame: public wxFrame class MainEmuFrame: public wxFrame
{ {
protected: public:
// ------------------------------------------------------------------------
// All Menu Options for the Main Window! :D
// ------------------------------------------------------------------------
enum Identifiers
{
// Main Menu Section
Menu_Run = 1,
Menu_Config, // General config, plus non audio/video plugins.
Menu_Video, // Video options filled in by GS plugin
Menu_Audio, // audio options filled in by SPU2 plugin
Menu_Misc, // Misc options and help!
// Run SubSection
Menu_RunIso = 20, // Opens submenu with Iso browser, and recent isos.
Menu_IsoBrowse, // Open dialog, runs selected iso.
Menu_BootCDVD, // opens a submenu filled by CDVD plugin (usually list of drives)
Menu_RunWithoutDisc, // used to enter the bios (subs in cdvdnull)
Menu_RunELF,
Menu_SkipBiosToggle, // enables the Bios Skip speedhack
Menu_EnableSkipBios, // check marked menu that toggles Skip Bios boot feature.
Menu_PauseExec, // suspends/resumes active emulation
Menu_Reset, // Issues a complete reset.
Menu_States, // Opens states submenu
Menu_Run_Exit = wxID_EXIT,
Menu_State_Load = 40,
Menu_State_LoadOther,
Menu_State_Load01, // first of many load slots
Menu_State_Save = 60,
Menu_State_SaveOther,
Menu_State_Save01, // first of many save slots
// Config Subsection
Menu_Config_Settings = 100,
Menu_Config_BIOS,
Menu_Config_CDVD,
Menu_Config_DEV9,
Menu_Config_USB,
Menu_Config_FireWire,
Menu_Config_Patches,
// Video Subsection
// Top items are Pcsx2-controlled. GS plugin items are inserted beneath.
Menu_Video_Basics = 200, // includes frame timings and skippings settings
Menu_Video_Advanced, // inserted at the bottom of the menu
Menu_GS_Custom1 = 210, // start index for GS custom entries (valid up to 299)
Menu_GS_CustomMax = 299,
// Audio subsection
// Top items are Pcsx2-controlled. SPU2 plugin items are inserted beneath.
// [no items at this time]
Menu_Audio_Advanced = 300, // inserted at the bottom of the menu
Menu_Audio_Custom1 = 310,
Menu_Audio_CustomMax = 399,
// Controller subsection
// Top items are Pcsx2-controlled. Pad plugin items are inserted beneath.
// [no items at this time]
Menu_Pad_Advanced = 400,
Menu_Pad_Custom1 = 410,
Menu_Pad_CustomMax = 499,
// Miscellaneous Menu! (Misc)
Menu_About = wxID_ABOUT,
Menu_Website = 500, // Visit our awesome website!
Menu_Profiler, // Enable profiler
Menu_Console, // Enable console
Menu_Patches,
// Debug Subsection
Menu_Debug_Open = 600, // opens the debugger window / starts a debug session
Menu_Debug_MemoryDump,
Menu_Debug_Logging, // dialog for selection additional log options
Menu_Debug_Usermode,
Menu_Languages,
// Language Menu
// (Updated entirely dynamically, all values from range 1000 onward are reserved for languages)
Menu_Language_Start = 1000
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// MainEmuFrame Protected Variables // MainEmuFrame Protected Variables
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
protected: protected:
@ -138,7 +55,7 @@ protected:
bool m_IsPaused; bool m_IsPaused;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// MainEmuFrame Constructors and Member Methods // MainEmuFrame Constructors and Member Methods
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
public: public:
@ -177,7 +94,7 @@ protected:
void Menu_ShowAboutBox(wxCommandEvent &event); void Menu_ShowAboutBox(wxCommandEvent &event);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// MainEmuFram Internal API for Populating Main Menu Contents // MainEmuFram Internal API for Populating Main Menu Contents
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
wxMenu* MakeStatesSubMenu( int baseid ) const; wxMenu* MakeStatesSubMenu( int baseid ) const;

View File

@ -118,7 +118,7 @@ namespace Panels
void StartBook( wxBookCtrlBase* book ); void StartBook( wxBookCtrlBase* book );
void StartWizard(); void StartWizard();
bool ApplyAll(); bool ApplyAll( bool saveOnSuccess=true );
bool ApplyPage( int pageid, bool saveOnSuccess=true ); bool ApplyPage( int pageid, bool saveOnSuccess=true );
void DoCleanup(); void DoCleanup();
}; };
@ -323,7 +323,7 @@ namespace Panels
class StandardPathsPanel : public BasePathsPanel class StandardPathsPanel : public BasePathsPanel
{ {
public: public:
StandardPathsPanel( wxWindow& parent ); StandardPathsPanel( wxWindow& parent, int idealWidth );
}; };
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
@ -452,8 +452,8 @@ namespace Panels
public: public:
virtual ~PluginSelectorPanel(); virtual ~PluginSelectorPanel();
PluginSelectorPanel( wxWindow& parent, int idealWidth ); PluginSelectorPanel( wxWindow& parent, int idealWidth );
virtual void CancelRefresh();
void CancelRefresh(); // used from destructor, stays non-virtual
void Apply( AppConfig& conf ); void Apply( AppConfig& conf );
protected: protected:

View File

@ -33,7 +33,7 @@ static wxString GetNormalizedConfigFolder( FoldersEnum_t folderId )
return normalized.ToString(); return normalized.ToString();
} }
// Pass me TRUE if the default path is to be used, and the DirPcikerCtrl disabled from use. // Pass me TRUE if the default path is to be used, and the DirPickerCtrl disabled from use.
void Panels::DirPickerPanel::UpdateCheckStatus( bool someNoteworthyBoolean ) void Panels::DirPickerPanel::UpdateCheckStatus( bool someNoteworthyBoolean )
{ {
m_pickerCtrl->Enable( !someNoteworthyBoolean ); m_pickerCtrl->Enable( !someNoteworthyBoolean );

View File

@ -100,9 +100,9 @@ bool Panels::StaticApplyState::ApplyPage( int pageid, bool saveOnSuccess )
// Returns false if one of the panels fails input validation (in which case dialogs // Returns false if one of the panels fails input validation (in which case dialogs
// should not be closed, etc). // should not be closed, etc).
bool Panels::StaticApplyState::ApplyAll() bool Panels::StaticApplyState::ApplyAll( bool saveOnSuccess )
{ {
return ApplyPage( -1 ); return ApplyPage( -1, saveOnSuccess );
} }
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------

View File

@ -40,7 +40,7 @@ Panels::DirPickerPanel& Panels::BasePathsPanel::AddDirPicker( wxBoxSizer& sizer,
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
Panels::StandardPathsPanel::StandardPathsPanel( wxWindow& parent ) : Panels::StandardPathsPanel::StandardPathsPanel( wxWindow& parent, __unused int idealWidth ) :
BasePathsPanel( parent ) BasePathsPanel( parent )
{ {
s_main.AddSpacer( BetweenFolderSpace ); s_main.AddSpacer( BetweenFolderSpace );

View File

@ -420,7 +420,7 @@ Panels::PluginSelectorPanel::EnumThread::EnumThread( PluginSelectorPanel& master
Panels::PluginSelectorPanel::EnumThread::~EnumThread() Panels::PluginSelectorPanel::EnumThread::~EnumThread()
{ {
Cancel(); EnumThread::Cancel();
safe_delete_array( Results ); safe_delete_array( Results );
} }
@ -435,7 +435,7 @@ sptr Panels::PluginSelectorPanel::EnumThread::ExecuteTask()
{ {
DevCon::Status( "Plugin Enumeration Thread started..." ); DevCon::Status( "Plugin Enumeration Thread started..." );
Sleep( 10 ); // gives the gui thread some time to refresh wxGetApp().Ping(); // gives the gui thread some time to refresh
for( int curidx=0; curidx < m_master.FileCount(); ++curidx ) for( int curidx=0; curidx < m_master.FileCount(); ++curidx )
{ {

View File

@ -31,6 +31,12 @@
IMPLEMENT_APP(Pcsx2App) IMPLEMENT_APP(Pcsx2App)
DEFINE_EVENT_TYPE( pxEVT_SemaphorePing )
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEVT_SemaphorePing, -1 )
END_DECLARE_EVENT_TYPES()
bool UseAdminMode = false; bool UseAdminMode = false;
AppConfig* g_Conf = NULL; AppConfig* g_Conf = NULL;
wxFileHistory* g_RecentIsoList = NULL; wxFileHistory* g_RecentIsoList = NULL;
@ -77,8 +83,15 @@ sptr AppEmuThread::ExecuteTask()
{ {
if( ex.StreamName == g_Conf->FullpathToBios() ) if( ex.StreamName == g_Conf->FullpathToBios() )
{ {
Msgbox::OkCancel( ex.FormatDisplayMessage() + GetPluginManager().Close();
bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() +
_("\n\nPress Ok to go to the BIOS Configuration Panel.") ); _("\n\nPress Ok to go to the BIOS Configuration Panel.") );
if( result )
{
wxGetApp().PostMenuAction( Menu_Config_BIOS );
wxGetApp().Ping();
}
} }
else else
{ {
@ -91,16 +104,17 @@ sptr AppEmuThread::ExecuteTask()
if( g_Conf->FullpathTo( pid ) == ex.StreamName ) break; if( g_Conf->FullpathTo( pid ) == ex.StreamName ) break;
} }
if( pi->shortname == NULL ) if( pi->shortname != NULL )
{ {
// Some other crap file failure >_< bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() +
} _("\n\nPress Ok to go to the Plugin Configuration Panel.") );
int result = Msgbox::OkCancel( ex.FormatDisplayMessage() + if( result )
_("\n\nPress Ok to go to the Plugin Configuration Panel.") ); {
g_Conf->SettingsTabName = L"Plugins";
if( result == wxID_OK ) wxGetApp().PostMenuAction( Menu_Config_Settings );
{ wxGetApp().Ping();
}
} }
} }
} }
@ -163,6 +177,7 @@ void Pcsx2App::ReadUserModeSettings()
// Save user's new settings // Save user's new settings
IniSaver saver( *conf_usermode ); IniSaver saver( *conf_usermode );
g_Conf->LoadSaveUserMode( saver, groupname ); g_Conf->LoadSaveUserMode( saver, groupname );
AppConfig_ReloadGlobalSettings( true );
g_Conf->Save(); g_Conf->Save();
} }
else else
@ -187,6 +202,7 @@ void Pcsx2App::ReadUserModeSettings()
// Save user's new settings // Save user's new settings
IniSaver saver( *conf_usermode ); IniSaver saver( *conf_usermode );
g_Conf->LoadSaveUserMode( saver, groupname ); g_Conf->LoadSaveUserMode( saver, groupname );
AppConfig_ReloadGlobalSettings( true );
g_Conf->Save(); g_Conf->Save();
} }
} }
@ -242,6 +258,13 @@ bool Pcsx2App::OnInit()
wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() ); wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() );
#define pxMessageBoxEventThing(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxMessageBoxEventFunction, &func )
Connect( pxEVT_MSGBOX, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_CallStackBox, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_SemaphorePing, wxCommandEventHandler( Pcsx2App::OnSemaphorePing ) );
// User/Admin Mode Dual Setup: // User/Admin Mode Dual Setup:
// Pcsx2 now supports two fundamental modes of operation. The default is Classic mode, // Pcsx2 now supports two fundamental modes of operation. The default is Classic mode,
// which uses the Current Working Directory (CWD) for all user data files, and requires // which uses the Current Working Directory (CWD) for all user data files, and requires
@ -270,9 +293,6 @@ bool Pcsx2App::OnInit()
m_ProgramLogBox = new ConsoleLogFrame( m_MainFrame, L"PCSX2 Program Log", g_Conf->ProgLogBox ); m_ProgramLogBox = new ConsoleLogFrame( m_MainFrame, L"PCSX2 Program Log", g_Conf->ProgLogBox );
m_Ps2ConLogBox = m_ProgramLogBox; // just use a single logger for now.
//m_Ps2ConLogBox = new ConsoleLogFrame( NULL, L"PS2 Console Log" );
SetTopWindow( m_MainFrame ); // not really needed... SetTopWindow( m_MainFrame ); // not really needed...
SetExitOnFrameDelete( true ); // but being explicit doesn't hurt... SetExitOnFrameDelete( true ); // but being explicit doesn't hurt...
m_MainFrame->Show(); m_MainFrame->Show();
@ -286,15 +306,42 @@ bool Pcsx2App::OnInit()
return false; return false;
} }
#define pxMessageBoxEventThing(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxMessageBoxEventFunction, &func )
Connect( pxEVT_MSGBOX, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_CallStackBox,pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
return true; return true;
} }
// Allows for activating menu actions from anywhere in PCSX2.
// And it's Thread Safe!
void Pcsx2App::PostMenuAction( MenuIdentifiers menu_id ) const
{
wxASSERT( m_MainFrame != NULL );
if( m_MainFrame == NULL ) return;
wxCommandEvent joe( wxEVT_COMMAND_MENU_SELECTED, menu_id );
m_MainFrame->GetEventHandler()->AddPendingEvent( joe );
}
// Waits for the main GUI thread to respond. If run from the main GUI thread, returns
// immediately without error.
void Pcsx2App::Ping() const
{
if( wxThread::IsMain() ) return;
Semaphore sema;
wxCommandEvent bean( pxEVT_SemaphorePing );
bean.SetClientData( &sema );
wxGetApp().AddPendingEvent( bean );
sema.WaitNoCancel();
}
// ----------------------------------------------------------------------------
// Pcsx2App Event Handlers
// ----------------------------------------------------------------------------
void Pcsx2App::OnSemaphorePing( wxCommandEvent& evt )
{
((Semaphore*)evt.GetClientData())->Post();
}
void Pcsx2App::OnMessageBox( pxMessageBoxEvent& evt ) void Pcsx2App::OnMessageBox( pxMessageBoxEvent& evt )
{ {
Msgbox::OnEvent( evt ); Msgbox::OnEvent( evt );
@ -351,7 +398,6 @@ int Pcsx2App::OnExit()
Pcsx2App::Pcsx2App() : Pcsx2App::Pcsx2App() :
m_ProgramLogBox( NULL ) m_ProgramLogBox( NULL )
, m_Ps2ConLogBox( NULL )
, m_ConfigImages( 32, 32 ) , m_ConfigImages( 32, 32 )
, m_ConfigImagesAreLoaded( false ) , m_ConfigImagesAreLoaded( false )
, m_ToolbarImages( NULL ) , m_ToolbarImages( NULL )

View File

@ -103,11 +103,9 @@ sptr CoreEmuThread::ExecuteTask()
m_ResumeEvent.Wait(); m_ResumeEvent.Wait();
} }
pthread_cleanup_push( _cet_callback_cleanup, this );
CpuInitializeMess(); CpuInitializeMess();
StateCheck(); StateCheck();
CpuExecute(); CpuExecute();
pthread_cleanup_pop( true );
return 0; return 0;
} }
@ -154,20 +152,17 @@ CoreEmuThread::CoreEmuThread( const wxString& elf_file ) :
, m_lock_ExecMode() , m_lock_ExecMode()
{ {
PersistentThread::Start(); PersistentThread::Start();
pthread_detach( m_thread );
} }
// Invoked by the pthread_exit or pthread_cancel // Invoked by the pthread_exit or pthread_cancel
void CoreEmuThread::DoThreadCleanup() void CoreEmuThread::DoThreadCleanup()
{ {
wxASSERT( IsSelf() ); // only allowed from our own thread, thanks. PersistentThread::DoThreadCleanup();
m_running = false;
GetPluginManager().Close(); GetPluginManager().Close();
} }
CoreEmuThread::~CoreEmuThread() CoreEmuThread::~CoreEmuThread()
{ {
Cancel();
} }
// Resumes the core execution state, or does nothing is the core is already running. If // Resumes the core execution state, or does nothing is the core is already running. If