OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::PluginManager Class Reference
module nullptr

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/slic3r/plugin/PluginManager.hpp>

Collaboration diagram for Slic3r::PluginManager:

Classes

struct  ScriptLaunchResult

Public Types

using PluginLifecycleCompleteFn = std::function<void(const std::string& )>
using CapabilityLifecycleFn = std::function<void(const PluginCapabilityId&)>
using ScriptCompletionFn = std::function<void(const ScriptCompletion&)>

Public Member Functions

 ~PluginManager ()
bool initialize ()
void shutdown ()
void set_shutting_down ()
bool is_shutting_down () const
void discover_plugins (bool async=false, bool clear=false)
void rescan_plugins ()
PluginConfig & get_config ()
const PluginConfig & get_config () const
bool is_discovery_complete () const
bool is_discovery_in_progress () const
std::string get_discovery_error () const
bool wait_for_discovery (std::chrono::milliseconds timeout, std::string &error) const
std::vector< PluginDescriptor > get_plugin_descriptors (bool include_invalid=false) const
bool try_get_plugin_descriptor (const std::string &plugin_key, PluginDescriptor &out) const
bool try_get_valid_plugin_descriptor (const std::string &plugin_key, PluginDescriptor &out) const
std::vector< PluginDescriptor > get_missing_plugin_descriptors () const
void remove_missing_plugins (const std::vector< std::string > &plugin_keys)
std::vector< std::string > get_enabled_plugin_keys () const
bool try_get_plugin_descriptor_for_capability (const std::string &capability_name, PluginCapabilityType type, PluginDescriptor &out) const
std::string get_storage_dir (const std::string &plugin_key) const
std::vector< std::shared_ptr< PluginCapabilityInterface > > get_plugin_capabilities (const std::string &plugin_key="", PluginCapabilityType type=PluginCapabilityType::Unknown, bool only_enabled=true) const
std::shared_ptr< PluginCapabilityInterface > get_plugin_capability (const PluginCapabilityId &id, bool only_enabled=true) const
std::shared_ptr< PluginCapabilityInterface > get_plugin_capability (const std::string &capability_name, PluginCapabilityType type=PluginCapabilityType::Unknown, bool only_enabled=true) const
bool get_install_state (const std::string &plugin_key, PluginInstallState &install_state)
void load_plugin (const std::string &plugin_key, bool skip_deps=false, std::vector< std::string > capabilities_to_enable={})
bool unload_plugin (const std::string &plugin_key)
void unload_all_plugins ()
void unload_cloud_plugins ()
bool is_plugin_loaded (const std::string &plugin_key) const
bool is_plugin_load_in_progress (const std::string &plugin_key) const
void wait_for_all_plugin_loads () const
bool wait_for_all_plugin_loads (std::chrono::milliseconds timeout) const
bool wait_for_plugin_load (const std::string &plugin_key, std::chrono::milliseconds timeout, std::string &error) const
bool cancel_plugin_load (const std::string &plugin_key)
std::string get_plugin_load_error (const std::string &plugin_key) const
void set_capability_enabled (const PluginCapabilityId &id, bool enabled)
void set_cloud_user (const std::string &user_id)
void subscribe_on_load_callback (PluginLifecycleCompleteFn fn)
void subscribe_on_unload_callback (PluginLifecycleCompleteFn fn)
void subscribe_on_capability_load_callback (CapabilityLifecycleFn fn)
void subscribe_on_capability_unload_callback (CapabilityLifecycleFn fn)
void set_cloud_agent (std::shared_ptr< OrcaCloudServiceAgent > agent)
bool install_plugin (const boost::filesystem::path &filepath, std::string &error)
bool install_plugin (const boost::filesystem::path &filepath, PluginDescriptor &plugin_descriptor, std::string &error)
bool inspect_local_plugin_package (const boost::filesystem::path &filepath, PluginDescriptor &plugin_descriptor, bool &existing_installation, std::string &error) const
bool set_plugin_error (const std::string &plugin_key, std::string error)
bool clear_plugin_error (const std::string &plugin_key)
void fetch_plugins_from_cloud (std::vector< std::string > *out_not_found=nullptr, std::vector< std::string > *out_unauthorized=nullptr)
void update_cloud_metadata (const std::vector< PluginDescriptor > &cloud_list)
void clear_cloud_plugin_metadata ()
bool download_and_install_cloud_plugin (const std::string &plugin_key, const std::string &version, std::string &error)
bool subscribe_and_install_cloud_plugin (const std::string &plugin_key, std::string &error)
bool update_cloud_plugin (const std::string &plugin_key, std::string &error, std::string version="")
bool delete_plugin (const std::string &plugin_key, std::string &error)
bool unsubscribe_cloud_plugin (const std::string &plugin_key, std::string &error)
bool delete_and_unsubscribe_cloud_plugin (const std::string &plugin_key, std::string &error)
ExecutionResult run_script_capability (const std::string &plugin_key, const std::string &capability_name, std::string &error)
ScriptLaunchResult launch_script_capability (const std::string &plugin_key, const std::string &capability_name, ScriptCompletionFn on_done={})
void dispatch_lifecycle_event (LifecycleEvent evt, const LifecycleEventContext &ctx)

Static Public Member Functions

static PluginManager & instance ()

Private Types

enum class  CallbackType { Load , Unload }

Private Member Functions

 PluginManager ()=default
 PluginManager (const PluginManager &)=delete
PluginManager & operator= (const PluginManager &)=delete
Plugin * find_plugin_locked (const std::string &plugin_key)
const Plugin * find_plugin_locked (const std::string &plugin_key) const
void load_plugin_impl (const std::string &plugin_key, bool skip_deps, const std::vector< std::string > &capabilities_to_enable)
std::string check_registry_locked (const std::string &plugin_key, const Plugin &candidate) const
void run_discovery (bool async, bool clear)
void run_discovery_task (bool clear)
void merge_discovered_plugins (std::vector< PluginDescriptor > discovered, bool clear)
void unload_and_erase_if (const std::function< bool(const Plugin &)> &should_remove, const std::function< void()> &after_erase_locked={})
bool cancel_plugin_load_locked (const std::string &plugin_key)
bool is_plugin_load_cancelled_locked (const std::string &plugin_key) const
void notify_plugin_load_state_changed (bool changed)
void release_load_slot (const std::string &plugin_key)
void cancel_and_wait_for_capabilities (const std::vector< std::shared_ptr< PluginCapabilityInterface > > &capabilities)
std::vector< PluginLifecycleCompleteFn > copy_callbacks (CallbackType type) const
std::vector< CapabilityLifecycleFn > copy_capability_callbacks (CallbackType type) const
void run_on_load_callbacks (const std::string &plugin_key)
void run_on_unload_callbacks (const std::string &plugin_key)
void run_on_capability_load_callbacks (const PluginCapabilityId &id)
void run_on_capability_unload_callbacks (const PluginCapabilityId &id)
void clear_callbacks ()
void write_loaded_plugin_install_state (const std::string &plugin_key)
void mark_plugin_install_state_disabled (const std::string &plugin_key)
void revoke_plugin_permissions (const std::string &plugin_key)
bool finalize_cloud_plugin_removal (const PluginDescriptor &plugin, bool keep_local, std::string &error)
bool delete_installed_plugin_package (const PluginDescriptor &plugin, std::string &error)
bool keep_installed_plugin_as_local (const PluginDescriptor &plugin_descriptor, std::string &error)

Private Attributes

bool m_initialized = false
CloudPluginService m_cloud_service
PluginConfig m_config
std::mutex m_mutex
std::vector< Plugin > m_plugins
std::unordered_set< std::string > m_missing_plugin_keys
std::unordered_set< std::string > m_load_in_progress
std::unordered_set< std::string > m_cancelled
std::map< std::string, std::string > m_load_errors
std::condition_variable m_load_cv
std::mutex m_install_state_mutex
std::map< CallbackType, std::vector< PluginLifecycleCompleteFn > > m_callbacks
std::map< CallbackType, std::vector< CapabilityLifecycleFn > > m_capability_callbacks
bool m_discovery_complete = false
bool m_discovery_in_progress = false
std::string m_discovery_error
std::condition_variable m_discovery_cv
std::string m_cloud_user_id
std::atomic< bool > m_shutting_down {false}

Detailed Description


Class Documentation

◆ Slic3r::PluginManager::ScriptLaunchResult

struct Slic3r::PluginManager::ScriptLaunchResult
Collaboration diagram for Slic3r::PluginManager::ScriptLaunchResult:
Class Members
bool launched_async = false
ScriptCompletion outcome

Member Typedef Documentation

◆ CapabilityLifecycleFn

◆ PluginLifecycleCompleteFn

using Slic3r::PluginManager::PluginLifecycleCompleteFn = std::function<void(const std::string& )>

◆ ScriptCompletionFn

using Slic3r::PluginManager::ScriptCompletionFn = std::function<void(const ScriptCompletion&)>

Member Enumeration Documentation

◆ CallbackType

enum class Slic3r::PluginManager::CallbackType
strongprivate
Enumerator
Load 
Unload 
258{ Load, Unload };

Constructor & Destructor Documentation

◆ ~PluginManager()

Slic3r::PluginManager::~PluginManager ( )
85{ shutdown(); }
void shutdown()
Definition PluginManager.cpp:150

References shutdown().

Here is the call graph for this function:

◆ PluginManager() [1/2]

Slic3r::PluginManager::PluginManager ( )
privatedefault

Referenced by PluginManager(), instance(), and operator=().

Here is the caller graph for this function:

◆ PluginManager() [2/2]

Slic3r::PluginManager::PluginManager ( const PluginManager & )
privatedelete

References PluginManager().

Here is the call graph for this function:

Member Function Documentation

◆ cancel_and_wait_for_capabilities()

void Slic3r::PluginManager::cancel_and_wait_for_capabilities ( const std::vector< std::shared_ptr< PluginCapabilityInterface > > & capabilities)
private
771{
772 // Ask the plugin to cancel first, then schedule the async cancellation: on_cancelled() can
773 // observe/record state while the run is still intact, and only afterwards is its asyncio task
774 // cancelled. Draining of capability references still follows, so the module is not unloaded
775 // until the run has settled.
776 for (const auto& capability : capabilities) {
777 if (!capability || !capability->is_enabled())
778 continue;
779
780 try {
781 capability->on_cancelled();
782 } catch (const std::exception& ex) {
783 BOOST_LOG_TRIVIAL(warning) << "Plugin on_cancelled failed for '" << capability->audit_plugin_key() << "/"
784 << capability->name() << "': " << ex.what();
785 } catch (...) {
786 BOOST_LOG_TRIVIAL(warning) << "Plugin on_cancelled failed for '" << capability->audit_plugin_key() << "/"
787 << capability->name() << "'";
788 }
789 }
790
791 for (const auto& capability : capabilities) {
792 if (capability)
793 ScriptAsyncRuntime::instance().cancel_plugin(capability->audit_plugin_key());
794 }
795
796 // Script rows are bounded only by their own worker: a cancelled or stalled invocation of another
797 // plugin can no longer couple to this one (each invocation owns a thread). A Script worker is
798 // drained without a deadline — forcing module unload while one is outstanding would leave Python
799 // executing against a freed module, or let a blocking preset selection mutate presets after the
800 // plugin is gone. Non-Script synchronous calls keep the historical bounded grace. On the GUI
801 // thread the event loop is pumped so a modal selection can settle instead of being starved by a
802 // blocking wait.
803 const auto grace_deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
804 for (;;) {
805 const bool all_released = std::all_of(capabilities.begin(), capabilities.end(), [](const auto& capability) {
806 return !capability || capability->ref_count() == 0;
807 });
808 // A cancellation that arrived before the invocation ran skips the ref counter, so an active
809 // worker must also be gone: never unload the module while any Script thread is still live.
810 const bool invocation_active = std::any_of(capabilities.begin(), capabilities.end(), [](const auto& capability) {
811 return capability != nullptr && ScriptAsyncRuntime::instance().has_active_run(capability.get());
812 });
813 if (all_released && !invocation_active)
814 return;
815
816 if (!invocation_active && std::chrono::steady_clock::now() >= grace_deadline)
817 break;
818
819 if (wxTheApp != nullptr && wxIsMainThread())
820 wxYield();
821 std::this_thread::sleep_for(std::chrono::milliseconds(5));
822 }
823
824 for (const auto& capability : capabilities) {
825 if (!capability)
826 continue;
827
828 const int refs = capability->ref_count();
829 if (refs > 0) {
830 BOOST_LOG_TRIVIAL(warning) << "Forcing unload of plugin capability with " << refs
831 << " active call(s): " << capability->audit_plugin_key() << "/" << capability->name();
832 }
833 }
834}
void cancel_plugin(const std::string &plugin_key)
Definition PluginManager.cpp:2326
static ScriptAsyncRuntime & instance()
Definition PluginManager.cpp:2280

References Slic3r::ScriptAsyncRuntime::cancel_plugin(), and Slic3r::ScriptAsyncRuntime::instance().

Referenced by unload_all_plugins(), and unload_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cancel_plugin_load()

bool Slic3r::PluginManager::cancel_plugin_load ( const std::string & plugin_key)
729{
730 bool cancelled = false;
731 {
732 std::lock_guard<std::mutex> lock(m_mutex);
733 cancelled = cancel_plugin_load_locked(plugin_key);
734 }
735
737 return cancelled;
738}
std::mutex m_mutex
Definition PluginManager.hpp:315
void notify_plugin_load_state_changed(bool changed)
Definition PluginManager.cpp:752
bool cancel_plugin_load_locked(const std::string &plugin_key)
Definition PluginManager.cpp:740
@ lock
Definition GLGizmoEmboss.cpp:209

References cancel_plugin_load_locked(), m_mutex, and notify_plugin_load_state_changed().

Here is the call graph for this function:

◆ cancel_plugin_load_locked()

bool Slic3r::PluginManager::cancel_plugin_load_locked ( const std::string & plugin_key)
private
741{
742 if (m_load_in_progress.count(plugin_key) == 0)
743 return false;
744
745 m_cancelled.insert(plugin_key);
746 m_load_errors.erase(plugin_key);
747 return true;
748}
std::unordered_set< std::string > m_cancelled
Definition PluginManager.hpp:327
std::map< std::string, std::string > m_load_errors
Definition PluginManager.hpp:328
std::unordered_set< std::string > m_load_in_progress
Definition PluginManager.hpp:321

References m_cancelled, m_load_errors, and m_load_in_progress.

Referenced by cancel_plugin_load(), and unload_plugin().

Here is the caller graph for this function:

◆ check_registry_locked()

std::string Slic3r::PluginManager::check_registry_locked ( const std::string & plugin_key,
const Plugin & candidate ) const
private
221{
222 const Plugin* entry = find_plugin_locked(plugin_key);
223 if (entry != nullptr && entry->is_loaded())
224 return GUI::format(_u8L("Plugin package is already loaded: %1%"), plugin_key);
225
226 // A capability name must be unique per type across every loaded package.
227 for (const auto& capability : candidate.capabilities) {
228 if (!capability)
229 continue;
230 for (const Plugin& other : m_plugins) {
231 if (!other.is_loaded() || other.descriptor.plugin_key == plugin_key)
232 continue;
233 for (const auto& existing : other.capabilities) {
234 if (existing && existing->type() == capability->type() && existing->name() == capability->name())
235 // TRN %1% is the capability type (e.g. Script), %2% the capability name.
236 return GUI::format(_u8L("Capability collision for type %1% and name '%2%'"),
237 PluginConfig::capability_type_label(capability->type()), capability->name());
238 }
239 }
240 }
241
242 return {};
243}
static std::string capability_type_label(PluginCapabilityType type)
Definition PluginConfig.cpp:522
std::vector< Plugin > m_plugins
Definition PluginManager.hpp:318
Plugin * find_plugin_locked(const std::string &plugin_key)
Definition PluginManager.cpp:206
@ Plugin
Definition ActionRegistry.hpp:25
std::string format(const char *fmt, TArgs &&... args)
Definition format.hpp:36
#define _u8L(s)
Definition I18N.hpp:11

References _u8L, Slic3r::Plugin::capabilities, Slic3r::PluginConfig::capability_type_label(), find_plugin_locked(), Slic3r::GUI::format(), Slic3r::Plugin::is_loaded(), and m_plugins.

Referenced by load_plugin_impl().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ clear_callbacks()

void Slic3r::PluginManager::clear_callbacks ( )
private
1282{
1283 std::lock_guard<std::mutex> lock(m_mutex);
1284 m_callbacks.clear();
1285 m_capability_callbacks.clear();
1286}
std::map< CallbackType, std::vector< PluginLifecycleCompleteFn > > m_callbacks
Definition PluginManager.hpp:334
std::map< CallbackType, std::vector< CapabilityLifecycleFn > > m_capability_callbacks
Definition PluginManager.hpp:335

References m_callbacks, m_capability_callbacks, and m_mutex.

Referenced by shutdown().

Here is the caller graph for this function:

◆ clear_cloud_plugin_metadata()

void Slic3r::PluginManager::clear_cloud_plugin_metadata ( )
1590{
1591 // Cloud entries may own live Python modules. Unload them before erasing their vector entries,
1592 // and do so outside m_mutex because both Python teardown and lifecycle callbacks can re-enter
1593 // the manager.
1595 [](const Plugin& plugin) { return plugin.descriptor.is_cloud_plugin(); },
1596 [this]() {
1597 // A local plugin can still be carrying a stale "not found in the cloud" error from
1598 // when it was cloud-tracked.
1599 for (Plugin& plugin : m_plugins) {
1600 if (plugin.descriptor.normalized_error() == CLOUD_PLUGIN_NOT_FOUND_ERROR)
1601 plugin.descriptor.clear_error();
1602 }
1603 });
1604
1605 BOOST_LOG_TRIVIAL(info) << "Cleared cloud plugin metadata";
1606}
void unload_and_erase_if(const std::function< bool(const Plugin &)> &should_remove, const std::function< void()> &after_erase_locked={})
Definition PluginManager.cpp:410

