diff --git a/CMakeLists.txt b/CMakeLists.txt index c66d7b8ad5c..ff154eb2e1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1034,6 +1034,7 @@ set(INPUT_DRIVER_SOURCES set(GUI_SOURCES ${GUI_SOURCES} + src/openscad_gui.cc src/gui/AutoUpdater.cc src/gui/CGALWorker.cc src/gui/ViewportControl.cc diff --git a/src/gui/ExportPdfDialog.cc b/src/gui/ExportPdfDialog.cc index ab23713db67..8d75969321d 100644 --- a/src/gui/ExportPdfDialog.cc +++ b/src/gui/ExportPdfDialog.cc @@ -113,7 +113,7 @@ ExportPdfDialog::ExportPdfDialog() bool ExportPdfDialog::getShowScaleMsg() { return cbScaleUsg->isChecked(); } - bool ExportPdfDialog::getShowDsnFn() { + bool ExportPdfDialog::getShowDesignFilename() { return cbDsnFn->isChecked(); } bool ExportPdfDialog::getShowGrid() { @@ -127,7 +127,7 @@ ExportPdfDialog::ExportPdfDialog() void ExportPdfDialog::setShowScaleMsg(bool state) { cbScaleUsg->setChecked(state); } - void ExportPdfDialog::setShowDsnFn(bool state) { + void ExportPdfDialog::setShowDesignFilename(bool state) { cbDsnFn->setChecked(state); } void ExportPdfDialog::setShowGrid(bool state) { diff --git a/src/gui/ExportPdfDialog.h b/src/gui/ExportPdfDialog.h index dbed0f60ee2..8b9ec78f514 100644 --- a/src/gui/ExportPdfDialog.h +++ b/src/gui/ExportPdfDialog.h @@ -43,12 +43,12 @@ class ExportPdfDialog : public QDialog, public Ui::ExportPdfDialog paperOrientations getOrientation(); bool getShowScale(); bool getShowScaleMsg(); - bool getShowDsnFn(); + bool getShowDesignFilename(); bool getShowGrid(); void setShowScale(bool state); void setShowScaleMsg(bool state); - void setShowDsnFn(bool state); + void setShowDesignFilename(bool state); void setShowGrid(bool state); void setPaperSize(paperSizes paper); diff --git a/src/gui/MainWindow.cc b/src/gui/MainWindow.cc index e61deae31bb..df9055b7bd3 100644 --- a/src/gui/MainWindow.cc +++ b/src/gui/MainWindow.cc @@ -2147,25 +2147,6 @@ void MainWindow::action3DPrint() #endif // ifdef ENABLE_3D_PRINTING } -namespace { - -ExportInfo createExportInfo(FileFormat format, const QString& exportFilename, const QString& sourceFilePath) -{ - const QFileInfo info(sourceFilePath); - - ExportInfo exportInfo; - exportInfo.format = format; - exportInfo.fileName = exportFilename.toLocal8Bit().constData(); - exportInfo.displayName = exportFilename.toUtf8().toStdString(); - exportInfo.sourceFilePath = sourceFilePath.toUtf8().toStdString(); - exportInfo.sourceFileName = info.fileName().toUtf8().toStdString(); - exportInfo.useStdOut = false; - exportInfo.options = nullptr; - return exportInfo; -} - -} - void MainWindow::sendToOctoPrint() { #ifdef ENABLE_3D_PRINTING @@ -2176,20 +2157,22 @@ void MainWindow::sendToOctoPrint() return; } + // FIXME: To make this cleaner, we could define which formats are supported by OctoPrint separately, + // then using fileformat::fromIdentifier() to convert. const QString fileFormat = QString::fromStdString(Settings::Settings::octoPrintFileFormat.value()); - FileFormat exportFileFormat{FileFormat::STL}; + FileFormat exportFileFormat{FileFormat::BINARY_STL}; if (fileFormat == "OBJ") { exportFileFormat = FileFormat::OBJ; } else if (fileFormat == "OFF") { exportFileFormat = FileFormat::OFF; } else if (fileFormat == "ASCIISTL") { - exportFileFormat = FileFormat::ASCIISTL; + exportFileFormat = FileFormat::ASCII_STL; } else if (fileFormat == "AMF") { exportFileFormat = FileFormat::AMF; } else if (fileFormat == "3MF") { exportFileFormat = FileFormat::_3MF; } else { - exportFileFormat = FileFormat::STL; + exportFileFormat = FileFormat::BINARY_STL; } QTemporaryFile exportFile{QDir::temp().filePath("OpenSCAD.XXXXXX." + fileFormat.toLower())}; @@ -2208,8 +2191,8 @@ void MainWindow::sendToOctoPrint() userFileName = fileInfo.baseName() + "." + fileFormat.toLower(); } - ExportInfo exportInfo = createExportInfo(exportFileFormat, exportFileName, activeEditor->filepath); - exportFileByName(this->root_geom, exportInfo); + ExportInfo exportInfo = {.format = exportFileFormat, .sourceFilePath = activeEditor->filepath.toStdString()}; + exportFileByName(this->root_geom, exportFileName.toStdString(), exportInfo); try { this->progresswidget = new ProgressWidget(this); @@ -2239,25 +2222,13 @@ void MainWindow::sendToLocalSlicer() #ifdef ENABLE_3D_PRINTING const QString slicer = QString::fromStdString(Settings::Settings::localSlicerExecutable.value()); - const QString fileFormat = QString::fromStdString(Settings::Settings::localSlicerFileFormat.value()); - FileFormat exportFileFormat{FileFormat::STL}; - if (fileFormat == "OBJ") { - exportFileFormat = FileFormat::OBJ; - } else if (fileFormat == "OFF") { - exportFileFormat = FileFormat::OFF; - } else if (fileFormat == "ASCIISTL") { - exportFileFormat = FileFormat::ASCIISTL; - } else if (fileFormat == "AMF") { - exportFileFormat = FileFormat::AMF; - } else if (fileFormat == "3MF") { - exportFileFormat = FileFormat::_3MF; - } else if (fileFormat == "POV") { - exportFileFormat = FileFormat::POV; - } else { - exportFileFormat = FileFormat::STL; + const QString fileFormat = QString::fromStdString(Settings::Settings::localSlicerFileFormat.value()).toLower(); + FileFormat exportFileFormat = FileFormat::BINARY_STL; + if (!fileformat::fromIdentifier(fileFormat.toStdString(), exportFileFormat)) { + LOG("Invalid suffix %1$s. Defaulting to binary STL.", fileFormat.toStdString()); } - const auto tmpPath = QDir::temp().filePath("OpenSCAD.XXXXXX."+fileFormat.toLower()); + const auto tmpPath = QDir::temp().filePath("OpenSCAD.XXXXXX."+fileFormat); auto exportFile = std::make_unique(tmpPath); if (!exportFile->open()) { LOG(message_group::Error, "Could not open temporary file '%1$s'.", tmpPath.toStdString()); @@ -2272,11 +2243,11 @@ void MainWindow::sendToLocalSlicer() userFileName = exportFileName; } else { QFileInfo fileInfo{activeEditor->filepath}; - userFileName = fileInfo.baseName() + fileFormat.toLower(); + userFileName = fileInfo.baseName() + fileFormat; } - ExportInfo exportInfo = createExportInfo(exportFileFormat, exportFileName, activeEditor->filepath); - exportFileByName(this->root_geom, exportInfo); + ExportInfo exportInfo = {.format = exportFileFormat, .sourceFilePath = activeEditor->filepath.toStdString()}; + exportFileByName(this->root_geom, exportFileName.toStdString(), exportInfo); QProcess process(this); process.setProcessChannelMode(QProcess::MergedChannels); @@ -2310,8 +2281,8 @@ void MainWindow::sendToPrintService() const QString exportFilename = exportFile.fileName(); //Render the stl to a temporary file: - ExportInfo exportInfo = createExportInfo(FileFormat::STL, exportFilename, activeEditor->filepath); - exportFileByName(this->root_geom, exportInfo); + ExportInfo exportInfo = {.format = FileFormat::BINARY_STL, .sourceFilePath = activeEditor->filepath.toStdString()}; + exportFileByName(this->root_geom, exportFilename.toStdString(), exportInfo); //Create a name that the order process will use to refer to the file. Base it off of the project name QString userFacingName = "unsaved.stl"; @@ -2942,11 +2913,11 @@ void MainWindow::actionExport(FileFormat format, const char *type_name, const ch } this->export_paths[suffix] = exportFilename; - ExportInfo exportInfo = createExportInfo(format, exportFilename, activeEditor->filepath); + ExportInfo exportInfo = {.format = format, .sourceFilePath = activeEditor->filepath.toStdString()}; // Add options exportInfo.options = options; - bool exportResult = exportFileByName(this->root_geom, exportInfo); + bool exportResult = exportFileByName(this->root_geom, exportFilename.toStdString(), exportInfo); if (exportResult) fileExportedMessage(type_name, exportFilename); clearCurrentOutput(); @@ -2955,9 +2926,9 @@ void MainWindow::actionExport(FileFormat format, const char *type_name, const ch void MainWindow::actionExportSTL() { if (Settings::Settings::exportUseAsciiSTL.value()) { - actionExport(FileFormat::ASCIISTL, "ASCIISTL", ".stl", 3); + actionExport(FileFormat::ASCII_STL, "ASCIISTL", ".stl", 3); } else { - actionExport(FileFormat::STL, "STL", ".stl", 3); + actionExport(FileFormat::BINARY_STL, "STL", ".stl", 3); } } @@ -3016,7 +2987,7 @@ void MainWindow::actionExportPDF() QString::fromStdString(paperSizeStrings[static_cast(exportPdfOptions.paperSize)])).toString())); // enum map exportPdfDialog->setOrientation(orientationsString2Enum(settings.value("exportPdfOpts/orientation", QString::fromStdString(paperOrientationsStrings[static_cast(exportPdfOptions.Orientation)])).toString())); // enum map - exportPdfDialog->setShowDsnFn(settings.value("exportPdfOpts/showDsgnFN", exportPdfOptions.showDsgnFN).toBool()); + exportPdfDialog->setShowDesignFilename(settings.value("exportPdfOpts/showDsgnFN", exportPdfOptions.showDesignFilename).toBool()); exportPdfDialog->setShowScale(settings.value("exportPdfOpts/showScale", exportPdfOptions.showScale).toBool()); exportPdfDialog->setShowScaleMsg(settings.value("exportPdfOpts/showScaleMsg", exportPdfOptions.showScaleMsg).toBool()); exportPdfDialog->setShowGrid(settings.value("exportPdfOpts/showGrid", exportPdfOptions.showGrid).toBool()); @@ -3029,7 +3000,7 @@ void MainWindow::actionExportPDF() exportPdfOptions.paperSize = exportPdfDialog->getPaperSize(); exportPdfOptions.Orientation = exportPdfDialog->getOrientation(); - exportPdfOptions.showDsgnFN = exportPdfDialog->getShowDsnFn(); + exportPdfOptions.showDesignFilename = exportPdfDialog->getShowDesignFilename(); exportPdfOptions.showScale = exportPdfDialog->getShowScale(); exportPdfOptions.showScaleMsg = exportPdfDialog->getShowScaleMsg(); exportPdfOptions.showGrid = exportPdfDialog->getShowGrid(); @@ -3037,7 +3008,7 @@ void MainWindow::actionExportPDF() settings.setValue("exportPdfOpts/paperSize", QString::fromStdString(paperSizeStrings[static_cast(exportPdfDialog->getPaperSize())])); settings.setValue("exportPdfOpts/orientation", QString::fromStdString(paperOrientationsStrings[static_cast(exportPdfDialog->getOrientation())])); - settings.setValue("exportPdfOpts/showDsgnFN", exportPdfDialog->getShowDsnFn()); + settings.setValue("exportPdfOpts/showDsgnFN", exportPdfDialog->getShowDesignFilename()); settings.setValue("exportPdfOpts/showScale", exportPdfDialog->getShowScale()); settings.setValue("exportPdfOpts/showScaleMsg", exportPdfDialog->getShowScaleMsg()); settings.setValue("exportPdfOpts/showGrid", exportPdfDialog->getShowGrid()); diff --git a/src/io/export.cc b/src/io/export.cc index 98f90a2abc7..007877d9148 100644 --- a/src/io/export.cc +++ b/src/io/export.cc @@ -33,12 +33,14 @@ #include #include #include -#include #include #include #include #include #include +#include +#include +#include #ifdef _WIN32 #include @@ -48,6 +50,78 @@ #define QUOTE(x__) # x__ #define QUOTED(x__) QUOTE(x__) +namespace { + +struct FileFormatInfo { + FileFormat format; + std::string identifier; + std::string suffix; + std::string description; +}; + +std::unordered_map &identifierToInfo() { + static auto identifierToInfo = std::make_unique>(); + return *identifierToInfo; +} + +std::unordered_map &fileFormatToInfo() { + static auto fileFormatToInfo = std::make_unique>(); + return *fileFormatToInfo; +} + +void add_item(const FileFormatInfo& info) { + + identifierToInfo()[info.identifier] = info; + fileFormatToInfo()[info.format] = info; +} + +bool initialized = false; +bool initialize() { + add_item({FileFormat::ASCII_STL, "asciistl", "stl", "STL (ascii)"}); + add_item({FileFormat::BINARY_STL, "binstl", "stl", "STL (binary)"}); + add_item({FileFormat::OBJ, "obj", "obj", "OBJ"}); + add_item({FileFormat::OFF, "off", "off", "OFF"}); + add_item({FileFormat::WRL, "wrl", "wrl", "VRML"}); + add_item({FileFormat::AMF, "amf", "amf", "AMF"}); + add_item({FileFormat::_3MF, "3mf", "3mf", "3MF"}); + add_item({FileFormat::DXF, "dxf", "dxf", "DXF"}); + add_item({FileFormat::SVG, "svg", "svg", "SVG"}); + add_item({FileFormat::NEFDBG, "nefdbg", "nefdbg", "nefdbg"}); + add_item({FileFormat::NEF3, "nef3", "nef3", "nef3"}); + add_item({FileFormat::CSG, "csg", "csg", "CSG"}); + add_item({FileFormat::PARAM, "param", "param", "param"}); + add_item({FileFormat::AST, "ast", "ast", "AST"}); + add_item({FileFormat::TERM, "term", "term", "term"}); + add_item({FileFormat::ECHO, "echo", "echo", "echo"}); + add_item({FileFormat::PNG, "png", "png", "PNG"}); + add_item({FileFormat::PDF, "pdf", "pdf", "PDF"}); + add_item({FileFormat::POV, "pov", "pov", "POV"}); + + // Alias + identifierToInfo()["stl"] = identifierToInfo()["asciistl"]; + + return true; +} + +} // namespace + +namespace fileformat { + +bool fromIdentifier(const std::string& suffix, FileFormat& format) +{ + if (!initialized) initialized = initialize(); + auto it = identifierToInfo().find(suffix); + if (it == identifierToInfo().end()) return false; + format = it->second.format; + return true; +} + +const std::string& toSuffix(FileFormat& format) +{ + if (!initialized) initialized = initialize(); + return fileFormatToInfo()[format].suffix; +} + bool canPreview(const FileFormat format) { return (format == FileFormat::AST || format == FileFormat::CSG || @@ -58,8 +132,8 @@ bool canPreview(const FileFormat format) { } bool is3D(const FileFormat format) { -return format == FileFormat::ASCIISTL || - format == FileFormat::STL || +return format == FileFormat::ASCII_STL || + format == FileFormat::BINARY_STL || format == FileFormat::OBJ || format == FileFormat::OFF || format == FileFormat::WRL || @@ -76,13 +150,15 @@ bool is2D(const FileFormat format) { format == FileFormat::PDF; } +} // namespace FileFormat + void exportFile(const std::shared_ptr& root_geom, std::ostream& output, const ExportInfo& exportInfo) { switch (exportInfo.format) { - case FileFormat::ASCIISTL: + case FileFormat::ASCII_STL: export_stl(root_geom, output, false); break; - case FileFormat::STL: + case FileFormat::BINARY_STL: export_stl(root_geom, output, true); break; case FileFormat::OBJ: @@ -125,7 +201,7 @@ void exportFile(const std::shared_ptr& root_geom, std::ostream& } } -bool exportFileByNameStdout(const std::shared_ptr& root_geom, const ExportInfo& exportInfo) +bool exportFileStdOut(const std::shared_ptr& root_geom, const ExportInfo& exportInfo) { #ifdef _WIN32 _setmode(_fileno(stdout), _O_BINARY); @@ -134,15 +210,16 @@ bool exportFileByNameStdout(const std::shared_ptr& root_geom, co return true; } -bool exportFileByNameStream(const std::shared_ptr& root_geom, const ExportInfo& exportInfo) +bool exportFileByName(const std::shared_ptr& root_geom, const std::string& filename, const ExportInfo& exportInfo) { std::ios::openmode mode = std::ios::out | std::ios::trunc; - if (exportInfo.format == FileFormat::_3MF || exportInfo.format == FileFormat::STL || exportInfo.format == FileFormat::PDF) { + if (exportInfo.format == FileFormat::_3MF || exportInfo.format == FileFormat::BINARY_STL || exportInfo.format == FileFormat::PDF) { mode |= std::ios::binary; } - std::ofstream fstream(exportInfo.fileName, mode); + const boost::filesystem::path path(filename); + boost::filesystem::ofstream fstream(path, mode); if (!fstream.is_open()) { - LOG(_("Can't open file \"%1$s\" for export"), exportInfo.displayName); + LOG(_("Can't open file \"%1$s\" for export"), filename); return false; } else { bool onerror = false; @@ -158,21 +235,12 @@ bool exportFileByNameStream(const std::shared_ptr& root_geom, co onerror = true; } if (onerror) { - LOG(message_group::Error, _("\"%1$s\" write error. (Disk full?)"), exportInfo.displayName); + LOG(message_group::Error, _("\"%1$s\" write error. (Disk full?)"), filename); } return !onerror; } } -bool exportFileByName(const std::shared_ptr& root_geom, const ExportInfo& exportInfo) -{ - if (exportInfo.useStdOut) { - return exportFileByNameStdout(root_geom, exportInfo); - } else { - return exportFileByNameStream(root_geom, exportInfo); - } -} - namespace { double remove_negative_zero(double x) { diff --git a/src/io/export.h b/src/io/export.h index 679c2c9d0d7..776c26621cf 100644 --- a/src/io/export.h +++ b/src/io/export.h @@ -17,8 +17,8 @@ class PolySet; enum class FileFormat { - ASCIISTL, - STL, + ASCII_STL, + BINARY_STL, OBJ, OFF, WRL, @@ -38,6 +38,16 @@ enum class FileFormat { PARAM }; +namespace fileformat { + +void setup(); +bool fromIdentifier(const std::string& suffix, FileFormat& format); +const std::string& toSuffix(FileFormat& format); +bool canPreview(const FileFormat format); +bool is3D(const FileFormat format); +bool is2D(const FileFormat format); + +} // namespace FileFormat // Paper Data used by ExportPDF enum class paperSizes { @@ -78,31 +88,23 @@ const std::array paperOrientationsStrings{ // include defaults to use without dialog or direction. // Defaults match values used prior to incorporation of options. struct ExportPdfOptions { - bool showScale=TRUE; - bool showScaleMsg=TRUE; - bool showGrid=FALSE; - double gridSize=10.; // New - bool showDsgnFN=TRUE; - paperOrientations Orientation=paperOrientations::PORTRAIT; - paperSizes paperSize=paperSizes::A4; + bool showScale = true; + bool showScaleMsg = true; + bool showGrid = false; + double gridSize = 10.0; + bool showDesignFilename = false; + paperOrientations Orientation = paperOrientations::PORTRAIT; + paperSizes paperSize = paperSizes::A4; }; struct ExportInfo { FileFormat format; - std::string displayName; - std::string fileName; - std::string sourceFilePath; - std::string sourceFileName; - bool useStdOut; + std::string sourceFilePath; // Full path to the OpenSCAD source file ExportPdfOptions *options; }; - -bool canPreview(const FileFormat format); -bool is3D(const FileFormat format); -bool is2D(const FileFormat format); - -bool exportFileByName(const std::shared_ptr& root_geom, const ExportInfo& exportInfo); +bool exportFileByName(const std::shared_ptr& root_geom, const std::string& filename, const ExportInfo& exportInfo); +bool exportFileStdOut(const std::shared_ptr& root_geom, const ExportInfo& exportInfo); void export_stl(const std::shared_ptr& geom, std::ostream& output, bool binary = true); @@ -122,31 +124,6 @@ void export_nef3(const std::shared_ptr& geom, std::ostream& outp enum class Previewer { OPENCSG, THROWNTOGETHER }; enum class RenderType { GEOMETRY, BACKEND_SPECIFIC, OPENCSG, THROWNTOGETHER }; -struct ExportFileFormatOptions { - const std::map exportFileFormats{ - {"asciistl", FileFormat::ASCIISTL}, - {"binstl", FileFormat::STL}, - {"stl", FileFormat::ASCIISTL}, // Deprecated. Later to FileFormat::STL - {"obj", FileFormat::OBJ}, - {"off", FileFormat::OFF}, - {"wrl", FileFormat::WRL}, - {"amf", FileFormat::AMF}, - {"3mf", FileFormat::_3MF}, - {"dxf", FileFormat::DXF}, - {"svg", FileFormat::SVG}, - {"nefdbg", FileFormat::NEFDBG}, - {"nef3", FileFormat::NEF3}, - {"csg", FileFormat::CSG}, - {"param", FileFormat::PARAM}, - {"ast", FileFormat::AST}, - {"term", FileFormat::TERM}, - {"echo", FileFormat::ECHO}, - {"png", FileFormat::PNG}, - {"pdf", FileFormat::PDF}, - {"pov", FileFormat::POV}, - }; -}; - struct ViewOption { const std::string name; bool& value; diff --git a/src/io/export_pdf.cc b/src/io/export_pdf.cc index ceb153618cf..f951d219527 100644 --- a/src/io/export_pdf.cc +++ b/src/io/export_pdf.cc @@ -258,8 +258,9 @@ if (exportInfo.options==nullptr) { return; } + #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 16, 0) - cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_TITLE, exportInfo.sourceFileName.c_str()); + cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_TITLE, boost::filesystem::path(exportInfo.sourceFilePath).filename().string().c_str()); cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_CREATOR, "OpenSCAD (https://www.openscad.org/)"); cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_CREATE_DATE, ""); cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_MOD_DATE, ""); @@ -278,7 +279,7 @@ if (exportInfo.options==nullptr) { std::string about = "Scale is to calibrate actual printed dimension. Check both X and Y. Measure between tick 0 and last tick"; cairo_set_source_rgba(cr, 0., 0., 0., 0.48); // Design Filename - if (exportPdfOptions->showDsgnFN) draw_text(exportInfo.sourceFilePath.c_str(), cr, Mlx, Mby, 10.); + if (exportPdfOptions->showDesignFilename) draw_text(exportInfo.sourceFilePath.c_str(), cr, Mlx, Mby, 10.); // Scale if (exportPdfOptions->showScale) { draw_axes(cr, Mlx,Mrx,Mty,Mby); diff --git a/src/io/export_pov.cc b/src/io/export_pov.cc index 55051ac6ca6..b31631cfdc0 100644 --- a/src/io/export_pov.cc +++ b/src/io/export_pov.cc @@ -44,7 +44,7 @@ void export_pov(const std::shared_ptr& geom, std::ostream& outpu } output << "// Generated by OpenSCAD!\n"; - output << "// Source file: " << exportInfo.sourceFileName << "\n\n"; + output << "// Source file: " << boost::filesystem::path(exportInfo.sourceFilePath).filename().string() << "\n\n"; output << "#version 3.7;\n"; output << "global_settings { assumed_gamma 1.0 }\n"; diff --git a/src/openscad.cc b/src/openscad.cc index 41426a83e63..3eba976d36e 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -25,55 +25,16 @@ */ #include "openscad.h" -#include "core/customizer/CommentParser.h" -#include "core/RenderVariables.h" -#include "core/node.h" -#include "core/SourceFile.h" -#include "core/BuiltinContext.h" -#include "core/Value.h" -#include "io/export.h" -#include "core/Builtins.h" -#include "utils/printutils.h" -#include "handle_dep.h" -#include "Feature.h" -#include "core/parsersettings.h" -#include "glview/RenderSettings.h" -#include "platform/PlatformUtils.h" -#include "LibraryInfo.h" -#include "utils/StackCheck.h" -#include "FontCache.h" -#include "glview/OffscreenView.h" -#include "geometry/GeometryEvaluator.h" -#include "RenderStatistic.h" -#include "core/customizer/ParameterObject.h" -#include "core/customizer/ParameterSet.h" -#include "openscad_mimalloc.h" + +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#ifdef ENABLE_MANIFOLD -#include "geometry/manifold/manifoldutils.h" -#endif - -#ifdef ENABLE_CGAL -#include "geometry/cgal/CGAL_Nef_polyhedron.h" -#include "geometry/cgal/cgalutils.h" +#ifdef _WIN32 +#include +#include #endif -#include "core/CSGNode.h" -#include "core/CSGTreeEvaluator.h" - -#include "glview/Camera.h" #include #include #include @@ -82,22 +43,30 @@ #include #include -#ifdef _WIN32 -#include -#include +#ifdef ENABLE_CGAL +#include #endif -#ifdef __APPLE__ -#include "platform/CocoaUtils.h" -#include "gui/AppleEvents.h" - #ifdef OPENSCAD_UPDATER - #include "gui/SparkleAutoUpdater.h" - #endif -#endif +#include "core/Builtins.h" +#include "core/CSGTreeEvaluator.h" +#include "core/customizer/CommentParser.h" +#include "core/customizer/ParameterObject.h" +#include "core/customizer/ParameterSet.h" +#include "core/parsersettings.h" +#include "core/RenderVariables.h" +#include "geometry/GeometryEvaluator.h" +#include "glview/ColorMap.h" +#include "glview/OffscreenView.h" +#include "glview/RenderSettings.h" +#include "handle_dep.h" +#include "io/export.h" +#include "LibraryInfo.h" +#include "openscad_gui.h" +#include "openscad_mimalloc.h" +#include "platform/PlatformUtils.h" +#include "RenderStatistic.h" +#include "utils/StackCheck.h" -#ifdef _MSC_VER -#define snprintf _snprintf -#endif #ifdef ENABLE_PYTHON extern std::shared_ptr python_result_node; @@ -107,16 +76,10 @@ bool python_trusted = false; #endif namespace po = boost::program_options; namespace fs = boost::filesystem; -using std::string; -using std::vector; -using std::unique_ptr; -using boost::lexical_cast; -using boost::bad_lexical_cast; -using boost::is_any_of; std::string commandline_commands; static bool arg_info = false; -static std::string arg_colorscheme; +std::string arg_colorscheme; class Echostream { @@ -143,7 +106,46 @@ class Echostream std::ostream& stream; }; -static void help(const char *arg0, const po::options_description& desc, bool failure = false) +namespace { + +#ifndef OPENSCAD_NOGUI +bool useGUI() +{ +#ifdef Q_OS_X11 + // see : + // On X11, the window system is initialized if GUIenabled is true. If GUIenabled + // is false, the application does not connect to the X server. On Windows and + // Macintosh, currently the window system is always initialized, regardless of the + // value of GUIenabled. This may change in future versions of Qt. + return getenv("DISPLAY") != 0; +#else + return true; +#endif +} +#endif // OPENSCAD_NOGUI + +bool checkAndExport(const std::shared_ptr& root_geom, unsigned dimensions, + FileFormat format, const bool is_stdout, const std::string& filename, const std::string& input_filename) +{ + if (root_geom->getDimension() != dimensions) { + LOG("Current top level object is not a %1$dD object.", dimensions); + return false; + } + if (root_geom->isEmpty()) { + LOG("Current top level object is empty."); + return false; + } + ExportInfo exportInfo = {.format = format, .sourceFilePath = input_filename}; + if (is_stdout) { + exportFileStdOut(root_geom, exportInfo); + } + else { + exportFileByName(root_geom, filename, exportInfo); + } + return true; +} + +void help(const char *arg0, const po::options_description& desc, bool failure = false) { const fs::path progpath(arg0); LOG("Usage: %1$s [options] file.scad\n%2$s", progpath.filename().string(), desc); @@ -152,13 +154,13 @@ static void help(const char *arg0, const po::options_description& desc, bool fai #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) -static void version() +void version() { LOG("OpenSCAD version %1$s", TOSTRING(OPENSCAD_VERSION)); exit(0); } -static int info() +int info() { std::cout << LibraryInfo::info() << "\n\n"; @@ -174,7 +176,7 @@ static int info() } template -static bool with_output(const bool is_stdout, const std::string& filename, F f, std::ios::openmode mode = std::ios::out) +bool with_output(const bool is_stdout, const std::string& filename, F f, std::ios::openmode mode = std::ios::out) { if (is_stdout) { #ifdef _WIN32 @@ -195,6 +197,28 @@ static bool with_output(const bool is_stdout, const std::string& filename, F f, } } +} // namespace + +void set_render_color_scheme(const std::string& color_scheme, const bool exit_if_not_found) +{ + if (color_scheme.empty()) { + return; + } + + if (ColorMap::inst()->findColorScheme(color_scheme)) { + RenderSettings::inst()->colorscheme = color_scheme; + return; + } + + if (exit_if_not_found) { + LOG((boost::algorithm::join(ColorMap::inst()->colorSchemeNames(), "\n"))); + + exit(1); + } else { + LOG("Unknown color scheme '%1$s', using default '%2$s'.", arg_colorscheme, ColorMap::inst()->defaultColorSchemeName()); + } +} + /** * Initialize gettext. This must be called after the application path was * determined so we can lookup the resource path for the language translation @@ -219,16 +243,16 @@ Camera get_camera(const po::variables_map& vm) Camera camera; if (vm.count("camera")) { - vector strs; - vector cam_parameters; - boost::split(strs, vm["camera"].as(), is_any_of(",")); + std::vector strs; + std::vector cam_parameters; + boost::split(strs, vm["camera"].as(), boost::is_any_of(",")); if (strs.size() == 6 || strs.size() == 7) { try { for (const auto& s : strs) { - cam_parameters.push_back(lexical_cast(s)); + cam_parameters.push_back(boost::lexical_cast(s)); } camera.setup(cam_parameters); - } catch (bad_lexical_cast&) { + } catch (boost::bad_lexical_cast&) { LOG("Camera setup requires numbers as parameters"); } } else { @@ -249,7 +273,7 @@ Camera get_camera(const po::variables_map& vm) } if (vm.count("projection")) { - auto proj = vm["projection"].as(); + auto proj = vm["projection"].as(); if (proj == "o" || proj == "ortho" || proj == "orthogonal") { camera.projection = Camera::ProjectionType::ORTHOGONAL; } else if (proj == "p" || proj == "perspective") { @@ -263,16 +287,16 @@ Camera get_camera(const po::variables_map& vm) auto w = RenderSettings::inst()->img_width; auto h = RenderSettings::inst()->img_height; if (vm.count("imgsize")) { - vector strs; - boost::split(strs, vm["imgsize"].as(), is_any_of(",")); + std::vector strs; + boost::split(strs, vm["imgsize"].as(), boost::is_any_of(",")); if (strs.size() != 2) { LOG("Need 2 numbers for imgsize"); exit(1); } else { try { - w = lexical_cast(strs[0]); - h = lexical_cast(strs[1]); - } catch (bad_lexical_cast&) { + w = boost::lexical_cast(strs[0]); + h = boost::lexical_cast(strs[1]); + } catch (boost::bad_lexical_cast&) { LOG("Need 2 numbers for imgsize"); } } @@ -283,52 +307,6 @@ Camera get_camera(const po::variables_map& vm) return camera; } -#ifndef OPENSCAD_NOGUI -#include "gui/QSettingsCached.h" -#define OPENSCAD_QTGUI 1 -#endif - -static bool checkAndExport(const std::shared_ptr& root_geom, unsigned dimensions, - FileFormat format, const bool is_stdout, const std::string& filename) -{ - if (root_geom->getDimension() != dimensions) { - LOG("Current top level object is not a %1$dD object.", dimensions); - return false; - } - if (root_geom->isEmpty()) { - LOG("Current top level object is empty."); - return false; - } - - exportFileByName(root_geom, ExportInfo { - .format = format, - .displayName = filename, - .fileName = filename, - .useStdOut = is_stdout, - }); - return true; -} - -void set_render_color_scheme(const std::string& color_scheme, const bool exit_if_not_found) -{ - if (color_scheme.empty()) { - return; - } - - if (ColorMap::inst()->findColorScheme(color_scheme)) { - RenderSettings::inst()->colorscheme = color_scheme; - return; - } - - if (exit_if_not_found) { - LOG((boost::algorithm::join(ColorMap::inst()->colorSchemeNames(), "\n"))); - - exit(1); - } else { - LOG("Unknown color scheme '%1$s', using default '%2$s'.", arg_colorscheme, ColorMap::inst()->defaultColorSchemeName()); - } -} - struct CommandLine { const bool is_stdin; @@ -346,147 +324,6 @@ struct CommandLine const std::string summaryFile; }; -int do_export(const CommandLine& cmd, const RenderVariables& render_variables, FileFormat export_format, SourceFile *root_file); - -int cmdline(const CommandLine& cmd) -{ - ExportFileFormatOptions exportFileFormatOptions; - FileFormat export_format; - - // Determine output file format and assign it to formatName - if (cmd.export_format.is_initialized()) { - export_format = cmd.export_format.get(); - } else { - // else extract format from file extension - const auto path = fs::path(cmd.output_file); - std::string suffix = path.has_extension() ? path.extension().generic_string().substr(1) : ""; - boost::algorithm::to_lower(suffix); - const auto format_iter = exportFileFormatOptions.exportFileFormats.find(suffix); - if (format_iter != exportFileFormatOptions.exportFileFormats.end()) { - export_format = format_iter->second; - } else { - LOG("Either add a valid suffix or specify one using the --export-format option."); - return 1; - } - } - - // Do some minimal checking of output directory before rendering (issue #432) - auto output_dir = fs::path(cmd.output_file).parent_path(); - if (output_dir.empty()) { - // If output_file_str has no directory prefix, set output directory to current directory. - output_dir = fs::current_path(); - } - if (!fs::is_directory(output_dir)) { - LOG("\n'%1$s' is not a directory for output file %2$s - Skipping\n", output_dir.generic_string(), cmd.output_file); - return 1; - } - - set_render_color_scheme(arg_colorscheme, true); - - std::shared_ptr echostream; - if (export_format == FileFormat::ECHO) { - echostream.reset(cmd.is_stdout ? new Echostream(std::cout) : new Echostream(cmd.output_file)); - } - - std::string text; - if (cmd.is_stdin) { - text = std::string((std::istreambuf_iterator(std::cin)), std::istreambuf_iterator()); - } else { - std::ifstream ifs(cmd.filename); - if (!ifs.is_open()) { - LOG("Can't open input file '%1$s'!\n", cmd.filename); - return 1; - } - handle_dep(cmd.filename); - text = std::string((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); - } - -#ifdef ENABLE_PYTHON - python_active = false; - if(cmd.filename.c_str() != NULL) { - if(boost::algorithm::ends_with(cmd.filename, ".py")) { - if( python_trusted == true) python_active = true; - else LOG("Python is not enabled"); - } - } - - if(python_active) { - auto fulltext_py = text; - auto error = evaluatePython(fulltext_py, 0.0); - if(error.size() > 0) LOG(error.c_str()); - text ="\n"; - } -#endif - text += "\n\x03\n" + commandline_commands; - - SourceFile *root_file = nullptr; - if (!parse(root_file, text, cmd.filename, cmd.filename, false)) { - delete root_file; // parse failed - root_file = nullptr; - } - if (!root_file) { - LOG("Can't parse file '%1$s'!\n", cmd.filename); - return 1; - } - - // add parameter to AST - CommentParser::collectParameters(text.c_str(), root_file); - if (!cmd.parameterFile.empty() && !cmd.setName.empty()) { - ParameterObjects parameters = ParameterObjects::fromSourceFile(root_file); - ParameterSets sets; - sets.readFile(cmd.parameterFile); - for (const auto& set : sets) { - if (set.name() == cmd.setName) { - parameters.importValues(set); - parameters.apply(root_file); - break; - } - } - } - - root_file->handleDependencies(); - - RenderVariables render_variables = { - .preview = canPreview(export_format) - ? (cmd.viewOptions.renderer == RenderType::OPENCSG - || cmd.viewOptions.renderer == RenderType::THROWNTOGETHER) - : false, - .camera = cmd.camera, - }; - - if (cmd.animate_frames == 0) { - render_variables.time = 0; - return do_export(cmd, render_variables, export_format, root_file); - } else { - // export the requested number of animated frames - for (unsigned frame = 0; frame < cmd.animate_frames; ++frame) { - render_variables.time = frame * (1.0 / cmd.animate_frames); - - std::ostringstream oss; - oss << std::setw(5) << std::setfill('0') << frame; - - auto frame_file = fs::path(cmd.output_file); - auto extension = frame_file.extension(); - frame_file.replace_extension(); - frame_file += oss.str(); - frame_file.replace_extension(extension); - string frame_str = frame_file.generic_string(); - - LOG("Exporting %1$s...", cmd.filename); - - CommandLine frame_cmd = cmd; - frame_cmd.output_file = frame_str; - - int r = do_export(frame_cmd, render_variables, export_format, root_file); - if (r != 0) { - return r; - } - } - - return 0; - } -} - int do_export(const CommandLine& cmd, const RenderVariables& render_variables, FileFormat export_format, SourceFile *root_file) { auto filename_str = fs::path(cmd.output_file).generic_string(); @@ -574,7 +411,7 @@ int do_export(const CommandLine& cmd, const RenderVariables& render_variables, F // start measuring render time RenderStatistic renderStatistic; GeometryEvaluator geomevaluator(tree); - unique_ptr glview; + std::unique_ptr glview; std::shared_ptr root_geom; if ((export_format == FileFormat::ECHO || export_format == FileFormat::PNG) && (cmd.viewOptions.renderer == RenderType::OPENCSG || cmd.viewOptions.renderer == RenderType::THROWNTOGETHER)) { // OpenCSG or throwntogether png -> just render a preview @@ -602,16 +439,11 @@ int do_export(const CommandLine& cmd, const RenderVariables& render_variables, F LOG("Converted to backend-specific geometry"); } } - if (is3D(export_format)) { - if (!checkAndExport(root_geom, 3, export_format, cmd.is_stdout, filename_str)) { - return 1; - } - } - if (is2D(export_format)) { - if (!checkAndExport(root_geom, 2, export_format, cmd.is_stdout, filename_str)) { - return 1; - } + const std::string input_filename = cmd.is_stdin ? "" : cmd.filename; + const int dim = fileformat::is3D(export_format) ? 3 : fileformat::is2D(export_format) ? 2 : 0; + if (dim > 0 && !checkAndExport(root_geom, dim, export_format, cmd.is_stdout, filename_str, input_filename)) { + return 1; } if (export_format == FileFormat::PNG) { @@ -633,255 +465,148 @@ int do_export(const CommandLine& cmd, const RenderVariables& render_variables, F return 0; } -#ifdef OPENSCAD_QTGUI -#include -#include "gui/MainWindow.h" -#include "gui/OpenSCADApp.h" -#include "gui/LaunchingScreen.h" -#include "gui/QSettingsCached.h" -#include "gui/input/InputDriverManager.h" -#ifdef ENABLE_HIDAPI -#include "gui/input/HidApiInputDriver.h" -#endif -#ifdef ENABLE_SPNAV -#include "gui/input/SpaceNavInputDriver.h" -#endif -#ifdef ENABLE_JOYSTICK -#include "gui/input/JoystickInputDriver.h" -#endif -#ifdef ENABLE_DBUS -#include "gui/input/DBusInputDriver.h" -#endif -#ifdef ENABLE_QGAMEPAD -#include "gui/input/QGamepadInputDriver.h" -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include "gui/Settings.h" - -Q_DECLARE_METATYPE(Message); -Q_DECLARE_METATYPE(std::shared_ptr); - -// Only if "fileName" is not absolute, prepend the "absoluteBase". -static QString assemblePath(const fs::path& absoluteBaseDir, - const string& fileName) { - if (fileName.empty()) return ""; - auto qsDir = QString::fromLocal8Bit(absoluteBaseDir.generic_string().c_str()); - auto qsFile = QString::fromLocal8Bit(fileName.c_str()); - // if qsfile is absolute, dir is ignored. (see documentation of QFileInfo) - QFileInfo fileInfo(qsDir, qsFile); - return fileInfo.absoluteFilePath(); -} - -bool QtUseGUI() -{ -#ifdef Q_OS_X11 - // see : - // On X11, the window system is initialized if GUIenabled is true. If GUIenabled - // is false, the application does not connect to the X server. On Windows and - // Macintosh, currently the window system is always initialized, regardless of the - // value of GUIenabled. This may change in future versions of Qt. - bool useGUI = getenv("DISPLAY") != 0; -#else - bool useGUI = true; -#endif - return useGUI; -} - -void dialogThreadFunc(FontCacheInitializer *initializer) +int cmdline(const CommandLine& cmd) { - initializer->run(); -} + FileFormat export_format; -void dialogInitHandler(FontCacheInitializer *initializer, void *) -{ - QFutureWatcher futureWatcher; - QObject::connect(&futureWatcher, SIGNAL(finished()), scadApp, SLOT(hideFontCacheDialog())); + // Determine output file format and assign it to formatName + if (cmd.export_format.is_initialized()) { + export_format = cmd.export_format.get(); + } else { + // else extract format from file extension + const auto path = fs::path(cmd.output_file); + std::string suffix = path.has_extension() ? path.extension().generic_string().substr(1) : ""; + boost::algorithm::to_lower(suffix); - auto future = QtConcurrent::run([initializer] { - return dialogThreadFunc(initializer); - }); - futureWatcher.setFuture(future); + if (!fileformat::fromIdentifier(suffix, export_format)) { + LOG("Invalid suffix %1$s. Either add a valid suffix or specify one using the --export-format option.", suffix); + return 1; + } + } - // We don't always get the started() signal, so we start manually - QMetaObject::invokeMethod(scadApp, "showFontCacheDialog"); + // Do some minimal checking of output directory before rendering (issue #432) + auto output_dir = fs::path(cmd.output_file).parent_path(); + if (output_dir.empty()) { + // If output_file_str has no directory prefix, set output directory to current directory. + output_dir = fs::current_path(); + } + if (!fs::is_directory(output_dir)) { + LOG("\n'%1$s' is not a directory for output file %2$s - Skipping\n", output_dir.generic_string(), cmd.output_file); + return 1; + } - // Block, in case we're in a separate thread, or the dialog was closed by the user - futureWatcher.waitForFinished(); + set_render_color_scheme(arg_colorscheme, true); - // We don't always receive the finished signal. We still need the signal to break - // out of the exec() though. - QMetaObject::invokeMethod(scadApp, "hideFontCacheDialog"); -} + std::shared_ptr echostream; + if (export_format == FileFormat::ECHO) { + echostream.reset(cmd.is_stdout ? new Echostream(std::cout) : new Echostream(cmd.output_file)); + } -#ifdef Q_OS_WIN -void registerDefaultIcon(QString applicationFilePath) { - // Not using cached instance here, so this needs to be in a - // separate scope to ensure the QSettings instance is released - // directly after use. - QSettings reg_setting(QLatin1String("HKEY_CURRENT_USER"), QSettings::NativeFormat); - auto appPath = QDir::toNativeSeparators(applicationFilePath + QLatin1String(",1")); - reg_setting.setValue(QLatin1String("Software/Classes/OpenSCAD_File/DefaultIcon/Default"), QVariant(appPath)); -} -#else -void registerDefaultIcon(const QString&) { } -#endif + std::string text; + if (cmd.is_stdin) { + text = std::string((std::istreambuf_iterator(std::cin)), std::istreambuf_iterator()); + } else { + std::ifstream ifs(cmd.filename); + if (!ifs.is_open()) { + LOG("Can't open input file '%1$s'!\n", cmd.filename); + return 1; + } + handle_dep(cmd.filename); + text = std::string((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + } -#ifdef OPENSCAD_SUFFIX -#define DESKTOP_FILENAME "openscad" OPENSCAD_SUFFIX -#else -#define DESKTOP_FILENAME "openscad" -#endif +#ifdef ENABLE_PYTHON + python_active = false; + if(cmd.filename.c_str() != NULL) { + if(boost::algorithm::ends_with(cmd.filename, ".py")) { + if( python_trusted == true) python_active = true; + else LOG("Python is not enabled"); + } + } -int gui(vector& inputFiles, const fs::path& original_path, int argc, char **argv) -{ - OpenSCADApp app(argc, argv); - // remove ugly frames in the QStatusBar when using additional widgets - app.setStyleSheet("QStatusBar::item { border: 0px solid black; }"); - - // set up groups for QSettings - QCoreApplication::setOrganizationName("OpenSCAD"); - QCoreApplication::setOrganizationDomain("openscad.org"); - QCoreApplication::setApplicationName("OpenSCAD"); - QCoreApplication::setApplicationVersion(TOSTRING(OPENSCAD_VERSION)); - QGuiApplication::setApplicationDisplayName("OpenSCAD"); - QGuiApplication::setDesktopFileName(DESKTOP_FILENAME); - QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); -#ifdef Q_OS_MACOS - app.setWindowIcon(QIcon(":/icon-macos.png")); -#else - app.setWindowIcon(QIcon(":/logo.png")); -#endif + if(python_active) { + auto fulltext_py = text; + auto error = evaluatePython(fulltext_py, 0.0); + if(error.size() > 0) LOG(error.c_str()); + text ="\n"; + } +#endif + text += "\n\x03\n" + commandline_commands; - // Other global settings - qRegisterMetaType(); - qRegisterMetaType>(); + SourceFile *root_file = nullptr; + if (!parse(root_file, text, cmd.filename, cmd.filename, false)) { + delete root_file; // parse failed + root_file = nullptr; + } + if (!root_file) { + LOG("Can't parse file '%1$s'!\n", cmd.filename); + return 1; + } - FontCache::registerProgressHandler(dialogInitHandler); + // add parameter to AST + CommentParser::collectParameters(text.c_str(), root_file); + if (!cmd.parameterFile.empty() && !cmd.setName.empty()) { + ParameterObjects parameters = ParameterObjects::fromSourceFile(root_file); + ParameterSets sets; + sets.readFile(cmd.parameterFile); + for (const auto& set : sets) { + if (set.name() == cmd.setName) { + parameters.importValues(set); + parameters.apply(root_file); + break; + } + } + } - parser_init(); + root_file->handleDependencies(); - QSettingsCached settings; - if (settings.value("advanced/localization", true).toBool()) { - localization_init(); - } + RenderVariables render_variables = { + .preview = fileformat::canPreview(export_format) + ? (cmd.viewOptions.renderer == RenderType::OPENCSG + || cmd.viewOptions.renderer == RenderType::THROWNTOGETHER) + : false, + .camera = cmd.camera, + }; -#ifdef Q_OS_MACOS - installAppleEventHandlers(); -#endif + if (cmd.animate_frames == 0) { + render_variables.time = 0; + return do_export(cmd, render_variables, export_format, root_file); + } else { + // export the requested number of animated frames + for (unsigned frame = 0; frame < cmd.animate_frames; ++frame) { + render_variables.time = frame * (1.0 / cmd.animate_frames); - registerDefaultIcon(app.applicationFilePath()); + std::ostringstream oss; + oss << std::setw(5) << std::setfill('0') << frame; -#ifdef OPENSCAD_UPDATER - AutoUpdater *updater = new SparkleAutoUpdater; - AutoUpdater::setUpdater(updater); - if (updater->automaticallyChecksForUpdates()) updater->checkForUpdates(); - updater->init(); -#endif + auto frame_file = fs::path(cmd.output_file); + auto extension = frame_file.extension(); + frame_file.replace_extension(); + frame_file += oss.str(); + frame_file.replace_extension(extension); + std::string frame_str = frame_file.generic_string(); - set_render_color_scheme(arg_colorscheme, false); - auto noInputFiles = false; + LOG("Exporting %1$s...", cmd.filename); - if (!inputFiles.size()) { - noInputFiles = true; - inputFiles.emplace_back(""); - } + CommandLine frame_cmd = cmd; + frame_cmd.output_file = frame_str; - auto showOnStartup = settings.value("launcher/showOnStartup"); - if (noInputFiles && (showOnStartup.isNull() || showOnStartup.toBool())) { - auto launcher = new LaunchingScreen(); - auto dialogResult = launcher->exec(); - if (dialogResult == QDialog::Accepted) { - if (launcher->isForceShowEditor()) { - settings.setValue("view/hideEditor", false); - } - auto files = launcher->selectedFiles(); - // If nothing is selected in the launching screen, leave - // the "" dummy in inputFiles to open an empty MainWindow. - if (!files.empty()) { - inputFiles.clear(); - for (const auto& f : files) { - inputFiles.push_back(f.toStdString()); - } + int r = do_export(frame_cmd, render_variables, export_format, root_file); + if (r != 0) { + return r; } - delete launcher; - } else { - return 0; } - } - QStringList inputFilesList; - for (const auto& infile: inputFiles) { - inputFilesList.append(assemblePath(original_path, infile)); - } - new MainWindow(inputFilesList); - app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(releaseQSettingsCached())); - app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); - -#ifdef ENABLE_HIDAPI - if (Settings::Settings::inputEnableDriverHIDAPI.value()) { - auto hidApi = new HidApiInputDriver(); - InputDriverManager::instance()->registerDriver(hidApi); - } -#endif -#ifdef ENABLE_SPNAV - if (Settings::Settings::inputEnableDriverSPNAV.value()) { - auto spaceNavDriver = new SpaceNavInputDriver(); - bool spaceNavDominantAxisOnly = Settings::Settings::inputEnableDriverHIDAPI.value(); - spaceNavDriver->setDominantAxisOnly(spaceNavDominantAxisOnly); - InputDriverManager::instance()->registerDriver(spaceNavDriver); - } -#endif -#ifdef ENABLE_JOYSTICK - if (Settings::Settings::inputEnableDriverJOYSTICK.value()) { - std::string nr = STR(Settings::Settings::joystickNr.value()); - auto joyDriver = new JoystickInputDriver(); - joyDriver->setJoystickNr(nr); - InputDriverManager::instance()->registerDriver(joyDriver); - } -#endif -#ifdef ENABLE_QGAMEPAD - if (Settings::Settings::inputEnableDriverQGAMEPAD.value()) { - auto qGamepadDriver = new QGamepadInputDriver(); - InputDriverManager::instance()->registerDriver(qGamepadDriver); - } -#endif -#ifdef ENABLE_DBUS - if (Feature::ExperimentalInputDriverDBus.is_enabled()) { - if (Settings::Settings::inputEnableDriverDBUS.value()) { - auto dBusDriver = new DBusInputDriver(); - InputDriverManager::instance()->registerDriver(dBusDriver); - } + return 0; } -#endif - - InputDriverManager::instance()->init(); - int rc = app.exec(); - const auto& windows = scadApp->windowManager.getWindows(); - while (!windows.empty()) delete *windows.begin(); - return rc; -} -#else // OPENSCAD_QTGUI -bool QtUseGUI() { return false; } -int gui(const vector& inputFiles, const fs::path& original_path, int argc, char **argv) -{ - LOG(message_group::Error, "Compiled without QT, but trying to run GUI\n"); - return 1; } -#endif // OPENSCAD_QTGUI #ifdef Q_OS_MACOS -std::pair customSyntax(const string& s) +std::pair customSyntax(const std::string& s) { if (s.find("-psn_") == 0) return {"psn", s.substr(5)}; #else -std::pair customSyntax(const string&) +std::pair customSyntax(const std::string&) { #endif @@ -959,20 +684,20 @@ int main(int argc, char **argv) auto original_path = fs::current_path(); - vector output_files; + std::vector output_files; const char *deps_output_file = nullptr; boost::optional export_format; ViewOptions viewOptions{}; po::options_description desc("Allowed options"); desc.add_options() - ("export-format", po::value(), "overrides format of exported scad file when using option '-o', arg can be any of its supported file extensions. For ascii stl export, specify 'asciistl', and for binary stl export, specify 'binstl'. Ascii export is the current stl default, but binary stl is planned as the future default so asciistl should be explicitly specified in scripts when needed.\n") - ("o,o", po::value>(), "output specified file instead of running the GUI, the file extension specifies the type: stl, off, wrl, amf, 3mf, csg, dxf, svg, pdf, png, echo, ast, term, nef3, nefdbg (May be used multiple time for different exports). Use '-' for stdout\n") - ("D,D", po::value>(), "var=val -pre-define variables") - ("p,p", po::value(), "customizer parameter file") - ("P,P", po::value(), "customizer parameter set") + ("export-format", po::value(), "overrides format of exported scad file when using option '-o', arg can be any of its supported file extensions. For ascii stl export, specify 'asciistl', and for binary stl export, specify 'binstl'. Ascii export is the current stl default, but binary stl is planned as the future default so asciistl should be explicitly specified in scripts when needed.\n") + ("o,o", po::value>(), "output specified file instead of running the GUI, the file extension specifies the type: stl, off, wrl, amf, 3mf, csg, dxf, svg, pdf, png, echo, ast, term, nef3, nefdbg (May be used multiple time for different exports). Use '-' for stdout\n") + ("D,D", po::value>(), "var=val -pre-define variables") + ("p,p", po::value(), "customizer parameter file") + ("P,P", po::value(), "customizer parameter set") #ifdef ENABLE_EXPERIMENTAL - ("enable", po::value>(), ("enable experimental features (specify 'all' for enabling all available features): " + + ("enable", po::value>(), ("enable experimental features (specify 'all' for enabling all available features): " + str_join(boost::make_iterator_range(Feature::begin(), Feature::end()), " | ", [](const Feature *feature) { return feature->get_name(); @@ -983,36 +708,36 @@ int main(int argc, char **argv) ("version,v", "print the version") ("info", "print information about the build process\n") - ("camera", po::value(), "camera parameters when exporting png: =translate_x,y,z,rot_x,y,z,dist or =eye_x,y,z,center_x,y,z") + ("camera", po::value(), "camera parameters when exporting png: =translate_x,y,z,rot_x,y,z,dist or =eye_x,y,z,center_x,y,z") ("autocenter", "adjust camera to look at object's center") ("viewall", "adjust camera to fit object") - ("backend", po::value(), "3D rendering backend to use: 'CGAL' (old/slow) [default] or 'Manifold' (new/fast)") - ("imgsize", po::value(), "=width,height of exported png") - ("render", po::value()->implicit_value(""), "for full geometry evaluation when exporting png") - ("preview", po::value()->implicit_value(""), "[=throwntogether] -for ThrownTogether preview png") + ("backend", po::value(), "3D rendering backend to use: 'CGAL' (old/slow) [default] or 'Manifold' (new/fast)") + ("imgsize", po::value(), "=width,height of exported png") + ("render", po::value()->implicit_value(""), "for full geometry evaluation when exporting png") + ("preview", po::value()->implicit_value(""), "[=throwntogether] -for ThrownTogether preview png") ("animate", po::value(), "export N animated frames") ("view", po::value(), ("=view options: " + boost::algorithm::join(viewOptions.names(), " | ")).c_str()) - ("projection", po::value(), "=(o)rtho or (p)erspective when exporting png") + ("projection", po::value(), "=(o)rtho or (p)erspective when exporting png") ("csglimit", po::value(), "=n -stop rendering at n CSG elements when exporting png") - ("summary", po::value>(), "enable additional render summary and statistics: all | cache | time | camera | geometry | bounding-box | area") - ("summary-file", po::value(), "output summary information in JSON format to the given file, using '-' outputs to stdout") - ("colorscheme", po::value(), ("=colorscheme: " + + ("summary", po::value>(), "enable additional render summary and statistics: all | cache | time | camera | geometry | bounding-box | area") + ("summary-file", po::value(), "output summary information in JSON format to the given file, using '-' outputs to stdout") + ("colorscheme", po::value(), ("=colorscheme: " + str_join(ColorMap::inst()->colorSchemeNames(), " | ", [](const std::string& colorScheme) { return (colorScheme == ColorMap::inst()->defaultColorSchemeName() ? "*" : "") + colorScheme; }) + "\n").c_str()) - ("d,d", po::value(), "deps_file -generate a dependency file for make") - ("m,m", po::value(), "make_cmd -runs make_cmd file if file is missing") + ("d,d", po::value(), "deps_file -generate a dependency file for make") + ("m,m", po::value(), "make_cmd -runs make_cmd file if file is missing") ("quiet,q", "quiet mode (don't print anything *except* errors)") ("hardwarnings", "Stop on the first warning") ("trace-depth", po::value(), "=n, maximum number of trace messages") - ("trace-usermodule-parameters", po::value(), "=true/false, configure the output of user module parameters in a trace") - ("check-parameters", po::value(), "=true/false, configure the parameter check for user modules and functions") - ("check-parameter-ranges", po::value(), "=true/false, configure the parameter range check for builtin modules") - ("debug", po::value(), "special debug info - specify 'all' or a set of source file names") - ("s,s", po::value(), "stl_file deprecated, use -o") - ("x,x", po::value(), "dxf_file deprecated, use -o") + ("trace-usermodule-parameters", po::value(), "=true/false, configure the output of user module parameters in a trace") + ("check-parameters", po::value(), "=true/false, configure the parameter check for user modules and functions") + ("check-parameter-ranges", po::value(), "=true/false, configure the parameter range check for builtin modules") + ("debug", po::value(), "special debug info - specify 'all' or a set of source file names") + ("s,s", po::value(), "stl_file deprecated, use -o") + ("x,x", po::value(), "dxf_file deprecated, use -o") #ifdef ENABLE_PYTHON ("trust-python", "Trust python") #endif @@ -1021,9 +746,9 @@ int main(int argc, char **argv) po::options_description hidden("Hidden options"); hidden.add_options() #ifdef Q_OS_MACOS - ("psn", po::value(), "process serial number") + ("psn", po::value(), "process serial number") #endif - ("input-file", po::value>(), "input file"); + ("input-file", po::value>(), "input file"); po::positional_options_description p; p.add("input-file", -1); @@ -1041,7 +766,7 @@ int main(int argc, char **argv) OpenSCAD::debug = ""; if (vm.count("debug")) { - OpenSCAD::debug = vm["debug"].as(); + OpenSCAD::debug = vm["debug"].as(); LOG("Debug on. --debug=%1$s", OpenSCAD::debug); } #ifdef ENABLE_PYTHON @@ -1068,7 +793,7 @@ int main(int argc, char **argv) for (const auto& flag : flags) { std::string name = flag.first; if (vm.count(name)) { - std::string opt = vm[name].as(); + std::string opt = vm[name].as(); try { (*(flag.second) = flagConvert(opt)); } catch (const std::runtime_error& e) { @@ -1081,14 +806,14 @@ int main(int argc, char **argv) if (vm.count("version")) version(); if (vm.count("info")) arg_info = true; if (vm.count("backend")) { - RenderSettings::inst()->backend3D = renderBackend3DFromString(vm["backend"].as()); + RenderSettings::inst()->backend3D = renderBackend3DFromString(vm["backend"].as()); } if (vm.count("preview")) { - if (vm["preview"].as() == "throwntogether") viewOptions.renderer = RenderType::THROWNTOGETHER; + if (vm["preview"].as() == "throwntogether") viewOptions.renderer = RenderType::THROWNTOGETHER; } else if (vm.count("render")) { // Note: "cgal" is here for backwards compatibility, can probably be removed soon - if (vm["render"].as() == "cgal" || vm["render"].as() == "force") { + if (vm["render"].as() == "cgal" || vm["render"].as() == "force") { viewOptions.renderer = RenderType::BACKEND_SPECIFIC; } else { viewOptions.renderer = RenderType::GEOMETRY; @@ -1113,33 +838,33 @@ int main(int argc, char **argv) } if (vm.count("o")) { - output_files = vm["o"].as>(); + output_files = vm["o"].as>(); } if (vm.count("s")) { LOG(message_group::Deprecated, "The -s option is deprecated. Use -o instead.\n"); - output_files.push_back(vm["s"].as()); + output_files.push_back(vm["s"].as()); } if (vm.count("x")) { LOG(message_group::Deprecated, "The -x option is deprecated. Use -o instead.\n"); - output_files.push_back(vm["x"].as()); + output_files.push_back(vm["x"].as()); } if (vm.count("d")) { if (deps_output_file) help(argv[0], desc, true); - deps_output_file = vm["d"].as().c_str(); + deps_output_file = vm["d"].as().c_str(); } if (vm.count("m")) { if (make_command) help(argv[0], desc, true); - make_command = vm["m"].as().c_str(); + make_command = vm["m"].as().c_str(); } if (vm.count("D")) { - for (const auto& cmd : vm["D"].as>()) { + for (const auto& cmd : vm["D"].as>()) { commandline_commands += cmd; commandline_commands += ";\n"; } } if (vm.count("enable")) { - for (const auto& feature : vm["enable"].as>()) { + for (const auto& feature : vm["enable"].as>()) { if (feature == "all") { Feature::enable_all(); break; @@ -1148,39 +873,39 @@ int main(int argc, char **argv) } } - string parameterFile; + std::string parameterFile; if (vm.count("p")) { if (!parameterFile.empty()) { help(argv[0], desc, true); } - parameterFile = vm["p"].as().c_str(); + parameterFile = vm["p"].as().c_str(); } - string parameterSet; + std::string parameterSet; if (vm.count("P")) { if (!parameterSet.empty()) { help(argv[0], desc, true); } - parameterSet = vm["P"].as().c_str(); + parameterSet = vm["P"].as().c_str(); } - vector inputFiles; + std::vector inputFiles; if (vm.count("input-file")) { - inputFiles = vm["input-file"].as>(); + inputFiles = vm["input-file"].as>(); } if (vm.count("colorscheme")) { - arg_colorscheme = vm["colorscheme"].as(); + arg_colorscheme = vm["colorscheme"].as(); } - ExportFileFormatOptions exportFileFormatOptions; if (vm.count("export-format")) { - const auto format = vm["export-format"].as(); - const auto format_iter = exportFileFormatOptions.exportFileFormats.find(format); - if (format_iter != exportFileFormatOptions.exportFileFormats.end()) { - export_format.emplace(format_iter->second); + const auto format_str = vm["export-format"].as(); + FileFormat format; + if (fileformat::fromIdentifier(format_str, format)) { + export_format.emplace(format); + } else { - LOG("Unknown --export-format option '%1$s'. Use -h to list available options.", format); + LOG("Unknown --export-format option '%1$s'. Use -h to list available options.", format_str); return 1; } } @@ -1247,18 +972,20 @@ int main(int argc, char **argv) if (deps_output_file) { std::string deps_out(deps_output_file); - const vector& geom_out(output_files); + const std::vector& geom_out(output_files); int result = write_deps(deps_out, geom_out); if (!result) { LOG("Error writing deps"); return 1; } } - } else if (QtUseGUI()) { +#ifndef OPENSCAD_NOGUI + } else if (useGUI()) { if (vm.count("export-format")) { LOG("Ignoring --export-format option"); } rc = gui(inputFiles, original_path, argc, argv); +#endif } else { LOG("Requested GUI mode but can't open display!\n"); return 1; diff --git a/src/openscad.h b/src/openscad.h index 114abd75920..04cf5263dd9 100644 --- a/src/openscad.h +++ b/src/openscad.h @@ -26,15 +26,17 @@ #pragma once +#include #include -#include -class SourceFile; +#include -extern bool parse(SourceFile *& file, const std::string& text, const std::string& filename, const std::string& mainFile, int debug); +extern bool parse(class SourceFile *& file, const std::string& text, const std::string& filename, const std::string& mainFile, int debug); -#include extern std::string commandline_commands; // Custom argument parser std::pair customSyntax(const std::string& s); + +void localization_init(); +void set_render_color_scheme(const std::string& color_scheme, const bool exit_if_not_found); diff --git a/src/openscad_gui.cc b/src/openscad_gui.cc new file mode 100644 index 00000000000..8dd123dca92 --- /dev/null +++ b/src/openscad_gui.cc @@ -0,0 +1,259 @@ +/* + * OpenSCAD (www.openscad.org) + * Copyright (C) 2009-2011 Clifford Wolf and + * Marius Kintel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * As a special exception, you have permission to link this program + * with the CGAL library and distribute executables, as long as you + * follow the requirements of the GNU GPL in regard to all of the + * software in the executable aside from CGAL. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "openscad_gui.h" + +#include +#include +#include +#include +#include + +#include "core/parsersettings.h" +#include "FontCache.h" +#include "geometry/Geometry.h" +#include "gui/AppleEvents.h" +#include "gui/LaunchingScreen.h" +#include "gui/MainWindow.h" +#include "gui/OpenSCADApp.h" +#include "gui/QSettingsCached.h" +#include "gui/Settings.h" +#include "openscad.h" +#include "utils/printutils.h" + +#include "gui/input/InputDriverManager.h" +#ifdef ENABLE_HIDAPI +#include "gui/input/HidApiInputDriver.h" +#endif +#ifdef ENABLE_SPNAV +#include "gui/input/SpaceNavInputDriver.h" +#endif +#ifdef ENABLE_JOYSTICK +#include "gui/input/JoystickInputDriver.h" +#endif +#ifdef ENABLE_DBUS +#include "gui/input/DBusInputDriver.h" +#endif +#ifdef ENABLE_QGAMEPAD +#include "gui/input/QGamepadInputDriver.h" +#endif + +Q_DECLARE_METATYPE(Message); +Q_DECLARE_METATYPE(std::shared_ptr); + +extern std::string arg_colorscheme; + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +namespace { + +// Only if "fileName" is not absolute, prepend the "absoluteBase". +QString assemblePath(const boost::filesystem::path& absoluteBaseDir, + const std::string& fileName) { + if (fileName.empty()) return ""; + auto qsDir = QString::fromLocal8Bit(absoluteBaseDir.generic_string().c_str()); + auto qsFile = QString::fromLocal8Bit(fileName.c_str()); + // if qsfile is absolute, dir is ignored. (see documentation of QFileInfo) + QFileInfo fileInfo(qsDir, qsFile); + return fileInfo.absoluteFilePath(); +} + +} // namespace + +void dialogThreadFunc(FontCacheInitializer *initializer) +{ + initializer->run(); +} + +void dialogInitHandler(FontCacheInitializer *initializer, void *) +{ + QFutureWatcher futureWatcher; + QObject::connect(&futureWatcher, SIGNAL(finished()), scadApp, SLOT(hideFontCacheDialog())); + + auto future = QtConcurrent::run([initializer] { + return dialogThreadFunc(initializer); + }); + futureWatcher.setFuture(future); + + // We don't always get the started() signal, so we start manually + QMetaObject::invokeMethod(scadApp, "showFontCacheDialog"); + + // Block, in case we're in a separate thread, or the dialog was closed by the user + futureWatcher.waitForFinished(); + + // We don't always receive the finished signal. We still need the signal to break + // out of the exec() though. + QMetaObject::invokeMethod(scadApp, "hideFontCacheDialog"); +} + +#ifdef Q_OS_WIN +void registerDefaultIcon(QString applicationFilePath) { + // Not using cached instance here, so this needs to be in a + // separate scope to ensure the QSettings instance is released + // directly after use. + QSettings reg_setting(QLatin1String("HKEY_CURRENT_USER"), QSettings::NativeFormat); + auto appPath = QDir::toNativeSeparators(applicationFilePath + QLatin1String(",1")); + reg_setting.setValue(QLatin1String("Software/Classes/OpenSCAD_File/DefaultIcon/Default"), QVariant(appPath)); +} +#else +void registerDefaultIcon(const QString&) { } +#endif + +#ifdef OPENSCAD_SUFFIX +#define DESKTOP_FILENAME "openscad" OPENSCAD_SUFFIX +#else +#define DESKTOP_FILENAME "openscad" +#endif + +int gui(std::vector& inputFiles, const boost::filesystem::path& original_path, int argc, char **argv) +{ + OpenSCADApp app(argc, argv); + // remove ugly frames in the QStatusBar when using additional widgets + app.setStyleSheet("QStatusBar::item { border: 0px solid black; }"); + + // set up groups for QSettings + QCoreApplication::setOrganizationName("OpenSCAD"); + QCoreApplication::setOrganizationDomain("openscad.org"); + QCoreApplication::setApplicationName("OpenSCAD"); + QCoreApplication::setApplicationVersion(TOSTRING(OPENSCAD_VERSION)); + QGuiApplication::setApplicationDisplayName("OpenSCAD"); + QGuiApplication::setDesktopFileName(DESKTOP_FILENAME); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#ifdef Q_OS_MACOS + app.setWindowIcon(QIcon(":/icon-macos.png")); +#else + app.setWindowIcon(QIcon(":/logo.png")); +#endif + + // Other global settings + qRegisterMetaType(); + qRegisterMetaType>(); + + FontCache::registerProgressHandler(dialogInitHandler); + + parser_init(); + + QSettingsCached settings; + if (settings.value("advanced/localization", true).toBool()) { + localization_init(); + } + +#ifdef Q_OS_MACOS + installAppleEventHandlers(); +#endif + + registerDefaultIcon(app.applicationFilePath()); + +#ifdef OPENSCAD_UPDATER + AutoUpdater *updater = new SparkleAutoUpdater; + AutoUpdater::setUpdater(updater); + if (updater->automaticallyChecksForUpdates()) updater->checkForUpdates(); + updater->init(); +#endif + + set_render_color_scheme(arg_colorscheme, false); + auto noInputFiles = false; + + if (!inputFiles.size()) { + noInputFiles = true; + inputFiles.emplace_back(""); + } + + auto showOnStartup = settings.value("launcher/showOnStartup"); + if (noInputFiles && (showOnStartup.isNull() || showOnStartup.toBool())) { + auto launcher = new LaunchingScreen(); + auto dialogResult = launcher->exec(); + if (dialogResult == QDialog::Accepted) { + if (launcher->isForceShowEditor()) { + settings.setValue("view/hideEditor", false); + } + auto files = launcher->selectedFiles(); + // If nothing is selected in the launching screen, leave + // the "" dummy in inputFiles to open an empty MainWindow. + if (!files.empty()) { + inputFiles.clear(); + for (const auto& f : files) { + inputFiles.push_back(f.toStdString()); + } + } + delete launcher; + } else { + return 0; + } + } + + QStringList inputFilesList; + for (const auto& infile: inputFiles) { + inputFilesList.append(assemblePath(original_path, infile)); + } + new MainWindow(inputFilesList); + app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(releaseQSettingsCached())); + app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); + +#ifdef ENABLE_HIDAPI + if (Settings::Settings::inputEnableDriverHIDAPI.value()) { + auto hidApi = new HidApiInputDriver(); + InputDriverManager::instance()->registerDriver(hidApi); + } +#endif +#ifdef ENABLE_SPNAV + if (Settings::Settings::inputEnableDriverSPNAV.value()) { + auto spaceNavDriver = new SpaceNavInputDriver(); + bool spaceNavDominantAxisOnly = Settings::Settings::inputEnableDriverHIDAPI.value(); + spaceNavDriver->setDominantAxisOnly(spaceNavDominantAxisOnly); + InputDriverManager::instance()->registerDriver(spaceNavDriver); + } +#endif +#ifdef ENABLE_JOYSTICK + if (Settings::Settings::inputEnableDriverJOYSTICK.value()) { + std::string nr = STR(Settings::Settings::joystickNr.value()); + auto joyDriver = new JoystickInputDriver(); + joyDriver->setJoystickNr(nr); + InputDriverManager::instance()->registerDriver(joyDriver); + } +#endif +#ifdef ENABLE_QGAMEPAD + if (Settings::Settings::inputEnableDriverQGAMEPAD.value()) { + auto qGamepadDriver = new QGamepadInputDriver(); + InputDriverManager::instance()->registerDriver(qGamepadDriver); + } +#endif +#ifdef ENABLE_DBUS + if (Feature::ExperimentalInputDriverDBus.is_enabled()) { + if (Settings::Settings::inputEnableDriverDBUS.value()) { + auto dBusDriver = new DBusInputDriver(); + InputDriverManager::instance()->registerDriver(dBusDriver); + } + } +#endif + + InputDriverManager::instance()->init(); + int rc = app.exec(); + const auto& windows = scadApp->windowManager.getWindows(); + while (!windows.empty()) delete *windows.begin(); + return rc; +} diff --git a/src/openscad_gui.h b/src/openscad_gui.h new file mode 100644 index 00000000000..52e9b508608 --- /dev/null +++ b/src/openscad_gui.h @@ -0,0 +1,35 @@ +/* + * OpenSCAD (www.openscad.org) + * Copyright (C) 2009-2011 Clifford Wolf and + * Marius Kintel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * As a special exception, you have permission to link this program + * with the CGAL library and distribute executables, as long as you + * follow the requirements of the GNU GPL in regard to all of the + * software in the executable aside from CGAL. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#pragma once + +#include +#include + +#include + + +int gui(std::vector& inputFiles, const boost::filesystem::path& original_path, int argc, char **argv); diff --git a/tests/regression/pdfexporttest/centered-expected.png b/tests/regression/pdfexporttest/centered-expected.png index fd73987d2bc..e10feee8d0c 100644 Binary files a/tests/regression/pdfexporttest/centered-expected.png and b/tests/regression/pdfexporttest/centered-expected.png differ diff --git a/tests/regression/pdfexporttest/simple-pdf-expected.png b/tests/regression/pdfexporttest/simple-pdf-expected.png index 97c59acbfcb..a4566a232b2 100644 Binary files a/tests/regression/pdfexporttest/simple-pdf-expected.png and b/tests/regression/pdfexporttest/simple-pdf-expected.png differ diff --git a/tests/regression/povexport/pov-export-expected.pov b/tests/regression/povexport/pov-export-expected.pov index f7382979b48..f68b35cc516 100644 --- a/tests/regression/povexport/pov-export-expected.pov +++ b/tests/regression/povexport/pov-export-expected.pov @@ -1,5 +1,5 @@ // Generated by OpenSCAD! -// Source file: +// Source file: pov-export.scad #version 3.7; global_settings { assumed_gamma 1.0 }