gs: continue merge, remove plugin selector references

This commit is contained in:
Gauvain 'GovanifY' Roussel-Tarbouriech 2021-05-11 11:09:43 +02:00 committed by Kojin
parent d272334c71
commit f75ae3e046
8 changed files with 2 additions and 1583 deletions

View File

@ -783,7 +783,6 @@ set(pcsx2DebugToolsHeaders
set(pcsx2GuiSources
gui/AppAssert.cpp
gui/AppConfig.cpp
gui/AppCorePlugins.cpp
gui/AppCoreThread.cpp
gui/AppEventSources.cpp
gui/AppGameDatabase.cpp
@ -837,7 +836,6 @@ set(pcsx2GuiSources
gui/Panels/MemoryCardListView.cpp
gui/Panels/MiscPanelStuff.cpp
gui/Panels/PathsPanel.cpp
gui/Panels/PluginSelectorPanel.cpp
gui/Panels/SpeedhacksPanel.cpp
gui/Panels/VideoPanel.cpp
gui/RecentIsoList.cpp
@ -851,7 +849,6 @@ set(pcsx2GuiHeaders
gui/AppAccelerators.h
gui/AppCommon.h
gui/AppConfig.h
gui/AppCorePlugins.h
gui/AppCoreThread.h
gui/AppEventListeners.h
gui/AppForwardDefs.h

View File

@ -47,10 +47,6 @@ wxDECLARE_EVENT(pxEvt_SetSettingsPage, wxCommandEvent);
// the universal Accelerator table.
static const int pxID_PadHandler_Keydown = 8030;
// Plugin ID sections are spaced out evenly at intervals to make it easy to use a
// single for-loop to create them.
static const int PluginMenuId_Interval = 0x10;
// ID and return code used for modal popups that have a custom button.
static const wxWindowID pxID_CUSTOM = wxID_LOWEST - 1;
@ -59,11 +55,6 @@ static const wxWindowID pxID_CUSTOM = wxID_LOWEST - 1;
static const wxWindowID pxID_RestartWizard = wxID_LOWEST - 100;
// Forces the Interface to destroy the GS viewport window when the GS plugin is
// destroyed. This has the side effect of forcing all plugins to close and re-open
// along with the GS, since the GS viewport window handle will have changed.
static const bool CloseViewportWithPlugins = false;
// ------------------------------------------------------------------------
// All Menu Options for the Main Window! :D
// ------------------------------------------------------------------------
@ -145,7 +136,6 @@ enum MenuIdentifiers
MenuId_Config_BIOS,
MenuId_Config_Language,
// Plugin ID order is important. Must match the order in tbl_PluginInfo.
MenuId_Config_GS,
MenuId_Config_PAD,
MenuId_Config_SPU2,
@ -166,15 +156,6 @@ enum MenuIdentifiers
MenuId_Help_Wiki,
MenuId_Help_Github,
// Plugin Sections
// ---------------
// Each plugin menu begins with its name, which is a grayed out option that's
// intended for display purposes only. Plugin ID sections are spaced out evenly
// at intervals to make it easy to use a single for-loop to create them.
MenuId_PluginBase_Name = 0x100,
MenuId_PluginBase_Settings = 0x101,
MenuId_Video_CoreSettings = 0x200, // includes frame timings and skippings settings
MenuId_Video_WindowSettings,
@ -247,7 +228,6 @@ struct AppImageIds
struct ConfigIds
{
int Paths,
Plugins,
Speedhacks,
Gamefixes,
MemoryCard,
@ -256,7 +236,7 @@ struct AppImageIds
ConfigIds()
{
Paths = Plugins =
Paths =
Speedhacks = Gamefixes =
Video = Cpu =
MemoryCard = -1;
@ -267,19 +247,13 @@ struct AppImageIds
{
int Settings,
Play,
Resume,
PluginVideo,
PluginAudio,
PluginPad;
Resume;
ToolbarIds()
{
Settings = -1;
Play = -1;
Resume = -1;
PluginVideo = -1;
PluginAudio = -1;
PluginPad = -1;
}
} Toolbars;
};
@ -419,14 +393,6 @@ public:
return SettingsFolder.IsOk() || VmSettingsFile.IsOk();
}
bool HasPluginsOverride() const
{
for (int i = 0; i < PluginId_Count; ++i)
if (Filenames.Plugins[i].IsOk())
return true;
return false;
}
};
// =====================================================================================================
@ -442,16 +408,10 @@ class Pcsx2App : public wxAppWithHelpers
// on them and they are, themselves, fairly self-contained.
protected:
EventSource<IEventListener_Plugins> m_evtsrc_CorePluginStatus;
EventSource<IEventListener_CoreThread> m_evtsrc_CoreThreadStatus;
EventSource<IEventListener_AppStatus> m_evtsrc_AppStatus;
public:
void AddListener(IEventListener_Plugins& listener)
{
m_evtsrc_CorePluginStatus.Add(listener);
}
void AddListener(IEventListener_CoreThread& listener)
{
m_evtsrc_CoreThreadStatus.Add(listener);
@ -462,11 +422,6 @@ public:
m_evtsrc_AppStatus.Add(listener);
}
void RemoveListener(IEventListener_Plugins& listener)
{
m_evtsrc_CorePluginStatus.Remove(listener);
}
void RemoveListener(IEventListener_CoreThread& listener)
{
m_evtsrc_CoreThreadStatus.Remove(listener);
@ -477,11 +432,6 @@ public:
m_evtsrc_AppStatus.Remove(listener);
}
void AddListener(IEventListener_Plugins* listener)
{
m_evtsrc_CorePluginStatus.Add(listener);
}
void AddListener(IEventListener_CoreThread* listener)
{
m_evtsrc_CoreThreadStatus.Add(listener);
@ -492,11 +442,6 @@ public:
m_evtsrc_AppStatus.Add(listener);
}
void RemoveListener(IEventListener_Plugins* listener)
{
m_evtsrc_CorePluginStatus.Remove(listener);
}
void RemoveListener(IEventListener_CoreThread* listener)
{
m_evtsrc_CoreThreadStatus.Remove(listener);
@ -507,7 +452,6 @@ public:
m_evtsrc_AppStatus.Remove(listener);
}
void DispatchEvent(PluginEventType evt);
void DispatchEvent(AppEventType evt);
void DispatchEvent(CoreThreadStatus evt);
void DispatchUiSettingsEvent(IniInterface& ini);
@ -819,12 +763,6 @@ extern pxDoAssertFnType AppDoAssert;
// External App-related Globals and Shortcuts
// --------------------------------------------------------------------------------------
extern int EnumeratePluginsInFolder(const wxDirName& searchPath, wxArrayString* dest);
extern void LoadPluginsPassive();
extern void LoadPluginsImmediate();
extern void UnloadPlugins();
extern void ShutdownPlugins();
extern bool SysHasValidState();
extern void SysUpdateIsoSrcFile(const wxString& newIsoFile);
extern void SysUpdateDiscSrcDrive(const wxString& newDiscDrive);
@ -836,7 +774,6 @@ extern MainEmuFrame* GetMainFramePtr();
extern __aligned16 AppCoreThread CoreThread;
extern __aligned16 SysMtgsThread mtgsThread;
extern __aligned16 AppCorePlugins CorePlugins;
extern void UI_UpdateSysControls();

View File

@ -57,13 +57,11 @@ namespace PathDefs
extern DocsModeType DocsFolderMode; //
extern bool UseDefaultSettingsFolder; // when TRUE, pcsx2 derives the settings folder from the DocsFolderMode
extern bool UseDefaultPluginsFolder;
extern wxDirName CustomDocumentsFolder; // allows the specification of a custom home folder for PCSX2 documents files.
extern wxDirName SettingsFolder; // dictates where the settings folder comes from, *if* UseDefaultSettingsFolder is FALSE.
extern wxDirName InstallFolder;
extern wxDirName PluginsFolder;
extern wxDirName GetSettingsFolder();
extern wxString GetVmSettingsFilename();
@ -189,11 +187,8 @@ public:
struct FilenameOptions
{
wxFileName Bios;
wxFileName Plugins[PluginId_Count];
void LoadSave( IniInterface& conf );
const wxFileName& operator[]( PluginsEnum_t pluginidx ) const;
};
// ------------------------------------------------------------------------
@ -384,9 +379,6 @@ public:
wxString FullpathToBios() const;
wxString FullpathToMcd( uint slot ) const;
wxString FullpathTo( PluginsEnum_t pluginId ) const;
bool FullpathMatchTest( PluginsEnum_t pluginId, const wxString& cmpto ) const;
void LoadSave( IniInterface& ini );
void LoadSaveRootItems( IniInterface& ini );

View File

@ -1,591 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "App.h"
#include "AppSaveStates.h"
#include "GSFrame.h"
#include <wx/dir.h>
#include <wx/file.h>
#include "Plugins.h"
#include "GS.h"
#include "AppConfig.h"
#include "gui/Dialogs/ModalPopups.h"
using namespace Threading;
// The GS plugin needs to be opened to save/load the state during plugin configuration, but
// the window shouldn't. This blocks it. :)
static bool s_DisableGsWindow = false;
__aligned16 AppCorePlugins CorePlugins;
SysCorePlugins& GetCorePlugins()
{
return CorePlugins;
}
// --------------------------------------------------------------------------------------
// CorePluginsEvent
// --------------------------------------------------------------------------------------
class CorePluginsEvent : public pxActionEvent
{
typedef pxActionEvent _parent;
protected:
PluginEventType m_evt;
public:
virtual ~CorePluginsEvent() = default;
CorePluginsEvent* Clone() const { return new CorePluginsEvent(*this); }
explicit CorePluginsEvent(PluginEventType evt, SynchronousActionState* sema = NULL)
: pxActionEvent(sema)
{
m_evt = evt;
}
explicit CorePluginsEvent(PluginEventType evt, SynchronousActionState& sema)
: pxActionEvent(sema)
{
m_evt = evt;
}
CorePluginsEvent(const CorePluginsEvent& src)
: pxActionEvent(src)
{
m_evt = src.m_evt;
}
void SetEventType(PluginEventType evt) { m_evt = evt; }
PluginEventType GetEventType() { return m_evt; }
protected:
void InvokeEvent()
{
sApp.DispatchEvent(m_evt);
}
};
static void PostPluginStatus(PluginEventType pevt)
{
sApp.PostAction(CorePluginsEvent(pevt));
}
static void ConvertPluginFilenames(wxString (&passins)[PluginId_Count])
{
ForPlugins([&](const PluginInfo* pi) {
passins[pi->id] = wxGetApp().Overrides.Filenames[pi->id].GetFullPath();
if (passins[pi->id].IsEmpty() || !wxFileExists(passins[pi->id]))
passins[pi->id] = g_Conf->FullpathTo(pi->id);
});
}
typedef void (AppCorePlugins::*FnPtr_AppPluginManager)();
typedef void (AppCorePlugins::*FnPtr_AppPluginPid)(PluginsEnum_t pid);
// --------------------------------------------------------------------------------------
// SysExecEvent_AppPluginManager
// --------------------------------------------------------------------------------------
class SysExecEvent_AppPluginManager : public SysExecEvent
{
protected:
FnPtr_AppPluginManager m_method;
public:
wxString GetEventName() const { return L"CorePluginsMethod"; }
virtual ~SysExecEvent_AppPluginManager() = default;
SysExecEvent_AppPluginManager* Clone() const { return new SysExecEvent_AppPluginManager(*this); }
SysExecEvent_AppPluginManager(FnPtr_AppPluginManager method)
{
m_method = method;
}
protected:
void InvokeEvent()
{
if (m_method)
(CorePlugins.*m_method)();
}
};
// --------------------------------------------------------------------------------------
// LoadSinglePluginEvent
// --------------------------------------------------------------------------------------
class LoadSinglePluginEvent : public pxActionEvent
{
typedef pxActionEvent _parent;
wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(LoadSinglePluginEvent);
protected:
wxString m_filename;
PluginsEnum_t m_pid;
public:
LoadSinglePluginEvent(const LoadSinglePluginEvent&) = default;
virtual ~LoadSinglePluginEvent() = default;
virtual LoadSinglePluginEvent* Clone() const { return new LoadSinglePluginEvent(*this); }
LoadSinglePluginEvent(PluginsEnum_t pid = PluginId_GS, const wxString& filename = wxEmptyString)
: m_filename(filename)
{
m_pid = pid;
}
protected:
void InvokeEvent()
{
GetCorePlugins().Load(m_pid, m_filename);
}
};
// --------------------------------------------------------------------------------------
// SinglePluginMethodEvent
// --------------------------------------------------------------------------------------
class SinglePluginMethodEvent : public pxActionEvent
{
typedef pxActionEvent _parent;
wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(SinglePluginMethodEvent);
protected:
PluginsEnum_t m_pid;
FnPtr_AppPluginPid m_method;
public:
SinglePluginMethodEvent(const SinglePluginMethodEvent&) = default;
virtual ~SinglePluginMethodEvent() = default;
virtual SinglePluginMethodEvent* Clone() const { return new SinglePluginMethodEvent(*this); }
SinglePluginMethodEvent(FnPtr_AppPluginPid method = NULL, PluginsEnum_t pid = PluginId_GS)
{
m_pid = pid;
m_method = method;
}
protected:
void InvokeEvent()
{
//GetCorePlugins().Unload( m_pid );
if (m_method)
(CorePlugins.*m_method)(m_pid);
}
};
wxIMPLEMENT_DYNAMIC_CLASS(LoadSinglePluginEvent, pxActionEvent);
wxIMPLEMENT_DYNAMIC_CLASS(SinglePluginMethodEvent, pxActionEvent);
// --------------------------------------------------------------------------------------
// AppCorePlugins
// --------------------------------------------------------------------------------------
//
// Thread Affinity Notes:
// It's important to ensure that Load/Unload/Init/Shutdown are all called from the
// MAIN/UI Thread only. Those APIs are allowed to issue modal popups, and as such
// are only safe when invoked form the UI thread. Under windows the popups themselves
// will typically work from any thread, but some common control activities will fail
// (such as opening the browser windows). On Linux it's probably just highly unsafe, period.
//
// My implementation is to execute the main Load/Init/Shutdown/Unload procedure on the
// SysExecutor, and then dispatch each individual plugin to the main thread. This keeps
// the main thread from being completely busy while plugins are loaded and initialized.
// (responsiveness is bliss!!) -- air
//
static void _SetSettingsFolder()
{
if (wxGetApp().Rpc_TryInvoke(_SetSettingsFolder))
return;
CorePlugins.SetSettingsFolder(GetSettingsFolder().ToString());
}
static void _SetLogFolder()
{
if (wxGetApp().Rpc_TryInvoke(_SetLogFolder))
return;
CorePlugins.SetLogFolder(GetLogFolder().ToString());
}
void AppCorePlugins::Load(PluginsEnum_t pid, const wxString& srcfile)
{
if (!wxThread::IsMain())
{
Sleep(5);
LoadSinglePluginEvent evt(pid, srcfile);
wxGetApp().ProcessAction(evt);
return;
}
_parent::Load(pid, srcfile);
}
void AppCorePlugins::Unload(PluginsEnum_t pid)
{
if (!wxThread::IsMain())
{
Sleep(5);
SinglePluginMethodEvent evt(&AppCorePlugins::Unload, pid);
wxGetApp().ProcessAction(evt);
return;
}
_parent::Unload(pid);
}
void AppCorePlugins::Load(const wxString (&folders)[PluginId_Count])
{
if (!pxAssert(!AreLoaded()))
return;
_SetLogFolder();
SendLogFolder();
_SetSettingsFolder();
SendSettingsFolder();
_parent::Load(folders);
PostPluginStatus(CorePlugins_Loaded);
}
void AppCorePlugins::Unload()
{
_parent::Unload();
PostPluginStatus(CorePlugins_Unloaded);
}
void AppCorePlugins::Init(PluginsEnum_t pid)
{
if (!wxTheApp)
return;
if (!wxThread::IsMain())
{
SinglePluginMethodEvent evt(&AppCorePlugins::Init, pid);
wxGetApp().ProcessAction(evt);
Sleep(5);
return;
}
_parent::Init(pid);
}
void AppCorePlugins::Shutdown(PluginsEnum_t pid)
{
if (!wxThread::IsMain() && wxTheApp)
{
SinglePluginMethodEvent evt(&AppCorePlugins::Shutdown, pid);
wxGetApp().ProcessAction(evt);
Sleep(5);
return;
}
_parent::Shutdown(pid);
}
bool AppCorePlugins::Init()
{
if (!NeedsInit())
return false;
_SetLogFolder();
SendLogFolder();
_SetSettingsFolder();
SendSettingsFolder();
if (_parent::Init())
{
PostPluginStatus(CorePlugins_Init);
return true;
}
return false;
}
bool AppCorePlugins::Shutdown()
{
if (_parent::Shutdown())
{
PostPluginStatus(CorePlugins_Shutdown);
return true;
}
return false;
}
void AppCorePlugins::Close()
{
if (!(NeedsClose() || GSDump::isRunning))
return;
PostPluginStatus(CorePlugins_Closing);
_parent::Close();
PostPluginStatus(CorePlugins_Closed);
}
void AppCorePlugins::Open()
{
AffinityAssert_AllowFrom_CoreThread();
SetLogFolder(GetLogFolder().ToString());
SetSettingsFolder(GetSettingsFolder().ToString());
if (!NeedsOpen())
return;
PostPluginStatus(CorePlugins_Opening);
_parent::Open();
PostPluginStatus(CorePlugins_Opened);
}
// Yay, this plugin is guaranteed to always be opened first and closed last.
bool AppCorePlugins::OpenPlugin_GS()
{
if (GSopen2 && !s_DisableGsWindow)
{
sApp.OpenGsPanel();
}
bool retval = _parent::OpenPlugin_GS();
GSsetVsync(EmuConfig.GS.GetVsync());
return retval;
}
// Yay, this plugin is guaranteed to always be opened first and closed last.
void AppCorePlugins::ClosePlugin_GS()
{
_parent::ClosePlugin_GS();
if (CloseViewportWithPlugins && GetMTGS().IsSelf() && GSopen2)
sApp.CloseGsPanel();
}
// --------------------------------------------------------------------------------------
// LoadCorePluginsEvent
// --------------------------------------------------------------------------------------
class LoadCorePluginsEvent : public SysExecEvent
{
protected:
wxString m_folders[PluginId_Count];
public:
LoadCorePluginsEvent()
{
ConvertPluginFilenames(m_folders);
}
wxString GetEventName() const
{
return L"LoadCorePlugins";
}
wxString GetEventMessage() const
{
return _("Loading PS2 system plugins...");
}
protected:
void InvokeEvent()
{
CorePlugins.Load(m_folders);
}
~LoadCorePluginsEvent() = default;
};
// --------------------------------------------------------------------------------------
// Public API / Interface
// --------------------------------------------------------------------------------------
int EnumeratePluginsInFolder(const wxDirName& searchpath, wxArrayString* dest)
{
if (!searchpath.Exists())
return 0;
std::unique_ptr<wxArrayString> placebo;
wxArrayString* realdest = dest;
if (realdest == NULL)
{
placebo = std::make_unique<wxArrayString>();
realdest = placebo.get();
}
#ifdef __WXMSW__
// Windows pretty well has a strict "must end in .dll" rule.
wxString pattern(L"*%s");
#else
// Other platforms seem to like to version their libs after the .so extension:
// blah.so.3.1.fail?
wxString pattern(L"*%s*");
#endif
wxDir::GetAllFiles(searchpath.ToString(), realdest, pxsFmt(pattern, WX_STR(wxDynamicLibrary::GetDllExt())), wxDIR_FILES);
// SECURITY ISSUE: (applies primarily to Windows, but is a good idea on any platform)
// The search folder order for plugins can vary across operating systems, and in some poorly designed
// cases (old versions of windows), the search order is a security hazard because it does not
// search where you might really expect. In our case wedo not want *any* searching. The only
// plugins we want to load are the ones we found in the directly the user specified, so make
// sure all paths are FULLY QUALIFIED ABSOLUTE PATHS.
//
// (for details, read: http://msdn.microsoft.com/en-us/library/ff919712.aspx )
for (uint i = 0; i < realdest->GetCount(); ++i)
{
(*realdest)[i] = Path::MakeAbsolute((*realdest)[i]);
}
return realdest->GetCount();
}
// Posts a message to the App to reload plugins. Plugins are loaded via a background thread
// which is started on a pending event, so don't expect them to be ready "right now."
// If plugins are already loaded, onComplete is invoked, and the function returns with no
// other actions performed.
void LoadPluginsPassive()
{
AffinityAssert_AllowFrom_MainUI();
// Plugins already loaded?
if (!CorePlugins.AreLoaded())
{
wxGetApp().SysExecutorThread.PostEvent(new LoadCorePluginsEvent());
}
}
static void _LoadPluginsImmediate()
{
if (CorePlugins.AreLoaded())
return;
wxString passins[PluginId_Count];
ConvertPluginFilenames(passins);
CorePlugins.Load(passins);
}
void LoadPluginsImmediate()
{
AffinityAssert_AllowFrom_SysExecutor();
_LoadPluginsImmediate();
}
// Performs a blocking load of plugins. If the emulation thread is active, it is shut down
// automatically to prevent race conditions (it depends on plugins).
//
// Exceptions regarding plugin failures will propagate out of this function, so be prepared
// to handle them.
//
// Note that this is not recommended for most situations, but coding improper passive loads
// is probably worse, so if in doubt use this and air will fix it up for you later. :)
//
void ScopedCoreThreadClose::LoadPlugins()
{
DbgCon.WriteLn("(ScopedCoreThreadClose) Loading plugins!");
_LoadPluginsImmediate();
}
class SysExecEvent_UnloadPlugins : public SysExecEvent
{
public:
wxString GetEventName() const { return L"UnloadPlugins"; }
virtual ~SysExecEvent_UnloadPlugins() = default;
SysExecEvent_UnloadPlugins* Clone() const { return new SysExecEvent_UnloadPlugins(*this); }
virtual bool AllowCancelOnExit() const { return false; }
virtual bool IsCriticalEvent() const { return true; }
void InvokeEvent()
{
CoreThread.Cancel();
CorePlugins.Unload();
}
};
class SysExecEvent_ShutdownPlugins : public SysExecEvent
{
public:
wxString GetEventName() const { return L"ShutdownPlugins"; }
virtual ~SysExecEvent_ShutdownPlugins() = default;
SysExecEvent_ShutdownPlugins* Clone() const { return new SysExecEvent_ShutdownPlugins(*this); }
virtual bool AllowCancelOnExit() const { return false; }
virtual bool IsCriticalEvent() const { return true; }
void InvokeEvent()
{
CoreThread.Cancel();
CorePlugins.Shutdown();
}
};
void UnloadPlugins()
{
GetSysExecutorThread().PostEvent(new SysExecEvent_UnloadPlugins());
}
void ShutdownPlugins()
{
GetSysExecutorThread().PostEvent(new SysExecEvent_ShutdownPlugins());
}
void SysExecEvent_SaveSinglePlugin::InvokeEvent()
{
s_DisableGsWindow = true; // keeps the GS window smooth by avoiding closing the window
ScopedCoreThreadPause paused_core;
if (CorePlugins.AreLoaded())
{
std::unique_ptr<VmStateBuffer> plugstore;
if (CoreThread.HasActiveMachine())
{
Console.WriteLn(Color_Green, L"Suspending single plugin: " + tbl_PluginInfo[m_pid].GetShortname());
plugstore = std::make_unique<VmStateBuffer>(L"StateCopy_SinglePlugin");
memSavingState save(plugstore.get());
GetCorePlugins().Freeze(m_pid, save);
}
const bool wasOpen = GetCorePlugins().IsOpen(m_pid);
GetCorePlugins().Close(m_pid);
_post_and_wait(paused_core);
if (plugstore)
{
Console.WriteLn(Color_Green, L"Recovering single plugin: " + tbl_PluginInfo[m_pid].GetShortname());
memLoadingState load(plugstore.get());
GetCorePlugins().Freeze(m_pid, load);
}
if (wasOpen)
GetCorePlugins().Open(m_pid);
}
s_DisableGsWindow = false;
paused_core.AllowResume();
}
void SysExecEvent_SaveSinglePlugin::CleanupEvent()
{
s_DisableGsWindow = false;
_parent::CleanupEvent();
}

View File

@ -1,52 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "AppCommon.h"
// --------------------------------------------------------------------------------------
// AppCorePlugins
// --------------------------------------------------------------------------------------
// This extension of SysCorePlugins provides event listener sources for plugins -- loading,
// unloading, open, close, shutdown, etc.
//
// FIXME : Should this be made part of the PCSX2 core emulation? (integrated into SysCorePlugins)
// I'm undecided on if it makes sense more in that context or in this one (interface).
//
class AppCorePlugins : public SysCorePlugins
{
typedef SysCorePlugins _parent;
public:
AppCorePlugins() = default;
virtual ~AppCorePlugins() = default;
void Load( const wxString (&folders)[PluginId_Count] );
void Load( PluginsEnum_t pid, const wxString& srcfile );
void Unload( PluginsEnum_t pid );
void Unload();
bool Init();
void Init( PluginsEnum_t pid );
void Shutdown( PluginsEnum_t pid );
bool Shutdown();
void Close();
void Open();
protected:
bool OpenPlugin_GS();
void ClosePlugin_GS();
};

View File

@ -185,7 +185,6 @@ void AppCoreThread::Resume()
return;
}
GetCorePlugins().Init();
SPU2init();
_parent::Resume();
}
@ -735,28 +734,9 @@ void SysExecEvent_CoreThreadClose::InvokeEvent()
void SysExecEvent_CoreThreadPause::InvokeEvent()
{
#ifdef PCSX2_DEVBUILD
bool CorePluginsAreOpen = GetCorePlugins().AreOpen();
ScopedCoreThreadPause paused_core;
_post_and_wait(paused_core);
// All plugins should be initialized and opened upon resuming from
// a paused state. If the thread that puased us changed plugin status, it should
// have used Close instead.
if (CorePluginsAreOpen)
{
CorePluginsAreOpen = GetCorePlugins().AreOpen();
pxAssertDev(CorePluginsAreOpen, "Invalid plugin close/shutdown detected during paused CoreThread; please Stop/Suspend the core instead.");
}
paused_core.AllowResume();
#else
ScopedCoreThreadPause paused_core;
_post_and_wait(paused_core);
paused_core.AllowResume();
#endif
}

View File

@ -19,7 +19,6 @@
#include "pxEventThread.h"
#include "AppCommon.h"
#include "AppCorePlugins.h"
#include "SaveState.h"
#define AffinityAssert_AllowFrom_CoreThread() \
@ -235,8 +234,6 @@ class ScopedCoreThreadClose : public BaseScopedCoreThread
public:
ScopedCoreThreadClose();
virtual ~ScopedCoreThreadClose();
void LoadPlugins();
};
struct ScopedCoreThreadPause : public BaseScopedCoreThread