References Slic3r::PluginDescriptor::clear_error(), Slic3r::Plugin::descriptor, Slic3r::PluginDescriptor::is_cloud_plugin(), m_plugins, Slic3r::PluginDescriptor::normalized_error(), and unload_and_erase_if().

Referenced by Slic3r::GUI::GUI_App::request_user_logout().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ clear_plugin_error()

bool Slic3r::PluginManager::clear_plugin_error ( const std::string & plugin_key)
1412{
1413 std::string wait_error;
1414 if (!wait_for_discovery(std::chrono::milliseconds::max(), wait_error))
1415 return false;
1416
1417 std::lock_guard<std::mutex> lock(m_mutex);
1418 Plugin* plugin = find_plugin_locked(plugin_key);
1419 if (plugin == nullptr || !plugin->descriptor.is_metadata_valid())
1420 return false;
1421
1422 plugin->descriptor.clear_error();
1423 return true;
1424}
bool wait_for_discovery(std::chrono::milliseconds timeout, std::string &error) const
Definition PluginManager.cpp:465

References Slic3r::PluginDescriptor::clear_error(), Slic3r::Plugin::descriptor, find_plugin_locked(), Slic3r::PluginDescriptor::is_metadata_valid(), m_mutex, and wait_for_discovery().

Referenced by download_and_install_cloud_plugin(), install_plugin(), load_plugin(), load_plugin_impl(), Slic3r::GUI::PluginsDialog::run_script_plugin_capability(), Slic3r::GUI::PluginsDialog::toggle_plugin(), and update_cloud_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ copy_callbacks()

std::vector< PluginManager::PluginLifecycleCompleteFn > Slic3r::PluginManager::copy_callbacks ( CallbackType type) const
private
1289{
1290 std::lock_guard<std::mutex> lock(m_mutex);
1291 const auto it = m_callbacks.find(type);
1292 return it == m_callbacks.end() ? std::vector<PluginLifecycleCompleteFn>{} : it->second;
1293}

References m_callbacks, and m_mutex.

Referenced by run_on_load_callbacks(), and run_on_unload_callbacks().

Here is the caller graph for this function:

◆ copy_capability_callbacks()

std::vector< PluginManager::CapabilityLifecycleFn > Slic3r::PluginManager::copy_capability_callbacks ( CallbackType type) const
private
1296{
1297 std::lock_guard<std::mutex> lock(m_mutex);
1298 const auto it = m_capability_callbacks.find(type);
1299 return it == m_capability_callbacks.end() ? std::vector<CapabilityLifecycleFn>{} : it->second;
1300}

References m_capability_callbacks, and m_mutex.

Referenced by run_on_capability_load_callbacks(), and run_on_capability_unload_callbacks().

Here is the caller graph for this function:

◆ delete_and_unsubscribe_cloud_plugin()

bool Slic3r::PluginManager::delete_and_unsubscribe_cloud_plugin ( const std::string & plugin_key,
std::string & error )
2076{
2077 if (!wait_for_discovery(std::chrono::milliseconds::max(), error))
2078 return false;
2079
2080 error.clear();
2081
2082 PluginDescriptor descriptor;
2083 if (!try_get_plugin_descriptor(plugin_key, descriptor)) {
2084 error = GUI::format(_u8L("Plugin not found: %1%"), plugin_key);
2085 return false;
2086 }
2087
2088 if (!descriptor.is_cloud_plugin()) {
2089 error = _u8L("Only cloud plugins can be deleted and unsubscribed.");
2090 set_plugin_error(plugin_key, error);
2091 return false;
2092 }
2093
2094 if (descriptor.cloud->is_mine) {
2095 error = _u8L("Cannot unsubscribe your own plugins. Use Delete to remove the local files.");
2096 set_plugin_error(plugin_key, error);
2097 return false;
2098 }
2099
2100 unload_plugin(plugin_key);
2101
2102 if (!m_cloud_service.request_cloud_unsubscribe(descriptor, error)) {
2103 set_plugin_error(plugin_key, error);
2104 return false;
2105 }
2106
2107 return finalize_cloud_plugin_removal(descriptor, false, error);
2108}
bool finalize_cloud_plugin_removal(const PluginDescriptor &plugin, bool keep_local, std::string &error)
Definition PluginManager.cpp:1992
bool unload_plugin(const std::string &plugin_key)
Definition PluginManager.cpp:1048
bool try_get_plugin_descriptor(const std::string &plugin_key, PluginDescriptor &out) const
Definition PluginManager.cpp:502
CloudPluginService m_cloud_service
Definition PluginManager.hpp:309
bool set_plugin_error(const std::string &plugin_key, std::string error)
Definition PluginManager.cpp:1396

References _u8L, Slic3r::PluginDescriptor::cloud, finalize_cloud_plugin_removal(), Slic3r::GUI::format(), Slic3r::PluginDescriptor::is_cloud_plugin(), m_cloud_service, set_plugin_error(), try_get_plugin_descriptor(), unload_plugin(), and wait_for_discovery().

Here is the call graph for this function:

◆ delete_installed_plugin_package()

bool Slic3r::PluginManager::delete_installed_plugin_package ( const PluginDescriptor & plugin,
std::string & error )
private
1873{
1874 boost::filesystem::path resolved_root;
1875 bool resolved_allowed_plugin_root = false;
1876 {
1877 std::lock_guard<std::mutex> lock(m_mutex);
1878 resolved_allowed_plugin_root = resolve_allowed_plugin_root(plugin, get_plugin_directories(m_cloud_user_id),
1879 _u8L("Refusing to delete a plugin outside the known plugin directories."),
1880 resolved_root, error);
1881 }
1882 if (!resolved_allowed_plugin_root)
1883 return false;
1884
1885 unload_plugin(plugin.plugin_key);
1886
1887 if (!delete_plugin_root(resolved_root, plugin.plugin_key, error))
1888 return false;
1889
1890 {
1891 std::lock_guard<std::mutex> lock(m_mutex);
1892 m_plugins.erase(std::remove_if(m_plugins.begin(), m_plugins.end(),
1893 [&plugin](const Plugin& entry) { return entry.descriptor.plugin_key == plugin.plugin_key; }),
1894 m_plugins.end());
1895 }
1896
1897 return true;
1898}
std::string m_cloud_user_id
Definition PluginManager.hpp:342
std::vector< std::string > get_plugin_directories(const std::string &cloud_user_id)
Definition PluginFsUtils.cpp:223
bool delete_plugin_root(const boost::filesystem::path &resolved_root, const std::string &plugin_id, std::string &error)
Definition PluginFsUtils.cpp:118
bool resolve_allowed_plugin_root(const PluginDescriptor &descriptor, const std::vector< std::string > &allowed_dirs, const std::string &out_of_scope_error, boost::filesystem::path &resolved_root, std::string &error)
Definition PluginFsUtils.cpp:85

References _u8L, Slic3r::delete_plugin_root(), Slic3r::get_plugin_directories(), m_cloud_user_id, m_mutex, m_plugins, Slic3r::PluginDescriptor::plugin_key, Slic3r::resolve_allowed_plugin_root(), and unload_plugin().

Referenced by delete_plugin(), and finalize_cloud_plugin_removal().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ delete_plugin()

bool Slic3r::PluginManager::delete_plugin ( const std::string & plugin_key,
std::string & error )
2021{
2022 if (!wait_for_discovery(std::chrono::milliseconds::max(), error))
2023 return false;
2024
2025 error.clear();
2026
2027 PluginDescriptor descriptor;
2028 if (!try_get_plugin_descriptor(plugin_key, descriptor)) {
2029 error = GUI::format(_u8L("Plugin not found: %1%"), plugin_key);
2030 return false;
2031 }
2032
2033 if (!delete_installed_plugin_package(descriptor, error)) {
2034 set_plugin_error(plugin_key, error);
2035 return false;
2036 }
2037
2038 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Deleted plugin: " << plugin_key;
2039 return true;
2040}
bool delete_installed_plugin_package(const PluginDescriptor &plugin, std::string &error)
Definition PluginManager.cpp:1872

References _u8L, delete_installed_plugin_package(), Slic3r::GUI::format(), set_plugin_error(), try_get_plugin_descriptor(), and wait_for_discovery().

Referenced by Slic3r::GUI::PluginsDialog::delete_local_plugin(), and Slic3r::GUI::PluginsDialog::reinstall_cloud_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ discover_plugins()

void Slic3r::PluginManager::discover_plugins ( bool async = false,
bool clear = false )
248{
249 if (!initialize())
250 return;
251
252 {
253 std::lock_guard<std::mutex> lock(m_mutex);
255 BOOST_LOG_TRIVIAL(debug) << "Plugin discovery already running";
256 return;
257 }
258 if (clear) {
259 for (Plugin& plugin : m_plugins)
260 plugin.descriptor.clear_error();
261 }
263 m_discovery_complete = false;
264 m_discovery_error.clear();
265 }
266
267 run_discovery(async, clear);
268}
bool m_discovery_complete
Definition PluginManager.hpp:337
void run_discovery(bool async, bool clear)
Definition PluginManager.cpp:280
bool initialize()
Definition PluginManager.cpp:87
std::string m_discovery_error
Definition PluginManager.hpp:339
bool m_discovery_in_progress
Definition PluginManager.hpp:338

References initialize(), m_discovery_complete, m_discovery_error, m_discovery_in_progress, m_mutex, m_plugins, and run_discovery().

Referenced by Slic3r::GUI::GUI_App::on_init_inner(), and rescan_plugins().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ dispatch_lifecycle_event()

void Slic3r::PluginManager::dispatch_lifecycle_event ( LifecycleEvent evt,
const LifecycleEventContext & ctx )
2491 {
2492 const auto is_canceled = [&ctx]() {
2493 return ctx.cancellation_check && ctx.cancellation_check();
2494 };
2495
2496 if (is_canceled())
2497 return;
2498
2499 for (const auto& cap : get_plugin_capabilities()) {
2500 if (!cap || !cap->is_enabled()) continue;
2501 if (is_canceled())
2502 break;
2503 try {
2504 cap->on_lifecycle_event(evt, ctx);
2505 } catch (const std::exception& ex) {
2506 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": plugin '" << cap->audit_plugin_key() << "/" << cap->name()
2507 << "' on_lifecycle_event(" << lifecycle_event_to_string(evt) << ") threw: " << ex.what()
2508 << " [ctx name='" << ctx.name << "', code=" << lifecycle_evt_code_to_string(ctx.code)
2509 << ", msg='" << ctx.msg << "']";
2510 } catch (...) {
2511 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": plugin '" << cap->audit_plugin_key() << "/" << cap->name()
2512 << "' on_lifecycle_event(" << lifecycle_event_to_string(evt)
2513 << ") threw a non-standard exception"
2514 << " [ctx name='" << ctx.name << "', code=" << lifecycle_evt_code_to_string(ctx.code)
2515 << ", msg='" << ctx.msg << "']";
2516 }
2517 }
2518}
std::vector< std::shared_ptr< PluginCapabilityInterface > > get_plugin_capabilities(const std::string &plugin_key="", PluginCapabilityType type=PluginCapabilityType::Unknown, bool only_enabled=true) const
Definition PluginManager.cpp:592
std::string lifecycle_event_to_string(LifecycleEvent event)
Definition LifecycleEvents.hpp:113
std::string lifecycle_evt_code_to_string(LifecycleEvtCode code)
Definition LifecycleEvents.hpp:158

References Slic3r::LifecycleEventContext::cancellation_check, Slic3r::LifecycleEventContext::code, get_plugin_capabilities(), Slic3r::lifecycle_event_to_string(), Slic3r::lifecycle_evt_code_to_string(), Slic3r::LifecycleEventContext::msg, and Slic3r::LifecycleEventContext::name.

Referenced by Slic3r::plugin_hooks::anonymous_namespace{PluginHooks.cpp}::install_lifecycle_event_hook().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ download_and_install_cloud_plugin()

bool Slic3r::PluginManager::download_and_install_cloud_plugin ( const std::string & plugin_key,
const std::string & version,
std::string & error )
1728{
1729 error.clear();
1730
1731 CloudPluginDownload download;
1732 PluginDescriptor descriptor;
1733 if (!try_get_plugin_descriptor(plugin_key, descriptor)) {
1734 error = GUI::format(_u8L("Plugin not found: %1%"), plugin_key);
1735 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": Failed to find plugin key " << plugin_key;
1736 return false;
1737 }
1738
1739 clear_plugin_error(plugin_key);
1740
1741 const std::string requested_version = version.empty() ? descriptor.version : version;
1742 if (!m_cloud_service.download_cloud_plugin(descriptor, requested_version, download, error)) {
1743 if (error.empty())
1744 error = _u8L("Failed to download cloud plugin.");
1745 set_plugin_error(plugin_key, error);
1746 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << error;
1747 return false;
1748 }
1749
1750 // Ensure cloud plugins install to _subscribed/<user_id>/
1751 if (auto agent = m_cloud_service.get_cloud_agent()) {
1752 const std::string user_id = agent->get_user_id();
1753 if (!user_id.empty())
1754 set_cloud_user(user_id);
1755 }
1756
1757 // Record the version we just fetched from the cloud so install_plugin persists it to the
1758 // install-state sidecar (the source of truth for the installed cloud version) instead of the
1759 // local manifest/PEP723 header version.
1760 descriptor.installed_version = requested_version;
1761
1762 if (!install_plugin(download.package_path, descriptor, error)) {
1763 if (error.empty())
1764 error = _u8L("Failed to install cloud plugin.");
1765 set_plugin_error(plugin_key, error);
1766 boost::system::error_code ec;
1767 boost::filesystem::remove(download.package_path, ec);
1768 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << error;
1769 return false;
1770 }
1771
1772 PluginDescriptor updated_descriptor = descriptor;
1773 if (!requested_version.empty())
1774 updated_descriptor.version = requested_version;
1775 // The version just downloaded and installed is now the locally installed version.
1776 updated_descriptor.installed_version = requested_version.empty() ? descriptor.version : requested_version;
1777 updated_descriptor.enabled = true;
1778 if (updated_descriptor.cloud.has_value()) {
1779 updated_descriptor.cloud->installed = true;
1780 updated_descriptor.cloud->update_available = false;
1781 updated_descriptor.cloud->unauthorized = false;
1782 }
1783 updated_descriptor.clear_error();
1784 updated_descriptor.set_unauthorized(false);
1785
1786 {
1787 std::lock_guard<std::mutex> lock(m_mutex);
1788 Plugin* entry = find_plugin_locked(plugin_key);
1789 if (entry == nullptr) {
1790 error = _u8L("Plugin not found.");
1791 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": Cloud plugin " << plugin_key
1792 << " downloaded successfully but failed to update plugin metadata. Plugin object not found.";
1793 } else {
1794 entry->descriptor = std::move(updated_descriptor);
1795 }
1796 }
1797 if (!error.empty()) {
1798 set_plugin_error(plugin_key, error);
1799 return false;
1800 }
1801
1802 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Cloud plugin " << plugin_key << " installed successfully";
1803 return true;
1804}
void set_cloud_user(const std::string &user_id)
Definition PluginManager.cpp:1388
bool clear_plugin_error(const std::string &plugin_key)
Definition PluginManager.cpp:1411
bool install_plugin(const boost::filesystem::path &filepath, std::string &error)
Definition PluginManager.cpp:1434

References _u8L, Slic3r::PluginDescriptor::clear_error(), clear_plugin_error(), Slic3r::PluginDescriptor::cloud, Slic3r::Plugin::descriptor, Slic3r::PluginDescriptor::enabled, find_plugin_locked(), Slic3r::GUI::format(), install_plugin(), Slic3r::PluginDescriptor::installed_version, m_cloud_service, m_mutex, Slic3r::CloudPluginDownload::package_path, set_cloud_user(), set_plugin_error(), Slic3r::PluginDescriptor::set_unauthorized(), try_get_plugin_descriptor(), and Slic3r::PluginDescriptor::version.

Referenced by Slic3r::GUI::PluginsDialog::install_cloud_plugin(), subscribe_and_install_cloud_plugin(), and update_cloud_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fetch_plugins_from_cloud()

