Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_transformations

This commit is contained in:
enricoturri1966 2023-03-31 16:10:36 +02:00
commit 3c8ecf5bd6
14 changed files with 513 additions and 201 deletions

View File

@ -1,7 +1,10 @@
contact_links:
- name: PrusaSlicer Manual and Support
- name: Do you need Support?
url: https://www.prusa3d.com/page/prusaslicer-support-form_233563/
about: If you are not sure whether what you are reporting is a bug, please contact our support team first. We are providing full 24/7 customer support.
- name: PrusaSlicer Manual
url: https://help.prusa3d.com/en/article/customer-support_2287/
about: If you are not sure that what you are reporting is really a bug, please, consult the manual first.
about: We have a comprehensive customer support page and help documentation that could be helpful for troubleshooting.
- name: PrusaPrinters Forum
url: https://forum.prusaprinters.org/forum/prusaslicer/
about: Please get in touch on our PrusaPrinters Community Forum! (Not an official support channel.)

View File

@ -113,8 +113,7 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const
//h^2 = (L / b)^2 [square it]
//h^2 = L^2 / b^2 [factor the divisor]
const auto height_2 = int64_t(double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2));
coord_t weighted_average_width;
const int64_t extrusion_area_error = calculateExtrusionAreaDeviationError(previous, current, next, weighted_average_width);
const int64_t extrusion_area_error = calculateExtrusionAreaDeviationError(previous, current, next);
if ((height_2 <= scaled<coord_t>(0.001) //Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current.p, previous.p, next.p) <= scaled<double>(0.001)) // Make sure that height_2 is not small because of cancellation of positive and negative areas
// We shouldn't remove middle junctions of colinear segments if the area changed for the C-P segment is exceeding the maximum allowed
@ -189,8 +188,7 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const
junctions = new_junctions;
}
int64_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C, coord_t& weighted_average_width)
{
int64_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C) {
/*
* A B C A C
* --------------- **************
@ -208,27 +206,19 @@ int64_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A,
* weighted-average width for the entire extrusion line.
*
* */
const int64_t ab_length = (B - A).cast<int64_t>().norm();
const int64_t bc_length = (C - B).cast<int64_t>().norm();
const coord_t width_diff = std::max(std::abs(B.w - A.w), std::abs(C.w - B.w));
if (width_diff > 1)
{
const int64_t ab_length = (B.p - A.p).cast<int64_t>().norm();
const int64_t bc_length = (C.p - B.p).cast<int64_t>().norm();
if (const coord_t width_diff = std::max(std::abs(B.w - A.w), std::abs(C.w - B.w)); width_diff > 1) {
// Adjust the width only if there is a difference, or else the rounding errors may produce the wrong
// weighted average value.
const int64_t ab_weight = (A.w + B.w) / 2;
const int64_t bc_weight = (B.w + C.w) / 2;
assert(((ab_length * ab_weight + bc_length * bc_weight) / (C - A).cast<int64_t>().norm()) <= std::numeric_limits<coord_t>::max());
weighted_average_width = (ab_length * ab_weight + bc_length * bc_weight) / (C - A).cast<int64_t>().norm();
assert((int64_t(std::abs(ab_weight - weighted_average_width)) * ab_length + int64_t(std::abs(bc_weight - weighted_average_width)) * bc_length) <= double(std::numeric_limits<int64_t>::max()));
return std::abs(ab_weight - weighted_average_width) * ab_length + std::abs(bc_weight - weighted_average_width) * bc_length;
}
else
{
const int64_t ab_weight = (A.w + B.w) / 2;
const int64_t bc_weight = (B.w + C.w) / 2;
const int64_t weighted_average_width = (ab_length * ab_weight + bc_length * bc_weight) / (ab_length + bc_length);
const int64_t ac_length = (C.p - A.p).cast<int64_t>().norm();
return std::abs((ab_weight * ab_length + bc_weight * bc_length) - (weighted_average_width * ac_length));
} else {
// If the width difference is very small, then select the width of the segment that is longer
weighted_average_width = ab_length > bc_length ? A.w : B.w;
assert((int64_t(width_diff) * int64_t(bc_length)) <= std::numeric_limits<coord_t>::max());
assert((int64_t(width_diff) * int64_t(ab_length)) <= std::numeric_limits<coord_t>::max());
return ab_length > bc_length ? width_diff * bc_length : width_diff * ab_length;
return ab_length > bc_length ? int64_t(width_diff) * bc_length : int64_t(width_diff) * ab_length;
}
}

View File

@ -186,9 +186,8 @@ struct ExtrusionLine
* \param A Start point of the 3-point-straight line
* \param B Intermediate point of the 3-point-straight line
* \param C End point of the 3-point-straight line
* \param weighted_average_width The weighted average of the widths of the two colinear extrusion segments
* */
static int64_t calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C, coord_t& weighted_average_width);
static int64_t calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C);
bool is_contour() const;

View File

