Compare commits

..

4 Commits

Author SHA1 Message Date
0f2199dd82 docs: translate README to English 2026-07-20 20:27:00 +02:00
406681b94b fix(macos): stable local codesign to stop repeated permission prompts
Local arm64 builds are only ad-hoc/linker-signed, so macOS identifies the
app by its cdhash. That hash changes on every rebuild, invalidating TCC
(folder access) grants and Keychain ACLs, so macOS re-prompts for folder
access and the keychain password on every launch/redeploy.

Add scripts/macos_codesign_local.sh: creates a stable, machine-local,
self-signed code-signing identity in the user's login keychain (idempotent,
no admin password) and re-signs the app top-level. The Designated Requirement
becomes certificate-based ("... and certificate leaf = H\"...\"") instead of
cdhash, so it is identical across rebuilds and permissions granted once
persist. Document the workflow in BUILD_MAC_KX.md.

The certificate/private key are per-machine and never leave the login
keychain; only the script is shared.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 10:50:20 -07:00
f1948f429c fix(macos): prevent Bambu network plugin SIGABRT on app quit
libbambu_networking aborts inside its C++ static destructors during the
exit() teardown on macOS while its background threads are still alive.
Two quit paths reach that exit():

- App menu / Dock / logout: -[NSApplication terminate:] -> exit() bypasses
  wx OnExit()/~GUI_App. Override OSXOnWillTerminate() (the last wx hook
  before that exit()) to hard-exit via std::_Exit(0).
- Cmd+Q / File>Quit / red button: graceful close -> ~GUI_App(). A guarded
  std::_Exit(0) at the end of ~GUI_App(), after all cleanup, covers it.