void Slic3r::PluginManager::fetch_plugins_from_cloud ( std::vector< std::string > * out_not_found = nullptr,
std::vector< std::string > * out_unauthorized = nullptr )
1609{
1610 if (!m_cloud_service.can_fetch_cloud_plugins())
1611 return;
1612
1613 std::vector<PluginDescriptor> cloud_list{};
1614 std::vector<std::string> not_found{}, unauthorized{};
1615 const bool cloud_fetch_succeeded = m_cloud_service.fetch_manifests_into_descriptors(cloud_list, not_found, unauthorized);
1616 if (!cloud_fetch_succeeded) {
1617 if (wxTheApp != nullptr) {
1618 GUI::wxGetApp().CallAfter([] {
1619 if (GUI::wxGetApp().is_closing())
1620 return;
1621 GUI::Plater* plater = GUI::wxGetApp().plater();
1622 if (plater == nullptr || GUI::wxGetApp().imgui() == nullptr || !GUI::wxGetApp().imgui()->display_initialized())
1623 return;
1624 plater->get_notification_manager()->push_notification(GUI::NotificationType::CustomNotification,
1626 _u8L("Failed to fetch plugins from the cloud. See logs for details."));
1627 });
1628 }
1629 }
1630
1631 if (cloud_fetch_succeeded)
1632 update_cloud_metadata(cloud_list);
1633
1634 if (cloud_fetch_succeeded) {
1635 std::lock_guard<std::mutex> lock(m_mutex);
1636
1637 // Clear the previous cloud verdicts before re-applying the fresh ones.
1638 for (Plugin& plugin : m_plugins) {
1639 PluginDescriptor& entry = plugin.descriptor;
1640 if (!entry.is_cloud_plugin())
1641 continue;
1642 entry.set_unauthorized(false);
1643 if (entry.cloud.has_value())
1644 entry.cloud->orphaned = false;
1645 if (entry.normalized_error() == CLOUD_PLUGIN_NOT_FOUND_ERROR)
1646 entry.clear_error();
1647 }
1648
1649 // A successful subscriptions response may report missing UUIDs explicitly, or it may
1650 // simply omit an unsubscribed plugin from `data`. Both cases leave a locally retained
1651 // cloud package orphaned. Owned plugins are returned by the separate mine endpoint and
1652 // must not be orphaned merely because they are not subscribed.
1653 for (Plugin& plugin : m_plugins) {
1654 PluginDescriptor& entry = plugin.descriptor;
1655 if (!entry.is_cloud_plugin() || entry.cloud->is_mine)
1656 continue;
1657
1658 const bool explicitly_not_found = std::find(not_found.begin(), not_found.end(), entry.cloud_uuid()) != not_found.end();
1659 const bool returned_by_cloud = std::any_of(cloud_list.begin(), cloud_list.end(), [&entry](const PluginDescriptor& cloud_entry) {
1660 return cloud_entry.cloud_uuid() == entry.cloud_uuid();
1661 });
1662 entry.cloud->orphaned = explicitly_not_found || !returned_by_cloud;
1663 if (entry.cloud->orphaned)
1664 entry.cloud->update_available = false;
1665 }
1666
1667 for (const std::string& uuid : unauthorized) {
1668 for (Plugin& plugin : m_plugins) {
1669 PluginDescriptor& entry = plugin.descriptor;
1670 if (!entry.is_cloud_plugin() || entry.cloud_uuid() != uuid)
1671 continue;
1672 entry.set_unauthorized(true);
1673 if (entry.cloud.has_value())
1674 entry.cloud->update_available = false;
1675 break;
1676 }
1677 }
1678 }
1679
1680 if (out_not_found)
1681 *out_not_found = std::move(not_found);
1682 if (out_unauthorized)
1683 *out_unauthorized = std::move(unauthorized);
1684
1685 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Cloud plugin fetch: " << cloud_list.size() << " plugins returned";
1686}
@ WarningNotificationLevel
Definition NotificationManager.hpp:224
void update_cloud_metadata(const std::vector< PluginDescriptor > &cloud_list)
Definition PluginManager.cpp:1503
@ CustomNotification
Definition NotificationManager.hpp:66
@ Plater
Definition Shortcuts.hpp:21

References _u8L, Slic3r::PluginDescriptor::clear_error(), Slic3r::PluginDescriptor::cloud, Slic3r::PluginDescriptor::cloud_uuid(), Slic3r::GUI::CustomNotification, Slic3r::GUI::Plater::get_notification_manager(), Slic3r::PluginDescriptor::is_cloud_plugin(), m_cloud_service, m_mutex, m_plugins, Slic3r::PluginDescriptor::normalized_error(), Slic3r::GUI::NotificationManager::push_notification(), Slic3r::PluginDescriptor::set_unauthorized(), update_cloud_metadata(), and Slic3r::GUI::NotificationManager::WarningNotificationLevel.

Referenced by finalize_cloud_plugin_removal(), Slic3r::GUI::GUI_App::on_init_inner(), Slic3r::GUI::refresh_plugin_metadata_blocking(), and subscribe_and_install_cloud_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ finalize_cloud_plugin_removal()

bool Slic3r::PluginManager::finalize_cloud_plugin_removal ( const PluginDescriptor & plugin,
bool keep_local,
std::string & error )
private
1993{
1994 if (keep_local && plugin.has_local_package()) {
1995 if (!keep_installed_plugin_as_local(plugin, error))
1996 return false;
1997 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Removed cloud tracking, kept local copy: " << plugin.plugin_key;
1998 return true;
1999 }
2000
2001 if (plugin.has_local_package()) {
2002 if (!delete_installed_plugin_package(plugin, error))
2003 return false;
2004
2006 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Deleted local package after cloud removal: " << plugin.plugin_key;
2007 return true;
2008 }
2009
2010 {
2011 std::lock_guard<std::mutex> lock(m_mutex);
2012 m_plugins.erase(std::remove_if(m_plugins.begin(), m_plugins.end(),
2013 [&plugin](const Plugin& entry) { return entry.descriptor.plugin_key == plugin.plugin_key; }),
2014 m_plugins.end());
2015 }
2016 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Removed cloud-only plugin: " << plugin.plugin_key;
2017 return true;
2018}
bool keep_installed_plugin_as_local(const PluginDescriptor &plugin_descriptor, std::string &error)
Definition PluginManager.cpp:1900
void fetch_plugins_from_cloud(std::vector< std::string > *out_not_found=nullptr, std::vector< std::string > *out_unauthorized=nullptr)
Definition PluginManager.cpp:1608

References delete_installed_plugin_package(), fetch_plugins_from_cloud(), Slic3r::PluginDescriptor::has_local_package(), keep_installed_plugin_as_local(), m_mutex, m_plugins, and Slic3r::PluginDescriptor::plugin_key.

Referenced by delete_and_unsubscribe_cloud_plugin(), and unsubscribe_cloud_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ find_plugin_locked() [1/2]

Plugin * Slic3r::PluginManager::find_plugin_locked ( const std::string & plugin_key)
private
207{
208 const auto it = std::find_if(m_plugins.begin(), m_plugins.end(),
209 [&plugin_key](const Plugin& plugin) { return plugin.descriptor.plugin_key == plugin_key; });
210 return it == m_plugins.end() ? nullptr : &*it;
211}

References m_plugins.

Referenced by check_registry_locked(), clear_plugin_error(), download_and_install_cloud_plugin(), get_plugin_capability(), is_plugin_loaded(), keep_installed_plugin_as_local(), load_plugin(), load_plugin_impl(), mark_plugin_install_state_disabled(), merge_discovered_plugins(), set_capability_enabled(), set_plugin_error(), try_get_plugin_descriptor(), try_get_valid_plugin_descriptor(), unload_plugin(), and write_loaded_plugin_install_state().

Here is the caller graph for this function:

◆ find_plugin_locked() [2/2]

const Plugin * Slic3r::PluginManager::find_plugin_locked ( const std::string & plugin_key) const
private
214{
215 const auto it = std::find_if(m_plugins.begin(), m_plugins.end(),
216 [&plugin_key](const Plugin& plugin) { return plugin.descriptor.plugin_key == plugin_key; });
217 return it == m_plugins.end() ? nullptr : &*it;
218}

References m_plugins.

◆ get_config() [1/2]

PluginConfig & Slic3r::PluginManager::get_config ( )
inline
139{ return m_config; }
PluginConfig m_config
Definition PluginManager.hpp:310

References m_config.

Referenced by Slic3r::capability_save_config(), and Slic3r::PresetPluginConfigService::get_effective_config().

Here is the caller graph for this function:

◆ get_config() [2/2]

const PluginConfig & Slic3r::PluginManager::get_config ( ) const
inline
140{ return m_config; }

References m_config.

◆ get_discovery_error()

std::string Slic3r::PluginManager::get_discovery_error ( ) const
460{
461 std::lock_guard<std::mutex> lock(m_mutex);
462 return m_discovery_error;
463}

References m_discovery_error, and m_mutex.

◆ get_enabled_plugin_keys()

std::vector< std::string > Slic3r::PluginManager::get_enabled_plugin_keys ( ) const
527{
528 std::lock_guard<std::mutex> lock(m_mutex);
529
530 std::vector<std::string> keys;
531 for (const Plugin& plugin : m_plugins) {
532 if (plugin.descriptor.enabled && plugin.descriptor.has_local_package())
533 keys.push_back(plugin.descriptor.plugin_key);
534 }
535 return keys;
536}

References m_mutex, and m_plugins.

Referenced by Slic3r::GUI::GUI_App::on_init_inner().

Here is the caller graph for this function:

◆ get_install_state()

bool Slic3r::PluginManager::get_install_state ( const std::string & plugin_key,
PluginInstallState & install_state )
659{
660 PluginDescriptor descriptor;
661 if (!try_get_plugin_descriptor(plugin_key, descriptor)) {
662 return false;
663 }
664
665 if (!read_install_state(boost::filesystem::path(descriptor.plugin_root), install_state)) {
666 return false;
667 }
668
669 return true;
670}
bool read_install_state(const boost::filesystem::path &plugin_dir, PluginDescriptor &entry)
Definition PluginFsUtils.cpp:738

References Slic3r::PluginDescriptor::plugin_root, Slic3r::read_install_state(), and try_get_plugin_descriptor().

Referenced by Slic3r::PluginAuditDetail::audit_plugin_event().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_missing_plugin_descriptors()

std::vector< PluginDescriptor > Slic3r::PluginManager::get_missing_plugin_descriptors ( ) const
382{
383 std::lock_guard<std::mutex> lock(m_mutex);
384
385 std::vector<PluginDescriptor> result;
386 result.reserve(m_missing_plugin_keys.size());
387 for (const Plugin& plugin : m_plugins)
388 if (m_missing_plugin_keys.count(plugin.descriptor.plugin_key) != 0)
389 result.push_back(plugin.descriptor);
390 return result;
391}
std::unordered_set< std::string > m_missing_plugin_keys
Definition PluginManager.hpp:319

References m_missing_plugin_keys, m_mutex, and m_plugins.

Referenced by Slic3r::GUI::PluginsDialog::prompt_for_missing_plugins().

Here is the caller graph for this function:

◆ get_plugin_capabilities()

std::vector< std::shared_ptr< PluginCapabilityInterface > > Slic3r::PluginManager::get_plugin_capabilities ( const std::string & plugin_key = "",
PluginCapabilityType type = PluginCapabilityType::Unknown,
bool only_enabled = true ) const
595{
596 std::lock_guard<std::mutex> lock(m_mutex);
597
598 std::vector<std::shared_ptr<PluginCapabilityInterface>> result;
599 for (const Plugin& plugin : m_plugins) {
600 if (!plugin_key.empty() && plugin.descriptor.plugin_key != plugin_key)
601 continue;
602
603 for (const auto& capability : plugin.capabilities) {
604 if (!capability)
605 continue;
606 if (type != PluginCapabilityType::Unknown && capability->type() != type)
607 continue;
608 if (only_enabled && !capability->is_enabled())
609 continue;
610 result.push_back(capability);
611 }
612 }
613 return result;
614}
@ Unknown
Definition PythonPluginInterface.hpp:17

References m_mutex, m_plugins, and Slic3r::Unknown.

Referenced by Slic3r::GUI::anonymous_namespace{PluginsDialog.cpp}::build_plugin_dialog_item(), dispatch_lifecycle_event(), Slic3r::GUI::ActionRegistry::init(), Slic3r::GUI::OptionsGroup::pick_plugin(), Slic3r::GUI::anonymous_namespace{PluginsDialog.cpp}::primary_capability_type_of(), and Slic3r::GUI::ActionRegistry::refresh_source().

Here is the caller graph for this function:

◆ get_plugin_capability() [1/2]

std::shared_ptr< PluginCapabilityInterface > Slic3r::PluginManager::get_plugin_capability ( const PluginCapabilityId & id,
bool only_enabled = true ) const
617{
618 std::lock_guard<std::mutex> lock(m_mutex);
619
620 const Plugin* plugin = find_plugin_locked(id.plugin_key);
621 if (plugin == nullptr)
622 return nullptr;
623
624 for (const auto& capability : plugin->capabilities) {
625 if (!capability || capability->name() != id.name)
626 continue;
627 if (id.type != PluginCapabilityType::Unknown && capability->type() != id.type)
628 continue;
629 if (only_enabled && !capability->is_enabled())
630 continue;
631 return capability;
632 }
633
634 return nullptr;
635}

References Slic3r::Plugin::capabilities, find_plugin_locked(), m_mutex, and Slic3r::Unknown.

Referenced by Slic3r::PluginResourceTextures::build_entry(), Slic3r::PluginConfig::capabilities_payload(), Slic3r::execute_capabilities_from_refs(), Slic3r::PluginConfig::get_config_response(), Slic3r::PluginPages::get_pages_cap(), Slic3r::plugin_hooks::anonymous_namespace{PluginHooks.cpp}::install_capability_resolver(), launch_script_capability(), Slic3r::loaded_capability_state(), Slic3r::GUI::anonymous_namespace{ActionRegistry.cpp}::make_action(), Slic3r::GUI::anonymous_namespace{ActionRegistry.cpp}::make_page_action(), Slic3r::NetworkAgentFactory::register_python_printer_agent(), Slic3r::PluginConfig::restore_config_response(), Slic3r::PluginConfig::save_config_response(), and Slic3r::GUI::PluginsConfigDialog::send_capability_config().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_plugin_capability() [2/2]

std::shared_ptr< PluginCapabilityInterface > Slic3r::PluginManager::get_plugin_capability ( const std::string & capability_name,
PluginCapabilityType type = PluginCapabilityType::Unknown,
bool only_enabled = true ) const
640{
641 std::lock_guard<std::mutex> lock(m_mutex);
642
643 for (const Plugin& plugin : m_plugins) {
644 for (const auto& capability : plugin.capabilities) {
645 if (!capability || capability->name() != capability_name)
646 continue;
647 if (type != PluginCapabilityType::Unknown && capability->type() != type)
648 continue;
649 if (only_enabled && !capability->is_enabled())
650 continue;
651 return capability;
652 }
653 }
654
655 return nullptr;
656}

References m_mutex, m_plugins, and Slic3r::Unknown.

◆ get_plugin_descriptors()

std::vector< PluginDescriptor > Slic3r::PluginManager::get_plugin_descriptors ( bool include_invalid = false) const
489{
490 std::lock_guard<std::mutex> lock(m_mutex);
491
492 std::vector<PluginDescriptor> result;
493 result.reserve(m_plugins.size());
494 for (const Plugin& plugin : m_plugins) {
495 if (!include_invalid && plugin.descriptor.is_invalid_package())
496 continue;
497 result.push_back(plugin.descriptor);
498 }
499 return result;
500}

References m_mutex, and m_plugins.

Referenced by Slic3r::GUI::PluginsDialog::build_plugins_payload(), and Slic3r::GUI::ActionRegistry::init().

Here is the caller graph for this function:

◆ get_plugin_load_error()

std::string Slic3r::PluginManager::get_plugin_load_error ( const std::string & plugin_key) const
722{
723 std::lock_guard<std::mutex> lock(m_mutex);
724 const auto it = m_load_errors.find(plugin_key);
725 return it == m_load_errors.end() ? std::string{} : it->second;
726}

References m_load_errors, and m_mutex.

◆ get_storage_dir()

std::string Slic3r::PluginManager::get_storage_dir ( const std::string & plugin_key) const
559{
560 namespace fs = boost::filesystem;
561
562 PluginDescriptor descriptor;
563 if (!try_get_plugin_descriptor(plugin_key, descriptor))
564 throw std::runtime_error("The current plugin is not registered");
565
566 const fs::path base_storage_dir = fs::path(get_orca_plugins_dir()) / PLUGIN_DATA_DIR;
567
568 if (!descriptor.is_cloud_plugin()) {
569 const fs::path local_storage_dir = base_storage_dir / plugin_key;
570 fs::create_directories(local_storage_dir);
571 return local_storage_dir.string();
572 }
573
574 auto agent = m_cloud_service.get_cloud_agent();
575 if (!agent)
576 throw std::runtime_error("Cloud plugin storage is unavailable before networking is initialized");
577
578 const std::string user_id = agent->get_user_id();
579 if (user_id.empty())
580 throw std::runtime_error("Cloud plugin storage is unavailable without a logged-in user");
581
582 if (!is_valid_plugin_id(plugin_key))
583 throw std::runtime_error("The current cloud plugin key is not a valid folder name");
584
585 const fs::path cloud_storage_dir = base_storage_dir / PLUGIN_SUBSCRIBED_DIR / user_id / plugin_key;
586 fs::create_directories(cloud_storage_dir);
587 return cloud_storage_dir.string();
588}
#define PLUGIN_DATA_DIR
Definition PluginFsUtils.hpp:18
#define PLUGIN_SUBSCRIBED_DIR
Definition PluginFsUtils.hpp:17
std::string get_orca_plugins_dir()
Definition PluginFsUtils.cpp:41
bool is_valid_plugin_id(const std::string &id)
Definition PluginFsUtils.cpp:346

References Slic3r::get_orca_plugins_dir(), Slic3r::PluginDescriptor::is_cloud_plugin(), Slic3r::is_valid_plugin_id(), m_cloud_service, PLUGIN_DATA_DIR, PLUGIN_SUBSCRIBED_DIR, and try_get_plugin_descriptor().

Referenced by Slic3r::host_bindings::register_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ initialize()

