Watchapp skeleton: app lifecycle, AppMessage inbox, placeholder page transitions (#8) #83

Merged
robert merged 1 commit from area/watchapp-skeleton into main 2026-09-04 09:05:35 +02:00
Owner

Closes #8, scoped per the issue's 2026-08-31 "view stack becomes a page carousel" update, which supersedes the original body (three-window stack, view_ride.c/view_nav.c/view_map.c, Up/Down cycling, Back confirmation). That original design is dead; button handling and the real carousel are #60's job, not this one's.

What this issue keeps, and what it lands

  • App lifecycle — watchapp/src/c/main.c: init/deinit and app_event_loop(). This is the watchapp's first real entry point; #75 only scaffolded the toolchain with an essentially-empty placeholder window (watchapp.c), which this replaces.
  • AppMessage inbox opened — app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum()), exactly the call the Pebble AppMessage docs themselves recommend. A minimal inbox-received handler is registered before that call, because the docs are explicit that an app with no handler registered gets every inbound message NACK'ed without ever seeing it. That handler currently just logs receipt — decoding tuples into the field store is separate work, out of scope here.
  • heap_bytes_free() logged at page transitions — via page_view.c/page_view.h, a deliberately minimal placeholder: Select cycles through page_default_descriptor()'s three default pages (page.h/page.c from #58/PR #81) and renders the active page's name plus its emphasised-slot field via field_format(). It is not #59's template renderer and not #60's carousel — no skip-a-sourceless-page rule, no Up/Down, no long-press jumps to page 1/the map, no Back confirmation. All of that is docs/DESIGN.md section 5, implemented in #60.
  • basalt (144×168) degradation — same TextLayer-based placeholder, centred, legible. See screenshots below.

Files — deviates from the issue's stale list

