Compare commits

..

6 Commits

11 changed files with 115 additions and 18 deletions

View File

@@ -201,9 +201,12 @@ if (WIN32)
if(MSVC)
target_link_options(OrcaSlicer_app_gui PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
endif()
target_compile_definitions(OrcaSlicer_app_gui PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE)
target_compile_definitions(OrcaSlicer_app_gui PRIVATE "$<$<NOT:$<CONFIG:RelWithDebInfo>>:SLIC3R_WRAPPER_NOCONSOLE>")
add_dependencies(OrcaSlicer_app_gui OrcaSlicer)
set_target_properties(OrcaSlicer_app_gui PROPERTIES OUTPUT_NAME "orca-slicer")
set_target_properties(OrcaSlicer_app_gui PROPERTIES
OUTPUT_NAME "orca-slicer"
WIN32_EXECUTABLE "$<NOT:$<CONFIG:RelWithDebInfo>>"
)
target_link_libraries(OrcaSlicer_app_gui PRIVATE boost_headeronly)
endif ()

View File

@@ -535,6 +535,7 @@ endif ()
encoding_check(libslic3r)
target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0)
target_compile_definitions(libslic3r PRIVATE $<$<CONFIG:RelWithDebInfo>:SLIC3R_CONSOLE_LOG>)
target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(libslic3r SYSTEM PUBLIC ${EXPAT_INCLUDE_DIRS})

View File

@@ -197,6 +197,7 @@ std::string debug_out_path(const char *name, ...);
// smaller level means less log. level=5 means saving all logs.
void set_log_path_and_level(const std::string& file, unsigned int level);
void flush_logs();
void shutdown_console_logging();
boost::filesystem::path get_log_file_name();
// A special type for strings encoded in the local Windows 8-bit code page.

View File

@@ -5,6 +5,7 @@
#include <locale>
#include <ctime>
#include <cstdarg>
#include <iostream>
#include <stdio.h>
#include <filesystem>
#include <sstream>
@@ -50,14 +51,19 @@
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/async_frontend.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/core/null_deleter.hpp>
#include <boost/locale.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
@@ -178,6 +184,7 @@ unsigned get_logging_level()
}
boost::shared_ptr<boost::log::sinks::synchronous_sink<boost::log::sinks::text_file_backend>> g_log_sink;
boost::shared_ptr<boost::log::sinks::asynchronous_sink<boost::log::sinks::text_ostream_backend>> g_console_log_sink;
// Force set_logging_level(<=error) after loading of the DLL.
// This is currently only needed if libslic3r is loaded as a shared library into Perl interpreter
@@ -350,6 +357,19 @@ namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace keywords = boost::log::keywords;
namespace attrs = boost::log::attributes;
namespace sinks = boost::log::sinks;
void shutdown_console_logging()
{
if (!g_console_log_sink)
return;
auto console_sink = g_console_log_sink;
boost::log::core::get()->remove_sink(console_sink);
console_sink->stop();
g_console_log_sink.reset();
}
void set_log_path_and_level(const std::string& file, unsigned int level)
{
#ifdef __APPLE__
@@ -381,6 +401,24 @@ void set_log_path_and_level(const std::string& file, unsigned int level)
keywords::auto_flush = true
);
shutdown_console_logging();
#ifdef SLIC3R_CONSOLE_LOG
auto console_backend = boost::make_shared<sinks::text_ostream_backend>();
console_backend->add_stream(boost::shared_ptr<std::ostream>(&std::cout, boost::null_deleter()));
console_backend->auto_flush(true);
g_console_log_sink = boost::make_shared<sinks::asynchronous_sink<sinks::text_ostream_backend>>(console_backend);
g_console_log_sink->set_formatter(
expr::stream
<< "[" << expr::attr< logging::trivial::severity_level >("Severity") << "]\t"
<< expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f") << " "
<<"[Thread " << expr::attr<attrs::current_thread_id::value_type>("ThreadID") << "]"
<< ": " << expr::smessage
);
boost::log::core::get()->add_sink(g_console_log_sink);
#endif
logging::add_common_attributes();
set_logging_level(level);

View File

@@ -2355,6 +2355,7 @@ GUI_App::~GUI_App()
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": exit");
shutdown_console_logging();
}
bool GUI_App::is_blocking_printing(MachineObject *obj_)

View File