bool Slic3r::PluginManager::initialize ( )
88{
89 {
90 std::lock_guard<std::mutex> lock(m_mutex);
91 if (m_initialized)
92 return true;
93 m_shutting_down.store(false, std::memory_order_release);
94 }
95
96 // Initialize the Python interpreter eagerly on the main thread. CPython must be initialized
97 // from the main thread; calling Py_InitializeFromConfig from a background thread (e.g. the
98 // load_plugin worker) causes heap corruption in CPython internals.
99 PythonInterpreter& interpreter = PythonInterpreter::instance();
100 if (!interpreter.is_initialized() && !interpreter.initialize()) {
101 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": Failed to initialize Python interpreter: " << interpreter.last_error();
102 return false;
103 }
104
105 {
106 std::lock_guard<std::mutex> lock(m_mutex);
107 if (m_initialized)
108 return true;
109 m_initialized = true;
110 }
111
112 // Re-arm the async Script runtime in case shutdown() ran earlier in this process.
114
115 // Bring every capability's stored config into memory. Deliberately unconditional and
116 // independent of which plugins are installed: an entry outlives uninstall/unsubscribe, so a
117 // plugin that comes back later finds its settings intact. A missing or malformed file just
118 // leaves the store empty (see PluginConfig::load), never blocking startup.
119 m_config.load();
120
121 // Install the libslic3r hooks (capability resolver, slicing-pipeline dispatcher).
122 // Uninstalled in shutdown() before the interpreter finalizes.
124
125 // Persist auto-load / capability state to each plugin's .install_state.json sidecar.
126 // On load: write enabled=true plus the current capability flags. On unload: flip enabled=false.
127 // The on-unload callback is skipped during shutdown, so app exit does not wipe the auto-load list.
128 subscribe_on_load_callback([this](const std::string& key) { write_loaded_plugin_install_state(key); });
129 subscribe_on_unload_callback([this](const std::string& key) {
130 if (m_shutting_down.load(std::memory_order_acquire))
131 return;
132 PluginDescriptor descriptor;
133 if (!try_get_plugin_descriptor(key, descriptor) || descriptor.plugin_root.empty())
134 return;
135 const boost::filesystem::path root(descriptor.plugin_root);
136 PluginInstallState state;
137 if (read_install_state(root, state)) {
138 state.enabled = false;
139 write_install_state(root, state);
140 }
141 });
142
143 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Plugin manager initialized";
144
145 return true;
146}
bool m_initialized
Definition PluginManager.hpp:308
void subscribe_on_load_callback(PluginLifecycleCompleteFn fn)
Definition PluginManager.cpp:1257
void subscribe_on_unload_callback(PluginLifecycleCompleteFn fn)
Definition PluginManager.cpp:1263
void write_loaded_plugin_install_state(const std::string &plugin_key)
Definition PluginManager.cpp:1189
std::atomic< bool > m_shutting_down
Definition PluginManager.hpp:343
static PythonInterpreter & instance()
Definition PythonInterpreter.cpp:434
void reset()
Definition PluginManager.cpp:2387
void install()
Definition PluginHooks.cpp:145
bool write_install_state(const boost::filesystem::path &plugin_dir, const PluginInstallState &state)
Definition PluginFsUtils.cpp:859

References Slic3r::PluginInstallState::enabled, Slic3r::PythonInterpreter::initialize(), Slic3r::plugin_hooks::install(), Slic3r::PythonInterpreter::instance(), Slic3r::ScriptAsyncRuntime::instance(), Slic3r::PythonInterpreter::is_initialized(), Slic3r::PythonInterpreter::last_error(), m_config, m_initialized, m_mutex, m_shutting_down, Slic3r::PluginDescriptor::plugin_root, Slic3r::read_install_state(), Slic3r::ScriptAsyncRuntime::reset(), subscribe_on_load_callback(), subscribe_on_unload_callback(), try_get_plugin_descriptor(), Slic3r::write_install_state(), and write_loaded_plugin_install_state().

Referenced by discover_plugins(), and Slic3r::GUI::GUI_App::on_init_inner().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ inspect_local_plugin_package()

bool Slic3r::PluginManager::inspect_local_plugin_package ( const boost::filesystem::path & filepath,
PluginDescriptor & plugin_descriptor,
bool & existing_installation,
std::string & error ) const
1432{ return plugin_loader::inspect_local_plugin_package(filepath, plugin_descriptor, existing_installation, error); }
bool inspect_local_plugin_package(const boost::filesystem::path &filepath, PluginDescriptor &plugin_descriptor, bool &existing_installation, std::string &error)
Definition PluginLoader.cpp:588

References Slic3r::plugin_loader::inspect_local_plugin_package().

Here is the call graph for this function:

◆ install_plugin() [1/2]

bool Slic3r::PluginManager::install_plugin ( const boost::filesystem::path & filepath,
PluginDescriptor & plugin_descriptor,
std::string & error )
1441{
1442 error.clear();
1443
1444 std::string wait_error;
1445 if (!wait_for_discovery(std::chrono::milliseconds::max(), wait_error)) {
1446 error = wait_error.empty() ? "Plugin discovery is still running" : wait_error;
1447 if (!plugin_descriptor.plugin_key.empty())
1448 set_plugin_error(plugin_descriptor.plugin_key, error);
1449 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": Plugin installation failed while waiting for discovery: " << error;
1450 return false;
1451 }
1452
1453 std::string cloud_user_id;
1454 {
1455 std::lock_guard<std::mutex> lock(m_mutex);
1456 cloud_user_id = m_cloud_user_id;
1457 }
1458
1459 // A local overwrite must not leave the old module holding the package file or its Python
1460 // namespace while the installer replaces the package on disk.
1461 if (!plugin_descriptor.is_cloud_plugin()) {
1462 const std::string local_plugin_key = make_local_plugin_key(filepath.stem().string());
1463 if (is_plugin_loaded(local_plugin_key))
1464 unload_plugin(local_plugin_key);
1465 }
1466
1467 if (!plugin_loader::install_plugin(filepath, cloud_user_id, plugin_descriptor, error)) {
1468 if (!plugin_descriptor.plugin_key.empty())
1469 set_plugin_error(plugin_descriptor.plugin_key, error);
1470 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << error;
1471 return false;
1472 }
1473
1474 // Every successful install may have replaced executable plugin code. Revoke any permissions
1475 // associated with the previous package so the newly installed version must request them again.
1476 if (!plugin_descriptor.plugin_key.empty())
1477 revoke_plugin_permissions(plugin_descriptor.plugin_key);
1478
1479 if (!plugin_descriptor.plugin_key.empty())
1480 clear_plugin_error(plugin_descriptor.plugin_key);
1481
1482 return true;
1483}
bool is_plugin_loaded(const std::string &plugin_key) const
Definition PluginManager.cpp:674
void revoke_plugin_permissions(const std::string &plugin_key)
Definition PluginManager.cpp:1235
bool install_plugin(const boost::filesystem::path &filepath, const std::string &cloud_user_id, std::string &error)
Definition PluginLoader.cpp:628
std::string make_local_plugin_key(const std::string &stem)
Definition PluginDescriptor.hpp:214

References clear_plugin_error(), Slic3r::plugin_loader::install_plugin(), Slic3r::PluginDescriptor::is_cloud_plugin(), is_plugin_loaded(), m_cloud_user_id, m_mutex, Slic3r::make_local_plugin_key(), Slic3r::PluginDescriptor::plugin_key, revoke_plugin_permissions(), set_plugin_error(), unload_plugin(), and wait_for_discovery().

Here is the call graph for this function:

◆ install_plugin() [2/2]

bool Slic3r::PluginManager::install_plugin ( const boost::filesystem::path & filepath,
std::string & error )
1435{
1436 PluginDescriptor descriptor{};
1437 return install_plugin(filepath, descriptor, error);
1438}

References install_plugin().

Referenced by download_and_install_cloud_plugin(), Slic3r::GUI::install_local_plugin_package(), and install_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ instance()

PluginManager & Slic3r::PluginManager::instance ( )
static
75{
76 // Touch the interpreter first so its static outlives this one: ~Plugin needs it alive to
77 // release module, sys.modules, and sys.path ownership safely. Touch the async Script runtime
78 // next, so ~PluginManager can still drain it during its own shutdown().
81 static PluginManager inst;
82 return inst;
83}

References PluginManager(), Slic3r::PythonInterpreter::instance(), and Slic3r::ScriptAsyncRuntime::instance().

Referenced by Slic3r::GUI::GUI_App::~GUI_App(), Slic3r::PluginAuditDetail::audit_plugin_event(), Slic3r::PluginResourceTextures::build_entry(), Slic3r::GUI::anonymous_namespace{PluginsDialog.cpp}::build_plugin_dialog_item(), Slic3r::GUI::PluginsDialog::build_plugins_payload(), Slic3r::PluginConfig::capabilities_payload(), Slic3r::capability_save_config(), Slic3r::anonymous_namespace{PluginResolver.cpp}::collect_capabilities_in_use(), Slic3r::GUI::anonymous_namespace{PluginsDialog.cpp}::current_cloud_metadata_snapshot(), Slic3r::GUI::PluginsDialog::delete_local_plugin(), Slic3r::PyPrinterAgentPluginCapabilityTrampoline::disconnect_printer(), Slic3r::GUI::GUI_App::enable_user_preset_folder(), Slic3r::execute_capabilities_from_refs(), Slic3r::PluginConfig::get_config_response(), Slic3r::GUI::PluginsDialog::get_descriptor(), Slic3r::PresetPluginConfigService::get_effective_config(), Slic3r::PluginPages::get_pages_cap(), Slic3r::GUI::ActionRegistry::init(), Slic3r::GUI::GUI_App::init_plugin_gui_wiring(), Slic3r::PluginResourceTextures::initial_sweep(), Slic3r::PluginPages::initialize(), Slic3r::plugin_hooks::anonymous_namespace{PluginHooks.cpp}::install_capability_resolver(), Slic3r::GUI::PluginsDialog::install_cloud_plugin(), Slic3r::plugin_hooks::anonymous_namespace{PluginHooks.cpp}::install_lifecycle_event_hook(), Slic3r::GUI::install_local_plugin_package(), Slic3r::loaded_capability_state(), Slic3r::GUI::anonymous_namespace{ActionRegistry.cpp}::make_action(), Slic3r::GUI::anonymous_namespace{ActionRegistry.cpp}::make_page_action(), Slic3r::GUI::GUI_App::on_init_inner(), Slic3r::PluginPages::on_plugin_register(), Slic3r::PluginResourceTextures::on_plugin_register(), Slic3r::GUI::GUI_App::on_user_login_handle(), Slic3r::PluginAuditDetail::persist_grant(), Slic3r::PluginAuditDetail::persist_permission(), Slic3r::GUI::OptionsGroup::pick_plugin(), Slic3r::GUI::PluginsDialog::prompt_for_missing_plugins(), Slic3r::GUI::ActionRegistry::refresh_capability(), Slic3r::refresh_missing_plugins(), Slic3r::GUI::ActionRegistry::refresh_page_capability(), Slic3r::GUI::refresh_plugin_metadata_blocking(), Slic3r::GUI::ActionRegistry::refresh_source(), Slic3r::host_bindings::register_plugin(), Slic3r::NetworkAgentFactory::register_python_plugin(), Slic3r::NetworkAgentFactory::register_python_printer_agent(), Slic3r::GUI::PluginsDialog::reinstall_cloud_plugin(), Slic3r::GUI::PluginsDialog::reload_local_plugin(), Slic3r::PluginAuditManager::request_permissions(), Slic3r::GUI::GUI_App::request_user_logout(), Slic3r::resolve_inactive_plugins(), Slic3r::resolve_missing_plugins(), Slic3r::GUI::PluginsDialog::resolve_pending_activation(), Slic3r::PluginConfig::restore_config_response(), Slic3r::GUI::anonymous_namespace{ActionRegistry.cpp}::PluginScriptAction::run(), Slic3r::GUI::PluginsDialog::run_script_plugin_capability(), Slic3r::anonymous_namespace{PluginConfig.cpp}::running_plugin_version(), Slic3r::PluginConfig::save_config_response(), Slic3r::GUI::PluginsConfigDialog::send_capability_config(), Slic3r::GUI::GUI_App::shutdown(), Slic3r::GUI::PluginsDialog::toggle_plugin(), Slic3r::GUI::PluginsDialog::toggle_plugin_capability(), and Slic3r::GUI::PluginsDialog::update_plugin().

Here is the call graph for this function:

◆ is_discovery_complete()

bool Slic3r::PluginManager::is_discovery_complete ( ) const
448{
449 std::lock_guard<std::mutex> lock(m_mutex);
451}

References m_discovery_complete, and m_mutex.

◆ is_discovery_in_progress()

bool Slic3r::PluginManager::is_discovery_in_progress ( ) const
454{
455 std::lock_guard<std::mutex> lock(m_mutex);
457}

References m_discovery_in_progress, and m_mutex.

◆ is_plugin_load_cancelled_locked()

bool Slic3r::PluginManager::is_plugin_load_cancelled_locked ( const std::string & plugin_key) const
private
750{ return m_cancelled.count(plugin_key) > 0; }

References m_cancelled.

Referenced by load_plugin_impl().

Here is the caller graph for this function:

◆ is_plugin_load_in_progress()

bool Slic3r::PluginManager::is_plugin_load_in_progress ( const std::string & plugin_key) const
682{
683 std::lock_guard<std::mutex> lock(m_mutex);
684 return m_load_in_progress.count(plugin_key) > 0;
685}

References m_load_in_progress, and m_mutex.

Referenced by Slic3r::GUI::anonymous_namespace{PluginsDialog.cpp}::build_plugin_dialog_item(), Slic3r::GUI::PluginsDialog::resolve_pending_activation(), and Slic3r::GUI::PluginsDialog::toggle_plugin().

Here is the caller graph for this function:

◆ is_plugin_loaded()

bool Slic3r::PluginManager::is_plugin_loaded ( const std::string & plugin_key) const
675{
676 std::lock_guard<std::mutex> lock(m_mutex);
677 const Plugin* plugin = find_plugin_locked(plugin_key);
678 return plugin != nullptr && plugin->is_loaded();
679}

References find_plugin_locked(), Slic3r::Plugin::is_loaded(), and m_mutex.

Referenced by Slic3r::GUI::anonymous_namespace{PluginsDialog.cpp}::build_plugin_dialog_item(), install_plugin(), Slic3r::loaded_capability_state(), Slic3r::GUI::anonymous_namespace{ActionRegistry.cpp}::make_action(), Slic3r::GUI::anonymous_namespace{ActionRegistry.cpp}::make_page_action(), Slic3r::GUI::GUI_App::on_init_inner(), Slic3r::refresh_missing_plugins(), Slic3r::GUI::PluginsDialog::reinstall_cloud_plugin(), Slic3r::GUI::PluginsDialog::reload_local_plugin(), Slic3r::resolve_missing_plugins(), Slic3r::GUI::PluginsDialog::resolve_pending_activation(), and Slic3r::GUI::PluginsDialog::toggle_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_shutting_down()

bool Slic3r::PluginManager::is_shutting_down ( ) const
inline
133{ return m_shutting_down.load(std::memory_order_acquire); }

References m_shutting_down.

◆ keep_installed_plugin_as_local()

bool Slic3r::PluginManager::keep_installed_plugin_as_local ( const PluginDescriptor & plugin_descriptor,
std::string & error )
private
1901{
1902 namespace fs = boost::filesystem;
1903
1904 boost::filesystem::path resolved_root;
1905
1906 bool resolved_allowed_plugin_root = false;
1907 {
1908 std::lock_guard<std::mutex> lock(m_mutex);
1909 resolved_allowed_plugin_root = resolve_allowed_plugin_root(plugin_descriptor, get_plugin_directories(m_cloud_user_id),
1910 _u8L("Refusing to update a plugin outside the known plugin directories."),
1911 resolved_root, error);
1912 }
1913
1914 if (!resolved_allowed_plugin_root)
1915 return false;
1916
1917 const std::string old_key = plugin_descriptor.plugin_key;
1918
1919 // Generate a new local key from the entry file stem (cloud -> local conversion).
1920 const std::string entry_stem = fs::path(plugin_descriptor.entry_path).stem().string();
1921 const std::string new_key = make_local_plugin_key(!entry_stem.empty() ? entry_stem : resolved_root.stem().string());
1922
1923 PluginDescriptor local_descriptor = plugin_descriptor;
1924 local_descriptor.plugin_key = new_key;
1925 local_descriptor.cloud = std::nullopt;
1926 local_descriptor.clear_error();
1927
1928 // Re-key the entry in place, carrying the live module/capabilities (if any) with it. The
1929 // capabilities' audit key must follow, since it is what identifies their owning package.
1930 std::vector<PluginCapabilityId> rekeyed;
1931 {
1932 std::lock_guard<std::mutex> lock(m_mutex);
1933
1934 Plugin* entry = find_plugin_locked(old_key);
1935 if (entry == nullptr) {
1936 error = GUI::format(_u8L("Plugin manifest not found: %1%"), old_key);
1937 return false;
1938 }
1939
1940 if (find_plugin_locked(new_key) != nullptr && new_key != old_key) {
1941 error = GUI::format(_u8L("Cannot keep plugin local: local plugin key already exists: %1%"), new_key);
1942 BOOST_LOG_TRIVIAL(warning) << error;
1943 return false;
1944 }
1945
1946 // Preserve the sidecar's package and capability choices while changing only its origin.
1947 // Re-keying an installed plugin must not silently re-enable capabilities.
1948 PluginInstallState install_state;
1949 if (!read_install_state(resolved_root, install_state)) {
1950 install_state.installed_version = !local_descriptor.installed_version.empty()
1951 ? local_descriptor.installed_version
1952 : local_descriptor.version;
1953 install_state.enabled = local_descriptor.enabled;
1954 }
1955 install_state.installed_from = "local";
1956 install_state.plugin_name = local_descriptor.name;
1957 install_state.cloud_uuid.clear();
1958 if (install_state.installed_version.empty())
1959 install_state.installed_version = local_descriptor.version;
1960 if (!write_install_state(resolved_root, install_state)) {
1961 error = GUI::format(_u8L("Failed to update plugin install state: %1%"), (resolved_root / INSTALL_STATE_FILE).string());
1962 return false;
1963 }
1964
1965 entry->descriptor.plugin_key = new_key;
1966 entry->descriptor.cloud.reset();
1967 entry->descriptor.clear_error();
1968
1969 for (const auto& capability : entry->capabilities) {
1970 if (capability)
1971 capability->set_audit_plugin_key(new_key);
1972 }
1973
1974 for (const auto& capability : entry->capabilities) {
1975 if (capability && capability->is_enabled())
1976 rekeyed.push_back(PluginCapabilityId{capability->type(), capability->name(), new_key});
1977 }
1978 }
1979
1980 // Subscribers key their own state by plugin_key. Publish the identity transition after the
1981 // registry update and outside m_mutex so they can safely query the manager.
1982 for (const PluginCapabilityId& id : rekeyed) {
1983 run_on_capability_unload_callbacks(PluginCapabilityId{id.type, id.name, old_key});
1985 }
1986
1987 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Transitioned plugin from " << old_key << " to " << new_key;
1988
1989 return true;
1990}
void run_on_capability_unload_callbacks(const PluginCapabilityId &id)
Definition PluginManager.cpp:1366
void run_on_capability_load_callbacks(const PluginCapabilityId &id)
Definition PluginManager.cpp:1346
const char *const INSTALL_STATE_FILE
Definition PluginFsUtils.cpp:39

