From 6836222cbc6dff03bb3e38c773e38f6ed5ddfbbc Mon Sep 17 00:00:00 2001
From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com>
Date: Wed, 7 Oct 2026 15:57:55 -0500
Subject: [PATCH] Env helpers come from boarddev now (follows pydevices' hard
break)
pydevices moves env_get, env_int, env_float, env_bool and env_set from
displaydev to boarddev with no re-export, so every example, util, tool and
doc here that imported them from displaydev imports them from boarddev.
The hand-written Peter Hinch gallery page moves with them.
tools/typings is regenerated with scripts/gen_package_pyi.sh against the
new pydevices, which now writes boarddev.pyi too; the rest of the stub diff
is the stubs catching up with pydevices main.
---
.site/gallery/peterhinch.html | 2 +-
AGENTS.md | 2 +-
lib/examples/car_cluster/car_cluster.py | 2 +-
lib/examples/drum_machine/drum_machine.py | 2 +-
lib/examples/google_photos/google_photos.py | 4 +-
lib/examples/lv_test_timer.py | 2 +-
lib/examples/roku_remote/roku_remote.py | 4 +-
lib/examples/roku_remote/roku_sim.py | 2 +-
lib/examples/spectrum/spectrum.py | 2 +-
lib/utils/ps_loader.py | 4 +-
lib/utils/secrets.py | 2 +-
scripts/gen_package_pyi.sh | 11 ++--
tools/README.md | 2 +-
tools/example_test_wrapper.py | 2 +-
tools/multimer_source_preload.py | 4 +-
tools/screenshot.py | 2 +-
tools/typings/appdev/app.pyi | 36 +++++++++----
tools/typings/boarddev.pyi | 10 ++++
tools/typings/displaydev/__init__.pyi | 57 ++++++++++++++++----
tools/typings/displaydev/_desktop.pyi | 15 ++++++
tools/typings/displaydev/auto.pyi | 2 +-
tools/typings/displaydev/fbdisplay.pyi | 2 +
tools/typings/displaydev/jndisplay.pyi | 8 ++-
tools/typings/displaydev/pgdisplay.pyi | 13 ++---
tools/typings/displaydev/psdisplay.pyi | 14 ++---
tools/typings/displaydev/sdldisplay.pyi | 13 ++---
tools/typings/displaydev/wasmdisplay.pyi | 18 +++++++
tools/typings/displaydev/windisplay.pyi | 11 ++--
tools/typings/keys.pyi | 5 ++
tools/typings/multimer/__init__.pyi | 13 +++--
tools/typings/multimer/_async_timer.pyi | 5 --
tools/typings/multimer/_core.pyi | 14 -----
tools/typings/multimer/_dispatch.pyi | 58 +++++++++++++++++++++
tools/typings/multimer/_hostloop.pyi | 14 +++++
tools/typings/multimer/_inputhook.pyi | 2 +
tools/typings/multimer/_mpasyncio.pyi | 12 ++++-
tools/typings/multimer/_repl.pyi | 1 +
tools/typings/multimer/_schedule.pyi | 3 --
tools/typings/multimer/_src_asyncio.pyi | 8 +++
tools/typings/multimer/_src_machine.pyi | 10 ++++
tools/typings/multimer/_src_native.pyi | 8 +++
tools/typings/multimer/_src_none.pyi | 8 +++
tools/typings/multimer/_src_pending.pyi | 8 +++
tools/typings/multimer/_src_signal.pyi | 15 ++++++
tools/typings/multimer/_src_wasm.pyi | 10 ++++
tools/typings/multimer/_ticks.pyi | 1 +
tools/typings/multimer/auto.pyi | 20 -------
tools/typings/multimer/librt.pyi | 20 -------
tools/typings/multimer/machine.pyi | 10 ----
tools/typings/multimer/polling.pyi | 12 -----
tools/typings/multimer/sdl2.pyi | 13 -----
tools/typings/multimer/threading.pyi | 13 -----
tools/typings/multimer/win32.pyi | 13 -----
53 files changed, 332 insertions(+), 212 deletions(-)
create mode 100644 tools/typings/boarddev.pyi
create mode 100644 tools/typings/displaydev/_desktop.pyi
create mode 100644 tools/typings/displaydev/wasmdisplay.pyi
delete mode 100644 tools/typings/multimer/_async_timer.pyi
delete mode 100644 tools/typings/multimer/_core.pyi
create mode 100644 tools/typings/multimer/_dispatch.pyi
create mode 100644 tools/typings/multimer/_hostloop.pyi
create mode 100644 tools/typings/multimer/_inputhook.pyi
create mode 100644 tools/typings/multimer/_repl.pyi
delete mode 100644 tools/typings/multimer/_schedule.pyi
create mode 100644 tools/typings/multimer/_src_asyncio.pyi
create mode 100644 tools/typings/multimer/_src_machine.pyi
create mode 100644 tools/typings/multimer/_src_native.pyi
create mode 100644 tools/typings/multimer/_src_none.pyi
create mode 100644 tools/typings/multimer/_src_pending.pyi
create mode 100644 tools/typings/multimer/_src_signal.pyi
create mode 100644 tools/typings/multimer/_src_wasm.pyi
delete mode 100644 tools/typings/multimer/auto.pyi
delete mode 100644 tools/typings/multimer/librt.pyi
delete mode 100644 tools/typings/multimer/machine.pyi
delete mode 100644 tools/typings/multimer/polling.pyi
delete mode 100644 tools/typings/multimer/sdl2.pyi
delete mode 100644 tools/typings/multimer/threading.pyi
delete mode 100644 tools/typings/multimer/win32.pyi
diff --git a/.site/gallery/peterhinch.html b/.site/gallery/peterhinch.html
index 408905d1a..cc7acd6c0 100644
--- a/.site/gallery/peterhinch.html
+++ b/.site/gallery/peterhinch.html
@@ -259,7 +259,7 @@
Console output
import sys
from js import document, window
-from displaydev import env_set
+from boarddev import env_set
# Hinch demos use 320x240 (gallery default is 320x480).
env_set("PYDEVICES_WIDTH", 320)
diff --git a/AGENTS.md b/AGENTS.md
index 4492de155..8adcbd8a2 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -102,7 +102,7 @@ Board configs never own an `appdev.App`. `AutoDisplay` is imported from
Host defaults and env semantics:
[App and board config — `timer_async`](https://github.com/PyDevices/pydevices/blob/main/docs/app-and-board-config.md#timer_async).
Examples never read this variable — only library `board_config` and harnesses
-that call `displaydev.env_set`.
+that call `boarddev.env_set`.
**Preferred for agents / matrix:** pass wrapper `--timer-async` (the example
kit does this). That uses `env_set` and works for Windows PE under WSL without
diff --git a/lib/examples/car_cluster/car_cluster.py b/lib/examples/car_cluster/car_cluster.py
index 85d4501de..576f1af64 100644
--- a/lib/examples/car_cluster/car_cluster.py
+++ b/lib/examples/car_cluster/car_cluster.py
@@ -41,7 +41,7 @@
input_map.capture_virtual_devices()
-from displaydev import env_bool, env_get, env_set
+from boarddev import env_bool, env_get, env_set
env_set("PYDEVICES_WIDTH", "1024")
env_set("PYDEVICES_HEIGHT", "512")
diff --git a/lib/examples/drum_machine/drum_machine.py b/lib/examples/drum_machine/drum_machine.py
index 1c5313402..2aa068050 100644
--- a/lib/examples/drum_machine/drum_machine.py
+++ b/lib/examples/drum_machine/drum_machine.py
@@ -51,7 +51,7 @@
# board_config reads these; a board's board_config ignores them (its display
# is a fixed size), so an app can ask for its designed resolution without
# ever fighting real hardware.
-from displaydev import env_set
+from boarddev import env_set
env_set("PYDEVICES_WIDTH", 720)
env_set("PYDEVICES_HEIGHT", 480)
diff --git a/lib/examples/google_photos/google_photos.py b/lib/examples/google_photos/google_photos.py
index 9d2152c6a..1d842a4fe 100644
--- a/lib/examples/google_photos/google_photos.py
+++ b/lib/examples/google_photos/google_photos.py
@@ -37,7 +37,7 @@
Join Wi-Fi before running on a microcontroller. Optional desktop panel size:
edit ``_WIDTH`` / ``_HEIGHT`` / ``_SCALE`` below (applied via
-``displaydev.env_set`` before ``board_config`` is imported; never on MCUs).
+``boarddev.env_set`` before ``board_config`` is imported; never on MCUs).
"""
import sys
@@ -46,7 +46,7 @@
if _PKG not in sys.path:
sys.path.insert(0, _PKG)
-from displaydev import env_set
+from boarddev import env_set
# Local desktop test panel -- change these and re-run. Must stay above board_config.
_WIDTH = None
diff --git a/lib/examples/lv_test_timer.py b/lib/examples/lv_test_timer.py
index 54172b391..c5a288444 100644
--- a/lib/examples/lv_test_timer.py
+++ b/lib/examples/lv_test_timer.py
@@ -48,7 +48,7 @@
import time
from board_config import display_drv
-from displaydev import env_get
+from boarddev import env_get
import multimer
# Optional logical orientation for LVGL (hw MADCTL/SDL/PG or software rotate).
diff --git a/lib/examples/roku_remote/roku_remote.py b/lib/examples/roku_remote/roku_remote.py
index d5b3162cd..2c9eef23f 100644
--- a/lib/examples/roku_remote/roku_remote.py
+++ b/lib/examples/roku_remote/roku_remote.py
@@ -38,7 +38,7 @@
on a microcontroller. Optional fixed target: ``ROKU_HOST`` in :mod:`roku_engine`.
Desktop panel size: edit ``_WIDTH`` / ``_HEIGHT`` / ``_SCALE`` below. Those are
-applied via ``displaydev.env_set`` before any front end imports ``board_config``.
+applied via ``boarddev.env_set`` before any front end imports ``board_config``.
Leave ``_SCALE`` as ``None`` to keep the desktop ``AutoDisplay`` default
(autoscale still fits the window).
"""
@@ -49,7 +49,7 @@
if _PKG not in sys.path:
sys.path.insert(0, _PKG)
-from displaydev import env_set
+from boarddev import env_set
# Local desktop test panel — change these and re-run. Must stay above board_config.
# Match LilyGO T-HMI (ST7789 I80): 240x320. Never apply on MCU boards (panel
diff --git a/lib/examples/roku_remote/roku_sim.py b/lib/examples/roku_remote/roku_sim.py
index f5025adc4..cf4081c4f 100644
--- a/lib/examples/roku_remote/roku_sim.py
+++ b/lib/examples/roku_remote/roku_sim.py
@@ -86,7 +86,7 @@ def sim_reason():
(missing ``socket`` or no station IPv4 / Wi‑Fi).
"""
try:
- from displaydev import env_bool, env_get
+ from boarddev import env_bool, env_get
except ImportError:
if _is_pyscript():
return "pyscript"
diff --git a/lib/examples/spectrum/spectrum.py b/lib/examples/spectrum/spectrum.py
index dea5e7ee8..af891d866 100644
--- a/lib/examples/spectrum/spectrum.py
+++ b/lib/examples/spectrum/spectrum.py
@@ -26,7 +26,7 @@
import sys
-from displaydev import env_get, env_set
+from boarddev import env_get, env_set
# A desktop board_config reads these; a real board's display ignores them.
if env_get("PYDEVICES_WIDTH") is None:
diff --git a/lib/utils/ps_loader.py b/lib/utils/ps_loader.py
index f2d1e58fb..395583323 100644
--- a/lib/utils/ps_loader.py
+++ b/lib/utils/ps_loader.py
@@ -107,7 +107,7 @@ def parse_names(raw):
def set_board_defaults():
"""Set the gallery's browser defaults without importing board_config."""
- from displaydev import env_set
+ from boarddev import env_set
env_set("PYDEVICES_WIDTH", BOARD_WIDTH)
env_set("PYDEVICES_HEIGHT", BOARD_HEIGHT)
@@ -337,7 +337,7 @@ def _ensure_board_config(mip_mod, status=None, url_base=None):
def ensure_board_config(status=None):
"""Ensure browser ``board_config`` (desktop package) is importable.
- Call after ``utils.path`` and before ``from displaydev import env_set`` /
+ Call after ``utils.path`` and before ``from boarddev import env_set`` /
importing demos or setup modules that ``import board_config``. The desktop
package pulls in ``displaydev`` (not frozen in the gallery wasm). Set size
overrides with ``env_set`` after this returns and before the first
diff --git a/lib/utils/secrets.py b/lib/utils/secrets.py
index 2eda8f22e..ccc97ca51 100644
--- a/lib/utils/secrets.py
+++ b/lib/utils/secrets.py
@@ -9,7 +9,7 @@
import sys
-from displaydev import env_get
+from boarddev import env_get
_DESKTOP = frozenset(("linux", "darwin", "win32", "unix", "webassembly", "emscripten"))
diff --git a/scripts/gen_package_pyi.sh b/scripts/gen_package_pyi.sh
index f3c196b54..1c64647b9 100755
--- a/scripts/gen_package_pyi.sh
+++ b/scripts/gen_package_pyi.sh
@@ -1,12 +1,13 @@
#!/usr/bin/env bash
-# Regenerate IDE type stubs for three core PyDevices packages into tools/typings/.
+# Regenerate IDE type stubs for the core PyDevices packages into tools/typings/.
#
# Usage:
# ./scripts/gen_package_pyi.sh
# ./scripts/gen_package_pyi.sh --help
#
# Requires the repo-root .venv (mypy / stubgen). Output is committed under
-# tools/typings/{displaydev,appdev,multimer}/ for stubPath.
+# tools/typings/{displaydev,appdev,multimer}/ and the single-module stubs
+# (events, keys, boarddev) for stubPath.
set -euo pipefail
@@ -14,7 +15,7 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
PACKAGES=(displaydev appdev multimer)
-MODULES=(events keys)
+MODULES=(events keys boarddev)
OUT=tools/typings
STUBGEN="${ROOT}/.venv/bin/stubgen"
PYTHON="${ROOT}/.venv/bin/python"
@@ -24,7 +25,7 @@ usage() {
Usage: ./scripts/gen_package_pyi.sh
Regenerate mypy stubgen .pyi trees for displaydev, appdev,
-multimer, events, and keys into tools/typings/ (Pylance / pyright stubPath).
+multimer, events, keys, and boarddev into tools/typings/ (Pylance / pyright stubPath).
(source is gitignored; public API is pygraphics.FrameBuffer).
EOF
@@ -66,7 +67,7 @@ fi
echo "Running stubgen → ${OUT}/ …"
"$STUBGEN" --ignore-errors -o "$OUT" \
-p displaydev -p appdev -p multimer \
- -m events -m keys
+ -m events -m keys -m boarddev
# Gitignored generated module; public FrameBuffer lives in _framebuf_plus.
diff --git a/tools/README.md b/tools/README.md
index aa0b07128..9b615ad00 100644
--- a/tools/README.md
+++ b/tools/README.md
@@ -112,7 +112,7 @@ see [Windows PE under WSL](#windows-pe-under-wsl).
`micropython.exe` and `python.exe` are Windows PE binaries launched from WSL.
They cannot read Linux-exported environment variables. The kit therefore
forwards only values that must cross that boundary via wrapper argv +
-`displaydev.env_set` (notably `--timer-async` / `--multimer-source`).
+`boarddev.env_set` (notably `--timer-async` / `--multimer-source`).
**Do not forward `SDL_VIDEODRIVER` / `SDL_AUDIODRIVER` to PE.** Unix cells stay
headless from the shell `SDL_*=dummy` export; PE keeps a real Windows video
diff --git a/tools/example_test_wrapper.py b/tools/example_test_wrapper.py
index 4050488ab..1c96f3efc 100755
--- a/tools/example_test_wrapper.py
+++ b/tools/example_test_wrapper.py
@@ -712,7 +712,7 @@ def main(argv=None):
# Apply --env / --timer-async via env_set before board_config / SDL init
# (deadline hook and the example both import board_config).
try:
- from displaydev import env_set
+ from boarddev import env_set
except Exception:
env_set = None
if env_set is not None:
diff --git a/tools/multimer_source_preload.py b/tools/multimer_source_preload.py
index 1e8fabfd7..ee4c23447 100755
--- a/tools/multimer_source_preload.py
+++ b/tools/multimer_source_preload.py
@@ -13,7 +13,7 @@
launched from WSL cannot see exported ones, so a sweep across interpreters sets
``MULTIMER_SOURCE`` inside the child, makes multimer choose its source at once,
and checks that the one it got is the one asked for. Other ``--env`` values go
-through ``displaydev.env_set()``. The target script keeps the real command line
+through ``boarddev.env_set()``. The target script keeps the real command line
(``sys.argv`` is read-only on CircuitPython), so scripts must locate their own
flags anywhere in ``sys.argv`` rather than at a fixed index.
@@ -118,7 +118,7 @@ def main(argv):
_bootstrap_path(source_workspace)
if env:
- from displaydev import env_set
+ from boarddev import env_set
for name, value in env:
env_set(name, value)
diff --git a/tools/screenshot.py b/tools/screenshot.py
index 146eac4ec..5d006bd37 100755
--- a/tools/screenshot.py
+++ b/tools/screenshot.py
@@ -82,7 +82,7 @@ def _positive_scale(value):
def _apply_display_overrides(resolution, scale):
- from displaydev import env_set
+ from boarddev import env_set
if resolution is not None:
width, height = resolution
diff --git a/tools/typings/appdev/app.pyi b/tools/typings/appdev/app.pyi
index 0325d6ee5..136167255 100644
--- a/tools/typings/appdev/app.pyi
+++ b/tools/typings/appdev/app.pyi
@@ -1,24 +1,33 @@
+import events
+import keys
import types
-from .devices import Encoder as Encoder, HostEvents as HostEvents, Joystick as Joystick, Keypad as Keypad, Touch as Touch
+from .devices import ENCODER as ENCODER, Encoder as Encoder, HOST as HOST, HostEvents as HostEvents, JOYSTICK as JOYSTICK, Joystick as Joystick, KEYPAD as KEYPAD, Keypad as Keypad, POINTER as POINTER, Touch as Touch
from _typeshed import Incomplete
DEFAULT_REFRESH_MS: int
SERVICE_TICK_MS: int
-class _TimerSubscription:
- def __init__(self, app, entry) -> None: ...
- def cancel(self) -> None: ...
-
class _RefreshClaim:
def __init__(self, app) -> None: ...
def release(self) -> None: ...
+class _PollingClaim:
+ def __init__(self, app) -> None: ...
+ def release(self) -> None: ...
+
class _RefreshPaused:
def __init__(self, app) -> None: ...
def __enter__(self): ...
def __exit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: types.TracebackType | None): ...
class App:
+ events = events
+ keys = keys
+ HOST = HOST
+ POINTER = POINTER
+ ENCODER = ENCODER
+ KEYPAD = KEYPAD
+ JOYSTICK = JOYSTICK
@classmethod
def current(cls): ...
devices: Incomplete
@@ -27,9 +36,9 @@ class App:
keypad_dev: Incomplete
encoder_dev: Incomplete
joystick_dev: Incomplete
- def __init__(self, board_config=None, *, displays=None, host_read=None, touch_read=None, touch_rotation_table=None, refresh_period=None, timer_async=None) -> None: ...
+ def __init__(self, board_config=None, *, displays=None, host_read=None, touch_read=None, touch_rotation_table=None, refresh_period=None) -> None: ...
@property
- def timer_async(self): ...
+ def strategy(self): ...
@property
def displays(self): ...
@property
@@ -40,6 +49,8 @@ class App:
def before_quit(self): ...
@before_quit.setter
def before_quit(self, value) -> None: ...
+ @property
+ def timers(self): ...
def add_display(self, drv): ...
def remove_display(self, drv) -> None: ...
def add_touch(self, read, *, display=None, rotation_table=None): ...
@@ -50,13 +61,16 @@ class App:
def unregister(self, dev) -> None: ...
def on(self, event_type_or_list, callback=None): ...
def off(self, event_type_or_list, callback) -> None: ...
- def every(self, ms, callback=None): ...
- def stop_timer(self) -> None: ...
+ def every(self, ms=None, callback=None, *, period=None, name=None): ...
+ def on_tick(self, callback, period=..., **_ignored): ...
+ def stop_timers(self) -> None: ...
+ stop_timer = stop_timers
def pause_refresh(self): ...
def resume_refresh(self) -> None: ...
def refresh_paused(self): ...
+ def pause_polling(self): ...
+ def resume_polling(self) -> None: ...
def poll(self): ...
- def arm_async_refresh(self) -> None: ...
- def run(self, tick_ms=...) -> None: ...
+ def run(self, tick_ms=...): ...
def run_async(self, coro_or_fn): ...
def request_quit(self, code=None) -> None: ...
diff --git a/tools/typings/boarddev.pyi b/tools/typings/boarddev.pyi
new file mode 100644
index 000000000..ef244f546
--- /dev/null
+++ b/tools/typings/boarddev.pyi
@@ -0,0 +1,10 @@
+def bind_lazy(ns, peripherals_mod): ...
+def audio_provider(): ...
+def pcm_out(format=None, **kwargs): ...
+def pcm_in(format=None, **kwargs): ...
+def audio_out(format=None, **kwargs): ...
+def env_set(name, value) -> None: ...
+def env_bool(name, default: bool = False): ...
+def env_get(name, default=None): ...
+def env_int(name, default: int = 0): ...
+def env_float(name, default: float = 0.0): ...
diff --git a/tools/typings/displaydev/__init__.pyi b/tools/typings/displaydev/__init__.pyi
index 8fad68c40..abe3f8850 100644
--- a/tools/typings/displaydev/__init__.pyi
+++ b/tools/typings/displaydev/__init__.pyi
@@ -1,17 +1,9 @@
from _typeshed import Incomplete
-__all__ = ['DisplayDriver', 'alloc_buffer', 'byteswap', 'capabilities', 'color332', 'color565', 'color565_swapped', 'color_rgb', 'desktop_work_area', 'env_bool', 'env_float', 'env_get', 'env_int', 'env_set', 'fit_scale_to_desktop', 'notify_board_config_scale_override']
+__all__ = ['DisplayDriver', 'alloc_buffer', 'byteswap', 'capabilities', 'color332', 'color565', 'color565_swapped', 'color_rgb']
byteswap: Incomplete
-def env_set(name, value) -> None: ...
-def env_bool(name, default: bool = False): ...
-def env_get(name, default=None): ...
-def env_int(name, default: int = 0): ...
-def env_float(name, default: float = 0.0): ...
-def desktop_work_area(display_index: int = 0): ...
-def fit_scale_to_desktop(width, height, scale, desktop_w, desktop_h, *, margin=..., chrome_w: int = 0, chrome_h: int = 0): ...
-def notify_board_config_scale_override(driver_name, requested, fitted, *, quiet: bool = False) -> None: ...
def capabilities(): ...
def alloc_buffer(size): ...
def color565(r, g=None, b=None): ...
@@ -19,11 +11,51 @@ def color565_swapped(r, g: int = 0, b: int = 0): ...
def color332(r, g, b): ...
def color_rgb(color): ...
+class FrameClock:
+ name: Incomplete
+ count: int
+ last: Incomplete
+ def __init__(self, period_ms, name=None) -> None: ...
+ @property
+ def period_ms(self): ...
+ @period_ms.setter
+ def period_ms(self, value) -> None: ...
+ @property
+ def running(self): ...
+ def subscribe(self, fn): ...
+ def unsubscribe(self, fn) -> None: ...
+ def stop(self) -> None: ...
+
+class _FpsMeter:
+ now: Incomplete
+ diff: Incomplete
+ def __init__(self, now, diff) -> None: ...
+ mark: Incomplete
+ win_us: int
+ win_frames: int
+ win_present: int
+ win_flushes: int
+ win_pixels: int
+ frame_us: int
+ frame_max: int
+ last_fps: Incomplete
+ seconds: float
+ frames: int
+ present_us: float
+ flushes: int
+ pixels: int
+ def reset(self) -> None: ...
+ def frame(self, t, dt) -> None: ...
+ def flush(self, t, dt, pixels) -> None: ...
+ def snapshot(self): ...
+
class DisplayDriver:
needs_refresh: bool
- requires_async_timer: bool
+ refresh_period_ms: int
share_framebuffer: bool
quit_chord: Incomplete
+ @property
+ def frame_clock(self): ...
def framebuffers(self) -> None: ...
byteswap: Incomplete
touch_scale: float
@@ -67,6 +99,11 @@ class DisplayDriver:
def vsa_area(self): ...
@property
def bfa_area(self): ...
+ def measure_fps(self, on: bool = True): ...
+ def frame_done(self) -> None: ...
+ def fps(self): ...
+ def fps_reset(self) -> None: ...
+ def fps_print(self, seconds: int = 5) -> None: ...
def vscrdef(self, tfa: int, vsa: int, bfa: int) -> None: ...
def vscsad(self, vssa: int | None = None) -> int: ...
@property
diff --git a/tools/typings/displaydev/_desktop.pyi b/tools/typings/displaydev/_desktop.pyi
new file mode 100644
index 000000000..d2da103b5
--- /dev/null
+++ b/tools/typings/displaydev/_desktop.pyi
@@ -0,0 +1,15 @@
+from displaydev import DisplayDriver as DisplayDriver
+
+def desktop_work_area(display_index: int = 0): ...
+def fit_scale_to_desktop(width, height, scale, desktop_w, desktop_h, *, margin=..., chrome_w: int = 0, chrome_h: int = 0): ...
+def notify_board_config_scale_override(driver_name, requested, fitted, *, quiet: bool = False) -> None: ...
+
+class DesktopDisplay(DisplayDriver):
+ def vscrdef(self, tfa: int, vsa: int, bfa: int) -> None: ...
+ def vscsad(self, vssa=None) -> int: ...
+
+class FrameRecorderMixin:
+ @property
+ def frame_recording(self) -> bool: ...
+ def open_frame_recorder(self, path, *, fps: int = 12, width=None, height=None): ...
+ def close_frame_recorder(self): ...
diff --git a/tools/typings/displaydev/auto.pyi b/tools/typings/displaydev/auto.pyi
index 440332e3e..f40a2cf7e 100644
--- a/tools/typings/displaydev/auto.pyi
+++ b/tools/typings/displaydev/auto.pyi
@@ -1,4 +1,4 @@
__all__ = ['AutoDisplay', 'host_kind']
def host_kind(): ...
-def AutoDisplay(width: int = 320, height: int = 240, rotation: int = 0, scale: float = 1.0, title: str = 'displaydev', canvas_id: str = 'display_canvas', *, quiet: bool = False): ...
+def AutoDisplay(width: int = 320, height: int = 240, rotation: int = 0, scale: float = 1.0, title: str = 'displaydev', canvas_id=None, *, quiet: bool = False): ...
diff --git a/tools/typings/displaydev/fbdisplay.pyi b/tools/typings/displaydev/fbdisplay.pyi
index 876823eed..c60f8c46a 100644
--- a/tools/typings/displaydev/fbdisplay.pyi
+++ b/tools/typings/displaydev/fbdisplay.pyi
@@ -2,6 +2,8 @@ from displaydev import DisplayDriver as DisplayDriver
class FBDisplay(DisplayDriver):
share_framebuffer: bool
+ @property
+ def needs_refresh(self): ...
color_depth: int
def __init__(self, buffer, width=None, height=None, reverse_bytes_in_word: bool = False, *, bitmap=None, display=None, quiet: bool = False) -> None: ...
def framebuffers(self): ...
diff --git a/tools/typings/displaydev/jndisplay.pyi b/tools/typings/displaydev/jndisplay.pyi
index 47a227dc7..106db2202 100644
--- a/tools/typings/displaydev/jndisplay.pyi
+++ b/tools/typings/displaydev/jndisplay.pyi
@@ -1,5 +1,6 @@
from _typeshed import Incomplete
-from displaydev import DisplayDriver as DisplayDriver, color_rgb as color_rgb
+from displaydev import color_rgb as color_rgb
+from displaydev._desktop import DesktopDisplay as DesktopDisplay
from displaydev._domkeys import enrich_mod as enrich_mod, key_to_keycode as key_to_keycode, mod_mask as mod_mask
class JNDevices:
@@ -8,9 +9,8 @@ class JNDevices:
def read(self): ...
def update_buffer(self, pil_image) -> None: ...
-class JNDisplay(DisplayDriver):
+class JNDisplay(DesktopDisplay):
needs_refresh: bool
- requires_async_timer: bool
quit_chord: Incomplete
color_depth: int
touch_scale: float
@@ -20,7 +20,5 @@ class JNDisplay(DisplayDriver):
def fill_rect(self, x, y, w, h, c): ...
def blit_rect(self, buf, x, y, w, h): ...
def pixel(self, x, y, c): ...
- def vscrdef(self, tfa: int, vsa: int, bfa: int) -> None: ...
- def vscsad(self, vssa=None) -> int: ...
def render(self, render_rect=None): ...
def show(self, _timer=None) -> None: ...
diff --git a/tools/typings/displaydev/pgdisplay.pyi b/tools/typings/displaydev/pgdisplay.pyi
index e36747627..cae7bcdd7 100644
--- a/tools/typings/displaydev/pgdisplay.pyi
+++ b/tools/typings/displaydev/pgdisplay.pyi
@@ -1,31 +1,24 @@
from _typeshed import Incomplete
-from displaydev import DisplayDriver
+from displaydev._desktop import DesktopDisplay, FrameRecorderMixin
__all__ = ['poll_event', 'get_events', 'PGDisplay']
def poll_event(): ...
def get_events(): ...
-class PGDisplay(DisplayDriver):
+class PGDisplay(FrameRecorderMixin, DesktopDisplay):
needs_refresh: bool
quit_chord: Incomplete
color_depth: Incomplete
touch_scale: Incomplete
- runtime: Incomplete
+ app: Incomplete
get_events: Incomplete
def __init__(self, width: int = 320, height: int = 240, rotation: int = 0, color_depth: int = 16, title: str = 'displaydev', scale: float = 1.0, window_flags=..., *, quiet: bool = False) -> None: ...
def init(self) -> None: ...
def blit_rect(self, buffer: memoryview, x: int, y: int, w: int, h: int): ...
def fill_rect(self, x: int, y: int, w: int, h: int, c: int): ...
def pixel(self, x: int, y: int, c: int): ...
- def vscrdef(self, tfa: int, vsa: int, bfa: int) -> None: ...
- def vscsad(self, vssa=None) -> int: ...
def capture_rgb(self): ...
- @property
- def frame_recording(self) -> bool: ...
- def open_frame_recorder(self, path, *, fps: int = 12, width=None, height=None): ...
- def close_frame_recorder(self): ...
def render(self, renderRect=None) -> None: ...
def show(self, _timer=None) -> None: ...
def quit(self, code: int = 0, force: bool = False) -> None: ...
- def force_quit(self, code: int = 0) -> None: ...
diff --git a/tools/typings/displaydev/psdisplay.pyi b/tools/typings/displaydev/psdisplay.pyi
index 241f0835b..29b7b2553 100644
--- a/tools/typings/displaydev/psdisplay.pyi
+++ b/tools/typings/displaydev/psdisplay.pyi
@@ -1,5 +1,6 @@
from _typeshed import Incomplete
-from displaydev import DisplayDriver as DisplayDriver, color_rgb as color_rgb
+from displaydev import color_rgb as color_rgb
+from displaydev._desktop import DesktopDisplay as DesktopDisplay
from displaydev._domkeys import dom_key_scrolls_page as dom_key_scrolls_page, enrich_mod as enrich_mod, key_to_keycode as key_to_keycode, mod_mask as mod_mask
def log(*args) -> None: ...
@@ -7,23 +8,24 @@ def log(*args) -> None: ...
class PSDevices:
canvas: Incomplete
def __init__(self, id, display=None) -> None: ...
+ def deinit(self) -> None: ...
def read(self): ...
-class PSDisplay(DisplayDriver):
+class PSDisplay(DesktopDisplay):
needs_refresh: bool
- requires_async_timer: bool
quit_chord: Incomplete
color_depth: int
- touch_scale: float
get_events: Incomplete
def __init__(self, id, width=None, height=None, *, quiet: bool = False) -> None: ...
def init(self) -> None: ...
def fill_rect(self, x, y, w, h, c): ...
def blit_rect(self, buf, x, y, w, h): ...
def pixel(self, x, y, c): ...
- def vscrdef(self, tfa: int, vsa: int, bfa: int) -> None: ...
- def vscsad(self, vssa=None) -> int: ...
def render(self, render_rect=None) -> None: ...
def show(self, _timer=None) -> None: ...
+ @property
+ def touch_scale(self): ...
+ @touch_scale.setter
+ def touch_scale(self, value) -> None: ...
def map_pointer(self, local_x, local_y): ...
def map_pointer_rel(self, dx, dy): ...
diff --git a/tools/typings/displaydev/sdldisplay.pyi b/tools/typings/displaydev/sdldisplay.pyi
index 6feae67ea..8b279749e 100644
--- a/tools/typings/displaydev/sdldisplay.pyi
+++ b/tools/typings/displaydev/sdldisplay.pyi
@@ -1,28 +1,23 @@
from _typeshed import Incomplete
-from displaydev import DisplayDriver as DisplayDriver, color_rgb as color_rgb, fit_scale_to_desktop as fit_scale_to_desktop, notify_board_config_scale_override as notify_board_config_scale_override
+from displaydev import color_rgb as color_rgb
+from displaydev._desktop import DesktopDisplay as DesktopDisplay, FrameRecorderMixin as FrameRecorderMixin
def poll_event(): ...
def get_events(): ...
def retcheck(retvalue) -> None: ...
-class SDLDisplay(DisplayDriver):
+class SDLDisplay(FrameRecorderMixin, DesktopDisplay):
needs_refresh: bool
quit_chord: Incomplete
color_depth: Incomplete
touch_scale: float
- runtime: Incomplete
+ app: Incomplete
get_events: Incomplete
def __init__(self, width: int = 320, height: int = 240, rotation: int = 0, color_depth: int = 16, title: str = 'SDL2 Display', scale: float = 1.0, window_flags=..., render_flags=..., x=..., y=..., *, quiet: bool = False) -> None: ...
def init(self) -> None: ...
def blit_rect(self, buffer: memoryview, x: int, y: int, w: int, h: int): ...
def fill_rect(self, x: int, y: int, w: int, h: int, c: int): ...
def pixel(self, x: int, y: int, c: int): ...
- def vscrdef(self, tfa: int, vsa: int, bfa: int) -> None: ...
- def vscsad(self, vssa=None) -> int: ...
- @property
- def frame_recording(self) -> bool: ...
- def open_frame_recorder(self, path, *, fps: int = 12, width=None, height=None): ...
- def close_frame_recorder(self): ...
def render(self, renderRect=None) -> None: ...
def show(self, _timer=None) -> None: ...
def capture_rgb(self): ...
diff --git a/tools/typings/displaydev/wasmdisplay.pyi b/tools/typings/displaydev/wasmdisplay.pyi
new file mode 100644
index 000000000..c6517b490
--- /dev/null
+++ b/tools/typings/displaydev/wasmdisplay.pyi
@@ -0,0 +1,18 @@
+from _typeshed import Incomplete
+from displaydev._desktop import DesktopDisplay as DesktopDisplay
+from displaydev._domkeys import enrich_mod as enrich_mod, key_to_keycode as key_to_keycode, mod_mask as mod_mask
+from displaydev.fbdisplay import FBDisplay as FBDisplay
+
+class WasmDevices:
+ def __init__(self, canvas_id) -> None: ...
+ def read(self): ...
+ def clear(self) -> None: ...
+
+class WasmDisplay(DesktopDisplay, FBDisplay):
+ needs_refresh: bool
+ refresh_period_ms: int
+ quit_chord: Incomplete
+ get_events: Incomplete
+ def __init__(self, width: int = 320, height: int = 240, canvas_id: str = 'display_canvas', *, quiet: bool = False) -> None: ...
+ def init(self) -> None: ...
+ def show(self, _timer=None) -> None: ...
diff --git a/tools/typings/displaydev/windisplay.pyi b/tools/typings/displaydev/windisplay.pyi
index f8b7d0e5d..50b52efb0 100644
--- a/tools/typings/displaydev/windisplay.pyi
+++ b/tools/typings/displaydev/windisplay.pyi
@@ -1,25 +1,22 @@
from _typeshed import Incomplete
-from displaydev import DisplayDriver
+from displaydev._desktop import DesktopDisplay
__all__ = ['poll_event', 'get_events', 'WinDisplay']
def poll_event(): ...
def get_events(): ...
-class WinDisplay(DisplayDriver):
+class WinDisplay(DesktopDisplay):
needs_refresh: bool
- requires_async_timer: bool
quit_chord: Incomplete
+ app: Incomplete
color_depth: Incomplete
- touch_scale: Incomplete
- runtime: Incomplete
+ touch_scale: float
get_events: Incomplete
def __init__(self, width: int = 320, height: int = 240, rotation: int = 0, color_depth: int = 16, title: str = 'Win32 Display', scale: float = 1.0, x=None, y=None, *, quiet: bool = False) -> None: ...
def init(self) -> None: ...
def blit_rect(self, buffer, x, y, w, h): ...
def fill_rect(self, x, y, w, h, c): ...
def pixel(self, x, y, c): ...
- def vscrdef(self, tfa, vsa, bfa) -> None: ...
- def vscsad(self, vssa=None): ...
def render(self, renderRect=None) -> None: ...
def show(self, _timer=None) -> None: ...
diff --git a/tools/typings/keys.pyi b/tools/typings/keys.pyi
index ed69f5a13..912b866ba 100644
--- a/tools/typings/keys.pyi
+++ b/tools/typings/keys.pyi
@@ -259,3 +259,8 @@ KMOD_ALT = KMOD_LALT | KMOD_RALT
KMOD_GUI = KMOD_LGUI | KMOD_RGUI
def chord_matches(chord, keycode, mod): ...
+def hid_keycode(usage): ...
+
+HID_MODIFIERS: Incomplete
+
+def hid_modifiers(bits): ...
diff --git a/tools/typings/multimer/__init__.pyi b/tools/typings/multimer/__init__.pyi
index b83974951..0b74e5f6c 100644
--- a/tools/typings/multimer/__init__.pyi
+++ b/tools/typings/multimer/__init__.pyi
@@ -1,11 +1,16 @@
-from ._async_timer import AsyncTimer as AsyncTimer
from ._asyncio_loader import loop_running as loop_running
-from ._schedule import schedule as schedule
-from ._ticks import monotonic as monotonic, run_deadline_hook as run_deadline_hook, set_deadline_hook as set_deadline_hook, ticks_add as ticks_add, ticks_diff as ticks_diff, ticks_less as ticks_less, ticks_ms as ticks_ms
+from ._dispatch import Timer as Timer, alive as alive, hold as hold, info as info, keepalive as keepalive, pump as pump, run_until as run_until, schedule as schedule, sleep_ms as sleep_ms, stop_all as stop_all, timers as timers
+from ._ticks import monotonic as monotonic, run_deadline_hook as run_deadline_hook, set_deadline_hook as set_deadline_hook, ticks_add as ticks_add, ticks_diff as ticks_diff, ticks_less as ticks_less, ticks_ms as ticks_ms, ticks_us as ticks_us
from _typeshed import Incomplete
asyncio: Incomplete
-__all__ = ['AsyncTimer', 'asyncio', 'loop_running', 'monotonic', 'run_deadline_hook', 'schedule', 'set_deadline_hook', 'ticks_add', 'ticks_diff', 'ticks_less', 'ticks_ms']
+__all__ = ['Timer', 'after', 'alive', 'asleep_ms', 'every', 'hold', 'info', 'keepalive', 'loop_running', 'monotonic', 'pump', 'repl', 'report', 'run_deadline_hook', 'run_until', 'schedule', 'set_deadline_hook', 'sleep_ms', 'stop_all', 'strategy', 'ticks_add', 'ticks_diff', 'ticks_less', 'ticks_ms', 'ticks_us', 'timers']
+def every(ms, callback, *, name=None): ...
+def after(ms, callback, *, name=None): ...
+async def asleep_ms(ms) -> None: ...
+def strategy(): ...
+def repl(namespace=None, prompt: str = '>>> ', tick_ms: int = 10): ...
+def report(file=None) -> None: ...
diff --git a/tools/typings/multimer/_async_timer.pyi b/tools/typings/multimer/_async_timer.pyi
deleted file mode 100644
index 3aeda639a..000000000
--- a/tools/typings/multimer/_async_timer.pyi
+++ /dev/null
@@ -1,5 +0,0 @@
-from ._asyncio_loader import load_asyncio as load_asyncio, loop_running as loop_running
-from ._core import _TimerCore
-
-class AsyncTimer(_TimerCore):
- def __init__(self, id: int = -1, **kwargs) -> None: ...
diff --git a/tools/typings/multimer/_core.pyi b/tools/typings/multimer/_core.pyi
deleted file mode 100644
index 4482582d4..000000000
--- a/tools/typings/multimer/_core.pyi
+++ /dev/null
@@ -1,14 +0,0 @@
-import types
-from ._schedule import schedule as schedule
-from ._ticks import ticks_diff as ticks_diff, ticks_ms as ticks_ms
-from _typeshed import Incomplete
-
-class _TimerCore:
- ONE_SHOT: Incomplete
- PERIODIC: Incomplete
- id: Incomplete
- def __init__(self, id: int = -1, **kwargs) -> None: ...
- def __enter__(self): ...
- def __exit__(self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None): ...
- def init(self, *, mode=..., freq: int = -1, period: int = -1, callback=None, hard: bool = True) -> None: ...
- def deinit(self) -> None: ...
diff --git a/tools/typings/multimer/_dispatch.pyi b/tools/typings/multimer/_dispatch.pyi
new file mode 100644
index 000000000..1869853af
--- /dev/null
+++ b/tools/typings/multimer/_dispatch.pyi
@@ -0,0 +1,58 @@
+import types
+from ._ticks import ticks_add as ticks_add, ticks_diff as ticks_diff, ticks_ms as ticks_ms
+from _typeshed import Incomplete
+
+ONE_SHOT: int
+PERIODIC: int
+
+def wake_from_source(safe: bool = False) -> None: ...
+def deliver(): ...
+def next_delay_ms(): ...
+def schedule(fn, arg) -> None: ...
+
+class Timer:
+ ONE_SHOT = ONE_SHOT
+ PERIODIC = PERIODIC
+ yield_cap: int
+ id: Incomplete
+ name: Incomplete
+ fired: int
+ missed: int
+ late_max: int
+ last: Incomplete
+ error: Incomplete
+ def __init__(self, id: int = -1, **kwargs) -> None: ...
+ def __enter__(self): ...
+ def __exit__(self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None): ...
+ def init(self, *, mode=..., freq: int = -1, period: int = -1, callback=None, hard: bool = False) -> None: ...
+ def deinit(self) -> None: ...
+ cancel = deinit
+ def reschedule(self, delay_ms) -> None: ...
+ @property
+ def period(self): ...
+ @property
+ def mode(self): ...
+ @property
+ def callback(self): ...
+ @property
+ def running(self): ...
+ @property
+ def due_in(self): ...
+
+def hold(): ...
+
+class _Hold:
+ def __enter__(self): ...
+ def __exit__(self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None): ...
+
+def sleep_ms(ms) -> None: ...
+def pump(): ...
+def run_until(pred, tick_ms: int = 10) -> None: ...
+def keepalive(flag: bool = True) -> None: ...
+def alive(): ...
+def timers(): ...
+def stop_all() -> None: ...
+def reset_stats() -> None: ...
+def source(): ...
+def stop_source() -> None: ...
+def info(): ...
diff --git a/tools/typings/multimer/_hostloop.pyi b/tools/typings/multimer/_hostloop.pyi
new file mode 100644
index 000000000..449e7a797
--- /dev/null
+++ b/tools/typings/multimer/_hostloop.pyi
@@ -0,0 +1,14 @@
+AMBIENT: str
+EXIT_HOOK: str
+NONE: str
+
+def ambient(): ...
+def interactive(): ...
+def batch(): ...
+def on_exit(fn): ...
+def mark_crashed() -> None: ...
+def claim() -> None: ...
+def release() -> None: ...
+def on_stop(fn) -> None: ...
+def ensure_installed(): ...
+def strategy(): ...
diff --git a/tools/typings/multimer/_inputhook.pyi b/tools/typings/multimer/_inputhook.pyi
new file mode 100644
index 000000000..f0ab32099
--- /dev/null
+++ b/tools/typings/multimer/_inputhook.pyi
@@ -0,0 +1,2 @@
+def install(): ...
+def installed(): ...
diff --git a/tools/typings/multimer/_mpasyncio.pyi b/tools/typings/multimer/_mpasyncio.pyi
index 0aff78dc0..277d13c41 100644
--- a/tools/typings/multimer/_mpasyncio.pyi
+++ b/tools/typings/multimer/_mpasyncio.pyi
@@ -1,6 +1,5 @@
from ._ticks import ticks_add as ticks_add, ticks_diff as ticks_diff
from _typeshed import Incomplete
-from collections.abc import Generator
class CancelledError(BaseException): ...
class TimeoutError(Exception): ...
@@ -10,8 +9,17 @@ class SingletonGenerator:
exc: Incomplete
def __init__(self) -> None: ...
def __iter__(self): ...
+ def __await__(self): ...
def __next__(self) -> None: ...
+class _Awaitable:
+ gen: Incomplete
+ def __init__(self, gen) -> None: ...
+ def __iter__(self): ...
+ def __await__(self): ...
+ def send(self, v): ...
+ def throw(self, *args): ...
+
class Event:
state: bool
waiting: Incomplete
@@ -19,7 +27,7 @@ class Event:
def is_set(self): ...
def set(self) -> None: ...
def clear(self) -> None: ...
- def wait(self) -> Generator[None, None, Incomplete]: ...
+ def wait(self): ...
def sleep_ms(t, sgen=...): ...
def sleep(t): ...
diff --git a/tools/typings/multimer/_repl.pyi b/tools/typings/multimer/_repl.pyi
new file mode 100644
index 000000000..c6118b5e6
--- /dev/null
+++ b/tools/typings/multimer/_repl.pyi
@@ -0,0 +1 @@
+def repl(namespace=None, prompt: str = '>>> ', tick_ms: int = 10) -> None: ...
diff --git a/tools/typings/multimer/_schedule.pyi b/tools/typings/multimer/_schedule.pyi
deleted file mode 100644
index 7fb499f2f..000000000
--- a/tools/typings/multimer/_schedule.pyi
+++ /dev/null
@@ -1,3 +0,0 @@
-from micropython import schedule as schedule
-
-def schedule(cb, arg) -> None: ...
diff --git a/tools/typings/multimer/_src_asyncio.pyi b/tools/typings/multimer/_src_asyncio.pyi
new file mode 100644
index 000000000..e42775e39
--- /dev/null
+++ b/tools/typings/multimer/_src_asyncio.pyi
@@ -0,0 +1,8 @@
+name: str
+delivery: str
+wakes_blocking: bool
+
+def start(wake) -> None: ...
+def arm(delay_ms) -> None: ...
+def cancel() -> None: ...
+def stop() -> None: ...
diff --git a/tools/typings/multimer/_src_machine.pyi b/tools/typings/multimer/_src_machine.pyi
new file mode 100644
index 000000000..5ed8c5161
--- /dev/null
+++ b/tools/typings/multimer/_src_machine.pyi
@@ -0,0 +1,10 @@
+from ._ticks import ticks_add as ticks_add, ticks_diff as ticks_diff, ticks_ms as ticks_ms
+
+name: str
+delivery: str
+wakes_blocking: bool
+
+def start(wake) -> None: ...
+def arm(delay_ms) -> None: ...
+def cancel() -> None: ...
+def stop() -> None: ...
diff --git a/tools/typings/multimer/_src_native.pyi b/tools/typings/multimer/_src_native.pyi
new file mode 100644
index 000000000..e42775e39
--- /dev/null
+++ b/tools/typings/multimer/_src_native.pyi
@@ -0,0 +1,8 @@
+name: str
+delivery: str
+wakes_blocking: bool
+
+def start(wake) -> None: ...
+def arm(delay_ms) -> None: ...
+def cancel() -> None: ...
+def stop() -> None: ...
diff --git a/tools/typings/multimer/_src_none.pyi b/tools/typings/multimer/_src_none.pyi
new file mode 100644
index 000000000..e42775e39
--- /dev/null
+++ b/tools/typings/multimer/_src_none.pyi
@@ -0,0 +1,8 @@
+name: str
+delivery: str
+wakes_blocking: bool
+
+def start(wake) -> None: ...
+def arm(delay_ms) -> None: ...
+def cancel() -> None: ...
+def stop() -> None: ...
diff --git a/tools/typings/multimer/_src_pending.pyi b/tools/typings/multimer/_src_pending.pyi
new file mode 100644
index 000000000..e42775e39
--- /dev/null
+++ b/tools/typings/multimer/_src_pending.pyi
@@ -0,0 +1,8 @@
+name: str
+delivery: str
+wakes_blocking: bool
+
+def start(wake) -> None: ...
+def arm(delay_ms) -> None: ...
+def cancel() -> None: ...
+def stop() -> None: ...
diff --git a/tools/typings/multimer/_src_signal.pyi b/tools/typings/multimer/_src_signal.pyi
new file mode 100644
index 000000000..c47336aa7
--- /dev/null
+++ b/tools/typings/multimer/_src_signal.pyi
@@ -0,0 +1,15 @@
+import ctypes
+
+name: str
+delivery: str
+wakes_blocking: bool
+
+class _timespec(ctypes.Structure): ...
+class _itimerspec(ctypes.Structure): ...
+class _sigval(ctypes.Union): ...
+class _sigevent(ctypes.Structure): ...
+
+def start(wake) -> None: ...
+def arm(delay_ms) -> None: ...
+def cancel() -> None: ...
+def stop() -> None: ...
diff --git a/tools/typings/multimer/_src_wasm.pyi b/tools/typings/multimer/_src_wasm.pyi
new file mode 100644
index 000000000..6b5bd89aa
--- /dev/null
+++ b/tools/typings/multimer/_src_wasm.pyi
@@ -0,0 +1,10 @@
+name: str
+delivery: str
+wakes_blocking: bool
+
+def start(wake) -> None: ...
+def arm(delay_ms) -> None: ...
+def cancel() -> None: ...
+def stop() -> None: ...
+def poll() -> None: ...
+def sleep_ms(ms) -> None: ...
diff --git a/tools/typings/multimer/_ticks.pyi b/tools/typings/multimer/_ticks.pyi
index 563fb4eff..85e974303 100644
--- a/tools/typings/multimer/_ticks.pyi
+++ b/tools/typings/multimer/_ticks.pyi
@@ -1,4 +1,5 @@
def ticks_ms(): ...
+def ticks_us(): ...
def monotonic(): ...
def ticks_add(ticks, delta): ...
def ticks_diff(ticks1, ticks2): ...
diff --git a/tools/typings/multimer/auto.pyi b/tools/typings/multimer/auto.pyi
deleted file mode 100644
index a190ff235..000000000
--- a/tools/typings/multimer/auto.pyi
+++ /dev/null
@@ -1,20 +0,0 @@
-from . import AsyncTimer
-from _typeshed import Incomplete
-
-__all__ = ['Timer', 'is_async', 'name', 'pump', 'sleep_ms', 'uses_interrupts']
-
-class _AsyncProvider:
- Timer = AsyncTimer
- name: str
- uses_interrupts: bool
- is_async: bool
- sleep_ms: Incomplete
- @staticmethod
- def pump() -> None: ...
-
-Timer: Incomplete
-name: Incomplete
-uses_interrupts: Incomplete
-is_async: Incomplete
-sleep_ms: Incomplete
-pump: Incomplete
diff --git a/tools/typings/multimer/librt.pyi b/tools/typings/multimer/librt.pyi
deleted file mode 100644
index d83814961..000000000
--- a/tools/typings/multimer/librt.pyi
+++ /dev/null
@@ -1,20 +0,0 @@
-import ctypes
-from ._core import _TimerCore
-
-__all__ = ['Timer', 'is_async', 'name', 'pump', 'sleep_ms', 'uses_interrupts']
-
-name: str
-uses_interrupts: bool
-is_async: bool
-
-def pump() -> None: ...
-def sleep_ms(ms) -> None: ...
-librt = libc
-
-class _timespec(ctypes.Structure): ...
-class _itimerspec(ctypes.Structure): ...
-class _sigval(ctypes.Union): ...
-class _sigevent(ctypes.Structure): ...
-librt = libc
-
-class Timer(_TimerCore): ...
diff --git a/tools/typings/multimer/machine.pyi b/tools/typings/multimer/machine.pyi
deleted file mode 100644
index 2a96b7713..000000000
--- a/tools/typings/multimer/machine.pyi
+++ /dev/null
@@ -1,10 +0,0 @@
-from machine import Timer as Timer
-
-__all__ = ['Timer', 'name', 'uses_interrupts', 'is_async', 'pump', 'sleep_ms']
-
-name: str
-uses_interrupts: bool
-is_async: bool
-
-def pump() -> None: ...
-def sleep_ms(ms) -> None: ...
diff --git a/tools/typings/multimer/polling.pyi b/tools/typings/multimer/polling.pyi
deleted file mode 100644
index 2102e8e7c..000000000
--- a/tools/typings/multimer/polling.pyi
+++ /dev/null
@@ -1,12 +0,0 @@
-from ._core import _TimerCore
-
-__all__ = ['Timer', 'is_async', 'name', 'pump', 'sleep_ms', 'uses_interrupts']
-
-name: str
-uses_interrupts: bool
-is_async: bool
-
-class Timer(_TimerCore): ...
-
-def pump() -> None: ...
-def sleep_ms(ms) -> None: ...
diff --git a/tools/typings/multimer/sdl2.pyi b/tools/typings/multimer/sdl2.pyi
deleted file mode 100644
index 5102adb9a..000000000
--- a/tools/typings/multimer/sdl2.pyi
+++ /dev/null
@@ -1,13 +0,0 @@
-from ._core import _TimerCore
-
-__all__ = ['Timer', 'is_async', 'name', 'pump', 'sleep_ms', 'uses_interrupts']
-
-name: str
-uses_interrupts: bool
-is_async: bool
-
-class Timer(_TimerCore):
- def __init__(self, id: int = -1, **kwargs) -> None: ...
-
-def pump() -> None: ...
-def sleep_ms(ms) -> None: ...
diff --git a/tools/typings/multimer/threading.pyi b/tools/typings/multimer/threading.pyi
deleted file mode 100644
index 18a8e888b..000000000
--- a/tools/typings/multimer/threading.pyi
+++ /dev/null
@@ -1,13 +0,0 @@
-from ._core import _TimerCore
-
-__all__ = ['Timer', 'is_async', 'name', 'pump', 'sleep_ms', 'uses_interrupts']
-
-name: str
-uses_interrupts: bool
-is_async: bool
-
-def pump() -> None: ...
-def sleep_ms(ms) -> None: ...
-
-class Timer(_TimerCore):
- def __init__(self, id: int = -1, **kwargs) -> None: ...
diff --git a/tools/typings/multimer/win32.pyi b/tools/typings/multimer/win32.pyi
deleted file mode 100644
index 2ba3dde15..000000000
--- a/tools/typings/multimer/win32.pyi
+++ /dev/null
@@ -1,13 +0,0 @@
-from ._core import _TimerCore
-
-__all__ = ['name', 'uses_interrupts', 'is_async', 'pump', 'sleep_ms', 'Timer']
-
-name: str
-uses_interrupts: bool
-is_async: bool
-
-def pump() -> None: ...
-def sleep_ms(ms) -> None: ...
-
-class Timer(_TimerCore):
- def __init__(self, id: int = -1, **kwargs) -> None: ...