feat(hosts): add PocketBook inkview e-ink host#172
Conversation
Move the renderer-independent half of the `ui` surface — UiSurface (pak feeding + HostOps), the pak walker, and the DevTools mailbox — out of pocket-ui-wgpu into a new pocket-ui-surface crate. pocket-ui-wgpu keeps the wgpu DrawList backend and re-exports UiSurface/walk_pak/PakEntry, so desktop consumers (uihost, pocket-widget, handheld, note-widget) stay source-compatible. This lets non-GPU hosts (the PocketBook e-ink host) build on the shared surface without dragging wgpu into their dependency tree.
The framework's frame entry point is frame(buttons, analog?, touches?) (framework/src/host.ts), with touches decoded from a packed u32 array ((id<<18)|(y<<9)|x, framework/src/touch.ts). pocket-mod only exposed frame/frame_with_analog, so touch-capable hosts had no way to deliver contacts. Add the 3-arg path as an additive method; frame and frame_with_analog (and every existing host, tape, and golden) are unchanged.
A new standalone host (same lone-bin model as hosts/psp and hosts/vita) that runs the PocketJS UI runtime on PocketBook e-readers via inkview: - reuses pocket-ui-surface::UiSurface for the whole `ui` HostOps surface and pak feeding, and the core's software rasterizer (render_scaled) at logical x density resolution; - framebuffer.rs: RGBA8 -> Gray8 luminance conversion with 16x16-tile damage tracking; - refresh.rs: e-ink update policy ported from inkview-slint (is_updating gate; partial when idle, throttled dynamic during motion, 200 ms quiet cleanup to clear ghosting); - input.rs: inkview keys -> spec BTN bitmask, touchscreen -> the packed touch wire format in logical px; - main.rs: iv_main forwards events into a channel; a second thread owns the Screen and the fixed-cadence tick/render loop (the inkview-slint demo model). The logical viewport stays <=511 px/axis so touch coordinates fit the 9-bit wire format; density is configurable (POCKET_DENSITY). Compiles natively, clippy-clean, with framebuffer/input unit tests. On-device validation (refresh tuning, touch accuracy, viewport choice) still needs hardware. See pocketjs-inkview-implementation.md.
The integration guide is the feasibility study; the implementation guide turns it into a buildable plan grounded in the actual pocketjs and inkview-rs APIs (corrections table, revised architecture, per-module code, the touch/viewport design decision, and a phased checklist).
|
Apologies for the draft MR, I was trying to do some investigation/MVP integration work and didn't realize that after the final validations the agent wanted to open a MR (and it even got quite creative to bypass the fact that I cloned using HTTPS, big mistake installing Let me know if a e-ink host is still in scope of the project or not, I discovered it this morning and the first thing that come to my mind was "oh wow this is so much better than slint, gotta try it!", I will keep it local only in case (this still needs work and testing on a physical device of course but I own both a Era Color and a Verse so I can continue working on it). |
Register a `pocketbook` target (hostAbi 4) in POCKET_TARGETS, shaped like vita: a 480x272 logical viewport at rasterDensity 2 (a 960x544 render), integer-fit presentation, and input.buttons + input.touch + text.glyphs.baked capabilities (no analog nub, real capacitive pointer instead of the synthesized cursor). physicalViewport is the nominal 2x surface — not the raw panel, which varies by model and is not an integer multiple of 480x272 — so integer-fit apps validate; the host centers the render on the actual panel. Add the matching `pocketbook` backend to tools/pocket.ts (required by the Record<PocketTargetId, …> satisfies). The host loads the pak + bundle from the device filesystem, so the backend just confirms the bundle is ready; the host ELF is cross-compiled separately. `bun pocket compile --target pocketbook` now builds hero (and any 480x272 integer-fit app) for the host.
…h bundle Two corrections from targeting real hardware (PocketBook Verse grayscale and Era Color Kaleido-3 color): - Blit RGB24 instead of a host-side Gray8 conversion. inkview's Screen::draw is generic over the pixel format and branches on panel depth: it converts RGB24 -> 8-bit gray internally on grayscale panels and writes RGB directly on color panels, so one blit path serves both. Damage now diffs the RGBA8 raster buffer directly (conservatively over-reports on equal-luminance hue shifts, which is safe). - Present the bundle's fixed 480x272 @2x viewport (a 960x544 render) and integer-fit center it on the panel, instead of auto-deriving a logical viewport from the panel size. The framework lays the app out for the viewport the target profile bakes (480x272), so the host must present exactly that; auto-deriving produced a mismatched 511x379 layout. Also split diff/advance so the blit reads the current frame (the previous swap-after-diff order would have drawn the prior frame).
… shim) Two fixes surfaced by `cargo zigbuild --target armv7-unknown-linux-gnueabi.2.23`: - rquickjs ships pre-generated FFI bindings for common targets but not the soft-float armv7-unknown-linux-gnueabi that PocketBook needs, so enable its bindgen feature for ARM builds only (target-specific dep; needs libclang). Native builds keep the pre-generated bindings. - LLVM 19+ lowers f32::max/min to C23 math symbols (fmaximum_numf, fminimum_num, …) that PocketBook's glibc 2.23 predates, failing the link. build.rs compiles a small shim (src/compat.c) providing those symbols for the cross-build only (glibc 2.23 defines none of them, so no clash); native builds use the system libm. Produces a stripped 2.3M ARM EABI5 ELF requiring only glibc <=2.18, dlopening libinkview.so at runtime.
deploy.sh installs the cross-compiled host + a built app bundle into applications/<name>/ on a USB-mounted PocketBook (auto-detects the mount point). README updated with the verified build steps (zigbuild, bindgen + libclang, the glibc math shim), the color/gray blit behavior, and a per-device checklist covering boot/render, touch + key input, and e-ink refresh for both the grayscale Verse and the color Era Color.
- Add nearest-neighbor scale-to-fit in Geometry::for_panel so the 960×544 render is downscaled to fit panels narrower than the render buffer (e.g. Verse 758×1024 → disp 758×429, centered). - Framebuffer blit_all/blit_dirty now iterate screen pixels and sample from the render buffer via inverse mapping. - Touch input maps screen→logical through disp_w/disp_h instead of dividing by density, so taps land correctly under scaling. - deploy.sh creates the .app launcher script (required for PocketBook firmware to discover the app) and redirects stdout/stderr to pocketjs.log in the app directory for post-mortem debugging. - Whitespace normalization in platforms.ts and pocket.ts.
Mark boot/render, scale-to-fit centering, @2x text, grayscale output, and animated partial updates as validated on the PocketBook Verse (2026-07-24). Add a 'Validated on hardware' subsection, a Logs note pointing at the on-device pocketjs.log, and refresh the top Status paragraph. Input, idle ghosting, background-return, and color panels remain unchecked.
|
Thanks for this awesome experiment! Will do some arch refactoring to make the host more scalable for landing this PR. |
- contracts/spec/platforms.ts and tools/pocket.ts: drop the wholesale tab reformat and reapply only the pocketbook additions in house style (the diff against main is now pure additions). - tests/platform-contracts.test.ts: register pocketbook in the exhaustive target-key assertion (the full bun chain fails without it — the repo has no CI for it) and pin its capabilities/display profile. - Move the two repo-root design docs into hosts/pocketbook/docs/ (docs/STRUCTURE.md: nothing else gets a top-level name) and update references. - deploy.sh -> deploy.ts: repo scripts are Bun TypeScript; the device-side .app launcher it writes stays /bin/sh (a firmware requirement). - Pin inkview to a git rev instead of a sibling-checkout path dep so the crate builds standalone (same policy as the PSP toolchain pins). - input.rs: reuse pocketjs_core::spec::btn instead of a local BTN table. - docs/STRUCTURE.md: record pocket-ui-surface and hosts/pocketbook. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Compose the two damage layers so each does what the other cannot: - raster::render_scaled_incremental + a core DamageTracker repaint only the DrawList-diffed regions of a persistent RGBA8 target — an idle frame plans, rasterizes, and scans nothing; - the 16x16 pixel tile diff now runs only INSIDE the damage regions and still owns the e-ink refresh decision: conservative DrawList damage that renders identical pixels flashes nothing. The retained buffer always holds the complete current frame, so the Show/full path re-blits without re-rasterizing; prev latches only the blitted tiles. Pixel parity with full renders is unit-tested across moving frames, density 2, structural changes, and equal-pixel repaints. On-device note: the Verse validation pass predates this change; the README checklist asks for a re-confirm of animations + Show. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Reviewed the whole PR and pushed three maintainer commits (a merge with current main + What I pushedMerge with main — brings in #160/#173/#174/#155; everything below builds on #173's damage infrastructure.
For your next device passThe Verse validation predates the incremental rewire — please re-confirm animations + Show/background-return on hardware (the README checklist notes this), plus the still-open items (touch, hardware keys, idle ghosting, Era Color). One design question before undrafting: |
|
@wonderbeel could you double check if current refactored branch works you there? I think we can merge after your confirmation, thanks! |
|
I will give it a try when I am back home, thank you for the extra pass 🙏 |
MOV_0853.mp4Tested after your refactor @doodlewind and it works just fine, the only thing that I noticed is that the progress bar animation now is slightly different but it actually looks more similar to how the demo renders on the desktop. If you are fine merging this branch as is I would flag that this host is still work in progress, I will try to iterate in the next weeks on top of it to complete it. PS: If there is enough interest in e-ink devices my parents have a couple of jailbroken kindles, after pocketbook is stable I can try to tackle them too :). |
…ew-host # Conflicts: # docs/STRUCTURE.md
pocket-stack#176 landed symbian-e7-dev on hostAbi 4 while this branch was open, so two contracts claimed the same wire generation. assertNativeHostContract checks (target, abi) so nothing mis-binds, but the number is a registry-wide sequence — the incoming target yields to 5 (psp=1, vita=2, macos-widget=3, symbian-e7-dev=4). Also register pocketbook in tests/symbian-runtime.test.ts (a second exhaustive target-key assertion) and record the on-device re-confirm plus the WIP status in the README. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Merged — thanks for the device re-run and the video 🙏 The animation difference you noticed is expected and benign: the tick loop no longer re-rasterizes and re-scans the whole 960×544 frame every 33 ms, so the progress bar now runs at its intended cadence (hence the closer resemblance to the desktop host). Recorded that in the README. Two things I changed while resolving the merge: #176 landed And yes — jailbroken Kindles would be very welcome. The e-ink layering here should port well: DrawList damage bounds the raster, the pixel tile diff decides what the panel refreshes, and only the panel-driver half (inkview → whatever the Kindle exposes) would be new. Happy to review whenever you get to it. |