References _u8L, Slic3r::Plugin::capabilities, Slic3r::PluginDescriptor::clear_error(), Slic3r::PluginDescriptor::cloud, Slic3r::PluginInstallState::cloud_uuid, Slic3r::Plugin::descriptor, Slic3r::PluginDescriptor::enabled, Slic3r::PluginInstallState::enabled, Slic3r::PluginDescriptor::entry_path, find_plugin_locked(), Slic3r::GUI::format(), Slic3r::get_plugin_directories(), Slic3r::INSTALL_STATE_FILE, Slic3r::PluginInstallState::installed_from, Slic3r::PluginDescriptor::installed_version, Slic3r::PluginInstallState::installed_version, m_cloud_user_id, m_mutex, Slic3r::make_local_plugin_key(), Slic3r::PluginDescriptor::name, Slic3r::PluginDescriptor::plugin_key, Slic3r::PluginInstallState::plugin_name, Slic3r::read_install_state(), Slic3r::resolve_allowed_plugin_root(), run_on_capability_load_callbacks(), run_on_capability_unload_callbacks(), Slic3r::PluginDescriptor::version, and Slic3r::write_install_state().

Referenced by finalize_cloud_plugin_removal().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ launch_script_capability()

PluginManager::ScriptLaunchResult Slic3r::PluginManager::launch_script_capability ( const std::string & plugin_key,
const std::string & capability_name,
ScriptCompletionFn on_done = {} )
2444{
2445 ScriptLaunchResult launch;
2446
2447 // Mirror the historical synchronous behavior for a dangling reference: nothing to run and
2448 // no error (the caller's guarded UI normally prevents this).
2449 if (plugin_key.empty() || capability_name.empty())
2450 return launch;
2451
2452 if (m_shutting_down.load(std::memory_order_acquire)) {
2453 launch.outcome.error = _u8L("Plugin system is shutting down.");
2454 return launch;
2455 }
2456
2457 auto cap = get_plugin_capability({PluginCapabilityType::Script, capability_name, plugin_key});
2458 if (!cap)
2459 return launch;
2460
2461 if (!std::dynamic_pointer_cast<ScriptPluginCapability>(cap))
2462 return launch;
2463
2464 // Every Script body runs off the caller's thread now: the invocation — including detection and
2465 // a synchronous `def execute` — is handed to the runtime, which runs it on its own worker and
2466 // reports the outcome through the completion callback. Nothing executes before returning here,
2467 // so the UI thread is never blocked by a Script invocation.
2468 const bool started = ScriptAsyncRuntime::instance().start(
2469 plugin_key, capability_name, cap,
2470 [this, plugin_key, on_done](const ScriptCompletion& completion) {
2471 // A cancellation requested by cancel_plugin() (unload/shutdown) is expected termination:
2472 // record no plugin error and surface nothing. Genuine user/plugin errors still record.
2473 if (!completion.cancelled && !completion.error.empty())
2474 set_plugin_error(plugin_key, completion.error);
2475 if (on_done)
2476 on_done(completion);
2477 });
2478
2479 if (!started) {
2480 launch.outcome.error = _u8L("Plugin system is shutting down.");
2481 set_plugin_error(plugin_key, launch.outcome.error);
2482 BOOST_LOG_TRIVIAL(error) << "Script plugin execution failed. plugin_key=" << plugin_key
2483 << " error=" << launch.outcome.error;
2484 return launch;
2485 }
2486
2487 launch.launched_async = true;
2488 return launch;
2489}
std::shared_ptr< PluginCapabilityInterface > get_plugin_capability(const PluginCapabilityId &id, bool only_enabled=true) const
Definition PluginManager.cpp:616
Definition PluginManager.hpp:234
bool start(const std::string &plugin_key, const std::string &capability_name, std::shared_ptr< PluginCapabilityInterface > capability, std::function< void(const ScriptCompletion &)> on_done)
Definition PluginManager.cpp:2290
@ Script
Definition PythonPluginInterface.hpp:17

References _u8L, Slic3r::ScriptCompletion::cancelled, Slic3r::ScriptCompletion::error, get_plugin_capability(), Slic3r::ScriptAsyncRuntime::instance(), Slic3r::PluginManager::ScriptLaunchResult::launched_async, m_shutting_down, Slic3r::PluginManager::ScriptLaunchResult::outcome, Slic3r::Script, set_plugin_error(), and Slic3r::ScriptAsyncRuntime::start().

Referenced by run_script_capability(), and Slic3r::GUI::PluginsDialog::run_script_plugin_capability().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ load_plugin()

void Slic3r::PluginManager::load_plugin ( const std::string & plugin_key,
bool skip_deps = false,
std::vector< std::string > capabilities_to_enable = {} )
837{
838 std::string plugin_id = plugin_key;
839
840 bool exists = false;
841 bool invalid = false;
842 bool already_loaded = false;
843 bool load_in_progress = false;
844 bool load_slot_claimed = false;
845 bool shutting_down = false;
846 std::string invalid_error;
847 std::vector<std::string> loaded_capability_names;
848
849 {
850 std::lock_guard<std::mutex> lock(m_mutex);
851
852 const Plugin* plugin = find_plugin_locked(plugin_key);
853 if (plugin != nullptr) {
854 exists = true;
855 plugin_id = plugin->descriptor.plugin_key;
856 invalid = plugin->descriptor.is_invalid_package();
857 already_loaded = plugin->is_loaded();
858 invalid_error = plugin->descriptor.normalized_error();
859 if (already_loaded) {
860 for (const auto& capability : plugin->capabilities)
861 if (capability)
862 loaded_capability_names.push_back(capability->name());
863 }
864 }
865
866 load_in_progress = m_load_in_progress.count(plugin_id) > 0;
867 shutting_down = m_shutting_down.load(std::memory_order_acquire);
868 if (exists && !invalid && !already_loaded && !load_in_progress && !shutting_down) {
869 m_load_in_progress.insert(plugin_id);
870 m_load_errors.erase(plugin_id);
871 load_slot_claimed = true;
872 }
873 }
874
875 if (already_loaded) {
876 // An empty request means "restore the persisted capability state". Only an explicit
877 // capability request may change an already-loaded capability, e.g. when resolving an
878 // inactive-plugin reference. Runs outside m_mutex.
879 for (const std::string& capability_name : capabilities_to_enable) {
880 if (std::find(loaded_capability_names.begin(), loaded_capability_names.end(), capability_name) !=
881 loaded_capability_names.end())
882 set_capability_enabled({PluginCapabilityType::Unknown, capability_name, plugin_id}, true);
883 }
884
885 run_on_load_callbacks(plugin_id);
886 return;
887 }
888
889 if (load_in_progress)
890 return;
891
892 if (shutting_down) {
893 BOOST_LOG_TRIVIAL(info) << "Plugin load rejected — shutting down: " << plugin_id;
894 run_on_load_callbacks(plugin_id);
895 return;
896 }
897
898 if (!exists || invalid) {
899 std::string message;
900 if (invalid) {
901 message = GUI::format(_u8L("Plugin is invalid: %1%"), plugin_id);
902 if (!invalid_error.empty())
903 message += " - " + invalid_error;
904 } else {
905 message = GUI::format(_u8L("Plugin not found: %1%"), plugin_id);
906 }
907
908 {
909 std::lock_guard<std::mutex> lock(m_mutex);
910 m_load_errors[plugin_id] = message;
911 BOOST_LOG_TRIVIAL(error) << message;
912 }
913
914 if (invalid)
915 set_plugin_error(plugin_id, message);
916
917 run_on_load_callbacks(plugin_id);
918 return;
919 }
920
921 if (!load_slot_claimed)
922 return;
923
924 clear_plugin_error(plugin_id);
925
926 std::thread([this, plugin_id, skip_deps, capabilities_to_enable]() {
927 auto fail_unexpected = [this, &plugin_id](std::string message) {
928 BOOST_LOG_TRIVIAL(error) << "[load_plugin] Unexpected worker failure plugin=" << plugin_id << " error=" << message;
929 set_plugin_error(plugin_id, message);
930 {
931 std::lock_guard<std::mutex> lock(m_mutex);
932 m_load_errors[plugin_id] = std::move(message);
933 }
934 };
935
936 try {
937 load_plugin_impl(plugin_id, skip_deps, capabilities_to_enable);
938 } catch (const std::exception& ex) {
939 fail_unexpected(GUI::format(_u8L("Unexpected plugin load failure: %1%"), ex.what()));
940 } catch (...) {
941 fail_unexpected(_u8L("Unexpected plugin load failure"));
942 }
943
944 if (!m_shutting_down.load(std::memory_order_acquire))
945 run_on_load_callbacks(plugin_id);
946
947 // The worker owns its slot and releases it here, once it is done with Python for good —
948 // including a load that was cancelled mid-flight, which is what keeps
949 // wait_for_all_plugin_loads() blocking until the worker is really gone.
950 release_load_slot(plugin_id);
951 }).detach();
952}
void load_plugin_impl(const std::string &plugin_key, bool skip_deps, const std::vector< std::string > &capabilities_to_enable)
Definition PluginManager.cpp:954
void release_load_slot(const std::string &plugin_key)
Definition PluginManager.cpp:758
void set_capability_enabled(const PluginCapabilityId &id, bool enabled)
Definition PluginManager.cpp:1153
void run_on_load_callbacks(const std::string &plugin_key)
Definition PluginManager.cpp:1308

References _u8L, Slic3r::Plugin::capabilities, clear_plugin_error(), Slic3r::Plugin::descriptor, find_plugin_locked(), Slic3r::GUI::format(), Slic3r::PluginDescriptor::is_invalid_package(), Slic3r::Plugin::is_loaded(), load_plugin_impl(), m_load_errors, m_load_in_progress, m_mutex, m_shutting_down, Slic3r::PluginDescriptor::normalized_error(), Slic3r::PluginDescriptor::plugin_key, release_load_slot(), run_on_load_callbacks(), set_capability_enabled(), set_plugin_error(), and Slic3r::Unknown.

Referenced by Slic3r::GUI::GUI_App::on_init_inner(), Slic3r::GUI::PluginsDialog::reinstall_cloud_plugin(), Slic3r::GUI::PluginsDialog::reload_local_plugin(), Slic3r::resolve_inactive_plugins(), Slic3r::resolve_missing_plugins(), and Slic3r::GUI::PluginsDialog::toggle_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ load_plugin_impl()

void Slic3r::PluginManager::load_plugin_impl ( const std::string & plugin_key,
bool skip_deps,
const std::vector< std::string > & capabilities_to_enable )
private
955{
956 auto fail = [this, &plugin_key](std::string message) {
957 BOOST_LOG_TRIVIAL(error) << "[load_plugin_impl] FAIL plugin=" << plugin_key << " error=" << message;
958 set_plugin_error(plugin_key, message);
959 {
960 std::lock_guard<std::mutex> lock(m_mutex);
961 m_load_errors[plugin_key] = std::move(message);
962 }
963 };
964
965 PluginDescriptor descriptor;
966 if (!try_get_plugin_descriptor(plugin_key, descriptor)) {
967 fail(GUI::format(_u8L("Plugin manifest not found: %1%"), plugin_key));
968 return;
969 }
970
971 // Reject a duplicate package or a capability-name collision BEFORE on_load() runs, so a
972 // rejected load costs no on_load/on_unload cycle. Also the point at which a cancellation that
973 // arrived while the module was importing is noticed.
974 auto registry_precheck = [this, &plugin_key](const Plugin& candidate) -> std::string {
975 std::lock_guard<std::mutex> lock(m_mutex);
976 if (is_plugin_load_cancelled_locked(plugin_key))
977 return LOAD_CANCELLED;
978 return check_registry_locked(plugin_key, candidate);
979 };
980
981 Plugin plugin;
982 std::string error;
983 bool registration_failed = false;
984 if (!plugin_loader::load(descriptor, skip_deps, capabilities_to_enable, registry_precheck, plugin, error, &registration_failed)) {
985 if (error == LOAD_CANCELLED)
986 return; // cancelled: nothing materialized survives, and no error is recorded
987 if (registration_failed)
989 fail(std::move(error));
990 return;
991 }
992
993 for (const auto& cap : plugin.capabilities) {
994 auto config = cap->get_default_config();
995 if (config.empty())
996 continue;
997 const PluginCapabilityId id = cap->identity();
998 if (m_config.has_config(id))
999 continue;
1000
1001 m_config.save_config({id, plugin.descriptor.installed_version, config});
1002 }
1003
1004 bool committed = false;
1005 bool cancelled = false;
1006 std::string registry_error;
1007 {
1008 std::lock_guard<std::mutex> lock(m_mutex);
1009
1010 cancelled = is_plugin_load_cancelled_locked(plugin_key);
1011 if (!cancelled) {
1012 registry_error = check_registry_locked(plugin_key, plugin);
1013 if (registry_error.empty()) {
1014 Plugin* entry = find_plugin_locked(plugin_key);
1015 if (entry == nullptr) {
1016 registry_error = GUI::format(_u8L("Plugin manifest not found: %1%"), plugin_key);
1017 } else {
1018 // Move everything via Plugin's own move assignment, but the package's
1019 // descriptor (identity/metadata) belongs to the registry entry, not to the
1020 // freshly-loaded `plugin` -- preserve it across the move.
1021 PluginDescriptor entry_descriptor = std::move(entry->descriptor);
1022 *entry = std::move(plugin);
1023 entry->descriptor = std::move(entry_descriptor);
1024 committed = true;
1025 }
1026 }
1027 }
1028 }
1029
1030 if (!committed) {
1031 // Outside the lock: this runs on_unload() and DECREFs the module.
1032 plugin_loader::unload(plugin);
1033 if (!cancelled)
1034 fail(std::move(registry_error));
1035 return;
1036 }
1037
1038 clear_plugin_error(plugin_key);
1039
1040 {
1041 std::lock_guard<std::mutex> lock(m_mutex);
1042 m_load_errors.erase(plugin_key);
1043 }
1044
1045 BOOST_LOG_TRIVIAL(info) << "[load_plugin_impl] SUCCESS plugin=" << plugin_key;
1046}
void mark_plugin_install_state_disabled(const std::string &plugin_key)
Definition PluginManager.cpp:1214
bool is_plugin_load_cancelled_locked(const std::string &plugin_key) const
Definition PluginManager.cpp:750
std::string check_registry_locked(const std::string &plugin_key, const Plugin &candidate) const
Definition PluginManager.cpp:220
const char *const LOAD_CANCELLED
Definition PluginManager.cpp:58
bool load(const PluginDescriptor &descriptor, bool skip_deps, const std::vector< std::string > &capabilities_to_enable, const std::function< std::string(const Plugin &)> &registry_precheck, Plugin &out, std::string &error, bool *registration_failed)
Definition PluginLoader.cpp:227
void unload(Plugin &plugin)
Definition PluginLoader.cpp:219

References _u8L, Slic3r::Plugin::capabilities, check_registry_locked(), clear_plugin_error(), Slic3r::Plugin::descriptor, find_plugin_locked(), Slic3r::GUI::format(), Slic3r::PluginDescriptor::installed_version, is_plugin_load_cancelled_locked(), Slic3r::plugin_loader::load(), m_config, m_load_errors, m_mutex, mark_plugin_install_state_disabled(), set_plugin_error(), try_get_plugin_descriptor(), and Slic3r::plugin_loader::unload().

Referenced by load_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mark_plugin_install_state_disabled()

void Slic3r::PluginManager::mark_plugin_install_state_disabled ( const std::string & plugin_key)
private
1215{
1216 std::lock_guard<std::mutex> state_lock(m_install_state_mutex);
1217
1218 PluginDescriptor descriptor;
1219 if (!try_get_plugin_descriptor(plugin_key, descriptor) || descriptor.plugin_root.empty())
1220 return;
1221
1222 const boost::filesystem::path root(descriptor.plugin_root);
1223 PluginInstallState state;
1224 if (!read_install_state(root, state))
1225 seed_install_metadata(state, descriptor);
1226 state.enabled = false;
1227 if (!write_install_state(root, state))
1228 return;
1229
1230 std::lock_guard<std::mutex> lock(m_mutex);
1231 if (Plugin* plugin = find_plugin_locked(plugin_key))
1232 plugin->descriptor.enabled = false;
1233}
std::mutex m_install_state_mutex
Definition PluginManager.hpp:332
void seed_install_metadata(PluginInstallState &state, const PluginDescriptor &entry)
Definition PluginFsUtils.cpp:850

