192 lines
5.9 KiB
Bash
Executable File
192 lines
5.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Start a disposable nested X11 server plus Lime's safe live-development
|
|
# runtime. Run this from the normal graphical session, never from inside the
|
|
# Xephyr session it creates.
|
|
#
|
|
# Usage:
|
|
# run-xephyr-dev [--config PATH | --no-config] [--display :99]
|
|
# [--screen 1600x1000] [--repl-port 4005]
|
|
|
|
set -euo pipefail
|
|
|
|
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
repo_dir="$(cd -- "$script_dir/.." && pwd)"
|
|
config_home="${XDG_CONFIG_HOME:-${HOME}/.config}"
|
|
default_config_file="$config_home/lime/config.lisp"
|
|
display_name=":99"
|
|
screen_size="1600x1000"
|
|
repl_port="4005"
|
|
config_file=""
|
|
session_mode="default"
|
|
session_file=""
|
|
|
|
if [[ -f "$default_config_file" ]]; then
|
|
config_file="$default_config_file"
|
|
fi
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: $(basename "$0") [options]
|
|
|
|
Start Lime in a disposable Xephyr session from the current desktop.
|
|
|
|
-c, --config PATH Load this Lime configuration file.
|
|
--no-config Use Lime's starter bindings instead of a config file.
|
|
-d, --display NAME Nested display name (default: :99).
|
|
-s, --screen SIZE Xephyr size (default: 1600x1000).
|
|
-p, --repl-port N Loopback Slynk port (default: 4005).
|
|
--session-file PATH Use a specific saved desk (separate from configuration).
|
|
--no-session Disable session restore/autosave for a disposable test.
|
|
-h, --help Show this help.
|
|
|
|
Without an explicit config choice, the runner loads
|
|
$default_config_file
|
|
when it exists; otherwise it uses the starter bindings.
|
|
Session data defaults to Lime's XDG state directory, separated by DISPLAY.
|
|
EOF
|
|
}
|
|
|
|
while (($#)); do
|
|
case "$1" in
|
|
-c|--config)
|
|
[[ $# -ge 2 ]] || { printf '%s needs a path.\n' "$1" >&2; exit 2; }
|
|
config_file="$2"
|
|
shift 2
|
|
;;
|
|
--no-config)
|
|
config_file=""
|
|
shift
|
|
;;
|
|
--session-file)
|
|
[[ $# -ge 2 ]] || { printf '%s needs a path.\n' "$1" >&2; exit 2; }
|
|
session_mode="file"
|
|
session_file="$2"
|
|
[[ "$session_file" = /* ]] || session_file="$PWD/$session_file"
|
|
shift 2
|
|
;;
|
|
--no-session)
|
|
session_mode="disabled"
|
|
shift
|
|
;;
|
|
-d|--display)
|
|
[[ $# -ge 2 ]] || { printf '%s needs a display name.\n' "$1" >&2; exit 2; }
|
|
display_name="$2"
|
|
shift 2
|
|
;;
|
|
-s|--screen)
|
|
[[ $# -ge 2 ]] || { printf '%s needs a size.\n' "$1" >&2; exit 2; }
|
|
screen_size="$2"
|
|
shift 2
|
|
;;
|
|
-p|--repl-port)
|
|
[[ $# -ge 2 ]] || { printf '%s needs a port number.\n' "$1" >&2; exit 2; }
|
|
repl_port="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
printf 'Unknown option: %s\n\n' "$1" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if ! [[ "$display_name" =~ ^:[0-9]+$ ]]; then
|
|
printf '%s\n' "--display must look like :99, got $display_name" >&2
|
|
exit 2
|
|
fi
|
|
|
|
display_number="${display_name#:}"
|
|
socket_path="/tmp/.X11-unix/X${display_number}"
|
|
if [[ -S "$socket_path" ]]; then
|
|
printf 'Display %s is already in use (%s). Choose another with --display.\n' \
|
|
"$display_name" "$socket_path" >&2
|
|
exit 2
|
|
fi
|
|
|
|
if ! [[ "$repl_port" =~ ^[1-9][0-9]*$ ]] || ((repl_port > 65535)); then
|
|
printf 'Slynk port must be an integer from 1 through 65535, got %s\n' "$repl_port" >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ -n "$config_file" ]]; then
|
|
if [[ ! -f "$config_file" ]]; then
|
|
printf 'Lime config does not exist: %s\n' "$config_file" >&2
|
|
exit 2
|
|
fi
|
|
# Resolve before changing into the repository so an explicit relative path
|
|
# still means the path relative to the shell that launched this runner.
|
|
config_file="$(cd -- "$(dirname -- "$config_file")" && pwd)/$(basename -- "$config_file")"
|
|
fi
|
|
|
|
command -v Xephyr >/dev/null || {
|
|
printf 'Xephyr is required but was not found in PATH.\n' >&2
|
|
exit 127
|
|
}
|
|
command -v sbcl >/dev/null || {
|
|
printf 'SBCL is required but was not found in PATH.\n' >&2
|
|
exit 127
|
|
}
|
|
|
|
cleanup() {
|
|
if kill -0 "$xephyr_pid" 2>/dev/null; then
|
|
kill -TERM "$xephyr_pid" 2>/dev/null || true
|
|
wait "$xephyr_pid" 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
Xephyr "$display_name" -ac -screen "$screen_size" -br -nolisten tcp &
|
|
xephyr_pid=$!
|
|
trap cleanup EXIT INT TERM
|
|
|
|
for attempt in {1..50}; do
|
|
if [[ -S "$socket_path" ]]; then
|
|
break
|
|
fi
|
|
if ! kill -0 "$xephyr_pid" 2>/dev/null; then
|
|
wait "$xephyr_pid"
|
|
fi
|
|
sleep 0.1
|
|
done
|
|
|
|
if [[ ! -S "$socket_path" ]]; then
|
|
printf 'Xephyr did not make display %s ready.\n' "$display_name" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# DISPLAY is exported only to this script and its children. Your normal dwm
|
|
# terminal/session keeps its original display and its existing applications.
|
|
export DISPLAY="$display_name"
|
|
export LIME_XEPHYR_INTERNAL_CONFIG_FILE="$config_file"
|
|
export LIME_XEPHYR_INTERNAL_REPL_PORT="$repl_port"
|
|
export LIME_XEPHYR_INTERNAL_SESSION_MODE="$session_mode"
|
|
export LIME_XEPHYR_INTERNAL_SESSION_FILE="$session_file"
|
|
|
|
printf 'Lime test session: Xephyr %s (%s), Slynk 127.0.0.1:%s\n' \
|
|
"$display_name" "$screen_size" "$repl_port"
|
|
if [[ -n "$config_file" ]]; then
|
|
printf 'Configuration: %s\n' "$config_file"
|
|
else
|
|
printf 'Configuration: starter bindings (no config file)\n'
|
|
fi
|
|
printf 'Close Lime or press Ctrl-C here to tear down the nested session.\n'
|
|
|
|
cd "$repo_dir"
|
|
sbcl --noinform --non-interactive \
|
|
--eval '(require :asdf)' \
|
|
--eval '(asdf:load-asd (merge-pathnames "lime.asd" (uiop:getcwd)))' \
|
|
--eval '(asdf:load-system "lime/dev")' \
|
|
--eval '(lime.dev:run-development-session
|
|
:display-name (uiop:getenv "DISPLAY")
|
|
:session-file (let ((mode (uiop:getenv "LIME_XEPHYR_INTERNAL_SESSION_MODE")))
|
|
(cond ((string= mode "disabled") nil)
|
|
((string= mode "file") (uiop:getenv "LIME_XEPHYR_INTERNAL_SESSION_FILE"))
|
|
(t :default)))
|
|
:config-file (let ((path (uiop:getenv "LIME_XEPHYR_INTERNAL_CONFIG_FILE")))
|
|
(and (plusp (length path)) path))
|
|
:repl-port (parse-integer (uiop:getenv "LIME_XEPHYR_INTERNAL_REPL_PORT")))'
|