Merge branch 'master' into fs_svg_SPE-1517

This commit is contained in:
Filip Sykala - NTB T15p 2023-10-17 11:21:38 +02:00
commit 86879c5d92
20 changed files with 1109 additions and 62 deletions

View File

@ -444,7 +444,7 @@ ArrItem AdvancedItemConverter<ArrItem>::get_arritem(const Arrangeable &arrbl,
auto simpl_tol = static_cast<double>(this->simplification_tolerance());
if (simpl_tol > 0)
if (simpl_tol > 0.)
{
outline = expolygons_simplify(outline, simpl_tol);
if (!envelope.empty())

View File

@ -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<ArrangeSettingsView::XLPivots>(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<ArrangeSettingsView::GeometryHandling>(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<ArrangeSettingsView::ArrangeStrategy>(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;
}

View File

@ -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; }

View File

@ -5,8 +5,15 @@
#ifndef ARRANGESETTINGSVIEW_HPP
#define ARRANGESETTINGSVIEW_HPP
#include <string_view>
#include <array>
#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<class EnumType, size_t N>
using EnumMap = StaticMap<std::string_view, EnumType, N>;
template<class EnumType, size_t N>
static constexpr std::optional<EnumType> get_enumval(std::string_view str,
const EnumMap<EnumType, N> &emap)
{
std::optional<EnumType> ret;
if (auto v = query(emap, str); v.has_value()) {
ret = *v;
}
return ret;
}
public:
static constexpr std::optional<GeometryHandling> to_geometry_handling(std::string_view str)
{
return get_enumval(str, GeometryHandlingLabels);
}
static constexpr std::optional<ArrangeStrategy> to_arrange_strategy(std::string_view str)
{
return get_enumval(str, ArrangeStrategyLabels);
}
static constexpr std::optional<XLPivots> to_xl_pivots(std::string_view str)
{
return get_enumval(str, XLPivotsLabels);
}
private:
static constexpr const auto GeometryHandlingLabels = make_staticmap<std::string_view, GeometryHandling>({
{"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<std::string_view, ArrangeStrategy>({
{"auto"sv, asAuto},
{"pulltocenter"sv, asPullToCenter},
{"0"sv, asAuto},
{"1"sv, asPullToCenter}
});
static constexpr const auto XLPivotsLabels = make_staticmap<std::string_view, XLPivots>({
{"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

View File

@ -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.");
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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.);
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();
}
}
}

View File

@ -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;
}

View File

@ -101,6 +101,7 @@ enum ConfigMenuIDs {
ConfigMenuLanguage,
ConfigMenuFlashFirmware,
ConfigMenuCnt,
ConfigMenuWifiConfigFile
};
class Tab;

View File

@ -838,5 +838,4 @@ void PhysicalPrinterDialog::DeletePreset(PresetForPrinter* preset_for_printer)
update_host_type(true);
}
}} // namespace Slic3r::GUI

View File

@ -105,7 +105,6 @@ protected:
};
} // namespace GUI
} // namespace Slic3r

View File

@ -1095,4 +1095,11 @@ void RemovableDriveManager::eject_thread_finish()
}
#endif // __APPLE__
std::vector<DriveData> RemovableDriveManager::get_drive_list()
{
{
std::lock_guard<std::mutex> guard(m_drives_mutex);
return m_current_drives;
}
}
}} // namespace Slic3r::GUI

View File

@ -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<DriveData> get_drive_list();
private:
bool m_initialized { false };
wxEvtHandler* m_callback_evt_handler { nullptr };

View File

@ -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 <wx/stattext.h>
#include <wx/button.h>
#include <boost/nowide/convert.hpp>
#include <boost/log/trivial.hpp>
#include <boost/filesystem.hpp>
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<DriveData> 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

View File

@ -0,0 +1,45 @@
#ifndef slic3r_WifiConfigDialog_hpp_
#define slic3r_WifiConfigDialog_hpp_
#include "GUI_Utils.hpp"
#include "../Utils/WifiScanner.hpp"
#include <wx/event.h>
#include <wx/dialog.h>
#include <wx/combobox.h>
#include <wx/textctrl.h>
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

View File

@ -0,0 +1,459 @@
#include "WifiScanner.hpp"
#include <boost/log/trivial.hpp>
#include <boost/nowide/system.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/filesystem.hpp>
#ifdef _WIN32
#include <windows.h>
#include <wlanapi.h>
#include <objbase.h>
#include <wtypes.h>
// 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 <dbus/dbus.h> /* 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<std::string>(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<std::string> 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

View File

@ -0,0 +1,40 @@
#ifndef slic3r_WifiScanner_hpp_
#define slic3r_WifiScanner_hpp_
#include <map>
#include <vector>
#include <string>
#include <wx/string.h>
namespace Slic3r {
typedef std::map<wxString, std::string> 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<std::string>& ssids);
std::string get_psk_mac(const std::string& ssid);
std::string get_current_ssid_mac();
void* m_impl_osx { nullptr };
#endif
};
} // Slic3r
#endif

View File

@ -0,0 +1,21 @@
#ifndef WiFiScanner_h
#define WiFiScanner_h
#import <Foundation/Foundation.h>
extern "C" {
#import <CoreWLAN/CoreWLAN.h>
}
@interface WifiScannerMac : NSObject
- (instancetype)init;
- (void)dealloc;
- (NSArray<NSString *> *)scan_ssids;
- (NSString *)retrieve_password_for_ssid:(NSString *)ssid;
- (NSString *)current_ssid;
@property (strong, nonatomic) CWInterface *wifiInterface;
@end
#endif /* WiFiScanner_h */

View File

@ -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<CWNetwork *> *networksSet = [self->_wifiInterface scanForNetworksWithName:nil error:&error];
if (error) {
return ssids;
}
NSArray<CWNetwork *> *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<std::string>& 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 {};
}
}