diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index c295d6def..c9fbe90ac 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -604,23 +604,31 @@ bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra) value = argv[++ i]; } // Store the option value. - const bool existing = this->has(opt_key); - if (ConfigOptionBool* opt = this->opt(opt_key, true)) { - opt->value = !no; - } else if (ConfigOptionBools* opt = this->opt(opt_key, true)) { - if (!existing) opt->values.clear(); // remove the default values - opt->values.push_back(!no); - } else if (ConfigOptionStrings* opt = this->opt(opt_key, true)) { - if (!existing) opt->values.clear(); // remove the default values - opt->deserialize(value, true); - } else if (ConfigOptionFloats* opt = this->opt(opt_key, true)) { - if (!existing) opt->values.clear(); // remove the default values - opt->deserialize(value, true); - } else if (ConfigOptionPoints* opt = this->opt(opt_key, true)) { - if (!existing) opt->values.clear(); // remove the default values - opt->deserialize(value, true); + const bool existing = this->has(opt_key); + ConfigOption *opt_base = this->option(opt_key, true); + ConfigOptionVectorBase *opt_vector = opt_base->is_vector() ? static_cast(opt_base) : nullptr; + if (opt_vector) { + // Vector values will be chained. Repeated use of a parameter will append the parameter or parameters + // to the end of the value. + if (!existing) + // remove the default values + opt_vector->deserialize("", true); + if (opt_base->type() == coBools) + static_cast(opt_base)->values.push_back(!no); + else + // Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way + // they get deserialized from an .ini file. For ConfigOptionStrings, that means that the C-style unescape + // will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be + // unescaped by the calling shell. + opt_vector->deserialize(value, true); + } else if (opt_base->type() == coBool) { + static_cast(opt_base)->value = !no; + } else if (opt_base->type() == coString) { + // Do not unescape single string values, the unescaping is left to the calling shell. + static_cast(opt_base)->value = value; } else { - this->set_deserialize(opt_key, value, true); + // Any scalar value of a type different from Bool and String. + this->set_deserialize(opt_key, value, false); } } return true; diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index d92667362..d456fb9c6 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -103,7 +103,7 @@ public: Point& operator+=(const Point& rhs) { (*this)(0) += rhs(0); (*this)(1) += rhs(1); return *this; } Point& operator-=(const Point& rhs) { (*this)(0) -= rhs(0); (*this)(1) -= rhs(1); return *this; } - Point& operator*=(const double &rhs) { (*this)(0) *= rhs; (*this)(1) *= rhs; return *this; } + Point& operator*=(const double &rhs) { (*this)(0) = coord_t((*this)(0) * rhs); (*this)(1) = coord_t((*this)(1) * rhs); return *this; } void rotate(double angle); void rotate(double angle, const Point ¢er); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8713ed03f..4e12114df 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1164,12 +1164,11 @@ public: class DynamicPrintAndCLIConfig : public DynamicPrintConfig { public: - DynamicPrintAndCLIConfig() { this->apply(FullPrintConfig::defaults()); this->apply(CLIConfig()); } - DynamicPrintAndCLIConfig(const DynamicPrintAndCLIConfig &other) : DynamicPrintConfig(other) {} + DynamicPrintAndCLIConfig() {} + DynamicPrintAndCLIConfig(const DynamicPrintAndCLIConfig &other) : DynamicPrintConfig(other) {} // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. const ConfigDef* def() const override { return &s_def; } - t_config_option_keys keys() const override { return s_def.keys(); } // Verify whether the opt_key has not been obsoleted or renamed. // Both opt_key and value may be modified by handle_legacy(). diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 6cd16889b..d242df168 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -59,10 +59,10 @@ int main(int argc, char **argv) } // parse all command line options into a DynamicConfig - DynamicPrintAndCLIConfig config; + DynamicPrintAndCLIConfig all_config; t_config_option_keys input_files; // if any option is unsupported, print usage and abort immediately - if (! config.read_cli(argc, argv, &input_files)) { + if (! all_config.read_cli(argc, argv, &input_files)) { printUsage(); return 0; } @@ -96,21 +96,15 @@ int main(int argc, char **argv) // apply command line options to a more handy CLIConfig CLIConfig cli_config; - cli_config.apply(config, true); + cli_config.apply(all_config, true); set_data_dir(cli_config.datadir.value); - DynamicPrintConfig print_config; + // Load the extra config values. + DynamicPrintConfig extra_config; + extra_config.apply(all_config, true); - if ((argc == 1 || cli_config.gui.value) && ! cli_config.no_gui.value && ! cli_config.help.value && cli_config.save.value.empty()) { -#if 1 - GUI::GUI_App *gui = new GUI::GUI_App(); - GUI::GUI_App::SetInstance(gui); - wxEntry(argc, argv); -#else - std::cout << "GUI support has not been built." << "\n"; -#endif - } // load config files supplied via --load + DynamicPrintConfig print_config; for (const std::string &file : cli_config.load.values) { if (! boost::filesystem::exists(file)) { boost::nowide::cout << "No such file: " << file << std::endl; @@ -126,10 +120,40 @@ int main(int argc, char **argv) c.normalize(); print_config.apply(c); } - + + if ((argc == 1 || cli_config.gui.value) && ! cli_config.no_gui.value && ! cli_config.help.value && cli_config.save.value.empty()) { +#if 1 + GUI::GUI_App *gui = new GUI::GUI_App(); + GUI::GUI_App::SetInstance(gui); + gui->CallAfter([gui, &input_files, &cli_config, &extra_config, &print_config] { +#if 0 + // Load the cummulative config over the currently active profiles. + //FIXME if multiple configs are loaded, only the last one will have an effect. + // We need to decide what to do about loading of separate presets (just print preset, just filament preset etc). + // As of now only the full configs are supported here. + if (! print_config.empty()) + gui->mainframe->load_config(print_config); +#endif + if (! cli_config.load.values.empty()) + // Load the last config to give it a name at the UI. The name of the preset may be later + // changed by loading an AMF or 3MF. + //FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config. + gui->mainframe->load_config_file(cli_config.load.values.back()); + // If loading a 3MF file, the config is loaded from the last one. + gui->plater()->load_files(input_files, true, true); + if (! extra_config.empty()) + gui->mainframe->load_config(extra_config); + }); + return wxEntry(argc, argv); +#else + std::cout << "GUI support has not been built." << "\n"; + return -1; +#endif + } + // apply command line options to a more specific DynamicPrintConfig which provides normalize() // (command line options override --load files) - print_config.apply(config, true); + print_config.apply(extra_config, true); // write config if requested if (! cli_config.save.value.empty()) { diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 7eff483cf..a721d1e18 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include "libslic3r/Print.hpp" #include "libslic3r/Polygon.hpp" @@ -635,28 +637,34 @@ void MainFrame::export_config() } // Load a config file containing a Print, Filament & Printer preset. -void MainFrame::load_config_file(wxString file/* = wxEmptyString*/) +void MainFrame::load_config_file() { - if (file.IsEmpty()) { - if (!wxGetApp().check_unsaved_changes()) - return; - auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")), - !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), - "config.ini", "INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g", wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (dlg->ShowModal() != wxID_OK) - return; - file = dlg->GetPath(); - dlg->Destroy(); + if (!wxGetApp().check_unsaved_changes()) + return; + auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")), + !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), + "config.ini", "INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g", wxFD_OPEN | wxFD_FILE_MUST_EXIST); + wxString file; + if (dlg->ShowModal() == wxID_OK) + file = dlg->GetPath(); + dlg->Destroy(); + if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) { + wxGetApp().app_config->update_config_dir(get_dir_name(file)); + m_last_config = file; } +} + +// Load a config file containing a Print, Filament & Printer preset from command line. +bool MainFrame::load_config_file(const std::string &path) +{ try { - wxGetApp().preset_bundle->load_config_file(file.ToUTF8().data()); + wxGetApp().preset_bundle->load_config_file(path); } catch (const std::exception &ex) { show_error(this, ex.what()); - return; + return false; } - wxGetApp().load_current_presets(); - wxGetApp().app_config->update_config_dir(get_dir_name(file)); - m_last_config = file; + wxGetApp().load_current_presets(); + return true; } void MainFrame::export_configbundle() @@ -700,11 +708,13 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), "config.ini", file_wildcards(FT_INI), wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (dlg->ShowModal() != wxID_OK) - return; + if (dlg->ShowModal() != wxID_OK) { + dlg->Destroy(); + return; + } file = dlg->GetPath(); - dlg->Destroy(); - } + dlg->Destroy(); + } wxGetApp().app_config->update_config_dir(get_dir_name(file)); @@ -727,10 +737,37 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re // Also update the platter with the new presets. void MainFrame::load_config(const DynamicPrintConfig& config) { - for (auto tab : wxGetApp().tabs_list) - tab->load_config(config); - if (m_plater) + PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); + const auto *opt_printer_technology = config.option>("printer_technology"); + if (opt_printer_technology != nullptr && opt_printer_technology->value != printer_technology) { + printer_technology = opt_printer_technology->value; + this->plater()->set_printer_technology(printer_technology); + } +#if 0 + for (auto tab : wxGetApp().tabs_list) + if (tab->supports_printer_technology(printer_technology)) { + if (tab->name() == "printer") + static_cast(tab)->update_pages(); + tab->load_config(config); + } + if (m_plater) m_plater->on_config_change(config); +#else + // Load the currently selected preset into the GUI, update the preset selection box. + //FIXME this is not quite safe for multi-extruder printers, + // as the number of extruders is not adjusted for the vector values. + // (see PresetBundle::update_multi_material_filament_presets()) + // Better to call PresetBundle::load_config() instead? + for (auto tab : wxGetApp().tabs_list) + if (tab->supports_printer_technology(printer_technology)) { + // Only apply keys, which are present in the tab's config. Ignore the other keys. + for (const std::string &opt_key : tab->get_config()->diff(config)) + // Ignore print_settings_id, printer_settings_id, filament_settings_id etc. + if (! boost::algorithm::ends_with(opt_key, "_settings_id")) + tab->get_config()->option(opt_key)->set(config.option(opt_key)); + } + wxGetApp().load_current_presets(); +#endif } void MainFrame::select_tab(size_t tab) const @@ -806,14 +843,14 @@ void MainFrame::update_ui_from_settings() tab->update_ui_from_settings(); } -std::string MainFrame::get_base_name(const wxString full_name) const +std::string MainFrame::get_base_name(const wxString &full_name) const { - return boost::filesystem::path(full_name).filename().string(); + return boost::filesystem::path(full_name.wx_str()).filename().string(); } -std::string MainFrame::get_dir_name(const wxString full_name) const +std::string MainFrame::get_dir_name(const wxString &full_name) const { - return boost::filesystem::path(full_name).parent_path().string(); + return boost::filesystem::path(full_name.wx_str()).parent_path().string(); } diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index e0411b6da..954906903 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -53,8 +53,8 @@ class MainFrame : public wxFrame PrintHostQueueDialog *m_printhost_queue_dlg; - std::string get_base_name(const wxString full_name) const ; - std::string get_dir_name(const wxString full_name) const ; + std::string get_base_name(const wxString &full_name) const; + std::string get_dir_name(const wxString &full_name) const; void on_presets_changed(SimpleEvent&); void on_value_changed(wxCommandEvent&); @@ -86,7 +86,10 @@ public: void reslice_now(); void repair_stl(); void export_config(); - void load_config_file(wxString file = wxEmptyString); + // Query user for the config file and open it. + void load_config_file(); + // Open a config file. Return true if loaded. + bool load_config_file(const std::string &path); void export_configbundle(); void load_configbundle(wxString file = wxEmptyString); void load_config(const DynamicPrintConfig& config); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 941a181d3..9fb87a197 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2594,6 +2594,16 @@ void Plater::extract_config_from_project() void Plater::load_files(const std::vector& input_files, bool load_model, bool load_config) { p->load_files(input_files, load_model, load_config); } +// To be called when providing a list of files to the GUI slic3r on command line. +void Plater::load_files(const std::vector& input_files, bool load_model, bool load_config) +{ + std::vector paths; + paths.reserve(input_files.size()); + for (const std::string &path : input_files) + paths.emplace_back(path); + p->load_files(paths, load_model, load_config); +} + void Plater::update() { p->update(); } void Plater::update_ui_from_settings() { p->update_ui_from_settings(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index f433d655d..11a4f0453 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -116,6 +116,8 @@ public: void extract_config_from_project(); void load_files(const std::vector& input_files, bool load_model = true, bool load_config = true); + // To be called when providing a list of files to the GUI slic3r on command line. + void load_files(const std::vector& input_files, bool load_model = true, bool load_config = true); void update(); void select_view(const std::string& direction); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 162e62155..4601d05d3 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2283,7 +2283,7 @@ void TabPrinter::update_sla() // Initialize the UI from the current preset void Tab::load_current_preset() { - auto preset = m_presets->get_edited_preset(); + const Preset& preset = m_presets->get_edited_preset(); (preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true); @@ -2302,11 +2302,14 @@ void Tab::load_current_preset() m_undo_to_sys_btn->Enable(!preset.is_default); +#if 0 // use CallAfter because some field triggers schedule on_change calls using CallAfter, // and we don't want them to be called after this update_dirty() as they would mark the // preset dirty again // (not sure this is true anymore now that update_dirty is idempotent) - wxTheApp->CallAfter([this]{ + wxTheApp->CallAfter([this] +#endif + { // checking out if this Tab exists till this moment if (!wxGetApp().checked_tab(this)) return; @@ -2354,7 +2357,10 @@ void Tab::load_current_preset() init_options_list(); update_visibility(); update_changed_ui(); - }); + } +#if 0 + ); +#endif } //Regerenerate content of the page tree. @@ -2428,7 +2434,6 @@ void Tab::select_preset(std::string preset_name) bool print_tab = m_presets->type() == Preset::TYPE_PRINT || m_presets->type() == Preset::TYPE_SLA_PRINT; bool printer_tab = m_presets->type() == Preset::TYPE_PRINTER; bool canceled = false; -// m_reload_dependent_tabs = {}; m_dependent_tabs = {}; if (current_dirty && !may_discard_current_dirty_preset()) { canceled = true; diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 4263696d1..b3288a80d 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -185,7 +185,6 @@ protected: bool m_disable_tree_sel_changed_event; bool m_show_incompatible_presets; - std::vector m_reload_dependent_tabs = {}; std::vector m_dependent_tabs = {}; enum OptStatus { osSystemValue = 1, osInitValue = 2 }; std::map m_options_list; @@ -265,7 +264,6 @@ public: DynamicPrintConfig* get_config() { return m_config; } PresetCollection* get_presets() { return m_presets; } - std::vector get_dependent_tabs() { return m_reload_dependent_tabs; } size_t get_selected_preset_item() { return m_selected_preset_item; } void on_value_change(const std::string& opt_key, const boost::any& value); diff --git a/src/slic3r_app_msvc.cpp b/src/slic3r_app_msvc.cpp index 7cd97dfc3..ff5ad6cfa 100644 --- a/src/slic3r_app_msvc.cpp +++ b/src/slic3r_app_msvc.cpp @@ -21,6 +21,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; #include #include +#include + class OpenGLVersionCheck { public: @@ -44,8 +46,8 @@ public: if (RegisterClass(&wc)) { HWND hwnd = CreateWindowW(wc.lpszClassName, L"slic3r_opengl_version_check", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, 0, 0, wc.hInstance, (LPVOID)this); if (hwnd) { - this->message_pump_exit = false; - while (GetMessage(&msg, NULL, 0, 0 ) > 0 && ! this->message_pump_exit) + message_pump_exit = false; + while (GetMessage(&msg, NULL, 0, 0 ) > 0 && ! message_pump_exit) DispatchMessage(&msg); } } @@ -55,13 +57,18 @@ public: void unload_opengl_dll() { if (this->hOpenGL) { - FreeLibrary(this->hOpenGL); + BOOL released = FreeLibrary(this->hOpenGL); + if (released) + printf("System OpenGL library released\n"); + else + printf("System OpenGL library NOT released\n"); this->hOpenGL = nullptr; } } bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { + // printf("is_version_greater_or_equal_to, version: %s\n", version.c_str()); std::vector tokens; boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on); if (tokens.empty()) @@ -76,6 +83,7 @@ public: gl_major = ::atoi(numbers[0].c_str()); if (numbers.size() > 1) gl_minor = ::atoi(numbers[1].c_str()); + // printf("Major: %d, minor: %d\n", gl_major, gl_minor); if (gl_major < major) return false; else if (gl_major > major) @@ -85,7 +93,7 @@ public: } protected: - bool message_pump_exit = false; + static bool message_pump_exit; void check(HWND hWnd) { @@ -131,17 +139,18 @@ protected: }; HDC ourWindowHandleToDeviceContext = ::GetDC(hWnd); - // Gdi32.dll + // Gdi32.dll int letWindowsChooseThisPixelFormat = ::ChoosePixelFormat(ourWindowHandleToDeviceContext, &pfd); - // Gdi32.dll - SetPixelFormat(ourWindowHandleToDeviceContext,letWindowsChooseThisPixelFormat, &pfd); - // Opengl32.dll + // Gdi32.dll + SetPixelFormat(ourWindowHandleToDeviceContext, letWindowsChooseThisPixelFormat, &pfd); + // Opengl32.dll HGLRC glcontext = wglCreateContext(ourWindowHandleToDeviceContext); wglMakeCurrent(ourWindowHandleToDeviceContext, glcontext); // Opengl32.dll const char *data = (const char*)glGetString(GL_VERSION); if (data != nullptr) this->version = data; + // printf("check -version: %s\n", version.c_str()); data = (const char*)glGetString(0x8B8C); // GL_SHADING_LANGUAGE_VERSION if (data != nullptr) this->glsl_version = data; @@ -153,6 +162,7 @@ protected: this->renderer = data; // Opengl32.dll wglDeleteContext(glcontext); + ::ReleaseDC(hWnd, ourWindowHandleToDeviceContext); this->success = true; } @@ -166,15 +176,19 @@ protected: OpenGLVersionCheck *ogl_data = reinterpret_cast(pCreate->lpCreateParams); ogl_data->check(hWnd); DestroyWindow(hWnd); - ogl_data->message_pump_exit = true; return 0; } + case WM_NCDESTROY: + message_pump_exit = true; + return 0; default: return DefWindowProc(hWnd, message, wParam, lParam); } } }; +bool OpenGLVersionCheck::message_pump_exit = false; + extern "C" { typedef int (__stdcall *Slic3rMainFunc)(int argc, wchar_t **argv); Slic3rMainFunc slic3r_main = nullptr; @@ -190,6 +204,16 @@ int wmain(int argc, wchar_t **argv) { #endif + std::vector argv_extended; + argv_extended.emplace_back(argv[0]); +#ifdef SLIC3R_WRAPPER_GUI + std::wstring cmd_gui = L"--gui"; + argv_extended.emplace_back(const_cast(cmd_gui.data())); +#endif + for (int i = 1; i < argc; ++i) + argv_extended.emplace_back(argv[i]); + argv_extended.emplace_back(nullptr); + OpenGLVersionCheck opengl_version_check; bool load_mesa = ! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0); @@ -213,7 +237,8 @@ int wmain(int argc, wchar_t **argv) HINSTANCE hInstance_OpenGL = LoadLibraryExW(path_to_mesa, nullptr, 0); if (hInstance_OpenGL == nullptr) { printf("MESA OpenGL library was not loaded\n"); - } + } else + printf("MESA OpenGL library was loaded sucessfully\n"); } wchar_t path_to_slic3r[MAX_PATH + 1] = { 0 }; @@ -239,15 +264,6 @@ int wmain(int argc, wchar_t **argv) printf("could not locate the function slic3r_main in slic3r.dll\n"); return -1; } - - std::vector argv_extended; - argv_extended.emplace_back(argv[0]); -#ifdef SLIC3R_WRAPPER_GUI - std::wstring cmd_gui = L"--gui"; - argv_extended.emplace_back(const_cast(cmd_gui.data())); -#endif - for (int i = 1; i < argc; ++ i) - argv_extended.emplace_back(argv[i]); - argv_extended.emplace_back(nullptr); - return slic3r_main(argc, argv_extended.data()); + // argc minus the trailing nullptr of the argv + return slic3r_main(argv_extended.size() - 1, argv_extended.data()); }