@@ -48,13 +48,14 @@ std::string plugin_defaults_user_script()
return WebViewHostDialog::document_start_injector(css, "orca-plugin-defaults", "beforeend");
}
// Injected into every page at document start (before the plugin's own scripts).
// Defines window.orca as the only host surface the page may use. It references
// window.wx lazily (at call time) so it never races the backend's deferred
// registration of the "wx" message handler. Guarded against double-injection so
// it is harmless if also prepended.
// Injected into the top-level page at document start (before the plugin's own
// scripts). Defines window.orca as the only host surface the page may use. It
// references window.wx lazily (at call time) so it never races the backend's
// deferred registration of the "wx" message handler. Guarded against
// double-injection so it is harmless if also prepended.
constexpr char ORCA_BRIDGE_JS[] = R"JS(
(function () {
if (window.top !== window.self) return;
if (window.orca) return;
var handlers = [];
function send(kind, data) {

View File

@@ -300,7 +300,12 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
add_action("open_folder", "Show in folder", has_local);
add_action("reinstall_plugin", "Reinstall");
if (is_cloud) {
add_action("reinstall_plugin", "Reinstall");
} else {
add_action("reload_plugin", "Reload");
add_action("clear_cache_reload_plugin", "Delete cache and reload");
}
return available_actions;
}
@@ -739,11 +744,13 @@ void PluginsDialog::handle_plugin_menu_action(const std::string& plugin_key, con
unsubscribe_cloud_plugin(row_data);
} else if (action == "delete_mine_plugin") {
delete_mine_local_and_cloud_plugin(plugin_key);
} else if (action == "reload_plugin") {
reload_local_plugin(plugin_key, /*clear_cache=*/false);
} else if (action == "clear_cache_reload_plugin") {
reload_local_plugin(plugin_key, /*clear_cache=*/true);
} else if (action == "reinstall_plugin") {
if (row_data.is_cloud_plugin())
reinstall_cloud_plugin(row_data);
else
reinstall_local_plugin(plugin_key);
}
}
@@ -1123,7 +1130,7 @@ void PluginsDialog::unsubscribe_cloud_plugin(const PluginDescriptor& plugin)
_L("Unsubscribing plugin"), _L("Deleting local files and unsubscribing plugin..."));
}
void PluginsDialog::reinstall_local_plugin(const std::string& plugin_key)
void PluginsDialog::reload_local_plugin(const std::string& plugin_key, bool clear_cache)
{
if (plugin_key.empty())
return;
@@ -1132,11 +1139,34 @@ void PluginsDialog::reinstall_local_plugin(const std::string& plugin_key)
std::pair<bool, std::string> reload_result{false, ""};
try {
reload_result = run_with_dialog_wait(
[plugin_key, was_loaded]() -> std::pair<bool, std::string> {
[plugin_key, was_loaded, clear_cache]() -> std::pair<bool, std::string> {
PluginManager& manager = PluginManager::instance();
boost::filesystem::path cache_dir;
if (clear_cache) {
PluginDescriptor descriptor;
if (!manager.try_get_plugin_descriptor(plugin_key, descriptor))
return {false, "Plugin not found."};
boost::filesystem::path resolved_root;
std::string resolve_error;
if (!resolve_allowed_plugin_root(descriptor, {get_orca_plugins_dir()},
"Refusing to clear a plugin cache outside the local plugin directory.",
resolved_root, resolve_error))
return {false, resolve_error};
cache_dir = resolved_root / "__whl_extracted__";
}
if (!manager.unload_plugin(plugin_key))
return {false, "Failed to unload plugin."};
if (clear_cache) {
boost::system::error_code ec;
boost::filesystem::remove_all(cache_dir, ec);
if (ec)
return {false, "Failed to clear plugin cache: " + ec.message()};
}
manager.load_plugin(plugin_key, false);
std::string error;
if (!manager.wait_for_plugin_load(plugin_key, std::chrono::minutes(5), error) || !manager.is_plugin_loaded(plugin_key))

View File

@@ -96,7 +96,7 @@ private:
void open_plugin_folder(const Slic3r::PluginDescriptor& plugin);
void delete_local_plugin(const Slic3r::PluginDescriptor& plugin);
void unsubscribe_cloud_plugin(const Slic3r::PluginDescriptor& plugin);
void reinstall_local_plugin(const std::string& plugin_key);
void reload_local_plugin(const std::string& plugin_key, bool clear_cache);
void reinstall_cloud_plugin(const Slic3r::PluginDescriptor& plugin);
void delete_mine_local_and_cloud_plugin(const std::string& plugin_key);

View File

@@ -96,6 +96,9 @@ std::string WebViewHostDialog::document_start_injector(const std::string& markup
const std::string literal = nlohmann::json(markup).dump();
std::string s;
s += "(function(){";
// wxWebView's AddUserScript runs in child frames too (including cross-origin
// frames on WebView2). Host theme state belongs only to the top-level page.
s += "if(window.top!==window.self)return;";
s += prelude;
s += "var css=" + literal + ";";
s += "function inject(){";

View File

@@ -54,7 +54,7 @@ struct PluginDescriptor
std::string description; // Plugin description
std::string author; // Plugin author from manifest, if available
std::string version; // Selected plugin version
std::string latest_version; // Latest available cloud version fallback when changelog is unavailable.
std::string latest_version; // Authoritative latest available cloud version.
std::string installed_version; // Locally installed package version. Preserved across cloud merges, which overwrite `version` with the latest cloud version. Empty when not installed.
std::vector<std::string> display_types; // Display-only "compatibility" labels (cloud: raw service labels; local: from real capabilities). Never used for dispatch.
std::string plugin_root; // Installed plugin directory, even when entry_path is invalid or ambiguous.
@@ -113,10 +113,6 @@ struct PluginDescriptor
bool is_unauthorized() const { return get_update_status() == PluginUpdateStatus::Unauthorized; }
std::string latest_available_version() const
{
for (const PluginChangelog& entry : changelog) {
if (!entry.version.empty())
return entry.version;
}
if (!latest_version.empty())
return latest_version;
return version;

View File

@@ -52,6 +52,29 @@ print('ok')
} // namespace
TEST_CASE("plugin latest version uses the authoritative catalog field", "[PluginDescriptor]")
{
PluginDescriptor descriptor;
descriptor.version = "1.3.0";
descriptor.latest_version = "1.3.0";
PluginChangelog changelog;
changelog.version = "1.2.0";
descriptor.changelog.push_back(changelog);
CHECK(descriptor.latest_available_version() == "1.3.0");
}
TEST_CASE("plugin latest version falls back to the descriptor version", "[PluginDescriptor]")
{
PluginDescriptor descriptor;
descriptor.version = "1.1.0";
PluginChangelog changelog;
changelog.version = "1.0.0";
descriptor.changelog.push_back(changelog);
CHECK(descriptor.latest_available_version() == "1.1.0");
}
// Regression: update_cloud_metadata() replaces a matched entry's descriptor wholesale with the
// cloud catalog record (`entry = cloud_entry`). Configuration used to ride on the descriptor, so
// that overwrite silently wiped it and plugins fell back to their built-in defaults (found via