Merge remote-tracking branch 'origin/master' into ys_improvements
This commit is contained in:
commit
380fffd248
61 changed files with 3004 additions and 2239 deletions
|
@ -101,8 +101,7 @@ void BackgroundSlicingProcess::process_fff()
|
|||
//FIXME localize the messages
|
||||
// Perform the final post-processing of the export path by applying the print statistics over the file name.
|
||||
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
|
||||
bool with_check = GUI::wxGetApp().removable_drive_manager()->is_path_on_removable_drive(export_path);
|
||||
int copy_ret_val = copy_file(m_temp_output_path, export_path, with_check);
|
||||
int copy_ret_val = copy_file(m_temp_output_path, export_path, m_export_path_on_removable_media);
|
||||
switch (copy_ret_val) {
|
||||
case SUCCESS: break; // no error
|
||||
case FAIL_COPY_FILE:
|
||||
|
@ -402,7 +401,7 @@ void BackgroundSlicingProcess::set_task(const PrintBase::TaskParams ¶ms)
|
|||
}
|
||||
|
||||
// Set the output path of the G-code.
|
||||
void BackgroundSlicingProcess::schedule_export(const std::string &path)
|
||||
void BackgroundSlicingProcess::schedule_export(const std::string &path, bool export_path_on_removable_media)
|
||||
{
|
||||
assert(m_export_path.empty());
|
||||
if (! m_export_path.empty())
|
||||
|
@ -412,6 +411,7 @@ void BackgroundSlicingProcess::schedule_export(const std::string &path)
|
|||
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
||||
this->invalidate_step(bspsGCodeFinalize);
|
||||
m_export_path = path;
|
||||
m_export_path_on_removable_media = export_path_on_removable_media;
|
||||
}
|
||||
|
||||
void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job)
|
||||
|
@ -432,6 +432,7 @@ void BackgroundSlicingProcess::reset_export()
|
|||
assert(! this->running());
|
||||
if (! this->running()) {
|
||||
m_export_path.clear();
|
||||
m_export_path_on_removable_media = false;
|
||||
// invalidate_step expects the mutex to be locked.
|
||||
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
||||
this->invalidate_step(bspsGCodeFinalize);
|
||||
|
|
|
@ -98,7 +98,7 @@ public:
|
|||
|
||||
// Set the export path of the G-code.
|
||||
// Once the path is set, the G-code
|
||||
void schedule_export(const std::string &path);
|
||||
void schedule_export(const std::string &path, bool export_path_on_removable_media);
|
||||
// Set print host upload job data to be enqueued to the PrintHostJobQueue
|
||||
// after current print slicing is complete
|
||||
void schedule_upload(Slic3r::PrintHostJob upload_job);
|
||||
|
@ -157,13 +157,14 @@ private:
|
|||
GCodePreviewData *m_gcode_preview_data = nullptr;
|
||||
#if ENABLE_THUMBNAIL_GENERATOR
|
||||
// Callback function, used to write thumbnails into gcode.
|
||||
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
|
||||
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
|
||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||
// Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID.
|
||||
std::string m_temp_output_path;
|
||||
// Output path provided by the user. The output path may be set even if the slicing is running,
|
||||
// but once set, it cannot be re-set.
|
||||
std::string m_export_path;
|
||||
bool m_export_path_on_removable_media = false;
|
||||
// Print host upload job to schedule after slicing is complete, used by schedule_upload(),
|
||||
// empty by default (ie. no upload to schedule)
|
||||
PrintHostJob m_upload_job;
|
||||
|
|
|
@ -188,7 +188,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
|
|||
|
||||
wxBitmap bitmap;
|
||||
int bitmap_width = 0;
|
||||
const wxString bitmap_file = GUI::from_u8(Slic3r::var((boost::format("printers/%1%_%2%.png") % vendor.id % model.id).str()));
|
||||
const wxString bitmap_file = GUI::from_u8(Slic3r::resources_dir() + "/profiles/" + vendor.id + "/" + model.id + "_thumbnail.png");
|
||||
if (wxFileExists(bitmap_file)) {
|
||||
bitmap.LoadFile(bitmap_file, wxBITMAP_TYPE_PNG);
|
||||
bitmap_width = bitmap.GetWidth();
|
||||
|
|
|
@ -663,7 +663,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
|
|||
if (it != m_warnings.end()) // this warning is already set to be shown
|
||||
return;
|
||||
|
||||
m_warnings.push_back(warning);
|
||||
m_warnings.emplace_back(warning);
|
||||
std::sort(m_warnings.begin(), m_warnings.end());
|
||||
}
|
||||
else {
|
||||
|
@ -1289,7 +1289,7 @@ void GLCanvas3D::Labels::render(const std::vector<const ModelInstance*>& sorted_
|
|||
if (model_object->instances.size() > 1)
|
||||
owner.label += " (" + std::to_string(inst_idx + 1) + ")";
|
||||
owner.selected = volume->selected;
|
||||
owners.push_back(owner);
|
||||
owners.emplace_back(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2029,7 +2029,7 @@ std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int ob
|
|||
{
|
||||
for (unsigned int i = 0; i < model_object.instances.size(); ++i)
|
||||
{
|
||||
instance_idxs.push_back(i);
|
||||
instance_idxs.emplace_back(i);
|
||||
}
|
||||
}
|
||||
return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_initialized);
|
||||
|
@ -2469,9 +2469,9 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio
|
|||
|
||||
for (const GCodePreviewData::Retraction::Position& position : copy)
|
||||
{
|
||||
volume->print_zs.push_back(unscale<double>(position.position(2)));
|
||||
volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
|
||||
volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size());
|
||||
volume->print_zs.emplace_back(unscale<double>(position.position(2)));
|
||||
volume->offsets.emplace_back(volume->indexed_vertex_array.quad_indices.size());
|
||||
volume->offsets.emplace_back(volume->indexed_vertex_array.triangle_indices.size());
|
||||
|
||||
_3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
|
||||
|
||||
|
@ -4109,7 +4109,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool
|
|||
if (!vol->is_modifier && !vol->is_wipe_tower && (!parts_only || (vol->composite_id.volume_id >= 0)))
|
||||
{
|
||||
if (!printable_only || is_visible(*vol))
|
||||
visible_volumes.push_back(vol);
|
||||
visible_volumes.emplace_back(vol);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4813,7 +4813,7 @@ void GLCanvas3D::_picking_pass() const
|
|||
}
|
||||
if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size()))
|
||||
{
|
||||
m_hover_volume_idxs.push_back(volume_id);
|
||||
m_hover_volume_idxs.emplace_back(volume_id);
|
||||
m_gizmos.set_hover_id(-1);
|
||||
}
|
||||
else
|
||||
|
@ -5057,7 +5057,7 @@ void GLCanvas3D::_render_overlays() const
|
|||
if (sequential_print) {
|
||||
for (ModelObject* model_object : m_model->objects)
|
||||
for (ModelInstance* model_instance : model_object->instances) {
|
||||
sorted_instances.push_back(model_instance);
|
||||
sorted_instances.emplace_back(model_instance);
|
||||
}
|
||||
}
|
||||
m_labels.render(sorted_instances);
|
||||
|
@ -5515,29 +5515,26 @@ void GLCanvas3D::_load_print_toolpaths()
|
|||
if ((skirt_height == 0) && (print->config().brim_width.value > 0))
|
||||
skirt_height = 1;
|
||||
|
||||
// get first skirt_height layers (maybe this should be moved to a PrintObject method?)
|
||||
const PrintObject* object0 = print->objects().front();
|
||||
// Get first skirt_height layers.
|
||||
//FIXME This code is fishy. It may not work for multiple objects with different layering due to variable layer height feature.
|
||||
// This is not critical as this is just an initial preview.
|
||||
const PrintObject* highest_object = *std::max_element(print->objects().begin(), print->objects().end(), [](auto l, auto r){ return l->layers().size() < r->layers().size(); });
|
||||
std::vector<float> print_zs;
|
||||
print_zs.reserve(skirt_height * 2);
|
||||
for (size_t i = 0; i < std::min(skirt_height, object0->layers().size()); ++i)
|
||||
{
|
||||
print_zs.push_back(float(object0->layers()[i]->print_z));
|
||||
}
|
||||
//FIXME why there are support layers?
|
||||
for (size_t i = 0; i < std::min(skirt_height, object0->support_layers().size()); ++i)
|
||||
{
|
||||
print_zs.push_back(float(object0->support_layers()[i]->print_z));
|
||||
}
|
||||
for (size_t i = 0; i < std::min(skirt_height, highest_object->layers().size()); ++ i)
|
||||
print_zs.emplace_back(float(highest_object->layers()[i]->print_z));
|
||||
// Only add skirt for the raft layers.
|
||||
for (size_t i = 0; i < std::min(skirt_height, std::min(highest_object->slicing_parameters().raft_layers(), highest_object->support_layers().size())); ++ i)
|
||||
print_zs.emplace_back(float(highest_object->support_layers()[i]->print_z));
|
||||
sort_remove_duplicates(print_zs);
|
||||
if (print_zs.size() > skirt_height)
|
||||
print_zs.erase(print_zs.begin() + skirt_height, print_zs.end());
|
||||
|
||||
skirt_height = std::min(skirt_height, print_zs.size());
|
||||
print_zs.erase(print_zs.begin() + skirt_height, print_zs.end());
|
||||
|
||||
GLVolume *volume = m_volumes.new_toolpath_volume(color, VERTEX_BUFFER_RESERVE_SIZE);
|
||||
for (size_t i = 0; i < skirt_height; ++i) {
|
||||
volume->print_zs.push_back(print_zs[i]);
|
||||
volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
|
||||
volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size());
|
||||
for (size_t i = 0; i < skirt_height; ++ i) {
|
||||
volume->print_zs.emplace_back(print_zs[i]);
|
||||
volume->offsets.emplace_back(volume->indexed_vertex_array.quad_indices.size());
|
||||
volume->offsets.emplace_back(volume->indexed_vertex_array.triangle_indices.size());
|
||||
if (i == 0)
|
||||
_3DScene::extrusionentity_to_verts(print->brim(), print_zs[i], Point(0, 0), *volume);
|
||||
_3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), *volume);
|
||||
|
@ -5703,10 +5700,10 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
|
|||
}
|
||||
if (ctxt.has_perimeters || ctxt.has_infill)
|
||||
for (const Layer *layer : print_object.layers())
|
||||
ctxt.layers.push_back(layer);
|
||||
ctxt.layers.emplace_back(layer);
|
||||
if (ctxt.has_support)
|
||||
for (const Layer *layer : print_object.support_layers())
|
||||
ctxt.layers.push_back(layer);
|
||||
ctxt.layers.emplace_back(layer);
|
||||
std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; });
|
||||
|
||||
// Maximum size of an allocation block: 32MB / sizeof(float)
|
||||
|
@ -5775,9 +5772,9 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
|
|||
|
||||
for (GLVolume *vol : vols)
|
||||
if (vol->print_zs.empty() || vol->print_zs.back() != layer->print_z) {
|
||||
vol->print_zs.push_back(layer->print_z);
|
||||
vol->offsets.push_back(vol->indexed_vertex_array.quad_indices.size());
|
||||
vol->offsets.push_back(vol->indexed_vertex_array.triangle_indices.size());
|
||||
vol->print_zs.emplace_back(layer->print_z);
|
||||
vol->offsets.emplace_back(vol->indexed_vertex_array.quad_indices.size());
|
||||
vol->offsets.emplace_back(vol->indexed_vertex_array.triangle_indices.size());
|
||||
}
|
||||
for (const PrintInstance &instance : *ctxt.shifted_copies) {
|
||||
const Point © = instance.shift;
|
||||
|
@ -5933,9 +5930,9 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
|
|||
for (size_t i = 0; i < vols.size(); ++i) {
|
||||
GLVolume &vol = *vols[i];
|
||||
if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
|
||||
vol.print_zs.push_back(layer.front().print_z);
|
||||
vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
|
||||
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
|
||||
vol.print_zs.emplace_back(layer.front().print_z);
|
||||
vol.offsets.emplace_back(vol.indexed_vertex_array.quad_indices.size());
|
||||
vol.offsets.emplace_back(vol.indexed_vertex_array.triangle_indices.size());
|
||||
}
|
||||
}
|
||||
for (const WipeTower::ToolChangeResult &extrusions : layer) {
|
||||
|
@ -6148,9 +6145,9 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
|
|||
assert(it_filter != filters.end() && key.first == it_filter->first);
|
||||
|
||||
GLVolume& vol = *it_filter->second;
|
||||
vol.print_zs.push_back(layer.z);
|
||||
vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
|
||||
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
|
||||
vol.print_zs.emplace_back(layer.z);
|
||||
vol.offsets.emplace_back(vol.indexed_vertex_array.quad_indices.size());
|
||||
vol.offsets.emplace_back(vol.indexed_vertex_array.triangle_indices.size());
|
||||
|
||||
_3DScene::extrusionentity_to_verts(path.polyline, path.width, path.height, layer.z, vol);
|
||||
}
|
||||
|
@ -6222,9 +6219,9 @@ inline void travel_paths_internal(
|
|||
assert(it != by_type.end() && it->first == func_value(polyline));
|
||||
|
||||
GLVolume& vol = *it->second;
|
||||
vol.print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2)));
|
||||
vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
|
||||
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
|
||||
vol.print_zs.emplace_back(unscale<double>(polyline.polyline.bounding_box().min(2)));
|
||||
vol.offsets.emplace_back(vol.indexed_vertex_array.quad_indices.size());
|
||||
vol.offsets.emplace_back(vol.indexed_vertex_array.triangle_indices.size());
|
||||
|
||||
_3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, vol);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#include <wx/stdpaths.h>
|
||||
#include <wx/imagpng.h>
|
||||
|
@ -50,6 +51,7 @@
|
|||
|
||||
#ifdef __WXMSW__
|
||||
#include <Shlobj.h>
|
||||
#include <dbt.h>
|
||||
#endif // __WXMSW__
|
||||
|
||||
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
|
||||
|
@ -60,6 +62,7 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class MainFrame;
|
||||
|
||||
wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
||||
{
|
||||
|
@ -96,9 +99,9 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
|||
|
||||
static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); }
|
||||
|
||||
static void register_dpi_event()
|
||||
{
|
||||
#ifdef WIN32
|
||||
static void register_win32_dpi_event()
|
||||
{
|
||||
enum { WM_DPICHANGED_ = 0x02e0 };
|
||||
|
||||
wxWindow::MSWRegisterMessageHandler(WM_DPICHANGED_, [](wxWindow *win, WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) {
|
||||
|
@ -111,9 +114,52 @@ static void register_dpi_event()
|
|||
|
||||
return true;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 };
|
||||
|
||||
static void register_win32_device_notification_event()
|
||||
{
|
||||
enum { WM_DPICHANGED_ = 0x02e0 };
|
||||
|
||||
wxWindow::MSWRegisterMessageHandler(WM_DEVICECHANGE, [](wxWindow *win, WXUINT /* nMsg */, WXWPARAM wParam, WXLPARAM lParam) {
|
||||
// Some messages are sent to top level windows by default, some messages are sent to only registered windows, and we explictely register on MainFrame only.
|
||||
auto main_frame = dynamic_cast<MainFrame*>(win);
|
||||
auto plater = (main_frame == nullptr) ? nullptr : main_frame->plater();
|
||||
if (plater == nullptr)
|
||||
// Maybe some other top level window like a dialog or maybe a pop-up menu?
|
||||
return true;
|
||||
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
|
||||
switch (wParam) {
|
||||
case DBT_DEVICEARRIVAL:
|
||||
if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
|
||||
plater->GetEventHandler()->AddPendingEvent(VolumeAttachedEvent(EVT_VOLUME_ATTACHED));
|
||||
else if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
|
||||
PDEV_BROADCAST_DEVICEINTERFACE lpdbi = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
|
||||
// if (lpdbi->dbcc_classguid == GUID_DEVINTERFACE_VOLUME) {
|
||||
// printf("DBT_DEVICEARRIVAL %d - Media has arrived: %ws\n", msg_count, lpdbi->dbcc_name);
|
||||
if (lpdbi->dbcc_classguid == GUID_DEVINTERFACE_HID)
|
||||
plater->GetEventHandler()->AddPendingEvent(HIDDeviceAttachedEvent(EVT_HID_DEVICE_ATTACHED, boost::nowide::narrow(lpdbi->dbcc_name)));
|
||||
}
|
||||
break;
|
||||
case DBT_DEVICEREMOVECOMPLETE:
|
||||
if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
|
||||
plater->GetEventHandler()->AddPendingEvent(VolumeDetachedEvent(EVT_VOLUME_DETACHED));
|
||||
else if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
|
||||
PDEV_BROADCAST_DEVICEINTERFACE lpdbi = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
|
||||
// if (lpdbi->dbcc_classguid == GUID_DEVINTERFACE_VOLUME)
|
||||
// printf("DBT_DEVICEARRIVAL %d - Media was removed: %ws\n", msg_count, lpdbi->dbcc_name);
|
||||
if (lpdbi->dbcc_classguid == GUID_DEVINTERFACE_HID)
|
||||
plater->GetEventHandler()->AddPendingEvent(HIDDeviceDetachedEvent(EVT_HID_DEVICE_DETACHED, boost::nowide::narrow(lpdbi->dbcc_name)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
#endif // WIN32
|
||||
|
||||
static void generic_exception_handle()
|
||||
{
|
||||
|
@ -248,7 +294,10 @@ bool GUI_App::on_init_inner()
|
|||
show_error(nullptr, ex.what());
|
||||
}
|
||||
|
||||
register_dpi_event();
|
||||
#ifdef WIN32
|
||||
register_win32_dpi_event();
|
||||
register_win32_device_notification_event();
|
||||
#endif // WIN32
|
||||
|
||||
// Let the libslic3r know the callback, which will translate messages on demand.
|
||||
Slic3r::I18N::set_translate_callback(libslic3r_translate_callback);
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
#ifdef _WIN32
|
||||
wxDEFINE_EVENT(EVT_HID_DEVICE_ATTACHED, HIDDeviceAttachedEvent);
|
||||
wxDEFINE_EVENT(EVT_HID_DEVICE_DETACHED, HIDDeviceDetachedEvent);
|
||||
wxDEFINE_EVENT(EVT_VOLUME_ATTACHED, VolumeAttachedEvent);
|
||||
wxDEFINE_EVENT(EVT_VOLUME_DETACHED, VolumeDetachedEvent);
|
||||
#endif // _WIN32
|
||||
|
||||
wxTopLevelWindow* find_toplevel_parent(wxWindow *window)
|
||||
{
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <wx/debug.h>
|
||||
#include <wx/settings.h>
|
||||
|
||||
#include "Event.hpp"
|
||||
|
||||
class wxCheckBox;
|
||||
class wxTopLevelWindow;
|
||||
class wxRect;
|
||||
|
@ -26,6 +28,19 @@ class wxRect;
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
#ifdef _WIN32
|
||||
// USB HID attach / detach events from Windows OS.
|
||||
using HIDDeviceAttachedEvent = Event<std::string>;
|
||||
using HIDDeviceDetachedEvent = Event<std::string>;
|
||||
wxDECLARE_EVENT(EVT_HID_DEVICE_ATTACHED, HIDDeviceAttachedEvent);
|
||||
wxDECLARE_EVENT(EVT_HID_DEVICE_DETACHED, HIDDeviceDetachedEvent);
|
||||
|
||||
// Disk aka Volume attach / detach events from Windows OS.
|
||||
using VolumeAttachedEvent = SimpleEvent;
|
||||
using VolumeDetachedEvent = SimpleEvent;
|
||||
wxDECLARE_EVENT(EVT_VOLUME_ATTACHED, VolumeAttachedEvent);
|
||||
wxDECLARE_EVENT(EVT_VOLUME_DETACHED, VolumeDetachedEvent);
|
||||
#endif /* _WIN32 */
|
||||
|
||||
wxTopLevelWindow* find_toplevel_parent(wxWindow *window);
|
||||
|
||||
|
|
|
@ -144,9 +144,7 @@ void KBShortcutsDialog::fill_shortcuts()
|
|||
{ ctrl + "J", L("Print host upload queue") },
|
||||
// View
|
||||
{ "0-6", L("Camera view") },
|
||||
#if ENABLE_SHOW_SCENE_LABELS
|
||||
{ "E", L("Show/Hide object/instance labels") },
|
||||
#endif // ENABLE_SHOW_SCENE_LABELS
|
||||
// Configuration
|
||||
{ ctrl + "P", L("Preferences") },
|
||||
// Help
|
||||
|
|
|
@ -31,6 +31,10 @@
|
|||
#include <fstream>
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <dbt.h>
|
||||
#endif // _WIN32
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
@ -104,6 +108,31 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
|||
update_title();
|
||||
|
||||
// declare events
|
||||
Bind(wxEVT_CREATE, [this](wxWindowCreateEvent& event) {
|
||||
|
||||
#ifdef _WIN32
|
||||
//static GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED };
|
||||
//static GUID GUID_DEVINTERFACE_DISK = { 0x53f56307, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b };
|
||||
//static GUID GUID_DEVINTERFACE_VOLUME = { 0x71a27cdd, 0x812a, 0x11d0, 0xbe, 0xc7, 0x08, 0x00, 0x2b, 0xe2, 0x09, 0x2f };
|
||||
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 };
|
||||
|
||||
// Register USB HID (Human Interface Devices) notifications to trigger the 3DConnexion enumeration.
|
||||
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = { 0 };
|
||||
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
|
||||
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_HID;
|
||||
m_hDeviceNotify = ::RegisterDeviceNotification(this->GetHWND(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
|
||||
// or register for file handle change?
|
||||
// DEV_BROADCAST_HANDLE NotificationFilter = { 0 };
|
||||
// NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
|
||||
// NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
||||
#endif // _WIN32
|
||||
|
||||
// propagate event
|
||||
event.Skip();
|
||||
});
|
||||
|
||||
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
|
||||
if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) {
|
||||
event.Veto();
|
||||
|
@ -131,6 +160,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
|||
// Called when closing the application and when switching the application language.
|
||||
void MainFrame::shutdown()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::UnregisterDeviceNotification(HDEVNOTIFY(m_hDeviceNotify));
|
||||
m_hDeviceNotify = nullptr;
|
||||
#endif // _WIN32
|
||||
|
||||
if (m_plater)
|
||||
m_plater->stop_jobs();
|
||||
|
||||
|
|
|
@ -141,6 +141,10 @@ public:
|
|||
wxNotebook* m_tabpanel { nullptr };
|
||||
wxProgressDialog* m_progress_dialog { nullptr };
|
||||
std::shared_ptr<ProgressStatusBar> m_statusbar;
|
||||
|
||||
#ifdef _WIN32
|
||||
void* m_hDeviceNotify { nullptr };
|
||||
#endif // _WIN32
|
||||
};
|
||||
|
||||
} // GUI
|
||||
|
|
|
@ -99,6 +99,25 @@ void Mouse3DController::State::append_button(unsigned int id, size_t /* input_qu
|
|||
}
|
||||
|
||||
#ifdef WIN32
|
||||
// Called by Win32 HID enumeration callback.
|
||||
void Mouse3DController::device_attached(const std::string &device)
|
||||
{
|
||||
int vid = 0;
|
||||
int pid = 0;
|
||||
if (sscanf(device.c_str(), "\\\\?\\HID#VID_%x&PID_%x&", &vid, &pid) == 2) {
|
||||
// BOOST_LOG_TRIVIAL(trace) << boost::format("Mouse3DController::device_attached(VID_%04xxPID_%04x)") % vid % pid;
|
||||
// BOOST_LOG_TRIVIAL(trace) << "Mouse3DController::device_attached: " << device;
|
||||
if (std::find(_3DCONNEXION_VENDORS.begin(), _3DCONNEXION_VENDORS.end(), vid) != _3DCONNEXION_VENDORS.end()) {
|
||||
// Signal the worker thread to wake up and enumerate HID devices, if not connected at the moment.
|
||||
// The message may come multiple times per each USB device. For example, some USB wireless dongles register as multiple HID sockets
|
||||
// for multiple devices to connect to.
|
||||
// Never mind, enumeration will be performed until connected.
|
||||
m_wakeup = true;
|
||||
m_stop_condition.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out mouse scroll events produced by the 3DConnexion driver.
|
||||
bool Mouse3DController::State::process_mouse_wheel()
|
||||
{
|
||||
|
@ -388,9 +407,13 @@ void Mouse3DController::disconnected()
|
|||
m_params_by_device[m_device_str] = m_params_ui;
|
||||
m_device_str.clear();
|
||||
m_connected = false;
|
||||
wxGetApp().plater()->get_camera().recover_from_free_camera();
|
||||
wxGetApp().plater()->set_current_canvas_as_dirty();
|
||||
wxWakeUpIdle();
|
||||
wxGetApp().plater()->CallAfter([]() {
|
||||
Plater *plater = wxGetApp().plater();
|
||||
if (plater != nullptr) {
|
||||
plater->get_camera().recover_from_free_camera();
|
||||
plater->set_current_canvas_as_dirty();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,6 +509,11 @@ void Mouse3DController::run()
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Enumerate once just after thread start.
|
||||
m_wakeup = true;
|
||||
#endif // _WIN32
|
||||
|
||||
for (;;) {
|
||||
{
|
||||
tbb::mutex::scoped_lock lock(m_params_ui_mutex);
|
||||
|
@ -518,7 +546,13 @@ bool Mouse3DController::connect_device()
|
|||
{
|
||||
// Wait for 2 seconds, but cancellable by m_stop.
|
||||
std::unique_lock<std::mutex> lock(m_stop_condition_mutex);
|
||||
m_stop_condition.wait_for(lock, std::chrono::seconds(2), [this]{ return this->m_stop; });
|
||||
#ifdef _WIN32
|
||||
// Wait indifinetely for the stop signal.
|
||||
m_stop_condition.wait(lock, [this]{ return m_stop || m_wakeup; });
|
||||
m_wakeup = false;
|
||||
#else
|
||||
m_stop_condition.wait_for(lock, std::chrono::seconds(2), [this]{ return m_stop; });
|
||||
#endif
|
||||
}
|
||||
|
||||
if (m_stop)
|
||||
|
@ -528,10 +562,14 @@ bool Mouse3DController::connect_device()
|
|||
hid_device_info* devices = hid_enumerate(0, 0);
|
||||
if (devices == nullptr)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to enumerate HID devices";
|
||||
BOOST_LOG_TRIVIAL(trace) << "Mouse3DController::connect_device() - no HID device enumerated.";
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
BOOST_LOG_TRIVIAL(trace) << "Mouse3DController::connect_device() - enumerating HID devices.";
|
||||
#endif // _WIN32
|
||||
|
||||
// Searches for 1st connected 3Dconnexion device
|
||||
struct DeviceData
|
||||
{
|
||||
|
@ -785,9 +823,17 @@ void Mouse3DController::disconnect_device()
|
|||
}
|
||||
m_device_str.clear();
|
||||
m_connected = false;
|
||||
wxGetApp().plater()->get_camera().recover_from_free_camera();
|
||||
wxGetApp().plater()->set_current_canvas_as_dirty();
|
||||
wxWakeUpIdle();
|
||||
#ifdef _WIN32
|
||||
// Enumerate once immediately after disconnect.
|
||||
m_wakeup = true;
|
||||
#endif // _WIN32
|
||||
wxGetApp().plater()->CallAfter([]() {
|
||||
Plater *plater = wxGetApp().plater();
|
||||
if (plater != nullptr) {
|
||||
plater->get_camera().recover_from_free_camera();
|
||||
plater->set_current_canvas_as_dirty();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -148,6 +148,9 @@ class Mouse3DController
|
|||
hid_device* m_device { nullptr };
|
||||
// Using m_stop_condition_mutex to synchronize m_stop.
|
||||
bool m_stop { false };
|
||||
#ifdef _WIN32
|
||||
std::atomic<bool> m_wakeup { false };
|
||||
#endif /* _WIN32 */
|
||||
// Mutex and condition variable for sleeping during the detection of 3DConnexion devices by polling while allowing
|
||||
// cancellation before the end of the polling interval.
|
||||
std::mutex m_stop_condition_mutex;
|
||||
|
@ -185,6 +188,9 @@ public:
|
|||
#endif // __APPLE__
|
||||
|
||||
#ifdef WIN32
|
||||
// Called by Win32 HID enumeration callback.
|
||||
void device_attached(const std::string &device);
|
||||
|
||||
// On Windows, the 3DConnexion driver sends out mouse wheel rotation events to an active application
|
||||
// if the application does not register at the driver. This is a workaround to ignore these superfluous
|
||||
// mouse wheel events.
|
||||
|
|
|
@ -1955,7 +1955,7 @@ struct Plater::priv
|
|||
GUI::show_error(this->q, msg);
|
||||
}
|
||||
}
|
||||
void export_gcode(fs::path output_path, PrintHostJob upload_job);
|
||||
void export_gcode(fs::path output_path, bool output_path_on_removable_media, PrintHostJob upload_job);
|
||||
void reload_from_disk();
|
||||
void reload_all_from_disk();
|
||||
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
|
||||
|
@ -2214,17 +2214,34 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
// Load the 3DConnexion device database.
|
||||
mouse3d_controller.load_config(*wxGetApp().app_config);
|
||||
// Start the background thread to detect and connect to a HID device (Windows and Linux).
|
||||
// Connect to a 3DConnextion driver (OSX).
|
||||
// Connect to a 3DConnextion driver (OSX).
|
||||
mouse3d_controller.init();
|
||||
#ifdef _WIN32
|
||||
// Register an USB HID (Human Interface Device) attach event. evt contains Win32 path to the USB device containing VID, PID and other info.
|
||||
// This event wakes up the Mouse3DController's background thread to enumerate HID devices, if the VID of the callback event
|
||||
// is one of the 3D Mouse vendors (3DConnexion or Logitech).
|
||||
this->q->Bind(EVT_HID_DEVICE_ATTACHED, [this](HIDDeviceAttachedEvent &evt) {
|
||||
mouse3d_controller.device_attached(evt.data);
|
||||
});
|
||||
#endif /* _WIN32 */
|
||||
|
||||
this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this](RemovableDriveEjectEvent &evt) {
|
||||
this->show_action_buttons(this->ready_to_slice);
|
||||
Slic3r::GUI::show_info(this->q, (boost::format(_utf8(L("Unmounting successful. The device %s(%s) can now be safely removed from the computer.")))
|
||||
% evt.data.name % evt.data.path).str());
|
||||
if (evt.data.second) {
|
||||
this->show_action_buttons(this->ready_to_slice);
|
||||
Slic3r::GUI::show_info(this->q, (boost::format(_utf8(L("Unmounting successful. The device %s(%s) can now be safely removed from the computer.")))
|
||||
% evt.data.first.name % evt.data.first.path).str());
|
||||
} else
|
||||
Slic3r::GUI::show_info(this->q, (boost::format(_utf8(L("Ejecting of device %s(%s) has failed.")))
|
||||
% evt.data.first.name % evt.data.first.path).str());
|
||||
});
|
||||
this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this](RemovableDrivesChangedEvent &) { this->show_action_buttons(this->ready_to_slice); });
|
||||
// Start the background thread and register this window as a target for update events.
|
||||
wxGetApp().removable_drive_manager()->init(this->q);
|
||||
#ifdef _WIN32
|
||||
// Trigger enumeration of removable media on Win32 notification.
|
||||
this->q->Bind(EVT_VOLUME_ATTACHED, [this](VolumeAttachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); });
|
||||
this->q->Bind(EVT_VOLUME_DETACHED, [this](VolumeDetachedEvent &evt) { wxGetApp().removable_drive_manager()->volumes_changed(); });
|
||||
#endif /* _WIN32 */
|
||||
|
||||
// Initialize the Undo / Redo stack with a first snapshot.
|
||||
this->take_snapshot(_(L("New Project")));
|
||||
|
@ -3242,7 +3259,7 @@ bool Plater::priv::restart_background_process(unsigned int state)
|
|||
return false;
|
||||
}
|
||||
|
||||
void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job)
|
||||
void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_removable_media, PrintHostJob upload_job)
|
||||
{
|
||||
wxCHECK_RET(!(output_path.empty() && upload_job.empty()), "export_gcode: output_path and upload_job empty");
|
||||
|
||||
|
@ -3263,7 +3280,7 @@ void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job)
|
|||
return;
|
||||
|
||||
if (! output_path.empty()) {
|
||||
background_process.schedule_export(output_path.string());
|
||||
background_process.schedule_export(output_path.string(), output_path_on_removable_media);
|
||||
} else {
|
||||
background_process.schedule_upload(std::move(upload_job));
|
||||
}
|
||||
|
@ -3745,7 +3762,12 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
|
|||
wxString message = evt.GetString();
|
||||
if (message.IsEmpty())
|
||||
message = _(L("Export failed"));
|
||||
show_error(q, message);
|
||||
if (q->m_tracking_popup_menu)
|
||||
// We don't want to pop-up a message box when tracking a pop-up menu.
|
||||
// We postpone the error message instead.
|
||||
q->m_tracking_popup_menu_error_message = message;
|
||||
else
|
||||
show_error(q, message);
|
||||
this->statusbar()->set_status_text(message);
|
||||
}
|
||||
if (canceled)
|
||||
|
@ -4927,8 +4949,8 @@ void Plater::export_gcode(bool prefer_removable)
|
|||
}
|
||||
|
||||
if (! output_path.empty()) {
|
||||
p->export_gcode(output_path, PrintHostJob());
|
||||
bool path_on_removable_media = removable_drive_manager.set_and_verify_last_save_path(output_path.string());
|
||||
p->export_gcode(output_path, path_on_removable_media, PrintHostJob());
|
||||
// Storing a path to AppConfig either as path to removable media or a path to internal media.
|
||||
// is_path_on_removable_drive() is called with the "true" parameter to update its internal database as the user may have shuffled the external drives
|
||||
// while the dialog was open.
|
||||
|
@ -5251,7 +5273,7 @@ void Plater::send_gcode()
|
|||
upload_job.upload_data.upload_path = dlg.filename();
|
||||
upload_job.upload_data.start_print = dlg.start_print();
|
||||
|
||||
p->export_gcode(fs::path(), std::move(upload_job));
|
||||
p->export_gcode(fs::path(), false, std::move(upload_job));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5627,7 +5649,7 @@ void Plater::schedule_background_process(bool schedule/* = true*/)
|
|||
this->p->suppressed_backround_processing_update = false;
|
||||
}
|
||||
|
||||
bool Plater::is_background_process_running() const
|
||||
bool Plater::is_background_process_update_scheduled() const
|
||||
{
|
||||
return this->p->background_process_timer.IsRunning();
|
||||
}
|
||||
|
@ -5749,15 +5771,34 @@ const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_red
|
|||
void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); }
|
||||
void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); }
|
||||
|
||||
SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() :
|
||||
m_was_running(wxGetApp().plater()->is_background_process_running())
|
||||
// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
|
||||
bool Plater::PopupMenu(wxMenu *menu, const wxPoint& pos)
|
||||
{
|
||||
wxGetApp().plater()->suppress_background_process(m_was_running);
|
||||
// Don't want to wake up and trigger reslicing while tracking the pop-up menu.
|
||||
SuppressBackgroundProcessingUpdate sbpu;
|
||||
// When tracking a pop-up menu, postpone error messages from the slicing result.
|
||||
m_tracking_popup_menu = true;
|
||||
bool out = this->wxPanel::PopupMenu(menu, pos);
|
||||
m_tracking_popup_menu = false;
|
||||
if (! m_tracking_popup_menu_error_message.empty()) {
|
||||
// Don't know whether the CallAfter is necessary, but it should not hurt.
|
||||
// The menus likely sends out some commands, so we may be safer if the dialog is shown after the menu command is processed.
|
||||
wxString message = std::move(m_tracking_popup_menu_error_message);
|
||||
wxTheApp->CallAfter([message, this]() { show_error(this, message); });
|
||||
m_tracking_popup_menu_error_message.clear();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() :
|
||||
m_was_scheduled(wxGetApp().plater()->is_background_process_update_scheduled())
|
||||
{
|
||||
wxGetApp().plater()->suppress_background_process(m_was_scheduled);
|
||||
}
|
||||
|
||||
SuppressBackgroundProcessingUpdate::~SuppressBackgroundProcessingUpdate()
|
||||
{
|
||||
wxGetApp().plater()->schedule_background_process(m_was_running);
|
||||
wxGetApp().plater()->schedule_background_process(m_was_scheduled);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
|
|
@ -208,7 +208,7 @@ public:
|
|||
void changed_object(int obj_idx);
|
||||
void changed_objects(const std::vector<size_t>& object_idxs);
|
||||
void schedule_background_process(bool schedule = true);
|
||||
bool is_background_process_running() const;
|
||||
bool is_background_process_update_scheduled() const;
|
||||
void suppress_background_process(const bool stop_background_process) ;
|
||||
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
|
||||
void send_gcode();
|
||||
|
@ -319,10 +319,20 @@ public:
|
|||
Plater *m_plater;
|
||||
};
|
||||
|
||||
// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
|
||||
bool PopupMenu(wxMenu *menu, const wxPoint& pos = wxDefaultPosition);
|
||||
bool PopupMenu(wxMenu *menu, int x, int y) { return this->PopupMenu(menu, wxPoint(x, y)); }
|
||||
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
|
||||
// Set true during PopupMenu() tracking to suppress immediate error message boxes.
|
||||
// The error messages are collected to m_tracking_popup_menu_error_message instead and these error messages
|
||||
// are shown after the pop-up dialog closes.
|
||||
bool m_tracking_popup_menu = false;
|
||||
wxString m_tracking_popup_menu_error_message;
|
||||
|
||||
void suppress_snapshots();
|
||||
void allow_snapshots();
|
||||
|
||||
|
@ -335,7 +345,7 @@ public:
|
|||
SuppressBackgroundProcessingUpdate();
|
||||
~SuppressBackgroundProcessingUpdate();
|
||||
private:
|
||||
bool m_was_running;
|
||||
bool m_was_scheduled;
|
||||
};
|
||||
|
||||
}}
|
||||
|
|
|
@ -56,7 +56,7 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
|
|||
volume_name.erase(volume_name.begin() + wcslen(volume_name.c_str()), volume_name.end());
|
||||
if (! file_system_name.empty()) {
|
||||
ULARGE_INTEGER free_space;
|
||||
::GetDiskFreeSpaceExA(path.c_str(), &free_space, nullptr, nullptr);
|
||||
::GetDiskFreeSpaceExW(wpath.c_str(), &free_space, nullptr, nullptr);
|
||||
if (free_space.QuadPart > 0) {
|
||||
path += "\\";
|
||||
current_drives.emplace_back(DriveData{ boost::nowide::narrow(volume_name), path });
|
||||
|
@ -86,9 +86,12 @@ void RemovableDriveManager::eject_drive()
|
|||
// get handle to device
|
||||
std::string mpath = "\\\\.\\" + m_last_save_path;
|
||||
mpath = mpath.substr(0, mpath.size() - 1);
|
||||
HANDLE handle = CreateFileA(mpath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
HANDLE handle = CreateFileW(boost::nowide::widen(mpath).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
std::cerr << "Ejecting " << mpath << " failed " << GetLastError() << " \n";
|
||||
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)));
|
||||
return;
|
||||
}
|
||||
DWORD deviceControlRetVal(0);
|
||||
|
@ -101,12 +104,15 @@ void RemovableDriveManager::eject_drive()
|
|||
if (error == 0) {
|
||||
CloseHandle(handle);
|
||||
BOOST_LOG_TRIVIAL(error) << "Ejecting " << mpath << " failed " << deviceControlRetVal << " " << GetLastError() << " \n";
|
||||
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)));
|
||||
return;
|
||||
}
|
||||
CloseHandle(handle);
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::move(*it_drive_data)));
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(*it_drive_data), true)));
|
||||
m_current_drives.erase(it_drive_data);
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +128,7 @@ std::string RemovableDriveManager::get_removable_drive_path(const std::string &p
|
|||
return std::string();
|
||||
std::size_t found = path.find_last_of("\\");
|
||||
std::string new_path = path.substr(0, found);
|
||||
int letter = PathGetDriveNumberA(new_path.c_str());
|
||||
int letter = PathGetDriveNumberW(boost::nowide::widen(new_path).c_str());
|
||||
for (const DriveData &drive_data : m_current_drives) {
|
||||
char drive = drive_data.path[0];
|
||||
if (drive == 'A' + letter)
|
||||
|
@ -136,7 +142,7 @@ std::string RemovableDriveManager::get_removable_drive_from_path(const std::stri
|
|||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
std::size_t found = path.find_last_of("\\");
|
||||
std::string new_path = path.substr(0, found);
|
||||
int letter = PathGetDriveNumberA(new_path.c_str());
|
||||
int letter = PathGetDriveNumberW(boost::nowide::widen(new_path).c_str());
|
||||
for (const DriveData &drive_data : m_current_drives) {
|
||||
assert(! drive_data.path.empty());
|
||||
if (drive_data.path.front() == 'A' + letter)
|
||||
|
@ -145,93 +151,16 @@ std::string RemovableDriveManager::get_removable_drive_from_path(const std::stri
|
|||
return std::string();
|
||||
}
|
||||
|
||||
#if 0
|
||||
// currently not used, left for possible future use
|
||||
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
// Called by Win32 Volume arrived / detached callback.
|
||||
void RemovableDriveManager::volumes_changed()
|
||||
{
|
||||
// here we need to catch messeges about device removal
|
||||
// problem is that when ejecting usb (how is it implemented above) there is no messege dispached. Only after physical removal of the device.
|
||||
//uncomment register_window() in init() to register and comment update() in GUI_App.cpp (only for windows!) to stop recieving periodical updates
|
||||
|
||||
LRESULT lRet = 1;
|
||||
static HDEVNOTIFY hDeviceNotify;
|
||||
static constexpr GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72, 0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
|
||||
|
||||
ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
|
||||
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
|
||||
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
NotificationFilter.dbcc_classguid = WceusbshGUID;
|
||||
|
||||
hDeviceNotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
break;
|
||||
|
||||
case WM_DEVICECHANGE:
|
||||
{
|
||||
// here is the important
|
||||
if(wParam == DBT_DEVICEREMOVECOMPLETE)
|
||||
{
|
||||
RemovableDriveManager::get_instance().update(0, true);
|
||||
}
|
||||
if (m_initialized) {
|
||||
// Signal the worker thread to wake up and enumerate removable drives.
|
||||
m_wakeup = true;
|
||||
m_thread_stop_condition.notify_all();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Send all other messages on to the default windows handler.
|
||||
lRet = DefWindowProc(hWnd, message, wParam, lParam);
|
||||
break;
|
||||
}
|
||||
return lRet;
|
||||
|
||||
}
|
||||
|
||||
void RemovableDriveManager::register_window()
|
||||
{
|
||||
//creates new unvisible window that is recieving callbacks from system
|
||||
// structure to register
|
||||
// currently not used, left for possible future use
|
||||
WNDCLASSEX wndClass;
|
||||
wndClass.cbSize = sizeof(WNDCLASSEX);
|
||||
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
|
||||
wndClass.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
|
||||
wndClass.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProcCallback);//this is callback
|
||||
wndClass.cbClsExtra = 0;
|
||||
wndClass.cbWndExtra = 0;
|
||||
wndClass.hIcon = LoadIcon(0, IDI_APPLICATION);
|
||||
wndClass.hbrBackground = CreateSolidBrush(RGB(192, 192, 192));
|
||||
wndClass.hCursor = LoadCursor(0, IDC_ARROW);
|
||||
wndClass.lpszClassName = L"PrusaSlicer_aux_class";
|
||||
wndClass.lpszMenuName = NULL;
|
||||
wndClass.hIconSm = wndClass.hIcon;
|
||||
if(!RegisterClassEx(&wndClass))
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
return;
|
||||
}
|
||||
|
||||
HWND hWnd = CreateWindowEx(
|
||||
WS_EX_NOACTIVATE,
|
||||
L"PrusaSlicer_aux_class",
|
||||
L"PrusaSlicer_aux_wnd",
|
||||
WS_DISABLED, // style
|
||||
CW_USEDEFAULT, 0,
|
||||
640, 480,
|
||||
NULL, NULL,
|
||||
GetModuleHandle(NULL),
|
||||
NULL);
|
||||
if(hWnd == NULL)
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
}
|
||||
//ShowWindow(hWnd, SW_SHOWNORMAL);
|
||||
UpdateWindow(hWnd);
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
namespace search_for_drives_internal
|
||||
|
@ -358,16 +287,18 @@ void RemovableDriveManager::eject_drive()
|
|||
// wait for command to finnish (blocks ui thread)
|
||||
child.wait();
|
||||
int err = child.exit_code();
|
||||
if(err)
|
||||
{
|
||||
if (err) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Ejecting 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)));
|
||||
return;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "Ejecting finished";
|
||||
|
||||
assert(m_callback_evt_handler);
|
||||
if (m_callback_evt_handler)
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::move(*it_drive_data)));
|
||||
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(std::move(*it_drive_data), true)));
|
||||
m_current_drives.erase(it_drive_data);
|
||||
}
|
||||
}
|
||||
|
@ -416,9 +347,7 @@ void RemovableDriveManager::init(wxEvtHandler *callback_evt_handler)
|
|||
m_initialized = true;
|
||||
m_callback_evt_handler = callback_evt_handler;
|
||||
|
||||
#if _WIN32
|
||||
//this->register_window_msw();
|
||||
#elif __APPLE__
|
||||
#if __APPLE__
|
||||
this->register_window_osx();
|
||||
#endif
|
||||
|
||||
|
@ -432,6 +361,10 @@ void RemovableDriveManager::init(wxEvtHandler *callback_evt_handler)
|
|||
|
||||
void RemovableDriveManager::shutdown()
|
||||
{
|
||||
#if __APPLE__
|
||||
this->unregister_window_osx();
|
||||
#endif
|
||||
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
if (m_thread.joinable()) {
|
||||
// Stop the worker thread, if running.
|
||||
|
@ -447,12 +380,6 @@ void RemovableDriveManager::shutdown()
|
|||
}
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
#if _WIN32
|
||||
//this->unregister_window_msw();
|
||||
#elif __APPLE__
|
||||
this->unregister_window_osx();
|
||||
#endif
|
||||
|
||||
m_initialized = false;
|
||||
m_callback_evt_handler = nullptr;
|
||||
}
|
||||
|
@ -469,9 +396,6 @@ bool RemovableDriveManager::set_and_verify_last_save_path(const std::string &pat
|
|||
|
||||
RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status()
|
||||
{
|
||||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
this->update();
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
RemovableDriveManager::RemovableDrivesStatus out;
|
||||
{
|
||||
|
@ -488,6 +412,10 @@ RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status()
|
|||
void RemovableDriveManager::update()
|
||||
{
|
||||
tbb::mutex::scoped_lock inside_update_lock;
|
||||
#ifdef _WIN32
|
||||
// All wake up calls up to now are now consumed when the drive enumeration starts.
|
||||
m_wakeup = false;
|
||||
#endif // _WIN32
|
||||
if (inside_update_lock.try_acquire(m_inside_update_mutex)) {
|
||||
// Got the lock without waiting. That means, the update was not running.
|
||||
// Run the update.
|
||||
|
@ -511,12 +439,23 @@ void RemovableDriveManager::update()
|
|||
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
void RemovableDriveManager::thread_proc()
|
||||
{
|
||||
// Signal the worker thread to update initially.
|
||||
#ifdef _WIN32
|
||||
m_wakeup = true;
|
||||
#endif // _WIN32
|
||||
|
||||
for (;;) {
|
||||
// Wait for 2 seconds before running the disk enumeration.
|
||||
// Cancellable.
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(m_thread_stop_mutex);
|
||||
#ifdef _WIN32
|
||||
// Wait 30 seconds for the stop signal, wake up time to time to remove those devices that the user ejected in file explorer
|
||||
// or another application (for example in Cura). This is a workaround, as Windows does not send an event on software eject of a drive.
|
||||
m_thread_stop_condition.wait_for(lck, std::chrono::seconds(30), [this]{ return m_stop || m_wakeup; });
|
||||
#else
|
||||
m_thread_stop_condition.wait_for(lck, std::chrono::seconds(2), [this]{ return m_stop; });
|
||||
#endif
|
||||
}
|
||||
if (m_stop)
|
||||
// Stop the worker thread.
|
||||
|
|
|
@ -32,7 +32,7 @@ inline bool operator< (const DriveData &lhs, const DriveData &rhs) { return lhs.
|
|||
inline bool operator> (const DriveData &lhs, const DriveData &rhs) { return lhs.path > rhs.path; }
|
||||
inline bool operator==(const DriveData &lhs, const DriveData &rhs) { return lhs.path == rhs.path; }
|
||||
|
||||
using RemovableDriveEjectEvent = Event<DriveData>;
|
||||
using RemovableDriveEjectEvent = Event<std::pair<DriveData, bool>>;
|
||||
wxDECLARE_EVENT(EVT_REMOVABLE_DRIVE_EJECTED, RemovableDriveEjectEvent);
|
||||
|
||||
using RemovableDrivesChangedEvent = SimpleEvent;
|
||||
|
@ -69,6 +69,8 @@ public:
|
|||
// On Windows, the function does not block, and the eject is detected in the background thread.
|
||||
void eject_drive();
|
||||
|
||||
// Status is used to retrieve info for showing UI buttons.
|
||||
// Status is called every time when change of UI buttons is possible therefore should not perform update.
|
||||
struct RemovableDrivesStatus {
|
||||
bool has_removable_drives { false };
|
||||
bool has_eject { false };
|
||||
|
@ -82,6 +84,11 @@ public:
|
|||
// It would be better to make this method private and friend to RemovableDriveManagerMM, but RemovableDriveManagerMM is an ObjectiveC class.
|
||||
void update();
|
||||
|
||||
#ifdef _WIN32
|
||||
// Called by Win32 Volume arrived / detached callback.
|
||||
void volumes_changed();
|
||||
#endif // _WIN32
|
||||
|
||||
private:
|
||||
bool m_initialized { false };
|
||||
wxEvtHandler* m_callback_evt_handler { nullptr };
|
||||
|
@ -93,6 +100,9 @@ private:
|
|||
std::condition_variable m_thread_stop_condition;
|
||||
mutable std::mutex m_thread_stop_mutex;
|
||||
bool m_stop { false };
|
||||
#ifdef _WIN32
|
||||
std::atomic<bool> m_wakeup { false };
|
||||
#endif /* _WIN32 */
|
||||
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||
|
||||
// Called from update() to enumerate removable drives.
|
||||
|
@ -112,10 +122,7 @@ private:
|
|||
// Set with set_and_verify_last_save_path() to a removable drive path to be ejected.
|
||||
std::string m_last_save_path;
|
||||
|
||||
#if _WIN32
|
||||
//registers for notifications by creating invisible window
|
||||
//void register_window_msw();
|
||||
#elif __APPLE__
|
||||
#if __APPLE__
|
||||
void register_window_osx();
|
||||
void unregister_window_osx();
|
||||
void list_devices(std::vector<DriveData> &out) const;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue