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..d62e59d1cd 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,115 @@ 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{ + "0"sv, // convex + "1"sv, // balanced + "2"sv, // advanced + "-1"sv, // undefined + }; + + return STR[v]; + } + + static constexpr std::string_view get_label(ArrangeStrategy v) + { + constexpr auto STR = std::array{ + "0"sv, // auto + "1"sv, // pulltocenter + "-1"sv, // undefined + }; + + return STR[v]; + } + + static constexpr std::string_view get_label(XLPivots v) + { + 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]; + } + +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; + } + + 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}, + {"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/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/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 274f3ccfec..8f9900bbf2 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -203,6 +203,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/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); } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f24c4f3a9e..86505c1dea 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -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(); + } } } 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 {}; +} +}