The issue names view_ride.c/view_nav.c/view_map.c, which is the pre-carousel design the 2026-08-31 update replaced. Actual files, matching the page-carousel shape #58 already built:

  • watchapp/src/c/main.c (new)
  • watchapp/src/c/page_view.c / page_view.h (new)
  • watchapp/src/c/watchapp.c (deleted — the placeholder window #75 scaffolded)

Heap cost of AppMessage — measured, not assumed

Bracketed heap_bytes_free() around the app_message_open() call only (not around window/layer setup), on all three emulator targets:

Target heap before heap after cost
emery 127028 110528 16500 bytes
gabbro 126988 110488 16500 bytes
basalt 61452 44952 16500 bytes

16500 = 8200 (inbox) + 8200 (outbox) + ~100 bytes of AppMessage bookkeeping, and it's identical across all three platforms in this SDK build — the "maximum" buffer size is not actually platform-scaled here, contrary to what I'd have guessed going in. On basalt's ~61.8 KB total heap that's roughly 73% of the entire heap gone to AppMessage alone before a single window or field buffer exists. emery/gabbro have ~127 KB total, so the same 16.5 KB is a much smaller bite there. Worth keeping in mind for anything else that wants heap on basalt.

One more thing this surfaced: my first version logged the cost in a single APP_LOG call and it silently truncated mid-sentence on the emery emulator (cut off right after (cost) — Pebble's APP_LOG has a buffer limit and the combined message ran past it. Fixed by splitting into two APP_LOG calls (see main.c); flagging it here since it's exactly the kind of "looks fine until you actually run it" thing D34 exists for.

Build / run confirmation

  • pebble build is clean for emery, gabbro, and basalt (all three in targetPlatforms) — no warnings from any new/changed source file.
  • All three installed and launched in the emulator without crashing (pebble install --emulator <target>).
  • emery's Select button was exercised live in the emulator (pebble emu-button --emulator emery click select) to confirm the placeholder page transition and its heap log both fire correctly:
    main.c:67> AppMessage open: result=0 inbox=8200 outbox=8200
    main.c:69> AppMessage heap_bytes_free 127028 -> 110528 (cost 16500 bytes)
    page_view.c:58> Page transition -> Effort: heap_bytes_free 110568 -> 110568
    page_view.c:58> Page transition -> Progress: heap_bytes_free 110568 -> 110568
    
    Heap is flat across placeholder transitions (no per-press growth), as expected since nothing allocates there.
  • Screenshots taken on all three targets (emery, gabbro, basalt) confirm the placeholder renders and is legible; -- correctly shows for the emphasised field since nothing has populated the field store yet (no AppMessage decoding in this issue).

Not in this PR (by design)

  • Real carousel navigation (Up/Down, wraparound-with-skip, long-press jumps, Back confirmation) — #60.
  • Template rendering (HERO1/HERO2/QUAD/GRID6 layouts) — #59.
  • Decoding inbound AppMessage tuples into the field store — separate, not yet ticketed as far as I could find.
  • Round-specific status-strip-as-arc geometry on gabbro — #62.
Closes #8, scoped per the issue's **2026-08-31 "view stack becomes a page carousel" update**, which supersedes the original body (three-window stack, `view_ride.c`/`view_nav.c`/`view_map.c`, Up/Down cycling, Back confirmation). That original design is dead; button handling and the real carousel are #60's job, not this one's. ## What this issue keeps, and what it lands - **App lifecycle** — `watchapp/src/c/main.c`: `init`/`deinit` and `app_event_loop()`. This is the watchapp's first real entry point; #75 only scaffolded the toolchain with an essentially-empty placeholder window (`watchapp.c`), which this replaces. - **AppMessage inbox opened** — `app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum())`, exactly the call the Pebble AppMessage docs themselves recommend. A minimal inbox-received handler is registered *before* that call, because the docs are explicit that an app with no handler registered gets every inbound message NACK'ed without ever seeing it. That handler currently just logs receipt — **decoding tuples into the field store is separate work**, out of scope here. - **`heap_bytes_free()` logged at page transitions** — via `page_view.c`/`page_view.h`, a deliberately minimal placeholder: Select cycles through `page_default_descriptor()`'s three default pages (`page.h`/`page.c` from #58/PR #81) and renders the active page's name plus its emphasised-slot field via `field_format()`. It is **not** #59's template renderer and **not** #60's carousel — no skip-a-sourceless-page rule, no Up/Down, no long-press jumps to page 1/the map, no Back confirmation. All of that is docs/DESIGN.md section 5, implemented in #60. - **`basalt` (144×168) degradation** — same TextLayer-based placeholder, centred, legible. See screenshots below. ## Files — deviates from the issue's stale list The issue names `view_ride.c`/`view_nav.c`/`view_map.c`, which is the pre-carousel design the 2026-08-31 update replaced. Actual files, matching the page-carousel shape #58 already built: - `watchapp/src/c/main.c` (new) - `watchapp/src/c/page_view.c` / `page_view.h` (new) - `watchapp/src/c/watchapp.c` (deleted — the placeholder window #75 scaffolded) ## Heap cost of AppMessage — measured, not assumed Bracketed `heap_bytes_free()` around the `app_message_open()` call only (not around window/layer setup), on all three emulator targets: | Target | heap before | heap after | cost | |---|---|---|---| | `emery` | 127028 | 110528 | **16500 bytes** | | `gabbro` | 126988 | 110488 | **16500 bytes** | | `basalt` | 61452 | 44952 | **16500 bytes** | 16500 = 8200 (inbox) + 8200 (outbox) + ~100 bytes of AppMessage bookkeeping, and it's identical across all three platforms in this SDK build — the "maximum" buffer size is not actually platform-scaled here, contrary to what I'd have guessed going in. On `basalt`'s ~61.8 KB total heap that's **roughly 73% of the entire heap** gone to AppMessage alone before a single window or field buffer exists. `emery`/`gabbro` have ~127 KB total, so the same 16.5 KB is a much smaller bite there. Worth keeping in mind for anything else that wants heap on `basalt`. One more thing this surfaced: my first version logged the cost in a single `APP_LOG` call and it **silently truncated mid-sentence** on the emery emulator (cut off right after `(cost`) — Pebble's `APP_LOG` has a buffer limit and the combined message ran past it. Fixed by splitting into two `APP_LOG` calls (see `main.c`); flagging it here since it's exactly the kind of "looks fine until you actually run it" thing D34 exists for. ## Build / run confirmation - `pebble build` is clean for `emery`, `gabbro`, and `basalt` (all three in `targetPlatforms`) — no warnings from any new/changed source file. - All three installed and launched in the emulator without crashing (`pebble install --emulator <target>`). - `emery`'s Select button was exercised live in the emulator (`pebble emu-button --emulator emery click select`) to confirm the placeholder page transition and its heap log both fire correctly: ``` main.c:67> AppMessage open: result=0 inbox=8200 outbox=8200 main.c:69> AppMessage heap_bytes_free 127028 -> 110528 (cost 16500 bytes) page_view.c:58> Page transition -> Effort: heap_bytes_free 110568 -> 110568 page_view.c:58> Page transition -> Progress: heap_bytes_free 110568 -> 110568 ``` Heap is flat across placeholder transitions (no per-press growth), as expected since nothing allocates there. - Screenshots taken on all three targets (emery, gabbro, basalt) confirm the placeholder renders and is legible; `--` correctly shows for the emphasised field since nothing has populated the field store yet (no AppMessage decoding in this issue). ## Not in this PR (by design) - Real carousel navigation (Up/Down, wraparound-with-skip, long-press jumps, Back confirmation) — #60. - Template rendering (HERO1/HERO2/QUAD/GRID6 layouts) — #59. - Decoding inbound AppMessage tuples into the field store — separate, not yet ticketed as far as I could find. - Round-specific status-strip-as-arc geometry on gabbro — #62.
Watchapp skeleton: app lifecycle, AppMessage inbox, placeholder page transitions (#8)
Some checks failed
dev-artifact / build-pbw (push) Has been cancelled
dev-artifact / build-apk (push) Has been cancelled
fast-lane / host-c-tests (push) Has been cancelled
fast-lane / jvm-tests (push) Has been cancelled
fast-lane / pebble-build (push) Has been cancelled
fast-lane / lint-and-secrets (push) Has been cancelled
fast-lane / meta-declares-required-jobs (push) Has been cancelled
fast-lane / host-c-tests (pull_request) Has been cancelled
fast-lane / jvm-tests (pull_request) Has been cancelled
fast-lane / pebble-build (pull_request) Has been cancelled
fast-lane / lint-and-secrets (pull_request) Has been cancelled
fast-lane / meta-declares-required-jobs (pull_request) Has been cancelled
dev-artifact / publish (push) Has been cancelled
965e534f54
Scope per the 2026-08-31 update on #8 ("view stack becomes a page
carousel"), which supersedes the issue's original three-window-stack
design (view_ride.c/view_nav.c/view_map.c, Up/Down cycling, Back
confirmation). Button handling and the real carousel are #60's job.

This lands:

- main.c: app lifecycle (init/deinit, app_event_loop), replacing the
  placeholder watchapp.c that #75 scaffolded.
- AppMessage opened via app_message_open(app_message_inbox_size_maximum(),
  app_message_outbox_size_maximum()) — the Pebble docs' own recommended
  call. A minimal inbox-received handler is registered first (unregistered
  inboxes NACK every message without the app seeing it, per the AppMessage
  docs); it does not decode tuples, which is separate work.
- page_view.c/.h: a minimal placeholder that cycles through
  page_default_descriptor()'s three default pages on a Select click,
  showing the page name and its emphasised field via field_format(). Not
  #59's template renderer, not #60's carousel — no skip-empty-page rule,
  no Up/Down, no long-press jumps, no Back confirmation.
- heap_bytes_free() logged (APP_LOG) around the AppMessage open call and
  at each placeholder page transition.

Measured, not assumed (emery emulator): AppMessage open costs 16500 bytes
of heap (8200 inbox + 8200 outbox + ~100 bytes bookkeeping), consistent
across emery/gabbro/basalt. On basalt's 61.8KB total heap that's roughly
73% of it gone to AppMessage alone — the issue's "measure before assuming
it fits" note was correct to insist on.

Files deviate from the issue's stale "Files" list (view_ride.c/view_nav.c/
view_map.c is the pre-carousel design); main.c + page_view.c/.h match the
shape #58 already built.

pebble build is clean for emery, gabbro and basalt; all three installed
and launched in the emulator without crashing. emery's Select button was
exercised live to confirm the placeholder transition and its heap log
fire correctly.

Claude-Session: https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt
robert merged commit c36416eae6 into main 2026-09-04 09:05:35 +02:00
Sign in to join this conversation.
No description provided.