mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-10-29 21:28:59 +01:00
* Configuration panels are all modal-less now, so that you can open the config panel and leave it open while running games. * Handful of thread sync improvements. * Fixed on-the-fly interpreter/recompiler configuration. * Fixed plugin hotswapping (mostly works, but still a little funny at times) * All new assertion dialogs and popup message handlers. * RecentIsoList defaults to 12 instead of 6 Dev Notes: * I had to create a new set of assertion functions called pxAssume*. Originally I hoped to avoid that complexity, and just use a single one-assert-fits-all case, but turned out blanketly using __assume() for all assertion cases wasn't reliable. * wxGuiTools: Replaced the operator, with operator& -- the latter has proper order of precedence, the former required () to scope correctly. >_< git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2339 96395faa-99c1-11dd-bbfe-3dabce05a288
624 lines
19 KiB
C++
624 lines
19 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
|
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
|
*
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "PrecompiledHeader.h"
|
|
#include "App.h"
|
|
#include "Plugins.h"
|
|
#include "SaveState.h"
|
|
#include "Utilities/ScopedPtr.h"
|
|
#include "ConfigurationPanels.h"
|
|
#include "Dialogs/ModalPopups.h"
|
|
|
|
#include <wx/dynlib.h>
|
|
#include <wx/dir.h>
|
|
|
|
// Allows us to force-disable threading for debugging/troubleshooting
|
|
static const bool DisableThreading =
|
|
#ifdef __LINUX__
|
|
true; // linux appears to have threading issues with loadlibrary.
|
|
#else
|
|
false;
|
|
#endif
|
|
|
|
using namespace pxSizerFlags;
|
|
using namespace Threading;
|
|
|
|
BEGIN_DECLARE_EVENT_TYPES()
|
|
DECLARE_EVENT_TYPE(pxEVT_EnumeratedNext, -1)
|
|
DECLARE_EVENT_TYPE(pxEVT_EnumerationFinished, -1)
|
|
DECLARE_EVENT_TYPE(pxEVT_ShowStatusBar, -1)
|
|
END_DECLARE_EVENT_TYPES()
|
|
|
|
DEFINE_EVENT_TYPE(pxEVT_EnumeratedNext)
|
|
DEFINE_EVENT_TYPE(pxEVT_EnumerationFinished);
|
|
DEFINE_EVENT_TYPE(pxEVT_ShowStatusBar);
|
|
|
|
typedef s32 (CALLBACK* PluginTestFnptr)();
|
|
typedef void (CALLBACK* PluginConfigureFnptr)();
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
namespace Exception
|
|
{
|
|
class NotEnumerablePlugin : public BadStream
|
|
{
|
|
public:
|
|
DEFINE_STREAM_EXCEPTION( NotEnumerablePlugin, wxLt("File is not a PCSX2 plugin") );
|
|
};
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// PluginEnumerator class
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
class PluginEnumerator
|
|
{
|
|
protected:
|
|
wxString m_plugpath;
|
|
wxDynamicLibrary m_plugin;
|
|
|
|
_PS2EgetLibType m_GetLibType;
|
|
_PS2EgetLibName m_GetLibName;
|
|
_PS2EgetLibVersion2 m_GetLibVersion2;
|
|
|
|
u32 m_type;
|
|
|
|
public:
|
|
|
|
// Constructor!
|
|
//
|
|
// Possible Exceptions:
|
|
// BadStream - thrown if the provided file is simply not a loadable DLL.
|
|
// NotEnumerablePlugin - thrown if the DLL is not a PCSX2 plugin, or if it's of an unsupported version.
|
|
//
|
|
PluginEnumerator( const wxString& plugpath ) :
|
|
m_plugpath( plugpath )
|
|
, m_plugin()
|
|
{
|
|
if( !m_plugin.Load( m_plugpath ) )
|
|
throw Exception::BadStream( m_plugpath, "File is not a valid dynamic library." );
|
|
|
|
wxDoNotLogInThisScope please;
|
|
m_GetLibType = (_PS2EgetLibType)m_plugin.GetSymbol( L"PS2EgetLibType" );
|
|
m_GetLibName = (_PS2EgetLibName)m_plugin.GetSymbol( L"PS2EgetLibName" );
|
|
m_GetLibVersion2 = (_PS2EgetLibVersion2)m_plugin.GetSymbol( L"PS2EgetLibVersion2" );
|
|
|
|
if( m_GetLibType == NULL || m_GetLibName == NULL || m_GetLibVersion2 == NULL )
|
|
throw Exception::NotEnumerablePlugin( m_plugpath );
|
|
|
|
m_type = m_GetLibType();
|
|
}
|
|
|
|
bool CheckVersion( PluginsEnum_t pluginTypeIndex ) const
|
|
{
|
|
const PluginInfo& info( tbl_PluginInfo[pluginTypeIndex] );
|
|
if( m_type & info.typemask )
|
|
{
|
|
int version = m_GetLibVersion2( info.typemask );
|
|
if ( ((version >> 16)&0xff) == tbl_PluginInfo[pluginTypeIndex].version )
|
|
return true;
|
|
|
|
Console.Warning("%s Plugin %s: Version %x != %x", info.shortname, m_plugpath.c_str(), 0xff&(version >> 16), info.version);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Test( int pluginTypeIndex ) const
|
|
{
|
|
// all test functions use the same parameterless API, so just pick one arbitrarily (I pick PAD!)
|
|
PluginTestFnptr testfunc = (PluginTestFnptr)m_plugin.GetSymbol( fromUTF8( tbl_PluginInfo[pluginTypeIndex].shortname ) + L"test" );
|
|
if( testfunc == NULL ) return false;
|
|
return (testfunc() == 0);
|
|
}
|
|
|
|
wxString GetName() const
|
|
{
|
|
pxAssert( m_GetLibName != NULL );
|
|
return fromUTF8(m_GetLibName());
|
|
}
|
|
|
|
void GetVersionString( wxString& dest, int pluginTypeIndex ) const
|
|
{
|
|
const PluginInfo& info( tbl_PluginInfo[pluginTypeIndex] );
|
|
int version = m_GetLibVersion2( info.typemask );
|
|
dest.Printf( L"%d.%d.%d", (version>>8)&0xff, version&0xff, (version>>24)&0xff );
|
|
}
|
|
};
|
|
|
|
static const wxString failed_separator( L"-------- Unsupported Plugins --------" );
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// PluginSelectorPanel implementations
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
Panels::PluginSelectorPanel::StatusPanel::StatusPanel( wxWindow* parent )
|
|
: wxPanelWithHelpers( parent, wxVERTICAL )
|
|
, m_gauge( *new wxGauge( this, wxID_ANY, 10 ) )
|
|
, m_label( *new wxStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE | wxST_NO_AUTORESIZE ) )
|
|
{
|
|
m_progress = 0;
|
|
|
|
m_gauge.SetToolTip( _("I'm givin' her all she's got, Captain!") );
|
|
|
|
*this += new pxStaticHeading( this, _( "Enumerating available plugins..." ) );
|
|
*this += m_gauge | wxSF.Expand().Border( wxLEFT | wxRIGHT, 32 );
|
|
*this += m_label | StdExpand();
|
|
|
|
Fit();
|
|
}
|
|
|
|
void Panels::PluginSelectorPanel::StatusPanel::SetGaugeLength( int len )
|
|
{
|
|
m_gauge.SetRange( len );
|
|
}
|
|
|
|
void Panels::PluginSelectorPanel::StatusPanel::AdvanceProgress( const wxString& msg )
|
|
{
|
|
m_label.SetLabel( msg );
|
|
m_gauge.SetValue( ++m_progress );
|
|
}
|
|
|
|
void Panels::PluginSelectorPanel::StatusPanel::Reset()
|
|
{
|
|
m_gauge.SetValue( m_progress = 0 );
|
|
m_label.SetLabel( wxEmptyString );
|
|
}
|
|
|
|
// Id for all Configure buttons (any non-negative arbitrary integer will do)
|
|
static const int ButtonId_Configure = 51;
|
|
|
|
// ------------------------------------------------------------------------
|
|
Panels::PluginSelectorPanel::ComboBoxPanel::ComboBoxPanel( PluginSelectorPanel* parent )
|
|
: wxPanelWithHelpers( parent, wxVERTICAL )
|
|
, m_FolderPicker( *new DirPickerPanel( this, FolderId_Plugins,
|
|
_("Plugins Search Path:"),
|
|
_("Select a folder with PCSX2 plugins") )
|
|
)
|
|
{
|
|
wxFlexGridSizer& s_plugin( *new wxFlexGridSizer( NumPluginTypes, 3, 16, 10 ) );
|
|
s_plugin.SetFlexibleDirection( wxHORIZONTAL );
|
|
s_plugin.AddGrowableCol( 1 ); // expands combo boxes to full width.
|
|
|
|
for( int i=0; i<NumPluginTypes; ++i )
|
|
{
|
|
wxStaticText* text = new wxStaticText( this, wxID_ANY, tbl_PluginInfo[i].GetShortname() );
|
|
|
|
m_combobox[i] = new wxComboBox( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY );
|
|
|
|
wxButton* confButton = new wxButton( this, ButtonId_Configure, L"Configure..." );
|
|
confButton->SetClientData( (void*)(int)tbl_PluginInfo[i].id );
|
|
|
|
s_plugin += text | wxSF.Border( wxTOP | wxLEFT, 2 );
|
|
s_plugin += m_combobox[i] | wxSF.Expand();
|
|
s_plugin += confButton;
|
|
}
|
|
|
|
m_FolderPicker.SetStaticDesc( _("Click the Browse button to select a different folder for PCSX2 plugins.") );
|
|
|
|
*this += s_plugin | wxSF.Expand();
|
|
*this += 6;
|
|
*this += m_FolderPicker | StdExpand();
|
|
}
|
|
|
|
void Panels::PluginSelectorPanel::ComboBoxPanel::Reset()
|
|
{
|
|
for( int i=0; i<NumPluginTypes; ++i )
|
|
{
|
|
m_combobox[i]->Clear();
|
|
m_combobox[i]->SetSelection( wxNOT_FOUND );
|
|
m_combobox[i]->SetValue( wxEmptyString );
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
Panels::PluginSelectorPanel::PluginSelectorPanel( wxWindow* parent, int idealWidth )
|
|
: BaseSelectorPanel( parent )
|
|
{
|
|
if( idealWidth != wxDefaultCoord ) m_idealWidth = idealWidth;
|
|
|
|
m_StatusPanel = new StatusPanel( this );
|
|
m_ComponentBoxes = new ComboBoxPanel( this );
|
|
|
|
// note: the status panel is a floating window, so that it can be positioned in the
|
|
// center of the dialog after it's been fitted to the contents.
|
|
|
|
*this += m_ComponentBoxes | StdExpand().ReserveSpaceEvenIfHidden();
|
|
|
|
m_StatusPanel->Hide();
|
|
m_ComponentBoxes->Hide();
|
|
|
|
// refresh button used for diagnostics... (don't think there's a point to having one otherwise) --air
|
|
//wxButton* refresh = new wxButton( this, wxID_ANY, L"Refresh" );
|
|
//s_main.Add( refresh );
|
|
//Connect( refresh->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PluginSelectorPanel::OnRefresh ) );
|
|
|
|
Connect( pxEVT_EnumeratedNext, wxCommandEventHandler( PluginSelectorPanel::OnProgress ) );
|
|
Connect( pxEVT_EnumerationFinished, wxCommandEventHandler( PluginSelectorPanel::OnEnumComplete ) );
|
|
Connect( pxEVT_ShowStatusBar, wxCommandEventHandler( PluginSelectorPanel::OnShowStatusBar ) );
|
|
Connect( ButtonId_Configure, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PluginSelectorPanel::OnConfigure_Clicked ) );
|
|
|
|
OnSettingsChanged();
|
|
}
|
|
|
|
Panels::PluginSelectorPanel::~PluginSelectorPanel() throw()
|
|
{
|
|
CancelRefresh(); // in case the enumeration thread is currently refreshing...
|
|
}
|
|
|
|
void Panels::PluginSelectorPanel::OnSettingsChanged()
|
|
{
|
|
m_ComponentBoxes->GetDirPicker().Reset();
|
|
}
|
|
|
|
static wxString GetApplyFailedMsg()
|
|
{
|
|
return pxE( ".Popup Error:PluginSelector:ApplyFailed",
|
|
L"All plugins must have valid selections for PCSX2 to run. If you are unable to make\n"
|
|
L"a valid selection due to missing plugins or an incomplete install of PCSX2, then\n"
|
|
L"press cancel to close the Configuration panel."
|
|
);
|
|
}
|
|
|
|
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();
|
|
if( sel == wxNOT_FOUND )
|
|
{
|
|
wxString plugname( tbl_PluginInfo[i].GetShortname() );
|
|
|
|
throw Exception::CannotApplySettings( this,
|
|
// English Log
|
|
wxsFormat( L"PluginSelectorPanel: Invalid or missing selection for the %s plugin.", plugname.c_str() ),
|
|
|
|
// Translated
|
|
wxsFormat( L"Please select a valid plugin for the %s.", plugname.c_str() ) + L"\n\n" + GetApplyFailedMsg()
|
|
);
|
|
}
|
|
|
|
g_Conf->BaseFilenames.Plugins[tbl_PluginInfo[i].id] = GetFilename((int)m_ComponentBoxes->Get(i).GetClientData(sel));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Make sure folders are up to date, and try to load/reload plugins if needed...
|
|
|
|
g_Conf->Folders.ApplyDefaults();
|
|
|
|
// Need to unload the current emulation state if the user changed plugins, because
|
|
// the whole plugin system needs to be re-loaded.
|
|
|
|
const PluginInfo* pi = tbl_PluginInfo; do {
|
|
if( g_Conf->FullpathTo( pi->id ) != curconf.FullpathTo( pi->id ) )
|
|
break;
|
|
} while( ++pi, pi->shortname != NULL );
|
|
|
|
bool isSuspended = false;
|
|
|
|
if( pi->shortname != NULL )
|
|
{
|
|
if( CoreThread.IsRunning() )
|
|
{
|
|
// [TODO] : Post notice that this shuts down existing emulation, and may not safely recover.
|
|
wxDialogWithHelpers dialog( this, _("Shutdown PS2 virtual machine?"), wxVERTICAL );
|
|
|
|
dialog += dialog.Heading( pxE( ".Popup:PluginSelector:ConfirmShutdown",
|
|
L"Warning! Changing plugins requires a complete shutdown and reset of the PS2 virtual machine. "
|
|
L"PCSX2 will attempt to save and restore the state, but if the newly selected plugins are "
|
|
L"incompatible the recovery may fail, and current progress will be lost."
|
|
L"\n\n"
|
|
L"Are you sure you want to apply settings now?"
|
|
) );
|
|
|
|
int result = pxIssueConfirmation( dialog, MsgButtons().OK().Cancel(), L"PluginSelector:ConfirmShutdown" );
|
|
|
|
if( result == wxID_CANCEL )
|
|
throw Exception::CannotApplySettings( this, "Cannot apply settings: canceled by user because plugins changed while the emulation state was active.", false );
|
|
|
|
// FIXME : We only actually have to save plugins here, except the recovery code
|
|
// in SysCoreThread isn't quite set up yet to handle that (I think...) --air
|
|
|
|
isSuspended = CoreThread.Suspend();
|
|
StateCopy_FreezeToMem_Blocking();
|
|
}
|
|
|
|
// Don't use SysShutdown, it clears the StateCopy.
|
|
CoreThread.Cancel();
|
|
wxGetApp().m_CorePlugins = NULL;
|
|
}
|
|
|
|
if( !wxGetApp().m_CorePlugins )
|
|
{
|
|
try {
|
|
LoadPluginsImmediate();
|
|
}
|
|
catch( Exception::PluginError& ex )
|
|
{
|
|
// Rethrow PluginLoadErrors as a failure to Apply...
|
|
|
|
wxString plugname( tbl_PluginInfo[ex.PluginId].GetShortname() );
|
|
|
|
throw Exception::CannotApplySettings( this,
|
|
// Diagnostic
|
|
ex.FormatDiagnosticMessage(),
|
|
|
|
// Translated
|
|
wxsFormat( _("The selected %s plugin failed to load.\n\nReason: %s\n\n"),
|
|
plugname.c_str(), ex.FormatDisplayMessage().c_str()
|
|
) + GetApplyFailedMsg()
|
|
);
|
|
}
|
|
}
|
|
|
|
if( isSuspended ) CoreThread.Resume();
|
|
}
|
|
|
|
void Panels::PluginSelectorPanel::CancelRefresh()
|
|
{
|
|
}
|
|
|
|
// This method is a callback from the BaseSelectorPanel. It is called when the page is shown
|
|
// and the page's enumerated selections are valid (meaning we should start our enumeration
|
|
// thread!)
|
|
void Panels::PluginSelectorPanel::DoRefresh()
|
|
{
|
|
m_ComponentBoxes->Reset();
|
|
if( !m_FileList )
|
|
{
|
|
wxCommandEvent evt;
|
|
OnEnumComplete( evt );
|
|
return;
|
|
}
|
|
|
|
// 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.
|
|
wxCommandEvent evt( pxEVT_ShowStatusBar );
|
|
GetEventHandler()->AddPendingEvent( evt );
|
|
|
|
m_EnumeratorThread.Delete() = new EnumThread( *this );
|
|
|
|
if( DisableThreading )
|
|
m_EnumeratorThread->DoNextPlugin( 0 );
|
|
else
|
|
m_EnumeratorThread->Start();
|
|
}
|
|
|
|
bool Panels::PluginSelectorPanel::ValidateEnumerationStatus()
|
|
{
|
|
if( m_EnumeratorThread ) return true; // Cant reset file lists while we're busy enumerating...
|
|
|
|
bool validated = true;
|
|
|
|
// re-enumerate plugins, and if anything changed then we need to wipe
|
|
// the contents of the combo boxes and re-enumerate everything.
|
|
|
|
// Impl Note: ScopedPtr used so that resources get cleaned up if an exception
|
|
// occurs during file enumeration.
|
|
ScopedPtr<wxArrayString> pluginlist( new wxArrayString() );
|
|
|
|
int pluggers = EnumeratePluginsInFolder( m_ComponentBoxes->GetPluginsPath(), pluginlist );
|
|
|
|
if( !m_FileList || (*pluginlist != *m_FileList) )
|
|
validated = false;
|
|
|
|
if( pluggers == 0 )
|
|
{
|
|
m_FileList = NULL;
|
|
return validated;
|
|
}
|
|
|
|
m_FileList.SwapPtr( pluginlist );
|
|
|
|
m_StatusPanel->SetGaugeLength( pluggers );
|
|
|
|
return validated;
|
|
}
|
|
|
|
void Panels::PluginSelectorPanel::OnConfigure_Clicked( wxCommandEvent& evt )
|
|
{
|
|
PluginsEnum_t pid = (PluginsEnum_t)(int)((wxEvtHandler*)evt.GetEventObject())->GetClientData();
|
|
|
|
int sel = m_ComponentBoxes->Get(pid).GetSelection();
|
|
if( sel == wxNOT_FOUND ) return;
|
|
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();
|
|
}
|
|
}
|
|
|
|
void Panels::PluginSelectorPanel::OnShowStatusBar( wxCommandEvent& evt )
|
|
{
|
|
m_StatusPanel->SetSize( m_ComponentBoxes->GetSize().GetWidth() - 8, wxDefaultCoord );
|
|
m_StatusPanel->CentreOnParent();
|
|
m_StatusPanel->Show();
|
|
}
|
|
|
|
void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt )
|
|
{
|
|
m_EnumeratorThread = NULL;
|
|
|
|
// fixme: Default plugins should be picked based on the timestamp of the DLL or something?
|
|
// (for now we just force it to selection zero if nothing's selected)
|
|
|
|
int emptyBoxes = 0;
|
|
for( int i=0; i<NumPluginTypes; ++i )
|
|
{
|
|
if( m_ComponentBoxes->Get(i).GetCount() <= 0 )
|
|
emptyBoxes++;
|
|
|
|
else if( m_ComponentBoxes->Get(i).GetSelection() == wxNOT_FOUND )
|
|
m_ComponentBoxes->Get(i).SetSelection( 0 );
|
|
}
|
|
|
|
m_ComponentBoxes->Show();
|
|
m_StatusPanel->Hide();
|
|
m_StatusPanel->Reset();
|
|
|
|
wxWindow* forwardButton = GetGrandParent()->FindWindow( wxID_FORWARD );
|
|
if( forwardButton != NULL )
|
|
forwardButton->Enable();
|
|
}
|
|
|
|
|
|
void Panels::PluginSelectorPanel::OnProgress( wxCommandEvent& evt )
|
|
{
|
|
if( !m_FileList ) return;
|
|
|
|
// The thread can get canceled and replaced with a new thread, which means all
|
|
// pending messages should be ignored.
|
|
if( m_EnumeratorThread != (EnumThread*)evt.GetClientData() ) return;
|
|
|
|
const size_t evtidx = evt.GetExtraLong();
|
|
|
|
if( DisableThreading )
|
|
{
|
|
const int nextidx = evtidx+1;
|
|
if( nextidx == m_FileList->Count() )
|
|
{
|
|
wxCommandEvent done( pxEVT_EnumerationFinished );
|
|
GetEventHandler()->AddPendingEvent( done );
|
|
}
|
|
else
|
|
m_EnumeratorThread->DoNextPlugin( nextidx );
|
|
}
|
|
|
|
m_StatusPanel->AdvanceProgress( (evtidx < m_FileList->Count()-1) ?
|
|
(*m_FileList)[evtidx + 1] : wxString(_("Completing tasks..."))
|
|
);
|
|
|
|
EnumeratedPluginInfo& result( m_EnumeratorThread->Results[evtidx] );
|
|
|
|
if( result.TypeMask == 0 )
|
|
{
|
|
Console.Error( L"Some kinda plugin failure: " + (*m_FileList)[evtidx] );
|
|
}
|
|
|
|
for( int i=0; i<NumPluginTypes; ++i )
|
|
{
|
|
if( result.TypeMask & tbl_PluginInfo[i].typemask )
|
|
{
|
|
if( result.PassedTest & tbl_PluginInfo[i].typemask )
|
|
{
|
|
int sel = m_ComponentBoxes->Get(i).Append( wxsFormat( L"%s %s [%s]",
|
|
result.Name.c_str(), result.Version[i].c_str(), Path::GetFilenameWithoutExt( (*m_FileList)[evtidx] ).c_str() ),
|
|
(void*)evtidx
|
|
);
|
|
|
|
wxFileName left( (*m_FileList)[evtidx] );
|
|
wxFileName right( g_Conf->FullpathTo(tbl_PluginInfo[i].id) );
|
|
|
|
left.MakeAbsolute();
|
|
right.MakeAbsolute();
|
|
|
|
if( left == right )
|
|
m_ComponentBoxes->Get(i).SetSelection( sel );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// EnumThread method implementations
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
Panels::PluginSelectorPanel::EnumThread::EnumThread( PluginSelectorPanel& master )
|
|
: PersistentThread()
|
|
, Results( master.FileCount(), L"PluginSelectorResults" )
|
|
, m_master( master )
|
|
, m_hourglass( Cursor_KindaBusy )
|
|
{
|
|
Results.MatchLengthToAllocatedSize();
|
|
}
|
|
|
|
void Panels::PluginSelectorPanel::EnumThread::DoNextPlugin( int curidx )
|
|
{
|
|
DbgCon.Indent().WriteLn( L"Enumerating Plugin: " + m_master.GetFilename( curidx ) );
|
|
|
|
try
|
|
{
|
|
EnumeratedPluginInfo& result( Results[curidx] );
|
|
result.TypeMask = 0;
|
|
|
|
PluginEnumerator penum( m_master.GetFilename( curidx ) );
|
|
|
|
result.Name = penum.GetName();
|
|
for( int pidx=0; pidx<PluginId_Count; ++pidx )
|
|
{
|
|
const PluginsEnum_t pid = (PluginsEnum_t)pidx;
|
|
result.TypeMask |= tbl_PluginInfo[pid].typemask;
|
|
if( penum.CheckVersion( pid ) )
|
|
{
|
|
result.PassedTest |= tbl_PluginInfo[pid].typemask;
|
|
penum.GetVersionString( result.Version[pid], pidx );
|
|
}
|
|
}
|
|
}
|
|
catch( Exception::BadStream& ex )
|
|
{
|
|
Console.Warning( ex.FormatDiagnosticMessage() );
|
|
}
|
|
|
|
wxCommandEvent yay( pxEVT_EnumeratedNext );
|
|
yay.SetClientData( this );
|
|
yay.SetExtraLong( curidx );
|
|
m_master.GetEventHandler()->AddPendingEvent( yay );
|
|
}
|
|
|
|
void Panels::PluginSelectorPanel::EnumThread::ExecuteTaskInThread()
|
|
{
|
|
DevCon.WriteLn( "Plugin Enumeration Thread started..." );
|
|
|
|
Sleep( 15 ); // give the window some time to paint.
|
|
YieldToMain();
|
|
|
|
for( int curidx=0; curidx < m_master.FileCount(); ++curidx )
|
|
{
|
|
DoNextPlugin( curidx );
|
|
|
|
// speed isn't critical here, but the pretty status bar sure is.
|
|
// second try yield should give the status bars UI a "good" chance to refresh before we advance. :)
|
|
|
|
YieldToMain();
|
|
YieldToMain();
|
|
//Sleep(150); // uncomment this to slow down the selector, for debugging threading.
|
|
}
|
|
|
|
wxCommandEvent done( pxEVT_EnumerationFinished );
|
|
done.SetClientData( this );
|
|
m_master.GetEventHandler()->AddPendingEvent( done );
|
|
|
|
DevCon.WriteLn( "Plugin Enumeration Thread complete!" );
|
|
}
|