@ -51,7 +51,7 @@ void remove_bad(ExPolygons &expolygons);
// helpr for heal shape
// Return true when erase otherwise false
bool remove_same_neighbor(Points &points);
bool remove_same_neighbor(Polygon &points);
bool remove_same_neighbor(Polygons &polygons);
bool remove_same_neighbor(ExPolygons &expolygons);
@ -272,14 +272,22 @@ void priv::remove_bad(ExPolygons &expolygons) {
remove_bad(expolygon.holes);
}
bool priv::remove_same_neighbor(Slic3r::Points &points)
bool priv::remove_same_neighbor(Slic3r::Polygon &polygon)
{
Points &points = polygon.points;
if (points.empty()) return false;
auto last = std::unique(points.begin(), points.end());
// remove first and last neighbor duplication
if (const Point& last_point = *(last - 1);
last_point == points.front()) {
--last;
}
// no duplicits
if (last == points.end()) return false;
points.erase(last, points.end());
// clear points without area
if (points.size() <= 2) points.clear();
return true;
}
@ -287,34 +295,30 @@ bool priv::remove_same_neighbor(Polygons &polygons) {
if (polygons.empty()) return false;
bool exist = false;
for (Polygon& polygon : polygons)
exist |= remove_same_neighbor(polygon.points);
exist |= remove_same_neighbor(polygon);
// remove empty polygons
polygons.erase(
std::remove_if(polygons.begin(), polygons.end(),
[](const Polygon &p) { return p.empty(); }),
[](const Polygon &p) { return p.points.size() <= 2; }),
polygons.end());
return exist;
}
bool priv::remove_same_neighbor(ExPolygons &expolygons) {
if(expolygons.empty()) return false;
bool exist = false;
bool remove_from_holes = false;
bool remove_from_contour = false;
for (ExPolygon &expoly : expolygons) {
exist |= remove_same_neighbor(expoly.contour.points);
Polygons &holes = expoly.holes;
for (Polygon &hole : holes)
exist |= remove_same_neighbor(hole.points);
holes.erase(
std::remove_if(holes.begin(), holes.end(),
[](const Polygon &p) { return p.size() < 3; }),
holes.end());
remove_from_contour |= remove_same_neighbor(expoly.contour);
remove_from_holes |= remove_same_neighbor(expoly.holes);
}
// Removing of point could create polygon with less than 3 points
if (exist)
remove_bad(expolygons);
return exist;
// Removing of expolygons without contour
if (remove_from_contour)
expolygons.erase(
std::remove_if(expolygons.begin(), expolygons.end(),
[](const ExPolygon &p) { return p.contour.points.size() <=2; }),
expolygons.end());
return remove_from_holes || remove_from_contour;
}
Points priv::collect_close_points(const ExPolygons &expolygons, double distance) {

View File

@ -438,6 +438,10 @@ void GCode::PlaceholderParserIntegration::reset()
this->opt_e_position = nullptr;
this->opt_e_retracted = nullptr;
this->opt_e_restart_extra = nullptr;
this->opt_extruded_volume = nullptr;
this->opt_extruded_weight = nullptr;
this->opt_extruded_volume_total = nullptr;
this->opt_extruded_weight_total = nullptr;
this->num_extruders = 0;
this->position.clear();
this->e_position.clear();
@ -463,6 +467,14 @@ void GCode::PlaceholderParserIntegration::init(const GCodeWriter &writer)
this->output_config.set_key_value("e_position", opt_e_position);
}
}
this->opt_extruded_volume = new ConfigOptionFloats(this->num_extruders, 0.f);
this->opt_extruded_weight = new ConfigOptionFloats(this->num_extruders, 0.f);
this->opt_extruded_volume_total = new ConfigOptionFloat(0.f);
this->opt_extruded_weight_total = new ConfigOptionFloat(0.f);
this->parser.set("extruded_volume", this->opt_extruded_volume);
this->parser.set("extruded_weight", this->opt_extruded_weight);
this->parser.set("extruded_volume_total", this->opt_extruded_volume_total);
this->parser.set("extruded_weight_total", this->opt_extruded_weight_total);
// Reserve buffer for current position.
this->position.assign(3, 0);
@ -484,10 +496,22 @@ void GCode::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWri
assert(! extruders.empty() && num_extruders == extruders.back().id() + 1);
this->e_retracted.assign(num_extruders, 0);
this->e_restart_extra.assign(num_extruders, 0);
this->opt_extruded_volume->values.assign(num_extruders, 0);
this->opt_extruded_weight->values.assign(num_extruders, 0);
double total_volume = 0.;
double total_weight = 0.;
for (const Extruder &e : extruders) {
this->e_retracted[e.id()] = e.retracted();
this->e_restart_extra[e.id()] = e.restart_extra();
double v = e.extruded_volume();
double w = v * e.filament_density() * 0.001;
this->opt_extruded_volume->values[e.id()] = v;
this->opt_extruded_weight->values[e.id()] = w;
total_volume += v;
total_weight += w;
}
opt_extruded_volume_total->value = total_volume;
opt_extruded_weight_total->value = total_weight;
opt_e_retracted->values = this->e_retracted;
opt_e_restart_extra->values = this->e_restart_extra;
if (! writer.config.use_relative_e_distances) {

View File

@ -362,6 +362,10 @@ private:
ConfigOptionFloats *opt_e_position { nullptr };
ConfigOptionFloats *opt_e_retracted { nullptr };
ConfigOptionFloats *opt_e_restart_extra { nullptr };
ConfigOptionFloats *opt_extruded_volume { nullptr };
ConfigOptionFloats *opt_extruded_weight { nullptr };
ConfigOptionFloat *opt_extruded_volume_total { nullptr };
ConfigOptionFloat *opt_extruded_weight_total { nullptr };
// Caches of the data passed to the script.
size_t num_extruders;
std::vector<double> position;

View File

@ -3508,6 +3508,17 @@ void GCodeProcessor::post_process()
ret += buf;
}
}
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = m_time_processor.machines[i];
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) {
char buf[128];
sprintf(buf, "; estimated first layer printing time (%s mode) = %s\n",
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()).c_str());
ret += buf;
}
}
}
}

View File

@ -1033,6 +1033,18 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
};
if (!first_overhang_is_closed_and_anchored) {
std::reverse(overhang_region.begin(), overhang_region.end());
} else {
size_t min_dist_idx = 0;
double min_dist = std::numeric_limits<double>::max();
for (size_t i = 0; i < overhang_region.front().polyline.size(); i++) {
Point p = overhang_region.front().polyline[i];
if (double d = lower_layer_aabb_tree.distance_from_lines<true>(p) < min_dist) {
min_dist = d;
min_dist_idx = i;
}
}
std::rotate(overhang_region.front().polyline.begin(), overhang_region.front().polyline.begin() + min_dist_idx,
overhang_region.front().polyline.end());
}
auto first_unanchored = std::stable_partition(overhang_region.begin(), overhang_region.end(), is_anchored);
int index_of_first_unanchored = first_unanchored - overhang_region.begin();

View File

