diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index ed2d70272..10da1cb07 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -38,6 +38,7 @@ #include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/ModelArrange.hpp" +#include "libslic3r/Platform.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/TriangleMesh.hpp" @@ -68,6 +69,8 @@ int CLI::run(int argc, char **argv) { // Mark the main thread for the debugger and for runtime checks. set_current_thread_name("slic3r_main"); + // Detect the operating system flavor. + detect_platform(); #ifdef __WXGTK__ // On Linux, wxGTK has no support for Wayland, and the app crashes on diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 6159b2c5c..4a762f7e1 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -141,6 +141,8 @@ add_library(libslic3r STATIC PerimeterGenerator.hpp PlaceholderParser.cpp PlaceholderParser.hpp + Platform.cpp + Platform.hpp Point.cpp Point.hpp Polygon.cpp diff --git a/src/slic3r/Utils/Platform.cpp b/src/libslic3r/Platform.cpp similarity index 92% rename from src/slic3r/Utils/Platform.cpp rename to src/libslic3r/Platform.cpp index 3d101d662..4c8805149 100644 --- a/src/slic3r/Utils/Platform.cpp +++ b/src/libslic3r/Platform.cpp @@ -1,15 +1,8 @@ #include "Platform.hpp" - -// For starting another PrusaSlicer instance on OSX. -// Fails to compile on Windows on the build server. - -#include - #include namespace Slic3r { -namespace GUI { static auto s_platform = Platform::Uninitialized; static auto s_platform_flavor = PlatformFlavor::Uninitialized; @@ -74,5 +67,4 @@ PlatformFlavor platform_flavor() return s_platform_flavor; } -} // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/Utils/Platform.hpp b/src/libslic3r/Platform.hpp similarity index 80% rename from src/slic3r/Utils/Platform.hpp rename to src/libslic3r/Platform.hpp index c0ee3541d..735728e89 100644 --- a/src/slic3r/Utils/Platform.hpp +++ b/src/libslic3r/Platform.hpp @@ -1,8 +1,7 @@ -#ifndef SLIC3R_GUI_Utils_Platform_HPP -#define SLIC3R_GUI_Utils_Platform_HPP +#ifndef SLIC3R_Platform_HPP +#define SLIC3R_Platform_HPP namespace Slic3r { -namespace GUI { enum class Platform { @@ -37,8 +36,6 @@ void detect_platform(); Platform platform(); PlatformFlavor platform_flavor(); - -} // namespace GUI } // namespace Slic3r -#endif // SLIC3R_GUI_Utils_Platform_HPP +#endif // SLIC3R_Platform_HPP diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 0c26d42c8..1ac45f1b5 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -6,6 +6,7 @@ #include #include +#include "Platform.hpp" #include "Time.hpp" #ifdef WIN32 @@ -19,9 +20,14 @@ #ifdef BSD #include #endif - #ifdef __APPLE__ + #ifdef __APPLE__ #include #endif + #ifdef __linux__ + #include + #include + #include + #endif #endif #include @@ -417,6 +423,140 @@ std::error_code rename_file(const std::string &from, const std::string &to) #endif } +#ifdef __linux__ +// Copied from boost::filesystem, to support copying a file to a weird filesystem, which does not support changing file attributes, +// for example ChromeOS Linux integration or FlashAIR WebDAV. +// Copied and simplified from boost::filesystem::detail::copy_file() with option = overwrite_if_exists and with just the Linux path kept, +// and only features supported by Linux 3.10 (on our build server with CentOS 7) are kept, namely sendfile with ranges and statx() are not supported. +bool copy_file_linux(const boost::filesystem::path &from, const boost::filesystem::path &to, boost::system::error_code &ec) +{ + using namespace boost::filesystem; + + struct fd_wrapper + { + int fd { -1 }; + fd_wrapper() = default; + explicit fd_wrapper(int fd) throw() : fd(fd) {} + ~fd_wrapper() throw() { if (fd >= 0) ::close(fd); } + }; + + ec.clear(); + int err = 0; + + // Note: Declare fd_wrappers here so that errno is not clobbered by close() that may be called in fd_wrapper destructors + fd_wrapper infile, outfile; + + while (true) { + infile.fd = ::open(from.c_str(), O_RDONLY | O_CLOEXEC); + if (infile.fd < 0) { + err = errno; + if (err == EINTR) + continue; + fail: + ec.assign(err, boost::system::system_category()); + return false; + } + break; + } + + struct ::stat from_stat; + if (::fstat(infile.fd, &from_stat) != 0) { + fail_errno: + err = errno; + goto fail; + } + + const mode_t from_mode = from_stat.st_mode; + if (!S_ISREG(from_mode)) { + err = ENOSYS; + goto fail; + } + + // Enable writing for the newly created files. Having write permission set is important e.g. for NFS, + // which checks the file permission on the server, even if the client's file descriptor supports writing. + mode_t to_mode = from_mode | S_IWUSR; + int oflag = O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC; + + while (true) { + outfile.fd = ::open(to.c_str(), oflag, to_mode); + if (outfile.fd < 0) { + err = errno; + if (err == EINTR) + continue; + goto fail; + } + break; + } + + struct ::stat to_stat; + if (::fstat(outfile.fd, &to_stat) != 0) + goto fail_errno; + + to_mode = to_stat.st_mode; + if (!S_ISREG(to_mode)) { + err = ENOSYS; + goto fail; + } + + if (from_stat.st_dev == to_stat.st_dev && from_stat.st_ino == to_stat.st_ino) { + err = EEXIST; + goto fail; + } + + //! copy_file implementation that uses sendfile loop. Requires sendfile to support file descriptors. + //FIXME Vojtech: This is a copy loop valid for Linux 2.6.33 and newer. + // copy_file_data_copy_file_range() supports cross-filesystem copying since 5.3, but Vojtech did not want to polute this + // function with that, we don't think the performance gain is worth it for the types of files we are copying, + // and our build server based on CentOS 7 with Linux 3.10 does not support that anyways. + { + // sendfile will not send more than this amount of data in one call + constexpr std::size_t max_send_size = 0x7ffff000u; + uintmax_t offset = 0u; + while (off_t(offset) < from_stat.st_size) { + uintmax_t size_left = from_stat.st_size - offset; + std::size_t size_to_copy = max_send_size; + if (size_left < static_cast(max_send_size)) + size_to_copy = static_cast(size_left); + ssize_t sz = ::sendfile(outfile.fd, infile.fd, nullptr, size_to_copy); + if (sz < 0) { + err = errno; + if (err == EINTR) + continue; + if (err == 0) + break; + goto fail; // err already contains the error code + } + offset += sz; + } + } + + // If we created a new file with an explicitly added S_IWUSR permission, + // we may need to update its mode bits to match the source file. + if (to_mode != from_mode && ::fchmod(outfile.fd, from_mode) != 0) { + if (platform_flavor() == PlatformFlavor::LinuxOnChromium) { + // Ignore that. 9p filesystem does not allow fmod(). + BOOST_LOG_TRIVIAL(info) << "copy_file_linux() failed to fchmod() the output file \"" << to.string() << "\" to " << from_mode << ": " << ec.message() << + " This may be expected when writing to a 9p filesystem."; + } else { + // Generic linux. Write out an error to console. At least we may get some feedback. + BOOST_LOG_TRIVIAL(error) << "copy_file_linux() failed to fchmod() the output file \"" << to.string() << "\" to " << from_mode << ": " << ec.message(); + } + } + + // Note: Use fsync/fdatasync followed by close to avoid dealing with the possibility of close failing with EINTR. + // Even if close fails, including with EINTR, most operating systems (presumably, except HP-UX) will close the + // file descriptor upon its return. This means that if an error happens later, when the OS flushes data to the + // underlying media, this error will go unnoticed and we have no way to receive it from close. Calling fsync/fdatasync + // ensures that all data have been written, and even if close fails for some unfathomable reason, we don't really + // care at that point. + err = ::fdatasync(outfile.fd); + if (err != 0) + goto fail_errno; + + return true; +} +#endif // __linux__ + CopyFileResult copy_file_inner(const std::string& from, const std::string& to, std::string& error_message) { const boost::filesystem::path source(from); @@ -434,7 +574,13 @@ CopyFileResult copy_file_inner(const std::string& from, const std::string& to, s if (ec) BOOST_LOG_TRIVIAL(debug) << "boost::filesystem::permisions before copy error message (this could be irrelevant message based on file system): " << ec.message(); ec.clear(); +#ifdef __linux__ + // We want to allow copying files on Linux to succeed even if changing the file attributes fails. + // That may happen when copying on some exotic file system, for example Linux on Chrome. + copy_file_linux(source, target, ec); +#else // __linux__ boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec); +#endif // __linux__ if (ec) { error_message = ec.message(); return FAIL_COPY_FILE; diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 6436de2ca..4b3a1c6ca 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -209,8 +209,6 @@ set(SLIC3R_GUI_SOURCES Utils/Bonjour.hpp Utils/PresetUpdater.cpp Utils/PresetUpdater.hpp - Utils/Platform.cpp - Utils/Platform.hpp Utils/Process.cpp Utils/Process.hpp Utils/Profile.hpp diff --git a/src/slic3r/GUI/GUI_Init.cpp b/src/slic3r/GUI/GUI_Init.cpp index 3c77f3335..839782741 100644 --- a/src/slic3r/GUI/GUI_Init.cpp +++ b/src/slic3r/GUI/GUI_Init.cpp @@ -9,7 +9,6 @@ #include "slic3r/GUI/format.hpp" #include "slic3r/GUI/MainFrame.hpp" #include "slic3r/GUI/Plater.hpp" -#include "slic3r/Utils/Platform.hpp" // To show a message box if GUI initialization ends up with an exception thrown. #include @@ -37,8 +36,6 @@ int GUI_Run(GUI_InitParams ¶ms) signal(SIGCHLD, SIG_DFL); #endif // __APPLE__ - detect_platform(); - try { GUI::GUI_App* gui = new GUI::GUI_App(params.start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor); if (gui->get_app_mode() != GUI::GUI_App::EAppMode::GCodeViewer) { diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f50c7e356..ad4468683 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1218,8 +1218,10 @@ void MainFrame::init_menubar_as_editor() [this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this, []() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this); append_menu_check_item(viewMenu, wxID_ANY, _L("&Full screen") + "\t" + "F11", _L("Full screen"), - [this](wxCommandEvent&) { this->ShowFullScreen(!this->IsFullScreen()); }, this, - []() { return true; }, [this]() { return this->IsFullScreen(); }, this); + [this](wxCommandEvent&) { this->ShowFullScreen(!this->IsFullScreen(), + // wxFULLSCREEN_ALL: wxFULLSCREEN_NOMENUBAR | wxFULLSCREEN_NOTOOLBAR | wxFULLSCREEN_NOSTATUSBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION + wxFULLSCREEN_NOSTATUSBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION); }, + this, []() { return true; }, [this]() { return this->IsFullScreen(); }, this); } // Help menu diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 8e8774036..c843da460 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -4,7 +4,8 @@ #include "GUI.hpp" #include "I18N.hpp" #include "3DScene.hpp" -#include "slic3r/Utils/Platform.hpp" + +#include "libslic3r/Platform.hpp" #include @@ -324,7 +325,7 @@ void OpenGLManager::detect_multisample(int* attribList) enable_multisample && // Disable multi-sampling on ChromeOS, as the OpenGL virtualization swaps Red/Blue channels with multi-sampling enabled, // at least on some platforms. - (platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium) && + platform_flavor() != PlatformFlavor::LinuxOnChromium && wxGLCanvas::IsDisplaySupported(attribList) ? EMultisampleState::Enabled : EMultisampleState::Disabled; // Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 15e89168a..273f39b1e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -76,7 +76,6 @@ #include "../Utils/FixModelByWin10.hpp" #include "../Utils/UndoRedo.hpp" #include "../Utils/PresetUpdater.hpp" -#include "../Utils/Platform.hpp" #include "../Utils/Process.hpp" #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" @@ -89,7 +88,9 @@ #include // Needs to be last because reasons :-/ #include "WipeTowerDialog.hpp" + #include "libslic3r/CustomGCode.hpp" +#include "libslic3r/Platform.hpp" using boost::optional; namespace fs = boost::filesystem; @@ -3660,7 +3661,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) show_action_buttons(false); notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, // Don't offer the "Eject" button on ChromeOS, the Linux side has no control over it. - platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium); + platform_flavor() != PlatformFlavor::LinuxOnChromium); wxGetApp().removable_drive_manager()->set_exporting_finished(true); }else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !has_error) notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, false); diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index 0e5e10d45..181dcfda4 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -139,7 +139,6 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle const ConfigOptionFloatOrPercent *first_layer_extrusion_width_ptr = (first_layer && first_layer_extrusion_width.value > 0) ? &first_layer_extrusion_width : nullptr; const float lh = float(first_layer ? first_layer_height : layer_height); - const float bfr = bridging ? bridge_flow_ratio : 0.f; double max_flow = 0.; std::string max_flow_extrusion_type; auto limit_by_first_layer_speed = [&first_layer_speed, first_layer](double speed_normal, double speed_max) { diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index c200956e1..33e61ab90 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -1,5 +1,5 @@ #include "RemovableDriveManager.hpp" -#include "slic3r/Utils/Platform.hpp" +#include "libslic3r/Platform.hpp" #include #include @@ -232,7 +232,7 @@ std::vector RemovableDriveManager::search_for_removable_drives() cons #else - if (platform() == Platform::Linux && platform_flavor() == PlatformFlavor::LinuxOnChromium) { + if (platform_flavor() == PlatformFlavor::LinuxOnChromium) { // ChromeOS specific: search /mnt/chromeos/removable/* folder search_for_drives_internal::search_path("/mnt/chromeos/removable/*", "/mnt/chromeos/removable", current_drives); } else { @@ -452,7 +452,7 @@ RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status() tbb::mutex::scoped_lock lock(m_drives_mutex); out.has_eject = // Cannot control eject on Chromium. - (platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium) && + platform_flavor() != PlatformFlavor::LinuxOnChromium && this->find_last_save_path_drive_data() != m_current_drives.end(); out.has_removable_drives = ! m_current_drives.empty(); }