Audacity 3.2.0
PluginStartupRegistration.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file PluginStartupRegistration.cpp
6
7 @author Vitaly Sverchinsky
8
9**********************************************************************/
10
12
13#include <thread>
14
15#include <wx/log.h>
16#include <wx/app.h>
17#include <wx/stattext.h>
18#include <wx/gauge.h>
19#include <wx/button.h>
20#include <wx/timer.h>
21#include <wx/sizer.h>
22
23#include "PluginManager.h"
24#include "PluginDescriptor.h"
26
27namespace
28{
29 enum {
30 OnPluginScanTimeout = wxID_HIGHEST + 1,
31 };
32
34 {
35 wxStaticText* mText{nullptr};
36 wxStaticText* mElapsed{nullptr};
37 wxGauge* mProgress{nullptr};
38 std::chrono::system_clock::time_point mStartTime;
39 public:
40
42 wxWindow* parent,
43 wxWindowID winid,
45 : wxDialogWrapper(parent, winid, title)
46 {
47 auto rootSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
48 auto topSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
49 topSizer->Add(mText =
50 safenew wxStaticText(
51 this,
52 wxID_ANY,
53 wxEmptyString,
54 wxDefaultPosition,
55 wxDefaultSize,
56 wxST_ELLIPSIZE_START | wxST_NO_AUTORESIZE),
57 1, wxEXPAND);
58 topSizer->AddSpacer(5);
59 topSizer->Add(safenew wxButton(this, wxID_IGNORE, _("&Skip")), 0);
60
61 auto timerSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
62 timerSizer->Add(
63 safenew wxStaticText(
64 this,
65 wxID_ANY,
66 _("Elapsed Time:"),
67 wxDefaultPosition,
68 wxDefaultSize,
69 wxALIGN_RIGHT | wxALIGN_CENTRE_VERTICAL), 1, wxEXPAND);
70 timerSizer->AddSpacer(5);
71 timerSizer->Add(mElapsed =
72 safenew wxStaticText(
73 this,
74 wxID_ANY,
75 wxEmptyString,
76 wxDefaultPosition,
77 wxDefaultSize,
78 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL), 1, wxEXPAND);
79
80 rootSizer->Add(topSizer.release(), 0, wxEXPAND | wxALL, 10);
81 rootSizer->Add(mProgress =
82 safenew wxGauge(this, wxID_ANY, 1000),
83 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
84 rootSizer->AddSpacer(10);
85 rootSizer->Add(timerSizer.release(), 0, wxEXPAND);
86#ifdef __WXMAC__
87 rootSizer->Add(CreateButtonSizer(wxCANCEL), 0, wxEXPAND);
88#else
89 rootSizer->Add(CreateButtonSizer(wxCANCEL), 0, wxEXPAND | wxALL, 10);
90#endif
91 SetSizer(rootSizer.release());
92
93 Bind(wxEVT_SHOW, &PluginScanDialog::OnShow, this);
94 Bind(wxEVT_IDLE, &PluginScanDialog::OnIdle, this);
95
96 SetInitialSize({500, -1});
97 }
98
99 void UpdateProgress(const wxString& text, float progress)
100 {
101 mText->SetLabel(text);
102 mText->SetToolTip(text);
103 mProgress->SetValue(mProgress->GetRange() * progress);
104 }
105
106 void OnShow(wxShowEvent& evt)
107 {
108 evt.Skip();
109 if(evt.IsShown())
110 {
111 mStartTime = std::chrono::system_clock::now();
112 UpdateElapsedTime();
113 }
114 }
115
116 void OnIdle(wxIdleEvent& evt)
117 {
118 evt.Skip();
119 UpdateElapsedTime();
120 }
121
123 {
124 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - mStartTime).count();
125 mElapsed->SetLabel(wxTimeSpan(0, 0, 0, elapsed).Format("%H:%M:%S"));
126 }
127 };
128}
129
130PluginStartupRegistration::PluginStartupRegistration(const std::map<wxString, std::vector<wxString>>& pluginsToProcess)
131{
132 for(auto& p : pluginsToProcess)
133 mPluginsToProcess.push_back(p);
134}
135
137{
138 StopWithError(error);
139}
140
142{
144 mFailedPluginsCache.clear();
145
146 mValidProviderFound = true;
147 if(!desc.IsValid())
148 mFailedPluginsCache.push_back(desc);
150}
151
152void PluginStartupRegistration::OnPluginValidationFailed(const wxString& providerId, const wxString& path)
153{
154 PluginID ID = providerId + wxT("_") + path;
155 PluginDescriptor pluginDescriptor;
156 pluginDescriptor.SetPluginType(PluginTypeStub);
157 pluginDescriptor.SetID(ID);
158 pluginDescriptor.SetProviderID(providerId);
159 pluginDescriptor.SetPath(path);
160 pluginDescriptor.SetEnabled(false);
161 pluginDescriptor.SetValid(false);
162
163 //Multiple providers can report same module paths
164 //do not register until all associated providers have tried to load the module
165 mFailedPluginsCache.push_back(std::move(pluginDescriptor));
166}
167
168
170{
174 {
175 if(!mFailedPluginsCache.empty())
176 {
177 //we've tried all providers associated with same module path...
179 {
180 //...but none of them succeeded
181 mFailedPluginsPaths.push_back(mFailedPluginsCache[0].GetPath());
182
183 //Same plugin path, but different providers, we need to register all of them
184 for(auto& desc : mFailedPluginsCache)
186 }
187 //plugin type was detected, but plugin instance validation has failed
188 else
189 {
190 for(auto& desc : mFailedPluginsCache)
191 {
192 if(desc.GetPluginType() != PluginTypeStub)
193 mFailedPluginsPaths.push_back(desc.GetPath());
194 }
195 }
196 }
199 mValidProviderFound = false;
200 mFailedPluginsCache.clear();
201 }
202 ProcessNext();
203}
204
205const std::vector<wxString>& PluginStartupRegistration::GetFailedPluginsPaths() const noexcept
206{
207 return mFailedPluginsPaths;
208}
209
210void PluginStartupRegistration::Run(std::chrono::seconds timeout)
211{
212 PluginScanDialog dialog(nullptr, wxID_ANY, XO("Searching for plugins"));
213 wxTimer timeoutTimer(&dialog, OnPluginScanTimeout);
214 mScanDialog = &dialog;
215 mTimeoutTimer = &timeoutTimer;
216 mTimeout = timeout;
217
218 dialog.Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) {
219 evt.Skip();
220 if(evt.GetId() == wxID_IGNORE)
221 Skip();
222 });
223 dialog.Bind(wxEVT_TIMER, [this](wxTimerEvent& evt) {
224 if(evt.GetId() == OnPluginScanTimeout)
225 {
226 if(mValidator && mValidator->InactiveSince() < mRequestStartTime)
227 Skip();
228 //else
229 // wxMessageBox("Please check for plugin popups!");
230 }
231 else
232 evt.Skip();
233 });
234 dialog.Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& evt) {
235 evt.Skip();
236 mValidator.reset();
238 });
239
240 dialog.CenterOnScreen();
241 ProcessNext();
242 dialog.ShowModal();
243}
244
246{
247 if(auto dialog = mScanDialog.get())
248 dialog->Close();
249}
250
252{
253 //Drop current validator, no more callbacks will be received from now
254 mValidator->SetDelegate(nullptr);
255 //While on Linux and MacOS socket `shutdown()` wakes up `select()` almost
256 //immediately, on Windows it sometimes get delayed on unspecified amount
257 //of time. As we do not expect any data we can safely move remaining
258 //operations to another thread.
259 std::thread([validator = std::shared_ptr<AsyncPluginValidator>(std::move(mValidator))]{ }).detach();
260
262 {
263 // Validator didn't report anything yet or it tried
264 // one or more providers that didn't recognize the plugin.
265 // In that case we assume that none of the remaining providers
266 // can recognize that plugin.
267 // Note: create stub `PluginDescriptors` for each associated provider
273 }
274 //else
275 // Don't assume that `OnValidationFinished()` and `OnPluginFound()`
276 // aren't deferred within run loop
277
279}
280
282{
283 //TODO: show error dialog?
284 wxLogError("Plugin registration error: %s", msg);
285 Stop();
286}
287
289{
291 {
292 Stop();
293 return;
294 }
295
296 try
297 {
298 if(auto dialog = static_cast<PluginScanDialog*>(mScanDialog.get()))
299 {
300 const auto progress = static_cast<float>(mCurrentPluginIndex) / static_cast<float>(mPluginsToProcess.size());
301 dialog->UpdateProgress(
303 progress);
304 }
305 if(!mValidator)
306 mValidator = std::make_unique<AsyncPluginValidator>(*this);
307
308 mValidator->Validate(
311 );
312 mRequestStartTime = std::chrono::system_clock::now();
313 if(auto timer = mTimeoutTimer.get())
314 timer->StartOnce(std::chrono::duration_cast<std::chrono::milliseconds>(mTimeout).count());
315 }
316 catch(std::exception& e)
317 {
318 StopWithError(e.what());
319 }
320 catch(...)
321 {
322 StopWithError("unknown error");
323 }
324}
325
wxT("CloseDown"))
wxString PluginID
Definition: EffectManager.h:30
const TranslatableString desc
Definition: ExportPCM.cpp:58
#define XO(s)
Definition: Internat.h:31
#define _(s)
Definition: Internat.h:75
#define safenew
Definition: MemoryX.h:10
static const auto title
@ PluginTypeStub
Abstract base class used in importing a file.
void SetEnabled(bool enable)
void SetPath(const PluginPath &path)
void SetValid(bool valid)
void SetProviderID(const PluginID &providerID)
void SetID(const PluginID &ID)
void SetPluginType(PluginType type)
void Save()
Save to preferences.
void RegisterPlugin(PluginDescriptor &&desc)
static PluginManager & Get()
void OnInternalError(const wxString &error) override
Called on error, further processing is not possible.
void Run(std::chrono::seconds timeout=std::chrono::seconds(30))
std::unique_ptr< AsyncPluginValidator > mValidator
void OnValidationFinished() override
Called when module processing finished.
std::vector< PluginDescriptor > mFailedPluginsCache
wxWeakRef< wxDialogWrapper > mScanDialog
void OnPluginFound(const PluginDescriptor &desc) override
Called for each plugin instance found inside module.
std::chrono::system_clock::time_point mRequestStartTime
void StopWithError(const wxString &msg)
std::vector< wxString > mFailedPluginsPaths
void OnPluginValidationFailed(const wxString &providerId, const wxString &path) override
const std::vector< wxString > & GetFailedPluginsPaths() const noexcept
Returns list of paths of plugins that didn't pass validation for some reason.
std::vector< std::pair< wxString, std::vector< wxString > > > mPluginsToProcess
PluginStartupRegistration(const std::map< wxString, std::vector< wxString > > &pluginsToProcess)
std::chrono::system_clock::duration mTimeout
Holds a msgid for the translation catalog; may also bind format arguments.
PluginScanDialog(wxWindow *parent, wxWindowID winid, const TranslatableString &title)