@ -1651,7 +1651,8 @@ void PrintObject::bridge_over_infill()
unsupported_area = closing(unsupported_area, SCALED_EPSILON);
// By expanding the lower layer solids, we avoid making bridges from the tiny internal overhangs that are (very likely) supported by previous layer solids
// NOTE that we cannot filter out polygons worth bridging by their area, because sometimes there is a very small internal island that will grow into large hole
lower_layer_solids = expand(lower_layer_solids, 3 * spacing);
lower_layer_solids = shrink(lower_layer_solids, 1 * spacing); // first remove thin regions that will not support anything
lower_layer_solids = expand(lower_layer_solids, (1 + 3) * spacing); // then expand back (opening), and further for parts supported by internal solids
// By shrinking the unsupported area, we avoid making bridges from narrow ensuring region along perimeters.
unsupported_area = shrink(unsupported_area, 3 * spacing);
unsupported_area = diff(unsupported_area, lower_layer_solids);
@ -1825,23 +1826,28 @@ void PrintObject::bridge_over_infill()
std::map<double, int> counted_directions;
for (const Polygon &p : bridged_area) {
double acc_distance = 0;
for (int point_idx = 0; point_idx < int(p.points.size()) - 1; ++point_idx) {
Vec2d start = p.points[point_idx].cast<double>();
Vec2d next = p.points[point_idx + 1].cast<double>();
Vec2d v = next - start; // vector from next to current
double dist_to_next = v.norm();
v.normalize();
int lines_count = int(std::ceil(dist_to_next / scaled(3.0)));
float step_size = dist_to_next / lines_count;
for (int i = 0; i < lines_count; ++i) {
Point a = (start + v * (i * step_size)).cast<coord_t>();
auto [distance, index, p] = lines_tree.distance_from_lines_extra<false>(a);
double angle = lines_tree.get_line(index).orientation();
if (angle > PI) {
angle -= PI;
acc_distance += dist_to_next;
if (acc_distance > scaled(2.0)) {
acc_distance = 0.0;
v.normalize();
int lines_count = int(std::ceil(dist_to_next / scaled(2.0)));
float step_size = dist_to_next / lines_count;
for (int i = 0; i < lines_count; ++i) {
Point a = (start + v * (i * step_size)).cast<coord_t>();
auto [distance, index, p] = lines_tree.distance_from_lines_extra<false>(a);
double angle = lines_tree.get_line(index).orientation();
if (angle > PI) {
angle -= PI;
}
angle += PI * 0.5;
counted_directions[angle]++;
}
angle += PI * 0.5;
counted_directions[angle]++;
}
}
}
@ -2083,27 +2089,43 @@ void PrintObject::bridge_over_infill()
};
return a.min.x() < b.min.x();
});
if (surfaces_by_layer[lidx].size() > 2) {
Vec2d origin = get_extents(surfaces_by_layer[lidx].front().new_polys).max.cast<double>();
std::stable_sort(surfaces_by_layer[lidx].begin() + 1, surfaces_by_layer[lidx].end(),
[origin](const CandidateSurface &left, const CandidateSurface &right) {
auto a = get_extents(left.new_polys);
auto b = get_extents(right.new_polys);
return (origin - a.min.cast<double>()).squaredNorm() <
(origin - b.min.cast<double>()).squaredNorm();
});
}
// Gather deep infill areas, where thick bridges fit
coordf_t spacing = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).scaled_spacing();
coordf_t target_flow_height = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).height() * target_flow_height_factor;
Polygons deep_infill_area = gather_areas_w_depth(po, lidx, target_flow_height);
// Now also remove area that has been already filled on lower layers by bridging expansion - For this
// reason we did the clustering of layers per thread.
double bottom_z = layer->print_z - target_flow_height - EPSILON;
if (job_idx > 0) {
for (int lower_job_idx = job_idx - 1; lower_job_idx >= 0; lower_job_idx--) {
size_t lower_layer_idx = clustered_layers_for_threads[cluster_idx][lower_job_idx];
const Layer *lower_layer = po->get_layer(lower_layer_idx);
if (lower_layer->print_z >= bottom_z) {
for (const auto &c : surfaces_by_layer[lower_layer_idx]) {
deep_infill_area = diff(deep_infill_area, c.new_polys);
{
// Now also remove area that has been already filled on lower layers by bridging expansion - For this
// reason we did the clustering of layers per thread.
Polygons filled_polyons_on_lower_layers;
double bottom_z = layer->print_z - target_flow_height - EPSILON;
if (job_idx > 0) {
for (int lower_job_idx = job_idx - 1; lower_job_idx >= 0; lower_job_idx--) {
size_t lower_layer_idx = clustered_layers_for_threads[cluster_idx][lower_job_idx];
const Layer *lower_layer = po->get_layer(lower_layer_idx);
if (lower_layer->print_z >= bottom_z) {
for (const auto &c : surfaces_by_layer[lower_layer_idx]) {
filled_polyons_on_lower_layers.insert(filled_polyons_on_lower_layers.end(), c.new_polys.begin(),
c.new_polys.end());
}
} else {
break;
}
} else {
break;
}
}
deep_infill_area = diff(deep_infill_area, filled_polyons_on_lower_layers);
}
deep_infill_area = expand(deep_infill_area, spacing * 1.5);
@ -2123,9 +2145,8 @@ void PrintObject::bridge_over_infill()
Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing));
#ifdef DEBUG_BRIDGE_OVER_INFILL
debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" +
"_total_area",
to_lines(total_fill_area), to_lines(expansion_area), to_lines(deep_infill_area), to_lines(anchors));
debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_total_area",
to_lines(total_fill_area), to_lines(expansion_area), to_lines(deep_infill_area), to_lines(anchors));
#endif
@ -2186,6 +2207,7 @@ void PrintObject::bridge_over_infill()
}
bridging_area = opening(bridging_area, flow.scaled_spacing());
bridging_area = closing(bridging_area, flow.scaled_spacing());
bridging_area = intersection(bridging_area, limiting_area);
bridging_area = intersection(bridging_area, total_fill_area);
expansion_area = diff(expansion_area, bridging_area);
@ -2220,35 +2242,33 @@ void PrintObject::bridge_over_infill()
for (LayerRegion *region : layer->regions()) {
Surfaces new_surfaces;
SurfacesPtr internal_infills = region->m_fill_surfaces.filter_by_type(stInternal);
ExPolygons new_internal_infills = diff_ex(internal_infills, cut_from_infill);
for (const ExPolygon &ep : new_internal_infills) {
new_surfaces.emplace_back(*internal_infills.front(), ep);
}
SurfacesPtr internal_solids = region->m_fill_surfaces.filter_by_type(stInternalSolid);
for (const CandidateSurface &cs : surfaces_by_layer.at(lidx)) {
for (Surface &surface : region->m_fill_surfaces.surfaces) {
if (cs.original_surface == &surface) {
Surface tmp(surface, {});
for (const ExPolygon &expoly : diff_ex(surface.expolygon, cs.new_polys)) {
if (expoly.area() > region->flow(frSolidInfill).scaled_width() * scale_(4.0)) {
new_surfaces.emplace_back(tmp, expoly);
}
}
for (const Surface *surface : internal_solids) {
if (cs.original_surface == surface) {
Surface tmp{*surface, {}};
tmp.surface_type = stInternalBridge;
tmp.bridge_angle = cs.bridge_angle;
for (const ExPolygon &expoly : union_ex(cs.new_polys)) {
new_surfaces.emplace_back(tmp, expoly);
for (const ExPolygon &ep : union_ex(cs.new_polys)) {
new_surfaces.emplace_back(tmp, ep);
}
surface.clear();
} else if (surface.surface_type == stInternal) {
Surface tmp(surface, {});
for (const ExPolygon &expoly : diff_ex(surface.expolygon, cut_from_infill)) {
new_surfaces.emplace_back(tmp, expoly);
}
surface.clear();
break;
}
}
}
region->m_fill_surfaces.surfaces.insert(region->m_fill_surfaces.surfaces.end(), new_surfaces.begin(), new_surfaces.end());
region->m_fill_surfaces.surfaces.erase(std::remove_if(region->m_fill_surfaces.surfaces.begin(),
region->m_fill_surfaces.surfaces.end(),
[](const Surface &s) { return s.empty(); }),
region->m_fill_surfaces.surfaces.end());
ExPolygons new_internal_solids = diff_ex(internal_solids, cut_from_infill);
for (const ExPolygon &ep : new_internal_solids) {
new_surfaces.emplace_back(*internal_solids.front(), ep);
}
region->m_fill_surfaces.remove_types({stInternalSolid, stInternal});
region->m_fill_surfaces.append(new_surfaces);
}
}
});

