diff --git a/Runner/suites/Multimedia/Graphics/weston-simple-egl/run.sh b/Runner/suites/Multimedia/Graphics/weston-simple-egl/run.sh index 9107651c..3d30c633 100755 --- a/Runner/suites/Multimedia/Graphics/weston-simple-egl/run.sh +++ b/Runner/suites/Multimedia/Graphics/weston-simple-egl/run.sh @@ -1,108 +1,128 @@ #!/bin/sh # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause-Clear +# # Validate weston-simple-egl runs under a working Wayland session. -# - Robust Wayland env resolution (adopts socket & fixes XDG_RUNTIME_DIR perms) -# - CI-friendly logs and PASS/FAIL semantics -# - Optional FPS parsing if build prints it (lenient if not present) +# - Wayland env resolution (adopts socket & fixes XDG_RUNTIME_DIR perms) +# - CI-friendly logs and PASS/FAIL/SKIP semantics (0/1/2) +# - Optional FPS parsing (best-effort) # ---------- Source init_env and functestlib ---------- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" INIT_ENV="" SEARCH="$SCRIPT_DIR" + while [ "$SEARCH" != "/" ]; do - if [ -f "$SEARCH/init_env" ]; then INIT_ENV="$SEARCH/init_env"; break; fi + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi SEARCH=$(dirname "$SEARCH") done + if [ -z "$INIT_ENV" ]; then echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 exit 1 fi -if [ -z "$__INIT_ENV_LOADED" ]; then + +if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" + __INIT_ENV_LOADED=1 fi + # shellcheck disable=SC1090,SC1091 . "$TOOLS/functestlib.sh" TESTNAME="weston-simple-egl" -# Tunables (env override) + +# ---------- Tunables (env override) ---------- DURATION="${DURATION:-30s}" # how long to run the client -STOP_GRACE="${STOP_GRACE:-3s}" # grace period on stop -EXPECT_FPS="${EXPECT_FPS:-60}" # nominal refresh (used for logging) +STOP_GRACE="${STOP_GRACE:-3s}" # grace period on stop (reserved for future) +EXPECT_FPS="${EXPECT_FPS:-60}" # nominal refresh (used for logs) FPS_TOL_PCT="${FPS_TOL_PCT:-10}" # +/- tolerance % REQUIRE_FPS="${REQUIRE_FPS:-0}" # 1=require FPS lines & threshold; 0=best effort +# ---------- Paths / logs ---------- test_path="$(find_test_case_by_name "$TESTNAME" 2>/dev/null || echo "$SCRIPT_DIR")" -cd "$test_path" || exit 1 +if ! cd "$test_path"; then + log_error "cd failed: $test_path" + exit 1 +fi + RES_FILE="./$TESTNAME.res" LOG_FILE="./${TESTNAME}_run.log" rm -f "$RES_FILE" "$LOG_FILE" log_info "--------------------------------------------------------------------------" log_info "------------------- Starting $TESTNAME Testcase --------------------------" -# FIX #1: Use ASCII '+/-' and keep it in a normal string + +# --- Platform details (robust logging; prefer helpers) --- +if command -v detect_platform >/dev/null 2>&1; then + detect_platform >/dev/null 2>&1 || true + log_info "Platform Details: machine='${PLATFORM_MACHINE:-unknown}' target='${PLATFORM_TARGET:-unknown}' kernel='${PLATFORM_KERNEL:-}' arch='${PLATFORM_ARCH:-}'" +else + log_info "Platform Details: unknown" +fi + log_info "Config: DURATION=$DURATION STOP_GRACE=$STOP_GRACE EXPECT_FPS=${EXPECT_FPS}+/-${FPS_TOL_PCT}% REQUIRE_FPS=$REQUIRE_FPS" -# Dependencies -check_dependencies weston-simple-egl || { - log_fail "$TESTNAME : weston-simple-egl binary not found in PATH" +# ---------- Dependencies ---------- +if ! check_dependencies weston-simple-egl; then + log_skip "$TESTNAME : SKIP (weston-simple-egl not found in PATH)" echo "$TESTNAME SKIP" > "$RES_FILE" - exit 0 -} + exit 2 +fi + BIN="$(command -v weston-simple-egl 2>/dev/null || true)" log_info "Using weston-simple-egl: ${BIN:-}" -# Resolve Wayland socket: -# 1) If current env points to a real socket, use it. -sock="" -if [ -n "$XDG_RUNTIME_DIR" ] && [ -n "$WAYLAND_DISPLAY" ] && [ -e "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then - sock="$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" -fi +# ----- Display presence check (DP/HDMI/etc.) ----- +# Quick snapshot for debugging (lists DRM nodes, sysfs connectors, weston outputs) +display_debug_snapshot "pre-display-check" + +have_connector=0 -# 2) Otherwise, scan common locations. -if [ -z "$sock" ]; then - for s in $(find_wayland_sockets); do - if [ -e "$s" ]; then sock="$s"; break; fi - done +# sysfs-based summary (existing helper) +sysfs_summary="$(display_connected_summary 2>/dev/null || printf '%s' '')" +if [ -n "$sysfs_summary" ] && [ "$sysfs_summary" != "none" ]; then + have_connector=1 + log_info "Connected display (sysfs): $sysfs_summary" fi -# 3) If still no socket, try to start Weston and wait a bit. -if [ -z "$sock" ]; then - if weston_is_running; then - log_warn "Weston running but no Wayland socket visible; attempting to continue." - else - log_info "Weston not running. Attempting to start..." - weston_start - fi - # Wait for socket to appear (up to ~5s) - n=0 - while [ $n -lt 5 ] && [ -z "$sock" ]; do - for s in $(find_wayland_sockets); do - if [ -e "$s" ]; then sock="$s"; break; fi - done - [ -n "$sock" ] && break - sleep 1 - n=$((n+1)) - done +if [ "$have_connector" -eq 0 ]; then + log_skip "$TESTNAME : SKIP (no connected display detected)" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 2 fi -if [ -z "$sock" ]; then +wayland_debug_snapshot "weston-simple-egl: start" + +# ---------- Choose/adopt Wayland socket (using helper) ---------- +# Capture only the actual socket path from helper output (filter out logs) +sock="$( + wayland_choose_or_start 2>/dev/null \ + | grep -E '/(run/user/[0-9]+|tmp|dev/socket/weston)/wayland-[0-9]+$' \ + | tail -n 1 +)" +if [ -n "$sock" ]; then + log_info "Found Wayland socket: $sock" +else log_fail "$TESTNAME : FAIL (no Wayland socket found after attempting to start Weston)" echo "$TESTNAME FAIL" > "$RES_FILE" + wayland_debug_snapshot "weston-simple-egl: no-socket" exit 1 fi -# Adopt env and fix runtime dir perms (done inside helper(s)) -adopt_wayland_env_from_socket "$sock" - -# FIX #2: Avoid duplicate prints — helpers can log the chosen socket/env once. -# If your helpers are quiet instead, uncomment the two lines below: -# log_info "Selected Wayland socket: $sock" -# log_info "Wayland env: XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR WAYLAND_DISPLAY=$WAYLAND_DISPLAY" +adopt_wayland_env_from_socket "$sock" || log_warn "adopt_wayland_env_from_socket: invalid: $sock" +log_info "Final Wayland env: XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-}WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-}" +# Replace to avoid confusion in CI logs: +# shellcheck disable=SC2016 +printf '%s\n' "" | sed 's/.*/[DBG] (env adopted)/' >/dev/null 2>&1 || true +# ---------- Sanity check Wayland connectivity ---------- if wayland_connection_ok; then - log_info "Wayland connection test: OK (wayland-info/env)" + log_info "Wayland connection test: OK" else log_fail "$TESTNAME : FAIL (Wayland connection test failed)" print_path_meta "$XDG_RUNTIME_DIR" | sed 's/^/[DBG] /' @@ -111,22 +131,25 @@ else exit 1 fi -# Try to enable FPS prints if supported by the client build (best effort). +# Try to enable FPS prints if supported by the client (best effort). export SIMPLE_EGL_FPS=1 export WESTON_SIMPLE_EGL_FPS=1 -# Run the client for DURATION with a stopwatch for CI logs. -log_info "Running weston-simple-egl for $DURATION ..." +# ---------- Run the client ---------- +log_info "Launching weston-simple-egl for $DURATION …" start_ts="$(date +%s 2>/dev/null || echo 0)" + if command -v run_with_timeout >/dev/null 2>&1; then + log_info "Using helper: run_with_timeout" run_with_timeout "$DURATION" weston-simple-egl >"$LOG_FILE" 2>&1 rc=$? else if command -v timeout >/dev/null 2>&1; then + log_info "Using coreutils timeout" timeout "$DURATION" weston-simple-egl >"$LOG_FILE" 2>&1 rc=$? else - # Last resort: background and sleep. + log_info "No timeout helpers; running in background with manual sleep-stop" sh -c 'weston-simple-egl' >"$LOG_FILE" 2>&1 & pid=$! # DURATION like "30s" → "30" @@ -138,24 +161,23 @@ else rc=143 fi fi + end_ts="$(date +%s 2>/dev/null || echo 0)" elapsed=$(( end_ts - start_ts )) [ "$elapsed" -lt 0 ] && elapsed=0 +log_info "Client finished: rc=$rc elapsed=${elapsed}s" -# FPS parsing (best effort) +# ---------- FPS parsing (best effort) ---------- fps="-" fps_line="$(grep -E '([Ff][Pp][Ss]|frames per second|^fps:)' "$LOG_FILE" 2>/dev/null | tail -n 1)" if [ -n "$fps_line" ]; then - # Extract last numeric token (integer or float) - fps="$(printf '%s\n' "$fps_line" | awk '{ - for (i=NF;i>=1;i--) if ($i ~ /^[0-9]+(\.[0-9]+)?$/) {print $i; exit} - }')" + fps="$(printf '%s\n' "$fps_line" | awk '{ for (i=NF;i>=1;i--) if ($i ~ /^[0-9]+(\.[0-9]+)?$/) {print $i; exit} }')" [ -z "$fps" ] && fps="-" fi -# CI debugging summary log_info "Result summary: rc=$rc elapsed=${elapsed}s fps=${fps} (expected ~${EXPECT_FPS}+/-${FPS_TOL_PCT}%)" -# Quick duration gate: must have run at least (DURATION-1) seconds to be considered OK. + +# ---------- Gating ---------- dur_s="$(printf '%s' "$DURATION" | sed -n 's/^\([0-9][0-9]*\)s$/\1/p')" [ -z "$dur_s" ] && dur_s="$DURATION" min_ok=$(( dur_s - 1 )) @@ -163,6 +185,7 @@ min_ok=$(( dur_s - 1 )) final="PASS" +# Must have run ~DURATION seconds if [ "$elapsed" -lt "$min_ok" ]; then final="FAIL" log_fail "$TESTNAME : FAIL (exited after ${elapsed}s; expected ~${dur_s}s) — rc=$rc" @@ -174,7 +197,6 @@ if [ "$final" = "PASS" ] && [ "$REQUIRE_FPS" -eq 1 ]; then final="FAIL" log_fail "$TESTNAME : FAIL (no FPS lines found but REQUIRE_FPS=1)" else - # Integer-only tolerance check (portable) lo=$(( (EXPECT_FPS * (100 - FPS_TOL_PCT)) / 100 )) hi=$(( (EXPECT_FPS * (100 + FPS_TOL_PCT)) / 100 )) fps_int="$(printf '%s' "$fps" | cut -d. -f1)" @@ -185,13 +207,21 @@ if [ "$final" = "PASS" ] && [ "$REQUIRE_FPS" -eq 1 ]; then fi fi +# ---------- Epilogue / exit codes ---------- case "$final" in PASS) log_pass "$TESTNAME : PASS" echo "$TESTNAME PASS" > "$RES_FILE" exit 0 ;; + SKIP) + # (Not used here, but keeping consistent mapping) + log_skip "$TESTNAME : SKIP" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 2 + ;; *) + log_fail "$TESTNAME : FAIL" echo "$TESTNAME FAIL" > "$RES_FILE" exit 1 ;; diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index 4e24a54d..5db85841 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -560,9 +560,21 @@ check_tar_file() { return 2 } -# Check if weston is running +# Return space-separated PIDs for 'weston' (BusyBox friendly). +weston_pids() { + pids="" + if command -v pgrep >/dev/null 2>&1; then + pids="$(pgrep -x weston 2>/dev/null || true)" + fi + if [ -z "$pids" ]; then + pids="$(ps -eo pid,comm 2>/dev/null | awk '$2=="weston"{print $1}')" + fi + echo "$pids" +} + +# Is Weston running? weston_is_running() { - pgrep -x weston >/dev/null 2>&1 + [ -n "$(weston_pids)" ] } # Stop all Weston processes @@ -588,31 +600,711 @@ weston_stop() { # Start weston with correct env if not running weston_start() { - export XDG_RUNTIME_DIR="/dev/socket/weston" - mkdir -p "$XDG_RUNTIME_DIR" + if weston_is_running; then + log_info "Weston already running." + return 0 + fi + + if command -v systemctl >/dev/null 2>&1; then + log_info "Attempting to start via systemd: weston.service" + systemctl start weston.service >/dev/null 2>&1 || true + sleep 1 + if weston_is_running; then + log_info "Weston started via systemd (weston.service)." + return 0 + fi + + log_info "Attempting to start via systemd: weston@.service" + systemctl start weston@.service >/dev/null 2>&1 || true + sleep 1 + if weston_is_running; then + log_info "Weston started via systemd (weston@.service)." + return 0 + fi + + log_warn "systemd start did not bring Weston up; will try direct spawn." + fi + + # Minimal-friendly direct spawn (no headless module guesses here). + ensure_xdg_runtime_dir + + if ! command -v weston >/dev/null 2>&1; then + log_fail "weston binary not found in PATH." + return 1 + fi + + log_info "Attempting to spawn Weston (no backend override). Log: /tmp/weston.self.log" + ( nohup weston --log=/tmp/weston.self.log >/dev/null 2>&1 & ) || true + + tries=0 + while [ $tries -lt 5 ]; do + if weston_is_running; then + log_info "Weston is now running (PID(s): $(weston_pids))." + return 0 + fi + if [ -n "$(find_wayland_sockets | head -n1)" ]; then + log_info "A Wayland socket appeared after spawn." + return 0 + fi + sleep 1 + tries=$((tries+1)) + done + + if [ -f /tmp/weston.self.log ]; then + log_warn "Weston spawn failed; last log lines:" + tail -n 20 /tmp/weston.self.log 2>/dev/null | sed 's/^/[weston.log] /' || true + else + log_warn "Weston spawn failed; no log file present." + fi + return 1 +} + +# Choose a socket (or try to start), adopt env, and echo chosen path. +wayland_choose_or_start() { + wayland_debug_snapshot "pre-choose" + sock="$(wayland_pick_socket || true)" + if [ -z "$sock" ]; then + log_info "No Wayland socket found; attempting to start Weston…" + weston_start || log_warn "weston_start() did not succeed." + # Re-scan a few times + n=0 + while [ $n -lt 5 ] && [ -z "$sock" ]; do + sock="$(wayland_pick_socket || true)" + [ -n "$sock" ] && break + sleep 1 + n=$((n+1)) + done + fi + if [ -n "$sock" ]; then + adopt_wayland_env_from_socket "$sock" + wayland_debug_snapshot "post-choose" + echo "$sock" + return 0 + fi + wayland_debug_snapshot "no-socket" + return 1 +} +# Ensure we have a writable XDG_RUNTIME_DIR for the current user. +# Prefers /run/user/, falls back to /tmp/xdg-runtime-. +ensure_xdg_runtime_dir() { + uid="$(id -u 2>/dev/null || echo 0)" + cand="/run/user/$uid" + + if [ ! -d "$cand" ]; then + mkdir -p "$cand" 2>/dev/null || cand="/tmp/xdg-runtime-$uid" + fi - # Remove stale Weston socket if it exists - if [ -S "$XDG_RUNTIME_DIR/weston" ]; then - log_info "Removing stale Weston socket." - rm -f "$XDG_RUNTIME_DIR/weston" + mkdir -p "$cand" 2>/dev/null || true + chmod 700 "$cand" 2>/dev/null || true + export XDG_RUNTIME_DIR="$cand" + + log_info "XDG_RUNTIME_DIR ensured: $XDG_RUNTIME_DIR" +} + +# Choose newest socket (by mtime); logs candidates for debugging. +wayland_pick_socket() { + best="" + best_mtime=0 + + log_info "Wayland sockets found (candidate list):" + for s in $(find_wayland_sockets | sort -u); do + mt="$(stat -c %Y "$s" 2>/dev/null || echo 0)" + log_info " - $s (mtime=$mt)" + if [ "$mt" -gt "$best_mtime" ]; then + best="$s" + best_mtime="$mt" + fi + done + + if [ -n "$best" ]; then + log_info "Picked Wayland socket (newest): $best" + echo "$best" + return 0 fi + return 1 +} +# ---- Wayland/Weston helpers ----------------------- +# Ensure a private XDG runtime directory exists and is usable (0700). +weston_start() { + # Already up? if weston_is_running; then log_info "Weston already running." return 0 fi - # Clean up stale sockets for wayland-0 (optional) - [ -S "$XDG_RUNTIME_DIR/wayland-1" ] && rm -f "$XDG_RUNTIME_DIR/wayland-1" - nohup weston --continue-without-input --idle-time=0 > weston.log 2>&1 & - sleep 3 + + # 1) Try systemd user/system units if present + if command -v systemctl >/dev/null 2>&1; then + for unit in weston.service weston@.service; do + log_info "Attempting to start via systemd: $unit" + systemctl start "$unit" >/dev/null 2>&1 || true + sleep 1 + if weston_is_running; then + log_info "Weston started via $unit." + return 0 + fi + done + log_warn "systemd start did not bring Weston up; will try direct spawn." + fi + + # Helper: attempt spawn for a given uid (empty => current user) + # Tries multiple backend names (to cover distro/plugin differences) + # Returns 0 if a weston process + socket appears, else non-zero. + spawn_weston_try() { + target_uid="$1" # "" or numeric uid + backends="${WESTON_BACKENDS:-headless headless-backend.so}" + + # Prepare runtime dir + if [ -n "$target_uid" ]; then + run_dir="/run/user/$target_uid" + mkdir -p "$run_dir" 2>/dev/null || true + chown "$target_uid:$target_uid" "$run_dir" 2>/dev/null || true + else + ensure_xdg_runtime_dir + run_dir="$XDG_RUNTIME_DIR" + fi + chmod 700 "$run_dir" 2>/dev/null || true + + # Where to log + log_file="/tmp/weston.${target_uid:-self}.log" + rm -f "$log_file" 2>/dev/null || true + + for be in $backends; do + log_info "Spawning weston (uid=${target_uid:-$(id -u)}) with backend='$be' …" + if ! command -v weston >/dev/null 2>&1; then + log_fail "weston binary not found in PATH." + return 1 + fi + + # Build the command: avoid optional modules that may not exist on minimal builds + cmd="XDG_RUNTIME_DIR='$run_dir' weston --backend='$be' --log='$log_file'" + + if [ -n "$target_uid" ]; then + # Run as that uid if we can + if command -v su >/dev/null 2>&1; then + su -s /bin/sh -c "$cmd >/dev/null 2>&1 &" "#$target_uid" || true + elif command -v runuser >/dev/null 2>&1; then + runuser -u "#$target_uid" -- sh -c "$cmd >/dev/null 2>&1 &" || true + else + log_warn "No su/runuser available to switch uid=$target_uid; skipping this mode." + continue + fi + else + # Current user + ( nohup sh -c "$cmd" >/dev/null 2>&1 & ) || true + fi + + # Wait up to ~5s for process + a socket to appear + tries=0 + while [ $tries -lt 5 ]; do + if weston_is_running; then + # See if a fresh socket is visible + sock="$(wayland_pick_socket)" + if [ -n "$sock" ]; then + log_info "Weston up (backend=$be). Socket: $sock" + return 0 + fi + fi + sleep 1 + tries=$((tries+1)) + done + + # Show weston log tail to aid debugging + if [ -r "$log_file" ]; then + log_warn "Weston did not come up with backend '$be'. Last log lines:" + tail -n 20 "$log_file" | sed 's/^/[weston.log] /' + else + log_warn "Weston did not come up with backend '$be' and no log file present ($log_file)." + fi + done + + return 1 + } + + # 2) Try as current user + if spawn_weston_try ""; then + return 0 + fi + + # 3) Try as 'weston' user (common on embedded images) + weston_uid="" + if command -v getent >/dev/null 2>&1; then + weston_uid="$(getent passwd weston 2>/dev/null | awk -F: '{print $3}')" + fi + [ -z "$weston_uid" ] && weston_uid="$(id -u weston 2>/dev/null || true)" + + if [ -n "$weston_uid" ]; then + log_info "Attempting to spawn Weston as uid=$weston_uid (user 'weston')." + if spawn_weston_try "$weston_uid"; then + return 0 + fi + else + log_info "No 'weston' user found; skipping user-switch spawn." + fi + + log_warn "All weston spawn attempts failed." + return 1 +} + +# Return first Wayland socket under a base dir (prints path or fails). +find_wayland_socket_in() { + base="$1" + [ -d "$base" ] || return 1 + for s in "$base"/wayland-*; do + [ -S "$s" ] || continue + printf '%s\n' "$s" + return 0 + done + return 1 +} + +# Best-effort discovery of a usable Wayland socket anywhere. +discover_wayland_socket_anywhere() { + uid="$(id -u 2>/dev/null || echo 0)" + bases="" + [ -n "$XDG_RUNTIME_DIR" ] && bases="$bases $XDG_RUNTIME_DIR" + bases="$bases /dev/socket/weston /run/user/$uid /tmp/wayland-$uid /dev/shm" + for b in $bases; do + ensure_private_runtime_dir "$b" >/dev/null 2>&1 || true + if s="$(find_wayland_socket_in "$b")"; then + printf '%s\n' "$s" + return 0 + fi + done + return 1 +} + +# Adopt env from a Wayland socket path like /run/user/0/wayland-0 +# Sets XDG_RUNTIME_DIR and WAYLAND_DISPLAY. Returns 0 on success. +adopt_wayland_env_from_socket() { + s="$1" + if [ -z "$s" ] || [ ! -S "$s" ]; then + log_warn "adopt_wayland_env_from_socket: invalid socket: ${s:-}" + return 1 + fi + XDG_RUNTIME_DIR="$(dirname "$s")" + WAYLAND_DISPLAY="$(basename "$s")" + export XDG_RUNTIME_DIR WAYLAND_DISPLAY + # Best-effort perms fix for minimal systems + chmod 700 "$XDG_RUNTIME_DIR" 2>/dev/null || true + log_info "Adopting Wayland environment from socket: $s" + log_info "Adopted Wayland env: XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR WAYLAND_DISPLAY=$WAYLAND_DISPLAY" + log_info "Reproduce with:" + log_info " export XDG_RUNTIME_DIR='$XDG_RUNTIME_DIR'" + log_info " export WAYLAND_DISPLAY='$WAYLAND_DISPLAY'" +} + +# Try to connect to Wayland. Returns 0 on OK. +wayland_can_connect() { + if command -v weston-info >/dev/null 2>&1; then + weston-info >/dev/null 2>&1 + return $? + fi + # fallback: quick client probe + ( env -i XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR" WAYLAND_DISPLAY="$WAYLAND_DISPLAY" true ) >/dev/null 2>&1 + return $? +} + +# Ensure a Weston socket exists; if not, stop+start Weston and adopt helper socket. +weston_pick_env_or_start() { + sock="$(discover_wayland_socket_anywhere 2>/dev/null || true)" + if [ -n "$sock" ]; then + adopt_wayland_env_from_socket "$sock" + log_info "Selected Wayland socket: $sock" + return 0 + fi if weston_is_running; then - log_info "Weston started." + log_info "Stopping Weston..." + weston_stop + i=0; while weston_is_running && [ "$i" -lt 5 ]; do i=$((i+1)); sleep 1; done + fi + + log_info "Starting Weston..." + weston_start + i=0; sock="" + while [ "$i" -lt 6 ]; do + sock="$(find_wayland_socket_in /dev/socket/weston 2>/dev/null || true)" + [ -n "$sock" ] && break + sleep 1; i=$((i+1)) + done + if [ -z "$sock" ]; then + log_fail "Could not find Wayland socket after starting Weston." + return 1 + fi + adopt_wayland_env_from_socket "$sock" + log_info "Weston started; socket: $sock" + return 0 +} + +# Find candidate Wayland sockets in common locations. +# Prints absolute socket paths, one per line, most-preferred first. +find_wayland_sockets() { + # Enumerate plausible Wayland sockets (one per line) + uid="$(id -u 2>/dev/null || echo 0)" + + # Current env first (if valid) + if [ -n "${XDG_RUNTIME_DIR:-}" ] && [ -n "${WAYLAND_DISPLAY:-}" ] && + [ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then + echo "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" + fi + + # Current uid + for f in "/run/user/$uid/wayland-0" "/run/user/$uid/wayland-1" "/run/user/$uid/wayland-2"; do + [ -S "$f" ] && echo "$f" + done + for f in /run/user/"$uid"/wayland-*; do + [ -S "$f" ] && echo "$f" + done 2>/dev/null + + # Any user under /run/user (root can traverse) — covers weston running as uid 1000 + for d in /run/user/*; do + [ -d "$d" ] || continue + for f in "$d"/wayland-*; do + [ -S "$f" ] && echo "$f" + done + done 2>/dev/null + + # weston-launch sockets + for f in /dev/socket/weston/wayland-*; do + [ -S "$f" ] && echo "$f" + done 2>/dev/null + + # Last resort + for f in /tmp/wayland-*; do + [ -S "$f" ] && echo "$f" + done 2>/dev/null +} + +# Ensure XDG_RUNTIME_DIR has owner=current-user and mode 0700. +# Returns 0 if OK (or fixed), non-zero if still not compliant. +ensure_wayland_runtime_dir_perms() { + dir="$1" + [ -n "$dir" ] && [ -d "$dir" ] || return 1 + + cur_uid="$(id -u 2>/dev/null || echo 0)" + cur_gid="$(id -g 2>/dev/null || echo 0)" + + # Best-effort fixups first (don’t error if chown/chmod fail) + chown "$cur_uid:$cur_gid" "$dir" 2>/dev/null || true + chmod 0700 "$dir" 2>/dev/null || true + + # Verify using stat (GNU first, then BSD). If stat is unavailable, + # we can’t verify—assume OK to avoid SC2012 (ls) usage. + if command -v stat >/dev/null 2>&1; then + # Mode: GNU: %a ; BSD: %Lp + mode="$(stat -c '%a' "$dir" 2>/dev/null || stat -f '%Lp' "$dir" 2>/dev/null || echo '')" + # Owner uid: GNU: %u ; BSD: %u + uid="$(stat -c '%u' "$dir" 2>/dev/null || stat -f '%u' "$dir" 2>/dev/null || echo '')" + + [ "$mode" = "700" ] && [ "$uid" = "$cur_uid" ] && return 0 + return 1 + fi + + # No stat available: directory exists and we attempted to fix perms/owner. + # Treat as success so clients can try; avoids SC2012 warnings. + return 0 +} + +# Quick Wayland handshake check. +# Prefers `wayland-info` with a short timeout; otherwise validates socket presence. +# Also enforces/fixes XDG_RUNTIME_DIR permissions so clients won’t reject it. +wayland_connection_ok() { + if command -v wayland-info >/dev/null 2>&1; then + log_info "Probing Wayland with: wayland-info" + wayland-info >/dev/null 2>&1 && return 0 + return 1 + fi + if command -v weston-info >/dev/null 2>&1; then + log_info "Probing Wayland with: weston-info" + weston-info >/dev/null 2>&1 && return 0 + return 1 + fi + if command -v weston-simple-egl >/dev/null 2>&1; then + log_info "Probing Wayland by briefly starting weston-simple-egl" + ( weston-simple-egl >/dev/null 2>&1 & echo $! >"/tmp/.wsegl.$$" ) + pid="$(cat "/tmp/.wsegl.$$" 2>/dev/null || echo)" + rm -f "/tmp/.wsegl.$$" 2>/dev/null || true + i=0 + while [ $i -lt 2 ]; do + sleep 1 + i=$((i+1)) + done + if [ -n "$pid" ]; then + kill "$pid" 2>/dev/null || true + fi + # If it started at all, consider the connection OK (best effort). + return 0 + fi + if [ -n "$XDG_RUNTIME_DIR" ] && [ -n "$WAYLAND_DISPLAY" ] && [ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then + log_info "No probe tools present; accepting socket existence as OK." return 0 + fi + return 1 +} +# Very verbose snapshot for debugging (processes, sockets, env, perms). +wayland_debug_snapshot() { + label="$1" + [ -n "$label" ] || label="snapshot" + log_info "----- Wayland/Weston debug snapshot: $label -----" + + # Processes + wpids="$(weston_pids)" + if [ -n "$wpids" ]; then + log_info "weston PIDs: $wpids" + for p in $wpids; do + if command -v ps >/dev/null 2>&1; then + ps -o pid,user,group,cmd -p "$p" 2>/dev/null | sed 's/^/[ps] /' || true + fi + if [ -r "/proc/$p/cmdline" ]; then + tr '\0' ' ' <"/proc/$p/cmdline" 2>/dev/null | sed 's/^/[cmdline] /' || true + fi + done else - log_error "Failed to start Weston." - return 1 + log_info "weston PIDs: (none)" + fi + + # Sockets (meta) — use stat instead of ls (SC2012) + for s in $(find_wayland_sockets | sort -u); do + log_info "socket: $s" + stat -c '[stat] %n -> owner=%U:%G mode=%A size=%s mtime=%y' "$s" 2>/dev/null || true + done + + # Current env + log_info "Env now: XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-} WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-}" + if [ -n "${XDG_RUNTIME_DIR:-}" ]; then + stat -c '[stat] %n -> owner=%U:%G mode=%A size=%s mtime=%y' "$XDG_RUNTIME_DIR" 2>/dev/null || true + fi + + log_info "Suggested export (current env):" + log_info " export XDG_RUNTIME_DIR='${XDG_RUNTIME_DIR:-}'" + log_info " export WAYLAND_DISPLAY='${WAYLAND_DISPLAY:-}'" + + log_info "----- End snapshot: $label -----" +} + +# Print concise metadata for a path (portable). +# Prefers stat(1) (GNU or BSD); falls back to ls(1) only if needed. +# Usage: print_path_meta "/some/path" +print_path_meta() { + p=$1 + if [ -z "$p" ]; then + return 1 + fi + # GNU stat + if stat -c '%A %U %G %a %n' "$p" >/dev/null 2>&1; then + stat -c '%A %U %G %a %n' "$p" + return 0 + fi + # BSD/Mac stat + if stat -f '%Sp %Su %Sg %OLp %N' "$p" >/dev/null 2>&1; then + stat -f '%Sp %Su %Sg %OLp %N' "$p" + return 0 + fi + # shellcheck disable=SC2012 + ls -ld -- "$p" 2>/dev/null +} + +############################################################################### +# DRM / Display helpers (portable, minimal-build friendly) +############################################################################### + +# Echo lines: "\t\t\t\t" +# Example: "card0-HDMI-A-1 connected HDMI-A 9 1920x1080" +display_list_connectors() { + found=0 + for d in /sys/class/drm/*-*; do + [ -e "$d" ] || continue + [ -f "$d/status" ] || continue + name="$(basename "$d")" + status="$(tr -d '\r\n' <"$d/status" 2>/dev/null)" + + # Derive connector type from name: cardX--N + # Strip "cardN-" prefix and trailing "-N" index. + typ="$(printf '%s' "$name" \ + | sed -n 's/^card[0-9]\+-\([A-Za-z0-9+]\+\(-[A-Za-z0-9+]\+\)*\)-[0-9]\+/\1/p')" + [ -z "$typ" ] && typ="unknown" + + # Modes + modes_file="$d/modes" + if [ -f "$modes_file" ]; then + # wc output can have spaces on BusyBox; trim + mc="$(wc -l <"$modes_file" 2>/dev/null | tr -d '[:space:]')" + [ -z "$mc" ] && mc=0 + fm="$(head -n 1 "$modes_file" 2>/dev/null | tr -d '\r\n')" + else + mc=0 + fm="" + fi + + printf '%s\t%s\t%s\t%s\t%s\n' "$name" "$status" "$typ" "$mc" "$fm" + found=1 + done + [ "$found" -eq 1 ] || return 1 + return 0 +} + +# Return 0 if any connector is connected; else 1 +display_any_attached() { + for d in /sys/class/drm/*-*; do + [ -f "$d/status" ] || continue + st="$(tr -d '\r\n' <"$d/status" 2>/dev/null)" + if [ "$st" = "connected" ]; then + return 0 + fi + done + return 1 +} + +# Print one compact human line summarizing connected outputs +display_connected_summary() { + have=0 + line="" + # shellcheck disable=SC2039 + while IFS="$(printf '\t')" read -r name status typ mc fm; do + [ "$status" = "connected" ] || continue + have=1 + if [ -n "$fm" ]; then + seg="${name}(${typ},${fm})" + else + seg="${name}(${typ})" + fi + if [ -z "$line" ]; then line="$seg"; else line="$line, $seg"; fi + done </dev/null || true) +EOF + if [ "$have" -eq 1 ]; then + echo "$line" + return 0 fi + echo "none" + return 1 +} + +# Best-effort "primary" guess: first connected with a mode; else first connected; echoes name +display_primary_guess() { + best="" + # Prefer one with modes + # shellcheck disable=SC2039 + while IFS="$(printf '\t')" read -r name status typ mc fm; do + [ "$status" = "connected" ] || continue + if [ -n "$fm" ]; then echo "$name"; return 0; fi + [ -z "$best" ] && best="$name" + done </dev/null || true) +EOF + [ -n "$best" ] && { echo "$best"; return 0; } + return 1 +} + +# Optional enrichment via weston-info (if available) +# Prints lines: "weston: model= make= phys=mm" +display_weston_outputs() { + if ! command -v weston-info >/dev/null 2>&1; then + return 0 + fi + # Very light parse; tolerate different locales + weston-info 2>/dev/null \ + | awk ' + $1=="output" && $2~/^[0-9]+:$/ {out=$2; sub(":","",out)} + /make:/ {make=$2} + /model:/ {model=$2} + /physical size:/ {w=$3; h=$5; sub("mm","",h)} + /scale:/ { + if (out!="") { + printf("weston: %s make=%s model=%s phys=%sx%sm\n", out, make, model, w, h); + out=""; make=""; model=""; w=""; h=""; + } + } + ' + return 0 +} + +# One-stop debug snapshot +display_debug_snapshot() { + ctx="$1" + [ -z "$ctx" ] && ctx="display-snapshot" + log_info "----- Display snapshot: $ctx -----" + + # DRM nodes (no ls; iterate) + nodes="" + for f in /dev/dri/card* /dev/dri/renderD*; do + if [ -e "$f" ]; then + if [ -z "$nodes" ]; then nodes="$f"; else nodes="$nodes $f"; fi + fi + done + if [ -n "$nodes" ]; then + log_info "DRM nodes: $nodes" + else + log_warn "No /dev/dri/* nodes found." + fi + + # Connectors + have=0 + # shellcheck disable=SC2039 + while IFS="$(printf '\t')" read -r name status typ mc fm; do + have=1 + if [ -n "$fm" ]; then + log_info "DRM: ${name} status=${status} type=${typ} modes=${mc} first=${fm}" + else + log_info "DRM: ${name} status=${status} type=${typ} modes=${mc}" + fi + done </dev/null || true) +EOF + [ "$have" -eq 1 ] || log_warn "No DRM connectors in /sys/class/drm." + + # Summary + weston outputs (if any) + sum="$(display_connected_summary 2>/dev/null || echo none)" + log_info "Connected summary: $sum" + display_weston_outputs | while IFS= read -r l; do + [ -n "$l" ] && log_info "$l" + done + + log_info "----- End display snapshot: $ctx -----" +} + +display_debug_snapshot() { + ctx="$1" + [ -z "$ctx" ] && ctx="display-snapshot" + log_info "----- Display snapshot: $ctx -----" + + # DRM nodes + nodes="" + for f in /dev/dri/card* /dev/dri/renderD*; do + [ -e "$f" ] && nodes="${nodes:+$nodes }$f" + done + if [ -n "$nodes" ]; then + log_info "DRM nodes: $nodes" + else + log_warn "No /dev/dri/* nodes found." + fi + + # Sysfs connectors (expects display_list_connectors to print tab-separated fields) + have=0 + while IFS="$(printf '\t')" read -r name status typ mc fm; do + [ -n "$name" ] || continue + have=1 + if [ -n "$fm" ]; then + log_info "DRM: ${name} status=${status} type=${typ} modes=${mc} first=${fm}" + else + log_info "DRM: ${name} status=${status} type=${typ} modes=${mc}" + fi + done </dev/null || true) +EOF + [ "$have" -eq 1 ] || log_warn "No DRM connectors in /sys/class/drm." + + # Connected summary (sysfs) + sum="$(display_connected_summary 2>/dev/null || echo none)" + log_info "Connected summary (sysfs): $sum" + + # Optional weston outputs (existing helper) + display_weston_outputs | while IFS= read -r l; do + [ -n "$l" ] && log_info "$l" + done + + log_info "----- End display snapshot: $ctx -----" } # Returns true (0) if interface is administratively and physically up