References Slic3r::PluginInstallState::enabled, find_plugin_locked(), m_install_state_mutex, m_mutex, Slic3r::PluginDescriptor::plugin_root, Slic3r::read_install_state(), Slic3r::seed_install_metadata(), try_get_plugin_descriptor(), and Slic3r::write_install_state().

Referenced by load_plugin_impl().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ merge_discovered_plugins()

void Slic3r::PluginManager::merge_discovered_plugins ( std::vector< PluginDescriptor > discovered,
bool clear )
private
322{
323 std::vector<std::string> seen;
324 seen.reserve(discovered.size());
325
326 {
327 std::lock_guard<std::mutex> lock(m_mutex);
328
329 for (PluginDescriptor& descriptor : discovered) {
330 if (descriptor.plugin_key.empty())
331 continue;
332
333 if (std::find(seen.begin(), seen.end(), descriptor.plugin_key) != seen.end()) {
334 const std::string duplicate_error = GUI::format(_u8L("Duplicate plugin key discovered: %1%"), descriptor.plugin_key);
335 if (Plugin* existing = find_plugin_locked(descriptor.plugin_key)) {
336 existing->descriptor.set_error(duplicate_error);
337 BOOST_LOG_TRIVIAL(error) << duplicate_error << " (" << existing->descriptor.entry_path << ", "
338 << descriptor.entry_path << ")";
339 } else {
340 BOOST_LOG_TRIVIAL(error) << duplicate_error;
341 }
342 continue;
343 }
344
345 seen.push_back(descriptor.plugin_key);
346 m_missing_plugin_keys.erase(descriptor.plugin_key);
347
348 Plugin* existing = find_plugin_locked(descriptor.plugin_key);
349 if (existing == nullptr) {
350 Plugin plugin;
351 plugin.descriptor = std::move(descriptor);
352 m_plugins.push_back(std::move(plugin));
353 continue;
354 }
355
356 // A manifest-only rescan has nothing to say about capabilities, so the live module and
357 // instances are left alone.
358 if (!descriptor.install_state_valid)
359 descriptor.enabled = existing->descriptor.enabled;
360 existing->descriptor = std::move(descriptor);
361 }
362
363 if (!clear)
364 return;
365 }
366
367 // A package can be temporarily absent while an external side-loader replaces it. Keep the
368 // descriptor and its persisted enable state until the user explicitly removes the missing
369 // entry, or a later scan rediscovers it. In particular, do not unload here: the unload callback
370 // would turn a transient filesystem gap into enabled=false in the sidecar.
371 {
372 std::lock_guard<std::mutex> lock(m_mutex);
373 for (const Plugin& plugin : m_plugins) {
374 if (plugin.descriptor.has_local_package() &&
375 std::find(seen.begin(), seen.end(), plugin.descriptor.plugin_key) == seen.end())
376 m_missing_plugin_keys.insert(plugin.descriptor.plugin_key);
377 }
378 }
379}

References _u8L, Slic3r::Plugin::descriptor, Slic3r::PluginDescriptor::enabled, find_plugin_locked(), Slic3r::GUI::format(), m_missing_plugin_keys, m_mutex, and m_plugins.

Referenced by run_discovery_task().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ notify_plugin_load_state_changed()

void Slic3r::PluginManager::notify_plugin_load_state_changed ( bool changed)
private
753{
754 if (changed)
755 m_load_cv.notify_all();
756}
std::condition_variable m_load_cv
Definition PluginManager.hpp:329

References m_load_cv.

Referenced by cancel_plugin_load(), release_load_slot(), and unload_plugin().

Here is the caller graph for this function:

◆ operator=()

PluginManager & Slic3r::PluginManager::operator= ( const PluginManager & )
privatedelete

References PluginManager().

Here is the call graph for this function:

◆ release_load_slot()

void Slic3r::PluginManager::release_load_slot ( const std::string & plugin_key)
private
759{
760 bool changed = false;
761 {
762 std::lock_guard<std::mutex> lock(m_mutex);
763 changed = m_load_in_progress.erase(plugin_key) > 0;
764 m_cancelled.erase(plugin_key);
765 }
767}

References m_cancelled, m_load_in_progress, m_mutex, and notify_plugin_load_state_changed().

Referenced by load_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ remove_missing_plugins()

void Slic3r::PluginManager::remove_missing_plugins ( const std::vector< std::string > & plugin_keys)
394{
395 const std::unordered_set<std::string> requested(plugin_keys.begin(), plugin_keys.end());
396
397 // The predicate is evaluated only while m_mutex is held by unload_and_erase_if(). Checking the
398 // current missing set here prevents a package that reappeared between the dialog and removal
399 // from being erased.
400 unload_and_erase_if([this, &requested](const Plugin& plugin) {
401 return requested.count(plugin.descriptor.plugin_key) != 0 &&
402 m_missing_plugin_keys.count(plugin.descriptor.plugin_key) != 0;
403 });
404
405 std::lock_guard<std::mutex> lock(m_mutex);
406 for (const std::string& plugin_key : requested)
407 m_missing_plugin_keys.erase(plugin_key);
408}

References Slic3r::Plugin::descriptor, m_missing_plugin_keys, m_mutex, Slic3r::PluginDescriptor::plugin_key, and unload_and_erase_if().

Referenced by Slic3r::GUI::PluginsDialog::prompt_for_missing_plugins().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ rescan_plugins()

void Slic3r::PluginManager::rescan_plugins ( )
271{
272 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Rescanning plugins...";
273
274 std::string wait_error;
275 wait_for_discovery(std::chrono::milliseconds::max(), wait_error);
276
277 discover_plugins(/*async=*/false, /*clear=*/true);
278}
void discover_plugins(bool async=false, bool clear=false)
Definition PluginManager.cpp:247

References discover_plugins(), and wait_for_discovery().

Referenced by Slic3r::GUI::refresh_plugin_metadata_blocking().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ revoke_plugin_permissions()

void Slic3r::PluginManager::revoke_plugin_permissions ( const std::string & plugin_key)
private
1236{
1237 std::lock_guard<std::mutex> state_lock(m_install_state_mutex);
1238
1239 PluginDescriptor descriptor;
1240 if (!try_get_plugin_descriptor(plugin_key, descriptor) || descriptor.plugin_root.empty())
1241 return;
1242
1243 const boost::filesystem::path root(descriptor.plugin_root);
1244 PluginInstallState state;
1245 if (!read_install_state(root, state)) {
1246 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": Failed to read install state for " << plugin_key;
1247 return;
1248 }
1249
1250 state.permissions = {};
1251 if (!write_install_state(root, state))
1252 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": Failed to revoke permissions for " << plugin_key;
1253}

References m_install_state_mutex, Slic3r::PluginInstallState::permissions, Slic3r::PluginDescriptor::plugin_root, Slic3r::read_install_state(), try_get_plugin_descriptor(), and Slic3r::write_install_state().

Referenced by install_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ run_discovery()

void Slic3r::PluginManager::run_discovery ( bool async,
bool clear )
private
281{
282 auto task = [this, clear]() { run_discovery_task(clear); };
283
284 if (async)
285 std::thread(std::move(task)).detach();
286 else
287 task();
288}
void run_discovery_task(bool clear)
Definition PluginManager.cpp:290

References run_discovery_task().

Referenced by discover_plugins().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ run_discovery_task()

void Slic3r::PluginManager::run_discovery_task ( bool clear)
private
291{
292 std::string error;
293
294 try {
295 std::string cloud_user_id;
296 {
297 std::lock_guard<std::mutex> lock(m_mutex);
298 cloud_user_id = m_cloud_user_id;
299 }
300
301 const std::vector<std::string> dirs = get_plugin_directories(cloud_user_id);
302 std::vector<PluginDescriptor> discovered = discover_plugin_packages(dirs, error);
303 merge_discovered_plugins(std::move(discovered), clear);
304 } catch (const std::exception& ex) {
305 error = std::string("Plugin discovery failed: ") + ex.what();
306 BOOST_LOG_TRIVIAL(error) << error;
307 } catch (...) {
308 error = "Plugin discovery failed: unknown error";
309 BOOST_LOG_TRIVIAL(error) << error;
310 }
311
312 {
313 std::lock_guard<std::mutex> lock(m_mutex);
314 m_discovery_error = std::move(error);
317 }
318 m_discovery_cv.notify_all();
319}
std::condition_variable m_discovery_cv
Definition PluginManager.hpp:340
void merge_discovered_plugins(std::vector< PluginDescriptor > discovered, bool clear)
Definition PluginManager.cpp:321
std::vector< PluginDescriptor > discover_plugin_packages(const std::vector< std::string > &dirs, std::string &error)
Definition PluginFsUtils.cpp:254

References Slic3r::discover_plugin_packages(), Slic3r::get_plugin_directories(), m_cloud_user_id, m_discovery_complete, m_discovery_cv, m_discovery_error, m_discovery_in_progress, m_mutex, and merge_discovered_plugins().

Referenced by run_discovery().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ run_on_capability_load_callbacks()

void Slic3r::PluginManager::run_on_capability_load_callbacks ( const PluginCapabilityId & id)
private
1347{
1348 auto work = [callbacks = copy_capability_callbacks(CallbackType::Load), id] {
1349 for (auto& fn : callbacks) {
1350 try {
1351 fn(id);
1352 } catch (const std::exception& ex) {
1353 BOOST_LOG_TRIVIAL(error) << "Plugin capability load callback failed for " << id.plugin_key << "/" << id.name << ": "
1354 << ex.what();
1355 } catch (...) {
1356 BOOST_LOG_TRIVIAL(error) << "Plugin capability load callback failed for " << id.plugin_key << "/" << id.name;
1357 }
1358 }
1359 };
1360 if (wxTheApp == nullptr || wxIsMainThread())
1361 work();
1362 else
1363 GUI::wxGetApp().CallAfter(std::move(work));
1364}
@ Load
Definition PluginManager.hpp:258
std::vector< CapabilityLifecycleFn > copy_capability_callbacks(CallbackType type) const
Definition PluginManager.cpp:1295

References copy_capability_callbacks(), and Load.

Referenced by keep_installed_plugin_as_local(), and set_capability_enabled().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ run_on_capability_unload_callbacks()

void Slic3r::PluginManager::run_on_capability_unload_callbacks ( const PluginCapabilityId & id)
private
1367{
1368 auto work = [callbacks = copy_capability_callbacks(CallbackType::Unload), id] {
1369 for (auto& fn : callbacks) {
1370 try {
1371 fn(id);
1372 } catch (const std::exception& ex) {
1373 BOOST_LOG_TRIVIAL(error) << "Plugin capability unload callback failed for " << id.plugin_key << "/" << id.name << ": "
1374 << ex.what();
1375 } catch (...) {
1376 BOOST_LOG_TRIVIAL(error) << "Plugin capability unload callback failed for " << id.plugin_key << "/" << id.name;
1377 }
1378 }
1379 };
1380 if (wxTheApp == nullptr || wxIsMainThread())
1381 work();
1382 else
1383 GUI::wxGetApp().CallAfter(std::move(work));
1384}
@ Unload
Definition PluginManager.hpp:258

References copy_capability_callbacks(), and Unload.

Referenced by keep_installed_plugin_as_local(), and set_capability_enabled().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ run_on_load_callbacks()

void Slic3r::PluginManager::run_on_load_callbacks ( const std::string & plugin_key)
private
1309{
1310 auto work = [callbacks = copy_callbacks(CallbackType::Load), plugin_key] {
1311 for (auto& fn : callbacks) {
1312 try {
1313 fn(plugin_key);
1314 } catch (const std::exception& ex) {
1315 BOOST_LOG_TRIVIAL(error) << "Plugin load completion callback failed for " << plugin_key << ": " << ex.what();
1316 } catch (...) {
1317 BOOST_LOG_TRIVIAL(error) << "Plugin load completion callback failed for " << plugin_key;
1318 }
1319 }
1320 };
1321 if (wxTheApp == nullptr || wxIsMainThread())
1322 work();
1323 else
1324 GUI::wxGetApp().CallAfter(std::move(work));
1325}
std::vector< PluginLifecycleCompleteFn > copy_callbacks(CallbackType type) const
Definition PluginManager.cpp:1288

References copy_callbacks(), and Load.

Referenced by load_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ run_on_unload_callbacks()

void Slic3r::PluginManager::run_on_unload_callbacks ( const std::string & plugin_key)
private
1328{
1329 auto work = [callbacks = copy_callbacks(CallbackType::Unload), plugin_key] {
1330 for (auto& fn : callbacks) {
1331 try {
1332 fn(plugin_key);
1333 } catch (const std::exception& ex) {
1334 BOOST_LOG_TRIVIAL(error) << "Plugin unload completion callback failed for " << plugin_key << ": " << ex.what();
1335 } catch (...) {
1336 BOOST_LOG_TRIVIAL(error) << "Plugin unload completion callback failed for " << plugin_key << ": unknown error";
1337 }
1338 }
1339 };
1340 if (wxTheApp == nullptr || wxIsMainThread())
1341 work();
1342 else
1343 GUI::wxGetApp().CallAfter(std::move(work));
1344}

References copy_callbacks(), and Unload.

Referenced by unload_all_plugins(), and unload_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ run_script_capability()

ExecutionResult Slic3r::PluginManager::run_script_capability ( const std::string & plugin_key,
const std::string & capability_name,
std::string & error )
2403{
2404 ExecutionResult result;
2405 std::mutex mutex;
2406 std::condition_variable cv;
2407 bool done = false;
2408
2410 plugin_key, capability_name, [&](const ScriptCompletion& completion) {
2411 std::lock_guard<std::mutex> lock(mutex);
2412 result = completion.result;
2413 // A cancel_plugin() cancellation is expected termination, not an error to report.
2414 error = completion.cancelled ? std::string() : completion.error;
2415 done = true;
2416 cv.notify_all();
2417 });
2418
2419 if (launch.launched_async) {
2420 // Async run: the completion is delivered on the GUI thread. When this call is itself on
2421 // the GUI thread it must pump the event loop, or the completion can never be delivered.
2422 std::unique_lock<std::mutex> lock(mutex);
2423 if (wxTheApp != nullptr && wxIsMainThread()) {
2424 while (!done) {
2425 cv.wait_for(lock, std::chrono::milliseconds(10));
2426 lock.unlock();
2427 wxYield();
2428 lock.lock();
2429 }
2430 } else {
2431 cv.wait(lock, [&] { return done; });
2432 }
2433 } else {
2434 result = launch.outcome.result;
2435 error = launch.outcome.error;
2436 }
2437
2438 return result;
2439}
ScriptLaunchResult launch_script_capability(const std::string &plugin_key, const std::string &capability_name, ScriptCompletionFn on_done={})
Definition PluginManager.cpp:2441

References Slic3r::ScriptCompletion::cancelled, Slic3r::ScriptCompletion::error, launch_script_capability(), Slic3r::PluginManager::ScriptLaunchResult::launched_async, Slic3r::PluginManager::ScriptLaunchResult::outcome, and Slic3r::ScriptCompletion::result.

Here is the call graph for this function:

◆ set_capability_enabled()

void Slic3r::PluginManager::set_capability_enabled ( const PluginCapabilityId & id,
bool enabled )
1154{
1155 PluginCapabilityId changed;
1156 bool did_change = false;
1157
1158 {
1159 std::lock_guard<std::mutex> lock(m_mutex);
1160
1161 Plugin* plugin = find_plugin_locked(id.plugin_key);
1162 if (plugin == nullptr || !plugin->is_loaded())
1163 return;
1164
1165 for (const auto& capability : plugin->capabilities) {
1166 if (!capability || capability->name() != id.name ||
1167 (id.type != PluginCapabilityType::Unknown && capability->type() != id.type) ||
1168 capability->is_enabled() == enabled)
1169 continue;
1170
1171 capability->set_enabled(enabled);
1172 changed = capability->identity();
1173 did_change = true;
1174 break;
1175 }
1176 }
1177
1178 if (!did_change)
1179 return;
1180
1181 write_loaded_plugin_install_state(id.plugin_key);
1182
1183 if (enabled)
1185 else
1187}

References Slic3r::Plugin::capabilities, find_plugin_locked(), Slic3r::Plugin::is_loaded(), m_mutex, run_on_capability_load_callbacks(), run_on_capability_unload_callbacks(), Slic3r::Unknown, and write_loaded_plugin_install_state().

Referenced by load_plugin(), Slic3r::NetworkAgentFactory::register_python_printer_agent(), and Slic3r::GUI::PluginsDialog::toggle_plugin_capability().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_cloud_agent()

void Slic3r::PluginManager::set_cloud_agent ( std::shared_ptr< OrcaCloudServiceAgent > agent)
inline
205{ m_cloud_service.set_cloud_agent(std::move(agent)); }

References m_cloud_service.

Referenced by Slic3r::GUI::GUI_App::on_init_inner().

Here is the caller graph for this function:

◆ set_cloud_user()

void Slic3r::PluginManager::set_cloud_user ( const std::string & user_id)
1389{
1390 std::lock_guard<std::mutex> lock(m_mutex);
1391 m_cloud_user_id = user_id;
1392}

References m_cloud_user_id, and m_mutex.

Referenced by download_and_install_cloud_plugin(), Slic3r::GUI::GUI_App::on_init_inner(), and Slic3r::GUI::GUI_App::request_user_logout().

Here is the caller graph for this function:

