mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-04 14:20:58 +00:00
245a94ab94f607c492cba2e178a02003280fade6
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6e0f04815b |
perf: speed up G-code export by up to 7% via post-processing fixes (#16031)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> |
||
|
|
84657ff11e |
Add Missing Includes Across the Remaining Sources and Tests (#16071)
* Ignore Clipper, libpng, mcut and Boost.Polygon Internals in clang-tidy Each only works through a wrapper or umbrella header: libslic3r/clipper.hpp or clipper_z.hpp configure Clipper before including it, png.h pulls in libpng's config headers, and Boost.Polygon's headers only compile through polygon.hpp or voronoi.hpp. * Ignore minilzo's Config Headers in clang-tidy lzoconf.h and lzodefs.h are internal to minilzo.h, which is what the code includes. * Add Missing Includes Across the Remaining Sources and Tests Covers src/slic3r/Utils, src/slic3r/plugin, src/slic3r/Config, src/libvgcode, src/dev-utils, src/OrcaSlicer.cpp and tests/, the directories left after src/slic3r/GUI and src/libslic3r. Generated with clang-tidy misc-include-cleaner. libvgcode's own headers are included by relative path as in the rest of that library, and Catch2 and pybind11 with angle brackets as elsewhere in the repo. * Make the GUI and Test Headers Compile on Their Own Each now includes, or forward-declares, what it uses instead of relying on what its includers happened to include first. Headers that only compile on one platform, or that nothing built includes, are left alone. * Keep Windows and nanosvg Setup Ahead of the Added Includes OrcaSlicer.cpp and several tests set _WIN32_WINNT, WIN32_LEAN_AND_MEAN or NOMINMAX before including Windows.h, and the profile validator defines NANOSVG_IMPLEMENTATION before any libslic3r header. The added includes had landed above those blocks, which broke the Windows build. * Add the GUI Includes the First Pass Missed Covers headers that only became editable once they compiled on their own, and wx symbols whose suggested header changed as the clang-tidy ignore list grew after the src/slic3r/GUI pass. * Keep the Added Test Includes Below the NOMINMAX Guard test_marchingsquares.cpp and test_texture_displacement.cpp had includes inside #ifndef NOMINMAX, which the tests inherit as defined on Windows from libslic3r, so those were skipped there. .clang-tidy also ignores the MSVC STL and UCRT internals, Boost.Multiprecision's fwd.hpp and CPython's Windows include directory, as in #16068. |
||
|
|
c023632a7d |
Lock Config and Preset Files Across Instances and Write Them Atomically (#15861)
* Lock Config and Preset Files Across Instances and Write Them Atomically Every running instance shares one OrcaSlicer.conf and one user preset tree, and nothing kept their writers apart. Two instances saving at the same moment, or the cloud preset sync thread writing while the GUI thread saved, could interleave, and a reader in another instance could open a preset JSON or .info file between truncate and close and get a partial file, dropping that preset for the session with a parse error. Add InstanceLock, a scoped guard that serialises the threads of one process through a recursive mutex and other processes through an advisory OS file lock: flock on POSIX, held on the guard's own descriptor so no other close in the process can drop it, and LockFileEx on Windows. The outermost guard opens the lock file and closes it on release, so nothing stays open between saves and a data dir can be removed once nothing is saving into it; the file itself is kept, since deleting it would let a third instance lock a fresh file while the second still holds the old one. It is best effort: when the lock file cannot be opened or locked, or another instance still holds it after a second, the guard logs once and lets the write proceed, then leaves the file alone for ten seconds, so a hung instance never blocks every other one and a holder stuck in a debugger does not cost a stall per save. The guard sits at the leaf readers and writers: set_sync_info_and_save() calls save_info() under the preset collection mutex, so a batch lock around save_user_presets() would invert the order against the sync thread. The user preset scan reads its files on worker threads without the guard, since the mutex would serialise them, and takes it per file in the serial commit step, so a save never waits for the whole scan. Each read keeps the bytes of the preset and its .info as they were before parsing; commit compares them with the disk under the guard and reads a file that changed again, so it never deletes or writes back over another instance's newer save, nor installs a .json and .info from two different saves; a preset another instance removed in the meantime is not installed. Without the guard, in a cool-down, the scan still loads the presets but leaves their files alone: an unreadable file stays for the next scan, and a derived compatible printer is not written back. Read-only scans, which is what the CLI does, take no lock and create no lock file. AppConfig holds OrcaSlicer.conf.lock in load() and save(); load is included because the Windows path restores from the .bak copy. Every user preset writer and reader holds user.lock: Preset::save(), which writes no .info when the preset itself could not be written, since an .info without its preset reads as a cloud deletion request, save_info(), reload() and remove_files(), each preset the scan commits, the bundle metadata reads and write, the .info removal after a cloud-confirmed delete, the orphaned-.info scan on the sync thread, the bundle folder removal on unsubscribe and the physical printer writers and delete paths. A bundle import extracts under cache/ into a folder per process and per import, where no scan reads. Preset JSON, .info, bundle metadata, physical printer and config files, and the caches and state files that already used a temporary by hand, now go through write_file_atomically(), which writes <file>.<pid>.<n>.tmp beside the target and renames it over, so a reader that never waits sees a complete old or new file. A symlink is followed; a target that is not a regular file is written in place; and when no temporary can be created beside an existing target, or the rename itself is refused, by a Windows reader holding the file open or a mount that cannot replace in one step, the helper writes in place as before, since losing the save is worse than a torn read. On POSIX the rename replaces the target atomically where the old code removed it first and left a window with no file at all; only a mount that refuses a one-step replace gets the old remove-then-rename. A crash between temporary and rename leaves the temporary behind, which no scan reads. Preset::save() returns whether it wrote the preset, so the scan counts a compatible printer it could not write back as an error. A failed config write keeps the config dirty, and the idle handler waits ten seconds before retrying while an explicit save always tries. * Run the Cross-Process Lock Test on Every Platform The test that checks the guard yields to a lock held elsewhere forked a child to hold it, so it was left out on Windows. The OS lock belongs to the handle on Windows and to the open file description elsewhere, so a second handle in the same process is refused like another instance would be. The test now holds the lock that way and runs everywhere. |
||
|
|
203bc63f35 |
Escape Project Metadata in the Project Page and Restrict Accessory Opening (#15956)
* Escape Project Metadata in the Project Page and Restrict Accessory Opening The Project page rendered the model and profile name, author, description and accessory file names from the 3MF as live HTML. Names, authors and file names are now set as text, and the file list is built from DOM nodes with bound click handlers instead of concatenated markup. Descriptions can legitimately carry rich-text HTML, so they are rebuilt from an inert DOMParser document, keeping only plain formatting tags, http(s) links and http(s) images, with every other attribute dropped. Opening an accessory from the page now only launches regular files that lie inside the project's extracted auxiliary directory. The containment check is a new libslic3r helper, is_absolute_path_within_root, built on is_path_within_root so symlinks leading out of the root are rejected too. * Tighten Project Page Description Rendering and Keep More Formatting Link and image URLs in descriptions must now start with an http or https scheme as written and parse as such with the URL parser. Preview images are built as DOM nodes like the file list, and accessory names show their full text as a tooltip. Descriptions keep more plain formatting: del, ins, figure, figcaption, dl, dt, dd, caption, q, abbr, kbd and wbr, plus alt, title, width and height on images, colspan and rowspan on table cells and start on ordered lists. Numeric attributes must be plain integers. Embedded YouTube players become a link to the video. * Confirm Before Opening Program Attachments and Load Only HTTPS Images Opening a project attachment whose type runs as a program or script (executables, installers, shortcuts, shell and PowerShell scripts, macOS command files and apps, Linux desktop entries) now asks for confirmation first. The check lives in libslic3r as is_executable_file_name and ignores the trailing dots and spaces Windows strips from file names. Images in project descriptions are kept only when they load over https, so opening the Project tab no longer issues plain-http requests. * Open Project Attachments Through One Guarded Helper The Edit Project Info view launched attachments directly, without the checks the project page has. Both now call desktop_open_project_attachment, which checks that the file is inside the auxiliary directory, asks for confirmation where needed and then opens it. The auxiliary root was built through encode_path, which returns code page bytes on Windows, while boost::filesystem reads a narrow string as UTF-8. With a non-ASCII temporary directory the root never matched and no attachment opened. It is now built from the UTF-8 path directly. The list of program extensions could not be kept complete and let unknown types open without a prompt. It is replaced by is_safe_to_open_file_name, a list of plain document, image, model and video types that open directly. Everything else asks first. |
||
|
|
ba468c842d |
Confine Updater and Plugin Archive Extraction to the Target Directory (#15957)
* Confine Updater Archive Extraction to the Target Directory The preset updater extracted downloaded archives by appending each entry name to the cache directory, and the network plugin installer did the same for the plugin folder, without checking that the result stays inside it. Move the updater's extraction into libslic3r as extract_archive_confined, which validates every entry with is_path_within_root before writing anything and fails the whole archive if one entry resolves outside the target. The plugin installer now rejects such an entry the same way. Well formed archives extract exactly as before. * Harden Archive Extraction Against Symlinks The plugin installer now creates a symlink entry only when its target is relative and, joined to the link's own directory, passes is_path_within_root, via the new is_symlink_target_within_root helper. Before writing any entry it checks the destination with symlink_status, so an existing symlink, dangling or not, is replaced rather than followed, and it creates parent directories inside the existing error handling. extract_archive_confined replaces a symlink at a destination file the same way. is_path_within_root now ignores a trailing separator on the root, which previously made every path fail the check. * Validate Plugin Symlink Targets Before Replacing Existing Files A symlink entry's target is now read and checked before anything already at its destination is removed or renamed aside, so an archive rejected for its link target leaves the installed plugin files in place. * Reject Paths with an Embedded NUL When Confining Extraction is_path_within_root compared each component with "..", so a name such as "..\0" passed the check. The filesystem calls stop at the NUL and act on a shorter path than the one that was checked: a symlink target read from a plugin archive as raw bytes was created as "..", pointing out of the plugin directory. A path containing a NUL is now rejected before anything touches the filesystem, which covers every caller, including entry names taken from the Unicode Path extra field. |
||
|
|
41eeaf3883 |
Sanitize Server-Supplied Download File Names (#15955)
* Sanitize Server-Supplied Download File Names The URL downloader used the file name from the Content-Disposition header as given, without the cleaning and unused-name search applied to the URL-derived name. Reduce the header name to a sanitized base name with the new sanitize_file_basename helper, which splits on both path separators and rejects names made only of dots and spaces. Run the result through the same unused-name search as the URL-derived name, now shared in find_unused_filename, and fall back to the URL-derived name when nothing usable remains. * Sanitize Download Names Before Choosing an Unused One The unused-name search probed the name as given and sanitized the result afterwards, so a name whose special characters are replaced could be mapped onto a file that already exists. Move the search into libslic3r as find_unused_filename, sanitize first and probe the name that is actually written. The download marker path is shared through download_marker_path. Restore the last tried name in the error reported when no free name is found, and cover the search with unit tests. * Keep Downloads on an Unused Name Until They Complete When the server supplied the name, the download marker stayed under the URL-derived name, so the adopted name was not reserved against other downloads. The final rename also replaced any file that took the name while the download ran. Move the marker to the adopted name before any data is written, and check the name again right before the final rename, picking the next free name if it is taken by then. * Sanitize the File Name of Model Import Links The model import took the file name from the link as given and only avoided an existing file with a substring match on the folder listing. Reduce the name to a sanitized base name, falling back to untitled.3mf, choose the name with the shared unused-name search, and check it again before the final rename. * Handle Filesystem Errors When Finishing a Model Import Download Choosing the final name and moving the downloaded project into place could throw from inside the download callback. Any such error now removes the temporary file and reports the existing import failure message. |
||
|
|
efc9f253ee |
fix: resolve relative input paths given on the command line (#14803)
Opening a model with a relative path, for example `orca-slicer ./some.3mf`, failed with "Loading of a model file failed." and "The file does not contain any geometry data.", while the same file opened by an absolute path or by drag and drop worked. GUI_App::init_app_config() changes the working directory to <data_dir>/log, and it runs from the GUI_App constructor because the app config is needed early for instance checking. The input files are opened much later, in post_init(), so a path still relative at that point resolved against the log directory instead of the directory OrcaSlicer was started from, and the 3MF reader failed to open it. Resolve the input paths in CLI::setup(), which runs before GUI_App is constructed and therefore before the working directory moves. Absolute paths are returned unchanged, so the forms that open today are unaffected, and custom open protocol URLs are passed through since post_init() hands those to the downloader rather than the file loader. The working directory change is left alone. It was added in #3248 so the TUTK logs land in the data directory instead of the working directory (#3209). |
||
|
|
fa3dbfcc6f |
fix: clear 1 warning - report the real error when a Windows G-code export fails (#15582)
fix: report the real error when a Windows G-code export fails copy_file built its failure message as "Error: " + errCode. Adding a DWORD to a string literal is pointer arithmetic, not concatenation, so the pointer lands errCode bytes into an 8-byte literal and runs past its end for any code above 7. std::string then calls strlen on it and throws length_error, and the catch(...) in BackgroundSlicingProcess::finalize_gcode replaces the diagnosis with "Unknown error occurred during exporting G-code." Every code a user is likely to hit is past the end: write-protected media is 19, no media 21, a full disk 112, and a destination held open by another program 32. Codes 1 to 7 stay inside the literal and produce a truncated message instead. So the "Maybe the SD card is write locked?" text has not been reachable on Windows since this path was added in #2923. Now that it is reachable, that guess only fits removable media, so it is conditional on m_export_path_on_removable_media. The existing string is untouched and keeps its 23 translations; the fixed-drive case adds one string. |
||
|
|
2860353b9f |
fix: multi-user slicing crash on shared temp dir (#14607)
On Linux every account shares /tmp, but slicing builds temp paths there under fixed, app-owned names via temporary_dir() (model backups, STEP import, part-skip). The first user to slice creates and owns those dirs, so the next user cannot write under them and slicing crashes with "No such file or directory". Tag the app temp root with the user id at startup (<temp>/orcaslicer_<uid>) so every temporary_dir() consumer is isolated at once. The id stays at the top level of the world-writable system temp so each user's dir is created directly there; a shared parent dir would be owned by whichever user made it first. The root is pre-created because STEP import writes into it directly. Windows keeps the plain temp dir since it is already per-user. Fixes #10108. Same root cause as #5969. |