From fd82efe08f3492f82295e080aba5c5b5d6ee80d4 Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Tue, 22 Sep 2026 16:21:05 -0400 Subject: [PATCH] Add native macOS simulator app packaging --- .../tactility-firmware-development/SKILL.md | 32 ++++++---- .gitignore | 4 +- Buildscripts/release-simulator-macos-app.sh | 61 +++++++++++++++++++ 3 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 Buildscripts/release-simulator-macos-app.sh diff --git a/.claude/skills/tactility-firmware-development/SKILL.md b/.claude/skills/tactility-firmware-development/SKILL.md index e861f7b8..7f6037e5 100644 --- a/.claude/skills/tactility-firmware-development/SKILL.md +++ b/.claude/skills/tactility-firmware-development/SKILL.md @@ -99,12 +99,10 @@ logs. HTTP 200 or a package simply appearing in `/api/apps` is insufficient. ## Host simulator (buildsim) + web viewer The POSIX simulator runs the real firmware (LVGL, services, web server) on -macOS/Linux with an SDL backend. macOS has no visible window (upstream -`.github/workflows/build-simulator.yml`: "macOS simulator currently fails due -to main thread requirement for rendering" — AppKit menu init must happen on -the process main thread, but FreeRTOS-POSIX parks main in `sigwait` and runs -everything on pthreads). The supported loop is **headless + web viewer**: -screenshots render server-side regardless of any window. +macOS/Linux with an SDL backend. On current firmware, `Main.cpp` runs SDL's +event loop on the process main thread while FreeRTOS runs on a separate thread. +This is required by AppKit and supports a native macOS window as well as the +web viewer. `SDL_VIDEODRIVER=dummy` remains useful for headless automation. Code locations: @@ -133,11 +131,14 @@ env -u ESP_IDF_VERSION -u IDF_PATH cmake --build buildsim --target TactilityKern app-module crypt-module gps-module http-module lvgl-module lvgl-window-manager-module service-module env -u ESP_IDF_VERSION -u IDF_PATH python3 Buildscripts/release-sdk-posix.py /tmp/sim-sdk -# release + headless run (MUST run from the firmware root: release-simulator.sh -# uses relative version.txt / Data paths) +# release (MUST run from the firmware root: release-simulator.sh uses relative +# version.txt / Data paths) sh Buildscripts/release-simulator.sh buildsim /tmp/simrun -(cd /tmp/simrun && SIM_DISPLAY_W=480 SIM_DISPLAY_H=320 SDL_VIDEODRIVER=dummy \ - nohup ./Tactility > /tmp/sim_web.log 2>&1 &) +# Native macOS window + web API. Run this from the release directory because +# data/ and system/ are relative to the process working directory. +(cd /tmp/simrun && SIM_DISPLAY_W=480 SIM_DISPLAY_H=320 \ + nohup ./Tactility > /tmp/sim_gui.log 2>&1 &) +# For CI/headless operation, set SDL_VIDEODRIVER=dummy instead. curl -s --max-time 5 http://127.0.0.1/api/sysinfo | head -c 120 ``` @@ -146,7 +147,8 @@ matching on-device screenshots). ES3C35P panel is 320x480 portrait in DTS but presents 480x320 landscape; ES3C28P is 320x240. The chosen geometry is logged as `Simulator Sim display WxH`. `SDL_VIDEODRIVER=dummy` is expected to log one `SdlDisplay Failed to create SDL window: Couldn't find matching render -driver` line — LVGL still renders and screenshots work. +driver` line — LVGL still renders and screenshots work. A native macOS launch +must not emit that line. ### Web viewer, touch, screenshots @@ -187,6 +189,14 @@ URLs would 404 at the edge (no `/api` mount there). - **Rebuild ≠ redeploy.** `cmake --build buildsim` updates `buildsim/` only. Re-run `release-simulator.sh`, restart the process, then retest. A stale `/tmp/simrun/Tactility` serves old handlers with new logs nowhere to be found. +- **One simulator owns port 80.** Do not launch a second instance while another + simulator is listening: it will initialize LVGL but fail `bind/listen`, so its + app API and viewer target the other process. Identify the listener with + `lsof -nP -iTCP:80 -sTCP:LISTEN`, stop only the intended simulator, then + release/restart it before installing or running POSIX apps. +- **Input is cross-thread on macOS.** SDL event pumping occurs on the real main + thread; LVGL and web touch injection run elsewhere. Keep all shared pointer, + key-queue, and touch-override state under `sdl_input.cpp`'s mutex. - **C array `sizeof` decay.** A helper like `f(HttpServerRequest*, char uri[256])` sees `sizeof(uri) == 8`, truncating `get_uri` output to 7 chars (`/api/sy`, `/sim/ap` 404s). Pass the size diff --git a/.gitignore b/.gitignore index c7642eff..4a87fe9b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ build*/ !.github/actions/build*/ +!Buildscripts/ +!Buildscripts/release-simulator-macos-app.sh cmake*/ CMakeCache.txt *.cbp @@ -27,4 +29,4 @@ sdkconfig.board.*.dev .caveman.json .ai/mcp -__pycache__ \ No newline at end of file +__pycache__ diff --git a/Buildscripts/release-simulator-macos-app.sh b/Buildscripts/release-simulator-macos-app.sh new file mode 100644 index 00000000..69cb0884 --- /dev/null +++ b/Buildscripts/release-simulator-macos-app.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +# Usage: release-simulator-macos-app.sh [builddir] [Tactility.app] +# Example: release-simulator-macos-app.sh buildsim release/Tactility.app + +set -eu + +build_path=$1 +bundle_path=$2 + +if [ -e "$bundle_path" ]; then + echo "Refusing to overwrite existing bundle: $bundle_path" >&2 + exit 1 +fi + +contents_path="$bundle_path/Contents" +resources_path="$contents_path/Resources" +macos_path="$contents_path/MacOS" + +mkdir -p "$resources_path" "$macos_path" + +cp "$build_path/Tactility/Tactility" "$resources_path/Tactility-bin" +cp version.txt "$resources_path/" +cp -R Data/data "$resources_path/" +cp -R Data/system "$resources_path/" + +cat > "$contents_path/Info.plist" <<'EOF' + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + Tactility + CFBundleIdentifier + org.tactilityproject.simulator + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Tactility + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.8.0-dev + CFBundleVersion + 1 + + +EOF + +cat > "$macos_path/Tactility" <<'EOF' +#!/bin/sh +set -eu + +resources_path="$(CDPATH= cd -- "$(dirname -- "$0")/../Resources" && pwd)" +cd "$resources_path" +exec "$resources_path/Tactility-bin" "$@" +EOF + +chmod +x "$macos_path/Tactility" "$resources_path/Tactility-bin"