256 lines
8.2 KiB
Bash
Executable File
256 lines
8.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -u
|
|
|
|
readonly REPO="/home/Rbanh/Documents/Bevy"
|
|
readonly TARGET_DIR="$REPO/target"
|
|
readonly EDITOR_BINARY="$TARGET_DIR/debug/editor"
|
|
readonly LAUNCHER_BINARY="$TARGET_DIR/debug/project_launcher"
|
|
readonly STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/blacksite-editor"
|
|
readonly LOG_FILE="$STATE_DIR/launch.log"
|
|
readonly STATUS_FILE="$STATE_DIR/splash.status"
|
|
readonly SPLASH="$REPO/scripts/editor/splash.py"
|
|
readonly BUILD_PROGRESS="$REPO/scripts/editor/build_progress.py"
|
|
readonly BUILD_STORAGE="$REPO/scripts/codex/build_storage.py"
|
|
|
|
build_only=false
|
|
project_browser=false
|
|
gpu_validation=0
|
|
sdr_fallback=false
|
|
splash_pid=""
|
|
keep_splash=false
|
|
|
|
case "${1:-}" in
|
|
--build-only) build_only=true ;;
|
|
--launcher) project_browser=true ;;
|
|
--gpu-validation) gpu_validation=1 ;;
|
|
--sdr) sdr_fallback=true ;;
|
|
"") ;;
|
|
*)
|
|
printf 'Unknown option: %s\n' "$1" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
|
|
if $project_browser; then
|
|
binary="$LAUNCHER_BINARY"
|
|
process_name="project_launcher"
|
|
service_description="Blacksite Project Browser"
|
|
else
|
|
binary="$EDITOR_BINARY"
|
|
process_name="editor"
|
|
service_description="Blacksite Editor"
|
|
fi
|
|
|
|
if ! $build_only && [[ "${BLACKSITE_EDITOR_SERVICE:-0}" != 1 ]]; then
|
|
service_environment=(--setenv=BLACKSITE_EDITOR_SERVICE=1 --setenv=PATH)
|
|
for variable in \
|
|
WAYLAND_DISPLAY DISPLAY XAUTHORITY XDG_RUNTIME_DIR \
|
|
HYPRLAND_INSTANCE_SIGNATURE DBUS_SESSION_BUS_ADDRESS \
|
|
XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE \
|
|
XDG_DATA_HOME XDG_CONFIG_HOME XDG_STATE_HOME GDK_BACKEND; do
|
|
if [[ -v "$variable" ]]; then
|
|
service_environment+=(--setenv="$variable")
|
|
fi
|
|
done
|
|
launcher_arguments=()
|
|
[[ $# -gt 0 ]] && launcher_arguments+=("$1")
|
|
exec systemd-run --user --collect --quiet --service-type=exec \
|
|
--description="$service_description" \
|
|
"${service_environment[@]}" \
|
|
"$0" "${launcher_arguments[@]}"
|
|
fi
|
|
|
|
write_status() {
|
|
local phase="$1"
|
|
local detail="$2"
|
|
local progress="$3"
|
|
local state="${4:-running}"
|
|
local crate_count="${5:-}"
|
|
printf '%s\n%s\n%s\n%s\n%s\n' \
|
|
"$phase" "$detail" "$progress" "$state" "$crate_count" >"$STATUS_FILE.tmp"
|
|
mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
}
|
|
|
|
close_splash() {
|
|
if [[ -n "$splash_pid" ]] && ! $keep_splash; then
|
|
write_status "Editor ready" "Opening workspace" 100 close
|
|
fi
|
|
}
|
|
trap close_splash EXIT
|
|
|
|
find_existing() {
|
|
local pid executable
|
|
while read -r pid; do
|
|
[[ -n "$pid" ]] || continue
|
|
executable="$(readlink -f "/proc/$pid/exe" 2>/dev/null || true)"
|
|
if [[ "$executable" == "$binary" ]]; then
|
|
printf '%s\n' "$pid"
|
|
return 0
|
|
fi
|
|
done < <(pgrep -x "$process_name" 2>/dev/null || true)
|
|
return 1
|
|
}
|
|
|
|
focus_process() {
|
|
local pid="$1"
|
|
if command -v hyprctl >/dev/null 2>&1; then
|
|
hyprctl dispatch focuswindow "pid:$pid" >/dev/null 2>&1 || true
|
|
fi
|
|
}
|
|
|
|
running_binary_is_current() {
|
|
local pid="$1"
|
|
local running_identity current_identity
|
|
running_identity="$(stat -Lc '%d:%i' "/proc/$pid/exe" 2>/dev/null || true)"
|
|
current_identity="$(stat -Lc '%d:%i' "$binary" 2>/dev/null || true)"
|
|
[[ -n "$running_identity" && "$running_identity" == "$current_identity" ]]
|
|
}
|
|
|
|
build_current_editor() {
|
|
write_status "Checking build storage" "Validating the managed development lane" 6
|
|
if ! python3 "$BUILD_STORAGE" enforce --phase pre >>"$LOG_FILE" 2>&1; then
|
|
write_status "Build storage limit reached" \
|
|
"Open the launch log for the checked storage report" 6 error
|
|
return 1
|
|
fi
|
|
|
|
python3 "$BUILD_PROGRESS" "$STATUS_FILE" "$LOG_FILE"
|
|
local build_result=$?
|
|
|
|
if ! python3 "$BUILD_STORAGE" enforce --phase post >>"$LOG_FILE" 2>&1; then
|
|
printf '%s\n' \
|
|
"warning: build-storage postflight failed; see the report above" \
|
|
>>"$LOG_FILE"
|
|
fi
|
|
return "$build_result"
|
|
}
|
|
|
|
mkdir -p "$STATE_DIR"
|
|
exec 9>"$STATE_DIR/build.lock"
|
|
if ! flock -n 9; then
|
|
exit 0
|
|
fi
|
|
|
|
: >"$LOG_FILE"
|
|
write_status "Preparing workspace" "Reading project configuration" 5
|
|
|
|
if ! $build_only; then
|
|
GSK_RENDERER=gl python3 "$SPLASH" "$STATUS_FILE" "$LOG_FILE" &
|
|
splash_pid=$!
|
|
fi
|
|
|
|
if ! cd "$REPO"; then
|
|
write_status "Project unavailable" "$REPO could not be opened" 100 error
|
|
keep_splash=true
|
|
exit 1
|
|
fi
|
|
|
|
# Capture the process before Cargo may atomically replace the on-disk executable. A launcher click
|
|
# always checks/builds source updates first; it never silently focuses a stale process and makes the
|
|
# user believe the running editor already contains the rebuilt UI.
|
|
existing_pid=""
|
|
if ! $build_only; then
|
|
existing_pid="$(find_existing || true)"
|
|
fi
|
|
|
|
if ! build_current_editor; then
|
|
if $build_only; then
|
|
tail -n 120 "$LOG_FILE" >&2
|
|
else
|
|
keep_splash=true
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -n "$existing_pid" ]] && kill -0 "$existing_pid" 2>/dev/null; then
|
|
focus_process "$existing_pid"
|
|
if running_binary_is_current "$existing_pid"; then
|
|
write_status "Editor already open" "Focused the current development build" 100 close
|
|
else
|
|
write_status "Update ready" "Close and reopen the editor to activate it" 100 close
|
|
if command -v notify-send >/dev/null 2>&1; then
|
|
notify-send --app-name="Blacksite Editor" \
|
|
"Blacksite update ready" \
|
|
"Close the running editor normally, then reopen it to activate the rebuilt UI." \
|
|
>/dev/null 2>&1 || true
|
|
fi
|
|
fi
|
|
# The status above already asked the splash process to close. Prevent the EXIT trap from
|
|
# replacing the meaningful "already open" or "update ready" result with a startup message.
|
|
splash_pid=""
|
|
exit 0
|
|
fi
|
|
|
|
if $build_only; then
|
|
printf 'Blacksite editor and project browser are current.\n'
|
|
exit 0
|
|
fi
|
|
|
|
if [[ ! -x "$binary" ]]; then
|
|
write_status "Editor build unavailable" \
|
|
"The managed build completed without the requested binary" 100 error
|
|
keep_splash=true
|
|
exit 1
|
|
fi
|
|
|
|
export RUST_BACKTRACE=1
|
|
export WGPU_VALIDATION="$gpu_validation"
|
|
if $sdr_fallback; then
|
|
export BEVY_FPS_HDR=0
|
|
fi
|
|
|
|
export LD_LIBRARY_PATH="$TARGET_DIR/debug/deps${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
|
write_status "Starting editor" "Opening the current development build" 82
|
|
|
|
watch_editor_window() {
|
|
local editor_pid="$1"
|
|
trap - EXIT
|
|
if command -v hyprctl >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
|
|
for ((attempt = 0; attempt < 300; attempt++)); do
|
|
if ! kill -0 "$editor_pid" 2>/dev/null; then
|
|
write_status "Editor stopped during startup" \
|
|
"Open the launch log for runtime details" 100 error
|
|
return
|
|
fi
|
|
if hyprctl clients -j 2>/dev/null | jq -e --argjson pid "$editor_pid" \
|
|
'any(.[]; .pid == $pid and .mapped == true)' >/dev/null; then
|
|
write_status "Editor ready" "Opening workspace" 100
|
|
sleep 0.4
|
|
write_status "Editor ready" "Opening workspace" 100 close
|
|
return
|
|
fi
|
|
if ((attempt == 10)); then
|
|
write_status "Initializing renderer" "Connecting to the graphics device" 88
|
|
elif ((attempt == 35)); then
|
|
write_status "Loading editor scene" "Preparing the workspace" 94
|
|
fi
|
|
sleep 0.1
|
|
done
|
|
else
|
|
write_status "Loading editor scene" "Preparing the workspace" 94
|
|
sleep 5
|
|
if kill -0 "$editor_pid" 2>/dev/null; then
|
|
write_status "Editor ready" "Opening workspace" 100
|
|
sleep 0.4
|
|
write_status "Editor ready" "Opening workspace" 100 close
|
|
return
|
|
fi
|
|
fi
|
|
write_status "Editor is taking longer than expected" \
|
|
"The process is still running; check the launch log" 100 error
|
|
}
|
|
|
|
# The lock protects the managed build and startup handoff, not the editor runtime. Leaving this
|
|
# descriptor inherited across `exec` prevents later build-only updates for as long as the editor is
|
|
# open.
|
|
flock -u 9
|
|
exec 9>&-
|
|
watch_editor_window "$$" &
|
|
exec "$binary" >>"$LOG_FILE" 2>&1
|
|
|
|
write_status "Editor could not be started" \
|
|
"Open the launch log for runtime details" 100 error
|
|
keep_splash=true
|
|
exit 1
|