Ported ChromeOS support from master aka PrusaSlicer 2.4.0-alpha:

1) Detect platform
2) Disable OpenGL multi-sampling on ChromeOS
3) Disable eject on ChromeOS, different location of external devices
   mount point.
This commit is contained in:
Vojtech Bubnik 2021-04-13 10:55:27 +02:00
parent 89b942ffb7
commit db324b2295
8 changed files with 303 additions and 18 deletions

View File

@ -37,6 +37,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"
@ -594,6 +595,9 @@ bool CLI::setup(int argc, char **argv)
}
}
// Detect the operating system flavor after SLIC3R_LOGLEVEL is set.
detect_platform();
boost::filesystem::path path_to_binary = boost::filesystem::system_complete(argv[0]);
// Path from the Slic3r binary to its resources.

View File

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

View File

@ -0,0 +1,71 @@
#include "Platform.hpp"
#include <boost/log/trivial.hpp>
#include <boost/filesystem/operations.hpp>
namespace Slic3r {
static auto s_platform = Platform::Uninitialized;
static auto s_platform_flavor = PlatformFlavor::Uninitialized;
void detect_platform()
{
#if defined(_WIN32)
BOOST_LOG_TRIVIAL(info) << "Platform: Windows";
s_platform = Platform::Windows;
s_platform_flavor = PlatformFlavor::Generic;
#elif defined(__APPLE__)
BOOST_LOG_TRIVIAL(info) << "Platform: OSX";
s_platform = Platform::OSX;
s_platform_flavor = PlatformFlavor::Generic;
#elif defined(__linux__)
BOOST_LOG_TRIVIAL(info) << "Platform: Linux";
s_platform = Platform::Linux;
s_platform_flavor = PlatformFlavor::GenericLinux;
// Test for Chromium.
{
FILE *f = ::fopen("/proc/version", "rt");
if (f) {
char buf[4096];
// Read the 1st line.
if (::fgets(buf, 4096, f)) {
if (strstr(buf, "Chromium OS") != nullptr) {
s_platform_flavor = PlatformFlavor::LinuxOnChromium;
BOOST_LOG_TRIVIAL(info) << "Platform flavor: LinuxOnChromium";
} else if (strstr(buf, "microsoft") != nullptr || strstr(buf, "Microsoft") != nullptr) {
if (boost::filesystem::exists("/run/WSL") && getenv("WSL_INTEROP") != nullptr) {
BOOST_LOG_TRIVIAL(info) << "Platform flavor: WSL2";
s_platform_flavor = PlatformFlavor::WSL2;
} else {
BOOST_LOG_TRIVIAL(info) << "Platform flavor: WSL";
s_platform_flavor = PlatformFlavor::WSL;
}
}
}
::fclose(f);
}
}
#elif defined(__OpenBSD__)
BOOST_LOG_TRIVIAL(info) << "Platform: OpenBSD";
s_platform = Platform::BSDUnix;
s_platform_flavor = PlatformFlavor::OpenBSD;
#else
// This should not happen.
BOOST_LOG_TRIVIAL(info) << "Platform: Unknown";
static_assert(false, "Unknown platform detected");
s_platform = Platform::Unknown;
s_platform_flavor = PlatformFlavor::Unknown;
#endif
}
Platform platform()
{
return s_platform;
}
PlatformFlavor platform_flavor()
{
return s_platform_flavor;
}
} // namespace Slic3r

View File

@ -0,0 +1,41 @@
#ifndef SLIC3R_Platform_HPP
#define SLIC3R_Platform_HPP
namespace Slic3r {
enum class Platform
{
Uninitialized,
Unknown,
Windows,
OSX,
Linux,
BSDUnix,
};
enum class PlatformFlavor
{
Uninitialized,
Unknown,
// For Windows and OSX, until we need to be more specific.
Generic,
// For Platform::Linux
GenericLinux,
LinuxOnChromium,
// Microsoft's Windows on Linux (Linux kernel simulated on NTFS kernel)
WSL,
// Microsoft's Windows on Linux, version 2 (virtual machine)
WSL2,
// For Platform::BSDUnix
OpenBSD,
};
// To be called on program start-up.
void detect_platform();
Platform platform();
PlatformFlavor platform_flavor();
} // namespace Slic3r
#endif // SLIC3R_Platform_HPP

