Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cursor/frozen-self-installer-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Private design scratchpad (not for RTD).

- [ ] Warn users where downloads come from: PyDevices micropython-lib MIP index (`https://PyDevices.github.io/micropython-lib/mip/PyDevices`), not the [official MicroPython micropython-lib](https://github.com/micropython/micropython-lib) package index — **maintainer-published**, not an endorsed upstream source (show URL on first run / in GUI)
- [ ] Freeze a small bootstrap module into desktop MicroPython builds so `from <xyz> import <clever_install_fn>` works out of the box
- [ ] Install or refresh all 4 core modules (`displaysys`, `eventsys`, `graphics`, `multimer`) via `mip` / `lib_install`-style fetch (skip re-download when up to date?)
- [ ] Install or refresh all 3 core modules (`displaysys`, `eventsys`, `graphics`, `multimer`) via `mip` / `lib_install`-style fetch (skip re-download when up to date?)
- [ ] Post-install GUI (TBD: terminal menu vs minimal on-display UI): download more files, system/platform info, `lv_test_timer_*`-style sanity checks, link to `spotapi_remote` / spotapi
- [ ] Flesh out scope, module name, and UX (name the import, entry points, error handling offline)

Expand Down
6 changes: 3 additions & 3 deletions .cursor/rules/graphics-parity-and-timer-async.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ alwaysApply: true

Active objectives for pydisplay + `cmods/graphics` work. Complements global `fix-not-workaround` rule.

## Graphics-cmod ↔ `src/lib/graphics`
## Graphics-cmod ↔ sibling `graphics/lib/graphics`

- **Goal:** 100% behavioral parity with `src/lib/graphics` (API, kwargs, duck typing, BMP565, Draw, Font, file I/O).
- **Goal:** 100% behavioral parity with [PyDevices/graphics](https://github.com/PyDevices/graphics) `lib/graphics/` (API, kwargs, duck typing, BMP565, Draw, Font, file I/O).
- When a difference is found (matrix failure, code review, or side-by-side with Python), **fix it in the cmod immediately** — do not defer or patch callers in pydisplay examples.
- Reference implementation is Python under `src/lib/graphics/`; cmod must match the contract, not a subset.
- Reference implementation is the pure-Python package under `graphics/lib/graphics/`; cmod must match the contract, not a subset.
- MP and CP share bindings (`gfx_bindings_mp.c`, `gfx_module_mp.c`); fix both runtimes when the gap is in shared code.
- Rebuild MP/CP after cmod changes; re-run affected examples before moving on.

Expand Down
8 changes: 4 additions & 4 deletions .cursor/testpypi-naming-convention.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ PyDevices publishes **CPython wheels to TestPyPI only** (not production PyPI). E
| Role | Example | Used by |
|------|---------|---------|
| **MIP / micropython-lib** | `graphics` | `mip.install("graphics", index=…)`, manifest `package("graphics")` |
| **pip / TestPyPI project** | `pydisplay-graphics` | `pip install … pydisplay-graphics`, hatch `[project].name` |
| **pip / TestPyPI project** | `graphics-py` | `pip install … graphics-py`, hatch `[project].name` |
| **Python import** | `graphics` | `import graphics` in application code |

MIP names stay short. pip project names must **not collide with [pypi.org](https://pypi.org)** — TestPyPI rejects sdists when the normalized name is already registered there (wheels may still upload; treat collisions as errors).
Expand All @@ -29,7 +29,7 @@ When the MIP name is **taken on pypi.org**, prefix with `pydisplay-`:

| MIP name | pip / TestPyPI | Import | Why |
|----------|----------------|--------|-----|
| `graphics` | **`pydisplay-graphics`** | `graphics` | [pypi.org/project/graphics](https://pypi.org/project/graphics) exists |
| `graphics` | **`graphics-py`** | `graphics` | [pypi.org/project/graphics](https://pypi.org/project/graphics) exists |

Mapping lives in `pypi_publish_name()` in [`scripts/publish_sync_packages.sh`](../scripts/publish_sync_packages.sh). MIP and source trees keep the short name `graphics/`.

Expand All @@ -44,7 +44,7 @@ Use a **repo-specific suffix** so pip names are unique and intent is obvious:

Do **not** publish as bare `lvgl` — [pypi.org/project/lvgl](https://pypi.org/project/lvgl) exists.

`graphics-cmod` and `pydisplay-graphics` both provide `import graphics`; prefer **`graphics-cmod`** on desktop/Android when the native wheel matches the platform, and **`pydisplay-graphics`** for pure-Python-only or cross-check installs.
`graphics-cmod` and `graphics-py` (both from [PyDevices/graphics](https://github.com/PyDevices/graphics)) provide `import graphics`; prefer **`graphics-cmod`** on desktop/Android when the native wheel matches the platform, and **`graphics-py`** for pure-Python-only or cross-check installs.

### 4. displaysys is one package

Expand Down Expand Up @@ -82,7 +82,7 @@ After a version is on TestPyPI, **do not rename** the project (TestPyPI rejects
| `displaysys` | `displaysys` | `displaysys`, `board_config` | free |
| `eventsys` | `eventsys` | `eventsys` | free |
| `multimer` | `multimer` | `multimer` | free |
| `graphics` | **`pydisplay-graphics`** | `graphics` | **taken** → mapped |
| `graphics` | **`graphics-py`** | `graphics` | **taken** → mapped |

### Sibling repos (own workflows)

Expand Down
13 changes: 8 additions & 5 deletions .cursor/testpypi-publish-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Audited **2026-07-08** from local clones under `~/github/cmods` and `~/github/py
| `displaysys` | `displaysys-0.0.7-py2.py3-none-any.whl` | universal; full tree + `board_config.py` |
| `eventsys` | `eventsys-0.0.7-py2.py3-none-any.whl` | universal |
| `multimer` | `multimer-0.0.7-py2.py3-none-any.whl` | universal |
| `pydisplay-graphics` | `pydisplay_graphics-0.0.7-py2.py3-none-any.whl` | universal (PyPI name mapped from `graphics`) |
| `graphics-py` | `pydisplay_graphics-0.0.7-py2.py3-none-any.whl` | universal (PyPI name mapped from `graphics`) |

**Layout:** `displaysys` is the full package (all modules under `src/lib/displaysys/` plus `board_config.py`). Per-backend `displaysys-*` packages are **not** published. Published packages do not include `examples/` trees.

Expand Down Expand Up @@ -57,9 +57,12 @@ Both use the same shape: matrix `ubuntu-latest` + `windows-latest`, plus a dedic

## usdl2

- **Workflow:** `usdl2/.github/workflows/publish-testpypi.yml`
- **Package:** pure-Python ctypes shim (`py3-none-any`); native code is the **MicroPython user C module** built into firmware, not a CPython wheel
- **Linux / Windows / Android:** one universal wheel is intentional — Android/desktop load `libSDL2.so` / `SDL2.dll` at runtime via ctypes
- **Workflows:** `usdl2/.github/workflows/publish-testpypi.yml` (native cibuildwheel);
`publish-micropython-lib.yml` (TestPyPI `usdl2-py` + MIP)
- **Native package:** platform wheels (`manylinux`, `win_amd64`, `android_21_*`) —
CPython extension + MicroPython/CircuitPython usermod
- **Pure Python:** `usdl2-py` / MIP `usdl2` from `lib/usdl2.py` (ctypes/ffi fallback);
same `vX.Y.Z` as native
- **Tags on GitHub:** semver release tags (`v*.*.*`) exist; **no GitHub Release** objects

## Repos without TestPyPI automation
Expand All @@ -81,7 +84,7 @@ Both use the same shape: matrix `ubuntu-latest` + `windows-latest`, plus a dedic
| Category | Status |
|----------|--------|
| **Native CPython extensions** (`lvgl-cpython`, `graphics-cmod`) | **Met** — CI builds linux + windows + android wheels |
| **Pure pydisplay libs** (`displaysys`, `eventsys`, `multimer`, `pydisplay-graphics`) | **Met by design** — universal wheels; manifest `require()` graph in § Pip dependency graph |
| **Pure pydisplay libs** (`displaysys`, `eventsys`, `multimer`, `graphics-py`) | **Met by design** — universal wheels; manifest `require()` graph in § Pip dependency graph |
| **usdl2** | **Met for CPython shim** — universal wheel; MP cmod is separate |
| **displayif** | **N/A** — firmware-only user C module, not a pip/MIP package |
| **displaysys-* backends** | **Removed** — use full `displaysys` only |
Expand Down
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
src/add_ons/gui

# Generated from src/add_ons/framebuf.py (scripts/install_sync_framebuf.py; publish + tests)
src/lib/graphics/framebuf.py

# Optional local/dev copy of LVGL wasm wheels (not required; pyodide uses TestPyPI)
web/pyscript/wheels
Expand Down Expand Up @@ -216,3 +214,7 @@ cython_debug/

.ruff_cache/


# CI / local sibling checkouts inside this workspace
graphics/
micropython-hardware/
19 changes: 9 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,15 @@ is a symlink to `../../src`, so editing `src/` updates the PyScript gallery too.
- Known pre-existing example failures on CPython (not environment issues to
"fix"): `nano_gui_simpletest` needs the matching Hinch `gui/` package.
`tools/png_test.py` in **pdwidgets** (PNG probe) needs `PDWIDGETS_PNG_DIR` / material-design-icons and a sibling pydisplay checkout.
- **`palettes` / `pdwidgets` sibling repos** (`hello`, `color_test`, `feathers`,
`graphics_simpletest`, `palettes_demo`, `widgets_*`, … import `palettes` and/or
`pdwidgets`). These are source-only PyDevices repos, **not** pip packages — the
PyPI project literally named `palettes` is an unrelated "random hex color"
library, so do **not** `pip install palettes`. They normally arrive as
repositoryDependencies under `/agent/repos/{palettes,pdwidgets}` (symlinked into
`~/gh/pydevices/`); if that clone is missing, clone
`github.com/PyDevices/{palettes,pdwidgets}` into a writable dir and put their
`src` dirs on the venv path (e.g. a `*.pth` in `.venv/lib/*/site-packages`
listing `<repo>/palettes/src` and `<repo>/pdwidgets/src`, or `PYTHONPATH`).
- **Sibling pure-Python repos** (`palettes`, `pdwidgets`, `graphics`, `usdl2`
under `lib/`). Examples that `import palettes` / `pdwidgets` / `graphics` /
the ctypes `usdl2` fallback need those `lib/` dirs on path. The PyPI project
literally named `palettes` is an unrelated "random hex color" library — do
**not** `pip install palettes`. Prefer native TestPyPI wheels for `graphics`
(`graphics-cmod`) and `usdl2` when available. Clones normally live under
`/agent/repos/{…}` (symlinked into `~/gh/pydevices/`); if missing, clone
`github.com/PyDevices/<name>` and put `<repo>/lib` on the venv path
(`.pth` / `PYTHONPATH`).
Quick setup: `bash scripts/setup_sibling_repos.sh` (clones current `main` and
writes `.pth` files). The example harness (`tools/sibling_repos.py`) auto-discovers
the same paths for matrix runs.
Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ flowchart TB
| **`eventsys`** | `Runtime` pumps input and dispatches PyGame/SDL2-style events to callbacks; prefer `runtime.on(...)` + `runtime.run_forever()`. |
| **`graphics`** | Optional helpers on top of `framebuf` (rounded rects, gradients, `Area` bounding boxes). |
| **`multimer`** | Cross-platform `Timer` / `AsyncTimer`, ticks/sleep, and `asyncio` exposure. |
| **`add_ons`** | Optional shims and integrations (`framebuf` on CPython, `displaybuf`, `pdwidgets`, config templates). |
| **`add_ons`** | Optional shims and integrations (`displaybuf`, config templates, GUI fetch helpers). |

## Typical boot sequence

Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/displays.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ For microcontrollers on **MicroPython and CircuitPython**.

SDL2 desktop backend (CPython, MicroPython Unix, CircuitPython Unix). Uses an SDL texture as GRAM. It is the default on MicroPython Unix and available on CPython via `board_configs/sdldisplay/`.

SDL2 bindings for **`SDLDisplay`**: native frozen **`usdl2`** when built in, else **`add_ons/usdl2.py`** on ``sys.path``. Build **`usdl2`** from [PyDevices/usdl2](https://github.com/PyDevices/usdl2) for frozen MicroPython and CircuitPython Unix — see [MicroPython — usdl2](../platforms/micropython.md#usdl2-native-sdl2).
SDL2 bindings for **`SDLDisplay`**: native **`usdl2`** when built in or installed from TestPyPI, else pure-Python **`usdl2-py`** / MIP ``usdl2`` from [PyDevices/usdl2](https://github.com/PyDevices/usdl2) (`lib/usdl2.py`). Build the native module for frozen MicroPython and CircuitPython Unix — see [MicroPython — usdl2](../platforms/micropython.md#usdl2-native-sdl2).

### PGDisplay

Expand Down
6 changes: 3 additions & 3 deletions docs/concepts/multimer.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,10 @@ Desktop SDL2 access is shared between display and timer code:

| Consumer | Import chain |
|----------|--------------|
| `displaysys.sdldisplay` | built-in `usdl2` → `add_ons/usdl2.py` |
| `multimer` SDL backend | `usdl2` (native or `add_ons/usdl2.py`) |
| `displaysys.sdldisplay` | built-in `usdl2` → pure-Python `usdl2-py` |
| `multimer` SDL backend | `usdl2` (native or `usdl2-py`) |

Both prefer the native **`usdl2`** module when it is frozen or built into the interpreter. See [Displays — SDLDisplay](displays.md#sdldisplay) and [MicroPython — usdl2](../platforms/micropython.md#usdl2-native-sdl2).
Both prefer the native **`usdl2`** module when it is frozen, built in, or installed from TestPyPI. Otherwise the pure-Python package from [PyDevices/usdl2](https://github.com/PyDevices/usdl2) (`lib/usdl2.py`) is used. See [Displays — SDLDisplay](displays.md#sdldisplay) and [MicroPython — usdl2](../platforms/micropython.md#usdl2-native-sdl2).

## Next

Expand Down
13 changes: 11 additions & 2 deletions docs/installation/mip-github.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ Install source `.py` files directly from the pydisplay GitHub repository using M

## Core install (recommended)

Install the four core libraries (and `path.py` from the displaysys package).
Install the three core libraries (`displaysys`, `eventsys`, `multimer`) plus
`graphics` (manifest still under `packages/graphics.json`, files hosted in
[PyDevices/graphics](https://github.com/PyDevices/graphics)).
Board-specific `board_config.py` comes from a [`board_configs/`](https://github.com/PyDevices/micropython-hardware/tree/main/board_configs) package, not from displaysys:

```python
Expand All @@ -21,6 +23,12 @@ for pkg in displaysys eventsys graphics multimer; do
done
```

Or install `graphics` from the MIP index published by the graphics repo:

```python
mip.install("graphics", index="https://PyDevices.github.io/micropython-lib/mip/PyDevices")
```

Add examples and add_ons separately:

```python
Expand All @@ -36,7 +44,8 @@ Manifests live in the [`packages/`](https://github.com/PyDevices/pydisplay/tree/
|---------|----------|
| displaysys | `packages/displaysys.json` |
| eventsys | `packages/eventsys.json` |
| graphics | `packages/graphics.json` |
| graphics | `packages/graphics.json` (sources in PyDevices/graphics) |
| usdl2 | `packages/usdl2.json` (sources in PyDevices/usdl2) |
| multimer | `packages/multimer.json` |
| add_ons | `packages/add_ons.json` |
| examples | `packages/examples.json` |
Expand Down
6 changes: 4 additions & 2 deletions docs/platforms/circuitpython.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,11 @@ directory exists. See [lv_circuitpython_mod README](https://github.com/PyDevices
`multimer` supplies Adafruit-compatible `ticks_*` helpers for application code;
frozen asyncio still uses `adafruit_ticks` internally unless the build is customized.

## framebuf shim
## framebuf

CircuitPython lacks MicroPython-compatible `framebuf`. Install `add_ons/framebuf.py` or copy it to your `lib/` folder.
CircuitPython lacks MicroPython-compatible `framebuf`. Use the `framebuf` module
from [PyDevices/graphics](https://github.com/PyDevices/graphics)
(`lib/graphics/framebuf.py`, MIP `graphics` / TestPyPI `graphics-py`).

## Installers

Expand Down
4 changes: 2 additions & 2 deletions docs/platforms/micropython.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ Use `board_configs/sdldisplay/` or the default `src/lib/board_config.py` for SDL

For best SDL2 performance on MicroPython Unix, CircuitPython Unix, and `micropython.exe`, build with the optional native **`usdl2`** module from [PyDevices/usdl2](https://github.com/PyDevices/usdl2). It provides the SDL2 subset used by **`SDLDisplay`** and, when the host selects the SDL timer backend, **`multimer._sdl2`**.

Without **`usdl2`**, `SDLDisplay` falls back to pure-Python ffi/ctypes bindings; timer selection is unchanged (`multimer` still picks `_librt` or threading backends first on each platform).
Without the native module, `SDLDisplay` falls back to pure-Python ffi/ctypes bindings from the same repo (`usdl2-py` on TestPyPI / MIP `usdl2`); timer selection is unchanged (`multimer` still picks `_librt` or threading backends first on each platform).

On **CPython** desktop, the ctypes shim is published on TestPyPI as package **`usdl2`** (install with the [two-index pip pattern](../publishing-micropython-lib.md#two-index-pip-install-required); install it alongside `displaysys` when using `SDLDisplay` — it is not a MIP/`require()` dependency).
On **CPython** desktop, prefer the native TestPyPI wheel **`usdl2`**; the ctypes fallback is **`usdl2-py`** (same import name). Install either with the [two-index pip pattern](../publishing-micropython-lib.md#two-index-pip-install-required) alongside `displaysys` when using `SDLDisplay`.

## Frozen firmware

Expand Down
6 changes: 3 additions & 3 deletions docs/publishing-micropython-lib.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Tags must match `v*.*.*` (e.g. `v0.0.5`, not `0.0.5`).
Pushing the tag starts [**Publish micropython-lib**](https://github.com/PyDevices/pydisplay/actions/workflows/publish-micropython-lib.yml), which:

1. Syncs `src/lib/*` into [micropython-lib](https://github.com/PyDevices/micropython-lib) (`PyDevices` branch) at version `X.Y.Z`
2. Uploads CPython wheels to TestPyPI (`displaysys`, `eventsys`, `pydisplay-graphics`, `multimer`, …)
2. Uploads CPython wheels to TestPyPI (`displaysys`, `eventsys`, `multimer`, …). Pure-Python `graphics` publishes from [PyDevices/graphics](https://github.com/PyDevices/graphics) as `graphics-py`.
3. Rebuilds the [MIP index](https://PyDevices.github.io/micropython-lib/mip/PyDevices) on micropython-lib `gh-pages`

Typical runtime: **~10–20 minutes**.
Expand Down Expand Up @@ -192,7 +192,7 @@ Or `mpremote mip install --index "https://PyDevices.github.io/micropython-lib/mi

### TestPyPI

PyDevices CPython wheels are published to [TestPyPI](https://test.pypi.org) only (not production PyPI). Browse package names there (`displaysys`, `eventsys`, `multimer`, `pydisplay-graphics`, …).
PyDevices CPython wheels are published to [TestPyPI](https://test.pypi.org) only (not production PyPI). Browse package names there (`displaysys`, `eventsys`, `multimer`, `graphics-py`, …).

**Naming:** MIP package names (e.g. `graphics`) may differ from the pip/TestPyPI project name when the MIP name is already taken on pypi.org. The mapping lives in `pypi_publish_name()` in [`publish_sync_packages.sh`](https://github.com/PyDevices/pydisplay/blob/main/scripts/publish_sync_packages.sh).

Expand Down Expand Up @@ -268,7 +268,7 @@ Script options: `./scripts/publish_sync_packages.sh --help`
|--------|------------|
| Version already exists | Push a **new tag** with a higher semver — TestPyPI rejects duplicate versions |
| Upload fails mid-run | Partial uploads may succeed; fix the error, bump the tag, push again |
| `graphics` sdist 400 | Name is taken on [pypi.org/project/graphics](https://pypi.org/project/graphics); PyPI project is `pydisplay-graphics` (MIP name stays `graphics`). See `pypi_publish_name()` in [`publish_sync_packages.sh`](https://github.com/PyDevices/pydisplay/blob/main/scripts/publish_sync_packages.sh). |
| `graphics` packaging | Pure-Python `graphics` / `graphics-py` is published from [PyDevices/graphics](https://github.com/PyDevices/graphics), not this repo. |
| Slow | Normal — each lib package gets hatch build + twine upload |
| Not for devices | Boards use the **MIP index**, not TestPyPI |

Expand Down
8 changes: 0 additions & 8 deletions packages/add_ons.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@
"fetch_ph_gui.py",
"github:PyDevices/pydisplay/src/add_ons/fetch_ph_gui.py"
],
[
"framebuf.py",
"github:PyDevices/pydisplay/src/add_ons/framebuf.py"
],
[
"hardware_setup.py",
"github:PyDevices/pydisplay/src/add_ons/hardware_setup.py"
Expand Down Expand Up @@ -80,10 +76,6 @@
"uctypes.py",
"github:PyDevices/pydisplay/src/add_ons/uctypes.py"
],
[
"usdl2.py",
"github:PyDevices/pydisplay/src/add_ons/usdl2.py"
],
[
"viper_tools.py",
"github:PyDevices/pydisplay/src/add_ons/viper_tools.py"
Expand Down
Loading
Loading