aboutsummaryrefslogtreecommitdiff
path: root/docker/scripts/ciq-env.sh
diff options
context:
space:
mode:
Diffstat (limited to 'docker/scripts/ciq-env.sh')
-rwxr-xr-xdocker/scripts/ciq-env.sh93
1 files changed, 92 insertions, 1 deletions
diff --git a/docker/scripts/ciq-env.sh b/docker/scripts/ciq-env.sh
index 945d59a..a52db78 100755
--- a/docker/scripts/ciq-env.sh
+++ b/docker/scripts/ciq-env.sh
@@ -1,5 +1,8 @@
#!/usr/bin/env bash
-# Shared helpers for the container scripts. Source this file; don't run it.
+# Author: cirakg & claude ai
+# Summary: Shared helpers for the container scripts (SDK and key lookup, headless display and simulator).
+#
+# Source this file; don't run it.
#
# Paths, as seen inside the container:
# /work the project folder (bind mount)
@@ -12,6 +15,12 @@ KEY_DIR="/work/.cache/keys"
DEV_KEY="${KEY_DIR}/developer_key.der"
BUILD_DIR="/work/build"
+# Virtual display for the headless simulator, and the TCP port (1234, hex
+# 04D2 as in /proc/net/tcp) on which the simulator accepts monkeydo.
+SIM_DISPLAY=":99"
+SIM_PORT_HEX="04D2"
+SIM_START_TIMEOUT_S=60
+
# Print an error and stop. Used for every user-facing failure, so the message
# says what to do next instead of leaving a bare stack trace.
die() {
@@ -47,3 +56,85 @@ ciq_require_device() {
[[ -f "${CIQ_HOME}/Devices/${device}/compiler.json" ]] \
|| die "No device files for '${device}'. Is it in manifest.xml? Then run 'make sdk' to download them."
}
+
+# Start a virtual X display (Xvfb) with the openbox window manager. The window
+# manager matters: screenshots and scripted input find the simulator window
+# through it.
+ciq_start_display() {
+ export DISPLAY="${SIM_DISPLAY}"
+ Xvfb "${DISPLAY}" -screen 0 1600x1200x24 -nolisten tcp >/tmp/xvfb.log 2>&1 &
+ local _
+ for _ in $(seq 1 50); do
+ xdpyinfo >/dev/null 2>&1 && break
+ sleep 0.1
+ done
+ xdpyinfo >/dev/null 2>&1 || die "Xvfb did not start (see /tmp/xvfb.log)."
+ openbox >/tmp/openbox.log 2>&1 &
+}
+
+# True when something listens on the simulator's monkeydo port.
+ciq_simulator_listening() {
+ awk -v port=":${SIM_PORT_HEX}" \
+ 'NR > 1 && $4 == "0A" && substr($2, length($2) - 4) == port { found = 1 } END { exit !found }' \
+ /proc/net/tcp
+}
+
+# Start the Connect IQ simulator (needs ciq_require_sdk and ciq_start_display)
+# and wait until it accepts monkeydo connections.
+ciq_start_simulator() {
+ # The simulator's single-instance lock (~/Sim-<user>) stores its process ID
+ # and survives in .cache/home after the container is gone. A later container
+ # often reuses that process ID for something else, and the simulator then
+ # believes it is already running and never opens its port. Each container
+ # runs exactly one simulator, so the stale lock is safe to remove.
+ rm -f "${HOME}"/Sim-*
+ simulator >/tmp/simulator.log 2>&1 &
+ local pid=$! _
+ for _ in $(seq 1 $((SIM_START_TIMEOUT_S * 5))); do
+ ciq_simulator_listening && return 0
+ kill -0 "${pid}" 2>/dev/null || break
+ sleep 0.2
+ done
+ # The container (and /tmp) disappears after a failure, so show the log now.
+ echo "--- last lines of the simulator log ---" >&2
+ tail -n 25 /tmp/simulator.log >&2 || true
+ die "The Connect IQ simulator did not start within ${SIM_START_TIMEOUT_S} s."
+}
+
+# The simulator's main window ID, for screenshots and scripted input.
+ciq_simulator_window() {
+ xdotool search --name '^CIQ Simulator' | head -n 1
+}
+
+# Longest wait for an app's first frame after launching it.
+FIRST_FRAME_TIMEOUT_S=60
+
+# Launch the debug build of the app on DEVICE and wait for its first frame,
+# which the app reports with a "DIAG first_frame" console line. The console
+# goes to LOG, followed by a "HARNESS first_frame wall_ms=N" line: the
+# wall-clock time from launch to first frame, including the JVM start of
+# monkeydo and the program load the app can't time itself. The app keeps
+# running (for screenshots) until ciq_stop_app or the next launch.
+# Returns 1 if no first frame appeared in time.
+ciq_launch_app() {
+ local device="$1" app_log="$2" start _
+ start="$(date +%s%3N)"
+ monkeydo "${BUILD_DIR}/debug/${device}.prg" "${device}" >"${app_log}" 2>&1 &
+ CIQ_MONKEYDO_PID=$!
+ for _ in $(seq 1 $((FIRST_FRAME_TIMEOUT_S * 20))); do
+ if grep -q "DIAG first_frame" "${app_log}"; then
+ echo "HARNESS first_frame wall_ms=$(($(date +%s%3N) - start))" >>"${app_log}"
+ return 0
+ fi
+ # monkeydo only exits on its own when the app could not run.
+ kill -0 "${CIQ_MONKEYDO_PID}" 2>/dev/null || break
+ sleep 0.05
+ done
+ return 1
+}
+
+# Stop the monkeydo session started by ciq_launch_app.
+ciq_stop_app() {
+ kill "${CIQ_MONKEYDO_PID:-0}" 2>/dev/null || true
+ wait "${CIQ_MONKEYDO_PID:-0}" 2>/dev/null || true
+}