Worked around some quirky Linux file system issues. Namely

the Chromebooks share their file system to Linux using the 9p file
system, which does not support setting file ownership. Newly PrusaSlicer
will detect platform and it will not panick if copy_file() cannot set
file ownership after copying. It just logs the incident, and on
chromebooks the loglevel for that incident is "Info", not "Error".

Adjusted the full screen mode to contain menu bar.
Moved Platform.cpp/hpp to libslic3r
This commit is contained in:
Vojtech Bubnik 2021-03-15 16:19:22 +01:00
parent 84a333e4ed
commit 01406fd521
12 changed files with 168 additions and 30 deletions

View File

@ -38,6 +38,7 @@
#include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/GCode/PostProcessor.hpp"
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include "libslic3r/ModelArrange.hpp" #include "libslic3r/ModelArrange.hpp"
#include "libslic3r/Platform.hpp"
#include "libslic3r/Print.hpp" #include "libslic3r/Print.hpp"
#include "libslic3r/SLAPrint.hpp" #include "libslic3r/SLAPrint.hpp"
#include "libslic3r/TriangleMesh.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. // Mark the main thread for the debugger and for runtime checks.
set_current_thread_name("slic3r_main"); set_current_thread_name("slic3r_main");
// Detect the operating system flavor.
detect_platform();
#ifdef __WXGTK__ #ifdef __WXGTK__
// On Linux, wxGTK has no support for Wayland, and the app crashes on // On Linux, wxGTK has no support for Wayland, and the app crashes on

View File

@ -141,6 +141,8 @@ add_library(libslic3r STATIC
PerimeterGenerator.hpp PerimeterGenerator.hpp
PlaceholderParser.cpp PlaceholderParser.cpp
PlaceholderParser.hpp PlaceholderParser.hpp
Platform.cpp
Platform.hpp
Point.cpp Point.cpp
Point.hpp Point.hpp
Polygon.cpp Polygon.cpp

View File

@ -1,15 +1,8 @@
#include "Platform.hpp" #include "Platform.hpp"
// For starting another PrusaSlicer instance on OSX.
// Fails to compile on Windows on the build server.
#include <wx/stdpaths.h>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
namespace Slic3r { namespace Slic3r {
namespace GUI {
static auto s_platform = Platform::Uninitialized; static auto s_platform = Platform::Uninitialized;
static auto s_platform_flavor = PlatformFlavor::Uninitialized; static auto s_platform_flavor = PlatformFlavor::Uninitialized;
@ -74,5 +67,4 @@ PlatformFlavor platform_flavor()
return s_platform_flavor; return s_platform_flavor;
} }
} // namespace GUI
} // namespace Slic3r } // namespace Slic3r

View File