View File

@ -31,7 +31,7 @@
#include "I18N.hpp"
#include <libnest2d/tools/benchmark.h>
#include "format.hpp"
namespace Slic3r {
@ -511,8 +511,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
if(slindex_it == po.m_slice_index.end())
//TRN To be shown at the status bar on SLA slicing error.
throw Slic3r::RuntimeError(
_u8L("Slicing had to be stopped due to an internal error: "
"Inconsistent slice index."));
format(_u8L("Model named: %s can not be sliced. Please check if the model is sane."), po.model_object()->name));
po.m_model_height_levels.clear();
po.m_model_height_levels.reserve(po.m_slice_index.size());

View File

@ -147,7 +147,7 @@ void Downloader::start_download(const std::string& full_url)
std::string escaped_url = FileGet::escape_url(full_url.substr(24));
#endif
if (!boost::starts_with(escaped_url, "https://") || !FileGet::is_subdomain(escaped_url, "printables.com")) {
std::string msg = format(_L("Download won't start. Download URL doesn't point to https://files.printables.com : %1%"), escaped_url);
std::string msg = format(_L("Download won't start. Download URL doesn't point to https://printables.com : %1%"), escaped_url);
BOOST_LOG_TRIVIAL(error) << msg;
NotificationManager* ntf_mngr = wxGetApp().notification_manager();
ntf_mngr->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::RegularNotificationLevel, msg);

View File