macOS-only (#ifdef __APPLE__); Windows/Linux unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 16:37:24 -07:00
b3102b3f70 docs(macos): add English build guide for Apple Silicon (arm64) 2026-07-02 14:23:54 -07:00
5 changed files with 358 additions and 24 deletions

167
BUILD_MAC_KX.md Normal file
View File

@ -0,0 +1,167 @@
# BUILD_MAC_KX.md — building OrcaSlicer-KX on macOS (Apple Silicon)
> macOS build guide for this fork. Upstream (viewit) ships only Linux & Windows
> binaries; this fork exists to add the **macOS build** (see Releases for the `.app`).
> The upstream `CLAUDE.md`/`AGENTS.md` are left untouched.
**Verified recipe:** run as-is on 2026-07-02 on a **Mac M2 (arm64, macOS 25.x)**.
It produced a working `.app` that launches. Timings below are real, measured.
---
## What this is
Fork of **OrcaSlicer-KX** (`https://gitea.it-drui.de/viewit/OrcaSlicer-KX`), itself
a fork of SoftFever/OrcaSlicer with patches for the **Anycubic Kobra X**. Current
checkout: tag **`v2.4.1-kx1`**.
The fork's changes vs upstream are **branding (logo/icon/splash/about), redirecting
the updater to Gitea, and bugfix backports**. They **do not touch anything
platform-specific** (no CMake, no build scripts, no `src/platform`, no `*.mm`, no
`Info.plist`). The macOS icon (`resources/Icon.icns`,
`resources/images/OrcaSlicer.icns`) already ships regenerated with KX branding. So
building on Mac **is not a port, it's a recompile** using upstream's own
`build_release_macos.sh`.
Upstream only publishes **Linux (AppImage)** and **Windows (zip)** — there is no
official `.dmg`. This clone is for producing the Mac build yourself.
---
## Requirements (one time)
- **Xcode Command Line Tools** (NOT full Xcode.app — see the `-x` note).
Check: `xcode-select -p` prints a path.
- **Homebrew** with:
```bash
brew install cmake ninja automake texinfo libtool pkg-config
```
(`automake texinfo libtool` are needed by the deps stage; `cmake`+`ninja` by the build.)
### Why `-x` (Ninja) and not the default
`build_release_macos.sh` defaults to the **Xcode** CMake generator, which requires
**full Xcode.app (~15 GB from the App Store)**. The **`-x`** flag switches to
**Ninja**, which works with just the Command Line Tools. Always use it here.
---
## Build (arm64 / Apple Silicon)
Two stages, run **from the repo root** (`~/Developer/OrcaSlicer-KX`):
```bash
# Stage 1 — dependencies (Boost, wxWidgets, TBB, OCCT, OpenCV, ...)
# 202 static libs → deps/build/arm64/OrcaSlicer_dep
./build_release_macos.sh -d -x -a arm64 # measured: ~14.5 min
# Stage 2 — the slicer itself → build/arm64/OrcaSlicer/OrcaSlicer.app
./build_release_macos.sh -s -x -a arm64 # measured: ~18 min
```
Flags: `-d` deps only · `-s` slicer only · `-x` Ninja · `-a arch`. On an M2 with
16 GB, `-1` (single job) is not needed; if a Mac with less RAM OOMs while linking,
add `-1`.
**Output:** `build/arm64/OrcaSlicer/OrcaSlicer.app` (native arm64, ~367 MB,
version `2.4.1-kx1`).
### Normal output that is NOT an error
- "CMake Deprecation" warnings and `Performing Test ... - Failed` lines in the deps
stage are **compiler feature detection**, not failures. The build still finishes
with exit 0 and `[202/202] Completed`.
- The signature is **ad-hoc / linker-signed**. Strict `codesign -v` and `spctl`
complain ("code has no resources but signature indicates they must be present").
This is **harmless** for launching: a local build is **not quarantined**, so it
opens with a double-click, no Gatekeeper block. It does, however, make macOS
re-ask for folder and keychain permissions on every rebuild — see
*Stop the repeated folder / keychain permission prompts* below to fix that.
---
## Install into /Applications
There is usually an official `/Applications/OrcaSlicer.app`. To avoid overwriting
it, copy under a different name:
```bash
ditto build/arm64/OrcaSlicer/OrcaSlicer.app /Applications/OrcaSlicer-KX.app
./scripts/macos_codesign_local.sh # stable local signature — stops repeated permission prompts (see below)
open /Applications/OrcaSlicer-KX.app # verify it boots
```
(`ditto` preserves the bundle + signature better than `cp -R`.)
---
## Stop the repeated folder / keychain permission prompts (recommended)
Because a local build is only **ad-hoc / linker-signed**, macOS identifies the app
by its content hash (`cdhash`). That hash changes on every rebuild, so macOS treats
each build as a brand-new app and re-asks for **folder access (TCC)** and the
**keychain password** every time — the grants never stick.
Fix it by signing the app with a **stable, machine-local, self-signed** identity, so
its Designated Requirement becomes certificate-based (identical across rebuilds):
```bash
./scripts/macos_codesign_local.sh # run AFTER copying the app to /Applications
```
On first run the script creates the identity `OrcaSlicer Local Codesign` in your
login keychain (no admin password needed) and re-signs the installed app + local
build output. Re-run it after every build/redeploy — it reuses the same identity.
The Designated Requirement changes from `cdhash H"…"` to
`identifier "com.orcaslicer.OrcaSlicer" and certificate leaf = H"…"`, which is stable.
On the **next launch only**, allow the folder prompts and click **"Always Allow"**
on the keychain prompt once; macOS then remembers them across future rebuilds.
**Per-machine — share the script, never the cert.** The certificate and its private
key are generated locally and live only in your login keychain; they are never
written to the repo. Each developer runs the script once on their own Mac to create
their own local identity. Commit and share the *script*, not the certificate.
---
## Update to a new tag (e.g. kx2)
```bash
git fetch --tags
git checkout <new-tag>
./build_release_macos.sh -s -x -a arm64 # deps are cached ⇒ only the slicer recompiles (~18 min)
```
If the new tag **changes a dependency**, run the `-d` stage again first.
---
## Publishing the macOS release
The distributable is a zip of the `.app` (matching how upstream ships Windows):
```bash
ditto -c -k --keepParent build/arm64/OrcaSlicer/OrcaSlicer.app \
OrcaSlicer-KX-v2.4.1-kx1-macOS-arm64.zip
```
Attach it to a Gitea Release on this fork. **Gotcha:** a freshly created Gitea fork
may have the **Releases unit disabled** (`has_releases:false`), which makes asset
downloads return 404 even though the upload succeeded. Enable it once:
`PATCH /api/v1/repos/<owner>/<repo>` with `{"has_releases": true}`.
---
## Gotchas
- **Updater points to Gitea, not upstream** — commit `7ed0173` hardcodes
`https://gitea.it-drui.de/api/v1/repos/viewit/OrcaSlicer-KX/releases/latest`.
There is no macOS artifact there, so "check for updates" won't find a macOS build.
Harmless; updating = recompiling (above).
- **Universal / Intel**: `-a x86_64` (Intel), `-a universal` (fat binary; requires
prior arm64 and x86_64 builds). On Apple Silicon, `arm64` is the right choice.
- **Not notarized** — distributing to third parties would need an Apple Developer
account ($99/yr) + notarization. Not needed for local use.

View File

@ -1,8 +1,8 @@
# OrcaSlicer-KX
**OrcaSlicer mit KX-Bridge-Patches für den Anycubic Kobra X**
**OrcaSlicer with KX-Bridge patches for the Anycubic Kobra X**
Fertige Binaries von [OrcaSlicer](https://github.com/SoftFever/OrcaSlicer) mit integrierten KX-spezifischen Erweiterungen — empfohlen für den Einsatz mit [KX-Bridge](https://gitea.it-drui.de/viewit/KX-Bridge-Release).
Ready-to-use binaries of [OrcaSlicer](https://github.com/SoftFever/OrcaSlicer) with integrated KX-specific extensions — recommended for use with [KX-Bridge](https://gitea.it-drui.de/viewit/KX-Bridge-Release).
[![Download](https://img.shields.io/badge/Download-Releases-2EA043?style=for-the-badge&logo=gitea&logoColor=white)](https://gitea.it-drui.de/viewit/OrcaSlicer-KX/releases)
@ -10,49 +10,50 @@ Fertige Binaries von [OrcaSlicer](https://github.com/SoftFever/OrcaSlicer) mit i
## Download
Aktuelle Version: **2.4.1-kx1** (Basis: OrcaSlicer 2.4.1 stable)
Current version: **2.4.1-kx1** (base: OrcaSlicer 2.4.1 stable)
| Plattform | Datei | Hinweis |
| Platform | File | Note |
|-----------|-------|---------|
| Linux x86_64 | `OrcaSlicer-KX-Linux-x86_64.AppImage` | `chmod +x` setzen, dann starten |
| Windows x86_64 | `OrcaSlicer-KX-Windows-x86_64.zip` | Entpacken, `orca-slicer.exe` starten |
| Linux x86_64 | `OrcaSlicer-KX-Linux-x86_64.AppImage` | `chmod +x`, then run |
| Windows x86_64 | `OrcaSlicer-KX-Windows-x86_64.zip` | Unzip, run `orca-slicer.exe` |
| macOS arm64 (Apple Silicon) | `OrcaSlicer-KX-<version>-macOS-arm64.zip` | on [this fork's Releases](https://gitea.it-drui.de/walterioo/OrcaSlicer-KX/releases) — not notarized, see the release notes |
macOS wird nicht bereitgestellt — bitte aus dem Upstream selbst bauen.
The macOS build (Apple Silicon / arm64) is provided on this fork's [Releases](https://gitea.it-drui.de/walterioo/OrcaSlicer-KX/releases); build details are in `BUILD_MAC_KX.md`. Intel Macs are not shipped — rebuild with `-a x86_64`.
---
## Enthaltene Patches
## Included patches
- **Moonraker-Bridge:** mehrstufiges Filament-Matching (Name, Sub-Brand, Vendor-Fallback) mit Vendor-Filter-Skip
- **AMS-Leerslot-Fix:** leere AMS-Slots werden korrekt grau dargestellt (kein Absturz / falsches Filament)
- **Filament-ID:** eindeutige `filament_id` für abgeleitete User-Presets (korrekte Bridge-Synchronisation)
- **Kobra X Profile:** Drucker- und G-Code-Profile für den Anycubic Kobra X
- **Moonraker bridge:** multi-stage filament matching (name, sub-brand, vendor fallback) with vendor-filter skip
- **AMS empty-slot fix:** empty AMS slots are shown correctly in grey (no crash / wrong filament)
- **Filament ID:** unique `filament_id` for derived user presets (correct bridge synchronization)
- **Kobra X profiles:** printer and G-code profiles for the Anycubic Kobra X
---
## Filament-Presets & KX-Bridge
## Filament presets & KX-Bridge
📄 [Anleitung: eigene Filament-Presets erstellen, prüfen und importieren](https://gitea.it-drui.de/viewit/KX-Bridge-Release/src/branch/master/docs/filament-preset-bridge-guide.md)
📄 [Guide: create, validate and import your own filament presets](https://gitea.it-drui.de/viewit/KX-Bridge-Release/src/branch/master/docs/filament-preset-bridge-guide.md)
---
## Branch-Struktur
## Branch structure
| Branch | Version | Rolle |
| Branch | Version | Role |
|--------|---------|-------|
| `stable` / `kx-v2.4` | 2.4.1-kx1 | **aktuell stabil** |
| `kx-v2.3` | 2.3.2-kx4 | eingefroren (Altstand) |
| `stable` / `kx-v2.4` | 2.4.1-kx1 | **current stable** |
| `kx-v2.3` | 2.3.2-kx4 | frozen (legacy) |
---
## Warum ein eigenes Repo?
## Why a separate repo?
KX-Bridge selbst liegt unter [viewit/KX-Bridge-Release](https://gitea.it-drui.de/viewit/KX-Bridge-Release).
OrcaSlicer steht unter AGPL-3.0 — ein separates Repo hält Lizenzen, Quellherkunft und Update-Zyklen sauber getrennt.
KX-Bridge itself lives at [viewit/KX-Bridge-Release](https://gitea.it-drui.de/viewit/KX-Bridge-Release).
OrcaSlicer is licensed under AGPL-3.0 — a separate repo keeps licenses, source provenance and update cycles cleanly separated.
---
## Lizenz & Quelle
## License & source
Diese Builds sind Ableitungen von [OrcaSlicer](https://github.com/SoftFever/OrcaSlicer), lizenziert unter **GNU AGPL-3.0**.
Der KX-Patch-Quellcode liegt auf Branch [`kx-v2.4`](https://gitea.it-drui.de/viewit/OrcaSlicer-KX/src/branch/kx-v2.4) dieses Repos.
These builds are derivatives of [OrcaSlicer](https://github.com/SoftFever/OrcaSlicer), licensed under **GNU AGPL-3.0**.
The KX patch source code lives on branch [`kx-v2.4`](https://gitea.it-drui.de/viewit/OrcaSlicer-KX/src/branch/kx-v2.4) of this repo.

128
scripts/macos_codesign_local.sh Executable file
View File

@ -0,0 +1,128 @@
#!/usr/bin/env bash
#
# macos_codesign_local.sh — stop macOS from re-prompting for folder (TCC) and
# Keychain access on every launch/rebuild of a locally-built OrcaSlicer.
#
# WHY THIS IS NEEDED
# On Apple Silicon the linker automatically applies an *ad-hoc* code signature
# to every binary. macOS then identifies the app by the exact hash (cdhash) of
# its contents, so its "Designated Requirement" looks like:
# designated => cdhash H"...."
# macOS binds TCC (folder access) grants and Keychain ACLs to that requirement.
# Because the cdhash changes on every rebuild / redeploy, macOS treats each new
# build as a brand-new unknown app and asks again for folder access and the
# keychain password. Permissions never persist.
#
# WHAT THIS DOES
# Signs the app with a STABLE, self-signed, machine-local code-signing
# certificate. The Designated Requirement then becomes certificate-based:
# designated => identifier "com.orcaslicer.OrcaSlicer" and certificate leaf = H"...."
# which is identical across every rebuild. Grant folder access + click
# "Always Allow" in the keychain ONCE and they persist forever after.
#
# SHARING / PRIVACY
# This script is safe to commit and share (e.g. via Gitea). It is per-machine:
# the certificate and its PRIVATE KEY are generated locally and live only in
# YOUR login keychain — they are never written to the repo. Each developer runs
# this once on their own Mac to create their own local identity. Do NOT export
# or share the certificate/private key; share the script, not the cert.
#
# USAGE
# scripts/macos_codesign_local.sh [APP_BUNDLE ...]
# With no arguments it signs the installed app and the local build output if
# they exist. Run it as the LAST step, AFTER copying the app to /Applications
# (any change to the bundle after signing invalidates the signature).
#
set -euo pipefail
CERT_CN="OrcaSlicer Local Codesign"
KEYCHAIN="$HOME/Library/Keychains/login.keychain-db"
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ENTITLEMENTS="$REPO_ROOT/src/dev-utils/platform/osx/entitlements.plist"
BUNDLE_ID="com.orcaslicer.OrcaSlicer"
# ---------------------------------------------------------------------------
# 1) Ensure a stable local code-signing identity exists (create it if missing).
# ---------------------------------------------------------------------------
if security find-identity -p codesigning 2>/dev/null | grep -qF "$CERT_CN"; then
echo "✓ Local signing identity '$CERT_CN' already present — reusing it."
else
echo "→ Creating local self-signed code-signing identity '$CERT_CN' ..."
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
cat > "$TMP/req.cnf" <<EOF
[req]
distinguished_name = dn
x509_extensions = v3
prompt = no
[dn]
CN = $CERT_CN
[v3]
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature
extendedKeyUsage = critical,codeSigning
EOF
openssl req -x509 -newkey rsa:2048 -sha256 -days 3650 -nodes \
-keyout "$TMP/key.pem" -out "$TMP/cert.pem" -config "$TMP/req.cnf" >/dev/null 2>&1
# Import key + cert separately (avoids PKCS#12 algorithm mismatches between
# OpenSSL 3.x and the macOS `security` importer). -A lets codesign use the key
# without a per-signature keychain prompt.
security import "$TMP/key.pem" -k "$KEYCHAIN" -T /usr/bin/codesign -A >/dev/null
security import "$TMP/cert.pem" -k "$KEYCHAIN" -T /usr/bin/codesign -A >/dev/null
echo "✓ Identity created (self-signed, local only, not exported)."
# Note: the certificate is intentionally left untrusted (CSSMERR_TP_NOT_TRUSTED).
# codesign can still sign with it, and that is all we need for a stable
# Designated Requirement — no admin password / trust settings required.
fi
# ---------------------------------------------------------------------------
# 2) Collect target bundles.
# ---------------------------------------------------------------------------
TARGETS=("$@")
if [ ${#TARGETS[@]} -eq 0 ]; then
for candidate in \
"/Applications/OrcaSlicer-KX.app" \
"/Applications/OrcaSlicer.app" \
"$REPO_ROOT/build/arm64/OrcaSlicer/OrcaSlicer.app"; do
[ -d "$candidate" ] && TARGETS+=("$candidate")
done
fi
if [ ${#TARGETS[@]} -eq 0 ]; then
echo "!! No app bundle found to sign. Pass one explicitly:"
echo " $0 /path/to/OrcaSlicer.app"
exit 1
fi
# ---------------------------------------------------------------------------
# 3) Sign each target with the stable identity.
# Top-level only (no --deep): nested/third-party dylibs keep their own
# signatures, which is fine because the app is not hardened-runtime.
# ---------------------------------------------------------------------------
ENT_ARG=()
[ -f "$ENTITLEMENTS" ] && ENT_ARG=(--entitlements "$ENTITLEMENTS")
for app in "${TARGETS[@]}"; do
echo "→ Signing: $app"
codesign --force --sign "$CERT_CN" --identifier "$BUNDLE_ID" "${ENT_ARG[@]}" "$app"
dr="$(codesign -d -r- "$app" 2>&1 | grep -i designated || true)"
echo " $dr"
if echo "$dr" | grep -q "certificate leaf"; then
echo " ✓ Certificate-based requirement — permissions will now persist across rebuilds."
else
echo " !! Unexpected: requirement is not certificate-based."
fi
done
cat <<'EOF'
Done. One last time, on the NEXT launch:
• allow the folder-access prompts, and
• click "Always Allow" on any keychain password prompt.
After that macOS will remember them across future rebuilds. Re-run this script
after each build/redeploy (it reuses the same identity).
EOF

View File

@ -2215,6 +2215,16 @@ GUI_App::~GUI_App()
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": exit");
#ifdef __APPLE__
// Belt-and-suspenders for the graceful quit path (Cmd+Q / File>Quit / red button),
// which reaches ~GUI_App() instead of OSXOnWillTerminate(). All app cleanup has run
// by this point (config saved, network agent deleted, Bambu plugin shut down), so we
// hard-exit to ensure the C runtime's exit() never runs libbambu_networking's static
// destructors — they abort during teardown on macOS. This mirrors the app-menu/Dock/
// logout path handled in GUI_App::OSXOnWillTerminate().
std::_Exit(0);
#endif /* __APPLE__ */
}
bool GUI_App::is_blocking_printing(MachineObject *obj_)
@ -2593,6 +2603,30 @@ int GUI_App::OnExit()
return wxApp::OnExit();
}
#ifdef __APPLE__
// On macOS, quitting via the app menu / Dock / logout (or a system Quit AppleEvent)
// routes through Cocoa's -[NSApplication terminate:], which calls exit() directly
// from inside the native run loop. That bypasses wx's normal shutdown
// (OnExit()/~GUI_App never run) and, during __cxa_finalize_ranges, runs the C++
// static/atexit destructors of the closed-source Bambu networking plugin
// (libbambu_networking) while its background threads are still alive. One of those
// destructors calls std::terminate() -> abort(), producing a SIGABRT crash report
// *after* the user has already quit (purely a teardown crash, no data at risk).
//
// OSXOnWillTerminate() is the last wx hook invoked before that exit(), and only
// runs once termination has already been approved (OSXOnShouldTerminate() returned
// no veto). We let the base class fire wxEVT_END_SESSION for any listeners, then
// hard-exit with _Exit() so __cxa_finalize_ranges — and thus the plugin's crashing
// destructors — never run. Nothing is persisted on this path today either, so this
// is behavior-preserving apart from removing the cosmetic crash.
void GUI_App::OSXOnWillTerminate()
{
BOOST_LOG_TRIVIAL(info) << "GUI_App::OSXOnWillTerminate: hard-exit to avoid Bambu network plugin teardown crash";
wxApp::OSXOnWillTerminate();
std::_Exit(0);
}
#endif /* __APPLE__ */
class wxBoostLog : public wxLog
{
void DoLogText(const wxString &msg) override {

View File

@ -626,6 +626,10 @@ public:
// wxWidgets override to get an event on open files.
void MacOpenFiles(const wxArrayString &fileNames) override;
void MacOpenURL(const wxString& url) override;
// Last wx hook before Cocoa's exit() on the app-menu/Dock/logout quit path.
// Hard-exits to skip the crashing static-destructor teardown of the Bambu
// networking plugin. See implementation in GUI_App.cpp.
void OSXOnWillTerminate() override;
#endif /* __APPLE */
Sidebar& sidebar();