From 5b7fdd0d6d2036d58fb157e12c94f02cfdcbff54 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Oct 2023 11:51:10 +0200 Subject: [PATCH 1/6] fixing arrange for sequential print SPE-1866 Fixing problem with incorrect arrange settings for sequential mode On first start of the app when the arrange dialog wasn't open yet, see also Remove leftover debug code Using a StaticMap in ArrangeSettingsView --- src/libslic3r/Arrange/ArrangeImpl.hpp | 2 +- .../Arrange/ArrangeSettingsDb_AppCfg.cpp | 74 ++++++------ .../Arrange/ArrangeSettingsDb_AppCfg.hpp | 2 + src/libslic3r/Arrange/ArrangeSettingsView.hpp | 105 ++++++++++++++++++ src/slic3r/GUI/GLCanvas3D.cpp | 30 +++-- 5 files changed, 157 insertions(+), 56 deletions(-) diff --git a/src/libslic3r/Arrange/ArrangeImpl.hpp b/src/libslic3r/Arrange/ArrangeImpl.hpp index 92786a41fb..d3cda35e3e 100644 --- a/src/libslic3r/Arrange/ArrangeImpl.hpp +++ b/src/libslic3r/Arrange/ArrangeImpl.hpp @@ -444,7 +444,7 @@ ArrItem AdvancedItemConverter::get_arritem(const Arrangeable &arrbl, auto simpl_tol = static_cast(this->simplification_tolerance()); - if (simpl_tol > 0) + if (simpl_tol > 0.) { outline = expolygons_simplify(outline, simpl_tol); if (!envelope.empty()) diff --git a/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.cpp b/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.cpp index 086ea957d7..fcd8e209c6 100644 --- a/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.cpp +++ b/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.cpp @@ -7,6 +7,11 @@ namespace Slic3r { ArrangeSettingsDb_AppCfg::ArrangeSettingsDb_AppCfg(AppConfig *appcfg) : m_appcfg{appcfg} +{ + sync(); +} + +void ArrangeSettingsDb_AppCfg::sync() { m_settings_fff.postfix = "_fff"; m_settings_fff_seq.postfix = "_fff_seq_print"; @@ -39,16 +44,6 @@ ArrangeSettingsDb_AppCfg::ArrangeSettingsDb_AppCfg(AppConfig *appcfg) : m_appcfg std::string en_rot_sla_str = m_appcfg->get("arrange", "enable_rotation_sla"); - // std::string alignment_fff_str = - // m_appcfg->get("arrange", "alignment_fff"); - - // std::string alignment_fff_seqp_str = - // m_appcfg->get("arrange", "alignment_fff_seq_pring"); - - // std::string alignment_sla_str = - // m_appcfg->get("arrange", "alignment_sla"); - - // Override default alignment and save save/load it to a temporary slot "alignment_xl" std::string alignment_xl_str = m_appcfg->get("arrange", "alignment_xl"); @@ -60,71 +55,64 @@ ArrangeSettingsDb_AppCfg::ArrangeSettingsDb_AppCfg(AppConfig *appcfg) : m_appcfg if (!dist_fff_str.empty()) m_settings_fff.vals.d_obj = string_to_float_decimal_point(dist_fff_str); + else + m_settings_fff.vals.d_obj = m_settings_fff.defaults.d_obj; if (!dist_bed_fff_str.empty()) m_settings_fff.vals.d_bed = string_to_float_decimal_point(dist_bed_fff_str); + else + m_settings_fff.vals.d_bed = m_settings_fff.defaults.d_bed; if (!dist_fff_seq_print_str.empty()) m_settings_fff_seq.vals.d_obj = string_to_float_decimal_point(dist_fff_seq_print_str); + else + m_settings_fff_seq.vals.d_obj = m_settings_fff_seq.defaults.d_obj; if (!dist_bed_fff_seq_print_str.empty()) m_settings_fff_seq.vals.d_bed = string_to_float_decimal_point(dist_bed_fff_seq_print_str); + else + m_settings_fff_seq.vals.d_bed = m_settings_fff_seq.defaults.d_bed; if (!dist_sla_str.empty()) m_settings_sla.vals.d_obj = string_to_float_decimal_point(dist_sla_str); + else + m_settings_sla.vals.d_obj = m_settings_sla.defaults.d_obj; if (!dist_bed_sla_str.empty()) m_settings_sla.vals.d_bed = string_to_float_decimal_point(dist_bed_sla_str); + else + m_settings_sla.vals.d_bed = m_settings_sla.defaults.d_bed; if (!en_rot_fff_str.empty()) m_settings_fff.vals.rotations = (en_rot_fff_str == "1" || en_rot_fff_str == "yes"); if (!en_rot_fff_seqp_str.empty()) m_settings_fff_seq.vals.rotations = (en_rot_fff_seqp_str == "1" || en_rot_fff_seqp_str == "yes"); + else + m_settings_fff_seq.vals.rotations = m_settings_fff_seq.defaults.rotations; if (!en_rot_sla_str.empty()) m_settings_sla.vals.rotations = (en_rot_sla_str == "1" || en_rot_sla_str == "yes"); + else + m_settings_sla.vals.rotations = m_settings_sla.defaults.rotations; - // if (!alignment_sla_str.empty()) - // m_arrange_settings_sla.alignment = std::stoi(alignment_sla_str); - - // if (!alignment_fff_str.empty()) - // m_arrange_settings_fff.alignment = std::stoi(alignment_fff_str); - - // if (!alignment_fff_seqp_str.empty()) - // m_arrange_settings_fff_seq_print.alignment = std::stoi(alignment_fff_seqp_str); - - // Override default alignment and save save/load it to a temporary slot "alignment_xl" - ArrangeSettingsView::XLPivots arr_alignment = ArrangeSettingsView::xlpFrontLeft; - if (!alignment_xl_str.empty()) { - int align_val = std::stoi(alignment_xl_str); - - if (align_val >= 0 && align_val < ArrangeSettingsView::xlpCount) - arr_alignment = - static_cast(align_val); - } + // Override default alignment and save/load it to a temporary slot "alignment_xl" + auto arr_alignment = ArrangeSettingsView::to_xl_pivots(alignment_xl_str) + .value_or(m_settings_fff.defaults.xl_align); m_settings_sla.vals.xl_align = arr_alignment ; m_settings_fff.vals.xl_align = arr_alignment ; m_settings_fff_seq.vals.xl_align = arr_alignment ; - ArrangeSettingsView::GeometryHandling geom_handl = arr2::ArrangeSettingsView::ghConvex; - if (!geom_handling_str.empty()) { - int gh = std::stoi(geom_handling_str); - if(gh >= 0 && gh < ArrangeSettingsView::GeometryHandling::ghCount) - geom_handl = static_cast(gh); - } + auto geom_handl = ArrangeSettingsView::to_geometry_handling(geom_handling_str) + .value_or(m_settings_fff.defaults.geom_handling); m_settings_sla.vals.geom_handling = geom_handl; m_settings_fff.vals.geom_handling = geom_handl; m_settings_fff_seq.vals.geom_handling = geom_handl; - ArrangeSettingsView::ArrangeStrategy arr_strategy = arr2::ArrangeSettingsView::asAuto; - if (!strategy_str.empty()) { - int strateg = std::stoi(strategy_str); - if(strateg >= 0 && strateg < ArrangeSettingsView::ArrangeStrategy::asCount) - arr_strategy = static_cast(strateg); - } + auto arr_strategy = ArrangeSettingsView::to_arrange_strategy(strategy_str) + .value_or(m_settings_fff.defaults.arr_strategy); m_settings_sla.vals.arr_strategy = arr_strategy; m_settings_fff.vals.arr_strategy = arr_strategy; @@ -177,7 +165,7 @@ arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_rotation_enabled(bool v) arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_xl_alignment(XLPivots v) { m_settings_fff.vals.xl_align = v; - m_appcfg->set("arrange", "alignment_xl", std::to_string(v)); + m_appcfg->set("arrange", "alignment_xl", std::string{get_label(v)}); return *this; } @@ -185,7 +173,7 @@ arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_xl_alignment(XLPivots v) arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_geometry_handling(GeometryHandling v) { m_settings_fff.vals.geom_handling = v; - m_appcfg->set("arrange", "geometry_handling", std::to_string(v)); + m_appcfg->set("arrange", "geometry_handling", std::string{get_label(v)}); return *this; } @@ -193,7 +181,7 @@ arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_geometry_handling(Geometr arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_arrange_strategy(ArrangeStrategy v) { m_settings_fff.vals.arr_strategy = v; - m_appcfg->set("arrange", "arrange_strategy", std::to_string(v)); + m_appcfg->set("arrange", "arrange_strategy", std::string{get_label(v)}); return *this; } diff --git a/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.hpp b/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.hpp index 741c079342..dec64fa0d5 100644 --- a/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.hpp +++ b/src/libslic3r/Arrange/ArrangeSettingsDb_AppCfg.hpp @@ -55,6 +55,8 @@ private: public: explicit ArrangeSettingsDb_AppCfg(AppConfig *appcfg); + void sync(); + float get_distance_from_objects() const override { return get_ref(this).d_obj; } float get_distance_from_bed() const override { return get_ref(this).d_bed; } bool is_rotation_enabled() const override { return get_ref(this).rotations; } diff --git a/src/libslic3r/Arrange/ArrangeSettingsView.hpp b/src/libslic3r/Arrange/ArrangeSettingsView.hpp index 2bdecfcfc9..e55b769415 100644 --- a/src/libslic3r/Arrange/ArrangeSettingsView.hpp +++ b/src/libslic3r/Arrange/ArrangeSettingsView.hpp @@ -5,8 +5,15 @@ #ifndef ARRANGESETTINGSVIEW_HPP #define ARRANGESETTINGSVIEW_HPP +#include +#include + +#include "libslic3r/StaticMap.hpp" + namespace Slic3r { namespace arr2 { +using namespace std::string_view_literals; + class ArrangeSettingsView { public: @@ -31,6 +38,104 @@ public: virtual XLPivots get_xl_alignment() const = 0; virtual GeometryHandling get_geometry_handling() const = 0; virtual ArrangeStrategy get_arrange_strategy() const = 0; + + static constexpr std::string_view get_label(GeometryHandling v) + { + constexpr auto STR = std::array{ + "convex"sv, "balanced"sv, "advanced"sv, "undefined"sv + }; + + return STR[v]; + } + + static constexpr std::string_view get_label(ArrangeStrategy v) + { + constexpr auto STR = std::array{ + "auto"sv, "pulltocenter"sv, "undefined"sv + }; + + return STR[v]; + } + + static constexpr std::string_view get_label(XLPivots v) + { + constexpr auto STR = std::array{"center"sv, + "rearleft"sv, + "frontleft"sv, + "frontright"sv, + "rearright"sv, + "random"sv, + "undefined"sv}; + + return STR[v]; + } + + static constexpr std::optional to_geometry_handling(std::string_view str) + { + return get_enumval(str, GeometryHandlingLabels); + } + + static constexpr std::optional to_arrange_strategy(std::string_view str) + { + return get_enumval(str, ArrangeStrategyLabels); + } + + static constexpr std::optional to_xl_pivots(std::string_view str) + { + return get_enumval(str, XLPivotsLabels); + } + +private: + + template + using EnumMap = StaticMap; + + template + static constexpr std::optional get_enumval(std::string_view str, + const EnumMap &emap) + { + std::optional ret; + + if (auto v = query(emap, str); v.has_value()) { + ret = v.value(); + } + + return ret; + } + + static constexpr const auto GeometryHandlingLabels = make_staticmap({ + {"convex"sv, ghConvex}, + {"balanced"sv, ghBalanced}, + {"advanced"sv, ghAdvanced}, + + {"0"sv, ghConvex}, + {"1"sv, ghBalanced}, + {"2"sv, ghAdvanced}, + }); + + static constexpr const auto ArrangeStrategyLabels = make_staticmap({ + {"auto"sv, asAuto}, + {"pulltocenter"sv, asPullToCenter}, + + {"0"sv, asAuto}, + {"1"sv, asPullToCenter} + }); + + static constexpr const auto XLPivotsLabels = make_staticmap({ + {"center"sv, xlpCenter }, + {"rearleft"sv, xlpRearLeft }, + {"frontleft"sv, xlpFrontLeft }, + {"frontright"sv, xlpFrontRight }, + {"rearright"sv, xlpRearRight }, + {"random"sv, xlpRandom }, + + {"0"sv, xlpCenter }, + {"1"sv, xlpRearLeft }, + {"2"sv, xlpFrontLeft }, + {"3"sv, xlpFrontRight }, + {"4"sv, xlpRearRight }, + {"5"sv, xlpRandom } + }); }; class ArrangeSettingsDb: public ArrangeSettingsView diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f29424b4cd..29eadf6657 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1,13 +1,13 @@ -///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, David Kocík @kocikdav, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral -///|/ Copyright (c) BambuStudio 2023 manch1n @manch1n -///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill -///|/ Copyright (c) 2020 Benjamin Greiner -///|/ Copyright (c) 2019 John Drake @foxox -///|/ Copyright (c) 2019 BeldrothTheGold @BeldrothTheGold -///|/ Copyright (c) 2019 Thomas Moore -///|/ -///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher -///|/ +///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, David Kocík @kocikdav, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral +///|/ Copyright (c) BambuStudio 2023 manch1n @manch1n +///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill +///|/ Copyright (c) 2020 Benjamin Greiner +///|/ Copyright (c) 2019 John Drake @foxox +///|/ Copyright (c) 2019 BeldrothTheGold @BeldrothTheGold +///|/ Copyright (c) 2019 Thomas Moore +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "libslic3r/libslic3r.h" #include "GLCanvas3D.hpp" @@ -1642,7 +1642,6 @@ void GLCanvas3D::set_config(const DynamicPrintConfig* config) m_config = config; m_layers_editing.set_config(config); - if (config) { PrinterTechnology ptech = current_printer_technology(); @@ -1663,7 +1662,14 @@ void GLCanvas3D::set_config(const DynamicPrintConfig* config) double objdst = min_object_distance(*config); double min_obj_dst = slot == ArrangeSettingsDb_AppCfg::slotFFFSeqPrint ? objdst : 0.; m_arrange_settings_db.set_distance_from_obj_range(slot, min_obj_dst, 100.); - m_arrange_settings_db.get_defaults(slot).d_obj = objdst; + + if (std::abs(m_arrange_settings_db.get_defaults(slot).d_obj - objdst) > EPSILON) { + m_arrange_settings_db.get_defaults(slot).d_obj = objdst; + + // Defaults have changed, so let's sync with the app config and fill + // in the missing values with the new defaults. + m_arrange_settings_db.sync(); + } } } From dc37d04e823fea1872f73679e910060e64fda7de Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 5 Oct 2023 14:38:18 +0200 Subject: [PATCH 2/6] Fix broken forward-compatibility for arrange settings with <=2.6.1 --- src/libslic3r/Arrange/ArrangeSettingsView.hpp | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Arrange/ArrangeSettingsView.hpp b/src/libslic3r/Arrange/ArrangeSettingsView.hpp index e55b769415..b3d81e06c1 100644 --- a/src/libslic3r/Arrange/ArrangeSettingsView.hpp +++ b/src/libslic3r/Arrange/ArrangeSettingsView.hpp @@ -42,7 +42,10 @@ public: static constexpr std::string_view get_label(GeometryHandling v) { constexpr auto STR = std::array{ - "convex"sv, "balanced"sv, "advanced"sv, "undefined"sv + "0"sv, // convex + "1"sv, // balanced + "2"sv, // advanced + "-1"sv, // undefined }; return STR[v]; @@ -51,7 +54,9 @@ public: static constexpr std::string_view get_label(ArrangeStrategy v) { constexpr auto STR = std::array{ - "auto"sv, "pulltocenter"sv, "undefined"sv + "0"sv, // auto + "1"sv, // pulltocenter + "-1"sv, // undefined }; return STR[v]; @@ -59,13 +64,15 @@ public: static constexpr std::string_view get_label(XLPivots v) { - constexpr auto STR = std::array{"center"sv, - "rearleft"sv, - "frontleft"sv, - "frontright"sv, - "rearright"sv, - "random"sv, - "undefined"sv}; + constexpr auto STR = std::array{ + "0"sv, // center + "1"sv, // rearleft + "2"sv, // frontleft + "3"sv, // frontright + "4"sv, // rearright + "5"sv, // random + "-1"sv, // undefined + }; return STR[v]; } From 9598a52aee3559612d01a8337ab8e7b631a21497 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 5 Oct 2023 16:00:16 +0200 Subject: [PATCH 3/6] Try to fix build on Mac --- src/libslic3r/Arrange/ArrangeSettingsView.hpp | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/Arrange/ArrangeSettingsView.hpp b/src/libslic3r/Arrange/ArrangeSettingsView.hpp index b3d81e06c1..59d171c110 100644 --- a/src/libslic3r/Arrange/ArrangeSettingsView.hpp +++ b/src/libslic3r/Arrange/ArrangeSettingsView.hpp @@ -77,21 +77,6 @@ public: return STR[v]; } - static constexpr std::optional to_geometry_handling(std::string_view str) - { - return get_enumval(str, GeometryHandlingLabels); - } - - static constexpr std::optional to_arrange_strategy(std::string_view str) - { - return get_enumval(str, ArrangeStrategyLabels); - } - - static constexpr std::optional to_xl_pivots(std::string_view str) - { - return get_enumval(str, XLPivotsLabels); - } - private: template @@ -110,6 +95,25 @@ private: return ret; } +public: + + static constexpr std::optional to_geometry_handling(std::string_view str) + { + return get_enumval(str, GeometryHandlingLabels); + } + + static constexpr std::optional to_arrange_strategy(std::string_view str) + { + return get_enumval(str, ArrangeStrategyLabels); + } + + static constexpr std::optional to_xl_pivots(std::string_view str) + { + return get_enumval(str, XLPivotsLabels); + } + +private: + static constexpr const auto GeometryHandlingLabels = make_staticmap({ {"convex"sv, ghConvex}, {"balanced"sv, ghBalanced}, From 564a4c7c3b5829aa7d7ffaf7850a01d690b8ddf5 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 5 Oct 2023 16:38:23 +0200 Subject: [PATCH 4/6] Follow up: fix for std::optional not having method value() on Mac --- src/libslic3r/Arrange/ArrangeSettingsView.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Arrange/ArrangeSettingsView.hpp b/src/libslic3r/Arrange/ArrangeSettingsView.hpp index 59d171c110..d62e59d1cd 100644 --- a/src/libslic3r/Arrange/ArrangeSettingsView.hpp +++ b/src/libslic3r/Arrange/ArrangeSettingsView.hpp @@ -89,7 +89,7 @@ private: std::optional ret; if (auto v = query(emap, str); v.has_value()) { - ret = v.value(); + ret = *v; } return ret; From 9850ad7c7d9ebc33db847958a93021941acb6e8e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 16 Oct 2023 14:44:58 +0200 Subject: [PATCH 5/6] Updated some phrases --- src/libslic3r/PrintConfig.cpp | 12 ++++++------ src/slic3r/GUI/EditGCodeDialog.cpp | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 38767ea62e..e9add773a6 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1523,7 +1523,7 @@ void PrintConfigDef::init_fff_params() def = this->add("gcode_binary", coBool); def->label = L("Export as binary G-code"); - def->tooltip = L("Exports G-code in binary format."); + def->tooltip = L("Export G-code in binary format."); def->mode = comExpert; def->set_default_value(new ConfigOptionBool(0)); @@ -5132,7 +5132,7 @@ OtherSlicingStatesConfigDef::OtherSlicingStatesConfigDef() def = this->add("is_extruder_used", coBools); def->label = L("Is extruder used?"); - def->tooltip = L("Vector of bools stating whether a given extruder is used in the print."); + def->tooltip = L("Vector of booleans stating whether a given extruder is used in the print."); } PrintStatisticsConfigDef::PrintStatisticsConfigDef() @@ -5220,7 +5220,7 @@ ObjectsInfoConfigDef::ObjectsInfoConfigDef() def->label = L("Scale per object"); def->tooltip = L("Contains a string with the information about what scaling was applied to the individual objects. " "Indexing of the objects is zero-based (first object has index 0).\n" - "Example: 'x:100% y:50% z:100'."); + "Example: 'x:100% y:50% z:100%'."); def = this->add("input_filename_base", coString); def->label = L("Input filename without extension"); @@ -5341,11 +5341,11 @@ CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef() def->tooltip = L("Zero-based index of the current layer (i.e. first layer is number 0)."); def = this->add("layer_z", coFloat); - def->label = L("Layer z"); + def->label = L("Layer Z"); def->tooltip = L("Height of the current layer above the print bed, measured to the top of the layer."); def = this->add("max_layer_z", coFloat); - def->label = L("Maximal layer z"); + def->label = L("Maximal layer Z"); def->tooltip = L("Height of the last layer above the print bed."); def = this->add("filament_extruder_id", coInt); @@ -5361,7 +5361,7 @@ CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef() def->tooltip = L("Index of the extruder that is being loaded. The index is zero based (first extruder has index 0)."); def = this->add("toolchange_z", coFloat); - def->label = L("Toolchange z"); + def->label = L("Toolchange Z"); def->tooltip = L("Height above the print bed when the toolchange takes place. Usually the same as layer_z, but can be different."); } diff --git a/src/slic3r/GUI/EditGCodeDialog.cpp b/src/slic3r/GUI/EditGCodeDialog.cpp index 93c8d59c26..6a628f29ac 100644 --- a/src/slic3r/GUI/EditGCodeDialog.cpp +++ b/src/slic3r/GUI/EditGCodeDialog.cpp @@ -159,15 +159,15 @@ void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) // Add slicing states placeholders - wxDataViewItem slicing_state = m_params_list->AppendGroup(_L("[Global] Slicing State"), "re_slice"); + wxDataViewItem slicing_state = m_params_list->AppendGroup(_L("[Global] Slicing state"), "re_slice"); if (!cgp_ro_slicing_states_config_def.empty()) { - wxDataViewItem read_only = m_params_list->AppendSubGroup(slicing_state, _L("Read Only"), "lock_closed"); + wxDataViewItem read_only = m_params_list->AppendSubGroup(slicing_state, _L("Read only"), "lock_closed"); for (const auto& [opt_key, def]: cgp_ro_slicing_states_config_def.options) m_params_list->AppendParam(read_only, get_type(opt_key, def), opt_key); } if (!cgp_rw_slicing_states_config_def.empty()) { - wxDataViewItem read_write = m_params_list->AppendSubGroup(slicing_state, _L("Read Write"), "lock_open"); + wxDataViewItem read_write = m_params_list->AppendSubGroup(slicing_state, _L("Read write"), "lock_open"); for (const auto& [opt_key, def] : cgp_rw_slicing_states_config_def.options) m_params_list->AppendParam(read_write, get_type(opt_key, def), opt_key); } @@ -175,7 +175,7 @@ void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) // add other universal params, which are related to slicing state if (!cgp_other_slicing_states_config_def.empty()) { - slicing_state = m_params_list->AppendGroup(_L("Slicing State"), "re_slice"); + slicing_state = m_params_list->AppendGroup(_L("Slicing state"), "re_slice"); for (const auto& [opt_key, def] : cgp_other_slicing_states_config_def.options) m_params_list->AppendParam(slicing_state, get_type(opt_key, def), opt_key); } @@ -186,7 +186,7 @@ void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) // Add print statistics subgroup if (!cgp_print_statistics_config_def.empty()) { - wxDataViewItem statistics = m_params_list->AppendGroup(_L("Print Statistics"), "info"); + wxDataViewItem statistics = m_params_list->AppendGroup(_L("Print statistics"), "info"); for (const auto& [opt_key, def] : cgp_print_statistics_config_def.options) m_params_list->AppendParam(statistics, get_type(opt_key, def), opt_key); } @@ -194,7 +194,7 @@ void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) // Add objects info subgroup if (!cgp_objects_info_config_def.empty()) { - wxDataViewItem objects_info = m_params_list->AppendGroup(_L("Objects Info"), "advanced_plus"); + wxDataViewItem objects_info = m_params_list->AppendGroup(_L("Objects info"), "advanced_plus"); for (const auto& [opt_key, def] : cgp_objects_info_config_def.options) m_params_list->AppendParam(objects_info, get_type(opt_key, def), opt_key); } From 0638b3057f5d12fbc334ce300008171da4dc3a9a Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 16 Oct 2023 16:09:02 +0200 Subject: [PATCH 6/6] wifi for printer config dialog Enumerating wifi ssid and psk for win, apple and linux. Win automatically loads password if possible. Apple retrieves password from keychain on button. Apple lists only connected network in some cases (ARM or latest OS wdk). Cmake linking CoreWLAN lib on apple. Result is written on usb drive. --- src/slic3r/CMakeLists.txt | 9 +- src/slic3r/GUI/GUI_App.cpp | 13 + src/slic3r/GUI/GUI_App.hpp | 1 + src/slic3r/GUI/PhysicalPrinterDialog.cpp | 1 - src/slic3r/GUI/PhysicalPrinterDialog.hpp | 1 - src/slic3r/GUI/RemovableDriveManager.cpp | 7 + src/slic3r/GUI/RemovableDriveManager.hpp | 3 +- src/slic3r/GUI/WifiConfigDialog.cpp | 249 ++++++++++++ src/slic3r/GUI/WifiConfigDialog.hpp | 45 +++ src/slic3r/Utils/WifiScanner.cpp | 459 +++++++++++++++++++++++ src/slic3r/Utils/WifiScanner.hpp | 40 ++ src/slic3r/Utils/WifiScannerMac.h | 21 ++ src/slic3r/Utils/WifiScannerMac.mm | 94 +++++ 13 files changed, 939 insertions(+), 4 deletions(-) create mode 100644 src/slic3r/GUI/WifiConfigDialog.cpp create mode 100644 src/slic3r/GUI/WifiConfigDialog.hpp create mode 100644 src/slic3r/Utils/WifiScanner.cpp create mode 100644 src/slic3r/Utils/WifiScanner.hpp create mode 100644 src/slic3r/Utils/WifiScannerMac.h create mode 100644 src/slic3r/Utils/WifiScannerMac.mm diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index c37d02482f..17bc0052ed 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -201,6 +201,8 @@ set(SLIC3R_GUI_SOURCES GUI/FirmwareDialog.hpp GUI/PrintHostDialogs.cpp GUI/PrintHostDialogs.hpp + GUI/WifiConfigDialog.cpp + GUI/WifiConfigDialog.hpp GUI/Jobs/Job.hpp GUI/Jobs/Worker.hpp GUI/Jobs/BoostThreadWorker.hpp @@ -304,6 +306,8 @@ set(SLIC3R_GUI_SOURCES Utils/WinRegistry.hpp Utils/WxFontUtils.cpp Utils/WxFontUtils.hpp + Utils/WifiScanner.hpp + Utils/WifiScanner.cpp ) find_package(NanoSVG REQUIRED) @@ -313,6 +317,8 @@ if (APPLE) Utils/RetinaHelperImpl.mm Utils/MacDarkMode.mm Utils/MacUtils.mm + Utils/WifiScannerMac.h + Utils/WifiScannerMac.mm GUI/RemovableDriveManagerMM.mm GUI/RemovableDriveManagerMM.h GUI/Mouse3DHandlerMac.mm @@ -320,6 +326,7 @@ if (APPLE) GUI/InstanceCheckMac.h ) FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration) + FIND_LIBRARY(COREWLAN_LIBRARY CoreWLAN) endif () add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) @@ -339,7 +346,7 @@ if (MSVC) elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES}) elseif (APPLE) - target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY}) + target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY} ${COREWLAN_LIBRARY}) endif() if (SLIC3R_STATIC) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index d69f5b33de..e8905b2f87 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -95,6 +95,8 @@ #include "DesktopIntegrationDialog.hpp" #include "SendSystemInfoDialog.hpp" #include "Downloader.hpp" +#include "PhysicalPrinterDialog.hpp" +#include "WifiConfigDialog.hpp" #include "BitmapCache.hpp" #include "Notebook.hpp" @@ -2446,6 +2448,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu) // TODO: for when we're able to flash dictionaries // local_menu->Append(config_id_base + FirmwareMenuDict, _L("Flash Language File"), _L("Upload a language dictionary file into a Prusa printer")); } + local_menu->Append(config_id_base + ConfigMenuWifiConfigFile, _L("Wi-Fi Configuration File"), _L("Generate a file to be loaded by a Prusa printer to configure a Wi-Fi connection.")); local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event) { switch (event.GetId() - config_id_base) { @@ -2544,6 +2547,16 @@ void GUI_App::add_config_menu(wxMenuBar *menu) case ConfigMenuFlashFirmware: FirmwareDialog::run(mainframe); break; + case ConfigMenuWifiConfigFile: + { + std::string file_path; + WifiConfigDialog dialog(mainframe, file_path, removable_drive_manager()); + if (dialog.ShowModal() == wxID_OK) + { + plater_->get_notification_manager()->push_exporting_finished_notification(file_path, boost::filesystem::path(file_path).parent_path().string(), true); + } + } + break; default: break; } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 6951f654b6..039818e012 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -101,6 +101,7 @@ enum ConfigMenuIDs { ConfigMenuLanguage, ConfigMenuFlashFirmware, ConfigMenuCnt, + ConfigMenuWifiConfigFile }; class Tab; diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index 90ce1a998b..807413e047 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -838,5 +838,4 @@ void PhysicalPrinterDialog::DeletePreset(PresetForPrinter* preset_for_printer) update_host_type(true); } - }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.hpp b/src/slic3r/GUI/PhysicalPrinterDialog.hpp index 05b482ab0a..fd98c3d61a 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.hpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.hpp @@ -105,7 +105,6 @@ protected: }; - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 206e8eff38..4eeae25041 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -1095,4 +1095,11 @@ void RemovableDriveManager::eject_thread_finish() } #endif // __APPLE__ +std::vector RemovableDriveManager::get_drive_list() +{ + { + std::lock_guard guard(m_drives_mutex); + return m_current_drives; + } +} }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index d3617fde79..14ea4d290e 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -92,7 +92,8 @@ public: // Called by Win32 Volume arrived / detached callback. void volumes_changed(); #endif // _WIN32 - + // returns copy of m_current_drives (protected by mutex) + std::vector get_drive_list(); private: bool m_initialized { false }; wxEvtHandler* m_callback_evt_handler { nullptr }; diff --git a/src/slic3r/GUI/WifiConfigDialog.cpp b/src/slic3r/GUI/WifiConfigDialog.cpp new file mode 100644 index 0000000000..dc11bfdfdd --- /dev/null +++ b/src/slic3r/GUI/WifiConfigDialog.cpp @@ -0,0 +1,249 @@ +#include "WifiConfigDialog.hpp" + +#include "GUI_App.hpp" +#include "GUI.hpp" +#include "I18N.hpp" +#include "format.hpp" +#include "RemovableDriveManager.hpp" +#include "MsgDialog.hpp" + +#include +#include +#include +#include +#include + +namespace Slic3r { +namespace GUI { + +WifiConfigDialog::WifiConfigDialog(wxWindow* parent, std::string& file_path, RemovableDriveManager* removable_manager) + : DPIDialog(parent, wxID_ANY, _L("Physical Printer Instalation"), wxDefaultPosition, wxDefaultSize/*wxSize(25 * wxGetApp().em_unit(), 20 * wxGetApp().em_unit())*/, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + , m_wifi_scanner(new WifiScanner()) + , out_file_path(file_path) + , m_removable_manager(removable_manager) +{ + const int em = GUI::wxGetApp().em_unit(); + + wxPanel* panel = new wxPanel(this); + wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); + panel->SetSizer(vsizer); + + auto* ssid_sizer = new wxBoxSizer(wxHORIZONTAL); + // TRN SSID of WiFi network. + wxStaticText* ssid_label = new wxStaticText(panel, wxID_ANY, GUI::format_wxstr("%1%:", _L("SSID"))); + m_ssid_combo = new wxComboBox(panel, wxID_ANY); +#if __APPLE__ + m_ssid_combo->SetToolTip(_L("On some versions of MacOS, this only loads SSID of connencted network.")); +#endif // __APPLE__ + rescan_networks(false); + wxButton* ssid_button = new wxButton(panel, wxID_ANY, _(L("Rescan"))); + ssid_sizer->Add(m_ssid_combo, 1, wxALIGN_CENTER_VERTICAL, 10); + ssid_sizer->Add(ssid_button, 0); + + auto* pass_sizer = new wxBoxSizer(wxHORIZONTAL); + // TRN Password of WiFi network. + wxStaticText* password_label = new wxStaticText(panel, wxID_ANY, GUI::format_wxstr("%1%:", _L("Password"))); + m_pass_textctrl = new wxTextCtrl(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize); + pass_sizer->Add(m_pass_textctrl, 1, wxALIGN_CENTER_VERTICAL, 10); +#if __APPLE__ + wxButton* pass_button = new wxButton(panel, wxID_ANY, _(L("Retrieve"))); + pass_sizer->Add(pass_button, 0); + pass_button->Bind(wxEVT_BUTTON, &WifiConfigDialog::on_retrieve_password, this); +#endif // __APPLE__ + // show password if current ssid was selected already + fill_password(); + + auto* drive_sizer = new wxBoxSizer(wxHORIZONTAL); + // TRN description of Combo Box with path to USB drive. + wxStaticText* drive_label = new wxStaticText(panel, wxID_ANY, GUI::format_wxstr("%1%:", _L("Drive"))); + m_drive_combo = new wxComboBox(panel, wxID_ANY); + rescan_drives(); + wxButton* drive_button = new wxButton(panel, wxID_ANY, _(L("Rescan"))); + drive_sizer->Add(m_drive_combo, 1, wxALIGN_CENTER_VERTICAL, 10); + drive_sizer->Add(drive_button, 0); + + wxButton* ok_button = new wxButton(panel, wxID_OK, _L("Write")); + wxButton* cancel_button = new wxButton(panel, wxID_CANCEL); + + auto* grid = new wxFlexGridSizer(2, 15, 15); + grid->AddGrowableCol(1); + + grid->Add(ssid_label, 0, wxALIGN_CENTER_VERTICAL); + grid->Add(ssid_sizer, 0, wxEXPAND); + + grid->Add(password_label, 0, wxALIGN_CENTER_VERTICAL); + grid->Add(pass_sizer, 0, wxEXPAND); + + grid->Add(drive_label, 0, wxALIGN_CENTER_VERTICAL); + grid->Add(drive_sizer, 0, wxEXPAND); + + vsizer->Add(grid, 0, wxEXPAND | wxTOP | wxBOTTOM, 15); + + wxBoxSizer* buttons_sizer = new wxBoxSizer(wxHORIZONTAL); + buttons_sizer->Add(ok_button, 1, wxLEFT); + buttons_sizer->AddStretchSpacer(); + buttons_sizer->Add(cancel_button, 1, wxRIGHT); + + vsizer->Add(buttons_sizer, 0, wxEXPAND); + + wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL); + topsizer->Add(panel, 1, wxEXPAND | wxALL, 15); + SetSizerAndFit(topsizer); + + ok_button->Bind(wxEVT_BUTTON, &WifiConfigDialog::on_ok, this); + m_ssid_combo->Bind(wxEVT_TEXT, &WifiConfigDialog::on_combo, this); + drive_button->Bind(wxEVT_BUTTON, &WifiConfigDialog::on_rescan_drives, this); + ssid_button->Bind(wxEVT_BUTTON, &WifiConfigDialog::on_rescan_networks, this); +} + +WifiConfigDialog::~WifiConfigDialog() +{ + if (m_wifi_scanner) + delete m_wifi_scanner; +} + +void WifiConfigDialog::on_combo(wxCommandEvent& e) +{ + fill_password(); +} + +void WifiConfigDialog::fill_password() +{ + assert(m_ssid_combo && m_pass_textctrl && m_wifi_scanner); + if (auto it = m_wifi_scanner->get_map().find(m_ssid_combo->GetValue()); it != m_wifi_scanner->get_map().end()) + m_pass_textctrl->SetValue(boost::nowide::widen(it->second)); +} + +void WifiConfigDialog::on_retrieve_password(wxCommandEvent& e) +{ + if (m_ssid_combo->GetValue().empty()) { + return; + } + + std::string psk = m_wifi_scanner->get_psk(boost::nowide::narrow(m_ssid_combo->GetValue())); + if (psk.empty()) { + // TRN Alert message when retrieving password for wifi from keychain. Probably will display only on Apple so keychain is MacOS term. + wxString msg = _L("No password in the keychain for given SSID."); + show_info(nullptr, msg); + return; + } + m_pass_textctrl->SetValue(boost::nowide::widen(psk)); +} + +void WifiConfigDialog::on_rescan_drives(wxCommandEvent& e) +{ + rescan_drives(); +} + +void WifiConfigDialog::rescan_drives() +{ + assert(m_drive_combo && m_removable_manager); + m_drive_combo->Clear(); + std::vector ddata = m_removable_manager->get_drive_list(); + for (const auto& data : ddata) { + m_drive_combo->Append(boost::nowide::widen(data.path)); + } + if (m_drive_combo->GetCount() > 0) { + m_drive_combo->Select(0); + } +} + +void WifiConfigDialog::on_rescan_networks(wxCommandEvent& e) +{ + rescan_networks(true); +} + +void WifiConfigDialog::rescan_networks(bool select) +{ + assert(m_ssid_combo && m_wifi_scanner); + m_wifi_scanner->scan(); + std::string current = m_wifi_scanner->get_current_ssid(); + const auto& map = m_wifi_scanner->get_map(); + m_ssid_combo->Clear(); + for (const auto pair : map) { + m_ssid_combo->Append(pair.first); + // select ssid of current network (if connected) + if (current == pair.first) + m_ssid_combo->Select(m_ssid_combo->GetCount() - 1); + } + if (m_ssid_combo->GetSelection() == -1 && m_ssid_combo->GetCount() > 0) + m_ssid_combo->Select(0); + if (select && m_ssid_combo->GetSelection() != -1) + fill_password(); +} + +void WifiConfigDialog::on_ok(wxCommandEvent& e) +{ + assert(m_ssid_combo && m_pass_textctrl); + if (m_ssid_combo->GetValue().empty()) { + // TRN Alert message when writing WiFi configuration file to usb drive. + wxString msg = _L("SSID field is empty."); + show_info(nullptr, msg); + return; + } + + std::string selected_path = boost::nowide::narrow(m_drive_combo->GetValue()); + + if (selected_path.empty()) { + // TRN Alert message when writing WiFi configuration file to usb drive. + wxString msg = _L("Drive field is empty."); + show_info(nullptr, msg); + return; + } + + boost::filesystem::path file_path = boost::filesystem::path(selected_path) / "prusa_printer_settings.ini"; + + bool path_on_removable_media = m_removable_manager->set_and_verify_last_save_path(file_path.string()); + if (!path_on_removable_media) { + // TRN Alert message when writing WiFi configuration file to usb drive. + wxString msg = _L("Selected path is not on removable media."); + show_info(nullptr, msg); + return; + } + + if (boost::filesystem::exists(file_path)) + { + wxString msg_text = GUI::format_wxstr("%1% already exists. Do you want to rewrite it?", file_path.string()); + WarningDialog dialog(m_parent, msg_text, _L("Warning"), wxYES | wxNO); + if (dialog.ShowModal() == wxID_NO) + { + return; + } + } + + wxString ssid = m_ssid_combo->GetValue(); + wxString pass = m_pass_textctrl->GetValue(); + wxString data = GUI::format( + "[wifi]\n" + "ssid=%1%\n" + "psk=%2%\n" + , ssid + , pass + ); + + FILE* file; + file = fopen(file_path.string().c_str(), "w"); + if (file == NULL) { + BOOST_LOG_TRIVIAL(error) << "Failed to write to file " << file_path; + // TODO show error + show_error(nullptr, _L("Failed to open file for writing.")); + return; + } + fwrite(data.c_str(), 1, data.size(), file); + fclose(file); + out_file_path = file_path.string(); + this->EndModal(wxID_OK); +} + +void WifiConfigDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + SetFont(wxGetApp().normal_font()); + + const int em = em_unit(); + + //msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); + + Fit(); + Refresh(); +} +}}// Slicer::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/WifiConfigDialog.hpp b/src/slic3r/GUI/WifiConfigDialog.hpp new file mode 100644 index 0000000000..366a80acf4 --- /dev/null +++ b/src/slic3r/GUI/WifiConfigDialog.hpp @@ -0,0 +1,45 @@ +#ifndef slic3r_WifiConfigDialog_hpp_ +#define slic3r_WifiConfigDialog_hpp_ + +#include "GUI_Utils.hpp" + +#include "../Utils/WifiScanner.hpp" + +#include +#include +#include +#include + +namespace Slic3r { +namespace GUI { + +class RemovableDriveManager; +class WifiConfigDialog : public DPIDialog +{ +public: + WifiConfigDialog(wxWindow* parent, std::string& file_path, RemovableDriveManager* removable_manager); + ~WifiConfigDialog(); +private: + wxComboBox* m_ssid_combo {nullptr}; + wxTextCtrl* m_pass_textctrl {nullptr}; + wxComboBox* m_drive_combo {nullptr}; + + void on_ok(wxCommandEvent& e); + void on_combo(wxCommandEvent& e); + void on_rescan_drives(wxCommandEvent& e); + void on_rescan_networks(wxCommandEvent& e); + void on_retrieve_password(wxCommandEvent& e); + void rescan_drives(); + void rescan_networks(bool select); + void fill_password(); + // reference to string that is filled after ShowModal is called from owner + std::string& out_file_path; + WifiScanner* m_wifi_scanner; + RemovableDriveManager* m_removable_manager; +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; + void on_sys_color_changed() override {} +}; + +}} // Slicer::GUI +#endif \ No newline at end of file diff --git a/src/slic3r/Utils/WifiScanner.cpp b/src/slic3r/Utils/WifiScanner.cpp new file mode 100644 index 0000000000..3282784afb --- /dev/null +++ b/src/slic3r/Utils/WifiScanner.cpp @@ -0,0 +1,459 @@ +#include "WifiScanner.hpp" + + +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#include +#include + +// Need to link with Wlanapi.lib and Ole32.lib +#pragma comment(lib, "wlanapi.lib") +#pragma comment(lib, "ole32.lib") +#elif __APPLE_ +#include "WifiScannerMac.h" +#endif + +#if __linux__ +#include /* Pull in all of D-Bus headers. */ +#endif //__linux__ + +namespace { +bool ptree_get_value(const boost::property_tree::ptree& pt, const std::string& target, std::string& result) +{ + // Check if the current node has the target element + if (pt.find(target) != pt.not_found()) { + result = pt.get(target); + return true; + } + + // Recursively search child nodes + for (const auto& child : pt) { + if (ptree_get_value(child.second, target, result)) { + return true; + } + } + + return false; // Element not found in this subtree +} +#ifdef _WIN32 +// Fill SSID map. Implementation from Raspberry Pi imager and Win32 Api examples. +// https://github.com/raspberrypi/rpi-imager/blob/qml/src/windows/winwlancredentials.cpp +// https://learn.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlangetavailablenetworklist +void fill_wifi_map(Slic3r::WifiSsidPskMap& wifi_map, std::string& connected_ssid) +{ + HANDLE handle; + DWORD supported_version = 0; + DWORD client_version = 2; + PWLAN_INTERFACE_INFO_LIST interface_list = NULL; + + + if (WlanOpenHandle(client_version, NULL, &supported_version, &handle) != ERROR_SUCCESS) + return; + + if (WlanEnumInterfaces(handle, NULL, &interface_list) != ERROR_SUCCESS) + return; + + for (DWORD i = 0; i < interface_list->dwNumberOfItems; i++) + { + if (interface_list->InterfaceInfo[i].isState == wlan_interface_state_connected) + { + PWLAN_CONNECTION_ATTRIBUTES pConnectInfo = NULL; + DWORD connectInfoSize = sizeof(WLAN_CONNECTION_ATTRIBUTES); + WLAN_OPCODE_VALUE_TYPE opCode = wlan_opcode_value_type_invalid; + + if (WlanQueryInterface(handle, &interface_list->InterfaceInfo[i].InterfaceGuid, + wlan_intf_opcode_current_connection, NULL, + &connectInfoSize, (PVOID*)&pConnectInfo, &opCode) == ERROR_SUCCESS && pConnectInfo && pConnectInfo->wlanAssociationAttributes.dot11Ssid.uSSIDLength) + { + connected_ssid = std::string((const char*)pConnectInfo->wlanAssociationAttributes.dot11Ssid.ucSSID, + pConnectInfo->wlanAssociationAttributes.dot11Ssid.uSSIDLength); + } + + WlanFreeMemory(pConnectInfo); + } + + PWLAN_PROFILE_INFO_LIST profile_list = NULL; + PWLAN_INTERFACE_INFO interface_info_entry = NULL; + PWLAN_AVAILABLE_NETWORK_LIST available_network_list = NULL; + WCHAR guid[39] = { 0 }; + + // Get all available networks. + interface_info_entry = (WLAN_INTERFACE_INFO*)&interface_list->InterfaceInfo[i]; + int iRet = StringFromGUID2(interface_info_entry->InterfaceGuid, (LPOLESTR)&guid, + sizeof(guid) / sizeof(*guid)); + + if (WlanGetAvailableNetworkList(handle, + &interface_info_entry->InterfaceGuid, + 0, + NULL, + &available_network_list) + != ERROR_SUCCESS) + { + continue; + } + + for (unsigned int j = 0; j < available_network_list->dwNumberOfItems; j++) + { + PWLAN_AVAILABLE_NETWORK available_network_entry = NULL; + wxString ssid; + + // Store SSID into the map. + available_network_entry = + (WLAN_AVAILABLE_NETWORK*)&available_network_list->Network[j]; + + if (available_network_entry->dot11Ssid.uSSIDLength != 0) + ssid = wxString(available_network_entry->dot11Ssid.ucSSID, + available_network_entry->dot11Ssid.uSSIDLength); + + if (ssid.empty()) + continue; + + if (wifi_map.find(ssid) != wifi_map.end()) + continue; + + wifi_map[ssid] = std::string(); + + if (WlanGetProfileList(handle, &interface_list->InterfaceInfo[i].InterfaceGuid, + NULL, &profile_list) != ERROR_SUCCESS) + { + continue; + } + // enmurate all stored profiles, take password from matching one. + for (DWORD k = 0; k < profile_list->dwNumberOfItems; k++) + { + DWORD flags = WLAN_PROFILE_GET_PLAINTEXT_KEY; + DWORD access = 0; + DWORD ret = 0; + LPWSTR xmlstr = NULL; + wxString s(profile_list->ProfileInfo[k].strProfileName); + + BOOST_LOG_TRIVIAL(debug) << "Enumerating wlan profiles, SSID found:" << s << " looking for:" << ssid; + + if (s != ssid) + continue; + + if ((ret = WlanGetProfile(handle, &interface_list->InterfaceInfo[i].InterfaceGuid, profile_list->ProfileInfo[k].strProfileName, + NULL, &xmlstr, &flags, &access)) == ERROR_SUCCESS && xmlstr) + { + wxString xml(xmlstr); + boost::property_tree::ptree pt; + std::stringstream ss(boost::nowide::narrow(xml)); + BOOST_LOG_TRIVIAL(error) << ss.str(); + boost::property_tree::read_xml(ss, pt); + std::string password; + std::string psk_protected; + + BOOST_LOG_TRIVIAL(debug) << "XML wlan profile:" << xml; + + // break if password is not readable + // TODO: what if there is other line "protected" in the XML? + if (ptree_get_value(pt, "protected", psk_protected) && psk_protected != "false") + break; + + if (ptree_get_value(pt, "keyMaterial", password)) + wifi_map[ssid] = password; + + WlanFreeMemory(xmlstr); + break; + } + } + + if (profile_list) { + WlanFreeMemory(profile_list); + } + } + } + + if (interface_list) + WlanFreeMemory(interface_list); + WlanCloseHandle(handle, NULL); +} +#elif __APPLE__ +void get_connected_ssid(std::string& connected_ssid) +{ + std::string program = "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I"; + std::string regexpstr = "[ \t]+SSID: (.+)"; + + std::ostringstream output; + std::string line; + + // Run the process and capture its output + FILE* pipe = popen(program.c_str(), "r"); + if (!pipe) { + BOOST_LOG_TRIVIAL(error) << "Error executing airport command." << std::endl; + return; + } + + char buffer[128]; + while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + line = buffer; + output << line; + } + BOOST_LOG_TRIVIAL(error) << output.str(); + pclose(pipe); + + // Process the captured output using regular expressions + std::regex rx(regexpstr); + std::smatch match; + + std::istringstream outputStream(output.str()); + while (std::getline(outputStream, line)) { + BOOST_LOG_TRIVIAL(error) << line; + if (std::regex_search(line, match, rx)) { + connected_ssid = match[1].str(); + // airport -I gets data only about current connection, so only 1 network. + return; + } + } +} +#else + +DBusMessage* dbus_query(DBusConnection* connection, const char* service, const char* object, const char* interface, const char* method) +{ + DBusError error; + dbus_error_init(&error); + + DBusMessage* msg = dbus_message_new_method_call(service, object, interface, method); + + DBusMessage* reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &error); + dbus_message_unref(msg); + + if (dbus_error_is_set(&error)) { + // todo (debug) + BOOST_LOG_TRIVIAL(debug) << "D-Bus method call error: " << error.message << std::endl; + dbus_error_free(&error); + return nullptr; + } + + return reply; +} + +// On each access point call method Get on interface org.freedesktop.DBus.Properties to get the ssid +void iter_access_points(DBusConnection* connection, DBusMessage* access_points, Slic3r::WifiSsidPskMap& wifi_map) +{ + DBusError error; + dbus_error_init(&error); + + DBusMessageIter iter; + dbus_message_iter_init(access_points, &iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { + BOOST_LOG_TRIVIAL(debug) << "Error iterating access points reply - not an array."; + return; + } + DBusMessageIter array_iter; + dbus_message_iter_recurse(&iter, &array_iter); + + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_OBJECT_PATH) { + const char* object_path; + dbus_message_iter_get_basic(&array_iter, &object_path); + + DBusMessage* msg = dbus_message_new_method_call( + "org.freedesktop.NetworkManager", + object_path, + "org.freedesktop.DBus.Properties", + "Get"); + + const char* arg1 = "org.freedesktop.NetworkManager.AccessPoint"; + const char* arg2 = "Ssid"; + dbus_message_append_args( + msg, + DBUS_TYPE_STRING, &arg1, + DBUS_TYPE_STRING, &arg2, + DBUS_TYPE_INVALID + ); + + DBusMessage* reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &error); + dbus_message_unref(msg); + + if (dbus_error_is_set(&error)) { + BOOST_LOG_TRIVIAL(debug) << "D-Bus method call error: " << error.message << std::endl; + dbus_error_free(&error); + dbus_message_iter_next(&array_iter); + continue; + } + + // process reply + DBusMessageIter rep_iter; + dbus_message_iter_init(reply, &rep_iter); + dbus_message_unref(reply); + if (dbus_message_iter_get_arg_type(&rep_iter) != DBUS_TYPE_VARIANT) { + BOOST_LOG_TRIVIAL(debug) << "Reply does not contain a Variant"; + dbus_message_iter_next(&array_iter); + continue; + } + + DBusMessageIter variant_iter; + dbus_message_iter_recurse(&rep_iter, &variant_iter); + + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY) { + BOOST_LOG_TRIVIAL(debug) << "Variant does not contain an array"; + dbus_message_iter_next(&array_iter); + continue; + } + + DBusMessageIter var_array_iter; + dbus_message_iter_recurse(&variant_iter, &var_array_iter); + + if (dbus_message_iter_get_arg_type(&var_array_iter) != DBUS_TYPE_BYTE) { + BOOST_LOG_TRIVIAL(debug) << "Array does not contain bytes"; + dbus_message_iter_next(&array_iter); + continue; + } + + unsigned char *result; + int result_len; + + // Get the array of bytes and its length + dbus_message_iter_get_fixed_array(&var_array_iter, &result, &result_len); + + wifi_map[result] = std::string(); + + dbus_message_iter_next(&array_iter); + } +} + +// For each device call method GetAllAccessPoints to get object path to all Access Point objects +void iter_devices(DBusConnection* connection, DBusMessage* devices, Slic3r::WifiSsidPskMap& wifi_map) +{ + DBusError error; + dbus_error_init(&error); + + DBusMessageIter iter; + dbus_message_iter_init(devices, &iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { + BOOST_LOG_TRIVIAL(debug) << "Error iterating devices reply - not an array."; + return; + } + DBusMessageIter array_iter; + dbus_message_iter_recurse(&iter, &array_iter); + + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_OBJECT_PATH) { + const char* object_path; + dbus_message_iter_get_basic(&array_iter, &object_path); + + // Create a new message to get all access points for this device + DBusMessage* reply = dbus_query( + connection, + "org.freedesktop.NetworkManager", // Service name + object_path, // Object path (device path) + "org.freedesktop.NetworkManager.Device.Wireless", // Interface for Wi-Fi devices + "GetAllAccessPoints" // Method name + ); + if (reply) { + iter_access_points(connection, reply, wifi_map); + dbus_message_unref(reply); + } + + dbus_message_iter_next(&array_iter); + } +} + +// Query NetworkManager for available Wi-Fi. +// On org.freedesktop.NetworkManager call method GetAllDevices to get object paths to all device objects. +// For each device call method GetAllAccessPoints to get object path to all Access Point objects (iter_devices function here). +// On each access point call method Get on interface org.freedesktop.DBus.Properties to get the ssid (iter_access_points function here). +void fill_wifi_map(Slic3r::WifiSsidPskMap& wifi_map) +{ + DBusConnection* connection; + DBusError error; + dbus_error_init(&error); + + // Connect to the system bus + connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + + if (dbus_error_is_set(&error)) { + BOOST_LOG_TRIVIAL(debug) << "D-Bus connection error: " << error.message << std::endl; + dbus_error_free(&error); + } + + // + DBusMessage* reply = dbus_query( + connection, + "org.freedesktop.NetworkManager", // Service name + "/org/freedesktop/NetworkManager", // Object path + "org.freedesktop.NetworkManager", // Interface + "GetAllDevices" // Method name + ); + if (reply) { + iter_devices(connection, reply, wifi_map); + dbus_message_unref(reply); + } + + dbus_connection_unref(connection); +} +#endif //__linux__ +} +namespace Slic3r +{ +WifiScanner::WifiScanner() +{} +WifiScanner::~WifiScanner() +{} +void WifiScanner::scan() +{ + m_map.clear(); +#ifdef _WIN32 + fill_wifi_map(m_map, m_current_ssid); +#elif __APPLE__ + std::vector ssids; + try + { + // Objective-c implementation using CoreWLAN library + // This failed to get data at ARM Sonoma + get_ssids_mac(ssids); + } + catch (const std::exception&) + { + BOOST_LOG_TRIVIAL(error) << "Exception caught: Getting SSIDs failed."; + } + + for ( const std::string& ssid : ssids) + { + m_map[boost::nowide::widen(ssid)] = {}; + } + if (m_map.empty()) { + try + { + // Second implementation calling "airport" system command + get_connected_ssid(m_current_ssid); + m_map[m_current_ssid] = std::string(); + } + catch (const std::exception&) + { + BOOST_LOG_TRIVIAL(error) << "Exception caught: get_connected_ssid failed."; + } + } else { + try + { + m_current_ssid = get_current_ssid_mac(); + } + catch (const std::exception&) + { + BOOST_LOG_TRIVIAL(error) << "Exception caught: Getting current SSID failed."; + } + } + +#else + fill_wifi_map(m_map); +#endif +} +std::string WifiScanner::get_psk(const std::string& ssid) +{ +#ifdef __APPLE__ + return get_psk_mac(ssid); +#endif + if (m_map.find(ssid) != m_map.end()) + { + return m_map[ssid]; + } + return {}; +} +} // Slic3r \ No newline at end of file diff --git a/src/slic3r/Utils/WifiScanner.hpp b/src/slic3r/Utils/WifiScanner.hpp new file mode 100644 index 0000000000..96befa5e65 --- /dev/null +++ b/src/slic3r/Utils/WifiScanner.hpp @@ -0,0 +1,40 @@ +#ifndef slic3r_WifiScanner_hpp_ +#define slic3r_WifiScanner_hpp_ + +#include +#include +#include +#include + +namespace Slic3r { + +typedef std::map WifiSsidPskMap; + +class WifiScanner +{ +public: + WifiScanner(); + ~WifiScanner(); + + const WifiSsidPskMap& get_map() const { return m_map; } + // returns psk for given ssid + // used on APPLE where each psk query requires user to give their password + std::string get_psk(const std::string& ssid); + const std::string get_current_ssid() { return m_current_ssid; } + // fills map with ssid psk couples (or only ssid if no psk) + // on APPLE only ssid + void scan(); +private: + WifiSsidPskMap m_map; + std::string m_current_ssid; +#if __APPLE__ + void get_ssids_mac(std::vector& ssids); + std::string get_psk_mac(const std::string& ssid); + std::string get_current_ssid_mac(); + void* m_impl_osx { nullptr }; +#endif +}; + +} // Slic3r + +#endif \ No newline at end of file diff --git a/src/slic3r/Utils/WifiScannerMac.h b/src/slic3r/Utils/WifiScannerMac.h new file mode 100644 index 0000000000..44cdd361a0 --- /dev/null +++ b/src/slic3r/Utils/WifiScannerMac.h @@ -0,0 +1,21 @@ +#ifndef WiFiScanner_h +#define WiFiScanner_h + +#import +extern "C" { +#import +} + +@interface WifiScannerMac : NSObject + +- (instancetype)init; +- (void)dealloc; +- (NSArray *)scan_ssids; +- (NSString *)retrieve_password_for_ssid:(NSString *)ssid; +- (NSString *)current_ssid; + +@property (strong, nonatomic) CWInterface *wifiInterface; + +@end + +#endif /* WiFiScanner_h */ \ No newline at end of file diff --git a/src/slic3r/Utils/WifiScannerMac.mm b/src/slic3r/Utils/WifiScannerMac.mm new file mode 100644 index 0000000000..5d7bd58eda --- /dev/null +++ b/src/slic3r/Utils/WifiScannerMac.mm @@ -0,0 +1,94 @@ +#import "WifiScanner.hpp" +#import "WifiScannerMac.h" + +@implementation WifiScannerMac + +- (instancetype)init { + self = [super init]; + if (self) { + // Create a CWInterface object to work with Wi-Fi interfaces + self->_wifiInterface = [CWInterface interface]; + } + return self; +} + +- (void)dealloc { + [super dealloc]; +} + +- (NSArray *)scan_ssids { + NSMutableArray *ssids = [NSMutableArray array]; + NSError *error = nil; + // Retrieve the list of available Wi-Fi networks + NSSet *networksSet = [self->_wifiInterface scanForNetworksWithName:nil error:&error]; + if (error) { + return ssids; + } + NSArray *availableNetworks = [networksSet allObjects]; + + // Loop through the list of available networks and store their SSIDs + for (CWNetwork *network in availableNetworks) { + if (network.ssid != nil) + { + [ssids addObject:network.ssid]; + } + } + return ssids; +} + +- (NSString *)retrieve_password_for_ssid:(NSString *)ssid { + NSString * psk; + OSStatus status = CWKeychainFindWiFiPassword(kCWKeychainDomainSystem, [ssid dataUsingEncoding:NSUTF8StringEncoding], &psk); + if (status == errSecSuccess) { + return psk; + } + return @""; // Password not found or an error occurred + +} + +- (NSString *)current_ssid +{ + if (self->_wifiInterface && self->_wifiInterface.ssid != nil) { + return self->_wifiInterface.ssid; + } + return @""; +} + +@end + +namespace Slic3r { + +void WifiScanner::get_ssids_mac(std::vector& ssids) +{ + if (!m_impl_osx) + m_impl_osx = [[WifiScannerMac alloc] init]; + if (m_impl_osx) { + NSArray *arr = [(id)m_impl_osx scan_ssids]; + for (NSString* ssid in arr) + ssids.push_back(std::string([ssid UTF8String])); + } +} + +std::string WifiScanner::get_psk_mac(const std::string &ssid) +{ + if (!m_impl_osx) + m_impl_osx = [[WifiScannerMac alloc] init]; + if (m_impl_osx) { + NSString *ns_ssid = [NSString stringWithCString:ssid.c_str() encoding:[NSString defaultCStringEncoding]]; + NSString *psk = [(id)m_impl_osx retrieve_password_for_ssid:ns_ssid]; + return std::string([psk UTF8String]); + } + return {}; +} + +std::string WifiScanner::get_current_ssid_mac() +{ + if (!m_impl_osx) + m_impl_osx = [[WifiScannerMac alloc] init]; + if (m_impl_osx) { + NSString *ssid = [(id)m_impl_osx current_ssid]; + return std::string([ssid UTF8String]); + } + return {}; +} +}