mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 03:35:52 +08:00
Merge branch 'master' into fs_svg_SPE-1517
This commit is contained in:
commit
86879c5d92
@ -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())
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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
|
||||
|
@ -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.");
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ enum ConfigMenuIDs {
|
||||
ConfigMenuLanguage,
|
||||
ConfigMenuFlashFirmware,
|
||||
ConfigMenuCnt,
|
||||
ConfigMenuWifiConfigFile
|
||||
};
|
||||
|
||||
class Tab;
|
||||
|
@ -838,5 +838,4 @@ void PhysicalPrinterDialog::DeletePreset(PresetForPrinter* preset_for_printer)
|
||||
|
||||
update_host_type(true);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
@ -105,7 +105,6 @@ protected:
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 };
|
||||
|
249
src/slic3r/GUI/WifiConfigDialog.cpp
Normal file
249
src/slic3r/GUI/WifiConfigDialog.cpp
Normal 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
|
45
src/slic3r/GUI/WifiConfigDialog.hpp
Normal file
45
src/slic3r/GUI/WifiConfigDialog.hpp
Normal 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
|
459
src/slic3r/Utils/WifiScanner.cpp
Normal file
459
src/slic3r/Utils/WifiScanner.cpp
Normal 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
|
40
src/slic3r/Utils/WifiScanner.hpp
Normal file
40
src/slic3r/Utils/WifiScanner.hpp
Normal 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
|
21
src/slic3r/Utils/WifiScannerMac.h
Normal file
21
src/slic3r/Utils/WifiScannerMac.h
Normal 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 */
|
94
src/slic3r/Utils/WifiScannerMac.mm
Normal file
94
src/slic3r/Utils/WifiScannerMac.mm
Normal 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 {};
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user