From 753d60550409e49efc072a785843ee040e7c0753 Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Thu, 8 Oct 2026 12:24:38 -0500 Subject: [PATCH] usdl2 builds into CircuitPython as a user C module only: the patch script and its shared-bindings copy go --- AGENTS.md | 6 +- README.md | 3 +- UPSTREAM | 7 +- apply_cp_patches.sh | 514 ------------------ circuitpython.mk | 47 -- docs/idempotent-lifecycle.md | 2 +- docs/newcomers.md | 4 +- src/circuitpython_spike/copy_manifest.txt | 4 - .../shared-bindings/usdl2/__init__.c | 23 - .../shared-bindings/usdl2/__init__.h | 7 - .../shared-module/usdl2/__init__.c | 6 - .../shared-module/usdl2/__init__.h | 5 - src/ports/desktop/usdl2/usdl2_mp.c | 7 +- 13 files changed, 14 insertions(+), 621 deletions(-) delete mode 100755 apply_cp_patches.sh delete mode 100644 circuitpython.mk delete mode 100644 src/circuitpython_spike/copy_manifest.txt delete mode 100644 src/circuitpython_spike/shared-bindings/usdl2/__init__.c delete mode 100644 src/circuitpython_spike/shared-bindings/usdl2/__init__.h delete mode 100644 src/circuitpython_spike/shared-module/usdl2/__init__.c delete mode 100644 src/circuitpython_spike/shared-module/usdl2/__init__.h diff --git a/AGENTS.md b/AGENTS.md index e875f58..9f8c4ff 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,8 @@ Native MicroPython display **interface** modules for PyDevices `displaydev` (`US Portable code in `src/ports/common/`; SoC code under `src/ports//`; desktop SDL (`usdl2`) under `src/ports/desktop/usdl2/`. The `jpegio` JPEG decoder moved to micropython-pydevices' `modules/jpegio` (2026-10-06). CircuitPython: MCU -interfaces stay on stock CP; only `usdl2` uses `./apply_cp_patches.sh` (unix). +interfaces stay on stock CP; only `usdl2` builds there, on unix, as a user C module +(root `micropython.mk`). **Before editing lifecycle / soft-reset / a new board bring-up**, read: @@ -21,14 +22,13 @@ attr / soft-reset patterns when changing siblings. |------|----------| | `src/include/` | Public headers (`displayif/` subdir) | | `src/ports/` | Port C sources + per-port `micropython.mk` / `.cmake` (`common/`, `esp32/`, `desktop/usdl2/`, …) | -| `src/circuitpython_spike/` | CP unix spike for `usdl2` (copied by `apply_cp_patches.sh`) | | `docs/` | Markdown docs (root keeps `README.md` and `AGENTS.md` only) | | `docs/ports/` | Per-port notes moved from former `ports/*/README.md` | | `tests/` | Unit tests only (e.g. `test_lifecycle_api.py`) | | `tools/` | Developer / hardware smoke tests (`*_smoke.py`), `test_usdl2.py`, `sdl2_dev_env.sh` | Root `micropython.mk` / `micropython.cmake` stay at the repo root for `USER_C_MODULES`. -Desktop also: root `circuitpython.mk` + `apply_cp_patches.sh` (unix `usdl2` only). +CircuitPython reads the same root `micropython.mk` (unix `usdl2` only). ## Hard rules diff --git a/README.md b/README.md index b6181ef..bc9dd7e 100644 --- a/README.md +++ b/README.md @@ -106,8 +106,7 @@ user C module, `make -C ports/unix USER_C_MODULES=/path/to/displayif` from a CircuitPython 11 checkout, or micropython-pydevices' `./build_mp.py --interpreter circuitpython --port unix --modules displayif`. Only `usdl2` builds there; any other CircuitPython port refuses this module, -because boards there use CircuitPython's own `displayio`. (`./apply_cp_patches.sh` -still patches a CircuitPython tree the older way.) +because boards there use CircuitPython's own `displayio`. ### First run (unix `usdl2` smoke) diff --git a/UPSTREAM b/UPSTREAM index ec517e6..f448661 100644 --- a/UPSTREAM +++ b/UPSTREAM @@ -9,9 +9,10 @@ Org-pinned upstream (the ref CI builds against): v1.29.0 https://github.com/micropython/micropython @ v1.29.0 Verify locally with: git -C describe --tags # expect v1.29.0 -## CircuitPython (usdl2 desktop path only, via apply_cp_patches.sh) -Org oracle (same pin as audiodsp/CIRCUITPYTHON_ORACLE): 10.2.1 - https://github.com/adafruit/circuitpython @ bcfcb511352652d7cb62d3b415e4a624380f1830 +## CircuitPython (usdl2 desktop path only, as a user C module) +Tested against: 11.0.0-alpha.1 (CircuitPython 11 is the first line whose +USER_C_MODULES block matches MicroPython 1.29's) + https://github.com/adafruit/circuitpython @ 520805e12 ## SDL2 (desktop usdl2 backend) Minimum: >= 2.0 (SDL_* API used by src/ports/desktop/usdl2/) diff --git a/apply_cp_patches.sh b/apply_cp_patches.sh deleted file mode 100755 index aedea17..0000000 --- a/apply_cp_patches.sh +++ /dev/null @@ -1,514 +0,0 @@ -#!/usr/bin/env bash -# Apply (or preview) CircuitPython usdl2 integration patches from displayif (unix only). -# -# Out-of-tree substitute for Adafruit Extending CircuitPython (no upstream PR): -# Learn / design-guide step This script -# shared-bindings// copy spike → CP shared-bindings/usdl2/ -# shared-module// copy spike → CP shared-module/usdl2/ -# enable CIRCUITPY_* / mpconfig CIRCUITPY_USDL2 in variant mk + mpconfig -# list sources in port Makefile variant .mk SRC lists + SRC_PATTERNS -# build CircuitPython caller runs make (or workspace build_cp.sh) -# Conceptual refs: -# https://learn.adafruit.com/extending-circuitpython -# https://docs.circuitpython.org/en/latest/docs/design_guide.html -# -# Usage: -# ./apply_cp_patches.sh --dry-run [--port PORT] [--variant VARIANT] -# ./apply_cp_patches.sh --apply [--port PORT] [--variant VARIANT] -# ./apply_cp_patches.sh --status [--port PORT] [--variant VARIANT] -# -# Environment: -# CP_DIR CircuitPython tree (default: sibling circuitpython/ next to this repo) -# WORKSPACE_DIR Parent of displayif (default: parent of this repo) -# PORT Must be unix (default: unix); other ports skip with exit 0 -# VARIANT Unix variant (default: coverage) -# -# Standalone: clone circuitpython + displayif as siblings, then: -# ./apply_cp_patches.sh --apply --port unix --variant coverage -# cd ../circuitpython/ports/unix && make -j VARIANT=coverage - -set -euo pipefail - -DISPLAYIF_MOD_DIR=$(cd "$(dirname "$0")" && pwd) -USDL2_MOD_DIR="$DISPLAYIF_MOD_DIR" -WORKSPACE_DIR="${WORKSPACE_DIR:-$(cd "$DISPLAYIF_MOD_DIR/.." && pwd)}" - -PORT="${PORT:-unix}" -BOARD="${BOARD:-}" -VARIANT="${VARIANT:-coverage}" -MODE="" - -while [[ $# -gt 0 ]]; do - case "$1" in - --dry-run|--apply|--status|--force-apply) MODE="$1"; shift ;; - --port) PORT="$2"; shift 2 ;; - --board) BOARD="$2"; shift 2 ;; - --variant) VARIANT="$2"; shift 2 ;; - -h|--help) - sed -n '2,16p' "$0" - exit 0 - ;; - *) - echo "Unknown argument: $1 (try --help)" >&2 - exit 1 - ;; - esac -done - -MODE="${MODE:---dry-run}" -case "$MODE" in - --force-apply) MODE=--apply ;; - --dry-run|--apply|--status) ;; - *) - echo "Unknown mode: $MODE (try --help)" >&2 - exit 1 - ;; -esac - -# displayif usdl2 CircuitPython integration is unix-only. -if [[ "$PORT" != unix ]]; then - echo "displayif usdl2 apply_cp_patches: port=$PORT is not unix; skipping" - exit 0 -fi - -# Resolve CircuitPython: CP_DIR, else sibling circuitpython/ under WORKSPACE_DIR. -if [[ -n "${CP_DIR:-}" && -d "${CP_DIR}/ports" ]]; then - CP_DIR=$(cd "$CP_DIR" && pwd) -elif [[ -d "$WORKSPACE_DIR/circuitpython/ports" ]]; then - CP_DIR=$(cd "$WORKSPACE_DIR/circuitpython" && pwd) -else - echo "CircuitPython tree not found (set CP_DIR, or place circuitpython/ next to this repo under $WORKSPACE_DIR)." >&2 - exit 1 -fi -SPIKE_DIR="$DISPLAYIF_MOD_DIR/src/circuitpython_spike" -SPIKE_MANIFEST="$SPIKE_DIR/copy_manifest.txt" - -MARKER_TAG="displayif-usdl2 begin (apply_cp_patches.sh)" -MARKER_BEGIN="# >>> $MARKER_TAG" -MARKER_END="# >>> displayif-usdl2 end" - -markers_for_file() { - local file="$1" - case "$file" in - *.h) - echo "/* >>> $MARKER_TAG */" - echo "/* >>> displayif-usdl2 end */" - ;; - *) - echo "$MARKER_BEGIN" - echo "$MARKER_END" - ;; - esac -} - -repair_invalid_header_markers() { - local file="$1" - [ -f "$file" ] || return 0 - case "$file" in - *.h) ;; - *) return 0 ;; - esac - if ! grep -qF "# >>> displayif-usdl2" "$file"; then - return 0 - fi - if [ "$DRY_RUN" = 1 ]; then - echo " [dry-run] repair invalid # markers in $file" - return 0 - fi - python3 - "$file" "$MARKER_TAG" <<'PY' -import sys -from pathlib import Path - -path = Path(sys.argv[1]) -tag = sys.argv[2] -text = path.read_text() -text = text.replace(f"# >>> {tag}", f"/* >>> {tag} */") -text = text.replace("# >>> displayif-usdl2 end", "/* >>> displayif-usdl2 end */") -path.write_text(text) -PY - log " repaired header markers: $file" -} - -DRY_RUN=0 -# Nonzero if any dry-run check finds something --apply could not do. -DRY_RC=0 -APPLY=0 -if [ "$MODE" = "--dry-run" ]; then DRY_RUN=1; fi -if [ "$MODE" = "--apply" ]; then APPLY=1; fi - -log() { echo "$*"; } - -patch_block_present() { - local file="$1" - local needle="${2:-displayif-usdl2 begin}" - [ -f "$file" ] && grep -qF "$needle" "$file" -} - -insert_block_before_line() { - local file="$1" - local anchor="$2" - local block="$3" - local needle="${4:-displayif-usdl2 begin}" - repair_invalid_header_markers "$file" - if patch_block_present "$file" "$needle"; then - log " skip (already patched): $file" - return 0 - fi - if [ "$DRY_RUN" = 1 ]; then - if grep -qF "$anchor" "$file" 2>/dev/null; then - echo " [dry-run] insert block into $file before: $anchor" - else - # The apply path fails here when the anchor is gone. The dry run - # used to print the same line either way, so a moved upstream - # anchor - the one thing a pin move must stop on - was invisible - # until the apply. - echo " [dry-run] ERROR: anchor not found in $file: $anchor" - DRY_RC=1 - fi - return 0 - fi - local begin end - begin=$(markers_for_file "$file" | sed -n '1p') - end=$(markers_for_file "$file" | sed -n '2p') - python3 - "$file" "$anchor" "$begin" "$end" "$block" <<'PY' -import sys -from pathlib import Path - -path = Path(sys.argv[1]) -anchor = sys.argv[2] -begin = sys.argv[3] -end = sys.argv[4] -block = sys.argv[5] - -text = path.read_text() -if begin in text: - sys.exit(0) -if anchor not in text: - raise SystemExit(f"anchor not found in {path}: {anchor!r}") -insert = f"\n{begin}\n{block}\n{end}\n" -path.write_text(text.replace(anchor, insert + anchor, 1)) -PY -} - -insert_block_after_line() { - local file="$1" - local anchor="$2" - local block="$3" - local needle="${4:-displayif-usdl2 begin}" - repair_invalid_header_markers "$file" - if patch_block_present "$file" "$needle"; then - log " skip (already patched): $file" - return 0 - fi - if [ "$DRY_RUN" = 1 ]; then - if grep -qF "$anchor" "$file" 2>/dev/null; then - echo " [dry-run] insert block into $file after: $anchor" - else - # The apply path fails here when the anchor is gone. The dry run - # used to print the same line either way, so a moved upstream - # anchor - the one thing a pin move must stop on - was invisible - # until the apply. - echo " [dry-run] ERROR: anchor not found in $file: $anchor" - DRY_RC=1 - fi - return 0 - fi - local begin end - begin=$(markers_for_file "$file" | sed -n '1p') - end=$(markers_for_file "$file" | sed -n '2p') - python3 - "$file" "$anchor" "$begin" "$end" "$block" <<'PY' -import sys -from pathlib import Path - -path = Path(sys.argv[1]) -anchor = sys.argv[2] -begin = sys.argv[3] -end = sys.argv[4] -block = sys.argv[5] - -text = path.read_text() -if begin in text: - sys.exit(0) -if anchor not in text: - raise SystemExit(f"anchor not found in {path}: {anchor!r}") -insert = f"\n{begin}\n{block}\n{end}\n" -path.write_text(text.replace(anchor, anchor + insert, 1)) -PY -} - -insert_raw_after_line() { - local file="$1" - local anchor="$2" - local line="$3" - if grep -qF "$line" "$file" 2>/dev/null; then - log " skip (already present): $file" - return 0 - fi - if [ "$DRY_RUN" = 1 ]; then - if grep -qF "$anchor" "$file" 2>/dev/null; then - echo " [dry-run] insert into $file after: $anchor" - else - # The apply path fails here when the anchor is gone. The dry run - # used to print the same line either way, so a moved upstream - # anchor - the one thing a pin move must stop on - was invisible - # until the apply. - echo " [dry-run] ERROR: anchor not found in $file: $anchor" - DRY_RC=1 - fi - return 0 - fi - python3 - "$file" "$anchor" "$line" <<'PY' -import sys -from pathlib import Path - -path = Path(sys.argv[1]) -anchor = sys.argv[2] -line = sys.argv[3] - -text = path.read_text() -if line in text: - sys.exit(0) -if anchor not in text: - raise SystemExit(f"anchor not found in {path}: {anchor!r}") -path.write_text(text.replace(anchor, anchor + "\n" + line, 1)) -PY -} - -copy_spike_files() { - python3 - "$SPIKE_DIR" "$CP_DIR" "$SPIKE_MANIFEST" "$DRY_RUN" <<'PY' -import filecmp -import shutil -import sys -from pathlib import Path - -spike_dir, cp_dir, manifest, dry = sys.argv[1:5] -dry_run = dry == "1" - -def copy_one(rel_dir: str, filename: str) -> None: - rel = f"{rel_dir}/{filename}" - src = Path(spike_dir) - dst = Path(cp_dir) - for part in rel_dir.split("/"): - src /= part - dst /= part - src /= filename - dst /= filename - if not src.is_file(): - raise SystemExit(f"missing spike file: {src}") - if dst.is_file() and filecmp.cmp(src, dst, shallow=False): - print(f" unchanged: {rel}") - return - if dry_run: - verb = "update" if dst.is_file() else "create" - print(f" [dry-run] {verb} {rel}") - return - dst.parent.mkdir(parents=True, exist_ok=True) - shutil.copy2(src, dst) - print(f" copied: {rel}") - -for raw in Path(manifest).read_text().splitlines(): - line = raw.split("#", 1)[0].strip() - if not line: - continue - rel_dir, filename = line.split("\t", 1) - copy_one(rel_dir.strip(), filename.strip()) -PY -} - -if [ ! -f "$SPIKE_MANIFEST" ]; then - echo "Missing spike manifest: $SPIKE_MANIFEST" >&2 - exit 1 -fi - -PORT_DIR="$CP_DIR/ports/$PORT" -VARIANT_MK="$PORT_DIR/variants/$VARIANT/mpconfigvariant.mk" -VARIANT_H="$PORT_DIR/variants/$VARIANT/mpconfigvariant.h" -DEFNS_MK="$CP_DIR/py/circuitpy_defns.mk" -MPCONFIG_MK="$CP_DIR/py/circuitpy_mpconfig.mk" -PORT_MK="$PORT_DIR/Makefile" - -USDL2_MOD_REL=$(python3 -c "import os; print(os.path.relpath('$USDL2_MOD_DIR', '$PORT_DIR'))") - -log "CircuitPython: $CP_DIR" -log "workspace: $WORKSPACE_DIR" -log "displayif/usdl2: $USDL2_MOD_DIR (as $USDL2_MOD_REL from port)" -log "variant: $VARIANT" -log "mode: $MODE" -log - -if [ "$MODE" = "--status" ]; then - SPIKE_INIT_C=$(python3 - "$SPIKE_MANIFEST" "$CP_DIR" <<'PY' -import sys -from pathlib import Path - -manifest, cp_dir = sys.argv[1:3] -rel_dir, filename = Path(manifest).read_text().splitlines()[0].split("\t", 1) -p = Path(cp_dir) -for part in rel_dir.split("/"): - p /= part -p /= filename.strip() -print(p) -PY -) - # Anything not in the state --apply would leave behind makes --status exit - # nonzero. It used to report the defect and exit 0, so a caller that - # checked $? read a broken tree as a good one - found by planting a fault - # in the pin move's step-0 rehearsal, 2026-09-09. - STATUS_RC=0 - report() { - local label="$1" - local file="$2" - if [ ! -e "$file" ]; then - echo "missing $file"; STATUS_RC=1 - elif [ "$label" = "spike" ]; then - echo "ok $file" - elif patch_block_present "$file"; then - echo "patched $file" - else - echo "pending $file"; STATUS_RC=1 - fi - } - report spike "$SPIKE_INIT_C" - report patch "$VARIANT_MK" - if [ -f "$VARIANT_H" ]; then - report patch "$VARIANT_H" - fi - report patch "$DEFNS_MK" - report patch "$MPCONFIG_MK" - report patch "$PORT_MK" - exit $STATUS_RC -fi - -log "==> Copy spike templates" -copy_spike_files -log - -USDL2_ENABLE_BLOCK="CIRCUITPY_USDL2 = 1 -CFLAGS += -DCIRCUITPY_USDL2=1" - -variant_mk_anchor() { - if [ "$VARIANT" = "coverage" ]; then - if [ -f "$VARIANT_MK" ] && grep -qF '# >>> lv-circuitpython-mod end' "$VARIANT_MK"; then - echo "# >>> lv-circuitpython-mod end" - else - echo "CIRCUITPY_MESSAGE_COMPRESSION_LEVEL = 1" - fi - elif [ "$VARIANT" = "standard" ]; then - echo 'FROZEN_MANIFEST ?= $(VARIANT_DIR)/manifest.py' - else - echo $'-DCIRCUITPY_LOCALE=1 \\' - fi -} - -variant_mk_binding_anchor() { - if grep -qF $'\tshared-bindings/lvgl/__init__.c \\' "$VARIANT_MK"; then - echo $'\tshared-bindings/lvgl/__init__.c \\' - elif grep -qF $'\tshared-bindings/pygraphics/__init__.c \\' "$VARIANT_MK"; then - echo $'\tshared-bindings/pygraphics/__init__.c \\' - else - echo $'\tshared-bindings/jpegio/JpegDecoder.c \\' - fi -} - -variant_mk_module_anchor() { - if grep -qF $'\tshared-module/lvgl/__init__.c \\' "$VARIANT_MK"; then - echo $'\tshared-module/lvgl/__init__.c \\' - elif grep -qF $'\tshared-module/pygraphics/__init__.c \\' "$VARIANT_MK"; then - echo $'\tshared-module/pygraphics/__init__.c \\' - else - echo $'\tshared-module/jpegio/JpegDecoder.c \\' - fi -} - -variant_h_anchor() { - if grep -qF '/* >>> lv-circuitpython-mod end */' "$VARIANT_H"; then - echo '/* >>> lv-circuitpython-mod end */' - else - echo '#include "../mpconfigvariant_common.h"' - fi -} - -mpconfig_anchor() { - if grep -qF '# >>> lv-circuitpython-mod end' "$MPCONFIG_MK" \ - && grep -qF 'CIRCUITPY_LVGL' "$MPCONFIG_MK"; then - echo "# >>> lv-circuitpython-mod end" - elif grep -qF 'CFLAGS += -DCIRCUITPY_LVGL=$(CIRCUITPY_LVGL)' "$MPCONFIG_MK"; then - echo 'CFLAGS += -DCIRCUITPY_LVGL=$(CIRCUITPY_LVGL)' - else - echo 'CFLAGS += -DCIRCUITPY_LOCALE=$(CIRCUITPY_LOCALE)' - fi -} - -port_mk_anchor() { - if grep -qF '# >>> lv-circuitpython-mod end' "$PORT_MK"; then - echo '# >>> lv-circuitpython-mod end' - elif grep -qF 'include ../../py/mkenv.mk' "$PORT_MK"; then - echo 'include ../../py/mkenv.mk' - else - echo 'include ../../py/circuitpy_mkenv.mk' - fi -} - -log "==> Patch unix variant mpconfigvariant.mk (enable flag)" -if [ ! -f "$VARIANT_MK" ]; then - echo "Variant makefile not found: $VARIANT_MK" >&2 - exit 1 -fi -insert_block_after_line "$VARIANT_MK" "$(variant_mk_anchor)" "$USDL2_ENABLE_BLOCK" -log - -log "==> Patch unix variant mpconfigvariant.mk (module sources)" -insert_raw_after_line "$VARIANT_MK" "$(variant_mk_binding_anchor)" $'\tshared-bindings/usdl2/__init__.c \\' -insert_raw_after_line "$VARIANT_MK" "$(variant_mk_module_anchor)" $'\tshared-module/usdl2/__init__.c \\' -log "==> Patch unix variant mpconfigvariant.h (ifndef guard)" -if [ -f "$VARIANT_H" ]; then - VARIANT_H_BLOCK="#ifndef CIRCUITPY_USDL2 -#define CIRCUITPY_USDL2 (0) -#endif" - insert_block_after_line "$VARIANT_H" "$(variant_h_anchor)" "$VARIANT_H_BLOCK" -fi -log - -log "==> Patch py/circuitpy_mpconfig.mk (default off)" -MPCONFIG_BLOCK="CIRCUITPY_USDL2 ?= 0 -CFLAGS += -DCIRCUITPY_USDL2=\$(CIRCUITPY_USDL2)" -insert_block_after_line "$MPCONFIG_MK" "$(mpconfig_anchor)" "$MPCONFIG_BLOCK" -log - -log "==> Patch py/circuitpy_defns.mk" -DEFNS_PATTERNS_BLOCK="ifeq (\$(CIRCUITPY_USDL2),1) -SRC_PATTERNS += usdl2/% -endif" -if grep -qF '# >>> lv-circuitpython-mod end' "$DEFNS_MK"; then - insert_block_after_line "$DEFNS_MK" "# >>> lv-circuitpython-mod end" "$DEFNS_PATTERNS_BLOCK" "SRC_PATTERNS += usdl2/%" -else - insert_block_before_line "$DEFNS_MK" "ifeq (\$(CIRCUITPY_MATH),1)" "$DEFNS_PATTERNS_BLOCK" "SRC_PATTERNS += usdl2/%" -fi - -if grep -qF $'\tlvgl/__init__.c \\' "$DEFNS_MK"; then - insert_raw_after_line "$DEFNS_MK" $'\tlvgl/__init__.c \\' $'\tusdl2/__init__.c \\' -elif grep -qF $'\tpygraphics/__init__.c \\' "$DEFNS_MK"; then - insert_raw_after_line "$DEFNS_MK" $'\tpygraphics/__init__.c \\' $'\tusdl2/__init__.c \\' -else - insert_raw_after_line "$DEFNS_MK" $'\tjpegio/JpegDecoder.c \\' $'\tusdl2/__init__.c \\' -fi -log - -log "==> Patch port Makefile (circuitpython.mk)" -PORT_BLOCK="USDL2_MOD_DIR := \$(abspath $USDL2_MOD_REL) -include \$(USDL2_MOD_DIR)/circuitpython.mk" -insert_block_after_line "$PORT_MK" "$(port_mk_anchor)" "$PORT_BLOCK" -log - -if [ "$DRY_RUN" = 1 ]; then - log "Dry run complete. Re-run with --apply to write changes." - [ "$DRY_RC" = 0 ] || log "Dry run reported at least one ERROR above." - exit "$DRY_RC" -elif [ "$APPLY" = 1 ]; then - log "Patches applied." - log - log "Next:" - log " cd $PORT_DIR && make -j VARIANT=$VARIANT" - log " $PORT_DIR/build-$VARIANT/micropython $USDL2_MOD_DIR/tools/test_usdl2.py" - log "For an easier way to build with other extensions, see the org's optional aggregator workspace: https://github.com/PyDevices/cmods" -fi diff --git a/circuitpython.mk b/circuitpython.mk deleted file mode 100644 index 46a7a0d..0000000 --- a/circuitpython.mk +++ /dev/null @@ -1,47 +0,0 @@ -# CircuitPython build glue for displayif usdl2 (unix-only SDL2 subset). -# -# Include from circuitpython/ports/unix/Makefile after setting USDL2_MOD_DIR -# to this repo root (displayif/): -# -# USDL2_MOD_DIR := $(abspath ../../../displayif) -# include $(USDL2_MOD_DIR)/circuitpython.mk -# -# Requires libsdl2-dev (pkg-config sdl2). Only include on unix port. -# -# Prefer caller-set USDL2_MOD_DIR. Do not use a lazy MAKEFILE_LIST default for -# QSTR_DEFS (see pygraphics/circuitpython.mk). - -ifndef USDL2_MOD_DIR - USDL2_MOD_DIR := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))) -endif - -USDL2_DIR := $(USDL2_MOD_DIR)/src/ports/desktop/usdl2 - -SDL2_CFLAGS := $(shell pkg-config --cflags sdl2 2>/dev/null) -SDL2_LIBS := $(shell pkg-config --libs sdl2 2>/dev/null) - -ifeq ($(SDL2_CFLAGS),) -$(error pkg-config sdl2 failed — install libsdl2-dev) -endif - -# __revision__: the same git describe micropython.mk passes, so usdl2 on -# CircuitPython reports which displayif it was built from too. -DISPLAYIF_REVISION := $(shell git -C $(USDL2_MOD_DIR) describe --always --dirty --abbrev=7 2>/dev/null || echo unknown) -CFLAGS += $(SDL2_CFLAGS) -I$(USDL2_DIR) -I$(USDL2_MOD_DIR)/src/include \ - -DCIRCUITPY_USDL2=1 -DDISPLAYIF_WRAP_GC_SWEEP=1 -DDISPLAYIF_WRAP_MP_DEINIT=1 \ - -DDISPLAYIF_REVISION='"$(DISPLAYIF_REVISION)"' -LDFLAGS += $(SDL2_LIBS) -Wl,--wrap=gc_sweep_all -Wl,--wrap=mp_deinit - -QSTR_DEFS += $(USDL2_DIR)/usdl2_qstrdefs.h - -USDL2_SOURCES := \ - $(USDL2_DIR)/usdl2_mp.c \ - $(USDL2_MOD_DIR)/src/ports/common/soft_reset.c \ - $(USDL2_MOD_DIR)/src/ports/common/build.c - -USDL2_SUPPRESS_CFLAGS := -Wno-sign-compare -Wno-unused-parameter -Wno-shadow -$(foreach _usdl,$(USDL2_SOURCES),$(eval $(BUILD)/$(_usdl:.c=.o): CFLAGS += $(USDL2_SUPPRESS_CFLAGS))) - -$(BUILD)/shared-bindings/usdl2/%.o: CFLAGS += $(USDL2_SUPPRESS_CFLAGS) - -SRC_C += $(USDL2_SOURCES) diff --git a/docs/idempotent-lifecycle.md b/docs/idempotent-lifecycle.md index 0cf08de..efaf97f 100644 --- a/docs/idempotent-lifecycle.md +++ b/docs/idempotent-lifecycle.md @@ -99,7 +99,7 @@ Verify with grep if unsure (`displayif_register_soft_reset`, `*_host_teardown`). | `rgbmatrix` | yes | yes | yes (Protomatter) | PM core in BSS; bitbang path has no host IRQs | | `usdl2` desktop | via `SDL_Quit` | n/a (module) | yes | Stops SDL timers (`malloc` entries), `SDL_Quit`; MP unix/windows + CP unix | -Shared: `src/include/displayif/soft_reset.h` + `src/ports/common/soft_reset.c` (`--wrap=gc_sweep_all` primary; `--wrap=mp_deinit` idempotent second pass). ESP32 also implements `displayif_port_pre_gc_sweep()` to stop `machine.Timer` before the sweep. When initialized LVGL is linked, the common pre-GC path weakly checks `lv_is_initialized()` and calls `lv_deinit()` after timers stop and before display hardware teardown, while LVGL's GC-backed global root is still valid. Desktop `usdl2` links the same wraps from `src/ports/desktop/usdl2/micropython.mk` / root `circuitpython.mk`. +Shared: `src/include/displayif/soft_reset.h` + `src/ports/common/soft_reset.c` (`--wrap=gc_sweep_all` primary; `--wrap=mp_deinit` idempotent second pass). ESP32 also implements `displayif_port_pre_gc_sweep()` to stop `machine.Timer` before the sweep. When initialized LVGL is linked, the common pre-GC path weakly checks `lv_is_initialized()` and calls `lv_deinit()` after timers stop and before display hardware teardown, while LVGL's GC-backed global root is still valid. Desktop `usdl2` links the same wraps from `src/ports/desktop/usdl2/micropython.mk`, on MicroPython and CircuitPython alike. --- diff --git a/docs/newcomers.md b/docs/newcomers.md index dab1137..61f02cc 100644 --- a/docs/newcomers.md +++ b/docs/newcomers.md @@ -35,8 +35,8 @@ displayif backend. CircuitPython already provides its MCU display interfaces; use its stock firmware and the CircuitPython board configs for those targets. The exception -is desktop `usdl2`, which displayif can add to CircuitPython unix through -`apply_cp_patches.sh`. +is desktop `usdl2`, which displayif adds to CircuitPython unix as a user C +module (`make -C ports/unix USER_C_MODULES=/path/to/displayif`). For MicroPython Make ports, `USER_C_MODULES` points at the workspace parent containing this repository. For CMake ports such as ESP32 and RP2, it points diff --git a/src/circuitpython_spike/copy_manifest.txt b/src/circuitpython_spike/copy_manifest.txt deleted file mode 100644 index 6454e73..0000000 --- a/src/circuitpython_spike/copy_manifest.txt +++ /dev/null @@ -1,4 +0,0 @@ -shared-bindings/usdl2 __init__.c -shared-bindings/usdl2 __init__.h -shared-module/usdl2 __init__.c -shared-module/usdl2 __init__.h diff --git a/src/circuitpython_spike/shared-bindings/usdl2/__init__.c b/src/circuitpython_spike/shared-bindings/usdl2/__init__.c deleted file mode 100644 index abb262d..0000000 --- a/src/circuitpython_spike/shared-bindings/usdl2/__init__.c +++ /dev/null @@ -1,23 +0,0 @@ -// Copy to circuitpython/shared-bindings/usdl2/__init__.c -// -// CircuitPython module registration (jpegio pattern). Implementation and binding fun objs live in -// displayif/src/ports/desktop/usdl2/usdl2_mp.c; MicroPython registers there. - -#include "py/obj.h" -#include "py/runtime.h" -#include "usdl2.h" - -//| """Unix-only SDL2 subset for PyDevices displaydev (linked against libSDL2).""" - -static const mp_rom_map_elem_t usdl2_module_globals_table[] = { -#include "usdl2_module_globals.inc" -}; - -static MP_DEFINE_CONST_DICT(usdl2_module_globals, usdl2_module_globals_table); - -const mp_obj_module_t usdl2_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&usdl2_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_usdl2, usdl2_module); diff --git a/src/circuitpython_spike/shared-bindings/usdl2/__init__.h b/src/circuitpython_spike/shared-bindings/usdl2/__init__.h deleted file mode 100644 index 4def44e..0000000 --- a/src/circuitpython_spike/shared-bindings/usdl2/__init__.h +++ /dev/null @@ -1,7 +0,0 @@ -// Copy to circuitpython/shared-bindings/usdl2/__init.h - -#pragma once - -#include "py/obj.h" - -extern const mp_obj_module_t usdl2_module; diff --git a/src/circuitpython_spike/shared-module/usdl2/__init__.c b/src/circuitpython_spike/shared-module/usdl2/__init__.c deleted file mode 100644 index c2e79bc..0000000 --- a/src/circuitpython_spike/shared-module/usdl2/__init__.c +++ /dev/null @@ -1,6 +0,0 @@ -// Copy to circuitpython/shared-module/usdl2/__init__.c - -#include "shared-module/usdl2/__init__.h" - -void shared_modules_usdl2_init(void) { -} diff --git a/src/circuitpython_spike/shared-module/usdl2/__init__.h b/src/circuitpython_spike/shared-module/usdl2/__init__.h deleted file mode 100644 index 1ee971a..0000000 --- a/src/circuitpython_spike/shared-module/usdl2/__init__.h +++ /dev/null @@ -1,5 +0,0 @@ -// Copy to circuitpython/shared-module/usdl2/__init.h - -#pragma once - -void shared_modules_usdl2_init(void); diff --git a/src/ports/desktop/usdl2/usdl2_mp.c b/src/ports/desktop/usdl2/usdl2_mp.c index d900466..21c9768 100644 --- a/src/ports/desktop/usdl2/usdl2_mp.c +++ b/src/ports/desktop/usdl2/usdl2_mp.c @@ -1340,10 +1340,9 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(SDL_GetDesktopDisplayMode_fun_obj, 1, 2, SDL // --- Module registration --- // -// Here for MicroPython, and for CircuitPython built through USER_C_MODULES -// (micropython.mk passes DISPLAYIF_USER_C_MODULE). A CircuitPython tree patched -// by apply_cp_patches.sh registers it from shared-bindings/usdl2/__init__.c -// instead. +// Here for MicroPython and for CircuitPython. CircuitPython's unix port sets +// CIRCUITPY, so micropython.mk passes DISPLAYIF_USER_C_MODULE to say this +// build is the user C module one. #if !CIRCUITPY || DISPLAYIF_USER_C_MODULE