# Description
This PR introduces lifecycle events to the plugin API.
For all plugin capabilities, you can define a `on_lifecycle_event`
function in the plugin that takes in a event enum and a small payload
for some generic information on the lifecycle event.
The idea is to keep the payload generic and small, and if you want to
get more information, you should invoke other more targeted APIs to get
more information.
For example, lets say you are keeping track of the the `ObjectAdded*`
event hook for model transformation, addition or deletion. The payload
would tell you the name of the model, and you should use a targeted API
such as `orca.host.plater().model()` to get more information on the
model. This is the overall design principle of the API.
Currently the lifecycle events are the following:
```cpp
enum class LifecycleEvent {
// Project (3mf)
NewProject,
ProjectOpened,
ProjectBeforeSave,
ProjectAfterSave,
ProjectClosed,
ProjectDirtyChanged,
// Slicing pipeline
SliceStarted,
SliceGeometryFinished,
GCodeExportStarted,
GCodeExportFinished,
SlicingJobComplete,
// Plate/model editing
ObjectAdded,
ObjectDeleted,
ObjectTransformed,
ObjectChanged,
ObjectRenamed,
PlateCreated,
PlateDeleted,
PlateSelected,
PlateRenamed,
// Preset
PresetSelected,
PresetSaved,
// Printer/device
PrintStateChanged,
DeviceOnlineChanged,
DeviceDiscovered,
DeviceSelected,
DeviceConnected,
DeviceDisconnected,
UploadStarted,
UploadFinished,
// Print/send jobs
PrintJobStarted,
PrintJobFinished,
SendJobStarted,
SendJobFinished,
};
```
This is an initial draft and lifecycle events can be included later on.
[orca_telegram_notifier_plugin_any.py](https://github.com/user-attachments/files/31220973/orca_telegram_notifier_plugin_any.py)
If you're familiar with telegram bots, after you install the telegram
bot, in the config of this plugin, you can enter the Bot ID and the Chat
ID with said bot.
# Screenshots/Recordings/Graphs
<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->
## Tests
<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->
For this plugin, I am testing it with a telegram bot that sends me a
message on lifecycle event.
<!--
> A guide for users on how to download the artifacts from this PR.
-->
[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
The CLI turned "align to Y axis" on for every i3 printer with no way to opt
out. With rotations forbidden the pre-rotation is the result, so every object
ends up turned 90 degrees from how it was loaded. The GUI defaults the
checkbox the same way for i3 printers, but lets the user untick it.
Add --align-to-y-axis. When it is not given the printer-structure rule still
applies, so existing calls are unchanged; the CLI's own options are filled
with defaults after parsing, so the keys the user typed are remembered to
tell the two apart.
* Answer the Preview's Per-Frame Time Query From a Cached Sum
The G-code preview's cost is linear in the number of toolpath vertices, and on a
tall multi-filament print the wipe tower dominates that count: it emits a roughly
constant 160-180 moves on every layer whatever the object is, measured at 57-61%
of all moves on a three-filament print.
Four places scanned or allocated across the whole vertex array. None of them
needed to.
get_estimated_time_at re-accumulated the estimated time from vertex 0 on every
call, and its caller is the tool marker tooltip, which ImGui re-renders every
frame while the properties panel is unfolded. It now starts from a running sum
kept at each layer's first vertex, built at load in vertex order, and adds only
that layer's vertices: the same additions in the same order, so the float result
is unchanged, at a cost of one float per layer and time mode rather than per
vertex. At the 351k vertices of a 636-layer test print the call scanned the whole
print (238us); it now scans one layer.
update_view_full_range walked from vertex 0 to find where the layer range starts,
on every slider tick. It now starts at the first vertex of that layer. The index
is derived from the vertices rather than from Layers::Item::range, because
Layers::update folds a vertex whose layer_id arrives out of order into whichever
bucket is open, which makes that range the wrong answer in general; the index
costs four bytes per layer, not per vertex.
update_colors_texture allocated one float per vertex of the whole print on every
slider tick. It now reuses a buffer.
render_legend fetched the layer Zs and the per-layer times from inside loops over
the custom G-code items, and built whole vectors only to test them for emptiness.
The times are hoisted, the Zs are built lazily so a print with no colour change
does not pay for them at all, and the emptiness tests use the existing counters.
No rendering behaviour changes.
* Draw the Preview's Toolpath Segments From an Index Buffer
The preview's frame cost is dominated by one call: a single instanced draw of
every visible toolpath segment. On a tall multi-filament print the wipe tower
supplies most of those segments, which is why the preview of a large tower is
slow and why shrinking the layer range speeds it up again.
That draw is not fill bound. Shrinking the model to about a fortieth of its
screen area moved the frame from 419 ms to 401 ms, so the cost is per segment,
not per pixel, and it is paid in the vertex shader: five texelFetch calls plus
several cross/normalize per invocation.
Each segment is a box of eight corners, but it was submitted with
glDrawArraysInstanced over a 24 entry array, so every corner was transformed
once per triangle that touches it and the shader ran 24 times per segment. The
same 24 entries are now an element buffer over the eight distinct corners, which
lets the post-transform cache reuse them and drops the shader to 8 runs per
segment. The triangles, their winding and the vertex_id each corner receives are
unchanged.
Measured over 100 frames on the 636-layer, 351k-vertex three-filament fixture,
the segment draw goes from 381 ms to 322 ms per frame. That is a software
rasterizer, where triangle setup dominates and understates the win; the drop in
shader invocations is the transferable part.
Verified by loading the same project in this build and in a build of the parent
commit and comparing the canvas across three states - the default view, a
rotated camera, and a reduced layer range: pixel identical in all three. The
rotated case matters because the shader picks its corner offsets from the camera
direction. The only pixels that differ anywhere on screen are in the G-code text
panel, which prints a per-process object id that varies between any two runs.
The slice check centres its cube on the bed, puts the prime tower beside
it, then pulls the tower alone inside the printable outline. On a bed too
narrow for the estimated footprint that pull drags the tower back over
the cube: Volumic EXO42 IDRE MIRROR MODE (189 mm wide, 87.6 mm estimate)
logged "gcode path conflicts found between WipeTower and cube" in every
run, and three ~105 mm beds were left with 0.15 to 3.3 mm of clearance.
The cube and the tower's footprint are now pulled inside as one rigid
pair, so the clearance between them is fixed by construction. A bed too
small for the pair keeps the old placement, and presets that were never
clamped keep their exact layout.
# Description
<!--
> Please provide a summary of the changes made in this PR. Include
details such as:
> * What issue does this PR address or fix?
> * What new features or enhancements does this PR introduce?
> * Are there any breaking changes or dependencies that need to be
considered?
-->
A Python printer agent plugin could take the host down, and one of its
operations could never report its result. This PR fixes both in
`PrinterAgentPluginCapabilityTrampoline.hpp`.
## Changes
### A faulty printer agent no longer throws into the GUI
`IPrinterAgent` reports failure through return values, and none of its
callers catch. A Python `raise`, a missing override or a wrongly typed
return from a printer agent plugin therefore escaped the trampoline as a
C++ exception.
Every trampoline operation now catches, logs `Printer agent plugin
'<key>': <operation> failed: <error>`, and answers with what
`NetworkAgent` returns when no printer agent is set. `BBLPrinterAgent`
returns the same values when the Bambu plug-in is unavailable:
- `-1` for every `int` status code
- `false` for `start_discovery` and `fetch_filament_info`
- `""` for `get_user_selected_machine`
- an empty `AgentInfo` for `get_agent_info` (registration already
rejects an empty agent ID)
- `FilamentSyncMode::none` for `get_filament_sync_mode`
`ORCA_PY_AGENT_OVERRIDE(ret, name, ...)` derives the fallback from the
return type through `printer_agent_failure<ret>()`, so the call sites
carry no fallback values of their own.
An exception is the safety net for plugin bugs, not an error channel. A
plugin reports an expected failure by returning a code, as the Bambu
plug-in does. A raise is logged as a failure and collapses to the
generic `-1`, so the GUI shows the generic message instead of the
specific one (`-18` cancelled, `-4020` FTP upload failed, …).
### `bind_detect` results now reach the host
`detect` is an out-parameter (`detectResult&`). pybind11 casts a
reference argument to an override with a copy, so a plugin that filled
in `detect` wrote to a throwaway object and the host always saw an empty
`detectResult`. It is now passed so that Python edits the caller's
struct. Plugins see the same `DetectResult` argument as before.
## TODO
- Expose the `BAMBU_NETWORK_*` return codes to Python (the
`orca.printer_agent` binding and the generated stub from
`scripts/generate_orca_python_stubs.py`). Plugins can already return
them, but only as hard-coded numbers.
# Screenshots/Recordings/Graphs
<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->
## Tests
<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->
- New `tests/slic3rutils/test_plugin_printer_agent.cpp`: an agent whose
operations raise, one that omits them, and one that returns the wrong
type all answer like a missing agent, and the interpreter stays usable.
A working agent's answers reach the host unchanged, including
`request_bind_ticket`'s out-param and the fields a plugin writes into
`bind_detect`'s `detect`.
- The `bind_detect` check failed before the fix (`"" == "192.168.0.2"`)
and passes after.
- `slic3rutils` passes under `ctest` (144/144); full Release build clean
on Linux.
- End to end on Linux with a test plugin whose chosen operations raise
(`start_discovery`, `get_filament_sync_mode`, `disconnect_printer`):
selecting the plugin's agent in the printer preset and switching back
logged each raise as a `Printer agent plugin '…': <operation> failed`
line, and the app kept running and closed cleanly (exit 0). Without the
guard, the first raise (`start_discovery`, on selecting the agent) ended
the app with `Uncaught exception` and SIGABRT (exit 134); that run used
a build whose printer-agent files are identical to `main`.
<!--
> A guide for users on how to download the artifacts from this PR.
-->
[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
pybind11 copies a reference argument to an override, so a Python
printer agent that filled in detect wrote to a throwaway object and the
host always saw an empty detectResult. Pass it as a pointer so Python
edits the caller's struct.
orca.host.ui.create_dock_panel(html, title, width, height, on_message,
on_close, dock) hosts plugin HTML in a pane of the Plater's dock manager,
next to the sidebar, and returns a UiDockPanel handle
(post/show/hide/close/is_open). The arguments follow create_window(). The
panel uses the window.orca bridge of plugin windows, restores its position
and size from the saved window layout, hides with the Plater off the Prepare
and Preview tabs when floating, and is closed with its plugin; plugin panes
are removed in MainFrame::shutdown().
The web view hosting moves out of PluginPage into a shared WebPanel base:
bootstrap page and swap to the plugin HTML, theme, element-default and
bridge scripts, window.orca message parsing, delivery to the page, and live
re-theming, also re-applied on every load after the swap. Pages tabs and
docked panels both derive from it. Pages tabs now re-theme in place on a
theme change instead of being reloaded, and a window.orca call a host does
not support is logged.
What the hosts share no longer lives in one of them: the bootstrap page, the
base URL and the plugin-window bridge move to Widgets/WebHosting, used by
WebDialog and WebPanel alike. The Plater restores plugin panes with a new
saved-layout parser, GUI/AuiPaneLayout, kept in its own small header so
slic3rutils can test it without pulling in the Plater.
The web hosting classes carry no plugin name, so other hosts can reuse them:
PluginWebDialog becomes WebDialog (its bootstrap page moves to
resources/web/dialog/WebDialog), and destroy_for_plugin(),
load_plugin_content() and plugin_defaults_user_script() become
destroy_silently(), load_page_html() and element_defaults_user_script().
Includes a sample plugin (sandboxes/orca_dock_panel_plugin_any.py) and
binding and layout-helper tests in slic3rutils.
IPrinterAgent callers do not catch, so a Python raise, a missing
override or a wrongly typed return from a printer agent plugin escaped
into the GUI. Each trampoline operation now logs the failure and
answers with NetworkAgent's no-agent value: -1 for status codes, the
empty value otherwise.
Plugin dialog content is injected with SetPage, so on the WebKit backends a
reload (context menu, keyboard shortcut or location.reload()) re-fetches the
SetPage base URL instead of the injected document, and the plugin UI is gone
for good: load_plugin_content() returned early once m_content_loaded was set.
Re-inject the plugin HTML when a main-frame load after the initial swap is
neither that swap nor a page the plugin linked to. m_own_page_load marks the
load our own SetPage caused, and the URL test recognises the reload: the
injected document and the directory a reload re-fetches both report the base
URL, so a load of any other URL is left alone. The test ignores a fragment the
page navigated to, and undoes the escaping the web view applies to what the
resources path holds.
A load reaching the base URL is not enough on its own, because WebKitGTK reports
a navigation that never committed against the document that stayed and then
finishes that document again: a link to a missing file therefore arrives as a
load of the base URL and reads exactly like a reload. So the re-injection also
requires a navigation to the base URL to have committed, which a reload always
does and a failure never does.
A bootstrap page that cannot be loaded is still not recovered from: WebKitGTK
substitutes a stock error page for it, and that load supersedes the swap
whichever way the swap is ordered around it. The file ships, so this is a
broken-install path; nothing here makes it worse than it already was.
No separate MSW path is needed: wxWebViewEdge ignores the SetPage base URL, so
its documents report about:blank and the test never matches there, and WebView2
reloads NavigateToString content from its own history entry anyway.
* Add Ultimaker S5 Profile - WIP
* Add Ultimaker physical printer base
* Ultimaker API WIP for base update.
* Fix undefined reference error.
* Add UMS5 profile, add placeholders for testAuth stuff.
* Fix non-const func definitions, make func names align with style guide.
* Localization stuff? IDK if this does anything or is required.
* Add cover image
* Auth code cleanup, add UI button for auth cred generation, implement various auth checks and tests.
* Fix auth stuff
* Clean up code
* Get upload code sort-of working, fix typo
* Update printer settings and start/end gcodes
* Add makeGriffinCompatible preprocessor script to prevent machine crash, update machine profile.
* Fix buildplate size, fix time missing bug, add S5 buildplate model
* Correct capitalization to UltiMaker
* Fix display bug, fix capitalization bug
* Implement credential generation button and logic
* Fix generate auth creds button, add todos, fix capitalization.
* Actually fix generate auth credentials.
* Fix generate auth creds message.
* Update UM S5 machine limits
* Revert accidental commit.
* Fix postprocessor, clean up code.
* Update presets for multi-extruder printing.
* Add Single Extruder and Fast profiles.
* Code cleanup
* Register and validate the UltiMaker S5 presets
* Show the Generate API Key button only for UltiMaker print hosts
The PR added it to the Physical Printer dialog for every host type, where pressing it runs an ordinary connection test and reports "API Key created". Gate it on the selected host type instead.
* Drop unused lambda captures in the UltiMaker host code
clang promotes -Wunused-lambda-capture to an error under the project's -Werror, so the file failed to build on macOS; the five callbacks do not touch this.
* Fix the Windows build of the UltiMaker print host
---------
Co-authored-by: SoftFever <softfeverever@gmail.com>
# Description
A plate using one filament no longer reserves or draws a prime tower in
the plater just because multiple filament slots are configured. The
shared footprint estimate gated "no tower" on the purge volume being
zero, but on non-Bambu printers with the shipped defaults that volume
comes from the SEMM flush matrix, which reads every configured slot and
stays nonzero even when only one filament is used — so the preview
showed a tower the print could never contain. The estimate now decides
from the filament count alone, with wrapping detection and smooth
timelapse staying the only reasons a single-filament plate keeps a
tower, matching `Print::has_wipe_tower()`. The gate also moved above the
flush-volume scan so the lone-filament path no longer pays for it.
No change to slicing output — toolpath generation already omitted the
tower for a single used filament; this removes the phantom preview,
placement reservation, and validation footprint.
# Screenshots/Recordings/Graphs
Before the fix:
https://github.com/user-attachments/assets/c5b0ea61-127d-408e-bafe-f1f76c58b996
After the fix:
https://github.com/user-attachments/assets/ac6632b3-5d44-4013-aca3-73f6cfd1139c
## Tests
<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->
Added a shipped-defaults single-filament case to
`tests/libslic3r/test_wipe_tower_estimate.cpp`: it estimates a 5.04
mm-deep tower before the fix and zero depth after. `[WipeTowerEstimate]`
and the fff_print `[WipeTower]` suites pass.
<!--
> A guide for users on how to download the artifacts from this PR.
-->
[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
The no-tower case was gated on there being no purge volume, but the SEMM flush matrix reads every configured slot and is nonzero even when only one filament is used, so the plater preview drew a tower the print would never contain.
* Fix Publish 3MF dialog styling
Use Orca's shared checkbox widget in the Publish 3MF dialog. Keep checkbox labels clickable, preserve toggle event propagation and disabled-row state, and use Windows-only double buffering on the tab page hosts to reduce flicker during page switches.
Also align the dialog's guide-link color with existing Orca dialogs.
* Fix Publish 3MF dialog styling
Use Orca's shared checkbox widget in the Publish 3MF dialog. Keep checkbox labels clickable, preserve toggle event propagation and disabled-row state, and use Windows-only double buffering on the tab page hosts to reduce flicker during page switches.
Also align the dialog's guide-link color with existing Orca dialogs.
* match font size and left margins
---------
Co-authored-by: yw4z <ywsyildiz@gmail.com>
* init
* drop usage of StaticGroup for ExtruderGroup
* completely remove StaticGroup from project
* fix alignment of "Not installed" text
* fix crash on linux while clicking edit button
* Fix background color on macOS
---------
Co-authored-by: Noisyfox <timemanager.rick@gmail.com>
libslic3r_version.h defines ORCA_CHECK_GCODE_PLACEHOLDERS from the CMake
option, 0 by default, and GCode.hpp then forces it to 1 whenever NDEBUG
is undefined. Clang reports the second definition under
-Wmacro-redefined, which is on by default, so since -Werror (#15660)
every clang Debug build fails at the libslic3r files that include
GCode.hpp. Release and RelWithDebInfo define NDEBUG and never compile
the override, which is why no CI configuration sees it. The override
dates from #3861 and has warned in every Debug build since.
Undefine the macro before overriding it. Debug builds get the same
value the redefinition already produced, so nothing else changes.
Follow-up to #15749, refs #15748.
fix(build): drop two unused lambda captures that break non-Release builds
Both handlers capture this and never use it. They sit inside
#if !BBL_RELEASE_TO_PUBLIC, which CMakeLists defines as $<CONFIG:Release>, so
the code compiles in Debug and RelWithDebInfo but not in Release. Clang warns
on an unused capture under -Wall, and since every warning became an error the
two captures fail any clang build that is not Release - an Xcode scheme on its
default Debug configuration, for instance. The CI matrix builds Release, where
the block does not exist, and GCC does not implement the warning at all, so
nothing in CI can see it.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Description
<!--
> Please provide a summary of the changes made in this PR. Include
details such as:
> * What issue does this PR address or fix?
> * What new features or enhancements does this PR introduce?
> * Are there any breaking changes or dependencies that need to be
considered?
-->
On windows, after installing and loading a plugin, if you try to
unsubscribe from a loaded plugin, and on refresh, it will be an orphaned
plugin.
# Screenshots/Recordings/Graphs
<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->
On unsubscribing from a loaded cloud plugin
<img width="1418" height="862" alt="image"
src="https://github.com/user-attachments/assets/9ba8353c-1645-4b21-86ff-2ed235f4d259"
/>
On plugin refresh
<img width="1418" height="862" alt="image"
src="https://github.com/user-attachments/assets/fd58c144-7970-4448-ba3b-e72e52980467"
/>
## Tests
<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->
<!--
> A guide for users on how to download the artifacts from this PR.
-->
[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)