@ -1,8 +1,7 @@
#ifndef SLIC3R_GUI_Utils_Platform_HPP #ifndef SLIC3R_Platform_HPP
#define SLIC3R_GUI_Utils_Platform_HPP #define SLIC3R_Platform_HPP
namespace Slic3r { namespace Slic3r {
namespace GUI {
enum class Platform enum class Platform
{ {
@ -37,8 +36,6 @@ void detect_platform();
Platform platform(); Platform platform();
PlatformFlavor platform_flavor(); PlatformFlavor platform_flavor();
} // namespace GUI
} // namespace Slic3r } // namespace Slic3r
#endif // SLIC3R_GUI_Utils_Platform_HPP #endif // SLIC3R_Platform_HPP

View File

@ -6,6 +6,7 @@
#include <cstdarg> #include <cstdarg>
#include <stdio.h> #include <stdio.h>
#include "Platform.hpp"
#include "Time.hpp" #include "Time.hpp"
#ifdef WIN32 #ifdef WIN32
@ -19,9 +20,14 @@
#ifdef BSD #ifdef BSD
#include <sys/sysctl.h> #include <sys/sysctl.h>
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
#include <mach/mach.h> #include <mach/mach.h>
#endif #endif
#ifdef __linux__
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#endif
#endif #endif
#include <boost/log/core.hpp> #include <boost/log/core.hpp>
@ -417,6 +423,140 @@ std::error_code rename_file(const std::string &from, const std::string &to)
#endif #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<uintmax_t>(max_send_size))
size_to_copy = static_cast<std::size_t>(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) CopyFileResult copy_file_inner(const std::string& from, const std::string& to, std::string& error_message)
{ {
const boost::filesystem::path source(from); 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) if (ec)
BOOST_LOG_TRIVIAL(debug) << "boost::filesystem::permisions before copy error message (this could be irrelevant message based on file system): " << ec.message(); BOOST_LOG_TRIVIAL(debug) << "boost::filesystem::permisions before copy error message (this could be irrelevant message based on file system): " << ec.message();
ec.clear(); 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); boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec);
#endif // __linux__
if (ec) { if (ec) {
error_message = ec.message(); error_message = ec.message();
return FAIL_COPY_FILE; return FAIL_COPY_FILE;

View File

@ -209,8 +209,6 @@ set(SLIC3R_GUI_SOURCES
Utils/Bonjour.hpp Utils/Bonjour.hpp
Utils/PresetUpdater.cpp Utils/PresetUpdater.cpp
Utils/PresetUpdater.hpp Utils/PresetUpdater.hpp
Utils/Platform.cpp
Utils/Platform.hpp
Utils/Process.cpp Utils/Process.cpp
Utils/Process.hpp Utils/Process.hpp
Utils/Profile.hpp Utils/Profile.hpp

View File

@ -9,7 +9,6 @@
#include "slic3r/GUI/format.hpp" #include "slic3r/GUI/format.hpp"
#include "slic3r/GUI/MainFrame.hpp" #include "slic3r/GUI/MainFrame.hpp"
#include "slic3r/GUI/Plater.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. // To show a message box if GUI initialization ends up with an exception thrown.
#include <wx/msgdlg.h> #include <wx/msgdlg.h>
@ -37,8 +36,6 @@ int GUI_Run(GUI_InitParams &params)
signal(SIGCHLD, SIG_DFL); signal(SIGCHLD, SIG_DFL);
#endif // __APPLE__ #endif // __APPLE__
detect_platform();
try { try {
GUI::GUI_App* gui = new GUI::GUI_App(params.start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor); 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) { if (gui->get_app_mode() != GUI::GUI_App::EAppMode::GCodeViewer) {

View File

@ -1218,8 +1218,10 @@ void MainFrame::init_menubar_as_editor()
[this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this, [this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this,
[]() { return true; }, [this]() { return 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"), append_menu_check_item(viewMenu, wxID_ANY, _L("&Full screen") + "\t" + "F11", _L("Full screen"),
[this](wxCommandEvent&) { this->ShowFullScreen(!this->IsFullScreen()); }, this, [this](wxCommandEvent&) { this->ShowFullScreen(!this->IsFullScreen(),
[]() { return true; }, [this]() { return this->IsFullScreen(); }, this); // 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 // Help menu

View File

@ -4,7 +4,8 @@
#include "GUI.hpp" #include "GUI.hpp"
#include "I18N.hpp" #include "I18N.hpp"
#include "3DScene.hpp" #include "3DScene.hpp"
#include "slic3r/Utils/Platform.hpp"
#include "libslic3r/Platform.hpp"
#include <GL/glew.h> #include <GL/glew.h>
@ -324,7 +325,7 @@ void OpenGLManager::detect_multisample(int* attribList)
enable_multisample && enable_multisample &&
// Disable multi-sampling on ChromeOS, as the OpenGL virtualization swaps Red/Blue channels with multi-sampling enabled, // Disable multi-sampling on ChromeOS, as the OpenGL virtualization swaps Red/Blue channels with multi-sampling enabled,
// at least on some platforms. // at least on some platforms.
(platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium) && platform_flavor() != PlatformFlavor::LinuxOnChromium &&
wxGLCanvas::IsDisplaySupported(attribList) wxGLCanvas::IsDisplaySupported(attribList)
? EMultisampleState::Enabled : EMultisampleState::Disabled; ? EMultisampleState::Enabled : EMultisampleState::Disabled;
// Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows // Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows

View File

@ -76,7 +76,6 @@
#include "../Utils/FixModelByWin10.hpp" #include "../Utils/FixModelByWin10.hpp"
#include "../Utils/UndoRedo.hpp" #include "../Utils/UndoRedo.hpp"
#include "../Utils/PresetUpdater.hpp" #include "../Utils/PresetUpdater.hpp"
#include "../Utils/Platform.hpp"
#include "../Utils/Process.hpp" #include "../Utils/Process.hpp"
#include "RemovableDriveManager.hpp" #include "RemovableDriveManager.hpp"
#include "InstanceCheck.hpp" #include "InstanceCheck.hpp"
@ -89,7 +88,9 @@
#include <wx/glcanvas.h> // Needs to be last because reasons :-/ #include <wx/glcanvas.h> // Needs to be last because reasons :-/
#include "WipeTowerDialog.hpp" #include "WipeTowerDialog.hpp"
#include "libslic3r/CustomGCode.hpp" #include "libslic3r/CustomGCode.hpp"
#include "libslic3r/Platform.hpp"
using boost::optional; using boost::optional;
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
@ -3660,7 +3661,7 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
show_action_buttons(false); show_action_buttons(false);
notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, 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. // 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); wxGetApp().removable_drive_manager()->set_exporting_finished(true);
}else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !has_error) }else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !has_error)
notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, false); notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, false);

View File

@ -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) ? const ConfigOptionFloatOrPercent *first_layer_extrusion_width_ptr = (first_layer && first_layer_extrusion_width.value > 0) ?
&first_layer_extrusion_width : nullptr; &first_layer_extrusion_width : nullptr;
const float lh = float(first_layer ? first_layer_height : layer_height); const float lh = float(first_layer ? first_layer_height : layer_height);
const float bfr = bridging ? bridge_flow_ratio : 0.f;
double max_flow = 0.; double max_flow = 0.;
std::string max_flow_extrusion_type; std::string max_flow_extrusion_type;
auto limit_by_first_layer_speed = [&first_layer_speed, first_layer](double speed_normal, double speed_max) { auto limit_by_first_layer_speed = [&first_layer_speed, first_layer](double speed_normal, double speed_max) {

View File

@ -1,5 +1,5 @@
#include "RemovableDriveManager.hpp" #include "RemovableDriveManager.hpp"
#include "slic3r/Utils/Platform.hpp" #include "libslic3r/Platform.hpp"
#include <libslic3r/libslic3r.h> #include <libslic3r/libslic3r.h>
#include <boost/nowide/convert.hpp> #include <boost/nowide/convert.hpp>
@ -232,7 +232,7 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
#else #else
if (platform() == Platform::Linux && platform_flavor() == PlatformFlavor::LinuxOnChromium) { if (platform_flavor() == PlatformFlavor::LinuxOnChromium) {
// ChromeOS specific: search /mnt/chromeos/removable/* folder // ChromeOS specific: search /mnt/chromeos/removable/* folder
search_for_drives_internal::search_path("/mnt/chromeos/removable/*", "/mnt/chromeos/removable", current_drives); search_for_drives_internal::search_path("/mnt/chromeos/removable/*", "/mnt/chromeos/removable", current_drives);
} else { } else {
@ -452,7 +452,7 @@ RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status()
tbb::mutex::scoped_lock lock(m_drives_mutex); tbb::mutex::scoped_lock lock(m_drives_mutex);
out.has_eject = out.has_eject =
// Cannot control eject on Chromium. // 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(); this->find_last_save_path_drive_data() != m_current_drives.end();
out.has_removable_drives = ! m_current_drives.empty(); out.has_removable_drives = ! m_current_drives.empty();
} }