@ -22,6 +22,7 @@
#include "libslic3r/NSVGUtils.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Preset.hpp"
#include "libslic3r/ClipperUtils.hpp" // union_ex
#include "libslic3r/AppConfig.hpp" // store/load font list
#include "libslic3r/Format/OBJ.hpp" // load obj file for default object
@ -1326,7 +1327,7 @@ void GLGizmoEmboss::draw_text_input()
// show warning about incorrectness view of font
std::string warning_tool_tip;
if (!exist_font) {
warning_tool_tip = _u8L("Can't write text by selected font.Try to choose another font.");
warning_tool_tip = _u8L("The text cannot be written using the selected font. Please try choosing a different font.");
} else {
auto append_warning = [&warning_tool_tip](std::string t) {
if (!warning_tool_tip.empty())
@ -1335,15 +1336,15 @@ void GLGizmoEmboss::draw_text_input()
};
if (priv::is_text_empty(m_text))
append_warning(_u8L("Embossed text can NOT contain only white spaces."));
append_warning(_u8L("Embossed text cannot contain only white spaces."));
if (m_text_contain_unknown_glyph)
append_warning(_u8L("Text contain character glyph (represented by '?') unknown by font."));
append_warning(_u8L("Text contains character glyph (represented by '?') unknown by font."));
const FontProp &prop = m_style_manager.get_font_prop();
if (prop.skew.has_value()) append_warning(_u8L("Text input do not show font skew."));
if (prop.boldness.has_value()) append_warning(_u8L("Text input do not show font boldness."));
if (prop.skew.has_value()) append_warning(_u8L("Text input doesn't show font skew."));
if (prop.boldness.has_value()) append_warning(_u8L("Text input doesn't show font boldness."));
if (prop.line_gap.has_value())
append_warning(_u8L("Text input do not show gap between lines."));
append_warning(_u8L("Text input doesn't show gap between lines."));
auto &ff = m_style_manager.get_font_file_with_cache();
float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file, scale);
if (imgui_size > StyleManager::max_imgui_font_size)
@ -1959,7 +1960,7 @@ void GLGizmoEmboss::draw_font_list()
void GLGizmoEmboss::draw_model_type()
{
bool is_last_solid_part = m_volume->is_the_only_one_part();
std::string title = _u8L("Text is to object");
std::string title = _u8L("Operation");
if (is_last_solid_part) {
ImVec4 color{.5f, .5f, .5f, 1.f};
m_imgui->text_colored(color, title.c_str());
@ -1973,14 +1974,15 @@ void GLGizmoEmboss::draw_model_type()
ModelVolumeType part = ModelVolumeType::MODEL_PART;
ModelVolumeType type = m_volume->type();
if (ImGui::RadioButton(_u8L("Added").c_str(), type == part))
//TRN EmbossOperation
if (ImGui::RadioButton(_u8L("Join").c_str(), type == part))
new_type = part;
else if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _u8L("Click to change text into object part.").c_str());
ImGui::SameLine();
std::string last_solid_part_hint = _u8L("You can't change a type of the last solid part of the object.");
if (ImGui::RadioButton(_u8L("Subtracted").c_str(), type == negative))
if (ImGui::RadioButton(_CTX_utf8(L_CONTEXT("Cut", "EmbossOperation"), "EmbossOperation").c_str(), type == negative))
new_type = negative;
else if (ImGui::IsItemHovered()) {
if (is_last_solid_part)
@ -2187,7 +2189,7 @@ void GLGizmoEmboss::draw_style_add_button()
}else if (only_add_style) {
ImGui::SetTooltip("%s", _u8L("Add style to my list.").c_str());
} else {
ImGui::SetTooltip("%s", _u8L("Add as new named style.").c_str());
ImGui::SetTooltip("%s", _u8L("Save as new style.").c_str());
}
}
@ -2203,32 +2205,47 @@ void GLGizmoEmboss::draw_delete_style_button() {
bool is_last = m_style_manager.get_styles().size() == 1;
bool can_delete = is_stored && !is_last;
std::string title = _u8L("Remove style");
const char * popup_id = title.c_str();
static size_t next_style_index = std::numeric_limits<size_t>::max();
if (draw_button(m_icons, IconType::erase, !can_delete)) {
std::string style_name = m_style_manager.get_style().name; // copy
wxString dialog_title = _L("Remove style");
size_t next_style_index = std::numeric_limits<size_t>::max();
Plater *plater = wxGetApp().plater();
bool exist_change = false;
while (true) {
// NOTE: can't use previous loaded activ index -> erase could change index
size_t active_index = m_style_manager.get_style_index();
next_style_index = (active_index > 0) ? active_index - 1 :
active_index + 1;
if (next_style_index >= m_style_manager.get_styles().size()) {
// can't remove last font style
// TODO: inform user
MessageDialog msg(plater, _L("Can't remove the last exising style."), dialog_title, wxICON_ERROR | wxOK);
msg.ShowModal();
break;
}
// IMPROVE: add function can_load?
// clean unactivable styles
if (!m_style_manager.load_style(next_style_index)) {
m_style_manager.erase(next_style_index);
exist_change = true;
continue;
}
// load back
m_style_manager.load_style(active_index);
ImGui::OpenPopup(popup_id);
wxString message = GUI::format_wxstr(_L("Are you sure,\nthat you want permanently and unrecoverable \nremove \"%1%\" style?"), style_name);
MessageDialog msg(plater, message, dialog_title, wxICON_WARNING | wxYES | wxNO);
if (msg.ShowModal() == wxID_YES) {
// delete style
m_style_manager.erase(active_index);
exist_change = true;
process();
} else {
// load back style
m_style_manager.load_style(active_index);
}
break;
}
if (exist_change)
m_style_manager.store_styles_to_app_config(wxGetApp().app_config);
}
if (ImGui::IsItemHovered()) {
@ -2239,25 +2256,6 @@ void GLGizmoEmboss::draw_delete_style_button() {
else/*if(!is_stored)*/ tooltip = GUI::format(_L("Can't delete temporary style \"%1%\"."), style_name);
ImGui::SetTooltip("%s", tooltip.c_str());
}
if (ImGui::BeginPopupModal(popup_id)) {
m_imgui->disable_background_fadeout_animation();
const std::string &style_name = m_style_manager.get_style().name;
std::string text_in_popup = GUI::format(_L("Are you sure,\nthat you want permanently and unrecoverable \nremove style \"%1%\"?"), style_name);
ImGui::Text("%s", text_in_popup.c_str());
if (ImGui::Button(_u8L("Yes").c_str())) {
size_t active_index = m_style_manager.get_style_index();
m_style_manager.load_style(next_style_index);
m_style_manager.erase(active_index);
m_style_manager.store_styles_to_app_config(wxGetApp().app_config);
ImGui::CloseCurrentPopup();
process();
}
ImGui::SameLine();
if (ImGui::Button(_u8L("No").c_str()))
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
}
// FIX IT: it should not change volume position before successfull change
@ -2304,7 +2302,7 @@ void GLGizmoEmboss::draw_style_list() {
trunc_name = ImGuiWrapper::trunc(current_name, max_style_name_width);
}
std::string title = _u8L("Presets");
std::string title = _u8L("Styles");
if (m_style_manager.exist_stored_style())
ImGui::Text("%s", title.c_str());
else
@ -2313,7 +2311,7 @@ void GLGizmoEmboss::draw_style_list() {
ImGui::SetNextItemWidth(m_gui_cfg->input_width);
auto add_text_modify = [&is_modified](const std::string& name) {
if (!is_modified) return name;
return name + " (" + _u8L("modified") + ")";
return name + Preset::suffix_modified();
};
std::optional<size_t> selected_style_index;
if (ImGui::BeginCombo("##style_selector", add_text_modify(trunc_name).c_str())) {
@ -2846,17 +2844,17 @@ void GLGizmoEmboss::draw_advanced()
}
m_imgui->disabled_end(); // !can_use_surface
// TRN EmbossGizmo: font units
std::string units = _u8L("font points");
std::string units = _u8L("points");
std::string units_fmt = "%.0f " + units;
// input gap between letters
// input gap between characters
auto def_char_gap = stored_style ?
&stored_style->prop.char_gap : nullptr;
int half_ascent = font_info.ascent / 2;
int min_char_gap = -half_ascent, max_char_gap = half_ascent;
if (rev_slider(tr.char_gap, font_prop.char_gap, def_char_gap, _u8L("Revert gap between letters"),
min_char_gap, max_char_gap, units_fmt, _L("Distance between letters"))){
if (rev_slider(tr.char_gap, font_prop.char_gap, def_char_gap, _u8L("Revert gap between characters"),
min_char_gap, max_char_gap, units_fmt, _L("Distance between characters"))){
// Condition prevent recalculation when insertint out of limits value by imgui input
if (!priv::Limits::apply(font_prop.char_gap, priv::limits.char_gap) ||
!m_volume->text_configuration->style.prop.char_gap.has_value() ||
@ -2918,7 +2916,7 @@ void GLGizmoEmboss::draw_advanced()
m_imgui->disabled_begin(!allowe_surface_distance);
const std::string undo_move_tooltip = _u8L("Undo translation");
const wxString move_tooltip = _L("Distance of the center of text from model surface");
const wxString move_tooltip = _L("Distance of the center of the text to the model surface.");
bool is_moved = false;
bool use_inch = wxGetApp().app_config->get_bool("use_inches");
if (use_inch) {

View File

@ -212,49 +212,6 @@ static coord_t brim_offset(const PrintObject &po, const ModelInstance &inst)
return has_outer_brim ? scaled(brim_width + brim_separation) : 0;
}
template<class It>
Polygon support_layers_chull (Points &pts, It from_lyr, It to_lyr) {
size_t cap = 0;
for (auto it = from_lyr; it != to_lyr; ++it) {
for (const ExPolygon &expoly : (*it)->support_islands)
cap += expoly.contour.points.size();
}
pts.reserve(pts.size() + cap);
for (auto it = from_lyr; it != to_lyr; ++it) {
for (const ExPolygon &expoly : (*it)->support_islands)
std::copy(expoly.contour.begin(), expoly.contour.end(),
std::back_inserter(pts));
}
Polygon ret = Geometry::convex_hull(pts);
return ret;
}
static void update_arrangepoly_fffprint(arrangement::ArrangePolygon &ret,
const PrintObject &po,
const ModelInstance &inst)
{
auto laststep = po.last_completed_step();
coord_t infl = brim_offset(po, inst);
if (laststep < posCount && laststep > posSupportMaterial) {
Points pts = std::move(ret.poly.contour.points);
Polygon poly = support_layers_chull(pts,
po.support_layers().begin(),
po.support_layers().end());
ret.poly.contour = std::move(poly);
ret.poly.holes = {};
}
ret.inflation = infl;
}
arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi)
{
arrangement::ArrangePolygon ap = get_arrange_poly(mi, m_plater);
@ -442,7 +399,7 @@ arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst,
plater->fff_print().get_print_object_by_model_object_id(obj_id);
if (po) {
update_arrangepoly_fffprint(ap, *po, *inst);
ap.inflation = brim_offset(*po, *inst);
}
}

View File

@ -13,6 +13,11 @@
#include <Dbt.h>
#include <Setupapi.h>
#include <cfgmgr32.h>
#include <initguid.h> // include before devpropdef.h
#include <devpropdef.h>
#include <devpkey.h>
#include <usbioctl.h>
#else
// unix, linux & OSX includes
#include <errno.h>
@ -73,6 +78,287 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
}
namespace {
int eject_alt(const std::wstring& volume_access_path)
{
HANDLE handle = CreateFileW(volume_access_path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
BOOST_LOG_TRIVIAL(error) << "Alt Ejecting " << volume_access_path << " failed (handle == INVALID_HANDLE_VALUE): " << GetLastError();
return 1;
}
DWORD deviceControlRetVal(0);
//these 3 commands should eject device safely but they dont, the device does disappear from file explorer but the "device was safely remove" notification doesnt trigger.
//sd cards does trigger WM_DEVICECHANGE messege, usb drives dont
BOOL e1 = DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
BOOST_LOG_TRIVIAL(debug) << "FSCTL_LOCK_VOLUME " << e1 << " ; " << deviceControlRetVal << " ; " << GetLastError();
BOOL e2 = DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
BOOST_LOG_TRIVIAL(debug) << "FSCTL_DISMOUNT_VOLUME " << e2 << " ; " << deviceControlRetVal << " ; " << GetLastError();
// some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here with FALSE as third parameter, which should set PreventMediaRemoval
BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
if (error == 0) {
CloseHandle(handle);
BOOST_LOG_TRIVIAL(error) << "Alt Ejecting " << volume_access_path << " failed (IOCTL_STORAGE_EJECT_MEDIA)" << deviceControlRetVal << " " << GetLastError();
return 1;
}
CloseHandle(handle);
BOOST_LOG_TRIVIAL(info) << "Alt Ejecting finished";
return 0;
}
// From https://github.com/microsoft/Windows-driver-samples/tree/main/usb/usbview
typedef struct _STRING_DESCRIPTOR_NODE
{
struct _STRING_DESCRIPTOR_NODE* Next;
UCHAR DescriptorIndex;
USHORT LanguageID;
USB_STRING_DESCRIPTOR StringDescriptor[1];
} STRING_DESCRIPTOR_NODE, * PSTRING_DESCRIPTOR_NODE;
// Based at https://github.com/microsoft/Windows-driver-samples/tree/main/usb/usbview
PSTRING_DESCRIPTOR_NODE GetStringDescriptor(
HANDLE handle_hub_device,
ULONG connection_index,
UCHAR descriptor_index,
USHORT language_ID
)
{
BOOL success = 0;
ULONG nbytes = 0;
ULONG nbytes_returned = 0;
UCHAR string_desc_req_buf[sizeof(USB_DESCRIPTOR_REQUEST) + MAXIMUM_USB_STRING_LENGTH];
PUSB_DESCRIPTOR_REQUEST string_desc_req = NULL;
PUSB_STRING_DESCRIPTOR string_desc = NULL;
PSTRING_DESCRIPTOR_NODE string_desc_node = NULL;
nbytes = sizeof(string_desc_req_buf);
string_desc_req = (PUSB_DESCRIPTOR_REQUEST)string_desc_req_buf;
string_desc = (PUSB_STRING_DESCRIPTOR)(string_desc_req + 1);
// Zero fill the entire request structure
memset(string_desc_req, 0, nbytes);
// Indicate the port from which the descriptor will be requested
string_desc_req->ConnectionIndex = connection_index;
// USBHUB uses URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE to process this
// IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION request.
//
// USBD will automatically initialize these fields:
// bmRequest = 0x80
// bRequest = 0x06
//
// We must inititialize these fields:
// wValue = Descriptor Type (high) and Descriptor Index (low byte)
// wIndex = Zero (or Language ID for String Descriptors)
// wLength = Length of descriptor buffer
string_desc_req->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8)
| descriptor_index;
string_desc_req->SetupPacket.wIndex = language_ID;
string_desc_req->SetupPacket.wLength = (USHORT)(nbytes - sizeof(USB_DESCRIPTOR_REQUEST));
// Now issue the get descriptor request.
success = DeviceIoControl(handle_hub_device,
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
string_desc_req,
nbytes,
string_desc_req,
nbytes,
&nbytes_returned,
NULL);
// Do some sanity checks on the return from the get descriptor request.
if (!success) {
return NULL;
}
if (nbytes_returned < 2) {
return NULL;
}
if (string_desc->bDescriptorType != USB_STRING_DESCRIPTOR_TYPE) {
return NULL;
}
if (string_desc->bLength != nbytes_returned - sizeof(USB_DESCRIPTOR_REQUEST)) {
return NULL;
}
if (string_desc->bLength % 2 != 0) {
return NULL;
}
// Looks good, allocate some (zero filled) space for the string descriptor
// node and copy the string descriptor to it.
string_desc_node = (PSTRING_DESCRIPTOR_NODE)malloc(sizeof(STRING_DESCRIPTOR_NODE) + string_desc->bLength * sizeof(DWORD));
if (string_desc_node == NULL) {
return NULL;
}
string_desc_node->Next = NULL;
string_desc_node->DescriptorIndex = descriptor_index;
string_desc_node->LanguageID = language_ID;
memcpy(string_desc_node->StringDescriptor,
string_desc,
string_desc->bLength);
return string_desc_node;
}
// Based at https://github.com/microsoft/Windows-driver-samples/tree/main/usb/usbview
HRESULT GetStringDescriptors(
_In_ HANDLE handle_hub_device,
_In_ ULONG connection_index,
_In_ UCHAR descriptor_index,
_In_ ULONG num_language_IDs,
_In_reads_(num_language_IDs) USHORT* language_IDs,
_In_ PSTRING_DESCRIPTOR_NODE string_desc_node_head,
std::wstring& result
)
{
PSTRING_DESCRIPTOR_NODE tail = NULL;
PSTRING_DESCRIPTOR_NODE trailing = NULL;
ULONG i = 0;
// Go to the end of the linked list, searching for the requested index to
// see if we've already retrieved it
for (tail = string_desc_node_head; tail != NULL; tail = tail->Next) {
if (tail->DescriptorIndex == descriptor_index) {
// copy string descriptor to result
for(int i = 0; i < tail->StringDescriptor->bLength / 2 - 1; i++) {
result += tail->StringDescriptor->bString[i];
}
return S_OK;
}
trailing = tail;
}
tail = trailing;
// Get the next String Descriptor. If this is NULL, then we're done (return)
// Otherwise, loop through all Language IDs
for (i = 0; (tail != NULL) && (i < num_language_IDs); i++) {
tail->Next = GetStringDescriptor(handle_hub_device,
connection_index,
descriptor_index,
language_IDs[i]);
tail = tail->Next;
}
if (tail == NULL) {
return E_FAIL;
} else {
// copy string descriptor to result
for (int i = 0; i < tail->StringDescriptor->bLength / 2 - 1; i++) {
result += tail->StringDescriptor->bString[i];
}
return S_OK;
}
}
bool get_handle_from_devinst(DEVINST devinst, HANDLE& handle)
{
// create path consisting of device id and guid
wchar_t device_id[MAX_PATH];
CM_Get_Device_ID(devinst, device_id, MAX_PATH, 0);
//convert device id string to device path - https://stackoverflow.com/a/32641140/981766
std::wstring dev_id_wstr(device_id);
dev_id_wstr = std::regex_replace(dev_id_wstr, std::wregex(LR"(\\)"), L"#"); // '\' is special for regex
dev_id_wstr = std::regex_replace(dev_id_wstr, std::wregex(L"^"), LR"(\\?\)", std::regex_constants::format_first_only);
dev_id_wstr = std::regex_replace(dev_id_wstr, std::wregex(L"$"), L"#", std::regex_constants::format_first_only);
// guid
wchar_t guid_wchar[64];//guid is 32 chars+4 hyphens+2 paranthesis+null => 64 should be more than enough
StringFromGUID2(GUID_DEVINTERFACE_USB_HUB, guid_wchar, 64);
dev_id_wstr.append(guid_wchar);
// get handle
std::wstring& usb_hub_path = dev_id_wstr;
handle = CreateFileW(usb_hub_path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (handle == INVALID_HANDLE_VALUE) {
// Sometimes device is not GUID_DEVINTERFACE_USB_HUB, than we need to check parent recursively
DEVINST parent_devinst = 0;
if (CM_Get_Parent(&parent_devinst, devinst, 0) != CR_SUCCESS)
return false;
return get_handle_from_devinst(parent_devinst, handle);
}
return true;
}
// Read Configuration Descriptor - configuration string indexed by iConfiguration and decide if card reader
bool is_card_reader(HDEVINFO h_dev_info, SP_DEVINFO_DATA& spdd)
{
// First get port number of device.
DEVINST parent_devinst = 0;
HANDLE handle; // usb hub handle
DWORD usb_port_number = 0;
DWORD required_size = 0;
// First we need handle for GUID_DEVINTERFACE_USB_HUB device.
if (CM_Get_Parent(&parent_devinst, spdd.DevInst, 0) != CR_SUCCESS) {
BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: Couldn't get parent DEVINST.";
return false;
}
if(!get_handle_from_devinst(parent_devinst, handle) || handle == INVALID_HANDLE_VALUE) {
BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: Couldn't get HANDLE for parent DEVINST.";
return false;
}
// Get port number to which the usb device is attached on the hub.
if (SetupDiGetDeviceRegistryProperty(h_dev_info, &spdd, SPDRP_ADDRESS, nullptr, (PBYTE)&usb_port_number, sizeof(usb_port_number), &required_size) == 0) {
BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: Couldn't get port number.";
return false;
}
// Fill USB request packet to get iConfiguration value.
int buffer_size = sizeof(USB_DESCRIPTOR_REQUEST) + sizeof(USB_CONFIGURATION_DESCRIPTOR);
BYTE* buffer = new BYTE[buffer_size];
USB_DESCRIPTOR_REQUEST* request_packet = (USB_DESCRIPTOR_REQUEST*)buffer;
USB_CONFIGURATION_DESCRIPTOR* configuration_descriptor = (USB_CONFIGURATION_DESCRIPTOR*)((BYTE*)buffer + sizeof(USB_DESCRIPTOR_REQUEST));
DWORD bytes_returned = 0;
// Fill information in packet.
request_packet->SetupPacket.bmRequest = 0x80;
request_packet->SetupPacket.bRequest = USB_REQUEST_GET_CONFIGURATION;
request_packet->ConnectionIndex = usb_port_number;
request_packet->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8 | 0 /*Since only 1 device descriptor => index : 0*/);
request_packet->SetupPacket.wLength = sizeof(USB_CONFIGURATION_DESCRIPTOR);
// Issue ioctl.
if (DeviceIoControl(handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, buffer, buffer_size, buffer, buffer_size, &bytes_returned, nullptr) == 0) {
BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: Couldn't get Configuration Descriptor.";
return false;
}
// Nothing to read.
if (configuration_descriptor->iConfiguration == 0) {
BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: iConfiguration value is 0.";
return false;
}
// Get string descriptor and read string on address given by iConfiguration index .
// Based at https://github.com/microsoft/Windows-driver-samples/tree/main/usb/usbview
PSTRING_DESCRIPTOR_NODE supported_languages_string = NULL;
ULONG num_language_IDs = 0;
USHORT* language_IDs = NULL;
std::wstring configuration_string;
// Get languages.
supported_languages_string = GetStringDescriptor(handle, usb_port_number, 0, 0);
if (supported_languages_string == NULL) {
BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: Couldn't get language string descriptor.";
return false;
}
num_language_IDs = (supported_languages_string->StringDescriptor->bLength - 2) / 2;
language_IDs = (USHORT*)&supported_languages_string->StringDescriptor->bString[0];
// Get configration string.
if (GetStringDescriptors(handle, usb_port_number, configuration_descriptor->iConfiguration, num_language_IDs, language_IDs, supported_languages_string, configuration_string) == E_FAIL) {
BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: Couldn't get configuration string descriptor.";
return false;
}
// Final compare.
BOOST_LOG_TRIVIAL(error) << "Ejecting information: Retrieved configuration string: " << configuration_string;
if (configuration_string.find(L"CARD READER") != std::wstring::npos) {
BOOST_LOG_TRIVIAL(info) << "Detected external reader.";
return true;
}
return false;
}
// returns the device instance handle of a storage volume or 0 on error
// called from eject_inner, based on https://stackoverflow.com/a/58848961
DEVINST get_dev_inst_by_device_number(long device_number, UINT drive_type, WCHAR* dos_device_name)
@ -80,7 +366,7 @@ DEVINST get_dev_inst_by_device_number(long device_number, UINT drive_type, WCHAR
bool is_floppy = (wcsstr(dos_device_name, L"\\Floppy") != NULL); // TODO: could be tested better?
if (drive_type != DRIVE_REMOVABLE || is_floppy) {
BOOST_LOG_TRIVIAL(debug) << "get_dev_inst_by_device_number failed: Drive is not removable.";
BOOST_LOG_TRIVIAL(warning) << "get_dev_inst_by_device_number failed: Drive is not removable.";
return 0;
}
@ -89,7 +375,7 @@ DEVINST get_dev_inst_by_device_number(long device_number, UINT drive_type, WCHAR
HDEVINFO h_dev_info = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (h_dev_info == INVALID_HANDLE_VALUE) {
BOOST_LOG_TRIVIAL(debug) << "get_dev_inst_by_device_number failed: Invalid dev info handle.";
BOOST_LOG_TRIVIAL(warning) << "get_dev_inst_by_device_number failed: Invalid dev info handle.";
return 0;
}
@ -135,13 +421,16 @@ DEVINST get_dev_inst_by_device_number(long device_number, UINT drive_type, WCHAR
if (device_number != (long)sdn.DeviceNumber) {
continue;
}
// this is the drive, return the device instance
// check if is sd card reader - if yes, indicate by returning invalid value.
bool reader = is_card_reader(h_dev_info, spdd);
SetupDiDestroyDeviceInfoList(h_dev_info);
return spdd.DevInst;
return !reader ? spdd.DevInst : 0;
}
SetupDiDestroyDeviceInfoList(h_dev_info);
BOOST_LOG_TRIVIAL(debug) << "get_dev_inst_by_device_number failed: Enmurating couldn't find the drive.";
BOOST_LOG_TRIVIAL(warning) << "get_dev_inst_by_device_number failed: Enmurating couldn't find the drive.";
return 0;
}
@ -194,10 +483,9 @@ int eject_inner(const std::string& path)
// get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
DEVINST dev_inst = get_dev_inst_by_device_number(device_number, drive_type, dos_device_name);
if (dev_inst == 0) {
BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Invalid device instance handle.", path);
return 1;
BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1%: Invalid device instance handle. Going to try alternative ejecting method.", path);
return eject_alt(volume_access_path);
}
PNP_VETO_TYPE veto_type = PNP_VetoTypeUnknown;
@ -249,7 +537,7 @@ int eject_inner(const std::string& path)
return 1;
}
}
} // namespace
// Called from UI therefore it blocks the UI thread.
// It also blocks updates at the worker thread.
// Win32 implementation.
@ -268,18 +556,21 @@ void RemovableDriveManager::eject_drive()
if (it_drive_data != m_current_drives.end()) {
if (!eject_inner(m_last_save_path)) {
// success
assert(m_callback_evt_handler);
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(*it_drive_data), true)));
BOOST_LOG_TRIVIAL(info) << "Ejecting has succeeded.";
assert(m_callback_evt_handler);
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(*it_drive_data), true)));
} else {
// failed to eject
// this should not happen, throwing exception might be the way here
BOOST_LOG_TRIVIAL(error) << "Ejecting has failed.";
assert(m_callback_evt_handler);
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false)));
}
} else {
// drive not found in m_current_drives
BOOST_LOG_TRIVIAL(error) << "Ejecting has failed. Drive not found in m_current_drives.";
assert(m_callback_evt_handler);
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>({"",""}, false)));