Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/slic3r/GUI')
-rw-r--r--src/slic3r/GUI/BackgroundSlicingProcess.cpp7
-rw-r--r--src/slic3r/GUI/BackgroundSlicingProcess.hpp5
-rw-r--r--src/slic3r/GUI/ConfigWizard.cpp2
-rw-r--r--src/slic3r/GUI/GLCanvas3D.cpp79
-rw-r--r--src/slic3r/GUI/GUI_App.cpp57
-rw-r--r--src/slic3r/GUI/GUI_Utils.cpp6
-rw-r--r--src/slic3r/GUI/GUI_Utils.hpp15
-rw-r--r--src/slic3r/GUI/KBShortcutsDialog.cpp2
-rw-r--r--src/slic3r/GUI/MainFrame.cpp34
-rw-r--r--src/slic3r/GUI/MainFrame.hpp4
-rw-r--r--src/slic3r/GUI/Mouse3DController.cpp62
-rw-r--r--src/slic3r/GUI/Mouse3DController.hpp6
-rw-r--r--src/slic3r/GUI/Plater.cpp69
-rw-r--r--src/slic3r/GUI/Plater.hpp14
-rw-r--r--src/slic3r/GUI/RemovableDriveManager.cpp145
-rw-r--r--src/slic3r/GUI/RemovableDriveManager.hpp17
16 files changed, 339 insertions, 185 deletions
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index 44b5a8bb8..35a55d767 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -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 &params)
}
// 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);
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp
index c8ece38f0..d091ecb31 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp
@@ -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;
diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp
index ac4357a2e..bbdf5f2a1 100644
--- a/src/slic3r/GUI/ConfigWizard.cpp
+++ b/src/slic3r/GUI/ConfigWizard.cpp
@@ -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();
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 9a7beddc1..9f6abf787 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -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 &copy = 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);
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 48ee9e32c..cc6c06690 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -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);
diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp
index 1e452b220..dc64141ba 100644
--- a/src/slic3r/GUI/GUI_Utils.cpp
+++ b/src/slic3r/GUI/GUI_Utils.cpp
@@ -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)
{
diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp
index f7bebd577..0d5249e25 100644
--- a/src/slic3r/GUI/GUI_Utils.hpp
+++ b/src/slic3r/GUI/GUI_Utils.hpp
@@ -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);
diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp
index b595f1275..842cec5e2 100644
--- a/src/slic3r/GUI/KBShortcutsDialog.cpp
+++ b/src/slic3r/GUI/KBShortcutsDialog.cpp
@@ -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
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index defd0b53a..1e22359ab 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -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();
diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp
index 2ccd77666..8c8b98090 100644
--- a/src/slic3r/GUI/MainFrame.hpp
+++ b/src/slic3r/GUI/MainFrame.hpp
@@ -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
diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp
index 3c1ffeb3e..dcce52ae8 100644
--- a/src/slic3r/GUI/Mouse3DController.cpp
+++ b/src/slic3r/GUI/Mouse3DController.cpp
@@ -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();
+ }
+ });
}
}
diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp
index e686e12cc..8f03606b0 100644
--- a/src/slic3r/GUI/Mouse3DController.hpp
+++ b/src/slic3r/GUI/Mouse3DController.hpp
@@ -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.
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index f1928d14e..fb61c4f24 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -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(); }
+// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
+bool Plater::PopupMenu(wxMenu *menu, const wxPoint& pos)
+{
+ // 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_running(wxGetApp().plater()->is_background_process_running())
+ m_was_scheduled(wxGetApp().plater()->is_background_process_update_scheduled())
{
- wxGetApp().plater()->suppress_background_process(m_was_running);
+ 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
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 89603f703..f737cf59a 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -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;
};
}}
diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp
index 3ec27ce5b..17aa60997 100644
--- a/src/slic3r/GUI/RemovableDriveManager.cpp
+++ b/src/slic3r/GUI/RemovableDriveManager.cpp
@@ -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,92 +151,15 @@ 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)
-{
- // 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);
- }
- }
- 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()
+// Called by Win32 Volume arrived / detached callback.
+void RemovableDriveManager::volumes_changed()
{
- //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();
+ if (m_initialized) {
+ // Signal the worker thread to wake up and enumerate removable drives.
+ m_wakeup = true;
+ m_thread_stop_condition.notify_all();
}
- //ShowWindow(hWnd, SW_SHOWNORMAL);
- UpdateWindow(hWnd);
}
-#endif
#else
@@ -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.
diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp
index c12f2dd0a..e1a8d6faf 100644
--- a/src/slic3r/GUI/RemovableDriveManager.hpp
+++ b/src/slic3r/GUI/RemovableDriveManager.hpp
@@ -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;