View File

@ -1,841 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 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 <wx/dynlib.h>
#include <wx/dir.h>
#include <memory>
#include "App.h"
#include "AppSaveStates.h"
#include "Plugins.h"
#include "ConfigurationPanels.h"
#include "Dialogs/ModalPopups.h"
#include "Utilities/ThreadingDialogs.h"
#include "Utilities/SafeArray.inl"
// 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;
wxDECLARE_EVENT(pxEvt_EnumeratedNext, wxCommandEvent);
wxDECLARE_EVENT(pxEvt_EnumerationFinished, wxCommandEvent);
wxDECLARE_EVENT(pxEVT_ShowStatusBar, wxCommandEvent);
wxDEFINE_EVENT(pxEvt_EnumeratedNext, wxCommandEvent);
wxDEFINE_EVENT(pxEvt_EnumerationFinished, wxCommandEvent);
wxDEFINE_EVENT(pxEVT_ShowStatusBar, wxCommandEvent);
typedef s32 (CALLBACK* TestFnptr)();
typedef void (CALLBACK* ConfigureFnptr)();
namespace Exception
{
class NotEnumerablePlugin : public BadStream
{
public:
DEFINE_STREAM_EXCEPTION( NotEnumerablePlugin, BadStream );
wxString FormatDiagnosticMessage() const
{
FastFormatUnicode retval;
retval.Write("File is not a PCSX2 plugin");
_formatDiagMsg(retval);
return retval;
}
};
}
// --------------------------------------------------------------------------------------
// 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 )
{
if( !m_plugin.Load( m_plugpath ) )
throw Exception::BadStream();
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, WX_STR(m_plugpath), 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!)
TestFnptr testfunc = (TestFnptr)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 );
}
};
// --------------------------------------------------------------------------------------
// ApplyPluginsDialog
// --------------------------------------------------------------------------------------
class ApplyPluginsDialog : public WaitForTaskDialog
{
wxDECLARE_DYNAMIC_CLASS_NO_COPY(ApplyPluginsDialog);
typedef wxDialogWithHelpers _parent;
protected:
BaseApplicableConfigPanel* m_panel;
public:
ApplyPluginsDialog( BaseApplicableConfigPanel* panel=NULL );
virtual ~ApplyPluginsDialog() = default;
BaseApplicableConfigPanel* GetApplicableConfigPanel() const { return m_panel; }
};
// --------------------------------------------------------------------------------------
// ApplyOverValidStateEvent
// --------------------------------------------------------------------------------------
class ApplyOverValidStateEvent : public pxActionEvent
{
//DeclareNoncopyableObject( ApplyOverValidStateEvent );
typedef pxActionEvent _parent;
protected:
ApplyPluginsDialog* m_owner;
public:
ApplyOverValidStateEvent( ApplyPluginsDialog* owner=NULL )
{
m_owner = owner;
}
virtual ~ApplyOverValidStateEvent() = default;
virtual ApplyOverValidStateEvent *Clone() const { return new ApplyOverValidStateEvent(*this); }
protected:
void InvokeEvent();
};
// --------------------------------------------------------------------------------------
// SysExecEvent_ApplyPlugins
// --------------------------------------------------------------------------------------
class SysExecEvent_ApplyPlugins : public SysExecEvent
{
typedef SysExecEvent _parent;
protected:
ApplyPluginsDialog* m_dialog;
public:
wxString GetEventName() const { return L"PluginSelectorPanel::ApplyPlugins"; }
virtual ~SysExecEvent_ApplyPlugins() = default;
SysExecEvent_ApplyPlugins* Clone() const { return new SysExecEvent_ApplyPlugins( *this ); }
SysExecEvent_ApplyPlugins( ApplyPluginsDialog* parent, SynchronousActionState& sync )
: SysExecEvent( &sync )
{
m_dialog = parent;
}
protected:
void InvokeEvent();
void CleanupEvent();
void PostFinishToDialog();
};
// --------------------------------------------------------------------------------------
// ApplyPluginsDialog Implementations
// --------------------------------------------------------------------------------------
wxIMPLEMENT_DYNAMIC_CLASS(ApplyPluginsDialog, WaitForTaskDialog);
ApplyPluginsDialog::ApplyPluginsDialog( BaseApplicableConfigPanel* panel )
: WaitForTaskDialog(_("Applying settings...")), m_panel(NULL)
{
GetSysExecutorThread().PostEvent( new SysExecEvent_ApplyPlugins( this, m_sync ) );
}
// --------------------------------------------------------------------------------------
// ApplyOverValidStateEvent Implementations
// --------------------------------------------------------------------------------------
void ApplyOverValidStateEvent::InvokeEvent()
{
wxDialogWithHelpers dialog( m_owner, _("Shutdown PS2 virtual machine?") );
dialog += dialog.Heading( pxE( L"Warning! Changing plugins requires a complete shutdown and reset of the PS2 virtual machine. PCSX2 will attempt to save and restore the state, but if the newly selected plugins are incompatible the recovery may fail, and current progress will be lost.\n\nAre you sure you want to apply settings now?"
) );
int result = pxIssueConfirmation( dialog, MsgButtons().OK().Cancel(), L"PluginSelector.ConfirmShutdown" );
if( result == wxID_CANCEL )
throw Exception::CannotApplySettings( m_owner->GetApplicableConfigPanel() ).Quiet()
.SetDiagMsg(L"Cannot apply settings: canceled by user because plugins changed while the emulation state was active.");
}
// --------------------------------------------------------------------------------------
// SysExecEvent_ApplyPlugins Implementations
// --------------------------------------------------------------------------------------
void SysExecEvent_ApplyPlugins::InvokeEvent()
{
ScopedCoreThreadPause paused_core;
std::unique_ptr<VmStateBuffer> buffer;
if( SysHasValidState() )
{
paused_core.AllowResume();
ApplyOverValidStateEvent aEvt( m_dialog );
wxGetApp().ProcessEvent( aEvt );
paused_core.DisallowResume();
// FIXME : We only actually have to save plugins here, except the recovery code
// in SysCoreThread isn't quite set up yet to handle that (I think...) --air
buffer.reset(new VmStateBuffer(L"StateBuffer_ApplyNewPlugins"));
memSavingState saveme(buffer.get());
saveme.FreezeAll();
}
ScopedCoreThreadClose closed_core;
CorePlugins.Shutdown();
CorePlugins.Unload();
LoadPluginsImmediate();
CorePlugins.Init();
if( buffer ) CoreThread.UploadStateCopy( *buffer );
PostFinishToDialog();
closed_core.AllowResume();
paused_core.AllowResume();
}
void SysExecEvent_ApplyPlugins::PostFinishToDialog()
{
if( !m_dialog ) return;
wxCommandEvent tevt( pxEvt_ThreadedTaskComplete );
m_dialog->GetEventHandler()->AddPendingEvent( tevt );
m_dialog = NULL;
}
void SysExecEvent_ApplyPlugins::CleanupEvent()
{
PostFinishToDialog();
_parent::CleanupEvent();
}
// --------------------------------------------------------------------------------------
// PluginSelectorPanel::StatusPanel 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 += Heading(_( "Enumerating available plugins..." )).Bold() | StdExpand();
*this += m_gauge | pxExpand.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 );
if (m_progress < m_gauge.GetRange())
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;
// --------------------------------------------------------------------------------------
// PluginSelectorPanel::ComboBoxPanel implementations
// --------------------------------------------------------------------------------------
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( PluginId_Count, 3, 16, 10 ) );
s_plugin.SetFlexibleDirection( wxHORIZONTAL );
s_plugin.AddGrowableCol( 1 ); // expands combo boxes to full width.
ForPlugins([&] (const PluginInfo * pi) {
const PluginsEnum_t pid = pi->id;
m_combobox[pid] = new wxComboBox( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY );
m_configbutton[pid] = new wxButton( this, ButtonId_Configure, _("Configure...") );
m_configbutton[pid]->SetClientData( (void*)pid );
s_plugin += Label( pi->GetShortname() ) | pxBorder( wxTOP | wxLEFT, 2 );
s_plugin += m_combobox[pid] | pxExpand;
s_plugin += m_configbutton[pid];
});
// if (InstallationMode != InstallMode_Portable)
m_FolderPicker.SetStaticDesc( _("Click the Browse button to select a different folder for PCSX2 plugins.") );
*this += s_plugin | pxExpand;
*this += 6;
*this += m_FolderPicker | StdExpand();
}
void Panels::PluginSelectorPanel::ComboBoxPanel::Reset()
{
for( int i=0; i<PluginId_Count; ++i )
{
m_combobox[i]->Clear();
m_combobox[i]->SetSelection( wxNOT_FOUND );
m_combobox[i]->SetValue( wxEmptyString );
m_configbutton[i]->Disable();
}
}
void Panels::PluginSelectorPanel::DispatchEvent( const PluginEventType& evt )
{
if( (evt != CorePlugins_Loaded) && (evt != CorePlugins_Unloaded) ) return; // everything else we don't care about
if( IsBeingDeleted() ) return;
ForPlugins([&] (const PluginInfo * pi) {
wxComboBox& box( m_ComponentBoxes->Get(pi->id) );
int sel = box.GetSelection();
if (sel != wxNOT_FOUND) {
m_ComponentBoxes->GetConfigButton(pi->id).Enable(
(m_FileList==NULL || m_FileList->Count() == 0) ? false :
g_Conf->FullpathMatchTest( pi->id,(*m_FileList)[((uptr)box.GetClientData(sel))] )
);
}
});
}
Panels::PluginSelectorPanel::PluginSelectorPanel( wxWindow* parent )
: BaseSelectorPanel( parent )
{
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 );
//Bind(wxEVT_BUTTON, &PluginSelectorPanel::OnRefresh, this, refresh->GetId());
Bind(pxEvt_EnumeratedNext, &PluginSelectorPanel::OnProgress, this);
Bind(pxEvt_EnumerationFinished, &PluginSelectorPanel::OnEnumComplete, this);
Bind(pxEVT_ShowStatusBar, &PluginSelectorPanel::OnShowStatusBar, this);
Bind(wxEVT_COMBOBOX, &PluginSelectorPanel::OnPluginSelected, this);
Bind(wxEVT_BUTTON, &PluginSelectorPanel::OnConfigure_Clicked, this, ButtonId_Configure);
}
Panels::PluginSelectorPanel::~PluginSelectorPanel()
{
CancelRefresh(); // in case the enumeration thread is currently refreshing...
}
void Panels::PluginSelectorPanel::AppStatusEvent_OnSettingsApplied()
{
m_ComponentBoxes->GetDirPicker().Reset();
}
static wxString GetApplyFailedMsg()
{
return pxsFmt( pxE( L"All plugins must have valid selections for %s to run. If you are unable to make a valid selection due to missing plugins or an incomplete install of %s, then press Cancel to close the Configuration panel."
), WX_STR(pxGetAppName()), WX_STR(pxGetAppName()) );
}
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 );
ForPlugins([&] (const PluginInfo * pi) {
const PluginsEnum_t pid = pi->id;
int sel = m_ComponentBoxes->Get(pid).GetSelection();
if( sel == wxNOT_FOUND )
{
wxString plugname( pi->GetShortname() );
throw Exception::CannotApplySettings( this )
.SetDiagMsg(pxsFmt( L"PluginSelectorPanel: Invalid or missing selection for the %s plugin.", WX_STR(plugname)) )
.SetUserMsg(pxsFmt( _("Please select a valid plugin for the %s."), WX_STR(plugname) ) + L"\n\n" + GetApplyFailedMsg() );
}
g_Conf->BaseFilenames.Plugins[pid] = GetFilename((uptr)m_ComponentBoxes->Get(pid).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 );
if( pi->shortname == NULL ) return; // no plugins changed? nothing left to do!
// ----------------------------------------------------------------------------
// Plugin names are not up-to-date -- RELOAD!
try
{
if( wxID_CANCEL == ApplyPluginsDialog( this ).ShowModal() )
throw Exception::CannotApplySettings( this ).Quiet().SetDiagMsg(L"User canceled plugin load process.");
}
catch( Exception::PluginError& ex )
{
// Rethrow PluginLoadErrors as a failure to Apply...
wxString plugname( tbl_PluginInfo[ex.PluginId].GetShortname() );
throw Exception::CannotApplySettings( this )
.SetDiagMsg(ex.FormatDiagnosticMessage())
.SetUserMsg(pxsFmt(
_("The selected %s plugin failed to load.\n\nReason: %s\n\n"),
WX_STR(plugname), WX_STR(ex.FormatDisplayMessage())
) + GetApplyFailedMsg());
}
}
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.reset(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: unique_ptr used so that resources get cleaned up if an exception
// occurs during file enumeration.
std::unique_ptr<wxArrayString> pluginlist(new wxArrayString());
int pluggers = EnumeratePluginsInFolder(m_ComponentBoxes->GetPluginsPath(), pluginlist.get());
if( !m_FileList || (*pluginlist != *m_FileList) )
validated = false;
if( pluggers == 0 )
{
m_FileList = NULL;
return validated;
}
m_FileList.swap(pluginlist);
// set the gauge length a little shorter than the plugin count. 2 reasons:
// * some of the plugins might be duds.
// * on high end machines and Win7, the statusbar lags a lot and never gets to 100% before being hidden.
m_StatusPanel->SetGaugeLength( std::max( 1, (pluggers-1) - (pluggers/8) ) );
return validated;
}
void Panels::PluginSelectorPanel::OnPluginSelected( wxCommandEvent& evt )
{
if( IsBeingDeleted() || m_ComponentBoxes->IsBeingDeleted() ) return;
IfPlugins([&] (const PluginInfo * pi) {
wxComboBox& box( m_ComponentBoxes->Get(pi->id) );
if ( box.GetId() == evt.GetId() )
{
// Button is enabled if:
// (a) plugins aren't even loaded yet.
// (b) current selection matches exactly the currently configured/loaded plugin.
bool isSame = (!CorePlugins.AreLoaded()) || g_Conf->FullpathMatchTest( pi->id, (*m_FileList)[(uptr)box.GetClientData(box.GetSelection())] );
m_ComponentBoxes->GetConfigButton( pi->id ).Enable( isSame );
if( !isSame ) evt.Skip(); // enabled Apply button! :D
return true;
}
return false;
});
}
void Panels::PluginSelectorPanel::OnConfigure_Clicked( wxCommandEvent& evt )
{
if( IsBeingDeleted() ) return;
PluginsEnum_t pid = (PluginsEnum_t)(uptr)((wxEvtHandler*)evt.GetEventObject())->GetClientData();
int sel = m_ComponentBoxes->Get(pid).GetSelection();
if( sel == wxNOT_FOUND ) return;
// Only allow configuration if the selected plugin matches exactly our currently loaded one.
// Otherwise who knows what sort of funny business could happen configuring a plugin while
// another instance/version is running. >_<
const wxString filename( (*m_FileList)[(uptr)m_ComponentBoxes->Get(pid).GetClientData(sel)] );
if( CorePlugins.AreLoaded() && !g_Conf->FullpathMatchTest( pid, filename ) )
{
Console.Warning( "(PluginSelector) Plugin name mismatch, configuration request ignored." );
return;
}
wxDynamicLibrary dynlib( filename );
if( ConfigureFnptr configfunc = (ConfigureFnptr)dynlib.GetSymbol( tbl_PluginInfo[pid].GetShortname() + L"configure" ) )
{
wxWindowDisabler disabler;
wxDoNotLogInThisScope quiettime;
ScopedCoreThreadPause paused_core( new SysExecEvent_SaveSinglePlugin(pid) );
if (!CorePlugins.AreLoaded())
{
CorePlugins.Load(pid, filename);
CorePlugins.SendLogFolder();
CorePlugins.SendSettingsFolder();
configfunc();
CorePlugins.Unload(pid);
}
else
{
configfunc();
}
}
}
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;
const PluginInfo* pi = tbl_PluginInfo;
do
{
const PluginsEnum_t pid = pi->id;
if( m_ComponentBoxes->Get(pid).GetCount() <= 0 )
emptyBoxes++;
else if( m_ComponentBoxes->Get(pid).GetSelection() == wxNOT_FOUND )
{
if (pid == PluginId_GS)
{
int count = (int)m_ComponentBoxes->Get(pid).GetCount();
int index_avx2 = -1;
int index_sse4 = -1;
for( int i = 0; i < count; i++ )
{
auto str = m_ComponentBoxes->Get(pid).GetString( i );
if( x86caps.hasAVX2 && str.Contains("AVX2") ) index_avx2 = i;
if( x86caps.hasStreamingSIMD4Extensions && str.Contains("SSE4") ) index_sse4 = i;
}
if( index_avx2 >= 0 ) m_ComponentBoxes->Get(pid).SetSelection( index_avx2 );
else if( index_sse4 >= 0 ) m_ComponentBoxes->Get(pid).SetSelection( index_sse4 );
else m_ComponentBoxes->Get(pid).SetSelection( 0 );
}
else
m_ComponentBoxes->Get(pid).SetSelection( 0 );
m_ComponentBoxes->GetConfigButton(pid).Enable( !CorePlugins.AreLoaded() );
}
} while( ++pi, pi->shortname != NULL );
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.get() != (EnumThread*)evt.GetClientData()) return;
const size_t evtidx = evt.GetExtraLong();
if( DisableThreading )
{
const u32 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] );
ForPlugins([&] (const PluginInfo * pi) {
const PluginsEnum_t pid = pi->id;
if( result.TypeMask & pi->typemask )
{
if( result.PassedTest & pi->typemask )
{
int sel = m_ComponentBoxes->Get(pid).Append( wxsFormat( L"%s %s [%s]",
WX_STR(result.Name), WX_STR(result.Version[pid]), WX_STR(Path::GetFilenameWithoutExt( (*m_FileList)[evtidx] )) ),
(void*)evtidx
);
if( g_Conf->FullpathMatchTest( pid, (*m_FileList)[evtidx] ) )
{
m_ComponentBoxes->Get(pid).SetSelection( sel );
m_ComponentBoxes->GetConfigButton(pid).Enable();
}
}
}
});
}
// --------------------------------------------------------------------------------------
// EnumThread method implementations
// --------------------------------------------------------------------------------------
Panels::PluginSelectorPanel::EnumThread::EnumThread( PluginSelectorPanel& master )
: pxThread()
, Results( master.FileCount() )
, m_master( master )
, m_hourglass( Cursor_KindaBusy )
{
}
void Panels::PluginSelectorPanel::EnumThread::DoNextPlugin( int curidx )
{
DbgCon.Indent().WriteLn( L"Plugin: " + m_master.GetFilename( curidx ) );
try
{
EnumeratedPluginInfo& result( Results[curidx] );
result.TypeMask = 0;
PluginEnumerator penum( m_master.GetFilename( curidx ) );
result.Name = penum.GetName();
ForPlugins([&] (const PluginInfo * pi) {
const PluginsEnum_t pid = pi->id;
result.TypeMask |= pi->typemask;
if( penum.CheckVersion( pid ) )
{
result.PassedTest |= tbl_PluginInfo[pid].typemask;
penum.GetVersionString( result.Version[pid], pid );
}
});
}
catch (Exception::NotEnumerablePlugin& ex)
{
Console.Warning(ex.FormatDiagnosticMessage());
}
// wx3.0 provides an error message if the library fails to load - we don't
// need to provide one ourselves
catch (Exception::BadStream&)
{
}
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..." );
wxGetApp().Ping();
Sleep( 2 );
for( int curidx=0; curidx < m_master.FileCount(); ++curidx )
{
DoNextPlugin( curidx );
// speed isn't critical here, but the pretty status bar sure is. Sleep off
// some brief cycles to give the status bar time to refresh.
Sleep( 5 );
//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!" );
}