View File

@ -6,6 +6,7 @@
#include <cstdarg>
#include <stdio.h>
#include "Platform.hpp"
#include "Time.hpp"
#ifdef WIN32
@ -417,6 +418,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<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)
{
const boost::filesystem::path source(from);
@ -434,7 +569,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;

View File

@ -5,6 +5,8 @@
#include "I18N.hpp"
#include "3DScene.hpp"
#include "libslic3r/Platform.hpp"
#include <GL/glew.h>
#include <boost/algorithm/string/split.hpp>
@ -333,7 +335,13 @@ void OpenGLManager::detect_multisample(int* attribList)
{
int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER;
bool enable_multisample = wxVersion >= 30003;
s_multisample = (enable_multisample && wxGLCanvas::IsDisplaySupported(attribList)) ? EMultisampleState::Enabled : EMultisampleState::Disabled;
s_multisample =
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_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
// s_multisample = enable_multisample && wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample");
}

View File

@ -88,6 +88,7 @@
#include <wx/glcanvas.h> // Needs to be last because reasons :-/
#include "WipeTowerDialog.hpp"
#include "libslic3r/CustomGCode.hpp"
#include "libslic3r/Platform.hpp"
using boost::optional;
namespace fs = boost::filesystem;
@ -3769,7 +3770,9 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
// If writing to removable drive was scheduled, show notification with eject button
if (exporting_status == ExportingStatus::EXPORTING_TO_REMOVABLE && !has_error) {
show_action_buttons(false);
notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, true);
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_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);

View File

@ -1,4 +1,5 @@
#include "RemovableDriveManager.hpp"
#include "libslic3r/Platform.hpp"
#include <libslic3r/libslic3r.h>
#include <boost/nowide/convert.hpp>
@ -185,8 +186,13 @@ namespace search_for_drives_internal
{
//confirms if the file is removable drive and adds it to vector
//if not same file system - could be removable drive
if (! compare_filesystem_id(path, parent_path)) {
if (
#ifdef __linux__
// Chromium mounts removable drives in a way that produces the same device ID.
platform_flavor() == PlatformFlavor::LinuxOnChromium ||
#endif
// If not same file system - could be removable drive.
! compare_filesystem_id(path, parent_path)) {
//free space
boost::system::error_code ec;
boost::filesystem::space_info si = boost::filesystem::space(path, ec);
@ -229,22 +235,28 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
#else
//search /media/* folder
search_for_drives_internal::search_path("/media/*", "/media", current_drives);
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 {
//search /media/* folder
search_for_drives_internal::search_path("/media/*", "/media", current_drives);
//search_path("/Volumes/*", "/Volumes");
std::string path(std::getenv("USER"));
std::string pp(path);
//search_path("/Volumes/*", "/Volumes");
std::string path(std::getenv("USER"));
std::string pp(path);
//search /media/USERNAME/* folder
pp = "/media/"+pp;
path = "/media/" + path + "/*";
search_for_drives_internal::search_path(path, pp, current_drives);
//search /media/USERNAME/* folder
pp = "/media/"+pp;
path = "/media/" + path + "/*";
search_for_drives_internal::search_path(path, pp, current_drives);
//search /run/media/USERNAME/* folder
path = "/run" + path;
pp = "/run"+pp;
search_for_drives_internal::search_path(path, pp, current_drives);
}
//search /run/media/USERNAME/* folder
path = "/run" + path;
pp = "/run"+pp;
search_for_drives_internal::search_path(path, pp, current_drives);
#endif
return current_drives;
@ -441,7 +453,10 @@ RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status()
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_eject =
// Cannot control eject on Chromium.
platform_flavor() != PlatformFlavor::LinuxOnChromium &&
this->find_last_save_path_drive_data() != m_current_drives.end();
out.has_removable_drives = ! m_current_drives.empty();
}
if (! out.has_eject)