mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 14:32:36 +00:00
snaporca-7ih's remaining half. Both flyout factories registered their verbs INSIDE the widget-building loop, so the ~40 retired tool buttons had to be constructed and then Hide()n: skipping construction would have deleted 42 offer verbs (26 fly:<family>#<row> + 16 Shift+keys) while their rows still rendered and did nothing when picked. Register first, build second. The addresses are pure data; the widget is one door onto them, not their owner. A family absent from kBarKeep now returns before any wxWindow is made. The keep-list stays a one-line data decision, not a structural one. And close the class of bug for good: the constructor now verifies, once, that every verb the atlas marks wired resolves to a real registration, logging each break and asserting in debug. Rows that render and do nothing have shipped three times (edit_feature and sk_move with action:null, then this) and are invisible from either side alone. Verified on the snaporca rig by walking the offer, not by reading the code — all four at-risk address kinds run with no widget behind them: fly:design_rect#2 drew an OBLIQUE rectangle (the third variant, not the family's first), key:S+E opened Extrude with its 10 mm gizmo, fly:material#4 opened Thicken. Hover hints, icons and nesting intact. This fork is code-identical here bar the two permitted DropDown divergences; it still owes a build of its own (snaporca-5pl). Two hints were wrong and are fixed: Cut said "Split the body with a plane", colliding with the Split verb one row away and pointing at a card for a value the canvas already offers as a draggable arrow; Split never said its plane comes from a picked face. Also, because it blocked the verification and will block the next one: gui-session.sh killed by full path while its own app_pid() matched by basename, so a differently-pathed instance survived, held the single-instance lock, and got reported as a healthy session — a Jul-30 binary nearly passed as this build. It now kills by basename and prints which binary is actually on screen. Traps 6 and 7 documented.
138 lines
7.2 KiB
Bash
Executable File
138 lines
7.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Bring the headless GUI up on a VNC-served X display, ready to drive or to attach Remmina to.
|
|
#
|
|
# Runs INSIDE the long-lived GUI container (see the header of scripts/docker-iter-build.sh for how
|
|
# that container is created). Idempotent: safe to re-run to recover a session whose app died.
|
|
#
|
|
# docker exec <container> /OrcaSlicer/scripts/gui-session.sh # launch + settle
|
|
# docker exec <container> /OrcaSlicer/scripts/gui-session.sh --status # report, change nothing
|
|
#
|
|
# WHY THIS EXISTS. Dismissing the first-run dialogs by computing the titlebar close box from
|
|
# `xdotool getwindowgeometry --shell` and clicking it went wrong whenever the dialog had already
|
|
# closed: the eval left the geometry variables stale or empty, the click landed at a garbage
|
|
# coordinate, and it repeatedly hit the Sketch button in the toolbar underneath, so the app came up
|
|
# in sketch mode with a stray Sketch feature. Three of those in one session.
|
|
#
|
|
# The titlebar click is nevertheless the RIGHT mechanism and is kept. `xdotool windowclose` looks
|
|
# cleaner but kills the app: it destroys the GdkWindow out from under the dialog and the process
|
|
# dies with "GdkWindow unexpectedly destroyed", three GLib-GObject criticals and a segfault
|
|
# (measured 2026-07-30). Escape does not close the Setup Wizard either. So the fix is not a
|
|
# different mechanism, it is refusing to click on geometry we have not validated.
|
|
set -euo pipefail
|
|
|
|
DISP="${DISP:-:11}"
|
|
GEOM="${GEOM:-1920x1080}"
|
|
BIN="${BIN:-/OrcaSlicer/build/src/Release/orca-slicer}"
|
|
# Packaging cannot bundle python (the deps python layer carries a doubled-DESTDIR RUNPATH), so the
|
|
# build-tree binary needs the deps libpython on the path. Packaging-only issue; the app runs fine.
|
|
LIBPY="/OrcaSlicer/deps/build/destdir/usr/local/libpython/lib"
|
|
LIBPY2="/OrcaSlicer/build/src/Release/python/lib"
|
|
LOG="${LOG:-/tmp/gui-session.log}"
|
|
|
|
export DISPLAY="$DISP" HOME=/root
|
|
export LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=llvmpipe
|
|
export LD_LIBRARY_PATH="$LIBPY:$LIBPY2:${LD_LIBRARY_PATH:-}"
|
|
mkdir -p /root/.config # startup dies in boost::filesystem::create_directory without this
|
|
|
|
# Skip zombies. This container accumulates <defunct> instances of the app across runs, and pgrep
|
|
# matches them, so the naive "first match" reported a dead pid as if the session were healthy.
|
|
app_pid() {
|
|
local p
|
|
for p in $(pgrep -f "$(basename "$BIN")" 2>/dev/null); do
|
|
[ "$(awk "{print \$3}" "/proc/$p/stat" 2>/dev/null)" = "Z" ] && continue
|
|
echo "$p"; return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
status() {
|
|
echo "display : $(pgrep -f "Xvfb $DISP" >/dev/null && echo up || echo DOWN)"
|
|
echo "wm : $(pgrep -x openbox >/dev/null && echo up || echo DOWN)"
|
|
if pgrep -x x11vnc >/dev/null; then echo "vnc : up on :5900"
|
|
elif ! command -v x11vnc >/dev/null; then echo "vnc : n/a (x11vnc not installed here)"
|
|
else echo "vnc : DOWN"; fi
|
|
local p; p="$(app_pid || true)"
|
|
echo "app : ${p:-DOWN}"
|
|
# WHICH binary is on screen, not just that something is. A pid alone cannot tell you whether
|
|
# you are looking at the build you just linked or one from last week, and that is precisely
|
|
# the question every rig verification is asking.
|
|
[ -n "${p:-}" ] && echo "binary : $(readlink -f "/proc/$p/exe" 2>/dev/null || echo unknown)"
|
|
[ -n "${p:-}" ] && echo "windows : $(xdotool search --name . getwindowname %@ 2>/dev/null | paste -sd'|' -)"
|
|
return 0
|
|
}
|
|
|
|
[ "${1:-}" = "--status" ] && { status; exit 0; }
|
|
|
|
# --- desktop: Xvfb, a window manager, and the VNC server ------------------------------------
|
|
# openbox is REQUIRED: without it xdotool windowactivate aborts with "windowmanager claims not to
|
|
# support _NET_ACTIVE_WINDOW" and dialogs never take focus.
|
|
pgrep -f "Xvfb $DISP" >/dev/null || { nohup Xvfb "$DISP" -screen 0 "${GEOM}x24" -nolisten tcp >/tmp/xvfb.log 2>&1 & sleep 3; }
|
|
pgrep -x openbox >/dev/null || { nohup openbox >/tmp/openbox.log 2>&1 & sleep 1; }
|
|
if ! pgrep -x x11vnc >/dev/null && command -v x11vnc >/dev/null; then
|
|
AUTH=()
|
|
[ -f /root/.vnc/passwd ] && AUTH=(-rfbauth /root/.vnc/passwd)
|
|
nohup x11vnc -display "$DISP" -rfbport 5900 "${AUTH[@]}" -forever -shared -noxdamage \
|
|
>/tmp/x11vnc.log 2>&1 &
|
|
sleep 2
|
|
fi
|
|
|
|
# --- app ------------------------------------------------------------------------------------
|
|
# Kill by BASENAME, not by "$BIN". The app enforces a single instance, so an older copy launched
|
|
# from a DIFFERENT path (the packaged build/package/bin/ one, say, when BIN points at the freshly
|
|
# linked build/src/Release/ one) survives a path-matched pkill, keeps the instance lock, and the
|
|
# new process exits seconds after loading fonts — leaving no error anywhere. app_pid() below has
|
|
# always matched by basename, so status then reported that stale process as a healthy session:
|
|
# the launch looked green while the window on screen was days old. Measured 2026-08-02, where it
|
|
# nearly passed a UI change against a Jul-30 binary. The killer and the reporter must agree on
|
|
# what counts as "the app".
|
|
pkill -9 -f "$(basename "$BIN")" 2>/dev/null || true
|
|
sleep 2
|
|
nohup "$BIN" >"$LOG" 2>&1 &
|
|
echo "launched $(basename "$BIN") pid $!"
|
|
|
|
# Wait for the main window rather than sleeping a fixed amount: cold starts vary a lot under
|
|
# software GL, and a fixed sleep either wastes time or races.
|
|
for _ in $(seq 1 40); do
|
|
xdotool search --name "Untitled" >/dev/null 2>&1 && break
|
|
sleep 1
|
|
done
|
|
|
|
# --- first-run dialogs ----------------------------------------------------------------------
|
|
# Click the titlebar close box, but only on geometry we have just read for a window that still
|
|
# exists, and only if the resulting point is inside the screen. Every variable is unset first so a
|
|
# failed read cannot leave the previous dialog's numbers behind — that is the whole bug.
|
|
screen_w="${GEOM%x*}"; screen_h="${GEOM#*x}"
|
|
close_dialog() {
|
|
local name="$1" id X Y WIDTH HEIGHT cx cy
|
|
id="$(xdotool search --name "$name" 2>/dev/null | head -1 || true)"
|
|
[ -z "$id" ] && return 1
|
|
unset X Y WIDTH HEIGHT
|
|
eval "$(xdotool getwindowgeometry --shell "$id" 2>/dev/null || true)"
|
|
# All four must be present and numeric: an empty or stale read is how the stray click happened.
|
|
for v in "${X:-}" "${Y:-}" "${WIDTH:-}" "${HEIGHT:-}"; do
|
|
[[ "$v" =~ ^-?[0-9]+$ ]] || { echo " $name: unreadable geometry, not clicking"; return 1; }
|
|
done
|
|
cx=$((X + WIDTH - 11)); cy=$((Y - 31)) # openbox decoration: close box above the frame
|
|
if [ "$cx" -lt 0 ] || [ "$cy" -lt 0 ] || [ "$cx" -ge "$screen_w" ] || [ "$cy" -ge "$screen_h" ]; then
|
|
echo " $name: close box at ${cx},${cy} is off-screen, not clicking"; return 1
|
|
fi
|
|
echo " $name: closing via titlebar at ${cx},${cy}"
|
|
xdotool mousemove "$cx" "$cy" click 1
|
|
sleep 2
|
|
return 0
|
|
}
|
|
for name in "Setup Wizard" "New version"; do
|
|
for _ in 1 2 3; do close_dialog "$name" || break; done
|
|
done
|
|
|
|
# --- main window ----------------------------------------------------------------------------
|
|
main="$(xdotool search --name "Untitled" 2>/dev/null | head -1 || true)"
|
|
if [ -n "$main" ]; then
|
|
xdotool windowmove "$main" 0 0 windowsize "$main" ${GEOM/x/ } 2>/dev/null || true
|
|
xdotool windowactivate "$main" 2>/dev/null || true
|
|
sleep 2
|
|
fi
|
|
|
|
echo "--- session ---"
|
|
status
|