◆ set_plugin_error()

bool Slic3r::PluginManager::set_plugin_error ( const std::string & plugin_key,
std::string error )
1397{
1398 std::string wait_error;
1399 if (!wait_for_discovery(std::chrono::milliseconds::max(), wait_error))
1400 return false;
1401
1402 std::lock_guard<std::mutex> lock(m_mutex);
1403 Plugin* plugin = find_plugin_locked(plugin_key);
1404 if (plugin == nullptr)
1405 return false;
1406
1407 plugin->descriptor.set_error(std::move(error));
1408 return true;
1409}

References Slic3r::Plugin::descriptor, find_plugin_locked(), m_mutex, Slic3r::PluginDescriptor::set_error(), and wait_for_discovery().

Referenced by delete_and_unsubscribe_cloud_plugin(), delete_plugin(), download_and_install_cloud_plugin(), Slic3r::GUI::PluginsDialog::install_cloud_plugin(), install_plugin(), launch_script_capability(), load_plugin(), load_plugin_impl(), Slic3r::NetworkAgentFactory::register_python_printer_agent(), Slic3r::GUI::PluginsDialog::run_script_plugin_capability(), Slic3r::GUI::PluginsDialog::toggle_plugin(), unsubscribe_cloud_plugin(), and update_cloud_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_shutting_down()

void Slic3r::PluginManager::set_shutting_down ( )
148{ m_shutting_down.store(true, std::memory_order_release); }

References m_shutting_down.

Referenced by Slic3r::GUI::GUI_App::shutdown(), and shutdown().

Here is the caller graph for this function:

◆ shutdown()

void Slic3r::PluginManager::shutdown ( )
151{
152 {
153 std::lock_guard<std::mutex> lock(m_mutex);
154 const bool idle_and_empty = m_load_in_progress.empty() && std::none_of(m_plugins.begin(), m_plugins.end(),
155 [](const Plugin& plugin) { return plugin.is_loaded(); });
156 if (!m_initialized && !m_discovery_in_progress && idle_and_empty)
157 return;
158 }
159
160 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": PluginManager shutdown enter";
161
162 // Detach the libslic3r hooks first so nothing dispatches into Python while (or after) plugins
163 // unload. The lifecycle-event hook also drains callbacks already in progress before returning;
164 // the remaining hook seams retain their existing shutdown requirements.
166
167 // Reject new plugin loads before we drain.
169
170 std::string wait_error;
171 if (!wait_for_discovery(std::chrono::milliseconds::max(), wait_error) && !wait_error.empty())
172 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": Plugin discovery did not finish cleanly during shutdown: " << wait_error;
173
174 // Wait for any in-progress plugin loads. Cancelled loads keep their slot until their worker
175 // has actually unwound, so this cannot return while one is still executing Python.
177
180
181 // Every async Script run has been drained by unload_all_plugins above; wait for every
182 // invocation worker before the interpreter is allowed to finalize.
184
185 // Every config write already goes to disk as it happens (store_capability_config), so this
186 // only catches an in-memory-only mutation. Note we flush rather than clear: unloading the
187 // plugins above must never discard their stored config.
188 if (m_config.dirty())
189 m_config.save();
190
191 // Drop the lifecycle subscriptions taken out during initialize(). Without this a second
192 // initialize() in the same process re-subscribes the same callbacks on top of the old ones,
193 // and every load would then write the install-state sidecar once per duplicate.
195
196 {
197 std::lock_guard<std::mutex> lock(m_mutex);
198 m_initialized = false;
199 }
200
201 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": PluginManager shutdown exit";
202}
void unload_all_plugins()
Definition PluginManager.cpp:1100
void set_shutting_down()
Definition PluginManager.cpp:148
void clear_callbacks()
Definition PluginManager.cpp:1281
void wait_for_all_plugin_loads() const
Definition PluginManager.cpp:687
void clear_pending_captures()
Definition PythonPluginBridge.cpp:337
static PythonPluginBridge & instance()
Definition PythonPluginBridge.cpp:97
void shutdown()
Definition PluginManager.cpp:2376
void uninstall()
Definition PluginHooks.cpp:152

References clear_callbacks(), Slic3r::PythonPluginBridge::clear_pending_captures(), Slic3r::PythonPluginBridge::instance(), Slic3r::ScriptAsyncRuntime::instance(), m_config, m_discovery_in_progress, m_initialized, m_load_in_progress, m_mutex, m_plugins, set_shutting_down(), Slic3r::ScriptAsyncRuntime::shutdown(), Slic3r::plugin_hooks::uninstall(), unload_all_plugins(), wait_for_all_plugin_loads(), and wait_for_discovery().

Referenced by Slic3r::GUI::GUI_App::~GUI_App(), ~PluginManager(), and Slic3r::GUI::GUI_App::on_init_inner().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ subscribe_and_install_cloud_plugin()

bool Slic3r::PluginManager::subscribe_and_install_cloud_plugin ( const std::string & plugin_key,
std::string & error )
1689{
1690 error.clear();
1691
1692 const std::string plugin_uuid = is_uuid(plugin_key) ? plugin_key : std::string{};
1693 if (plugin_uuid.empty()) {
1694 error = _u8L("Cloud plugin key is missing UUID.");
1695 return false;
1696 }
1697 if (!m_cloud_service.can_fetch_cloud_plugins()) {
1698 error = _u8L("Sign in to OrcaCloud to install this plugin.");
1699 return false;
1700 }
1701
1702 PluginDescriptor descriptor;
1703 bool found = try_get_plugin_descriptor(plugin_key, descriptor) && descriptor.is_cloud_plugin();
1704 if (!found) {
1706 found = try_get_plugin_descriptor(plugin_key, descriptor) && descriptor.is_cloud_plugin();
1707 }
1708
1709 if (!found) {
1710 if (!m_cloud_service.request_cloud_subscribe(plugin_uuid, error))
1711 return false;
1712
1714 found = try_get_plugin_descriptor(plugin_key, descriptor) && descriptor.is_cloud_plugin();
1715 if (!found) {
1716 error = _u8L("Subscribed cloud plugin was not returned by OrcaCloud.");
1717 return false;
1718 }
1719 }
1720
1721 if (descriptor.has_local_package())
1722 return true;
1723
1724 return download_and_install_cloud_plugin(descriptor.plugin_key, descriptor.version, error);
1725}
bool download_and_install_cloud_plugin(const std::string &plugin_key, const std::string &version, std::string &error)
Definition PluginManager.cpp:1727
bool is_uuid(const std::string &s)
Definition PluginDescriptor.hpp:194

References _u8L, download_and_install_cloud_plugin(), fetch_plugins_from_cloud(), Slic3r::PluginDescriptor::has_local_package(), Slic3r::PluginDescriptor::is_cloud_plugin(), Slic3r::is_uuid(), m_cloud_service, Slic3r::PluginDescriptor::plugin_key, try_get_plugin_descriptor(), and Slic3r::PluginDescriptor::version.

Referenced by Slic3r::resolve_missing_plugins().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ subscribe_on_capability_load_callback()

void Slic3r::PluginManager::subscribe_on_capability_load_callback ( CapabilityLifecycleFn fn)
1270{
1271 std::lock_guard<std::mutex> lock(m_mutex);
1272 m_capability_callbacks[CallbackType::Load].push_back(std::move(fn));
1273}

References Load, m_capability_callbacks, and m_mutex.

Referenced by Slic3r::GUI::ActionRegistry::init(), and Slic3r::GUI::GUI_App::init_plugin_gui_wiring().

Here is the caller graph for this function:

◆ subscribe_on_capability_unload_callback()

void Slic3r::PluginManager::subscribe_on_capability_unload_callback ( CapabilityLifecycleFn fn)
1276{
1277 std::lock_guard<std::mutex> lock(m_mutex);
1278 m_capability_callbacks[CallbackType::Unload].push_back(std::move(fn));
1279}

References m_capability_callbacks, m_mutex, and Unload.

Referenced by Slic3r::GUI::ActionRegistry::init(), and Slic3r::GUI::GUI_App::init_plugin_gui_wiring().

Here is the caller graph for this function:

◆ subscribe_on_load_callback()

void Slic3r::PluginManager::subscribe_on_load_callback ( PluginLifecycleCompleteFn fn)
1258{
1259 std::lock_guard<std::mutex> lock(m_mutex);
1260 m_callbacks[CallbackType::Load].push_back(std::move(fn));
1261}

References Load, m_callbacks, and m_mutex.

Referenced by Slic3r::GUI::ActionRegistry::init(), Slic3r::GUI::GUI_App::init_plugin_gui_wiring(), and initialize().

Here is the caller graph for this function:

◆ subscribe_on_unload_callback()

void Slic3r::PluginManager::subscribe_on_unload_callback ( PluginLifecycleCompleteFn fn)
1264{
1265 std::lock_guard<std::mutex> lock(m_mutex);
1266 m_callbacks[CallbackType::Unload].push_back(std::move(fn));
1267}

References m_callbacks, m_mutex, and Unload.

Referenced by Slic3r::GUI::ActionRegistry::init(), Slic3r::GUI::GUI_App::init_plugin_gui_wiring(), and initialize().

Here is the caller graph for this function:

◆ try_get_plugin_descriptor()

bool Slic3r::PluginManager::try_get_plugin_descriptor ( const std::string & plugin_key,
PluginDescriptor & out ) const
503{
504 std::lock_guard<std::mutex> lock(m_mutex);
505
506 const Plugin* plugin = find_plugin_locked(plugin_key);
507 if (plugin == nullptr)
508 return false;
509
510 out = plugin->descriptor;
511 return true;
512}

References Slic3r::Plugin::descriptor, find_plugin_locked(), and m_mutex.

Referenced by delete_and_unsubscribe_cloud_plugin(), delete_plugin(), download_and_install_cloud_plugin(), Slic3r::GUI::anonymous_namespace{ActionRegistry.cpp}::find_loaded_source_name(), Slic3r::GUI::PluginsDialog::get_descriptor(), get_install_state(), get_storage_dir(), initialize(), load_plugin_impl(), mark_plugin_install_state_disabled(), Slic3r::GUI::OptionsGroup::pick_plugin(), Slic3r::refresh_missing_plugins(), Slic3r::NetworkAgentFactory::register_python_printer_agent(), Slic3r::GUI::PluginsDialog::reload_local_plugin(), Slic3r::resolve_inactive_plugins(), Slic3r::resolve_missing_plugins(), revoke_plugin_permissions(), subscribe_and_install_cloud_plugin(), unsubscribe_cloud_plugin(), and update_cloud_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ try_get_plugin_descriptor_for_capability()

bool Slic3r::PluginManager::try_get_plugin_descriptor_for_capability ( const std::string & capability_name,
PluginCapabilityType type,
PluginDescriptor & out ) const
541{
542 std::lock_guard<std::mutex> lock(m_mutex);
543
544 for (const Plugin& plugin : m_plugins) {
545 for (const auto& capability : plugin.capabilities) {
546 if (!capability || capability->name() != capability_name)
547 continue;
548 if (type != PluginCapabilityType::Unknown && capability->type() != type)
549 continue;
550 out = plugin.descriptor;
551 return true;
552 }
553 }
554
555 return false;
556}

References m_mutex, m_plugins, and Slic3r::Unknown.

Referenced by Slic3r::plugin_hooks::anonymous_namespace{PluginHooks.cpp}::install_capability_resolver().

Here is the caller graph for this function:

◆ try_get_valid_plugin_descriptor()

bool Slic3r::PluginManager::try_get_valid_plugin_descriptor ( const std::string & plugin_key,
PluginDescriptor & out ) const
515{
516 std::lock_guard<std::mutex> lock(m_mutex);
517
518 const Plugin* plugin = find_plugin_locked(plugin_key);
519 if (plugin == nullptr || plugin->descriptor.is_invalid_package())
520 return false;
521
522 out = plugin->descriptor;
523 return true;
524}

References Slic3r::Plugin::descriptor, find_plugin_locked(), Slic3r::PluginDescriptor::is_invalid_package(), and m_mutex.

Referenced by Slic3r::GUI::PluginsDialog::get_descriptor(), and Slic3r::GUI::PluginsDialog::toggle_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ unload_all_plugins()

void Slic3r::PluginManager::unload_all_plugins ( )
1101{
1102 std::vector<Plugin> removed;
1103 std::vector<std::string> removed_keys;
1104 std::vector<std::shared_ptr<PluginCapabilityInterface>> capabilities;
1105 {
1106 std::lock_guard<std::mutex> lock(m_mutex);
1107 for (Plugin& plugin : m_plugins) {
1108 if (!plugin.is_loaded())
1109 continue;
1110
1111 // Take the live parts out via Plugin's own move; the descriptor stays behind, as in
1112 // unload_plugin() above.
1113 PluginDescriptor kept_descriptor = std::move(plugin.descriptor);
1114 Plugin taken = std::move(plugin);
1115 plugin.descriptor = std::move(kept_descriptor);
1116 removed.push_back(std::move(taken));
1117 removed_keys.push_back(plugin.descriptor.plugin_key);
1118 capabilities.insert(capabilities.end(), removed.back().capabilities.begin(), removed.back().capabilities.end());
1119 }
1120 }
1121
1122 // Bulk shutdown intentionally skips the normal unload notification path, but resources owned
1123 // by GUI subscribers still need to be torn down before the interpreter is finalized.
1124 for (const std::string& key : removed_keys)
1126
1127 // Broadcast cancellation to every plugin before waiting. This keeps bulk unload from
1128 // starving a later plugin while an earlier plugin uses the full cancellation grace period.
1130
1131 for (Plugin& plugin : removed)
1132 plugin_loader::unload(plugin);
1133}
void run_on_unload_callbacks(const std::string &plugin_key)
Definition PluginManager.cpp:1327
void cancel_and_wait_for_capabilities(const std::vector< std::shared_ptr< PluginCapabilityInterface > > &capabilities)
Definition PluginManager.cpp:769

References cancel_and_wait_for_capabilities(), m_mutex, m_plugins, run_on_unload_callbacks(), and Slic3r::plugin_loader::unload().

Referenced by shutdown().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ unload_and_erase_if()

void Slic3r::PluginManager::unload_and_erase_if ( const std::function< bool(const Plugin &)> & should_remove,
const std::function< void()> & after_erase_locked = {} )
private
412{
413 for (;;) {
414 std::vector<std::string> unload_keys;
415 {
416 std::lock_guard<std::mutex> lock(m_mutex);
417 for (const Plugin& plugin : m_plugins) {
418 if (should_remove(plugin) && plugin.is_loaded())
419 unload_keys.push_back(plugin.descriptor.plugin_key);
420 }
421 }
422
423 if (unload_keys.empty()) {
424 std::lock_guard<std::mutex> lock(m_mutex);
425 // Keep the check and erase in one critical section so a concurrent load cannot make a
426 // live entry reach vector compaction and move-assignment.
427 bool loaded_entry_found = false;
428 for (const Plugin& plugin : m_plugins) {
429 if (should_remove(plugin) && plugin.is_loaded()) {
430 loaded_entry_found = true;
431 break;
432 }
433 }
434 if (loaded_entry_found)
435 continue;
436
437 m_plugins.erase(std::remove_if(m_plugins.begin(), m_plugins.end(), should_remove), m_plugins.end());
438 if (after_erase_locked)
439 after_erase_locked();
440 break;
441 }
442 for (const std::string& key : unload_keys)
443 unload_plugin(key);
444 }
445}

References m_mutex, m_plugins, and unload_plugin().

Referenced by clear_cloud_plugin_metadata(), and remove_missing_plugins().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ unload_cloud_plugins()

void Slic3r::PluginManager::unload_cloud_plugins ( )
1136{
1137 std::vector<std::string> keys;
1138 {
1139 std::lock_guard<std::mutex> lock(m_mutex);
1140 for (const Plugin& plugin : m_plugins) {
1141 if (plugin.is_loaded() && plugin.descriptor.is_cloud_plugin())
1142 keys.push_back(plugin.descriptor.plugin_key);
1143 }
1144 }
1145
1146 // Release m_mutex before unloading: unload_plugin() re-acquires it and runs plugin teardown
1147 // plus unload callbacks, which can re-enter the manager.
1148 for (const std::string& key : keys)
1149 unload_plugin(key);
1150}

References m_mutex, m_plugins, and unload_plugin().

Referenced by Slic3r::GUI::GUI_App::request_user_logout().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ unload_plugin()

bool Slic3r::PluginManager::unload_plugin ( const std::string & plugin_key)
1049{
1050 auto notify_unload_complete = [this, &plugin_key]() {
1051 if (!m_shutting_down.load(std::memory_order_acquire))
1052 run_on_unload_callbacks(plugin_key);
1053 };
1054
1055 Plugin removed;
1056 bool cancelled = false;
1057 bool found = false;
1058
1059 {
1060 std::lock_guard<std::mutex> lock(m_mutex);
1061
1062 // Cancel any in-progress load for this plugin so its worker discards the result.
1063 cancelled = cancel_plugin_load_locked(plugin_key);
1064
1065 Plugin* plugin = find_plugin_locked(plugin_key);
1066 if (plugin != nullptr && plugin->is_loaded()) {
1067 found = true;
1068
1069 // Take the live parts out of the entry via Plugin's own move assignment; the
1070 // descriptor stays behind, so the package remains discovered. Everything
1071 // capability-shaped is discarded with the capabilities — the user's enable choices
1072 // live on in the .install_state.json sidecar.
1073 PluginDescriptor kept_descriptor = std::move(plugin->descriptor);
1074 removed = std::move(*plugin);
1075 plugin->descriptor = std::move(kept_descriptor);
1076 }
1077 }
1078
1079 if (!found) {
1081 return true;
1082 }
1083
1084 notify_unload_complete();
1085
1086 cancel_and_wait_for_capabilities(removed.capabilities);
1087
1089
1090 // Teardown runs outside m_mutex: it calls into Python and must not hold the registry lock.
1091 plugin_loader::unload(removed);
1092
1093 // The .install_state.json sidecar is flipped to enabled=false by the on-unload callback
1094 // (skipped during shutdown), so the auto-load list survives app exit.
1095 BOOST_LOG_TRIVIAL(info) << "Unloaded plugin: " << plugin_key;
1096
1097 return true;
1098}

