mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-09-14 15:16:40 +00:00
feat(sim): long-press context menu + pixel-perfect real font rendering
Two prototype polish fixes ahead of the website embed work: 1. Holding Enter (the sim's main way into "the rest of the options") did nothing -- the JS bridge's sim_enqueue_key() went straight to enqueueKey(), bypassing UITask::handleLongPress() entirely, so KEY_CONTEXT_MENU could never be reached. Add injectSimKeyLongPress()/ sim_enqueue_key_longpress(), which does route through the real handleLongPress() (same code a real MomentaryButton(pin, 1000, ...) reaches), and wire up press-and-hold (buttons + Enter/Space key) in both web harnesses with the same 1000ms threshold real hardware uses. 2. SimDisplayDriverCanvas::print() drew text with the browser's own system font (ctx.fillText, '8px monospace') instead of the real bitmap font a MeshCore-Solo board renders with OLED_MISC_FIXED_FONT=1 (see solo/heltec_v3/platformio.ini). Vendor the real Adafruit_GFX (unmodified, from the same PlatformIO registry package a real board build pulls) into variants/sim/thirdparty/gfx/, and render print() through the real, shared src/helpers/ui/MiscFixedRenderer.h + MiscFixedFont.h -- byte- identical glyphs to real hardware, not a look-alike. Verified in a real Chromium (Playwright): short Enter -> "CLOCK TOOLS", held Enter -> "CLOCK FIELDS" (gotoDashboardConfig(), proving KEY_CONTEXT_MENU is really reached); font renders as hard square pixels, inverse/selected-row text still punches correctly through a filled bar. Full regression re-run clean: all 3 native envs, wasm companion_radio, and the two-instance + repeater mesh demo (relay routing, DM delivery). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include <Arduino.h>
|
||||
#include <cstring>
|
||||
#include "target.h"
|
||||
#include "SimRNG.h"
|
||||
|
||||
@@ -26,3 +27,73 @@ mesh::LocalIdentity radio_new_identity() {
|
||||
rng.begin();
|
||||
return mesh::LocalIdentity(&rng);
|
||||
}
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
// Real bitmap-font text rendering for SimDisplayDriverCanvas::print()
|
||||
// (declared in SimDisplayDriver.h, defined here -- the one TU allowed to
|
||||
// include MiscFixedRenderer.h; see that header's own "include only from a
|
||||
// .cpp" comment). Uses the exact same font tables + glyph-plotting math a
|
||||
// real MeshCore-Solo board renders with OLED_MISC_FIXED_FONT=1 (see
|
||||
// solo/heltec_v3/platformio.ini), instead of the browser's own system font.
|
||||
// Adafruit_GFX.h/.cpp branch on `#if ARDUINO >= 100` in exactly two spots
|
||||
// (which Arduino.h to #include, and whether write(uint8_t) returns size_t
|
||||
// or void) -- our own Arduino.h shim deliberately never defines ARDUINO
|
||||
// globally (other vendored libs branch on #ifdef ARDUINO to pick their
|
||||
// portable std:: path instead of Arduino String/Stream), so define it here,
|
||||
// scoped to this one translation unit, purely so Adafruit_GFX picks the
|
||||
// modern branch that actually matches our Print.h shim's
|
||||
// `size_t write(uint8_t)` signature.
|
||||
#define ARDUINO 100
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <helpers/ui/MiscFixedRenderer.h>
|
||||
|
||||
// Adafruit_GFX subclass that plots into a plain 128x64 byte buffer (one
|
||||
// pixel per byte, 0/1) instead of real display hardware -- drawPixel() is
|
||||
// the only thing MiscFixedRenderer.h's glyph-plotting code and
|
||||
// Adafruit_GFX's own fillRect()/writeFillRect() (used for the "unmapped
|
||||
// codepoint" substitution box) ultimately call down to.
|
||||
class SimGfxCanvas : public Adafruit_GFX {
|
||||
public:
|
||||
uint8_t px[128 * 64];
|
||||
SimGfxCanvas() : Adafruit_GFX(128, 64) { memset(px, 0, sizeof(px)); }
|
||||
void drawPixel(int16_t x, int16_t y, uint16_t color) override {
|
||||
if ((unsigned)x >= 128 || (unsigned)y >= 64) return;
|
||||
px[y * 128 + x] = (color != 0) ? 1 : 0;
|
||||
}
|
||||
};
|
||||
|
||||
void SimDisplayDriverCanvas::print(const char* str) {
|
||||
if (!str) return;
|
||||
static SimGfxCanvas gfx;
|
||||
memset(gfx.px, 0, sizeof(gfx.px));
|
||||
gfx.setCursor(_cursor_x, _cursor_y);
|
||||
// color arg is just our own internal "lit" marker (1) -- the real on-screen
|
||||
// amber/black choice is applied once at blit time below, from _color, same
|
||||
// as every other primitive in this class.
|
||||
miscFixedPrint(gfx, str, 1, 1);
|
||||
|
||||
// startFrame() already blanks the whole canvas to black every frame, so
|
||||
// only the lit pixels need drawing here -- unlit buffer cells are already
|
||||
// correct background. One EM_ASM call blits the whole 128x64 buffer
|
||||
// (reading it directly out of wasm memory, same pattern as drawXbm()
|
||||
// below) rather than one call per glyph pixel.
|
||||
EM_ASM({
|
||||
if (!Module.__simCtx) return;
|
||||
var ctx = Module.__simCtx;
|
||||
var buf = $0;
|
||||
ctx.fillStyle = UTF8ToString($1) === 'L' ? '#ffb000' : '#000';
|
||||
for (var y = 0; y < 64; y++) {
|
||||
for (var x = 0; x < 128; x++) {
|
||||
if (HEAPU8[buf + y * 128 + x]) ctx.fillRect(x, y, 1, 1);
|
||||
}
|
||||
}
|
||||
}, gfx.px, (_color != DARK) ? "L" : "D");
|
||||
|
||||
// Same external contract as every other DisplayDriver backend here (see
|
||||
// SimDisplayDriver's own ASCII print()): only _cursor_x advances by the
|
||||
// printed width; _cursor_y is left for the caller to manage via
|
||||
// setCursor() between lines, even though miscFixedPrint() itself tracks a
|
||||
// real y position across embedded '\n's internally.
|
||||
_cursor_x += getTextWidth(str);
|
||||
}
|
||||
#endif // __EMSCRIPTEN__
|
||||
|
||||
Reference in New Issue
Block a user