diff --git a/resources/icons/printers/BarBaz_M1.png b/resources/icons/printers/BarBaz_M1.png deleted file mode 100644 index 5924cc88b..000000000 Binary files a/resources/icons/printers/BarBaz_M1.png and /dev/null differ diff --git a/resources/icons/printers/BarBaz_M2.png b/resources/icons/printers/BarBaz_M2.png deleted file mode 100644 index 5924cc88b..000000000 Binary files a/resources/icons/printers/BarBaz_M2.png and /dev/null differ diff --git a/resources/icons/printers/BarBaz_M3.png b/resources/icons/printers/BarBaz_M3.png deleted file mode 100644 index 5924cc88b..000000000 Binary files a/resources/icons/printers/BarBaz_M3.png and /dev/null differ diff --git a/resources/icons/printers/Foobar_M1.png b/resources/icons/printers/Foobar_M1.png deleted file mode 100644 index 61a76a63d..000000000 Binary files a/resources/icons/printers/Foobar_M1.png and /dev/null differ diff --git a/resources/icons/printers/Foobar_M2.png b/resources/icons/printers/Foobar_M2.png deleted file mode 100644 index 61a76a63d..000000000 Binary files a/resources/icons/printers/Foobar_M2.png and /dev/null differ diff --git a/resources/icons/printers/Foobar_M3.png b/resources/icons/printers/Foobar_M3.png deleted file mode 100644 index 61a76a63d..000000000 Binary files a/resources/icons/printers/Foobar_M3.png and /dev/null differ diff --git a/resources/icons/printers/PrusaResearch_MK2.5.png b/resources/icons/printers/PrusaResearch_MK2.5.png new file mode 100644 index 000000000..efbbc598d Binary files /dev/null and b/resources/icons/printers/PrusaResearch_MK2.5.png differ diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 5c68e3c7c..767c372e8 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,3 +1,4 @@ +0.1.6 Split the MK2.5 profile from the MK2S 0.1.5 fixed printer_variant fields for the i3 MK3 0.25 and 0.6mm nozzles 0.1.4 edited fw version, added z-raise after print 0.1.3 Fixed an incorrect position of the max_print_height parameter diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 5f1d654a3..fdeefb8c9 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the Slic3r configuration to be downgraded. -config_version = 0.1.5 +config_version = 0.1.6 # Where to get the updates from? config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/ @@ -19,7 +19,11 @@ name = Original Prusa i3 MK3 variants = 0.4; 0.25; 0.6 [printer_model:MK2S] -name = Original Prusa i3 MK2S, MK2.5 +name = Original Prusa i3 MK2S +variants = 0.4; 0.25; 0.6 + +[printer_model:MK2.5] +name = Original Prusa i3 MK2.5 variants = 0.4; 0.25; 0.6 [printer_model:MK2SMM] @@ -166,6 +170,10 @@ support_material_threshold = 80 support_material_with_sheath = 1 wipe_tower = 1 +# XXXXXXXXXXXXXXXXXXXX +# XXX--- 0.05mm ---XXX +# XXXXXXXXXXXXXXXXXXXX + [print:*0.05mm*] inherits = *common* bottom_solid_layers = 10 @@ -226,6 +234,10 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and fill_pattern = grid top_infill_extrusion_width = 0.4 +# XXXXXXXXXXXXXXXXXXXX +# XXX--- 0.10mm ---XXX +# XXXXXXXXXXXXXXXXXXXX + [print:*0.10mm*] inherits = *common* bottom_solid_layers = 7 @@ -299,6 +311,10 @@ solid_infill_speed = 200 top_infill_extrusion_width = 0.4 top_solid_infill_speed = 50 +# XXXXXXXXXXXXXXXXXXXX +# XXX--- 0.15mm ---XXX +# XXXXXXXXXXXXXXXXXXXX + [print:*0.15mm*] inherits = *common* bottom_solid_layers = 5 @@ -420,6 +436,10 @@ perimeter_speed = 45 solid_infill_speed = 200 top_solid_infill_speed = 50 +# XXXXXXXXXXXXXXXXXXXX +# XXX--- 0.20mm ---XXX +# XXXXXXXXXXXXXXXXXXXX + [print:0.20mm 100mms Linear Advance] inherits = *0.20mm* compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 @@ -483,6 +503,10 @@ perimeter_speed = 45 solid_infill_speed = 200 top_solid_infill_speed = 50 +# XXXXXXXXXXXXXXXXXXXX +# XXX--- 0.35mm ---XXX +# XXXXXXXXXXXXXXXXXXXX + [print:*0.35mm*] inherits = *common* bottom_solid_layers = 3 @@ -532,6 +556,10 @@ support_material_interface_layers = 2 support_material_with_sheath = 0 support_material_xy_spacing = 150% +# XXXXXXxxXXXXXXXXXXXXXX +# XXX--- filament ---XXX +# XXXXXXXXxxXXXXXXXXXXXX + [filament:*common*] cooling = 1 compatible_printers = @@ -958,6 +986,10 @@ variable_layer_height = 0 default_print_profile = 0.15mm OPTIMAL default_filament_profile = Prusa PLA +# XXXXXXXXXXXXXXXXX +# XXX--- MK2 ---XXX +# XXXXXXXXXXXXXXXXX + [printer:Original Prusa i3 MK2] inherits = *common* @@ -980,6 +1012,10 @@ nozzle_diameter = 0.6 printer_variant = 0.6 default_print_profile = 0.20mm NORMAL 0.6 nozzle +# XXXXXXXXXXXXXXXXXXX +# XXX--- MK2MM ---XXX +# XXXXXXXXXXXXXXXXXXX + [printer:Original Prusa i3 MK2 MM Single Mode] inherits = *mm-single* @@ -999,6 +1035,29 @@ nozzle_diameter = 0.6,0.6,0.6,0.6 printer_variant = 0.6 default_print_profile = 0.20mm NORMAL 0.6 nozzle +# XXXXXXXXXXXXXXXXXXX +# XXX--- MK2.5 ---XXX +# XXXXXXXXXXXXXXXXXXX + +[printer:Original Prusa i3 MK2.5] +inherits = Original Prusa i3 MK2 +printer_model = MK2.5 +start_gcode = M115 U3.2.1 ; tell printer latest fw version\nM201 X9000 Y9000 Z500 E10000 ; sets maximum accelerations, mm/sec^2\nM203 X500 Y500 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1500 T1500 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.2 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 + +[printer:Original Prusa i3 MK2.5 0.25 nozzle] +inherits = Original Prusa i3 MK2 0.25 nozzle +printer_model = MK2.5 +start_gcode = M115 U3.2.1 ; tell printer latest fw version\nM201 X9000 Y9000 Z500 E10000 ; sets maximum accelerations, mm/sec^2\nM203 X500 Y500 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1500 T1500 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.2 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 + +[printer:Original Prusa i3 MK2.5 0.6 nozzle] +inherits = Original Prusa i3 MK2 0.6 nozzle +printer_model = MK2.5 +start_gcode = M115 U3.2.1 ; tell printer latest fw version\nM201 X9000 Y9000 Z500 E10000 ; sets maximum accelerations, mm/sec^2\nM203 X500 Y500 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1500 T1500 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.2 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 + +# XXXXXXXXXXXXXXXXX +# XXX--- MK3 ---XXX +# XXXXXXXXXXXXXXXXX + [printer:Original Prusa i3 MK3] inherits = *common* end_gcode = G4 ; wait\nM221 S100\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp index 996959b2e..aed0c3534 100644 --- a/xs/src/slic3r/GUI/ConfigWizard.cpp +++ b/xs/src/slic3r/GUI/ConfigWizard.cpp @@ -59,7 +59,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, cons auto *sizer = new wxBoxSizer(wxVERTICAL); auto *printer_grid = new wxFlexGridSizer(models.size(), 0, 20); - printer_grid->SetFlexibleDirection(wxVERTICAL); + printer_grid->SetFlexibleDirection(wxVERTICAL | wxHORIZONTAL); sizer->Add(printer_grid); auto namefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); @@ -136,7 +136,7 @@ void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked) // Wizard page base ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname) : - wxPanel(parent), + wxPanel(parent->p->hscroll), parent(parent), shortname(std::move(shortname)), p_prev(nullptr), @@ -182,8 +182,8 @@ ConfigWizardPage* ConfigWizardPage::chain(ConfigWizardPage *page) void ConfigWizardPage::append_text(wxString text) { auto *widget = new wxStaticText(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - widget->Wrap(CONTENT_WIDTH); - widget->SetMinSize(wxSize(CONTENT_WIDTH, -1)); + widget->Wrap(WRAP_WIDTH); + widget->SetMinSize(wxSize(WRAP_WIDTH, -1)); append(widget); } @@ -285,7 +285,7 @@ PageUpdate::PageUpdate(ConfigWizard *parent) : const auto text_bold = _(L("Updates are never applied without user's consent and never overwrite user's customized settings.")); auto *label_bold = new wxStaticText(this, wxID_ANY, text_bold); label_bold->SetFont(boldfont); - label_bold->Wrap(CONTENT_WIDTH); + label_bold->Wrap(WRAP_WIDTH); append(label_bold); append_text(_(L("Additionally a backup snapshot of the whole configuration is created before an update is applied."))); @@ -622,7 +622,7 @@ void ConfigWizard::priv::load_vendors() catch (const std::exception& e) { BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % it->path() % e.what(); } - + } } @@ -668,7 +668,7 @@ void ConfigWizard::priv::index_refresh() void ConfigWizard::priv::add_page(ConfigWizardPage *page) { - topsizer->Add(page, 0, wxEXPAND); + hscroll_sizer->Add(page, 0, wxEXPAND); auto *extra_buttons = page->extra_buttons(); if (extra_buttons != nullptr) { @@ -795,12 +795,19 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) : p->index = new ConfigWizardIndex(this); auto *vsizer = new wxBoxSizer(wxVERTICAL); - p->topsizer = new wxBoxSizer(wxHORIZONTAL); + auto *topsizer = new wxBoxSizer(wxHORIZONTAL); auto *hline = new wxStaticLine(this); p->btnsizer = new wxBoxSizer(wxHORIZONTAL); - p->topsizer->Add(p->index, 0, wxEXPAND); - p->topsizer->AddSpacer(INDEX_MARGIN); + // Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling. + // Later, we compare that to the size of the current screen and set minimum width based on that (see below). + p->hscroll = new wxScrolledWindow(this); + p->hscroll_sizer = new wxBoxSizer(wxHORIZONTAL); + p->hscroll->SetSizer(p->hscroll_sizer); + + topsizer->Add(p->index, 0, wxEXPAND); + topsizer->AddSpacer(INDEX_MARGIN); + topsizer->Add(p->hscroll, 1, wxEXPAND); p->btn_prev = new wxButton(this, wxID_BACKWARD); p->btn_next = new wxButton(this, wxID_FORWARD); @@ -827,13 +834,25 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) : ->chain(p->page_diams) ->chain(p->page_temps); - vsizer->Add(p->topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN); + vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN); vsizer->Add(hline, 0, wxEXPAND); vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN); p->set_page(p->page_welcome); + SetSizer(vsizer); SetSizerAndFit(vsizer); - SetMinSize(GetSize()); + + // We can now enable scrolling on hscroll + p->hscroll->SetScrollRate(30, 30); + // Compare current ("ideal") wizard size with the size of the current screen. + // If the screen is smaller, resize wizrad to match, which will enable scrollbars. + auto wizard_size = GetSize(); + unsigned width, height; + GUI::get_current_screen_size(width, height); + wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN))); + wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN))); + SetMinSize(wizard_size); + Fit(); p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); }); p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_next(); }); diff --git a/xs/src/slic3r/GUI/ConfigWizard_private.hpp b/xs/src/slic3r/GUI/ConfigWizard_private.hpp index 474394bc3..72cb88655 100644 --- a/xs/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/xs/src/slic3r/GUI/ConfigWizard_private.hpp @@ -26,7 +26,7 @@ namespace Slic3r { namespace GUI { enum { - CONTENT_WIDTH = 500, + WRAP_WIDTH = 500, DIALOG_MARGIN = 15, INDEX_MARGIN = 40, @@ -196,7 +196,8 @@ struct ConfigWizard::priv std::unordered_map vendors_rsrc; std::unique_ptr custom_config; - wxBoxSizer *topsizer = nullptr; + wxScrolledWindow *hscroll = nullptr; + wxBoxSizer *hscroll_sizer = nullptr; wxBoxSizer *btnsizer = nullptr; ConfigWizardPage *page_current = nullptr; ConfigWizardIndex *index = nullptr; diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp index 64f3426c4..25925e3b6 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/xs/src/slic3r/GUI/Field.cpp @@ -199,7 +199,25 @@ namespace Slic3r { namespace GUI { }), temp->GetId()); #endif // __WXGTK__ - temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent) { on_change_field(); }), temp->GetId()); + temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent) + { +#ifdef __WXGTK__ + bool bChangedValueEvent = true; +#else + on_change_field(); +#endif //__WXGTK__ + }), temp->GetId()); + +#ifdef __WXGTK__ + temp->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) + { + if (bChangedValueEvent) { + on_change_field(); + bChangedValueEvent = false; + } + event.Skip(); + }); +#endif //__WXGTK__ // select all text using Ctrl+A temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event) diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp index 7e421244b..948178d3e 100644 --- a/xs/src/slic3r/GUI/Field.hpp +++ b/xs/src/slic3r/GUI/Field.hpp @@ -220,6 +220,9 @@ inline bool is_sizer_field(const t_field& obj) { return !is_bad_field(obj) && ob class TextCtrl : public Field { using Field::Field; +#ifdef __WXGTK__ + bool bChangedValueEvent = false; +#endif //__WXGTK__ public: TextCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} TextCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 3a8f2163f..974c554b6 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include "wxExtensions.hpp" @@ -933,6 +934,14 @@ int get_export_option(wxFileDialog* dlg) } +void get_current_screen_size(unsigned &width, unsigned &height) +{ + wxDisplay display(wxDisplay::GetFromWindow(g_wxMainFrame)); + const auto disp_size = display.GetClientArea(); + width = disp_size.GetWidth(); + height = disp_size.GetHeight(); +} + void about() { AboutDialog dlg; diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 2dc18ae3b..285354446 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -169,6 +169,9 @@ wxButton* get_wiping_dialog_button(); void add_export_option(wxFileDialog* dlg, const std::string& format); int get_export_option(wxFileDialog* dlg); +// Returns the dimensions of the screen on which the main frame is displayed +void get_current_screen_size(unsigned &width, unsigned &height); + // Display an About dialog extern void about(); // Ask the destop to open the datadir using the default file explorer. diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index f24b5ebc6..6eabc2f47 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -2056,7 +2056,15 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr void Tab::OnTreeSelChange(wxTreeEvent& event) { if (m_disable_tree_sel_changed_event) return; + +// There is a bug related to Ubuntu overlay scrollbars, see https://github.com/prusa3d/Slic3r/issues/898 and https://github.com/prusa3d/Slic3r/issues/952. +// The issue apparently manifests when Show()ing a window with overlay scrollbars while the UI is frozen. For this reason, +// we will Thaw the UI prematurely on Linux. This means destroing the no_updates object prematurely. +#ifdef __linux__ + std::unique_ptr no_updates(new wxWindowUpdateLocker(this)); +#else wxWindowUpdateLocker noUpdates(this); +#endif Page* page = nullptr; auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); @@ -2072,6 +2080,11 @@ void Tab::OnTreeSelChange(wxTreeEvent& event) for (auto& el : m_pages) el.get()->Hide(); + +#ifdef __linux__ + no_updates.reset(nullptr); +#endif + page->Show(); m_hsizer->Layout(); Refresh(); diff --git a/xs/src/slic3r/Utils/PresetUpdater.cpp b/xs/src/slic3r/Utils/PresetUpdater.cpp index dd46c43fe..f34fc4c19 100644 --- a/xs/src/slic3r/Utils/PresetUpdater.cpp +++ b/xs/src/slic3r/Utils/PresetUpdater.cpp @@ -116,6 +116,8 @@ struct PresetUpdater::priv void check_install_indices() const; Updates get_config_updates() const; void perform_updates(Updates &&updates, bool snapshot = true) const; + + static void copy_file(const fs::path &from, const fs::path &to); }; PresetUpdater::priv::priv(int version_online_event) : @@ -285,7 +287,7 @@ void PresetUpdater::priv::check_install_indices() const if (! fs::exists(path_in_cache)) { BOOST_LOG_TRIVIAL(info) << "Install index from resources: " << path.filename(); - fs::copy_file(path, path_in_cache, fs::copy_option::overwrite_if_exists); + copy_file(path, path_in_cache); } else { Index idx_rsrc, idx_cache; idx_rsrc.load(path); @@ -293,7 +295,7 @@ void PresetUpdater::priv::check_install_indices() const if (idx_cache.version() < idx_rsrc.version()) { BOOST_LOG_TRIVIAL(info) << "Update index from resources: " << path.filename(); - fs::copy_file(path, path_in_cache, fs::copy_option::overwrite_if_exists); + copy_file(path, path_in_cache); } } } @@ -397,7 +399,7 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons for (const auto &update : updates.updates) { BOOST_LOG_TRIVIAL(info) << '\t' << update; - fs::copy_file(update.source, update.target, fs::copy_option::overwrite_if_exists); + copy_file(update.source, update.target); PresetBundle bundle; bundle.load_configbundle(update.target.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM); @@ -433,6 +435,18 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons } } +void PresetUpdater::priv::copy_file(const fs::path &source, const fs::path &target) +{ + static const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read; // aka 644 + + // Make sure the file has correct permission both before and after we copy over it + if (fs::exists(target)) { + fs::permissions(target, perms); + } + fs::copy_file(source, target, fs::copy_option::overwrite_if_exists); + fs::permissions(target, perms); +} + PresetUpdater::PresetUpdater(int version_online_event) : p(new priv(version_online_event))