References cancel_and_wait_for_capabilities(), cancel_plugin_load_locked(), Slic3r::Plugin::capabilities, Slic3r::Plugin::descriptor, find_plugin_locked(), Slic3r::Plugin::is_loaded(), m_mutex, m_shutting_down, notify_plugin_load_state_changed(), run_on_unload_callbacks(), and Slic3r::plugin_loader::unload().

Referenced by delete_and_unsubscribe_cloud_plugin(), delete_installed_plugin_package(), install_plugin(), Slic3r::GUI::PluginsDialog::reload_local_plugin(), Slic3r::GUI::PluginsDialog::run_script_plugin_capability(), Slic3r::GUI::PluginsDialog::toggle_plugin(), unload_and_erase_if(), unload_cloud_plugins(), and update_cloud_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ unsubscribe_cloud_plugin()

bool Slic3r::PluginManager::unsubscribe_cloud_plugin ( const std::string & plugin_key,
std::string & error )
2043{
2044 if (!wait_for_discovery(std::chrono::milliseconds::max(), error))
2045 return false;
2046
2047 error.clear();
2048
2049 PluginDescriptor descriptor;
2050 if (!try_get_plugin_descriptor(plugin_key, descriptor)) {
2051 error = GUI::format(_u8L("Plugin not found: %1%"), plugin_key);
2052 return false;
2053 }
2054
2055 if (!descriptor.is_cloud_plugin()) {
2056 error = _u8L("Only cloud plugins can be unsubscribed.");
2057 set_plugin_error(plugin_key, error);
2058 return false;
2059 }
2060
2061 if (descriptor.cloud && descriptor.cloud->is_mine) {
2062 error = _u8L("Cannot unsubscribe your own plugins.");
2063 set_plugin_error(plugin_key, error);
2064 return false;
2065 }
2066
2067 if (!m_cloud_service.request_cloud_unsubscribe(descriptor, error)) {
2068 set_plugin_error(plugin_key, error);
2069 return false;
2070 }
2071
2072 return finalize_cloud_plugin_removal(descriptor, true, error);
2073}

References _u8L, Slic3r::PluginDescriptor::cloud, finalize_cloud_plugin_removal(), Slic3r::GUI::format(), Slic3r::PluginDescriptor::is_cloud_plugin(), m_cloud_service, set_plugin_error(), try_get_plugin_descriptor(), and wait_for_discovery().

Here is the call graph for this function:

◆ update_cloud_metadata()

void Slic3r::PluginManager::update_cloud_metadata ( const std::vector< PluginDescriptor > & cloud_list)
1504{
1505 std::lock_guard<std::mutex> lock(m_mutex);
1506
1507 // Collected and appended after the scan: push_back into m_plugins would invalidate the
1508 // pointers the loop is holding.
1509 std::vector<PluginDescriptor> new_entries;
1510
1511 for (const PluginDescriptor& cloud_entry : cloud_list) {
1512 const std::string cloud_uuid = cloud_entry.cloud_uuid();
1513 const std::string cloud_key = cloud_entry.plugin_key;
1514 if (cloud_uuid.empty()) {
1515 BOOST_LOG_TRIVIAL(warning) << "Skipping cloud plugin record without UUID";
1516 continue;
1517 }
1518
1519 const auto matches_cloud_descriptor = [&cloud_key, &cloud_uuid](const PluginDescriptor& entry) {
1520 if (!cloud_key.empty() && entry.plugin_key == cloud_key)
1521 return true;
1522 return entry.is_cloud_plugin() && entry.cloud_uuid() == cloud_uuid;
1523 };
1524
1525 const auto existing = std::find_if(m_plugins.begin(), m_plugins.end(), [&matches_cloud_descriptor](const Plugin& plugin) {
1526 return matches_cloud_descriptor(plugin.descriptor);
1527 });
1528
1529 if (existing == m_plugins.end()) {
1530 PluginDescriptor normalized_entry = cloud_entry;
1531 if (normalized_entry.plugin_key.empty())
1532 normalized_entry.plugin_key = cloud_uuid;
1533 if (!normalized_entry.cloud.has_value())
1534 normalized_entry.cloud = CloudPluginState{cloud_uuid, false, false, false};
1535 else if (normalized_entry.cloud->uuid.empty())
1536 normalized_entry.cloud->uuid = cloud_uuid;
1537 new_entries.push_back(std::move(normalized_entry));
1538 continue;
1539 }
1540
1541 PluginDescriptor& entry = existing->descriptor;
1542
1543 const PluginDescriptor local_entry = entry;
1544 const std::string installed_version = entry.installed_version;
1545 const std::string latest_version = cloud_entry.latest_available_version();
1546 const std::string local_plugin_root = entry.plugin_root;
1547 const std::string local_entry_path = entry.entry_path;
1548 const bool local_metadata_ok = entry.is_metadata_valid();
1549 const bool has_local_package = entry.has_local_package();
1550 const std::string previous_error = entry.error;
1551 // The cloud does not know about the local auto-load flag, which comes from the sidecar.
1552 const bool enabled = entry.enabled;
1553
1554 entry = cloud_entry;
1555 entry.plugin_root = local_plugin_root;
1556 entry.entry_path = local_entry_path;
1557 if (has_local_package)
1558 apply_plugin_metadata_fallbacks(entry, local_entry);
1559 if (entry.plugin_key.empty())
1560 entry.plugin_key = cloud_uuid;
1561 entry.metadata_valid = has_local_package ? local_metadata_ok : cloud_entry.metadata_valid;
1562 entry.error = previous_error;
1563 entry.enabled = enabled;
1564 if (!entry.cloud.has_value())
1565 entry.cloud = CloudPluginState{cloud_uuid, has_local_package, false, false};
1566 else if (entry.cloud->uuid.empty())
1567 entry.cloud->uuid = cloud_uuid;
1568
1569 entry.cloud->installed = has_local_package;
1570 // The installed version is the source of truth read back from the install-state sidecar
1571 // (the version fetched from the cloud at install time), not the local manifest/PEP723
1572 // header. The header may be stale — the cloud can bump the version without the header
1573 // changing — which would otherwise make an already-updated plugin appear perpetually out
1574 // of date.
1575 entry.installed_version = has_local_package ? installed_version : std::string{};
1576 entry.cloud->update_available = has_local_package && local_metadata_ok && !installed_version.empty() && !latest_version.empty() &&
1577 is_cloud_version_newer(latest_version, installed_version);
1578 if (entry.normalized_error() == CLOUD_PLUGIN_NOT_FOUND_ERROR)
1579 entry.clear_error();
1580 }
1581
1582 for (PluginDescriptor& descriptor : new_entries) {
1583 Plugin plugin;
1584 plugin.descriptor = std::move(descriptor);
1585 m_plugins.push_back(std::move(plugin));
1586 }
1587}
bool is_cloud_version_newer(const std::string &cloud_version, const std::string &local_version)
Definition PluginManager.cpp:1489
boost::filesystem::path local_plugin_root()
Definition PluginLoader.cpp:74
void apply_plugin_metadata_fallbacks(PluginDescriptor &target, const PluginDescriptor &fallback)
Definition PluginDescriptor.hpp:149

References Slic3r::apply_plugin_metadata_fallbacks(), Slic3r::PluginDescriptor::clear_error(), Slic3r::PluginDescriptor::cloud, Slic3r::Plugin::descriptor, Slic3r::PluginDescriptor::enabled, Slic3r::PluginDescriptor::entry_path, Slic3r::PluginDescriptor::error, Slic3r::PluginDescriptor::has_local_package(), Slic3r::PluginDescriptor::installed_version, Slic3r::PluginDescriptor::is_metadata_valid(), m_mutex, m_plugins, Slic3r::PluginDescriptor::metadata_valid, Slic3r::PluginDescriptor::normalized_error(), Slic3r::PluginDescriptor::plugin_key, and Slic3r::PluginDescriptor::plugin_root.

Referenced by Slic3r::GUI::PluginsDialog::delete_local_plugin(), fetch_plugins_from_cloud(), Slic3r::GUI::refresh_plugin_metadata_blocking(), and Slic3r::GUI::PluginsDialog::reinstall_cloud_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_cloud_plugin()

bool Slic3r::PluginManager::update_cloud_plugin ( const std::string & plugin_key,
std::string & error,
std::string version = "" )
1807{
1808 error.clear();
1809
1810 PluginDescriptor descriptor;
1811 if (!try_get_plugin_descriptor(plugin_key, descriptor)) {
1812 error = GUI::format(_u8L("Plugin not found: %1%"), plugin_key);
1813 return false;
1814 }
1815
1816 if (!descriptor.is_cloud_plugin()) {
1817 error = _u8L("Only cloud plugins can be updated.");
1818 set_plugin_error(plugin_key, error);
1819 return false;
1820 }
1821
1822 // An empty version means "update the current plugin". If there is no update available, the
1823 // intention is unclear.
1824 if (version.empty()) {
1825 if (descriptor.cloud->update_available) {
1826 version = descriptor.latest_available_version();
1827 } else {
1828 error = _u8L("No update is available for this plugin.");
1829 set_plugin_error(plugin_key, error);
1830 return false;
1831 }
1832 }
1833
1834 clear_plugin_error(plugin_key);
1835
1836 if (descriptor.has_local_package()) {
1837 boost::filesystem::path resolved_root;
1838 bool resolved_allowed_plugin_root = false;
1839 {
1840 std::lock_guard<std::mutex> lock(m_mutex);
1841 resolved_allowed_plugin_root = resolve_allowed_plugin_root(descriptor, get_plugin_directories(m_cloud_user_id),
1842 _u8L("Refusing to delete a plugin outside the known plugin directories."),
1843 resolved_root, error);
1844 }
1845
1846 if (!resolved_allowed_plugin_root) {
1847 set_plugin_error(plugin_key, error);
1848 return false;
1849 }
1850
1851 unload_plugin(plugin_key);
1852
1853 if (!delete_plugin_root(resolved_root, plugin_key, error)) {
1854 set_plugin_error(plugin_key, error);
1855 return false;
1856 }
1857 }
1858
1859 if (!download_and_install_cloud_plugin(plugin_key, version, error)) {
1860 set_plugin_error(plugin_key, error);
1861 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": " << error;
1862 return false;
1863 }
1864
1865 clear_plugin_error(plugin_key);
1866
1867 return true;
1868}

References _u8L, clear_plugin_error(), Slic3r::PluginDescriptor::cloud, Slic3r::delete_plugin_root(), download_and_install_cloud_plugin(), Slic3r::GUI::format(), Slic3r::get_plugin_directories(), Slic3r::PluginDescriptor::has_local_package(), Slic3r::PluginDescriptor::is_cloud_plugin(), Slic3r::PluginDescriptor::latest_available_version(), m_cloud_user_id, m_mutex, Slic3r::resolve_allowed_plugin_root(), set_plugin_error(), try_get_plugin_descriptor(), and unload_plugin().

Referenced by Slic3r::GUI::PluginsDialog::update_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ wait_for_all_plugin_loads() [1/2]

void Slic3r::PluginManager::wait_for_all_plugin_loads ( ) const
688{
689 std::unique_lock<std::mutex> lock(m_mutex);
690 m_load_cv.wait(lock, [this]() { return m_load_in_progress.empty(); });
691}

References m_load_cv, m_load_in_progress, and m_mutex.

Referenced by Slic3r::execute_capabilities_from_refs(), and shutdown().

Here is the caller graph for this function:

◆ wait_for_all_plugin_loads() [2/2]

bool Slic3r::PluginManager::wait_for_all_plugin_loads ( std::chrono::milliseconds timeout) const
694{
695 std::unique_lock<std::mutex> lock(m_mutex);
696 return m_load_cv.wait_for(lock, timeout, [this]() { return m_load_in_progress.empty(); });
697}

References m_load_cv, m_load_in_progress, and m_mutex.

◆ wait_for_discovery()

bool Slic3r::PluginManager::wait_for_discovery ( std::chrono::milliseconds timeout,
std::string & error ) const
466{
467 std::unique_lock<std::mutex> lock(m_mutex);
469 return true;
470
471 const auto done = [this]() { return !m_discovery_in_progress; };
472
473 if (timeout == std::chrono::milliseconds::max()) {
474 m_discovery_cv.wait(lock, done);
475 return true;
476 }
477
478 if (!m_discovery_cv.wait_for(lock, timeout, done)) {
479 error = "Plugin discovery is still running";
480 return false;
481 }
482
483 return true;
484}

References m_discovery_cv, m_discovery_in_progress, and m_mutex.

Referenced by clear_plugin_error(), delete_and_unsubscribe_cloud_plugin(), delete_plugin(), install_plugin(), rescan_plugins(), set_plugin_error(), shutdown(), and unsubscribe_cloud_plugin().

Here is the caller graph for this function:

◆ wait_for_plugin_load()

bool Slic3r::PluginManager::wait_for_plugin_load ( const std::string & plugin_key,
std::chrono::milliseconds timeout,
std::string & error ) const
700{
701 std::unique_lock<std::mutex> lock(m_mutex);
702
703 const auto done = [this, &plugin_key]() { return m_load_in_progress.count(plugin_key) == 0; };
704
705 if (timeout == std::chrono::milliseconds::max()) {
706 m_load_cv.wait(lock, done);
707 } else if (!m_load_cv.wait_for(lock, timeout, done)) {
708 error = _u8L("Plugin load is still in progress");
709 return false;
710 }
711
712 const auto it = m_load_errors.find(plugin_key);
713 if (it != m_load_errors.end()) {
714 error = it->second;
715 return false;
716 }
717
718 return true;
719}

References _u8L, m_load_cv, m_load_errors, m_load_in_progress, and m_mutex.

Referenced by Slic3r::GUI::PluginsDialog::reinstall_cloud_plugin(), Slic3r::GUI::PluginsDialog::reload_local_plugin(), Slic3r::resolve_inactive_plugins(), and Slic3r::resolve_missing_plugins().

Here is the caller graph for this function:

◆ write_loaded_plugin_install_state()

void Slic3r::PluginManager::write_loaded_plugin_install_state ( const std::string & plugin_key)
private
1190{
1191 std::lock_guard<std::mutex> state_lock(m_install_state_mutex);
1192
1193 PluginDescriptor descriptor;
1194 std::vector<std::pair<std::string, bool>> capabilities;
1195 {
1196 std::lock_guard<std::mutex> lock(m_mutex);
1197
1198 const Plugin* plugin = find_plugin_locked(plugin_key);
1199 if (plugin == nullptr || !plugin->is_loaded())
1200 return;
1201
1202 descriptor = plugin->descriptor;
1203 for (const auto& capability : plugin->capabilities)
1204 if (capability)
1205 capabilities.emplace_back(capability->name(), capability->is_enabled());
1206 }
1207
1208 if (descriptor.plugin_root.empty())
1209 return;
1210
1211 write_install_state(boost::filesystem::path(descriptor.plugin_root), descriptor, /*enabled=*/true, capabilities);
1212}

References Slic3r::Plugin::capabilities, Slic3r::Plugin::descriptor, find_plugin_locked(), Slic3r::Plugin::is_loaded(), m_install_state_mutex, m_mutex, Slic3r::PluginDescriptor::plugin_root, and Slic3r::write_install_state().

Referenced by initialize(), and set_capability_enabled().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ m_callbacks

std::map<CallbackType, std::vector<PluginLifecycleCompleteFn> > Slic3r::PluginManager::m_callbacks
private

◆ m_cancelled

std::unordered_set<std::string> Slic3r::PluginManager::m_cancelled
private

◆ m_capability_callbacks

std::map<CallbackType, std::vector<CapabilityLifecycleFn> > Slic3r::PluginManager::m_capability_callbacks
private

◆ m_cloud_service

◆ m_cloud_user_id

◆ m_config

PluginConfig Slic3r::PluginManager::m_config
private

◆ m_discovery_complete

bool Slic3r::PluginManager::m_discovery_complete = false
private

◆ m_discovery_cv

std::condition_variable Slic3r::PluginManager::m_discovery_cv
mutableprivate

◆ m_discovery_error

std::string Slic3r::PluginManager::m_discovery_error
private

◆ m_discovery_in_progress

bool Slic3r::PluginManager::m_discovery_in_progress = false
private

◆ m_initialized

bool Slic3r::PluginManager::m_initialized = false
private

Referenced by initialize(), and shutdown().

◆ m_install_state_mutex

std::mutex Slic3r::PluginManager::m_install_state_mutex
mutableprivate

◆ m_load_cv

std::condition_variable Slic3r::PluginManager::m_load_cv
mutableprivate

◆ m_load_errors

std::map<std::string, std::string> Slic3r::PluginManager::m_load_errors
private

◆ m_load_in_progress

std::unordered_set<std::string> Slic3r::PluginManager::m_load_in_progress
private

◆ m_missing_plugin_keys

std::unordered_set<std::string> Slic3r::PluginManager::m_missing_plugin_keys
private

◆ m_mutex

◆ m_plugins

◆ m_shutting_down

std::atomic<bool> Slic3r::PluginManager::m_shutting_down {false}
private

The documentation for this class was generated from the following files: