/[pcsx2_0.9.7]/trunk/pcsx2/gui/Panels/PluginSelectorPanel.cpp
ViewVC logotype

Contents of /trunk/pcsx2/gui/Panels/PluginSelectorPanel.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 280 - (show annotations) (download)
Thu Dec 23 12:02:12 2010 UTC (9 years, 2 months ago) by william
File size: 26748 byte(s)
re-commit (had local access denied errors when committing)
1 /* PCSX2 - PS2 Emulator for PCs
2 * Copyright (C) 2002-2010 PCSX2 Dev Team
3 *
4 * PCSX2 is free software: you can redistribute it and/or modify it under the terms
5 * of the GNU Lesser General Public License as published by the Free Software Found-
6 * ation, either version 3 of the License, or (at your option) any later version.
7 *
8 * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10 * PURPOSE. See the GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along with PCSX2.
13 * If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include "PrecompiledHeader.h"
17
18 #include <wx/dynlib.h>
19 #include <wx/dir.h>
20
21 #include "App.h"
22 #include "AppSaveStates.h"
23 #include "Plugins.h"
24 #include "ConfigurationPanels.h"
25
26 #include "Dialogs/ModalPopups.h"
27
28 #include "Utilities/ThreadingDialogs.h"
29 #include "Utilities/SafeArray.inl"
30
31 // Allows us to force-disable threading for debugging/troubleshooting
32 static const bool DisableThreading =
33 #ifdef __LINUX__
34 true; // linux appears to have threading issues with loadlibrary.
35 #else
36 false;
37 #endif
38
39 using namespace pxSizerFlags;
40 using namespace Threading;
41
42 BEGIN_DECLARE_EVENT_TYPES()
43 DECLARE_EVENT_TYPE(pxEvt_EnumeratedNext, -1)
44 DECLARE_EVENT_TYPE(pxEvt_EnumerationFinished, -1)
45 DECLARE_EVENT_TYPE(pxEVT_ShowStatusBar, -1)
46 END_DECLARE_EVENT_TYPES()
47
48 DEFINE_EVENT_TYPE(pxEvt_EnumeratedNext);
49 DEFINE_EVENT_TYPE(pxEvt_EnumerationFinished);
50 DEFINE_EVENT_TYPE(pxEVT_ShowStatusBar);
51
52 typedef s32 (CALLBACK* TestFnptr)();
53 typedef void (CALLBACK* ConfigureFnptr)();
54
55 static const wxString failed_separator( L"-------- Unsupported Plugins --------" );
56
57 namespace Exception
58 {
59 class NotEnumerablePlugin : public BadStream
60 {
61 public:
62 DEFINE_STREAM_EXCEPTION( NotEnumerablePlugin, BadStream );
63
64 wxString FormatDiagnosticMessage() const
65 {
66 FastFormatUnicode retval;
67 retval.Write("File is not a PCSX2 plugin");
68 _formatDiagMsg(retval);
69 return retval;
70 }
71 };
72 }
73
74 // --------------------------------------------------------------------------------------
75 // PluginEnumerator class
76 // --------------------------------------------------------------------------------------
77 class PluginEnumerator
78 {
79 protected:
80 wxString m_plugpath;
81 wxDynamicLibrary m_plugin;
82
83 _PS2EgetLibType m_GetLibType;
84 _PS2EgetLibName m_GetLibName;
85 _PS2EgetLibVersion2 m_GetLibVersion2;
86
87 u32 m_type;
88
89 public:
90
91 // Constructor!
92 //
93 // Possible Exceptions:
94 // BadStream - thrown if the provided file is simply not a loadable DLL.
95 // NotEnumerablePlugin - thrown if the DLL is not a PCSX2 plugin, or if it's of an unsupported version.
96 //
97 PluginEnumerator( const wxString& plugpath )
98 : m_plugpath( plugpath )
99 {
100 if( !m_plugin.Load( m_plugpath ) )
101 throw Exception::BadStream( m_plugpath ).SetBothMsgs(L"File is not a valid dynamic library.");
102
103 wxDoNotLogInThisScope please;
104 m_GetLibType = (_PS2EgetLibType)m_plugin.GetSymbol( L"PS2EgetLibType" );
105 m_GetLibName = (_PS2EgetLibName)m_plugin.GetSymbol( L"PS2EgetLibName" );
106 m_GetLibVersion2 = (_PS2EgetLibVersion2)m_plugin.GetSymbol( L"PS2EgetLibVersion2" );
107
108 if( m_GetLibType == NULL || m_GetLibName == NULL || m_GetLibVersion2 == NULL)
109 throw Exception::NotEnumerablePlugin( m_plugpath );
110
111 m_type = m_GetLibType();
112 }
113
114 bool CheckVersion( PluginsEnum_t pluginTypeIndex ) const
115 {
116 const PluginInfo& info( tbl_PluginInfo[pluginTypeIndex] );
117 if( m_type & info.typemask )
118 {
119 int version = m_GetLibVersion2( info.typemask );
120 if ( ((version >> 16)&0xff) == tbl_PluginInfo[pluginTypeIndex].version )
121 return true;
122
123 Console.Warning("%s Plugin %s: Version %x != %x", info.shortname, m_plugpath.c_str(), 0xff&(version >> 16), info.version);
124 }
125 return false;
126 }
127
128 bool Test( int pluginTypeIndex ) const
129 {
130 // all test functions use the same parameterless API, so just pick one arbitrarily (I pick PAD!)
131 TestFnptr testfunc = (TestFnptr)m_plugin.GetSymbol( fromUTF8( tbl_PluginInfo[pluginTypeIndex].shortname ) + L"test" );
132 if( testfunc == NULL ) return false;
133 return (testfunc() == 0);
134 }
135
136 wxString GetName() const
137 {
138 pxAssert( m_GetLibName != NULL );
139 return fromUTF8(m_GetLibName());
140 }
141
142 void GetVersionString( wxString& dest, int pluginTypeIndex ) const
143 {
144 const PluginInfo& info( tbl_PluginInfo[pluginTypeIndex] );
145 int version = m_GetLibVersion2( info.typemask );
146 dest.Printf( L"%d.%d.%d", (version>>8)&0xff, version&0xff, (version>>24)&0xff );
147 }
148 };
149
150 // --------------------------------------------------------------------------------------
151 // ApplyPluginsDialog
152 // --------------------------------------------------------------------------------------
153 class ApplyPluginsDialog : public WaitForTaskDialog
154 {
155 DECLARE_DYNAMIC_CLASS_NO_COPY(ApplyPluginsDialog)
156
157 typedef wxDialogWithHelpers _parent;
158
159 protected:
160 BaseApplicableConfigPanel* m_panel;
161
162 public:
163 ApplyPluginsDialog( BaseApplicableConfigPanel* panel=NULL );
164 virtual ~ApplyPluginsDialog() throw() {}
165
166 BaseApplicableConfigPanel* GetApplicableConfigPanel() const { return m_panel; }
167 };
168
169
170 // --------------------------------------------------------------------------------------
171 // ApplyOverValidStateEvent
172 // --------------------------------------------------------------------------------------
173 class ApplyOverValidStateEvent : public pxActionEvent
174 {
175 //DeclareNoncopyableObject( ApplyOverValidStateEvent );
176 typedef pxActionEvent _parent;
177
178 protected:
179 ApplyPluginsDialog* m_owner;
180
181 public:
182 ApplyOverValidStateEvent( ApplyPluginsDialog* owner=NULL )
183 {
184 m_owner = owner;
185 }
186
187 virtual ~ApplyOverValidStateEvent() throw() { }
188 virtual ApplyOverValidStateEvent *Clone() const { return new ApplyOverValidStateEvent(*this); }
189
190 protected:
191 void InvokeEvent();
192 };
193
194
195 // --------------------------------------------------------------------------------------
196 // SysExecEvent_ApplyPlugins
197 // --------------------------------------------------------------------------------------
198 class SysExecEvent_ApplyPlugins : public SysExecEvent
199 {
200 typedef SysExecEvent _parent;
201
202 protected:
203 ApplyPluginsDialog* m_dialog;
204
205 public:
206 wxString GetEventName() const { return L"PluginSelectorPanel::ApplyPlugins"; }
207
208 virtual ~SysExecEvent_ApplyPlugins() throw() {}
209 SysExecEvent_ApplyPlugins* Clone() const { return new SysExecEvent_ApplyPlugins( *this ); }
210
211 SysExecEvent_ApplyPlugins( ApplyPluginsDialog* parent, SynchronousActionState& sync )
212 : SysExecEvent( &sync )
213 {
214 m_dialog = parent;
215 }
216
217 protected:
218 void InvokeEvent();
219 void CleanupEvent();
220
221 void PostFinishToDialog();
222 };
223
224 // --------------------------------------------------------------------------------------
225 // ApplyPluginsDialog Implementations
226 // --------------------------------------------------------------------------------------
227 IMPLEMENT_DYNAMIC_CLASS(ApplyPluginsDialog, WaitForTaskDialog)
228
229 ApplyPluginsDialog::ApplyPluginsDialog( BaseApplicableConfigPanel* panel )
230 : WaitForTaskDialog( _("Applying settings...") )
231 {
232 GetSysExecutorThread().PostEvent( new SysExecEvent_ApplyPlugins( this, m_sync ) );
233 }
234
235 // --------------------------------------------------------------------------------------
236 // ApplyOverValidStateEvent Implementations
237 // --------------------------------------------------------------------------------------
238 void ApplyOverValidStateEvent::InvokeEvent()
239 {
240 wxDialogWithHelpers dialog( m_owner, _("Shutdown PS2 virtual machine?") );
241
242 dialog += dialog.Heading( pxE( "!Notice:PluginSelector:ConfirmShutdown",
243 L"Warning! Changing plugins requires a complete shutdown and reset of the PS2 virtual machine. "
244 L"PCSX2 will attempt to save and restore the state, but if the newly selected plugins are "
245 L"incompatible the recovery may fail, and current progress will be lost."
246 L"\n\n"
247 L"Are you sure you want to apply settings now?"
248 ) );
249
250 int result = pxIssueConfirmation( dialog, MsgButtons().OK().Cancel(), L"PluginSelector.ConfirmShutdown" );
251
252 if( result == wxID_CANCEL )
253 throw Exception::CannotApplySettings( m_owner->GetApplicableConfigPanel() ).Quiet()
254 .SetDiagMsg(L"Cannot apply settings: canceled by user because plugins changed while the emulation state was active.");
255 }
256
257 // --------------------------------------------------------------------------------------
258 // SysExecEvent_ApplyPlugins Implementations
259 // --------------------------------------------------------------------------------------
260 void SysExecEvent_ApplyPlugins::InvokeEvent()
261 {
262 ScopedCoreThreadPause paused_core;
263
264 ScopedPtr< VmStateBuffer > buffer;
265
266 if( SysHasValidState() )
267 {
268 paused_core.AllowResume();
269 ApplyOverValidStateEvent aEvt( m_dialog );
270 wxGetApp().ProcessEvent( aEvt );
271 paused_core.DisallowResume();
272
273 // FIXME : We only actually have to save plugins here, except the recovery code
274 // in SysCoreThread isn't quite set up yet to handle that (I think...) --air
275
276 memSavingState( *(buffer.Reassign(new VmStateBuffer(L"StateBuffer_ApplyNewPlugins"))) ).FreezeAll();
277 }
278
279 ScopedCoreThreadClose closed_core;
280
281 CorePlugins.Shutdown();
282 CorePlugins.Unload();
283 LoadPluginsImmediate();
284 CorePlugins.Init();
285
286 if( buffer ) CoreThread.UploadStateCopy( *buffer );
287
288 PostFinishToDialog();
289
290 closed_core.AllowResume();
291 paused_core.AllowResume();
292 }
293
294 void SysExecEvent_ApplyPlugins::PostFinishToDialog()
295 {
296 if( !m_dialog ) return;
297 wxCommandEvent tevt( pxEvt_ThreadedTaskComplete );
298 m_dialog->GetEventHandler()->AddPendingEvent( tevt );
299 m_dialog = NULL;
300 }
301
302 void SysExecEvent_ApplyPlugins::CleanupEvent()
303 {
304 PostFinishToDialog();
305 _parent::CleanupEvent();
306 }
307
308 // --------------------------------------------------------------------------------------
309 // PluginSelectorPanel::StatusPanel implementations
310 // --------------------------------------------------------------------------------------
311
312 Panels::PluginSelectorPanel::StatusPanel::StatusPanel( wxWindow* parent )
313 : wxPanelWithHelpers( parent, wxVERTICAL )
314 , m_gauge( *new wxGauge( this, wxID_ANY, 10 ) )
315 , m_label( *new wxStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE | wxST_NO_AUTORESIZE ) )
316 {
317 m_progress = 0;
318
319 m_gauge.SetToolTip( _("I'm givin' her all she's got, Captain!") );
320
321 *this += Heading(_( "Enumerating available plugins..." )).Bold() | StdExpand();
322 *this += m_gauge | pxExpand.Border( wxLEFT | wxRIGHT, 32 );
323 *this += m_label | StdExpand();
324
325 Fit();
326 }
327
328 void Panels::PluginSelectorPanel::StatusPanel::SetGaugeLength( int len )
329 {
330 m_gauge.SetRange( len );
331 }
332
333 void Panels::PluginSelectorPanel::StatusPanel::AdvanceProgress( const wxString& msg )
334 {
335 m_label.SetLabel( msg );
336 m_gauge.SetValue( ++m_progress );
337 }
338
339 void Panels::PluginSelectorPanel::StatusPanel::Reset()
340 {
341 m_gauge.SetValue( m_progress = 0 );
342 m_label.SetLabel( wxEmptyString );
343 }
344
345 // Id for all Configure buttons (any non-negative arbitrary integer will do)
346 static const int ButtonId_Configure = 51;
347
348 // --------------------------------------------------------------------------------------
349 // PluginSelectorPanel::ComboBoxPanel implementations
350 // --------------------------------------------------------------------------------------
351 Panels::PluginSelectorPanel::ComboBoxPanel::ComboBoxPanel( PluginSelectorPanel* parent )
352 : wxPanelWithHelpers( parent, wxVERTICAL )
353 , m_FolderPicker( *new DirPickerPanel( this, FolderId_Plugins,
354 _("Plugins Search Path:"),
355 _("Select a folder with PCSX2 plugins") )
356 )
357 {
358 wxFlexGridSizer& s_plugin( *new wxFlexGridSizer( PluginId_Count, 3, 16, 10 ) );
359 s_plugin.SetFlexibleDirection( wxHORIZONTAL );
360 s_plugin.AddGrowableCol( 1 ); // expands combo boxes to full width.
361
362 const PluginInfo* pi = tbl_PluginInfo; do
363 {
364 const PluginsEnum_t pid = pi->id;
365
366 m_combobox[pid] = new wxComboBox( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY );
367
368 m_configbutton[pid] = new wxButton( this, ButtonId_Configure, L"Configure..." );
369 m_configbutton[pid]->SetClientData( (void*)(int)pid );
370
371 s_plugin += Label( pi->GetShortname() ) | pxBorder( wxTOP | wxLEFT, 2 );
372 s_plugin += m_combobox[pid] | pxExpand;
373 s_plugin += m_configbutton[pid];
374 } while( ++pi, pi->shortname != NULL );
375
376 m_FolderPicker.SetStaticDesc( _("Click the Browse button to select a different folder for PCSX2 plugins.") );
377
378 *this += s_plugin | pxExpand;
379 *this += 6;
380 *this += m_FolderPicker | StdExpand();
381 }
382
383 void Panels::PluginSelectorPanel::ComboBoxPanel::Reset()
384 {
385 for( int i=0; i<PluginId_Count; ++i )
386 {
387 m_combobox[i]->Clear();
388 m_combobox[i]->SetSelection( wxNOT_FOUND );
389 m_combobox[i]->SetValue( wxEmptyString );
390
391 m_configbutton[i]->Disable();
392 }
393 }
394
395 void Panels::PluginSelectorPanel::DispatchEvent( const PluginEventType& evt )
396 {
397 if( (evt != CorePlugins_Loaded) && (evt != CorePlugins_Unloaded) ) return; // everything else we don't care about
398
399 if( IsBeingDeleted() ) return;
400
401 const PluginInfo* pi = tbl_PluginInfo; do
402 {
403 wxComboBox& box( m_ComponentBoxes->Get(pi->id) );
404 int sel = box.GetSelection();
405 if( sel == wxNOT_FOUND ) continue;
406
407 m_ComponentBoxes->GetConfigButton(pi->id).Enable(
408 (m_FileList==NULL || m_FileList->Count() == 0) ? false :
409 g_Conf->FullpathMatchTest( pi->id,(*m_FileList)[((int)box.GetClientData(sel))] )
410 );
411 } while( ++pi, pi->shortname != NULL );
412
413 }
414
415 Panels::PluginSelectorPanel::PluginSelectorPanel( wxWindow* parent )
416 : BaseSelectorPanel( parent )
417 {
418 m_StatusPanel = new StatusPanel( this );
419 m_ComponentBoxes = new ComboBoxPanel( this );
420
421 // note: the status panel is a floating window, so that it can be positioned in the
422 // center of the dialog after it's been fitted to the contents.
423
424 *this += m_ComponentBoxes | StdExpand().ReserveSpaceEvenIfHidden();
425
426 m_StatusPanel->Hide();
427 m_ComponentBoxes->Hide();
428
429 // refresh button used for diagnostics... (don't think there's a point to having one otherwise) --air
430 //wxButton* refresh = new wxButton( this, wxID_ANY, L"Refresh" );
431 //s_main.Add( refresh );
432 //Connect( refresh->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PluginSelectorPanel::OnRefresh ) );
433
434 Connect( pxEvt_EnumeratedNext, wxCommandEventHandler( PluginSelectorPanel::OnProgress ) );
435 Connect( pxEvt_EnumerationFinished, wxCommandEventHandler( PluginSelectorPanel::OnEnumComplete ) );
436 Connect( pxEVT_ShowStatusBar, wxCommandEventHandler( PluginSelectorPanel::OnShowStatusBar ) );
437 Connect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( PluginSelectorPanel::OnPluginSelected ) );
438
439 Connect( ButtonId_Configure, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PluginSelectorPanel::OnConfigure_Clicked ) );
440
441 AppStatusEvent_OnSettingsApplied();
442 }
443
444 Panels::PluginSelectorPanel::~PluginSelectorPanel() throw()
445 {
446 CancelRefresh(); // in case the enumeration thread is currently refreshing...
447 }
448
449 void Panels::PluginSelectorPanel::AppStatusEvent_OnSettingsApplied()
450 {
451 m_ComponentBoxes->GetDirPicker().Reset();
452 }
453
454 static wxString GetApplyFailedMsg()
455 {
456 return wxsFormat( pxE( "!Notice:PluginSelector:ApplyFailed",
457 L"All plugins must have valid selections for %s to run. If you are unable to make "
458 L"a valid selection due to missing plugins or an incomplete install of %s, then "
459 L"press cancel to close the Configuration panel."
460 ), pxGetAppName().c_str(), pxGetAppName().c_str() );
461 }
462
463 void Panels::PluginSelectorPanel::Apply()
464 {
465 // user never entered plugins panel? Skip application since combo boxes are invalid/uninitialized.
466 if( !m_FileList ) return;
467
468 AppConfig curconf( *g_Conf );
469
470 const PluginInfo* pi = tbl_PluginInfo; do
471 {
472 const PluginsEnum_t pid = pi->id;
473 int sel = m_ComponentBoxes->Get(pid).GetSelection();
474 if( sel == wxNOT_FOUND )
475 {
476 wxString plugname( pi->GetShortname() );
477
478 throw Exception::CannotApplySettings( this )
479 .SetDiagMsg(wxsFormat( L"PluginSelectorPanel: Invalid or missing selection for the %s plugin.", plugname.c_str()) )
480 .SetUserMsg(wxsFormat( L"Please select a valid plugin for the %s.", plugname.c_str() ) + L"\n\n" + GetApplyFailedMsg() );
481 }
482
483 g_Conf->BaseFilenames.Plugins[pid] = GetFilename((int)m_ComponentBoxes->Get(pid).GetClientData(sel));
484 } while( ++pi, pi->shortname != NULL );
485
486 // ----------------------------------------------------------------------------
487 // Make sure folders are up to date, and try to load/reload plugins if needed...
488
489 g_Conf->Folders.ApplyDefaults();
490
491 // Need to unload the current emulation state if the user changed plugins, because
492 // the whole plugin system needs to be re-loaded.
493
494 pi = tbl_PluginInfo; do {
495 if( g_Conf->FullpathTo( pi->id ) != curconf.FullpathTo( pi->id ) )
496 break;
497 } while( ++pi, pi->shortname != NULL );
498
499 if( pi->shortname == NULL ) return; // no plugins changed? nothing left to do!
500
501 // ----------------------------------------------------------------------------
502 // Plugin names are not up-to-date -- RELOAD!
503
504 try
505 {
506 if( wxID_CANCEL == ApplyPluginsDialog( this ).ShowModal() )
507 throw Exception::CannotApplySettings( this ).Quiet().SetDiagMsg(L"User canceled plugin load process.");
508 }
509 catch( Exception::PluginError& ex )
510 {
511 // Rethrow PluginLoadErrors as a failure to Apply...
512
513 wxString plugname( tbl_PluginInfo[ex.PluginId].GetShortname() );
514
515 throw Exception::CannotApplySettings( this )
516 .SetDiagMsg(ex.FormatDiagnosticMessage())
517 .SetUserMsg(wxsFormat(
518 _("The selected %s plugin failed to load.\n\nReason: %s\n\n"),
519 plugname.c_str(), ex.FormatDisplayMessage().c_str()
520 ) + GetApplyFailedMsg());
521 }
522 }
523
524 void Panels::PluginSelectorPanel::CancelRefresh()
525 {
526 }
527
528 // This method is a callback from the BaseSelectorPanel. It is called when the page is shown
529 // and the page's enumerated selections are valid (meaning we should start our enumeration
530 // thread!)
531 void Panels::PluginSelectorPanel::DoRefresh()
532 {
533 m_ComponentBoxes->Reset();
534 if( !m_FileList )
535 {
536 wxCommandEvent evt;
537 OnEnumComplete( evt );
538 return;
539 }
540
541 // Disable all controls until enumeration is complete
542 m_ComponentBoxes->Hide();
543
544 // (including next button if it's a Wizard)
545 wxWindow* forwardButton = GetGrandParent()->FindWindow( wxID_FORWARD );
546 if( forwardButton != NULL )
547 forwardButton->Disable();
548
549 // Show status bar for plugin enumeration. Use a pending event so that
550 // the window's size can get initialized properly before trying to custom-
551 // fit the status panel to it.
552 wxCommandEvent evt( pxEVT_ShowStatusBar );
553 GetEventHandler()->AddPendingEvent( evt );
554
555 m_EnumeratorThread.Delete() = new EnumThread( *this );
556
557 if( DisableThreading )
558 m_EnumeratorThread->DoNextPlugin( 0 );
559 else
560 m_EnumeratorThread->Start();
561 }
562
563 bool Panels::PluginSelectorPanel::ValidateEnumerationStatus()
564 {
565 if( m_EnumeratorThread ) return true; // Cant reset file lists while we're busy enumerating...
566
567 bool validated = true;
568
569 // re-enumerate plugins, and if anything changed then we need to wipe
570 // the contents of the combo boxes and re-enumerate everything.
571
572 // Impl Note: ScopedPtr used so that resources get cleaned up if an exception
573 // occurs during file enumeration.
574 ScopedPtr<wxArrayString> pluginlist( new wxArrayString() );
575
576 int pluggers = EnumeratePluginsInFolder( m_ComponentBoxes->GetPluginsPath(), pluginlist );
577
578 if( !m_FileList || (*pluginlist != *m_FileList) )
579 validated = false;
580
581 if( pluggers == 0 )
582 {
583 m_FileList = NULL;
584 return validated;
585 }
586
587 m_FileList.SwapPtr( pluginlist );
588
589 // set the gague length a little shorter than the plugin count. 2 reasons:
590 // * some of the plugins might be duds.
591 // * on high end machines and Win7, the statusbar lags a lot and never gets to 100% before being hidden.
592
593 m_StatusPanel->SetGaugeLength( std::max( 1, (pluggers-1) - (pluggers/8) ) );
594
595 return validated;
596 }
597
598 void Panels::PluginSelectorPanel::OnPluginSelected( wxCommandEvent& evt )
599 {
600 if( IsBeingDeleted() || m_ComponentBoxes->IsBeingDeleted() ) return;
601
602 const PluginInfo* pi = tbl_PluginInfo; do
603 {
604 wxComboBox& box( m_ComponentBoxes->Get(pi->id) );
605 if( box.GetId() == evt.GetId() )
606 {
607 // Button is enabled if:
608 // (a) plugins aren't even loaded yet.
609 // (b) current selection matches exactly the currently configured/loaded plugin.
610
611 bool isSame = (!CorePlugins.AreLoaded()) || g_Conf->FullpathMatchTest( pi->id, (*m_FileList)[(int)box.GetClientData(box.GetSelection())] );
612 m_ComponentBoxes->GetConfigButton( pi->id ).Enable( isSame );
613
614 if( !isSame ) evt.Skip(); // enabled Apply button! :D
615 return;
616 }
617 } while( ++pi, pi->shortname != NULL );
618 }
619
620 void Panels::PluginSelectorPanel::OnConfigure_Clicked( wxCommandEvent& evt )
621 {
622 if( IsBeingDeleted() ) return;
623
624 PluginsEnum_t pid = (PluginsEnum_t)(int)((wxEvtHandler*)evt.GetEventObject())->GetClientData();
625
626 int sel = m_ComponentBoxes->Get(pid).GetSelection();
627 if( sel == wxNOT_FOUND ) return;
628
629 // Only allow configuration if the selected plugin matches exactly our currently loaded one.
630 // Otherwise who knows what sort of funny business could happen configuring a plugin while
631 // another instance/version is running. >_<
632
633 const wxString filename( (*m_FileList)[(int)m_ComponentBoxes->Get(pid).GetClientData(sel)] );
634
635 if( CorePlugins.AreLoaded() && !g_Conf->FullpathMatchTest( pid, filename ) )
636 {
637 Console.Warning( "(PluginSelector) Plugin name mismatch, configuration request ignored." );
638 return;
639 }
640
641 wxDynamicLibrary dynlib( filename );
642
643 if( ConfigureFnptr configfunc = (ConfigureFnptr)dynlib.GetSymbol( tbl_PluginInfo[pid].GetShortname() + L"configure" ) )
644 {
645
646 wxWindowDisabler disabler;
647 ScopedCoreThreadPause paused_core( new SysExecEvent_SaveSinglePlugin(pid) );
648 if (!CorePlugins.AreLoaded())
649 {
650 typedef void (CALLBACK* SetDirFnptr)( const char* dir );
651
652 if( SetDirFnptr func = (SetDirFnptr)dynlib.GetSymbol( tbl_PluginInfo[pid].GetShortname() + L"setSettingsDir" ) )
653 {
654 func( GetSettingsFolder().ToUTF8() );
655 }
656
657 if( SetDirFnptr func = (SetDirFnptr)dynlib.GetSymbol( tbl_PluginInfo[pid].GetShortname() + L"setLogDir" ) )
658 {
659 func( GetLogFolder().ToUTF8() );
660 }
661 }
662
663 configfunc();
664 }
665 }
666
667 void Panels::PluginSelectorPanel::OnShowStatusBar( wxCommandEvent& evt )
668 {
669 m_StatusPanel->SetSize( m_ComponentBoxes->GetSize().GetWidth() - 8, wxDefaultCoord );
670 m_StatusPanel->CentreOnParent();
671 m_StatusPanel->Show();
672 }
673
674 void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt )
675 {
676 m_EnumeratorThread = NULL;
677
678 // fixme: Default plugins should be picked based on the timestamp of the DLL or something?
679 // (for now we just force it to selection zero if nothing's selected)
680
681 int emptyBoxes = 0;
682 const PluginInfo* pi = tbl_PluginInfo; do
683 {
684 const PluginsEnum_t pid = pi->id;
685 if( m_ComponentBoxes->Get(pid).GetCount() <= 0 )
686 emptyBoxes++;
687
688 else if( m_ComponentBoxes->Get(pid).GetSelection() == wxNOT_FOUND )
689 {
690 m_ComponentBoxes->Get(pid).SetSelection( 0 );
691 m_ComponentBoxes->GetConfigButton(pid).Enable( !CorePlugins.AreLoaded() );
692 }
693 } while( ++pi, pi->shortname != NULL );
694
695 m_ComponentBoxes->Show();
696 m_StatusPanel->Hide();
697 m_StatusPanel->Reset();
698
699 wxWindow* forwardButton = GetGrandParent()->FindWindow( wxID_FORWARD );
700 if( forwardButton != NULL )
701 forwardButton->Enable();
702 }
703
704
705 void Panels::PluginSelectorPanel::OnProgress( wxCommandEvent& evt )
706 {
707 if( !m_FileList ) return;
708
709 // The thread can get canceled and replaced with a new thread, which means all
710 // pending messages should be ignored.
711 if( m_EnumeratorThread != (EnumThread*)evt.GetClientData() ) return;
712
713 const size_t evtidx = evt.GetExtraLong();
714
715 if( DisableThreading )
716 {
717 const int nextidx = evtidx+1;
718 if( nextidx == m_FileList->Count() )
719 {
720 wxCommandEvent done( pxEvt_EnumerationFinished );
721 GetEventHandler()->AddPendingEvent( done );
722 }
723 else
724 m_EnumeratorThread->DoNextPlugin( nextidx );
725 }
726
727 m_StatusPanel->AdvanceProgress( (evtidx < m_FileList->Count()-1) ?
728 (*m_FileList)[evtidx + 1] : wxString(_("Completing tasks..."))
729 );
730
731 EnumeratedPluginInfo& result( m_EnumeratorThread->Results[evtidx] );
732
733 if( result.TypeMask == 0 )
734 {
735 Console.Error( L"Some kinda plugin failure: " + (*m_FileList)[evtidx] );
736 }
737
738 const PluginInfo* pi = tbl_PluginInfo; do
739 {
740 const PluginsEnum_t pid = pi->id;
741 if( result.TypeMask & pi->typemask )
742 {
743 if( result.PassedTest & pi->typemask )
744 {
745 int sel = m_ComponentBoxes->Get(pid).Append( wxsFormat( L"%s %s [%s]",
746 result.Name.c_str(), result.Version[pid].c_str(), Path::GetFilenameWithoutExt( (*m_FileList)[evtidx] ).c_str() ),
747 (void*)evtidx
748 );
749
750 if( g_Conf->FullpathMatchTest( pid, (*m_FileList)[evtidx] ) )
751 {
752 m_ComponentBoxes->Get(pid).SetSelection( sel );
753 m_ComponentBoxes->GetConfigButton(pid).Enable();
754 }
755 }
756 }
757 } while( ++pi, pi->shortname != NULL );
758 }
759
760
761 // --------------------------------------------------------------------------------------
762 // EnumThread method implementations
763 // --------------------------------------------------------------------------------------
764
765 Panels::PluginSelectorPanel::EnumThread::EnumThread( PluginSelectorPanel& master )
766 : pxThread()
767 , Results( master.FileCount(), L"PluginSelectorResults" )
768 , m_master( master )
769 , m_hourglass( Cursor_KindaBusy )
770 {
771 Results.MatchLengthToAllocatedSize();
772 }
773
774 void Panels::PluginSelectorPanel::EnumThread::DoNextPlugin( int curidx )
775 {
776 DbgCon.Indent().WriteLn( L"Plugin: " + m_master.GetFilename( curidx ) );
777
778 try
779 {
780 EnumeratedPluginInfo& result( Results[curidx] );
781 result.TypeMask = 0;
782
783 PluginEnumerator penum( m_master.GetFilename( curidx ) );
784
785 result.Name = penum.GetName();
786 const PluginInfo* pi = tbl_PluginInfo; do
787 {
788 const PluginsEnum_t pid = pi->id;
789 result.TypeMask |= pi->typemask;
790 if( penum.CheckVersion( pid ) )
791 {
792 result.PassedTest |= tbl_PluginInfo[pid].typemask;
793 penum.GetVersionString( result.Version[pid], pid );
794 }
795 } while( ++pi, pi->shortname != NULL );
796 }
797 catch( Exception::BadStream& ex )
798 {
799 Console.Warning( ex.FormatDiagnosticMessage() );
800 }
801
802 wxCommandEvent yay( pxEvt_EnumeratedNext );
803 yay.SetClientData( this );
804 yay.SetExtraLong( curidx );
805 m_master.GetEventHandler()->AddPendingEvent( yay );
806 }
807
808 void Panels::PluginSelectorPanel::EnumThread::ExecuteTaskInThread()
809 {
810 DevCon.WriteLn( "Plugin Enumeration Thread started..." );
811
812 wxGetApp().Ping();
813 Sleep( 2 );
814
815 for( int curidx=0; curidx < m_master.FileCount(); ++curidx )
816 {
817 DoNextPlugin( curidx );
818
819 // speed isn't critical here, but the pretty status bar sure is. Sleep off
820 // some brief cycles to give the status bar time to refresh.
821
822 Sleep( 5 );
823 //Sleep(150); // uncomment this to slow down the selector, for debugging threading.
824 }
825
826 wxCommandEvent done( pxEvt_EnumerationFinished );
827 done.SetClientData( this );
828 m_master.GetEventHandler()->AddPendingEvent( done );
829
830 DevCon.WriteLn( "Plugin Enumeration Thread complete!" );
831 }

  ViewVC Help
Powered by ViewVC 1.1.22