Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer
This commit is contained in:
commit
877e824de1
@ -328,13 +328,21 @@ find_package(TBB REQUIRED)
|
|||||||
# add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0)
|
# add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0)
|
||||||
|
|
||||||
find_package(CURL REQUIRED)
|
find_package(CURL REQUIRED)
|
||||||
include_directories(${CURL_INCLUDE_DIRS})
|
|
||||||
|
add_library(libcurl INTERFACE)
|
||||||
|
target_link_libraries(libcurl INTERFACE CURL::libcurl)
|
||||||
|
|
||||||
|
if (NOT WIN32)
|
||||||
|
# Required by libcurl
|
||||||
|
find_package(ZLIB REQUIRED)
|
||||||
|
target_link_libraries(libcurl INTERFACE ZLIB::ZLIB)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (SLIC3R_STATIC)
|
if (SLIC3R_STATIC)
|
||||||
if (NOT APPLE)
|
if (NOT APPLE)
|
||||||
# libcurl is always linked dynamically to the system libcurl on OSX.
|
# libcurl is always linked dynamically to the system libcurl on OSX.
|
||||||
# On other systems, libcurl is linked statically if SLIC3R_STATIC is set.
|
# On other systems, libcurl is linked statically if SLIC3R_STATIC is set.
|
||||||
add_definitions(-DCURL_STATICLIB)
|
target_compile_definitions(libcurl INTERFACE CURL_STATICLIB)
|
||||||
endif()
|
endif()
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
# As of now, our build system produces a statically linked libcurl,
|
# As of now, our build system produces a statically linked libcurl,
|
||||||
@ -342,7 +350,8 @@ if (SLIC3R_STATIC)
|
|||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}")
|
message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}")
|
||||||
message("OpenSSL libraries: ${OPENSSL_LIBRARIES}")
|
message("OpenSSL libraries: ${OPENSSL_LIBRARIES}")
|
||||||
include_directories(${OPENSSL_INCLUDE_DIR})
|
target_include_directories(libcurl INTERFACE ${OPENSSL_INCLUDE_DIR})
|
||||||
|
target_link_libraries(libcurl INTERFACE ${OPENSSL_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -104,33 +104,7 @@ endif ()
|
|||||||
# Add the Slic3r GUI library, libcurl, OpenGL and GLU libraries.
|
# Add the Slic3r GUI library, libcurl, OpenGL and GLU libraries.
|
||||||
if (SLIC3R_GUI)
|
if (SLIC3R_GUI)
|
||||||
# target_link_libraries(PrusaSlicer ws2_32 uxtheme setupapi libslic3r_gui ${wxWidgets_LIBRARIES})
|
# target_link_libraries(PrusaSlicer ws2_32 uxtheme setupapi libslic3r_gui ${wxWidgets_LIBRARIES})
|
||||||
target_link_libraries(PrusaSlicer libslic3r_gui ${wxWidgets_LIBRARIES})
|
target_link_libraries(PrusaSlicer libslic3r_gui)
|
||||||
|
|
||||||
# Configure libcurl and its dependencies OpenSSL & zlib
|
|
||||||
find_package(CURL REQUIRED)
|
|
||||||
if (NOT WIN32)
|
|
||||||
# Required by libcurl
|
|
||||||
find_package(ZLIB REQUIRED)
|
|
||||||
endif()
|
|
||||||
target_include_directories(PrusaSlicer PRIVATE ${CURL_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(PrusaSlicer ${CURL_LIBRARIES} ${ZLIB_LIBRARIES})
|
|
||||||
if (SLIC3R_STATIC)
|
|
||||||
if (NOT APPLE)
|
|
||||||
# libcurl is always linked dynamically to the system libcurl on OSX.
|
|
||||||
# On other systems, libcurl is linked statically if SLIC3R_STATIC is set.
|
|
||||||
target_compile_definitions(PrusaSlicer PRIVATE CURL_STATICLIB)
|
|
||||||
endif()
|
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|
||||||
# As of now, our build system produces a statically linked libcurl,
|
|
||||||
# which links the OpenSSL library dynamically.
|
|
||||||
find_package(OpenSSL REQUIRED)
|
|
||||||
message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}")
|
|
||||||
message("OpenSSL libraries: ${OPENSSL_LIBRARIES}")
|
|
||||||
target_include_directories(PrusaSlicer PRIVATE ${OPENSSL_INCLUDE_DIR})
|
|
||||||
target_link_libraries(PrusaSlicer ${OPENSSL_LIBRARIES})
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
# Generate debug symbols even in release mode.
|
# Generate debug symbols even in release mode.
|
||||||
target_link_options(PrusaSlicer PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
|
target_link_options(PrusaSlicer PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
|
||||||
|
@ -632,8 +632,11 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
|||||||
// Negative support_contact_z is not taken into account, it can result in false positives in cases
|
// Negative support_contact_z is not taken into account, it can result in false positives in cases
|
||||||
// where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752)
|
// where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752)
|
||||||
|
|
||||||
|
// Only check this layer in case it has some extrusions.
|
||||||
|
bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
|
||||||
|
|| (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions());
|
||||||
|
|
||||||
if (layer_to_print.print_z() > maximal_print_z + 2. * EPSILON)
|
if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON)
|
||||||
throw std::runtime_error(_(L("Empty layers detected, the output would not be printable.")) + "\n\n" +
|
throw std::runtime_error(_(L("Empty layers detected, the output would not be printable.")) + "\n\n" +
|
||||||
_(L("Object name")) + ": " + object.model_object()->name + "\n" + _(L("Print z")) + ": " +
|
_(L("Object name")) + ": " + object.model_object()->name + "\n" + _(L("Print z")) + ": " +
|
||||||
std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is "
|
std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is "
|
||||||
|
@ -212,10 +212,8 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
|||||||
if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors)
|
if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors)
|
||||||
something_nonoverriddable = false;
|
something_nonoverriddable = false;
|
||||||
for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
|
for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
|
||||||
if (!layer_tools.wiping_extrusions().is_overriddable_and_mark(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
|
if (!layer_tools.wiping_extrusions().is_overriddable_and_mark(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region))
|
||||||
something_nonoverriddable = true;
|
something_nonoverriddable = true;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (something_nonoverriddable)
|
if (something_nonoverriddable)
|
||||||
@ -237,7 +235,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
|||||||
has_infill = true;
|
has_infill = true;
|
||||||
|
|
||||||
if (m_print_config_ptr) {
|
if (m_print_config_ptr) {
|
||||||
if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable_and_mark(*fill, *m_print_config_ptr, object, region))
|
if (! layer_tools.wiping_extrusions().is_overriddable_and_mark(*fill, *m_print_config_ptr, object, region))
|
||||||
something_nonoverriddable = true;
|
something_nonoverriddable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ PRODUCTVERSION @SLIC3R_RC_VERSION@
|
|||||||
VALUE "ProductName", "@SLIC3R_APP_NAME@"
|
VALUE "ProductName", "@SLIC3R_APP_NAME@"
|
||||||
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
|
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
|
||||||
VALUE "InternalName", "@SLIC3R_APP_NAME@"
|
VALUE "InternalName", "@SLIC3R_APP_NAME@"
|
||||||
VALUE "LegalCopyright", "Copyright \251 2016-2019 Prusa Research, \251 2011-2018 Alessandro Ranelucci"
|
VALUE "LegalCopyright", "Copyright \251 2016-2020 Prusa Research, \251 2011-2018 Alessandro Ranelucci"
|
||||||
VALUE "OriginalFilename", "prusa-slicer.exe"
|
VALUE "OriginalFilename", "prusa-slicer.exe"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>@SLIC3R_APP_KEY@</string>
|
<string>@SLIC3R_APP_KEY@</string>
|
||||||
<key>CFBundleGetInfoString</key>
|
<key>CFBundleGetInfoString</key>
|
||||||
<string>@SLIC3R_APP_NAME@ Copyright (C) 2011-2019 Alessandro Ranellucci, (C) 2016-2019 Prusa Reseach</string>
|
<string>@SLIC3R_APP_NAME@ Copyright (C) 2011-2019 Alessandro Ranellucci, (C) 2016-2020 Prusa Reseach</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>PrusaSlicer.icns</string>
|
<string>PrusaSlicer.icns</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
|
@ -191,7 +191,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
|
|||||||
|
|
||||||
encoding_check(libslic3r_gui)
|
encoding_check(libslic3r_gui)
|
||||||
|
|
||||||
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi)
|
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES})
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY})
|
target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY})
|
||||||
|
@ -266,7 +266,7 @@ AboutDialog::AboutDialog()
|
|||||||
"<html>"
|
"<html>"
|
||||||
"<body bgcolor= %1% link= %2%>"
|
"<body bgcolor= %1% link= %2%>"
|
||||||
"<font color=%3%>"
|
"<font color=%3%>"
|
||||||
"%4% © 2016-2019 Prusa Research. <br />"
|
"%4% © 2016-2020 Prusa Research. <br />"
|
||||||
"%5% © 2011-2018 Alessandro Ranellucci. <br />"
|
"%5% © 2011-2018 Alessandro Ranellucci. <br />"
|
||||||
"<a href=\"http://slic3r.org/\">Slic3r</a> %6% "
|
"<a href=\"http://slic3r.org/\">Slic3r</a> %6% "
|
||||||
"<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">%7%</a>."
|
"<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">%7%</a>."
|
||||||
|
@ -101,10 +101,9 @@ void BackgroundSlicingProcess::process_fff()
|
|||||||
//FIXME localize the messages
|
//FIXME localize the messages
|
||||||
// Perform the final post-processing of the export path by applying the print statistics over the file name.
|
// 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);
|
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
|
||||||
GUI::RemovableDriveManager::get_instance().update();
|
bool with_check = GUI::wxGetApp().removable_drive_manager()->is_path_on_removable_drive(export_path);
|
||||||
bool with_check = GUI::RemovableDriveManager::get_instance().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, with_check);
|
||||||
switch (copy_ret_val){
|
switch (copy_ret_val) {
|
||||||
case SUCCESS: break; // no error
|
case SUCCESS: break; // no error
|
||||||
case FAIL_COPY_FILE:
|
case FAIL_COPY_FILE:
|
||||||
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
|
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
|
||||||
@ -236,7 +235,7 @@ void BackgroundSlicingProcess::thread_proc()
|
|||||||
// Only post the canceled event, if canceled by user.
|
// Only post the canceled event, if canceled by user.
|
||||||
// Don't post the canceled event, if canceled from Print::apply().
|
// Don't post the canceled event, if canceled from Print::apply().
|
||||||
wxCommandEvent evt(m_event_finished_id);
|
wxCommandEvent evt(m_event_finished_id);
|
||||||
evt.SetString(error);
|
evt.SetString(GUI::from_u8(error));
|
||||||
evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0));
|
evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0));
|
||||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
|
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
|
||||||
}
|
}
|
||||||
|
@ -40,11 +40,19 @@ template<class T, size_t N> struct ArrayEvent : public wxEvent
|
|||||||
return new ArrayEvent<T, N>(GetEventType(), data, GetEventObject());
|
return new ArrayEvent<T, N>(GetEventType(), data, GetEventObject());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template<class T> struct ArrayEvent<T, 1> : public wxEvent
|
|
||||||
|
template<class T> struct Event : public wxEvent
|
||||||
{
|
{
|
||||||
T data;
|
T data;
|
||||||
|
|
||||||
ArrayEvent(wxEventType type, T data, wxObject* origin = nullptr)
|
Event(wxEventType type, const T &data, wxObject* origin = nullptr)
|
||||||
|
: wxEvent(0, type), data(std::move(data))
|
||||||
|
{
|
||||||
|
m_propagationLevel = wxEVENT_PROPAGATE_MAX;
|
||||||
|
SetEventObject(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
Event(wxEventType type, T&& data, wxObject* origin = nullptr)
|
||||||
: wxEvent(0, type), data(std::move(data))
|
: wxEvent(0, type), data(std::move(data))
|
||||||
{
|
{
|
||||||
m_propagationLevel = wxEVENT_PROPAGATE_MAX;
|
m_propagationLevel = wxEVENT_PROPAGATE_MAX;
|
||||||
@ -53,13 +61,10 @@ template<class T> struct ArrayEvent<T, 1> : public wxEvent
|
|||||||
|
|
||||||
virtual wxEvent* Clone() const
|
virtual wxEvent* Clone() const
|
||||||
{
|
{
|
||||||
return new ArrayEvent<T, 1>(GetEventType(), data, GetEventObject());
|
return new Event<T>(GetEventType(), data, GetEventObject());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T> using Event = ArrayEvent<T, 1>;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +155,7 @@ GUI_App::GUI_App()
|
|||||||
, m_em_unit(10)
|
, m_em_unit(10)
|
||||||
, m_imgui(new ImGuiWrapper())
|
, m_imgui(new ImGuiWrapper())
|
||||||
, m_wizard(nullptr)
|
, m_wizard(nullptr)
|
||||||
|
, m_removable_drive_manager(std::make_unique<RemovableDriveManager>())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
GUI_App::~GUI_App()
|
GUI_App::~GUI_App()
|
||||||
@ -262,7 +263,6 @@ bool GUI_App::on_init_inner()
|
|||||||
|
|
||||||
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||||
|
|
||||||
RemovableDriveManager::get_instance().init();
|
|
||||||
|
|
||||||
Bind(wxEVT_IDLE, [this](wxIdleEvent& event)
|
Bind(wxEVT_IDLE, [this](wxIdleEvent& event)
|
||||||
{
|
{
|
||||||
@ -274,10 +274,6 @@ bool GUI_App::on_init_inner()
|
|||||||
|
|
||||||
this->obj_manipul()->update_if_dirty();
|
this->obj_manipul()->update_if_dirty();
|
||||||
|
|
||||||
#if !__APPLE__
|
|
||||||
RemovableDriveManager::get_instance().update(wxGetLocalTime(), true);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Preset updating & Configwizard are done after the above initializations,
|
// Preset updating & Configwizard are done after the above initializations,
|
||||||
// and after MainFrame is created & shown.
|
// and after MainFrame is created & shown.
|
||||||
// The extra CallAfter() is needed because of Mac, where this is the only way
|
// The extra CallAfter() is needed because of Mac, where this is the only way
|
||||||
|
@ -29,9 +29,9 @@ class PresetUpdater;
|
|||||||
class ModelObject;
|
class ModelObject;
|
||||||
class PrintHostJobQueue;
|
class PrintHostJobQueue;
|
||||||
|
|
||||||
namespace GUI
|
|
||||||
{
|
|
||||||
|
|
||||||
|
namespace GUI{
|
||||||
|
class RemovableDriveManager;
|
||||||
enum FileType
|
enum FileType
|
||||||
{
|
{
|
||||||
FT_STL,
|
FT_STL,
|
||||||
@ -96,6 +96,8 @@ class GUI_App : public wxApp
|
|||||||
// Best translation language, provided by Windows or OSX, owned by wxWidgets.
|
// Best translation language, provided by Windows or OSX, owned by wxWidgets.
|
||||||
const wxLanguageInfo *m_language_info_best = nullptr;
|
const wxLanguageInfo *m_language_info_best = nullptr;
|
||||||
|
|
||||||
|
std::unique_ptr<RemovableDriveManager> m_removable_drive_manager;
|
||||||
|
|
||||||
std::unique_ptr<ImGuiWrapper> m_imgui;
|
std::unique_ptr<ImGuiWrapper> m_imgui;
|
||||||
std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
|
std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
|
||||||
ConfigWizard* m_wizard; // Managed by wxWindow tree
|
ConfigWizard* m_wizard; // Managed by wxWindow tree
|
||||||
@ -180,6 +182,8 @@ public:
|
|||||||
|
|
||||||
std::vector<Tab *> tabs_list;
|
std::vector<Tab *> tabs_list;
|
||||||
|
|
||||||
|
RemovableDriveManager* removable_drive_manager() { return m_removable_drive_manager.get(); }
|
||||||
|
|
||||||
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
||||||
|
|
||||||
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }
|
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }
|
||||||
|
@ -122,6 +122,8 @@ void KBShortcutsDialog::fill_shortcuts()
|
|||||||
{ ctrl + "G", L("Export G-code") },
|
{ ctrl + "G", L("Export G-code") },
|
||||||
{ ctrl + "Shift+" + "G", L("Send G-code") },
|
{ ctrl + "Shift+" + "G", L("Send G-code") },
|
||||||
{ ctrl + "E", L("Export config") },
|
{ ctrl + "E", L("Export config") },
|
||||||
|
{ ctrl + "U", L("Export to SD card / Flash drive") },
|
||||||
|
{ ctrl + "T", L("Eject SD card / Flash drive") },
|
||||||
// Edit
|
// Edit
|
||||||
{ ctrl + "A", L("Select all objects") },
|
{ ctrl + "A", L("Select all objects") },
|
||||||
{ "Esc", L("Deselect all") },
|
{ "Esc", L("Deselect all") },
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "wxExtensions.hpp"
|
#include "wxExtensions.hpp"
|
||||||
#include "GUI_ObjectList.hpp"
|
#include "GUI_ObjectList.hpp"
|
||||||
#include "Mouse3DController.hpp"
|
#include "Mouse3DController.hpp"
|
||||||
|
#include "RemovableDriveManager.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@ -124,6 +125,9 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
|||||||
// Store the device parameter database back to appconfig.
|
// Store the device parameter database back to appconfig.
|
||||||
m_plater->get_mouse3d_controller().save_config(*wxGetApp().app_config);
|
m_plater->get_mouse3d_controller().save_config(*wxGetApp().app_config);
|
||||||
|
|
||||||
|
// Stop the background thread of the removable drive manager, so that no new updates will be sent to the Plater.
|
||||||
|
wxGetApp().removable_drive_manager()->shutdown();
|
||||||
|
|
||||||
// Save the slic3r.ini.Usually the ini file is saved from "on idle" callback,
|
// Save the slic3r.ini.Usually the ini file is saved from "on idle" callback,
|
||||||
// but in rare cases it may not have been called yet.
|
// but in rare cases it may not have been called yet.
|
||||||
wxGetApp().app_config->save();
|
wxGetApp().app_config->save();
|
||||||
@ -323,6 +327,27 @@ bool MainFrame::can_send_gcode() const
|
|||||||
return print_host_opt != nullptr && !print_host_opt->value.empty();
|
return print_host_opt != nullptr && !print_host_opt->value.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MainFrame::can_export_gcode_sd() const
|
||||||
|
{
|
||||||
|
if (m_plater == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_plater->model().objects.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_plater->is_export_gcode_scheduled())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// TODO:: add other filters
|
||||||
|
|
||||||
|
return wxGetApp().removable_drive_manager()->status().has_removable_drives;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainFrame::can_eject() const
|
||||||
|
{
|
||||||
|
return wxGetApp().removable_drive_manager()->status().has_eject;
|
||||||
|
}
|
||||||
|
|
||||||
bool MainFrame::can_slice() const
|
bool MainFrame::can_slice() const
|
||||||
{
|
{
|
||||||
bool bg_proc = wxGetApp().app_config->get("background_processing") == "1";
|
bool bg_proc = wxGetApp().app_config->get("background_processing") == "1";
|
||||||
@ -493,6 +518,9 @@ void MainFrame::init_menubar()
|
|||||||
[this](wxCommandEvent&) { if (m_plater) m_plater->send_gcode(); }, "export_gcode", nullptr,
|
[this](wxCommandEvent&) { if (m_plater) m_plater->send_gcode(); }, "export_gcode", nullptr,
|
||||||
[this](){return can_send_gcode(); }, this);
|
[this](){return can_send_gcode(); }, this);
|
||||||
m_changeable_menu_items.push_back(item_send_gcode);
|
m_changeable_menu_items.push_back(item_send_gcode);
|
||||||
|
append_menu_item(export_menu, wxID_ANY, _(L("Export G-code to SD card / Flash drive")) + dots + "\tCtrl+U", _(L("Export current plate as G-code to SD card / Flash drive")),
|
||||||
|
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(true); }, "export_to_sd", nullptr,
|
||||||
|
[this]() {return can_export_gcode_sd(); }, this);
|
||||||
export_menu->AppendSeparator();
|
export_menu->AppendSeparator();
|
||||||
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")),
|
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")),
|
||||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "export_plater", nullptr,
|
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "export_plater", nullptr,
|
||||||
@ -516,6 +544,10 @@ void MainFrame::init_menubar()
|
|||||||
[this]() {return true; }, this);
|
[this]() {return true; }, this);
|
||||||
append_submenu(fileMenu, export_menu, wxID_ANY, _(L("&Export")), "");
|
append_submenu(fileMenu, export_menu, wxID_ANY, _(L("&Export")), "");
|
||||||
|
|
||||||
|
append_menu_item(fileMenu, wxID_ANY, _(L("Ejec&t SD card / Flash drive")) + dots + "\tCtrl+T", _(L("Eject SD card / Flash drive after the G-code was exported to it.")),
|
||||||
|
[this](wxCommandEvent&) { if (m_plater) m_plater->eject_drive(); }, "eject_sd", nullptr,
|
||||||
|
[this]() {return can_eject(); }, this);
|
||||||
|
|
||||||
fileMenu->AppendSeparator();
|
fileMenu->AppendSeparator();
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -70,6 +70,8 @@ class MainFrame : public DPIFrame
|
|||||||
bool can_export_supports() const;
|
bool can_export_supports() const;
|
||||||
bool can_export_gcode() const;
|
bool can_export_gcode() const;
|
||||||
bool can_send_gcode() const;
|
bool can_send_gcode() const;
|
||||||
|
bool can_export_gcode_sd() const;
|
||||||
|
bool can_eject() const;
|
||||||
bool can_slice() const;
|
bool can_slice() const;
|
||||||
bool can_change_view() const;
|
bool can_change_view() const;
|
||||||
bool can_select() const;
|
bool can_select() const;
|
||||||
|
@ -451,12 +451,13 @@ void Mouse3DController::shutdown()
|
|||||||
// Stop the worker thread, if running.
|
// Stop the worker thread, if running.
|
||||||
{
|
{
|
||||||
// Notify the worker thread to cancel wait on detection polling.
|
// Notify the worker thread to cancel wait on detection polling.
|
||||||
std::unique_lock<std::mutex> lock(m_stop_condition_mutex);
|
std::lock_guard<std::mutex> lock(m_stop_condition_mutex);
|
||||||
m_stop = true;
|
m_stop = true;
|
||||||
m_stop_condition.notify_all();
|
|
||||||
}
|
}
|
||||||
|
m_stop_condition.notify_all();
|
||||||
// Wait for the worker thread to stop.
|
// Wait for the worker thread to stop.
|
||||||
m_thread.join();
|
m_thread.join();
|
||||||
|
m_stop = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ class Mouse3DController
|
|||||||
std::map<std::string, Params> m_params_by_device;
|
std::map<std::string, Params> m_params_by_device;
|
||||||
|
|
||||||
mutable State m_state;
|
mutable State m_state;
|
||||||
std::atomic<bool> m_connected;
|
std::atomic<bool> m_connected { false };
|
||||||
std::string m_device_str;
|
std::string m_device_str;
|
||||||
|
|
||||||
#if ! __APPLE__
|
#if ! __APPLE__
|
||||||
|
@ -876,8 +876,8 @@ Sidebar::Sidebar(Plater *parent)
|
|||||||
};
|
};
|
||||||
|
|
||||||
init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer")) + "\tCtrl+Shift+G");
|
init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer")) + "\tCtrl+Shift+G");
|
||||||
init_scalable_btn(&p->btn_remove_device, "eject_sd" , _(L("Remove device")));
|
init_scalable_btn(&p->btn_remove_device, "eject_sd" , _(L("Remove device")) + "\tCtrl+T");
|
||||||
init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _(L("Export to SD card / Flash drive")));
|
init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _(L("Export to SD card / Flash drive")) + "\tCtrl+U");
|
||||||
|
|
||||||
// regular buttons "Slice now" and "Export G-code"
|
// regular buttons "Slice now" and "Export G-code"
|
||||||
|
|
||||||
@ -1953,6 +1953,11 @@ struct Plater::priv
|
|||||||
wxString get_project_filename(const wxString& extension = wxEmptyString) const;
|
wxString get_project_filename(const wxString& extension = wxEmptyString) const;
|
||||||
void set_project_filename(const wxString& filename);
|
void set_project_filename(const wxString& filename);
|
||||||
|
|
||||||
|
// Caching last value of show_action_buttons parameter for show_action_buttons(), so that a callback which does not know this state will not override it.
|
||||||
|
mutable bool ready_to_slice = { false };
|
||||||
|
// Flag indicating that the G-code export targets a removable device, therefore the show_action_buttons() needs to be called at any case when the background processing finishes.
|
||||||
|
bool writing_to_removable_device = { false };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool init_object_menu();
|
bool init_object_menu();
|
||||||
bool init_common_menu(wxMenu* menu, const bool is_part = false);
|
bool init_common_menu(wxMenu* menu, const bool is_part = false);
|
||||||
@ -1978,8 +1983,8 @@ private:
|
|||||||
* we should call tack_snapshot just ones
|
* we should call tack_snapshot just ones
|
||||||
* instead of calls for each action separately
|
* instead of calls for each action separately
|
||||||
* */
|
* */
|
||||||
std::string m_last_fff_printer_profile_name;
|
std::string m_last_fff_printer_profile_name;
|
||||||
std::string m_last_sla_printer_profile_name;
|
std::string m_last_sla_printer_profile_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase);
|
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase);
|
||||||
@ -2151,11 +2156,17 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||||||
// Connect to a 3DConnextion driver (OSX).
|
// Connect to a 3DConnextion driver (OSX).
|
||||||
mouse3d_controller.init();
|
mouse3d_controller.init();
|
||||||
|
|
||||||
|
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());
|
||||||
|
});
|
||||||
|
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);
|
||||||
|
|
||||||
// Initialize the Undo / Redo stack with a first snapshot.
|
// Initialize the Undo / Redo stack with a first snapshot.
|
||||||
this->take_snapshot(_(L("New Project")));
|
this->take_snapshot(_(L("New Project")));
|
||||||
|
|
||||||
//void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
|
|
||||||
RemovableDriveManager::get_instance().set_drive_count_changed_callback(std::bind(&Plater::priv::show_action_buttons, this, std::placeholders::_1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Plater::priv::~priv()
|
Plater::priv::~priv()
|
||||||
@ -3697,14 +3708,9 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
|
|||||||
sidebar->set_btn_label(ActionButtonType::abReslice, "Slice now");
|
sidebar->set_btn_label(ActionButtonType::abReslice, "Slice now");
|
||||||
show_action_buttons(true);
|
show_action_buttons(true);
|
||||||
}
|
}
|
||||||
else if (wxGetApp().get_mode() == comSimple)
|
else if (this->writing_to_removable_device || wxGetApp().get_mode() == comSimple)
|
||||||
show_action_buttons(false);
|
show_action_buttons(false);
|
||||||
|
this->writing_to_removable_device = false;
|
||||||
if(!canceled && RemovableDriveManager::get_instance().get_is_writing())
|
|
||||||
{
|
|
||||||
RemovableDriveManager::get_instance().set_is_writing(false);
|
|
||||||
show_action_buttons(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::on_layer_editing_toggled(bool enable)
|
void Plater::priv::on_layer_editing_toggled(bool enable)
|
||||||
@ -4264,32 +4270,36 @@ void Plater::priv::update_object_menu()
|
|||||||
sidebar->obj_list()->append_menu_items_add_volume(&object_menu);
|
sidebar->obj_list()->append_menu_items_add_volume(&object_menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
|
void Plater::priv::show_action_buttons(const bool ready_to_slice) const
|
||||||
{
|
{
|
||||||
RemovableDriveManager::get_instance().set_plater_ready_to_slice(is_ready_to_slice);
|
// Cache this value, so that the callbacks from the RemovableDriveManager may repeat that value when calling show_action_buttons().
|
||||||
|
this->ready_to_slice = ready_to_slice;
|
||||||
|
|
||||||
wxWindowUpdateLocker noUpdater(sidebar);
|
wxWindowUpdateLocker noUpdater(sidebar);
|
||||||
const auto prin_host_opt = config->option<ConfigOptionString>("print_host");
|
const auto prin_host_opt = config->option<ConfigOptionString>("print_host");
|
||||||
const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty();
|
const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty();
|
||||||
|
|
||||||
bool disconnect_shown = !RemovableDriveManager::get_instance().is_last_drive_removed();
|
|
||||||
bool export_removable_shown = RemovableDriveManager::get_instance().get_drives_count() > 0;
|
|
||||||
// when a background processing is ON, export_btn and/or send_btn are showing
|
// when a background processing is ON, export_btn and/or send_btn are showing
|
||||||
if (wxGetApp().app_config->get("background_processing") == "1")
|
if (wxGetApp().app_config->get("background_processing") == "1")
|
||||||
{
|
{
|
||||||
|
RemovableDriveManager::RemovableDrivesStatus removable_media_status = wxGetApp().removable_drive_manager()->status();
|
||||||
if (sidebar->show_reslice(false) |
|
if (sidebar->show_reslice(false) |
|
||||||
sidebar->show_export(true) |
|
sidebar->show_export(true) |
|
||||||
sidebar->show_send(send_gcode_shown) |
|
sidebar->show_send(send_gcode_shown) |
|
||||||
sidebar->show_export_removable(export_removable_shown) |
|
sidebar->show_export_removable(removable_media_status.has_removable_drives) |
|
||||||
sidebar->show_disconnect(disconnect_shown))
|
sidebar->show_disconnect(removable_media_status.has_eject))
|
||||||
sidebar->Layout();
|
sidebar->Layout();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (sidebar->show_reslice(is_ready_to_slice) |
|
RemovableDriveManager::RemovableDrivesStatus removable_media_status;
|
||||||
sidebar->show_export(!is_ready_to_slice) |
|
if (! ready_to_slice)
|
||||||
sidebar->show_send(send_gcode_shown && !is_ready_to_slice) |
|
removable_media_status = wxGetApp().removable_drive_manager()->status();
|
||||||
sidebar->show_export_removable(export_removable_shown && !is_ready_to_slice) |
|
if (sidebar->show_reslice(ready_to_slice) |
|
||||||
sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice))
|
sidebar->show_export(!ready_to_slice) |
|
||||||
|
sidebar->show_send(send_gcode_shown && !ready_to_slice) |
|
||||||
|
sidebar->show_export_removable(!ready_to_slice && removable_media_status.has_removable_drives) |
|
||||||
|
sidebar->show_disconnect(!ready_to_slice && removable_media_status.has_eject))
|
||||||
sidebar->Layout();
|
sidebar->Layout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4822,51 +4832,38 @@ void Plater::export_gcode(bool prefer_removable)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
|
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
|
||||||
auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string());
|
AppConfig &appconfig = *wxGetApp().app_config;
|
||||||
bool removable_drives_connected = GUI::RemovableDriveManager::get_instance().update();
|
RemovableDriveManager &removable_drive_manager = *wxGetApp().removable_drive_manager();
|
||||||
if(prefer_removable)
|
// Get a last save path, either to removable media or to an internal media.
|
||||||
{
|
std::string start_dir = appconfig.get_last_output_dir(default_output_file.parent_path().string(), prefer_removable);
|
||||||
if(removable_drives_connected)
|
if (prefer_removable) {
|
||||||
{
|
// Returns a path to a removable media if it exists, prefering start_dir. Update the internal removable drives database.
|
||||||
auto start_dir_removable = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string(), true);
|
start_dir = removable_drive_manager.get_removable_drive_path(start_dir);
|
||||||
if (RemovableDriveManager::get_instance().is_path_on_removable_drive(start_dir_removable))
|
if (start_dir.empty())
|
||||||
{
|
// Direct user to the last internal media.
|
||||||
start_dir = start_dir_removable;
|
start_dir = appconfig.get_last_output_dir(default_output_file.parent_path().string(), false);
|
||||||
}else
|
|
||||||
{
|
|
||||||
start_dir = RemovableDriveManager::get_instance().get_drive_path();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save SL1 file as:")),
|
|
||||||
start_dir,
|
|
||||||
from_path(default_output_file.filename()),
|
|
||||||
GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP, default_output_file.extension().string()),
|
|
||||||
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
|
|
||||||
);
|
|
||||||
|
|
||||||
fs::path output_path;
|
fs::path output_path;
|
||||||
if (dlg.ShowModal() == wxID_OK) {
|
{
|
||||||
fs::path path = into_path(dlg.GetPath());
|
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save SL1 file as:")),
|
||||||
wxGetApp().app_config->update_last_output_dir(path.parent_path().string(), RemovableDriveManager::get_instance().is_path_on_removable_drive(path.parent_path().string()));
|
start_dir,
|
||||||
output_path = std::move(path);
|
from_path(default_output_file.filename()),
|
||||||
|
GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP, default_output_file.extension().string()),
|
||||||
|
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
|
||||||
|
);
|
||||||
|
if (dlg.ShowModal() == wxID_OK)
|
||||||
|
output_path = into_path(dlg.GetPath());
|
||||||
}
|
}
|
||||||
if (! output_path.empty())
|
|
||||||
{
|
|
||||||
std::string path = output_path.string();
|
|
||||||
p->export_gcode(std::move(output_path), PrintHostJob());
|
|
||||||
|
|
||||||
RemovableDriveManager::get_instance().update(0, false);
|
if (! output_path.empty()) {
|
||||||
RemovableDriveManager::get_instance().set_last_save_path(path);
|
p->export_gcode(output_path, PrintHostJob());
|
||||||
RemovableDriveManager::get_instance().verify_last_save_path();
|
bool path_on_removable_media = removable_drive_manager.set_and_verify_last_save_path(output_path.string());
|
||||||
|
// Storing a path to AppConfig either as path to removable media or a path to internal media.
|
||||||
if(!RemovableDriveManager::get_instance().is_last_drive_removed())
|
// 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.
|
||||||
RemovableDriveManager::get_instance().set_is_writing(true);
|
appconfig.update_last_output_dir(output_path.parent_path().string(), path_on_removable_media);
|
||||||
RemovableDriveManager::get_instance().erase_callbacks();
|
p->writing_to_removable_device = path_on_removable_media;
|
||||||
RemovableDriveManager::get_instance().add_remove_callback(std::bind(&Plater::drive_ejected_callback, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5188,28 +5185,11 @@ void Plater::send_gcode()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called when the Eject button is pressed.
|
||||||
void Plater::eject_drive()
|
void Plater::eject_drive()
|
||||||
{
|
{
|
||||||
RemovableDriveManager::get_instance().update(0, true);
|
wxGetApp().removable_drive_manager()->eject_drive();
|
||||||
RemovableDriveManager::get_instance().erase_callbacks();
|
|
||||||
RemovableDriveManager::get_instance().add_remove_callback(std::bind(&Plater::drive_ejected_callback, this));
|
|
||||||
RemovableDriveManager::get_instance().eject_drive(RemovableDriveManager::get_instance().get_last_save_path());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
void Plater::drive_ejected_callback()
|
|
||||||
{
|
|
||||||
if (RemovableDriveManager::get_instance().get_did_eject())
|
|
||||||
{
|
|
||||||
RemovableDriveManager::get_instance().set_did_eject(false);
|
|
||||||
show_info(this,
|
|
||||||
(boost::format(_utf8(L("Unmounting successful. The device %s(%s) can now be safely removed from the computer.")))
|
|
||||||
% RemovableDriveManager::get_instance().get_ejected_name()
|
|
||||||
% RemovableDriveManager::get_instance().get_ejected_path()).str());
|
|
||||||
}
|
|
||||||
p->show_action_buttons(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot(snapshot_name); }
|
void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot(snapshot_name); }
|
||||||
void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); }
|
void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); }
|
||||||
@ -5592,7 +5572,7 @@ void Plater::suppress_background_process(const bool stop_background_process)
|
|||||||
void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); }
|
void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); }
|
||||||
|
|
||||||
void Plater::update_object_menu() { p->update_object_menu(); }
|
void Plater::update_object_menu() { p->update_object_menu(); }
|
||||||
void Plater::show_action_buttons(const bool is_ready_to_slice) const { p->show_action_buttons(is_ready_to_slice); }
|
void Plater::show_action_buttons(const bool ready_to_slice) const { p->show_action_buttons(ready_to_slice); }
|
||||||
|
|
||||||
void Plater::copy_selection_to_clipboard()
|
void Plater::copy_selection_to_clipboard()
|
||||||
{
|
{
|
||||||
|
@ -213,7 +213,6 @@ public:
|
|||||||
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
|
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
|
||||||
void send_gcode();
|
void send_gcode();
|
||||||
void eject_drive();
|
void eject_drive();
|
||||||
void drive_ejected_callback();
|
|
||||||
|
|
||||||
void take_snapshot(const std::string &snapshot_name);
|
void take_snapshot(const std::string &snapshot_name);
|
||||||
void take_snapshot(const wxString &snapshot_name);
|
void take_snapshot(const wxString &snapshot_name);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "RemovableDriveManager.hpp"
|
#include "RemovableDriveManager.hpp"
|
||||||
#include <iostream>
|
#include <libslic3r/libslic3r.h>
|
||||||
#include "boost/nowide/convert.hpp"
|
|
||||||
|
#include <boost/nowide/convert.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@ -9,11 +11,9 @@
|
|||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
|
|
||||||
#include <Dbt.h>
|
#include <Dbt.h>
|
||||||
GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
|
|
||||||
0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
//linux includes
|
// unix, linux & OSX includes
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -26,124 +26,171 @@ GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
|
|||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
|
wxDEFINE_EVENT(EVT_REMOVABLE_DRIVE_EJECTED, RemovableDriveEjectEvent);
|
||||||
|
wxDEFINE_EVENT(EVT_REMOVABLE_DRIVES_CHANGED, RemovableDrivesChangedEvent);
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
/* currently not used, left for possible future use
|
std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() const
|
||||||
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
|
||||||
*/
|
|
||||||
void RemovableDriveManager::search_for_drives()
|
|
||||||
{
|
{
|
||||||
m_current_drives.clear();
|
|
||||||
//get logical drives flags by letter in alphabetical order
|
//get logical drives flags by letter in alphabetical order
|
||||||
DWORD drives_mask = GetLogicalDrives();
|
DWORD drives_mask = ::GetLogicalDrives();
|
||||||
for (size_t i = 0; i < 26; i++)
|
|
||||||
{
|
// Allocate the buffers before the loop.
|
||||||
if(drives_mask & (1 << i))
|
std::wstring volume_name;
|
||||||
{
|
volume_name.resize(MAX_PATH + 1);
|
||||||
std::string path (1,(char)('A' + i));
|
std::wstring file_system_name;
|
||||||
path+=":";
|
file_system_name.resize(MAX_PATH + 1);
|
||||||
UINT drive_type = GetDriveTypeA(path.c_str());
|
|
||||||
|
// Iterate the Windows drives from 'A' to 'Z'
|
||||||
|
std::vector<DriveData> current_drives;
|
||||||
|
for (size_t i = 0; i < 26; ++ i)
|
||||||
|
if (drives_mask & (1 << i)) {
|
||||||
|
std::string path { char('A' + i), ':' };
|
||||||
|
UINT drive_type = ::GetDriveTypeA(path.c_str());
|
||||||
// DRIVE_REMOVABLE on W are sd cards and usb thumbnails (not usb harddrives)
|
// DRIVE_REMOVABLE on W are sd cards and usb thumbnails (not usb harddrives)
|
||||||
if (drive_type == DRIVE_REMOVABLE)
|
if (drive_type == DRIVE_REMOVABLE) {
|
||||||
{
|
|
||||||
// get name of drive
|
// get name of drive
|
||||||
std::wstring wpath = boost::nowide::widen(path);
|
std::wstring wpath = boost::nowide::widen(path);
|
||||||
std::wstring volume_name;
|
BOOL error = ::GetVolumeInformationW(wpath.c_str(), volume_name.data(), sizeof(volume_name), nullptr, nullptr, nullptr, file_system_name.data(), sizeof(file_system_name));
|
||||||
volume_name.resize(1024);
|
if (error != 0) {
|
||||||
std::wstring file_system_name;
|
volume_name.erase(volume_name.begin() + wcslen(volume_name.c_str()), volume_name.end());
|
||||||
file_system_name.resize(1024);
|
if (! file_system_name.empty()) {
|
||||||
LPWSTR lp_volume_name_buffer = new wchar_t;
|
|
||||||
BOOL error = GetVolumeInformationW(wpath.c_str(), &volume_name[0], sizeof(volume_name), NULL, NULL, NULL, &file_system_name[0], sizeof(file_system_name));
|
|
||||||
if(error != 0)
|
|
||||||
{
|
|
||||||
volume_name.erase(std::find(volume_name.begin(), volume_name.end(), '\0'), volume_name.end());
|
|
||||||
if (file_system_name != L"")
|
|
||||||
{
|
|
||||||
ULARGE_INTEGER free_space;
|
ULARGE_INTEGER free_space;
|
||||||
GetDiskFreeSpaceExA(path.c_str(), &free_space, NULL, NULL);
|
::GetDiskFreeSpaceExA(path.c_str(), &free_space, nullptr, nullptr);
|
||||||
if (free_space.QuadPart > 0)
|
if (free_space.QuadPart > 0) {
|
||||||
{
|
|
||||||
path += "\\";
|
path += "\\";
|
||||||
m_current_drives.push_back(DriveData(boost::nowide::narrow(volume_name), path));
|
current_drives.emplace_back(DriveData{ boost::nowide::narrow(volume_name), path });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return current_drives;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from UI therefore it blocks the UI thread.
|
||||||
|
// It also blocks updates at the worker thread.
|
||||||
|
// Win32 implementation.
|
||||||
|
void RemovableDriveManager::eject_drive()
|
||||||
|
{
|
||||||
|
if (m_last_save_path.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
|
this->update();
|
||||||
|
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
|
|
||||||
|
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||||
|
auto it_drive_data = this->find_last_save_path_drive_data();
|
||||||
|
if (it_drive_data != m_current_drives.end()) {
|
||||||
|
// 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);
|
||||||
|
if (handle == INVALID_HANDLE_VALUE) {
|
||||||
|
std::cerr << "Ejecting " << mpath << " failed " << GetLastError() << " \n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DWORD deviceControlRetVal(0);
|
||||||
|
//these 3 commands should eject device safely but they dont, the device does disappear from file explorer but the "device was safely remove" notification doesnt trigger.
|
||||||
|
//sd cards does trigger WM_DEVICECHANGE messege, usb drives dont
|
||||||
|
DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||||
|
DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||||
|
// some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here but it returns error to me
|
||||||
|
BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
||||||
|
if (error == 0) {
|
||||||
|
CloseHandle(handle);
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Ejecting " << mpath << " failed " << deviceControlRetVal << " " << GetLastError() << " \n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CloseHandle(handle);
|
||||||
|
m_drive_data_last_eject = *it_drive_data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void RemovableDriveManager::eject_drive(const std::string &path)
|
|
||||||
|
std::string RemovableDriveManager::get_removable_drive_path(const std::string &path)
|
||||||
{
|
{
|
||||||
if(m_current_drives.empty())
|
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
return;
|
this->update();
|
||||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
|
|
||||||
|
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||||
|
if (m_current_drives.empty())
|
||||||
|
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());
|
||||||
|
for (const DriveData &drive_data : m_current_drives) {
|
||||||
|
char drive = drive_data.path[0];
|
||||||
|
if (drive == 'A' + letter)
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
return m_current_drives.front().path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RemovableDriveManager::get_removable_drive_from_path(const std::string& path)
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
for (const DriveData &drive_data : m_current_drives) {
|
||||||
|
assert(! drive_data.path.empty());
|
||||||
|
if (drive_data.path.front() == 'A' + letter)
|
||||||
|
return drive_data.path;
|
||||||
|
}
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
if ((*it).path == path)
|
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)
|
||||||
{
|
{
|
||||||
// get handle to device
|
RemovableDriveManager::get_instance().update(0, true);
|
||||||
std::string mpath = "\\\\.\\" + 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);
|
|
||||||
if (handle == INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
std::cerr << "Ejecting " << mpath << " failed " << GetLastError() << " \n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DWORD deviceControlRetVal(0);
|
|
||||||
//these 3 commands should eject device safely but they dont, the device does disappear from file explorer but the "device was safely remove" notification doesnt trigger.
|
|
||||||
//sd cards does trigger WM_DEVICECHANGE messege, usb drives dont
|
|
||||||
|
|
||||||
DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
|
||||||
DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
|
||||||
// some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here but it returns error to me
|
|
||||||
BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
|
|
||||||
if (error == 0)
|
|
||||||
{
|
|
||||||
CloseHandle(handle);
|
|
||||||
std::cerr << "Ejecting " << mpath << " failed " << deviceControlRetVal << " " << GetLastError() << " \n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CloseHandle(handle);
|
|
||||||
m_did_eject = true;
|
|
||||||
m_current_drives.erase(it);
|
|
||||||
m_ejected_path = m_last_save_path;
|
|
||||||
m_ejected_name = m_last_save_name;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
|
|
||||||
{
|
default:
|
||||||
if (m_current_drives.empty())
|
// Send all other messages on to the default windows handler.
|
||||||
return false;
|
lRet = DefWindowProc(hWnd, message, wParam, lParam);
|
||||||
std::size_t found = path.find_last_of("\\");
|
break;
|
||||||
std::string new_path = path.substr(0, found);
|
|
||||||
int letter = PathGetDriveNumberA(new_path.c_str());
|
|
||||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
|
||||||
{
|
|
||||||
char drive = (*it).path[0];
|
|
||||||
if (drive == ('A' + letter))
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return lRet;
|
||||||
}
|
|
||||||
std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
|
|
||||||
{
|
|
||||||
std::size_t found = path.find_last_of("\\");
|
|
||||||
std::string new_path = path.substr(0, found);
|
|
||||||
int letter = PathGetDriveNumberA(new_path.c_str());
|
|
||||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
|
||||||
{
|
|
||||||
char drive = (*it).path[0];
|
|
||||||
if (drive == ('A' + letter))
|
|
||||||
return (*it).path;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemovableDriveManager::register_window()
|
void RemovableDriveManager::register_window()
|
||||||
{
|
{
|
||||||
//creates new unvisible window that is recieving callbacks from system
|
//creates new unvisible window that is recieving callbacks from system
|
||||||
// structure to register
|
// structure to register
|
||||||
/* currently not used, left for possible future use
|
// currently not used, left for possible future use
|
||||||
WNDCLASSEX wndClass;
|
WNDCLASSEX wndClass;
|
||||||
wndClass.cbSize = sizeof(WNDCLASSEX);
|
wndClass.cbSize = sizeof(WNDCLASSEX);
|
||||||
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
|
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
|
||||||
@ -179,432 +226,291 @@ void RemovableDriveManager::register_window()
|
|||||||
}
|
}
|
||||||
//ShowWindow(hWnd, SW_SHOWNORMAL);
|
//ShowWindow(hWnd, SW_SHOWNORMAL);
|
||||||
UpdateWindow(hWnd);
|
UpdateWindow(hWnd);
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
/* currently not used, left for possible future use
|
#endif
|
||||||
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
namespace search_for_drives_internal
|
||||||
{
|
{
|
||||||
// here we need to catch messeges about device removal
|
static bool compare_filesystem_id(const std::string &path_a, const std::string &path_b)
|
||||||
// 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;
|
|
||||||
|
|
||||||
switch (message)
|
|
||||||
{
|
{
|
||||||
case WM_CREATE:
|
struct stat buf;
|
||||||
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
|
stat(path_a.c_str() ,&buf);
|
||||||
|
dev_t id_a = buf.st_dev;
|
||||||
|
stat(path_b.c_str() ,&buf);
|
||||||
|
dev_t id_b = buf.st_dev;
|
||||||
|
return id_a == id_b;
|
||||||
|
}
|
||||||
|
|
||||||
ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
|
void inspect_file(const std::string &path, const std::string &parent_path, std::vector<DriveData> &out)
|
||||||
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
|
//confirms if the file is removable drive and adds it to vector
|
||||||
if(wParam == DBT_DEVICEREMOVECOMPLETE)
|
|
||||||
{
|
//if not same file system - could be removable drive
|
||||||
- RemovableDriveManager::get_instance().update(0, true);
|
if (! compare_filesystem_id(path, parent_path)) {
|
||||||
|
//free space
|
||||||
|
boost::filesystem::space_info si = boost::filesystem::space(path);
|
||||||
|
if (si.available != 0) {
|
||||||
|
//user id
|
||||||
|
struct stat buf;
|
||||||
|
stat(path.c_str(), &buf);
|
||||||
|
uid_t uid = buf.st_uid;
|
||||||
|
std::string username(std::getenv("USER"));
|
||||||
|
struct passwd *pw = getpwuid(uid);
|
||||||
|
if (pw != 0 && pw->pw_name == username)
|
||||||
|
out.emplace_back(DriveData{ boost::filesystem::basename(boost::filesystem::path(path)), path });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Send all other messages on to the default windows handler.
|
|
||||||
lRet = DefWindowProc(hWnd, message, wParam, lParam);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return lRet;
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
#else
|
|
||||||
void RemovableDriveManager::search_for_drives()
|
|
||||||
{
|
|
||||||
|
|
||||||
m_current_drives.clear();
|
|
||||||
|
|
||||||
#if __APPLE__
|
|
||||||
// if on macos obj-c class will enumerate
|
|
||||||
if(m_rdmmm)
|
|
||||||
{
|
|
||||||
m_rdmmm->list_devices();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
|
|
||||||
|
static void search_path(const std::string &path, const std::string &parent_path, std::vector<DriveData> &out)
|
||||||
|
{
|
||||||
|
glob_t globbuf;
|
||||||
|
globbuf.gl_offs = 2;
|
||||||
|
int error = glob(path.c_str(), GLOB_TILDE, NULL, &globbuf);
|
||||||
|
if (error == 0) {
|
||||||
|
for (size_t i = 0; i < globbuf.gl_pathc; ++ i)
|
||||||
|
inspect_file(globbuf.gl_pathv[i], parent_path, out);
|
||||||
|
} else {
|
||||||
|
//if error - path probably doesnt exists so function just exits
|
||||||
|
//std::cout<<"glob error "<< error<< "\n";
|
||||||
|
}
|
||||||
|
globfree(&globbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() const
|
||||||
|
{
|
||||||
|
std::vector<DriveData> current_drives;
|
||||||
|
|
||||||
|
#if __APPLE__
|
||||||
|
|
||||||
|
this->list_devices(current_drives);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
//search /media/* folder
|
//search /media/* folder
|
||||||
search_path("/media/*", "/media");
|
search_for_drives_internal::search_path("/media/*", "/media", current_drives);
|
||||||
|
|
||||||
//search_path("/Volumes/*", "/Volumes");
|
//search_path("/Volumes/*", "/Volumes");
|
||||||
std::string path(std::getenv("USER"));
|
std::string path(std::getenv("USER"));
|
||||||
std::string pp(path);
|
std::string pp(path);
|
||||||
|
|
||||||
{
|
//search /media/USERNAME/* folder
|
||||||
//search /media/USERNAME/* folder
|
pp = "/media/"+pp;
|
||||||
pp = "/media/"+pp;
|
path = "/media/" + path + "/*";
|
||||||
path = "/media/" + path + "/*";
|
search_for_drives_internal::search_path(path, pp, current_drives);
|
||||||
search_path(path, pp);
|
|
||||||
|
|
||||||
//search /run/media/USERNAME/* folder
|
//search /run/media/USERNAME/* folder
|
||||||
path = "/run" + path;
|
path = "/run" + path;
|
||||||
pp = "/run"+pp;
|
pp = "/run"+pp;
|
||||||
search_path(path, pp);
|
search_for_drives_internal::search_path(path, pp, current_drives);
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
void RemovableDriveManager::search_path(const std::string &path,const std::string &parent_path)
|
|
||||||
{
|
|
||||||
glob_t globbuf;
|
|
||||||
globbuf.gl_offs = 2;
|
|
||||||
int error = glob(path.c_str(), GLOB_TILDE, NULL, &globbuf);
|
|
||||||
if(error == 0)
|
|
||||||
{
|
|
||||||
for(size_t i = 0; i < globbuf.gl_pathc; i++)
|
|
||||||
{
|
|
||||||
inspect_file(globbuf.gl_pathv[i], parent_path);
|
|
||||||
}
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
//if error - path probably doesnt exists so function just exits
|
|
||||||
//std::cout<<"glob error "<< error<< "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
globfree(&globbuf);
|
|
||||||
}
|
|
||||||
void RemovableDriveManager::inspect_file(const std::string &path, const std::string &parent_path)
|
|
||||||
{
|
|
||||||
//confirms if the file is removable drive and adds it to vector
|
|
||||||
|
|
||||||
//if not same file system - could be removable drive
|
return current_drives;
|
||||||
if(!compare_filesystem_id(path, parent_path))
|
|
||||||
{
|
|
||||||
//free space
|
|
||||||
boost::filesystem::space_info si = boost::filesystem::space(path);
|
|
||||||
if(si.available != 0)
|
|
||||||
{
|
|
||||||
//user id
|
|
||||||
struct stat buf;
|
|
||||||
stat(path.c_str(), &buf);
|
|
||||||
uid_t uid = buf.st_uid;
|
|
||||||
std::string username(std::getenv("USER"));
|
|
||||||
struct passwd *pw = getpwuid(uid);
|
|
||||||
if (pw != 0 && pw->pw_name == username)
|
|
||||||
m_current_drives.push_back(DriveData(boost::filesystem::basename(boost::filesystem::path(path)), path));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bool RemovableDriveManager::compare_filesystem_id(const std::string &path_a, const std::string &path_b)
|
|
||||||
|
// Called from UI therefore it blocks the UI thread.
|
||||||
|
// It also blocks updates at the worker thread.
|
||||||
|
// Unix & OSX implementation.
|
||||||
|
void RemovableDriveManager::eject_drive()
|
||||||
{
|
{
|
||||||
struct stat buf;
|
if (m_last_save_path.empty())
|
||||||
stat(path_a.c_str() ,&buf);
|
|
||||||
dev_t id_a = buf.st_dev;
|
|
||||||
stat(path_b.c_str() ,&buf);
|
|
||||||
dev_t id_b = buf.st_dev;
|
|
||||||
return id_a == id_b;
|
|
||||||
}
|
|
||||||
void RemovableDriveManager::eject_drive(const std::string &path)
|
|
||||||
{
|
|
||||||
if (m_current_drives.empty())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
{
|
this->update();
|
||||||
if((*it).path == path)
|
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
{
|
|
||||||
|
|
||||||
std::string correct_path(path);
|
|
||||||
for (size_t i = 0; i < correct_path.size(); ++i)
|
|
||||||
{
|
|
||||||
if(correct_path[i]==' ')
|
|
||||||
{
|
|
||||||
correct_path = correct_path.insert(i,1,'\\');
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
|
|
||||||
// there is no usable command in c++ so terminal command is used instead
|
|
||||||
// but neither triggers "succesful safe removal messege"
|
|
||||||
std::string command = "";
|
|
||||||
#if __APPLE__
|
|
||||||
//m_rdmmm->eject_device(path);
|
|
||||||
command = "diskutil unmount ";
|
|
||||||
#else
|
|
||||||
command = "umount ";
|
|
||||||
#endif
|
|
||||||
command += correct_path;
|
|
||||||
int err = system(command.c_str());
|
|
||||||
if(err)
|
|
||||||
{
|
|
||||||
std::cerr<<"Ejecting failed\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_did_eject = true;
|
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||||
m_current_drives.erase(it);
|
auto it_drive_data = this->find_last_save_path_drive_data();
|
||||||
m_ejected_path = m_last_save_path;
|
if (it_drive_data != m_current_drives.end()) {
|
||||||
m_ejected_name = m_last_save_name;
|
std::string correct_path(m_last_save_path);
|
||||||
break;
|
for (size_t i = 0; i < correct_path.size(); ++i)
|
||||||
|
if (correct_path[i]==' ') {
|
||||||
|
correct_path = correct_path.insert(i,1,'\\');
|
||||||
|
++ i;
|
||||||
|
}
|
||||||
|
//std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
|
||||||
|
// there is no usable command in c++ so terminal command is used instead
|
||||||
|
// but neither triggers "succesful safe removal messege"
|
||||||
|
std::string command =
|
||||||
|
#if __APPLE__
|
||||||
|
//this->eject_device(m_last_save_path);
|
||||||
|
"diskutil unmount ";
|
||||||
|
#else
|
||||||
|
"umount ";
|
||||||
|
#endif
|
||||||
|
command += correct_path;
|
||||||
|
int err = system(command.c_str());
|
||||||
|
if (err) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Ejecting " << m_last_save_path << " failed";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_drive_data_last_eject = *it_drive_data;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
std::string RemovableDriveManager::get_removable_drive_path(const std::string &path)
|
||||||
bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
|
|
||||||
{
|
{
|
||||||
if (m_current_drives.empty())
|
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
return false;
|
this->update();
|
||||||
|
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
|
|
||||||
std::size_t found = path.find_last_of("/");
|
std::size_t found = path.find_last_of("/");
|
||||||
std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
|
std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
|
||||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
|
||||||
{
|
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||||
if(compare_filesystem_id(new_path, (*it).path))
|
for (const DriveData &data : m_current_drives)
|
||||||
return true;
|
if (search_for_drives_internal::compare_filesystem_id(new_path, data.path))
|
||||||
}
|
return path;
|
||||||
return false;
|
return m_current_drives.empty() ? std::string() : m_current_drives.front().path;
|
||||||
}
|
}
|
||||||
std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
|
|
||||||
|
std::string RemovableDriveManager::get_removable_drive_from_path(const std::string& path)
|
||||||
{
|
{
|
||||||
std::size_t found = path.find_last_of("/");
|
std::size_t found = path.find_last_of("/");
|
||||||
std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
|
std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
|
||||||
|
|
||||||
// trim the filename
|
// trim the filename
|
||||||
found = new_path.find_last_of("/");
|
found = new_path.find_last_of("/");
|
||||||
new_path = new_path.substr(0, found);
|
new_path = new_path.substr(0, found);
|
||||||
|
|
||||||
//check if same filesystem
|
// check if same filesystem
|
||||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||||
{
|
for (const DriveData &drive_data : m_current_drives)
|
||||||
if (compare_filesystem_id(new_path, (*it).path))
|
if (search_for_drives_internal::compare_filesystem_id(new_path, drive_data.path))
|
||||||
return (*it).path;
|
return drive_data.path;
|
||||||
}
|
return std::string();
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
RemovableDriveManager::RemovableDriveManager():
|
void RemovableDriveManager::init(wxEvtHandler *callback_evt_handler)
|
||||||
m_drives_count(0),
|
|
||||||
m_last_update(0),
|
|
||||||
m_last_save_path(""),
|
|
||||||
m_last_save_name(""),
|
|
||||||
m_last_save_path_verified(false),
|
|
||||||
m_is_writing(false),
|
|
||||||
m_did_eject(false),
|
|
||||||
m_plater_ready_to_slice(true),
|
|
||||||
m_ejected_path(""),
|
|
||||||
m_ejected_name("")
|
|
||||||
#if __APPLE__
|
|
||||||
, m_rdmmm(new RDMMMWrapper())
|
|
||||||
#endif
|
|
||||||
{}
|
|
||||||
RemovableDriveManager::~RemovableDriveManager()
|
|
||||||
{
|
{
|
||||||
#if __APPLE__
|
assert(! m_initialized);
|
||||||
delete m_rdmmm;
|
assert(m_callback_evt_handler == nullptr);
|
||||||
#endif
|
|
||||||
}
|
if (m_initialized)
|
||||||
void RemovableDriveManager::init()
|
return;
|
||||||
{
|
|
||||||
//add_callback([](void) { RemovableDriveManager::get_instance().print(); });
|
m_initialized = true;
|
||||||
|
m_callback_evt_handler = callback_evt_handler;
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
//register_window();
|
//this->register_window_msw();
|
||||||
#elif __APPLE__
|
#elif __APPLE__
|
||||||
m_rdmmm->register_window();
|
this->register_window_osx();
|
||||||
#endif
|
#endif
|
||||||
update(0, true);
|
|
||||||
}
|
#ifdef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
bool RemovableDriveManager::update(const long time,const bool check)
|
this->update();
|
||||||
{
|
#else // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
if(time != 0) //time = 0 is forced update
|
// Don't call update() manually, as the UI triggered APIs call this->update() anyways.
|
||||||
{
|
m_thread = boost::thread((boost::bind(&RemovableDriveManager::thread_proc, this)));
|
||||||
long diff = m_last_update - time;
|
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
if(diff <= -2)
|
|
||||||
{
|
|
||||||
m_last_update = time;
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
return false; // return value shouldnt matter if update didnt run
|
|
||||||
}
|
|
||||||
}
|
|
||||||
search_for_drives();
|
|
||||||
if (m_drives_count != m_current_drives.size())
|
|
||||||
{
|
|
||||||
if (check)
|
|
||||||
{
|
|
||||||
check_and_notify();
|
|
||||||
}
|
|
||||||
m_drives_count = m_current_drives.size();
|
|
||||||
}
|
|
||||||
return !m_current_drives.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RemovableDriveManager::is_drive_mounted(const std::string &path) const
|
void RemovableDriveManager::shutdown()
|
||||||
{
|
{
|
||||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
{
|
if (m_thread.joinable()) {
|
||||||
if ((*it).path == path)
|
// Stop the worker thread, if running.
|
||||||
{
|
{
|
||||||
return true;
|
// Notify the worker thread to cancel wait on detection polling.
|
||||||
|
std::lock_guard<std::mutex> lck(m_thread_stop_mutex);
|
||||||
|
m_stop = true;
|
||||||
|
}
|
||||||
|
m_thread_stop_condition.notify_all();
|
||||||
|
// Wait for the worker thread to stop.
|
||||||
|
m_thread.join();
|
||||||
|
m_stop = false;
|
||||||
|
}
|
||||||
|
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
//this->unregister_window_msw();
|
||||||
|
#elif __APPLE__
|
||||||
|
this->unregister_window_osx();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RemovableDriveManager::set_and_verify_last_save_path(const std::string &path)
|
||||||
|
{
|
||||||
|
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
|
this->update();
|
||||||
|
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
|
|
||||||
|
m_last_save_path = this->get_removable_drive_from_path(path);
|
||||||
|
return ! m_last_save_path.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status()
|
||||||
|
{
|
||||||
|
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
|
this->update();
|
||||||
|
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
|
|
||||||
|
RemovableDriveManager::RemovableDrivesStatus out;
|
||||||
|
{
|
||||||
|
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||||
|
out.has_eject = this->find_last_save_path_drive_data() != m_current_drives.end();
|
||||||
|
out.has_removable_drives = ! m_current_drives.empty();
|
||||||
|
}
|
||||||
|
if (! out.has_eject)
|
||||||
|
m_last_save_path.clear();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is called from thread_proc() and from most of the public methods on demand.
|
||||||
|
void RemovableDriveManager::update()
|
||||||
|
{
|
||||||
|
std::vector<DriveData> current_drives = this->search_for_removable_drives();
|
||||||
|
|
||||||
|
// Post update events.
|
||||||
|
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||||
|
std::sort(current_drives.begin(), current_drives.end());
|
||||||
|
if (current_drives != m_current_drives) {
|
||||||
|
if (! m_drive_data_last_eject.empty() && std::find(current_drives.begin(), current_drives.end(), m_drive_data_last_eject) == current_drives.end()) {
|
||||||
|
assert(m_callback_evt_handler);
|
||||||
|
if (m_callback_evt_handler)
|
||||||
|
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::move(m_drive_data_last_eject)));
|
||||||
|
m_drive_data_last_eject.clear();
|
||||||
|
} else {
|
||||||
|
assert(m_callback_evt_handler);
|
||||||
|
if (m_callback_evt_handler)
|
||||||
|
wxPostEvent(m_callback_evt_handler, RemovableDrivesChangedEvent(EVT_REMOVABLE_DRIVES_CHANGED));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
m_current_drives = std::move(current_drives);
|
||||||
}
|
}
|
||||||
std::string RemovableDriveManager::get_drive_path()
|
|
||||||
|
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
|
void RemovableDriveManager::thread_proc()
|
||||||
{
|
{
|
||||||
if (m_current_drives.size() == 0)
|
for (;;) {
|
||||||
{
|
// Wait for 2 seconds before running the disk enumeration.
|
||||||
reset_last_save_path();
|
// Cancellable.
|
||||||
return "";
|
|
||||||
}
|
|
||||||
if (m_last_save_path_verified)
|
|
||||||
return m_last_save_path;
|
|
||||||
return m_current_drives.back().path;
|
|
||||||
}
|
|
||||||
std::string RemovableDriveManager::get_last_save_path() const
|
|
||||||
{
|
|
||||||
if (!m_last_save_path_verified)
|
|
||||||
return "";
|
|
||||||
return m_last_save_path;
|
|
||||||
}
|
|
||||||
std::string RemovableDriveManager::get_last_save_name() const
|
|
||||||
{
|
|
||||||
return m_last_save_name;
|
|
||||||
}
|
|
||||||
std::vector<DriveData> RemovableDriveManager::get_all_drives() const
|
|
||||||
{
|
|
||||||
return m_current_drives;
|
|
||||||
}
|
|
||||||
void RemovableDriveManager::check_and_notify()
|
|
||||||
{
|
|
||||||
if(m_drive_count_changed_callback)
|
|
||||||
{
|
|
||||||
m_drive_count_changed_callback(m_plater_ready_to_slice);
|
|
||||||
}
|
|
||||||
if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && !is_drive_mounted(m_last_save_path))
|
|
||||||
{
|
|
||||||
for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it)
|
|
||||||
{
|
{
|
||||||
(*it)();
|
std::unique_lock<std::mutex> lck(m_thread_stop_mutex);
|
||||||
|
m_thread_stop_condition.wait_for(lck, std::chrono::seconds(2), [this]{ return m_stop; });
|
||||||
}
|
}
|
||||||
|
if (m_stop)
|
||||||
|
// Stop the worker thread.
|
||||||
|
break;
|
||||||
|
// Update m_current drives and send out update events.
|
||||||
|
this->update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void RemovableDriveManager::add_remove_callback(std::function<void()> callback)
|
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
|
|
||||||
|
std::vector<DriveData>::const_iterator RemovableDriveManager::find_last_save_path_drive_data() const
|
||||||
{
|
{
|
||||||
m_callbacks.push_back(callback);
|
return Slic3r::binary_find_by_predicate(m_current_drives.begin(), m_current_drives.end(),
|
||||||
|
[this](const DriveData &data){ return data.path < m_last_save_path; },
|
||||||
|
[this](const DriveData &data){ return data.path == m_last_save_path; });
|
||||||
}
|
}
|
||||||
void RemovableDriveManager::erase_callbacks()
|
|
||||||
{
|
}} // namespace Slic3r::GUI
|
||||||
m_callbacks.clear();
|
|
||||||
}
|
|
||||||
void RemovableDriveManager::set_drive_count_changed_callback(std::function<void(const bool)> callback)
|
|
||||||
{
|
|
||||||
m_drive_count_changed_callback = callback;
|
|
||||||
}
|
|
||||||
void RemovableDriveManager::set_plater_ready_to_slice(bool b)
|
|
||||||
{
|
|
||||||
m_plater_ready_to_slice = b;
|
|
||||||
}
|
|
||||||
void RemovableDriveManager::set_last_save_path(const std::string& path)
|
|
||||||
{
|
|
||||||
if(m_last_save_path_verified)// if old path is on drive
|
|
||||||
{
|
|
||||||
if(get_drive_from_path(path) != "") //and new is too, rewrite the path
|
|
||||||
{
|
|
||||||
m_last_save_path_verified = false;
|
|
||||||
m_last_save_path = path;
|
|
||||||
}//else do nothing
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
m_last_save_path = path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void RemovableDriveManager::verify_last_save_path()
|
|
||||||
{
|
|
||||||
std::string last_drive = get_drive_from_path(m_last_save_path);
|
|
||||||
if (last_drive != "")
|
|
||||||
{
|
|
||||||
m_last_save_path_verified = true;
|
|
||||||
m_last_save_path = last_drive;
|
|
||||||
m_last_save_name = get_drive_name(last_drive);
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
reset_last_save_path();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::string RemovableDriveManager::get_drive_name(const std::string& path) const
|
|
||||||
{
|
|
||||||
if (m_current_drives.size() == 0)
|
|
||||||
return "";
|
|
||||||
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
|
|
||||||
{
|
|
||||||
if ((*it).path == path)
|
|
||||||
{
|
|
||||||
return (*it).name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
bool RemovableDriveManager::is_last_drive_removed()
|
|
||||||
{
|
|
||||||
if(!m_last_save_path_verified)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool r = !is_drive_mounted(m_last_save_path);
|
|
||||||
if (r)
|
|
||||||
{
|
|
||||||
reset_last_save_path();
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
bool RemovableDriveManager::is_last_drive_removed_with_update(const long time)
|
|
||||||
{
|
|
||||||
update(time, false);
|
|
||||||
return is_last_drive_removed();
|
|
||||||
}
|
|
||||||
void RemovableDriveManager::reset_last_save_path()
|
|
||||||
{
|
|
||||||
m_last_save_path_verified = false;
|
|
||||||
m_last_save_path = "";
|
|
||||||
m_last_save_name = "";
|
|
||||||
}
|
|
||||||
void RemovableDriveManager::set_is_writing(const bool b)
|
|
||||||
{
|
|
||||||
m_is_writing = b;
|
|
||||||
if (b)
|
|
||||||
{
|
|
||||||
m_did_eject = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool RemovableDriveManager::get_is_writing() const
|
|
||||||
{
|
|
||||||
return m_is_writing;
|
|
||||||
}
|
|
||||||
bool RemovableDriveManager::get_did_eject() const
|
|
||||||
{
|
|
||||||
return m_did_eject;
|
|
||||||
}
|
|
||||||
void RemovableDriveManager::set_did_eject(const bool b)
|
|
||||||
{
|
|
||||||
m_did_eject = b;
|
|
||||||
}
|
|
||||||
size_t RemovableDriveManager::get_drives_count() const
|
|
||||||
{
|
|
||||||
return m_current_drives.size();
|
|
||||||
}
|
|
||||||
std::string RemovableDriveManager::get_ejected_path() const
|
|
||||||
{
|
|
||||||
return m_ejected_path;
|
|
||||||
}
|
|
||||||
std::string RemovableDriveManager::get_ejected_name() const
|
|
||||||
{
|
|
||||||
return m_ejected_name;
|
|
||||||
}
|
|
||||||
}}//namespace Slicer::Gui
|
|
||||||
|
@ -3,119 +3,129 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
|
||||||
|
#include <boost/thread.hpp>
|
||||||
|
#include <tbb/mutex.h>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
// Custom wxWidget events
|
||||||
|
#include "Event.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
#if __APPLE__
|
|
||||||
class RDMMMWrapper;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct DriveData
|
struct DriveData
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string path;
|
std::string path;
|
||||||
DriveData(std::string n, std::string p):name(n),path(p){}
|
|
||||||
|
void clear() {
|
||||||
|
name.clear();
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
bool empty() const {
|
||||||
|
return path.empty();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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; }
|
||||||
|
inline bool operator==(const DriveData &lhs, const DriveData &rhs) { return lhs.path == rhs.path; }
|
||||||
|
|
||||||
|
using RemovableDriveEjectEvent = Event<DriveData>;
|
||||||
|
wxDECLARE_EVENT(EVT_REMOVABLE_DRIVE_EJECTED, RemovableDriveEjectEvent);
|
||||||
|
|
||||||
|
using RemovableDrivesChangedEvent = SimpleEvent;
|
||||||
|
wxDECLARE_EVENT(EVT_REMOVABLE_DRIVES_CHANGED, RemovableDrivesChangedEvent);
|
||||||
|
|
||||||
|
#if __APPLE__
|
||||||
|
// Callbacks on device plug / unplug work reliably on OSX.
|
||||||
|
#define REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
|
#endif // __APPLE__
|
||||||
|
|
||||||
class RemovableDriveManager
|
class RemovableDriveManager
|
||||||
{
|
{
|
||||||
#if __APPLE__
|
|
||||||
friend class RDMMMWrapper;
|
|
||||||
#endif
|
|
||||||
public:
|
public:
|
||||||
static RemovableDriveManager& get_instance()
|
RemovableDriveManager() = default;
|
||||||
{
|
|
||||||
static RemovableDriveManager instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
RemovableDriveManager(RemovableDriveManager const&) = delete;
|
RemovableDriveManager(RemovableDriveManager const&) = delete;
|
||||||
void operator=(RemovableDriveManager const&) = delete;
|
void operator=(RemovableDriveManager const&) = delete;
|
||||||
~RemovableDriveManager();
|
~RemovableDriveManager() { assert(! m_initialized); }
|
||||||
//call only once. on apple register for unmnount callbacks. on windows register for device notification is prepared but not called (eject usb drive on widnows doesnt trigger the callback, sdc ard does), also enumerates devices for first time so init shoud be called on linux too.
|
|
||||||
void init();
|
// Start the background thread and register this window as a target for update events.
|
||||||
//update() searches for removable devices, returns false if empty. /time = 0 is forced update, time expects wxGetLocalTime()
|
// Register for OSX notifications.
|
||||||
bool update(const long time = 0,const bool check = false);
|
void init(wxEvtHandler *callback_evt_handler);
|
||||||
bool is_drive_mounted(const std::string &path) const;
|
// Stop the background thread of the removable drive manager, so that no new updates will be sent out.
|
||||||
void eject_drive(const std::string &path);
|
// Deregister OSX notifications.
|
||||||
//returns path to last drive which was used, if none was used, returns device that was enumerated last
|
void shutdown();
|
||||||
std::string get_last_save_path() const;
|
|
||||||
std::string get_last_save_name() const;
|
// Returns path to a removable media if it exists, prefering the input path.
|
||||||
//returns path to last drive which was used, if none was used, returns empty string
|
std::string get_removable_drive_path(const std::string &path);
|
||||||
std::string get_drive_path();
|
bool is_path_on_removable_drive(const std::string &path) { return this->get_removable_drive_path(path) == path; }
|
||||||
std::vector<DriveData> get_all_drives() const;
|
|
||||||
bool is_path_on_removable_drive(const std::string &path);
|
// Verify whether the path provided is on removable media. If so, save the path for further eject and return true, otherwise return false.
|
||||||
// callback will notify only if device with last save path was removed
|
bool set_and_verify_last_save_path(const std::string &path);
|
||||||
void add_remove_callback(std::function<void()> callback);
|
// Eject drive of a file set by set_and_verify_last_save_path().
|
||||||
// erases all remove callbacks added by add_remove_callback()
|
void eject_drive();
|
||||||
void erase_callbacks();
|
|
||||||
//drive_count_changed callback is called on every added or removed device
|
struct RemovableDrivesStatus {
|
||||||
void set_drive_count_changed_callback(std::function<void(const bool)> callback);
|
bool has_removable_drives { false };
|
||||||
//thi serves to set correct value for drive_count_changed callback
|
bool has_eject { false };
|
||||||
void set_plater_ready_to_slice(bool b);
|
};
|
||||||
// marks one of the eveices in vector as last used
|
RemovableDrivesStatus status();
|
||||||
void set_last_save_path(const std::string &path);
|
|
||||||
void verify_last_save_path();
|
// Enumerates current drives and sends out wxWidget events on change or eject.
|
||||||
bool is_last_drive_removed();
|
// Called by each public method, by the background thread and from RemovableDriveManagerMM::on_device_unmount OSX notification handler.
|
||||||
// param as update()
|
// Not to be called manually.
|
||||||
bool is_last_drive_removed_with_update(const long time = 0);
|
// Public to be accessible from RemovableDriveManagerMM::on_device_unmount OSX notification handler.
|
||||||
void set_is_writing(const bool b);
|
// It would be better to make this method private and friend to RemovableDriveManagerMM, but RemovableDriveManagerMM is an ObjectiveC class.
|
||||||
bool get_is_writing() const;
|
void update();
|
||||||
bool get_did_eject() const;
|
|
||||||
void set_did_eject(const bool b);
|
private:
|
||||||
std::string get_drive_name(const std::string& path) const;
|
bool m_initialized { false };
|
||||||
size_t get_drives_count() const;
|
wxEvtHandler* m_callback_evt_handler { nullptr };
|
||||||
std::string get_ejected_path() const;
|
|
||||||
std::string get_ejected_name() const;
|
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
private:
|
// Worker thread, worker thread synchronization and callbacks to the UI thread.
|
||||||
RemovableDriveManager();
|
void thread_proc();
|
||||||
void search_for_drives();
|
boost::thread m_thread;
|
||||||
//triggers callbacks if last used drive was removed
|
std::condition_variable m_thread_stop_condition;
|
||||||
void check_and_notify();
|
mutable std::mutex m_thread_stop_mutex;
|
||||||
//returns drive path (same as path in DriveData) if exists otherwise empty string ""
|
bool m_stop { false };
|
||||||
std::string get_drive_from_path(const std::string& path);
|
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
|
||||||
void reset_last_save_path();
|
|
||||||
|
// Called from update() to enumerate removable drives.
|
||||||
|
std::vector<DriveData> search_for_removable_drives() const;
|
||||||
|
|
||||||
|
// m_current_drives is guarded by m_drives_mutex
|
||||||
|
// sorted ascending by path
|
||||||
|
std::vector<DriveData> m_current_drives;
|
||||||
|
// When user requested an eject, the drive to be forcefuly ejected is stored here, so the next update will
|
||||||
|
// recognize that the eject was finished with success and an eject event is sent out.
|
||||||
|
// guarded with m_drives_mutex
|
||||||
|
DriveData m_drive_data_last_eject;
|
||||||
|
mutable tbb::mutex m_drives_mutex;
|
||||||
|
|
||||||
|
// Returns drive path (same as path in DriveData) if exists otherwise empty string.
|
||||||
|
std::string get_removable_drive_from_path(const std::string& path);
|
||||||
|
// Returns iterator to a drive in m_current_drives with path equal to m_last_save_path or end().
|
||||||
|
std::vector<DriveData>::const_iterator find_last_save_path_drive_data() const;
|
||||||
|
// Set with set_and_verify_last_save_path() to a removable drive path to be ejected.
|
||||||
|
std::string m_last_save_path;
|
||||||
|
|
||||||
std::vector<DriveData> m_current_drives;
|
|
||||||
std::vector<std::function<void()>> m_callbacks;
|
|
||||||
std::function<void(const bool)> m_drive_count_changed_callback;
|
|
||||||
size_t m_drives_count;
|
|
||||||
long m_last_update;
|
|
||||||
std::string m_last_save_path;
|
|
||||||
bool m_last_save_path_verified;
|
|
||||||
std::string m_last_save_name;
|
|
||||||
bool m_is_writing;//on device
|
|
||||||
bool m_did_eject;
|
|
||||||
bool m_plater_ready_to_slice;
|
|
||||||
std::string m_ejected_path;
|
|
||||||
std::string m_ejected_name;
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
//registers for notifications by creating invisible window
|
//registers for notifications by creating invisible window
|
||||||
void register_window();
|
//void register_window_msw();
|
||||||
#else
|
#elif __APPLE__
|
||||||
#if __APPLE__
|
void register_window_osx();
|
||||||
RDMMMWrapper * m_rdmmm;
|
void unregister_window_osx();
|
||||||
#endif
|
void list_devices(std::vector<DriveData> &out) const;
|
||||||
void search_path(const std::string &path, const std::string &parent_path);
|
// not used as of now
|
||||||
bool compare_filesystem_id(const std::string &path_a, const std::string &path_b);
|
|
||||||
void inspect_file(const std::string &path, const std::string &parent_path);
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
// apple wrapper for RemovableDriveManagerMM which searches for drives and/or ejects them
|
|
||||||
#if __APPLE__
|
|
||||||
class RDMMMWrapper
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RDMMMWrapper();
|
|
||||||
~RDMMMWrapper();
|
|
||||||
void register_window();
|
|
||||||
void list_devices();
|
|
||||||
void eject_device(const std::string &path);
|
void eject_device(const std::string &path);
|
||||||
void log(const std::string &msg);
|
// Opaque pointer to RemovableDriveManagerMM
|
||||||
protected:
|
void *m_impl_osx;
|
||||||
void *m_imp;
|
#endif
|
||||||
//friend void RemovableDriveManager::inspect_file(const std::string &path, const std::string &parent_path);
|
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
}}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif // slic3r_GUI_RemovableDriveManager_hpp_
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#import "RemovableDriveManager.hpp"
|
#import "RemovableDriveManager.hpp"
|
||||||
#import "RemovableDriveManagerMM.h"
|
#import "RemovableDriveManagerMM.h"
|
||||||
|
#import "GUI_App.hpp"
|
||||||
#import <AppKit/AppKit.h>
|
#import <AppKit/AppKit.h>
|
||||||
#import <DiskArbitration/DiskArbitration.h>
|
#import <DiskArbitration/DiskArbitration.h>
|
||||||
|
|
||||||
@ -10,22 +11,23 @@
|
|||||||
-(instancetype) init
|
-(instancetype) init
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if(self)
|
//if(self){}
|
||||||
{
|
|
||||||
}
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) on_device_unmount: (NSNotification*) notification
|
-(void) on_device_unmount: (NSNotification*) notification
|
||||||
{
|
{
|
||||||
NSLog(@"on device change");
|
//NSLog(@"on device change");
|
||||||
Slic3r::GUI::RemovableDriveManager::get_instance().update(0,true);
|
Slic3r::GUI::wxGetApp().removable_drive_manager()->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) add_unmount_observer
|
-(void) add_unmount_observer
|
||||||
{
|
{
|
||||||
NSLog(@"add unmount observer");
|
//NSLog(@"add unmount observer");
|
||||||
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: @selector(on_device_unmount:) name:NSWorkspaceDidUnmountNotification object:nil];
|
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: @selector(on_device_unmount:) name:NSWorkspaceDidUnmountNotification object:nil];
|
||||||
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: @selector(on_device_unmount:) name:NSWorkspaceDidMountNotification object:nil];
|
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: @selector(on_device_unmount:) name:NSWorkspaceDidMountNotification object:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(NSArray*) list_dev
|
-(NSArray*) list_dev
|
||||||
{
|
{
|
||||||
// DEPRICATED:
|
// DEPRICATED:
|
||||||
@ -40,118 +42,99 @@
|
|||||||
DADiskRef disk;
|
DADiskRef disk;
|
||||||
DASessionRef session;
|
DASessionRef session;
|
||||||
CFDictionaryRef descDict;
|
CFDictionaryRef descDict;
|
||||||
session = DASessionCreate(NULL);
|
session = DASessionCreate(nullptr);
|
||||||
if (session == NULL) {
|
if (session == nullptr)
|
||||||
err = EINVAL;
|
err = EINVAL;
|
||||||
}
|
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
disk = DADiskCreateFromVolumePath(NULL,session,(CFURLRef)volURL);
|
disk = DADiskCreateFromVolumePath(nullptr,session,(CFURLRef)volURL);
|
||||||
if (session == NULL) {
|
if (session == nullptr)
|
||||||
err = EINVAL;
|
err = EINVAL;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
descDict = DADiskCopyDescription(disk);
|
descDict = DADiskCopyDescription(disk);
|
||||||
if (descDict == NULL) {
|
if (descDict == nullptr)
|
||||||
err = EINVAL;
|
err = EINVAL;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
CFTypeRef mediaEjectableKey = CFDictionaryGetValue(descDict,kDADiskDescriptionMediaEjectableKey);
|
CFTypeRef mediaEjectableKey = CFDictionaryGetValue(descDict,kDADiskDescriptionMediaEjectableKey);
|
||||||
BOOL ejectable = [mediaEjectableKey boolValue];
|
BOOL ejectable = [mediaEjectableKey boolValue];
|
||||||
CFTypeRef deviceProtocolName = CFDictionaryGetValue(descDict,kDADiskDescriptionDeviceProtocolKey);
|
CFTypeRef deviceProtocolName = CFDictionaryGetValue(descDict,kDADiskDescriptionDeviceProtocolKey);
|
||||||
CFTypeRef deviceModelKey = CFDictionaryGetValue(descDict, kDADiskDescriptionDeviceModelKey);
|
CFTypeRef deviceModelKey = CFDictionaryGetValue(descDict, kDADiskDescriptionDeviceModelKey);
|
||||||
if (mediaEjectableKey != NULL)
|
if (mediaEjectableKey != nullptr) {
|
||||||
{
|
|
||||||
BOOL op = ejectable && (CFEqual(deviceProtocolName, CFSTR("USB")) || CFEqual(deviceModelKey, CFSTR("SD Card Reader")));
|
BOOL op = ejectable && (CFEqual(deviceProtocolName, CFSTR("USB")) || CFEqual(deviceModelKey, CFSTR("SD Card Reader")));
|
||||||
//!CFEqual(deviceModelKey, CFSTR("Disk Image"));
|
//!CFEqual(deviceModelKey, CFSTR("Disk Image"));
|
||||||
//
|
if (op)
|
||||||
if (op) {
|
|
||||||
[result addObject:volURL.path];
|
[result addObject:volURL.path];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (descDict != NULL) {
|
if (descDict != nullptr)
|
||||||
CFRelease(descDict);
|
CFRelease(descDict);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//this eject drive is not used now
|
||||||
-(void)eject_drive:(NSString *)path
|
-(void)eject_drive:(NSString *)path
|
||||||
{
|
{
|
||||||
DADiskRef disk;
|
DADiskRef disk;
|
||||||
DASessionRef session;
|
DASessionRef session;
|
||||||
NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
|
NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
|
||||||
int err = 0;
|
int err = 0;
|
||||||
session = DASessionCreate(NULL);
|
session = DASessionCreate(nullptr);
|
||||||
if (session == NULL) {
|
if (session == nullptr)
|
||||||
err = EINVAL;
|
err = EINVAL;
|
||||||
}
|
if (err == 0)
|
||||||
if (err == 0) {
|
disk = DADiskCreateFromVolumePath(nullptr,session,(CFURLRef)url);
|
||||||
disk = DADiskCreateFromVolumePath(NULL,session,(CFURLRef)url);
|
|
||||||
}
|
|
||||||
if( err == 0)
|
if( err == 0)
|
||||||
{
|
DADiskUnmount(disk, kDADiskUnmountOptionDefault, nullptr, nullptr);
|
||||||
DADiskUnmount(disk, kDADiskUnmountOptionDefault,
|
if (disk != nullptr)
|
||||||
NULL, NULL);
|
|
||||||
}
|
|
||||||
if (disk != NULL) {
|
|
||||||
CFRelease(disk);
|
CFRelease(disk);
|
||||||
}
|
if (session != nullptr)
|
||||||
if (session != NULL) {
|
|
||||||
CFRelease(session);
|
CFRelease(session);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
namespace Slic3r {
|
|
||||||
namespace GUI {
|
|
||||||
RDMMMWrapper::RDMMMWrapper():m_imp(nullptr){
|
|
||||||
m_imp = [[RemovableDriveManagerMM alloc] init];
|
|
||||||
}
|
|
||||||
RDMMMWrapper::~RDMMMWrapper()
|
|
||||||
{
|
|
||||||
if(m_imp)
|
|
||||||
{
|
|
||||||
[m_imp release];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void RDMMMWrapper::register_window()
|
|
||||||
{
|
|
||||||
if(m_imp)
|
|
||||||
{
|
|
||||||
[m_imp add_unmount_observer];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void RDMMMWrapper::list_devices()
|
|
||||||
{
|
|
||||||
if(m_imp)
|
|
||||||
{
|
|
||||||
NSArray* devices = [m_imp list_dev];
|
|
||||||
for (NSString* volumePath in devices)
|
|
||||||
{
|
|
||||||
NSLog(@"%@", volumePath);
|
|
||||||
Slic3r::GUI::RemovableDriveManager::get_instance().inspect_file(std::string([volumePath UTF8String]), "/Volumes");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void RDMMMWrapper::log(const std::string &msg)
|
|
||||||
{
|
|
||||||
NSLog(@"%s", msg.c_str());
|
|
||||||
}
|
|
||||||
void RDMMMWrapper::eject_device(const std::string &path)
|
|
||||||
{
|
|
||||||
if(m_imp)
|
|
||||||
{
|
|
||||||
NSString * pth = [NSString stringWithCString:path.c_str()
|
|
||||||
encoding:[NSString defaultCStringEncoding]];
|
|
||||||
[m_imp eject_drive:pth];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}//namespace Slicer::GUI
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
void RemovableDriveManager::register_window_osx()
|
||||||
|
{
|
||||||
|
assert(m_impl_osx == nullptr);
|
||||||
|
m_impl_osx = [[RemovableDriveManagerMM alloc] init];
|
||||||
|
if (m_impl_osx)
|
||||||
|
[m_impl_osx add_unmount_observer];
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemovableDriveManager::unregister_window_osx()
|
||||||
|
{
|
||||||
|
if (m_impl_osx)
|
||||||
|
[m_impl_osx release];
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace search_for_drives_internal
|
||||||
|
{
|
||||||
|
void inspect_file(const std::string &path, const std::string &parent_path, std::vector<DriveData> &out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemovableDriveManager::list_devices(std::vector<DriveData> &out) const
|
||||||
|
{
|
||||||
|
assert(m_impl_osx != nullptr);
|
||||||
|
if (m_impl_osx) {
|
||||||
|
NSArray* devices = [m_impl_osx list_dev];
|
||||||
|
for (NSString* volumePath in devices)
|
||||||
|
search_for_drives_internal::inspect_file(std::string([volumePath UTF8String]), "/Volumes", out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not used as of now
|
||||||
|
void RemovableDriveManager::eject_device(const std::string &path)
|
||||||
|
{
|
||||||
|
assert(m_impl_osx != nullptr);
|
||||||
|
if (m_impl_osx) {
|
||||||
|
NSString * pth = [NSString stringWithCString:path.c_str() encoding:[NSString defaultCStringEncoding]];
|
||||||
|
[m_impl_osx eject_drive:pth];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}}//namespace Slicer::GUI
|
||||||
|
@ -29,7 +29,7 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
|||||||
|
|
||||||
add_subdirectory(libnest2d)
|
add_subdirectory(libnest2d)
|
||||||
add_subdirectory(libslic3r)
|
add_subdirectory(libslic3r)
|
||||||
add_subdirectory(timeutils)
|
add_subdirectory(slic3rutils)
|
||||||
add_subdirectory(fff_print)
|
add_subdirectory(fff_print)
|
||||||
add_subdirectory(sla_print)
|
add_subdirectory(sla_print)
|
||||||
add_subdirectory(cpp17 EXCLUDE_FROM_ALL) # does not have to be built all the time
|
add_subdirectory(cpp17 EXCLUDE_FROM_ALL) # does not have to be built all the time
|
||||||
|
@ -13,6 +13,7 @@ add_executable(${_TEST_NAME}_tests
|
|||||||
test_stl.cpp
|
test_stl.cpp
|
||||||
test_meshsimplify.cpp
|
test_meshsimplify.cpp
|
||||||
test_meshboolean.cpp
|
test_meshboolean.cpp
|
||||||
|
test_timeutils.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (TARGET OpenVDB::openvdb)
|
if (TARGET OpenVDB::openvdb)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include <catch_main.hpp>
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
#include "libslic3r/Time.hpp"
|
#include "libslic3r/Time.hpp"
|
||||||
|
|
||||||
@ -6,45 +6,44 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
|
||||||
namespace {
|
using namespace Slic3r;
|
||||||
|
|
||||||
void test_time_fmt(Slic3r::Utils::TimeFormat fmt) {
|
static void test_time_fmt(Slic3r::Utils::TimeFormat fmt) {
|
||||||
using namespace Slic3r::Utils;
|
using namespace Slic3r::Utils;
|
||||||
time_t t = get_current_time_utc();
|
time_t t = get_current_time_utc();
|
||||||
|
|
||||||
std::string tstr = time2str(t, TimeZone::local, fmt);
|
std::string tstr = time2str(t, TimeZone::local, fmt);
|
||||||
time_t parsedtime = str2time(tstr, TimeZone::local, fmt);
|
time_t parsedtime = str2time(tstr, TimeZone::local, fmt);
|
||||||
REQUIRE(t == parsedtime);
|
REQUIRE(t == parsedtime);
|
||||||
|
|
||||||
tstr = time2str(t, TimeZone::utc, fmt);
|
tstr = time2str(t, TimeZone::utc, fmt);
|
||||||
parsedtime = str2time(tstr, TimeZone::utc, fmt);
|
parsedtime = str2time(tstr, TimeZone::utc, fmt);
|
||||||
REQUIRE(t == parsedtime);
|
REQUIRE(t == parsedtime);
|
||||||
|
|
||||||
parsedtime = str2time("not valid string", TimeZone::local, fmt);
|
parsedtime = str2time("not valid string", TimeZone::local, fmt);
|
||||||
REQUIRE(parsedtime == time_t(-1));
|
REQUIRE(parsedtime == time_t(-1));
|
||||||
|
|
||||||
parsedtime = str2time("not valid string", TimeZone::utc, fmt);
|
parsedtime = str2time("not valid string", TimeZone::utc, fmt);
|
||||||
REQUIRE(parsedtime == time_t(-1));
|
REQUIRE(parsedtime == time_t(-1));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("ISO8601Z", "[Timeutils]") {
|
TEST_CASE("ISO8601Z", "[Timeutils]") {
|
||||||
test_time_fmt(Slic3r::Utils::TimeFormat::iso8601Z);
|
test_time_fmt(Slic3r::Utils::TimeFormat::iso8601Z);
|
||||||
|
|
||||||
std::string mydate = "20190710T085000Z";
|
std::string mydate = "20190710T085000Z";
|
||||||
time_t t = Slic3r::Utils::parse_iso_utc_timestamp(mydate);
|
time_t t = Slic3r::Utils::parse_iso_utc_timestamp(mydate);
|
||||||
std::string date = Slic3r::Utils::iso_utc_timestamp(t);
|
std::string date = Slic3r::Utils::iso_utc_timestamp(t);
|
||||||
|
|
||||||
REQUIRE(date == mydate);
|
REQUIRE(date == mydate);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Slic3r_UTC_Time_Format", "[Timeutils]") {
|
TEST_CASE("Slic3r_UTC_Time_Format", "[Timeutils]") {
|
||||||
using namespace Slic3r::Utils;
|
using namespace Slic3r::Utils;
|
||||||
test_time_fmt(TimeFormat::gcode);
|
test_time_fmt(TimeFormat::gcode);
|
||||||
|
|
||||||
std::string mydate = "2019-07-10 at 08:50:00 UTC";
|
std::string mydate = "2019-07-10 at 08:50:00 UTC";
|
||||||
time_t t = Slic3r::Utils::str2time(mydate, TimeZone::utc, TimeFormat::gcode);
|
time_t t = Slic3r::Utils::str2time(mydate, TimeZone::utc, TimeFormat::gcode);
|
||||||
std::string date = Slic3r::Utils::utc_timestamp(t);
|
std::string date = Slic3r::Utils::utc_timestamp(t);
|
||||||
|
|
||||||
REQUIRE(date == mydate);
|
REQUIRE(date == mydate);
|
||||||
}
|
}
|
@ -1,11 +1,10 @@
|
|||||||
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
||||||
add_executable(${_TEST_NAME}_tests
|
add_executable(${_TEST_NAME}_tests
|
||||||
${_TEST_NAME}_tests_main.cpp
|
${_TEST_NAME}_tests_main.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/libslic3r/Time.cpp
|
|
||||||
${PROJECT_SOURCE_DIR}/src/libslic3r/Time.hpp
|
|
||||||
)
|
)
|
||||||
target_link_libraries(${_TEST_NAME}_tests test_common)
|
|
||||||
|
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r_gui)
|
||||||
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
|
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
|
||||||
|
|
||||||
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
||||||
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS})
|
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "${CATCH_EXTRA_ARGS} exclude:[NotWorking]")
|
22
tests/slic3rutils/slic3rutils_tests_main.cpp
Normal file
22
tests/slic3rutils/slic3rutils_tests_main.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include <catch_main.hpp>
|
||||||
|
|
||||||
|
#include "slic3r/Utils/Http.hpp"
|
||||||
|
|
||||||
|
TEST_CASE("Http", "[Http][NotWorking]") {
|
||||||
|
|
||||||
|
Slic3r::Http g = Slic3r::Http::get("https://github.com/");
|
||||||
|
|
||||||
|
unsigned status = 0;
|
||||||
|
g.on_error([&status](std::string, std::string, unsigned http_status) {
|
||||||
|
status = http_status;
|
||||||
|
});
|
||||||
|
|
||||||
|
g.on_complete([&status](std::string /* body */, unsigned http_status){
|
||||||
|
status = http_status;
|
||||||
|
});
|
||||||
|
|
||||||
|
g.perform_sync();
|
||||||
|
|
||||||
|
REQUIRE(status == 200);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user