Summary
A new PocketJS host that renders the UI runtime on PocketBook e-readers via the inkview SDK, plus the two small shared changes it builds on. Design is documented in
pocketjs-inkview-implementation.md(companion to thepocketjs-inkview-integration.mdfeasibility study).Commits
refactor(engine): extract backend-agnostic pocket-ui-surface crate— moves the renderer-independentuisurface (UiSurface, pak walker, DevTools mailbox) out ofpocket-ui-wgpuso non-GPU hosts can reuse it without dragging in wgpu.pocket-ui-wgpure-exports it; desktop consumers are source-compatible.feat(pocket-mod): add Guest::frame_with_touches— additive 3-argframe(buttons, analog, touches)path (the framework already decodes a packed-touch array; pocket-mod had no way to deliver it).feat(hosts): add PocketBook inkview e-ink host— the standalone host (same lone-bin model ashosts/psp/hosts/vita).docs: integration + implementation guides.How the host works
UiSurfacefor the entireuiHostOps surface + pak feeding, and the core's software rasterizer (render_scaled) atlogical × density.framebuffer.rs: RGBA8 → Gray8 luminance + 16×16-tile damage.refresh.rs: e-ink update policy ported frominkview-slint(partial when idle, throttled dynamic during motion, 200 ms quiet cleanup).input.rs: inkview keys → spec BTN bits; touchscreen → packed touch wire format.main.rs:iv_mainforwards events into a channel; a second thread owns theScreen+ fixed-cadence tick/render loop (theinkview-slintdemo model).Verification
cargo checkpasses;pocket-mod/pocket-ui-surface/pocket-widgettests pass.Not done yet (draft)
pocketbookentry incontracts/spec/platforms.ts+ build backend (Phase 5). The host currently setsset_identity("pocketbook", 4)in anticipation.inkviewdependency is a local path dep for now; switch to the git dep inhosts/pocketbook/Cargo.tomlfor CI.