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:
@@ -2124,6 +2124,10 @@ void UITask::injectSimKey(char c) {
|
|||||||
enqueueKey(checkDisplayOn(c));
|
enqueueKey(checkDisplayOn(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UITask::injectSimKeyLongPress(char c) {
|
||||||
|
enqueueKey(handleLongPress(c));
|
||||||
|
}
|
||||||
|
|
||||||
// Called directly from a host HTML page's JS (button onclick / keydown
|
// Called directly from a host HTML page's JS (button onclick / keydown
|
||||||
// listener) -- e.g. `Module._sim_enqueue_key(keyCode)` -- to drive the real
|
// listener) -- e.g. `Module._sim_enqueue_key(keyCode)` -- to drive the real
|
||||||
// on-device menu. `c` is one of the KEY_* codes in src/helpers/ui/
|
// on-device menu. `c` is one of the KEY_* codes in src/helpers/ui/
|
||||||
@@ -2134,6 +2138,18 @@ void UITask::injectSimKey(char c) {
|
|||||||
extern "C" EMSCRIPTEN_KEEPALIVE void sim_enqueue_key(char c) {
|
extern "C" EMSCRIPTEN_KEEPALIVE void sim_enqueue_key(char c) {
|
||||||
if (g_sim_ui_task_for_js) g_sim_ui_task_for_js->injectSimKey(c);
|
if (g_sim_ui_task_for_js) g_sim_ui_task_for_js->injectSimKey(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Long-press counterpart -- the host page's own press-and-hold timer (see
|
||||||
|
// web/index.html/mesh.html) calls this instead of sim_enqueue_key() once a
|
||||||
|
// button/key has been held past the same ~1000ms threshold every real
|
||||||
|
// board's MomentaryButton uses. Real hardware never gets both a short-press
|
||||||
|
// AND a long-press event for the same physical press (MomentaryButton fires
|
||||||
|
// one or the other), so the host page's timer must do the same: fire this
|
||||||
|
// on hold-past-threshold and suppress the plain click that would otherwise
|
||||||
|
// follow on release.
|
||||||
|
extern "C" EMSCRIPTEN_KEEPALIVE void sim_enqueue_key_longpress(char c) {
|
||||||
|
if (g_sim_ui_task_for_js) g_sim_ui_task_for_js->injectSimKeyLongPress(c);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool UITask::dequeueKey(char& c) {
|
bool UITask::dequeueKey(char& c) {
|
||||||
|
|||||||
@@ -219,6 +219,14 @@ public:
|
|||||||
// sim's substitute for a real board's GPIO/joystick poll, not a new input
|
// sim's substitute for a real board's GPIO/joystick poll, not a new input
|
||||||
// path of its own.
|
// path of its own.
|
||||||
void injectSimKey(char c);
|
void injectSimKey(char c);
|
||||||
|
// Same idea, but for a held-down press: routes through handleLongPress()
|
||||||
|
// instead of checkDisplayOn() directly -- the real long-press mechanism
|
||||||
|
// (KEY_ENTER -> KEY_CONTEXT_MENU, plus the first-8-seconds CLI-rescue
|
||||||
|
// gate) every real board's MomentaryButton(pin, 1000, ...) already
|
||||||
|
// reaches via BUTTON_EVENT_LONG_PRESS. Without this, a sim instance could
|
||||||
|
// never open the context menu at all -- injectSimKey() alone has no way
|
||||||
|
// to signal "this press was held".
|
||||||
|
void injectSimKeyLongPress(char c);
|
||||||
private:
|
private:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -244,32 +244,13 @@ public:
|
|||||||
// comment: "on b/w screen, colors will be !=0 synonym of light").
|
// comment: "on b/w screen, colors will be !=0 synonym of light").
|
||||||
static const char* jsColor(Color c) { return c == DARK ? "#000" : "#ffb000"; }
|
static const char* jsColor(Color c) { return c == DARK ? "#000" : "#ffb000"; }
|
||||||
|
|
||||||
void print(const char* str) override {
|
// Real bitmap-font rendering (byte-identical glyphs to a real MeshCore-Solo
|
||||||
if (!str) return;
|
// board with OLED_MISC_FIXED_FONT=1 -- see solo/heltec_v3/platformio.ini),
|
||||||
EM_ASM({
|
// not the browser's own system font -- defined out-of-line in target.cpp,
|
||||||
if (!Module.__simCtx) return;
|
// the one Emscripten-only translation unit allowed to pull in
|
||||||
var ctx = Module.__simCtx;
|
// MiscFixedRenderer.h (its own header comment: including it from more than
|
||||||
ctx.fillStyle = UTF8ToString($3) === 'L' ? '#ffb000' : '#000';
|
// one .cpp would duplicate the font's static const tables in each).
|
||||||
ctx.font = '8px monospace';
|
void print(const char* str) override;
|
||||||
ctx.textBaseline = 'top';
|
|
||||||
// Advance width is fixed (getCharWidth()==6, DisplayDriver.h default) --
|
|
||||||
// draw one character per cell so glyph spacing matches the layout math
|
|
||||||
// every screen already does off getTextWidth()'s strlen()*6 estimate,
|
|
||||||
// instead of leaving it to the font's own (proportional) metrics.
|
|
||||||
// NB: EM_ASM's argument-splitting only understands parens, not
|
|
||||||
// braces -- an unparenthesized top-level comma (e.g. a multi-name
|
|
||||||
// `var a, b;`) gets misread as separating this macro's own C++
|
|
||||||
// arguments and breaks the whole block. Every declaration below is
|
|
||||||
// therefore its own separate `var` statement.
|
|
||||||
var s = UTF8ToString($0);
|
|
||||||
var x = $1;
|
|
||||||
var y = $2;
|
|
||||||
for (var i = 0; i < s.length; i++) {
|
|
||||||
ctx.fillText(s[i], x + i * 6, y);
|
|
||||||
}
|
|
||||||
}, str, _cursor_x, _cursor_y, (_color != DARK) ? "L" : "D");
|
|
||||||
_cursor_x += getTextWidth(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fillRect(int x, int y, int w, int h) override {
|
void fillRect(int x, int y, int w, int h) override {
|
||||||
EM_ASM({
|
EM_ASM({
|
||||||
|
|||||||
@@ -75,6 +75,20 @@ inline long random(long howsmall, long howbig) {
|
|||||||
#ifndef PROGMEM
|
#ifndef PROGMEM
|
||||||
#define PROGMEM
|
#define PROGMEM
|
||||||
#endif
|
#endif
|
||||||
|
// Real pgm_read_*() on a native host is just a plain dereference -- there's
|
||||||
|
// no separate flash address space to special-case. Needed once Adafruit_GFX
|
||||||
|
// + MiscFixedFont.h (variants/sim/thirdparty/gfx/, src/helpers/ui/) entered
|
||||||
|
// the sim build; nothing before that read PROGMEM data at all (see this
|
||||||
|
// file's own header comment).
|
||||||
|
#ifndef pgm_read_byte
|
||||||
|
#define pgm_read_byte(addr) (*(const uint8_t*)(addr))
|
||||||
|
#endif
|
||||||
|
#ifndef pgm_read_word
|
||||||
|
#define pgm_read_word(addr) (*(const uint16_t*)(addr))
|
||||||
|
#endif
|
||||||
|
#ifndef pgm_read_dword
|
||||||
|
#define pgm_read_dword(addr) (*(const uint32_t*)(addr))
|
||||||
|
#endif
|
||||||
#ifndef F
|
#ifndef F
|
||||||
#define F(x) (x)
|
#define F(x) (x)
|
||||||
#endif
|
#endif
|
||||||
@@ -142,6 +156,29 @@ inline void yield() { }
|
|||||||
// declares cleanly, exactly like on a real board.
|
// declares cleanly, exactly like on a real board.
|
||||||
class __FlashStringHelper;
|
class __FlashStringHelper;
|
||||||
|
|
||||||
|
// Adafruit_GFX.h declares a getTextBounds(const String&, ...) overload
|
||||||
|
// (variants/sim/thirdparty/gfx/) -- MeshCore's own app code never
|
||||||
|
// constructs or passes a real Arduino String anywhere (this file's own
|
||||||
|
// header comment), so this exists purely to satisfy that one declaration's
|
||||||
|
// compile, not to be a real String replacement.
|
||||||
|
#include <string>
|
||||||
|
class String {
|
||||||
|
std::string _s;
|
||||||
|
public:
|
||||||
|
String(const char* s = "") : _s(s ? s : "") {}
|
||||||
|
const char* c_str() const { return _s.c_str(); }
|
||||||
|
size_t length() const { return _s.length(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Real Arduino cores define these; Adafruit_GFX.cpp's drawArc() uses
|
||||||
|
// radians(). Same DEG_TO_RAD/RAD_TO_DEG constants as the real macros.
|
||||||
|
#ifndef radians
|
||||||
|
#define radians(deg) ((deg) * 0.017453292519943295)
|
||||||
|
#endif
|
||||||
|
#ifndef degrees
|
||||||
|
#define degrees(rad) ((rad) * 57.29577951308232)
|
||||||
|
#endif
|
||||||
|
|
||||||
// --- Serial -------------------------------------------------------------
|
// --- Serial -------------------------------------------------------------
|
||||||
// Only ever used for Serial.begin() (main.cpp, ignored) and MyMesh.cpp's
|
// Only ever used for Serial.begin() (main.cpp, ignored) and MyMesh.cpp's
|
||||||
// CLI-rescue debug command handler (Serial.print/println/printf as output,
|
// CLI-rescue debug command handler (Serial.print/println/printf as output,
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ SRCS=(
|
|||||||
variants/sim/thirdparty/crypto/SHA512.cpp
|
variants/sim/thirdparty/crypto/SHA512.cpp
|
||||||
variants/sim/thirdparty/cayennelpp/CayenneLPP.cpp
|
variants/sim/thirdparty/cayennelpp/CayenneLPP.cpp
|
||||||
variants/sim/thirdparty/cayennelpp/CayenneLPPPolyline.cpp
|
variants/sim/thirdparty/cayennelpp/CayenneLPPPolyline.cpp
|
||||||
|
variants/sim/thirdparty/gfx/Adafruit_GFX.cpp
|
||||||
examples/companion_radio/main.cpp
|
examples/companion_radio/main.cpp
|
||||||
examples/companion_radio/MyMesh.cpp
|
examples/companion_radio/MyMesh.cpp
|
||||||
examples/companion_radio/DataStore.cpp
|
examples/companion_radio/DataStore.cpp
|
||||||
@@ -112,6 +113,7 @@ INCLUDES=(
|
|||||||
-Ivariants/sim/thirdparty/crypto
|
-Ivariants/sim/thirdparty/crypto
|
||||||
-Ivariants/sim/thirdparty/cayennelpp
|
-Ivariants/sim/thirdparty/cayennelpp
|
||||||
-Ivariants/sim/thirdparty/arduinojson
|
-Ivariants/sim/thirdparty/arduinojson
|
||||||
|
-Ivariants/sim/thirdparty/gfx
|
||||||
-Ilib/ed25519
|
-Ilib/ed25519
|
||||||
-Isrc
|
-Isrc
|
||||||
-Iexamples/companion_radio
|
-Iexamples/companion_radio
|
||||||
@@ -158,7 +160,21 @@ OBJS=()
|
|||||||
for src in "${SRCS[@]}"; do
|
for src in "${SRCS[@]}"; do
|
||||||
obj="$OBJ_DIR/${src%.*}.o"
|
obj="$OBJ_DIR/${src%.*}.o"
|
||||||
mkdir -p "$(dirname "$obj")"
|
mkdir -p "$(dirname "$obj")"
|
||||||
"$EMXX" -c "${COMMON_FLAGS[@]}" "$src" -o "$obj"
|
extra_flags=()
|
||||||
|
if [ "$src" = "variants/sim/thirdparty/gfx/Adafruit_GFX.cpp" ]; then
|
||||||
|
# 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) -- ARDUINO is deliberately never defined globally for
|
||||||
|
# this build (CayenneLPP.cpp/ArduinoJson branch on #ifdef ARDUINO to
|
||||||
|
# pick their portable std:: path instead of Arduino String/Stream), so
|
||||||
|
# this is scoped to just this one file's own compile, matching the same
|
||||||
|
# `#define ARDUINO 100` target.cpp does locally before its own
|
||||||
|
# #include <Adafruit_GFX.h> (see that file's comment) -- both need it so
|
||||||
|
# Adafruit_GFX's declaration (parsed by target.cpp) and its out-of-line
|
||||||
|
# definition (parsed here) agree on the same write(uint8_t) signature.
|
||||||
|
extra_flags=(-DARDUINO=100)
|
||||||
|
fi
|
||||||
|
"$EMXX" -c "${COMMON_FLAGS[@]}" ${extra_flags[@]+"${extra_flags[@]}"} "$src" -o "$obj"
|
||||||
OBJS+=("$obj")
|
OBJS+=("$obj")
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <cstring>
|
||||||
#include "target.h"
|
#include "target.h"
|
||||||
#include "SimRNG.h"
|
#include "SimRNG.h"
|
||||||
|
|
||||||
@@ -26,3 +27,73 @@ mesh::LocalIdentity radio_new_identity() {
|
|||||||
rng.begin();
|
rng.begin();
|
||||||
return mesh::LocalIdentity(&rng);
|
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__
|
||||||
|
|||||||
+2952
File diff suppressed because it is too large
Load Diff
+406
@@ -0,0 +1,406 @@
|
|||||||
|
#ifndef _ADAFRUIT_GFX_H
|
||||||
|
#define _ADAFRUIT_GFX_H
|
||||||
|
|
||||||
|
#if ARDUINO >= 100
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "Print.h"
|
||||||
|
#else
|
||||||
|
#include "WProgram.h"
|
||||||
|
#endif
|
||||||
|
#include "gfxfont.h"
|
||||||
|
|
||||||
|
#include <Adafruit_I2CDevice.h>
|
||||||
|
#include <Adafruit_SPIDevice.h>
|
||||||
|
|
||||||
|
/// A generic graphics superclass that can handle all sorts of drawing. At a
|
||||||
|
/// minimum you can subclass and provide drawPixel(). At a maximum you can do a
|
||||||
|
/// ton of overriding to optimize. Used for any/all Adafruit displays!
|
||||||
|
class Adafruit_GFX : public Print {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Adafruit_GFX(int16_t w, int16_t h); // Constructor
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Draw to the screen/framebuffer/etc.
|
||||||
|
Must be overridden in subclass.
|
||||||
|
@param x X coordinate in pixels
|
||||||
|
@param y Y coordinate in pixels
|
||||||
|
@param color 16-bit pixel color.
|
||||||
|
*/
|
||||||
|
/**********************************************************************/
|
||||||
|
virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0;
|
||||||
|
|
||||||
|
// TRANSACTION API / CORE DRAW API
|
||||||
|
// These MAY be overridden by the subclass to provide device-specific
|
||||||
|
// optimized code. Otherwise 'generic' versions are used.
|
||||||
|
virtual void startWrite(void);
|
||||||
|
virtual void writePixel(int16_t x, int16_t y, uint16_t color);
|
||||||
|
virtual void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h,
|
||||||
|
uint16_t color);
|
||||||
|
virtual void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
|
||||||
|
virtual void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
|
||||||
|
virtual void writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
|
||||||
|
uint16_t color);
|
||||||
|
virtual void endWrite(void);
|
||||||
|
|
||||||
|
// CONTROL API
|
||||||
|
// These MAY be overridden by the subclass to provide device-specific
|
||||||
|
// optimized code. Otherwise 'generic' versions are used.
|
||||||
|
virtual void setRotation(uint8_t r);
|
||||||
|
virtual void invertDisplay(bool i);
|
||||||
|
|
||||||
|
// BASIC DRAW API
|
||||||
|
// These MAY be overridden by the subclass to provide device-specific
|
||||||
|
// optimized code. Otherwise 'generic' versions are used.
|
||||||
|
|
||||||
|
// It's good to implement those, even if using transaction API
|
||||||
|
virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
|
||||||
|
virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
|
||||||
|
virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
|
||||||
|
uint16_t color);
|
||||||
|
virtual void fillScreen(uint16_t color);
|
||||||
|
// Optional and probably not necessary to change
|
||||||
|
virtual void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
|
||||||
|
uint16_t color);
|
||||||
|
virtual void drawRect(int16_t x, int16_t y, int16_t w, int16_t h,
|
||||||
|
uint16_t color);
|
||||||
|
|
||||||
|
// These exist only with Adafruit_GFX (no subclass overrides)
|
||||||
|
void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
|
||||||
|
void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername,
|
||||||
|
uint16_t color);
|
||||||
|
void fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
|
||||||
|
void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername,
|
||||||
|
int16_t delta, uint16_t color);
|
||||||
|
void drawEllipse(int16_t x0, int16_t y0, int16_t rw, int16_t rh,
|
||||||
|
uint16_t color);
|
||||||
|
void fillEllipse(int16_t x0, int16_t y0, int16_t rw, int16_t rh,
|
||||||
|
uint16_t color);
|
||||||
|
void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2,
|
||||||
|
int16_t y2, uint16_t color);
|
||||||
|
void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2,
|
||||||
|
int16_t y2, uint16_t color);
|
||||||
|
void drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
|
||||||
|
int16_t radius, uint16_t color);
|
||||||
|
void fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
|
||||||
|
int16_t radius, uint16_t color);
|
||||||
|
void drawRotatedRect(int16_t cenX, int16_t cenY, int16_t w, int16_t h,
|
||||||
|
int16_t angleDeg, uint16_t color);
|
||||||
|
void fillRotatedRect(int16_t cenX, int16_t cenY, int16_t w, int16_t h,
|
||||||
|
int16_t angleDeg, uint16_t color);
|
||||||
|
void rotatePoint(int16_t &x0, int16_t &y0, int16_t angleDeg);
|
||||||
|
void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w,
|
||||||
|
int16_t h, uint16_t color);
|
||||||
|
void drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w,
|
||||||
|
int16_t h, uint16_t color, uint16_t bg);
|
||||||
|
void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h,
|
||||||
|
uint16_t color);
|
||||||
|
void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h,
|
||||||
|
uint16_t color, uint16_t bg);
|
||||||
|
void drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w,
|
||||||
|
int16_t h, uint16_t color);
|
||||||
|
void drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
|
||||||
|
int16_t w, int16_t h);
|
||||||
|
void drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w,
|
||||||
|
int16_t h);
|
||||||
|
void drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
|
||||||
|
const uint8_t mask[], int16_t w, int16_t h);
|
||||||
|
void drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, uint8_t *mask,
|
||||||
|
int16_t w, int16_t h);
|
||||||
|
void drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[], int16_t w,
|
||||||
|
int16_t h);
|
||||||
|
void drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, int16_t w,
|
||||||
|
int16_t h);
|
||||||
|
void drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[],
|
||||||
|
const uint8_t mask[], int16_t w, int16_t h);
|
||||||
|
void drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, uint8_t *mask,
|
||||||
|
int16_t w, int16_t h);
|
||||||
|
void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color,
|
||||||
|
uint16_t bg, uint8_t size);
|
||||||
|
void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color,
|
||||||
|
uint16_t bg, uint8_t size_x, uint8_t size_y);
|
||||||
|
void getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1,
|
||||||
|
int16_t *y1, uint16_t *w, uint16_t *h);
|
||||||
|
void getTextBounds(const __FlashStringHelper *s, int16_t x, int16_t y,
|
||||||
|
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
|
||||||
|
void getTextBounds(const String &str, int16_t x, int16_t y, int16_t *x1,
|
||||||
|
int16_t *y1, uint16_t *w, uint16_t *h);
|
||||||
|
void setTextSize(uint8_t s);
|
||||||
|
void setTextSize(uint8_t sx, uint8_t sy);
|
||||||
|
void setFont(const GFXfont *f = NULL);
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Set text cursor location
|
||||||
|
@param x X coordinate in pixels
|
||||||
|
@param y Y coordinate in pixels
|
||||||
|
*/
|
||||||
|
/**********************************************************************/
|
||||||
|
void setCursor(int16_t x, int16_t y) {
|
||||||
|
cursor_x = x;
|
||||||
|
cursor_y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Set text font color with transparant background
|
||||||
|
@param c 16-bit 5-6-5 Color to draw text with
|
||||||
|
@note For 'transparent' background, background and foreground
|
||||||
|
are set to same color rather than using a separate flag.
|
||||||
|
*/
|
||||||
|
/**********************************************************************/
|
||||||
|
void setTextColor(uint16_t c) { textcolor = textbgcolor = c; }
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Set text font color with custom background color
|
||||||
|
@param c 16-bit 5-6-5 Color to draw text with
|
||||||
|
@param bg 16-bit 5-6-5 Color to draw background/fill with
|
||||||
|
*/
|
||||||
|
/**********************************************************************/
|
||||||
|
void setTextColor(uint16_t c, uint16_t bg) {
|
||||||
|
textcolor = c;
|
||||||
|
textbgcolor = bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Set whether text that is too long for the screen width should
|
||||||
|
automatically wrap around to the next line (else clip right).
|
||||||
|
@param w true for wrapping, false for clipping
|
||||||
|
*/
|
||||||
|
/**********************************************************************/
|
||||||
|
void setTextWrap(bool w) { wrap = w; }
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Enable (or disable) Code Page 437-compatible charset.
|
||||||
|
There was an error in glcdfont.c for the longest time -- one
|
||||||
|
character (#176, the 'light shade' block) was missing -- this
|
||||||
|
threw off the index of every character that followed it.
|
||||||
|
But a TON of code has been written with the erroneous
|
||||||
|
character indices. By default, the library uses the original
|
||||||
|
'wrong' behavior and old sketches will still work. Pass
|
||||||
|
'true' to this function to use correct CP437 character values
|
||||||
|
in your code.
|
||||||
|
@param x true = enable (new behavior), false = disable (old behavior)
|
||||||
|
*/
|
||||||
|
/**********************************************************************/
|
||||||
|
void cp437(bool x = true) { _cp437 = x; }
|
||||||
|
|
||||||
|
using Print::write;
|
||||||
|
#if ARDUINO >= 100
|
||||||
|
virtual size_t write(uint8_t);
|
||||||
|
#else
|
||||||
|
virtual void write(uint8_t);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Get width of the display, accounting for current rotation
|
||||||
|
@returns Width in pixels
|
||||||
|
*/
|
||||||
|
/************************************************************************/
|
||||||
|
int16_t width(void) const { return _width; };
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Get height of the display, accounting for current rotation
|
||||||
|
@returns Height in pixels
|
||||||
|
*/
|
||||||
|
/************************************************************************/
|
||||||
|
int16_t height(void) const { return _height; }
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Get rotation setting for display
|
||||||
|
@returns 0 thru 3 corresponding to 4 cardinal rotations
|
||||||
|
*/
|
||||||
|
/************************************************************************/
|
||||||
|
uint8_t getRotation(void) const { return rotation; }
|
||||||
|
|
||||||
|
// get current cursor position (get rotation safe maximum values,
|
||||||
|
// using: width() for x, height() for y)
|
||||||
|
/************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Get text cursor X location
|
||||||
|
@returns X coordinate in pixels
|
||||||
|
*/
|
||||||
|
/************************************************************************/
|
||||||
|
int16_t getCursorX(void) const { return cursor_x; }
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Get text cursor Y location
|
||||||
|
@returns Y coordinate in pixels
|
||||||
|
*/
|
||||||
|
/************************************************************************/
|
||||||
|
int16_t getCursorY(void) const { return cursor_y; };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void charBounds(unsigned char c, int16_t *x, int16_t *y, int16_t *minx,
|
||||||
|
int16_t *miny, int16_t *maxx, int16_t *maxy);
|
||||||
|
int16_t WIDTH; ///< This is the 'raw' display width - never changes
|
||||||
|
int16_t HEIGHT; ///< This is the 'raw' display height - never changes
|
||||||
|
int16_t _width; ///< Display width as modified by current rotation
|
||||||
|
int16_t _height; ///< Display height as modified by current rotation
|
||||||
|
int16_t cursor_x; ///< x location to start print()ing text
|
||||||
|
int16_t cursor_y; ///< y location to start print()ing text
|
||||||
|
uint16_t textcolor; ///< 16-bit background color for print()
|
||||||
|
uint16_t textbgcolor; ///< 16-bit text color for print()
|
||||||
|
uint8_t textsize_x; ///< Desired magnification in X-axis of text to print()
|
||||||
|
uint8_t textsize_y; ///< Desired magnification in Y-axis of text to print()
|
||||||
|
uint8_t rotation; ///< Display rotation (0 thru 3)
|
||||||
|
bool wrap; ///< If set, 'wrap' text at right edge of display
|
||||||
|
bool _cp437; ///< If set, use correct CP437 charset (default is off)
|
||||||
|
GFXfont *gfxFont; ///< Pointer to special font
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A simple drawn button UI element
|
||||||
|
class Adafruit_GFX_Button {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Adafruit_GFX_Button(void);
|
||||||
|
// "Classic" initButton() uses center & size
|
||||||
|
void initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, uint16_t w,
|
||||||
|
uint16_t h, uint16_t outline, uint16_t fill,
|
||||||
|
uint16_t textcolor, char *label, uint8_t textsize);
|
||||||
|
void initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, uint16_t w,
|
||||||
|
uint16_t h, uint16_t outline, uint16_t fill,
|
||||||
|
uint16_t textcolor, char *label, uint8_t textsize_x,
|
||||||
|
uint8_t textsize_y);
|
||||||
|
// New/alt initButton() uses upper-left corner & size
|
||||||
|
void initButtonUL(Adafruit_GFX *gfx, int16_t x1, int16_t y1, uint16_t w,
|
||||||
|
uint16_t h, uint16_t outline, uint16_t fill,
|
||||||
|
uint16_t textcolor, char *label, uint8_t textsize);
|
||||||
|
void initButtonUL(Adafruit_GFX *gfx, int16_t x1, int16_t y1, uint16_t w,
|
||||||
|
uint16_t h, uint16_t outline, uint16_t fill,
|
||||||
|
uint16_t textcolor, char *label, uint8_t textsize_x,
|
||||||
|
uint8_t textsize_y);
|
||||||
|
void drawButton(bool inverted = false);
|
||||||
|
bool contains(int16_t x, int16_t y);
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Sets button state, should be done by some touch function
|
||||||
|
@param p True for pressed, false for not.
|
||||||
|
*/
|
||||||
|
/**********************************************************************/
|
||||||
|
void press(bool p) {
|
||||||
|
laststate = currstate;
|
||||||
|
currstate = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool justPressed();
|
||||||
|
bool justReleased();
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Query whether the button is currently pressed
|
||||||
|
@returns True if pressed
|
||||||
|
*/
|
||||||
|
/**********************************************************************/
|
||||||
|
bool isPressed(void) { return currstate; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
Adafruit_GFX *_gfx;
|
||||||
|
int16_t _x1, _y1; // Coordinates of top-left corner
|
||||||
|
uint16_t _w, _h;
|
||||||
|
uint8_t _textsize_x;
|
||||||
|
uint8_t _textsize_y;
|
||||||
|
uint16_t _outlinecolor, _fillcolor, _textcolor;
|
||||||
|
char _label[10];
|
||||||
|
|
||||||
|
bool currstate, laststate;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A GFX 1-bit canvas context for graphics
|
||||||
|
class GFXcanvas1 : public Adafruit_GFX {
|
||||||
|
public:
|
||||||
|
GFXcanvas1(uint16_t w, uint16_t h, bool allocate_buffer = true);
|
||||||
|
~GFXcanvas1(void);
|
||||||
|
void drawPixel(int16_t x, int16_t y, uint16_t color);
|
||||||
|
void fillScreen(uint16_t color);
|
||||||
|
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
|
||||||
|
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
|
||||||
|
bool getPixel(int16_t x, int16_t y) const;
|
||||||
|
/**********************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Get a pointer to the internal buffer memory
|
||||||
|
@returns A pointer to the allocated buffer
|
||||||
|
*/
|
||||||
|
/**********************************************************************/
|
||||||
|
uint8_t *getBuffer(void) const { return buffer; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool getRawPixel(int16_t x, int16_t y) const;
|
||||||
|
void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
|
||||||
|
void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
|
||||||
|
uint8_t *buffer; ///< Raster data: no longer private, allow subclass access
|
||||||
|
bool buffer_owned; ///< If true, destructor will free buffer, else it will do
|
||||||
|
///< nothing
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef __AVR__
|
||||||
|
// Bitmask tables of 0x80>>X and ~(0x80>>X), because X>>Y is slow on AVR
|
||||||
|
static const uint8_t PROGMEM GFXsetBit[], GFXclrBit[];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A GFX 8-bit canvas context for graphics
|
||||||
|
class GFXcanvas8 : public Adafruit_GFX {
|
||||||
|
public:
|
||||||
|
GFXcanvas8(uint16_t w, uint16_t h, bool allocate_buffer = true);
|
||||||
|
~GFXcanvas8(void);
|
||||||
|
void drawPixel(int16_t x, int16_t y, uint16_t color);
|
||||||
|
void fillScreen(uint16_t color);
|
||||||
|
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
|
||||||
|
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
|
||||||
|
uint8_t getPixel(int16_t x, int16_t y) const;
|
||||||
|
/**********************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Get a pointer to the internal buffer memory
|
||||||
|
@returns A pointer to the allocated buffer
|
||||||
|
*/
|
||||||
|
/**********************************************************************/
|
||||||
|
uint8_t *getBuffer(void) const { return buffer; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8_t getRawPixel(int16_t x, int16_t y) const;
|
||||||
|
void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
|
||||||
|
void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
|
||||||
|
uint8_t *buffer; ///< Raster data: no longer private, allow subclass access
|
||||||
|
bool buffer_owned; ///< If true, destructor will free buffer, else it will do
|
||||||
|
///< nothing
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A GFX 16-bit canvas context for graphics
|
||||||
|
class GFXcanvas16 : public Adafruit_GFX {
|
||||||
|
public:
|
||||||
|
GFXcanvas16(uint16_t w, uint16_t h, bool allocate_buffer = true);
|
||||||
|
~GFXcanvas16(void);
|
||||||
|
void drawPixel(int16_t x, int16_t y, uint16_t color);
|
||||||
|
void fillScreen(uint16_t color);
|
||||||
|
void byteSwap(void);
|
||||||
|
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
|
||||||
|
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
|
||||||
|
uint16_t getPixel(int16_t x, int16_t y) const;
|
||||||
|
/**********************************************************************/
|
||||||
|
/*!
|
||||||
|
@brief Get a pointer to the internal buffer memory
|
||||||
|
@returns A pointer to the allocated buffer
|
||||||
|
*/
|
||||||
|
/**********************************************************************/
|
||||||
|
uint16_t *getBuffer(void) const { return buffer; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint16_t getRawPixel(int16_t x, int16_t y) const;
|
||||||
|
void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
|
||||||
|
void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
|
||||||
|
uint16_t *buffer; ///< Raster data: no longer private, allow subclass access
|
||||||
|
bool buffer_owned; ///< If true, destructor will free buffer, else it will do
|
||||||
|
///< nothing
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _ADAFRUIT_GFX_H
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
// Adafruit_GFX.h #includes this unconditionally, but nothing in
|
||||||
|
// Adafruit_GFX.h/.cpp actually references any symbol from it (checked by
|
||||||
|
// grep) -- it's real hardware I2C plumbing Adafruit_GFX itself never needs,
|
||||||
|
// only its device-specific subclasses (Adafruit_SSD1306 etc., which this
|
||||||
|
// sim doesn't compile at all -- see variants/sim/SimDisplayDriver.h's own
|
||||||
|
// canvas backend instead). An empty stub is enough to satisfy the #include.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
// See Adafruit_I2CDevice.h's stub in this same directory -- same reasoning,
|
||||||
|
// same unused #include in Adafruit_GFX.h.
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
// Font structures for newer Adafruit_GFX (1.1 and later).
|
||||||
|
// Example fonts are included in 'Fonts' directory.
|
||||||
|
// To use a font in your Arduino sketch, #include the corresponding .h
|
||||||
|
// file and pass address of GFXfont struct to setFont(). Pass NULL to
|
||||||
|
// revert to 'classic' fixed-space bitmap font.
|
||||||
|
|
||||||
|
#ifndef _GFXFONT_H_
|
||||||
|
#define _GFXFONT_H_
|
||||||
|
|
||||||
|
/// Font data stored PER GLYPH
|
||||||
|
typedef struct {
|
||||||
|
uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap
|
||||||
|
uint8_t width; ///< Bitmap dimensions in pixels
|
||||||
|
uint8_t height; ///< Bitmap dimensions in pixels
|
||||||
|
uint8_t xAdvance; ///< Distance to advance cursor (x axis)
|
||||||
|
int8_t xOffset; ///< X dist from cursor pos to UL corner
|
||||||
|
int8_t yOffset; ///< Y dist from cursor pos to UL corner
|
||||||
|
} GFXglyph;
|
||||||
|
|
||||||
|
/// Data stored for FONT AS A WHOLE
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *bitmap; ///< Glyph bitmaps, concatenated
|
||||||
|
GFXglyph *glyph; ///< Glyph array
|
||||||
|
uint16_t first; ///< ASCII extents (first char)
|
||||||
|
uint16_t last; ///< ASCII extents (last char)
|
||||||
|
uint8_t yAdvance; ///< Newline distance (y axis)
|
||||||
|
} GFXfont;
|
||||||
|
|
||||||
|
#endif // _GFXFONT_H_
|
||||||
+143
@@ -0,0 +1,143 @@
|
|||||||
|
// This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0.
|
||||||
|
// See gfxfont.h for newer custom bitmap font info.
|
||||||
|
|
||||||
|
#ifndef FONT5X7_H
|
||||||
|
#define FONT5X7_H
|
||||||
|
|
||||||
|
#ifdef __AVR__
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#elif defined(ESP8266)
|
||||||
|
#include <pgmspace.h>
|
||||||
|
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
|
||||||
|
// PROGMEM is defefind for T4 to place data in specific memory section
|
||||||
|
#undef PROGMEM
|
||||||
|
#define PROGMEM
|
||||||
|
#else
|
||||||
|
#define PROGMEM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Standard ASCII 5x7 font
|
||||||
|
|
||||||
|
static const unsigned char font[] PROGMEM = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x3E, 0x6B,
|
||||||
|
0x4F, 0x6B, 0x3E, 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x18, 0x3C, 0x7E, 0x3C,
|
||||||
|
0x18, 0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00,
|
||||||
|
0x18, 0x3C, 0x18, 0x00, 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00, 0x18, 0x24,
|
||||||
|
0x18, 0x00, 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x30, 0x48, 0x3A, 0x06, 0x0E,
|
||||||
|
0x26, 0x29, 0x79, 0x29, 0x26, 0x40, 0x7F, 0x05, 0x05, 0x07, 0x40, 0x7F,
|
||||||
|
0x05, 0x25, 0x3F, 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x7F, 0x3E, 0x1C, 0x1C,
|
||||||
|
0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x14, 0x22, 0x7F, 0x22, 0x14, 0x5F,
|
||||||
|
0x5F, 0x00, 0x5F, 0x5F, 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, 0x66, 0x89,
|
||||||
|
0x95, 0x6A, 0x60, 0x60, 0x60, 0x60, 0x60, 0x94, 0xA2, 0xFF, 0xA2, 0x94,
|
||||||
|
0x08, 0x04, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x20, 0x10, 0x08, 0x08,
|
||||||
|
0x2A, 0x1C, 0x08, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x1E, 0x10, 0x10, 0x10,
|
||||||
|
0x10, 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 0x30, 0x38, 0x3E, 0x38, 0x30, 0x06,
|
||||||
|
0x0E, 0x3E, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F,
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14,
|
||||||
|
0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 0x36, 0x49,
|
||||||
|
0x56, 0x20, 0x50, 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x1C, 0x22, 0x41,
|
||||||
|
0x00, 0x00, 0x41, 0x22, 0x1C, 0x00, 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x08,
|
||||||
|
0x08, 0x3E, 0x08, 0x08, 0x00, 0x80, 0x70, 0x30, 0x00, 0x08, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x00, 0x00, 0x60, 0x60, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02,
|
||||||
|
0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x72, 0x49,
|
||||||
|
0x49, 0x49, 0x46, 0x21, 0x41, 0x49, 0x4D, 0x33, 0x18, 0x14, 0x12, 0x7F,
|
||||||
|
0x10, 0x27, 0x45, 0x45, 0x45, 0x39, 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x41,
|
||||||
|
0x21, 0x11, 0x09, 0x07, 0x36, 0x49, 0x49, 0x49, 0x36, 0x46, 0x49, 0x49,
|
||||||
|
0x29, 0x1E, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00,
|
||||||
|
0x00, 0x08, 0x14, 0x22, 0x41, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41,
|
||||||
|
0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3E, 0x41, 0x5D, 0x59,
|
||||||
|
0x4E, 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E,
|
||||||
|
0x41, 0x41, 0x41, 0x22, 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x49, 0x49,
|
||||||
|
0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x3E, 0x41, 0x41, 0x51, 0x73,
|
||||||
|
0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x41, 0x7F, 0x41, 0x00, 0x20, 0x40,
|
||||||
|
0x41, 0x3F, 0x01, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40,
|
||||||
|
0x40, 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x3E,
|
||||||
|
0x41, 0x41, 0x41, 0x3E, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x3E, 0x41, 0x51,
|
||||||
|
0x21, 0x5E, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x26, 0x49, 0x49, 0x49, 0x32,
|
||||||
|
0x03, 0x01, 0x7F, 0x01, 0x03, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x1F, 0x20,
|
||||||
|
0x40, 0x20, 0x1F, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14,
|
||||||
|
0x63, 0x03, 0x04, 0x78, 0x04, 0x03, 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00,
|
||||||
|
0x7F, 0x41, 0x41, 0x41, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41,
|
||||||
|
0x41, 0x7F, 0x04, 0x02, 0x01, 0x02, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40,
|
||||||
|
0x00, 0x03, 0x07, 0x08, 0x00, 0x20, 0x54, 0x54, 0x78, 0x40, 0x7F, 0x28,
|
||||||
|
0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x28,
|
||||||
|
0x7F, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x08, 0x7E, 0x09, 0x02, 0x18,
|
||||||
|
0xA4, 0xA4, 0x9C, 0x78, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7D,
|
||||||
|
0x40, 0x00, 0x20, 0x40, 0x40, 0x3D, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00,
|
||||||
|
0x00, 0x41, 0x7F, 0x40, 0x00, 0x7C, 0x04, 0x78, 0x04, 0x78, 0x7C, 0x08,
|
||||||
|
0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xFC, 0x18, 0x24, 0x24,
|
||||||
|
0x18, 0x18, 0x24, 0x24, 0x18, 0xFC, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x48,
|
||||||
|
0x54, 0x54, 0x54, 0x24, 0x04, 0x04, 0x3F, 0x44, 0x24, 0x3C, 0x40, 0x40,
|
||||||
|
0x20, 0x7C, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x3C, 0x40, 0x30, 0x40, 0x3C,
|
||||||
|
0x44, 0x28, 0x10, 0x28, 0x44, 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x44, 0x64,
|
||||||
|
0x54, 0x4C, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00,
|
||||||
|
0x00, 0x00, 0x41, 0x36, 0x08, 0x00, 0x02, 0x01, 0x02, 0x04, 0x02, 0x3C,
|
||||||
|
0x26, 0x23, 0x26, 0x3C, 0x1E, 0xA1, 0xA1, 0x61, 0x12, 0x3A, 0x40, 0x40,
|
||||||
|
0x20, 0x7A, 0x38, 0x54, 0x54, 0x55, 0x59, 0x21, 0x55, 0x55, 0x79, 0x41,
|
||||||
|
0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
|
||||||
|
0x21, 0x55, 0x54, 0x78, 0x40, 0x20, 0x54, 0x55, 0x79, 0x40, 0x0C, 0x1E,
|
||||||
|
0x52, 0x72, 0x12, 0x39, 0x55, 0x55, 0x55, 0x59, 0x39, 0x54, 0x54, 0x54,
|
||||||
|
0x59, 0x39, 0x55, 0x54, 0x54, 0x58, 0x00, 0x00, 0x45, 0x7C, 0x41, 0x00,
|
||||||
|
0x02, 0x45, 0x7D, 0x42, 0x00, 0x01, 0x45, 0x7C, 0x40, 0x7D, 0x12, 0x11,
|
||||||
|
0x12, 0x7D, // A-umlaut
|
||||||
|
0xF0, 0x28, 0x25, 0x28, 0xF0, 0x7C, 0x54, 0x55, 0x45, 0x00, 0x20, 0x54,
|
||||||
|
0x54, 0x7C, 0x54, 0x7C, 0x0A, 0x09, 0x7F, 0x49, 0x32, 0x49, 0x49, 0x49,
|
||||||
|
0x32, 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
|
||||||
|
0x32, 0x4A, 0x48, 0x48, 0x30, 0x3A, 0x41, 0x41, 0x21, 0x7A, 0x3A, 0x42,
|
||||||
|
0x40, 0x20, 0x78, 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 0x3D, 0x42, 0x42, 0x42,
|
||||||
|
0x3D, // O-umlaut
|
||||||
|
0x3D, 0x40, 0x40, 0x40, 0x3D, 0x3C, 0x24, 0xFF, 0x24, 0x24, 0x48, 0x7E,
|
||||||
|
0x49, 0x43, 0x66, 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 0xFF, 0x09, 0x29, 0xF6,
|
||||||
|
0x20, 0xC0, 0x88, 0x7E, 0x09, 0x03, 0x20, 0x54, 0x54, 0x79, 0x41, 0x00,
|
||||||
|
0x00, 0x44, 0x7D, 0x41, 0x30, 0x48, 0x48, 0x4A, 0x32, 0x38, 0x40, 0x40,
|
||||||
|
0x22, 0x7A, 0x00, 0x7A, 0x0A, 0x0A, 0x72, 0x7D, 0x0D, 0x19, 0x31, 0x7D,
|
||||||
|
0x26, 0x29, 0x29, 0x2F, 0x28, 0x26, 0x29, 0x29, 0x29, 0x26, 0x30, 0x48,
|
||||||
|
0x4D, 0x40, 0x20, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||||
|
0x38, 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 0x2F, 0x10, 0x28, 0x34, 0xFA, 0x00,
|
||||||
|
0x00, 0x7B, 0x00, 0x00, 0x08, 0x14, 0x2A, 0x14, 0x22, 0x22, 0x14, 0x2A,
|
||||||
|
0x14, 0x08, 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old
|
||||||
|
// code
|
||||||
|
0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block
|
||||||
|
0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block
|
||||||
|
0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x10, 0x10, 0xFF, 0x00, 0x14, 0x14,
|
||||||
|
0x14, 0xFF, 0x00, 0x10, 0x10, 0xFF, 0x00, 0xFF, 0x10, 0x10, 0xF0, 0x10,
|
||||||
|
0xF0, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00,
|
||||||
|
0x00, 0xFF, 0x00, 0xFF, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x14, 0x14, 0x17,
|
||||||
|
0x10, 0x1F, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0x1F, 0x00,
|
||||||
|
0x10, 0x10, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10,
|
||||||
|
0x10, 0x1F, 0x10, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x00, 0x00, 0x00, 0xFF,
|
||||||
|
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0x14, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x1F,
|
||||||
|
0x10, 0x17, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14, 0x17, 0x10, 0x17,
|
||||||
|
0x14, 0x14, 0xF4, 0x04, 0xF4, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14,
|
||||||
|
0x14, 0x14, 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x17,
|
||||||
|
0x14, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x10,
|
||||||
|
0x10, 0xF0, 0x10, 0xF0, 0x00, 0x00, 0x1F, 0x10, 0x1F, 0x00, 0x00, 0x00,
|
||||||
|
0x1F, 0x14, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x00, 0x00, 0xF0, 0x10, 0xF0,
|
||||||
|
0x10, 0x10, 0xFF, 0x10, 0xFF, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x10, 0x10,
|
||||||
|
0x10, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x10, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x38, 0x44, 0x44,
|
||||||
|
0x38, 0x44, 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
|
||||||
|
0x7E, 0x02, 0x02, 0x06, 0x06, 0x02, 0x7E, 0x02, 0x7E, 0x02, 0x63, 0x55,
|
||||||
|
0x49, 0x41, 0x63, 0x38, 0x44, 0x44, 0x3C, 0x04, 0x40, 0x7E, 0x20, 0x1E,
|
||||||
|
0x20, 0x06, 0x02, 0x7E, 0x02, 0x02, 0x99, 0xA5, 0xE7, 0xA5, 0x99, 0x1C,
|
||||||
|
0x2A, 0x49, 0x2A, 0x1C, 0x4C, 0x72, 0x01, 0x72, 0x4C, 0x30, 0x4A, 0x4D,
|
||||||
|
0x4D, 0x30, 0x30, 0x48, 0x78, 0x48, 0x30, 0xBC, 0x62, 0x5A, 0x46, 0x3D,
|
||||||
|
0x3E, 0x49, 0x49, 0x49, 0x00, 0x7E, 0x01, 0x01, 0x01, 0x7E, 0x2A, 0x2A,
|
||||||
|
0x2A, 0x2A, 0x2A, 0x44, 0x44, 0x5F, 0x44, 0x44, 0x40, 0x51, 0x4A, 0x44,
|
||||||
|
0x40, 0x40, 0x44, 0x4A, 0x51, 0x40, 0x00, 0x00, 0xFF, 0x01, 0x03, 0xE0,
|
||||||
|
0x80, 0xFF, 0x00, 0x00, 0x08, 0x08, 0x6B, 0x6B, 0x08, 0x36, 0x12, 0x36,
|
||||||
|
0x24, 0x36, 0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00, 0x00, 0x18, 0x18, 0x00,
|
||||||
|
0x00, 0x00, 0x10, 0x10, 0x00, 0x30, 0x40, 0xFF, 0x01, 0x01, 0x00, 0x1F,
|
||||||
|
0x01, 0x01, 0x1E, 0x00, 0x19, 0x1D, 0x17, 0x12, 0x00, 0x3C, 0x3C, 0x3C,
|
||||||
|
0x3C, 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP
|
||||||
|
};
|
||||||
|
|
||||||
|
// allow clean compilation with [-Wunused-const-variable=] and [-Wall]
|
||||||
|
static inline void avoid_unused_const_variable_compiler_warning(void) {
|
||||||
|
(void)font;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FONT5X7_H
|
||||||
@@ -128,19 +128,47 @@
|
|||||||
statusEl.textContent = 'running -- IDBFS-backed identity/prefs persist across a real reload.';
|
statusEl.textContent = 'running -- IDBFS-backed identity/prefs persist across a real reload.';
|
||||||
log('module ready; sim_enqueue_key = ' + (typeof Module._sim_enqueue_key));
|
log('module ready; sim_enqueue_key = ' + (typeof Module._sim_enqueue_key));
|
||||||
|
|
||||||
|
// A real MomentaryButton(pin, 1000, ...) fires either a short click OR
|
||||||
|
// a long-press event for one physical press, never both -- e.g.
|
||||||
|
// holding OK/Enter opens the real context menu (see
|
||||||
|
// UITask::handleLongPress() mapping KEY_ENTER -> KEY_CONTEXT_MENU)
|
||||||
|
// instead of the normal short-press action. LONG_PRESS_MS mirrors
|
||||||
|
// that same 1000ms threshold every real board's MomentaryButton uses.
|
||||||
|
const LONG_PRESS_MS = 1000;
|
||||||
|
|
||||||
function sendKey(code) {
|
function sendKey(code) {
|
||||||
if (Module && Module._sim_enqueue_key) Module._sim_enqueue_key(code);
|
if (Module && Module._sim_enqueue_key) Module._sim_enqueue_key(code);
|
||||||
}
|
}
|
||||||
|
function sendKeyLongPress(code) {
|
||||||
|
if (Module && Module._sim_enqueue_key_longpress) Module._sim_enqueue_key_longpress(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Press-and-hold for the on-page buttons: start a timer on press-down,
|
||||||
|
// fire the long-press call if still held past the threshold, and
|
||||||
|
// suppress the short click on release if it already fired.
|
||||||
document.querySelectorAll('button[data-key]').forEach((btn) => {
|
document.querySelectorAll('button[data-key]').forEach((btn) => {
|
||||||
btn.addEventListener('click', () => {
|
const code = Number(btn.dataset.key);
|
||||||
const code = Number(btn.dataset.key);
|
let timer = null, longFired = false;
|
||||||
sendKey(code);
|
btn.addEventListener('pointerdown', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
longFired = false;
|
||||||
|
timer = setTimeout(() => { longFired = true; sendKeyLongPress(code); }, LONG_PRESS_MS);
|
||||||
});
|
});
|
||||||
|
btn.addEventListener('pointerup', () => {
|
||||||
|
if (timer) { clearTimeout(timer); timer = null; }
|
||||||
|
if (!longFired) sendKey(code);
|
||||||
|
});
|
||||||
|
const cancel = () => { if (timer) { clearTimeout(timer); timer = null; } };
|
||||||
|
btn.addEventListener('pointerleave', cancel);
|
||||||
|
btn.addEventListener('pointercancel', cancel);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Keyboard passthrough: arrows/Enter/Escape/n/p/WASD, same mapping
|
// Keyboard passthrough: arrows/Enter/Escape/n/p/WASD, same mapping
|
||||||
// UITask.cpp's native SIM_PLATFORM stdin branch already uses.
|
// UITask.cpp's native SIM_PLATFORM stdin branch already uses. Same
|
||||||
|
// press-and-hold logic as the buttons above, keyed by e.key so
|
||||||
|
// multiple keys can be held independently; the `keyHold.has()` guard
|
||||||
|
// ignores the browser's own keydown auto-repeat (which would
|
||||||
|
// otherwise restart the long-press timer on every repeat tick).
|
||||||
const KEYMAP = {
|
const KEYMAP = {
|
||||||
ArrowUp: 0xB5, ArrowDown: 0xB6, ArrowLeft: 0xB4, ArrowRight: 0xB7,
|
ArrowUp: 0xB5, ArrowDown: 0xB6, ArrowLeft: 0xB4, ArrowRight: 0xB7,
|
||||||
Enter: 13, ' ': 13,
|
Enter: 13, ' ': 13,
|
||||||
@@ -148,8 +176,22 @@
|
|||||||
w: 0xB5, s: 0xB6, a: 0xB4, d: 0xB7,
|
w: 0xB5, s: 0xB6, a: 0xB4, d: 0xB7,
|
||||||
n: 0xF1, p: 0xF2,
|
n: 0xF1, p: 0xF2,
|
||||||
};
|
};
|
||||||
|
const keyHold = new Map();
|
||||||
window.addEventListener('keydown', (e) => {
|
window.addEventListener('keydown', (e) => {
|
||||||
if (e.key in KEYMAP) { sendKey(KEYMAP[e.key]); e.preventDefault(); }
|
if (!(e.key in KEYMAP)) return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (keyHold.has(e.key)) return; // auto-repeat, not a new press
|
||||||
|
const code = KEYMAP[e.key];
|
||||||
|
const state = { longFired: false };
|
||||||
|
state.timer = setTimeout(() => { state.longFired = true; sendKeyLongPress(code); }, LONG_PRESS_MS);
|
||||||
|
keyHold.set(e.key, state);
|
||||||
|
});
|
||||||
|
window.addEventListener('keyup', (e) => {
|
||||||
|
const state = keyHold.get(e.key);
|
||||||
|
if (!state) return;
|
||||||
|
clearTimeout(state.timer);
|
||||||
|
if (!state.longFired) sendKey(KEYMAP[e.key]);
|
||||||
|
keyHold.delete(e.key);
|
||||||
});
|
});
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
statusEl.textContent = 'failed to start: ' + err;
|
statusEl.textContent = 'failed to start: ' + err;
|
||||||
|
|||||||
@@ -252,6 +252,14 @@
|
|||||||
function sendKey(inst, code) {
|
function sendKey(inst, code) {
|
||||||
inst.mod.ccall('sim_enqueue_key', null, ['number'], [code]);
|
inst.mod.ccall('sim_enqueue_key', null, ['number'], [code]);
|
||||||
}
|
}
|
||||||
|
function sendKeyLongPress(inst, code) {
|
||||||
|
inst.mod.ccall('sim_enqueue_key_longpress', null, ['number'], [code]);
|
||||||
|
}
|
||||||
|
// Same 1000ms MomentaryButton hold threshold as the single-instance
|
||||||
|
// harness (web/index.html) -- see that file's comment for why holding
|
||||||
|
// OK/Enter matters (real context-menu access via
|
||||||
|
// UITask::handleLongPress()).
|
||||||
|
const LONG_PRESS_MS = 1000;
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
log('booting instance A (companion_radio)...');
|
log('booting instance A (companion_radio)...');
|
||||||
@@ -298,10 +306,21 @@
|
|||||||
setInterval(() => pollRelay(R), 100);
|
setInterval(() => pollRelay(R), 100);
|
||||||
|
|
||||||
document.querySelectorAll('button[data-instance]').forEach((btn) => {
|
document.querySelectorAll('button[data-instance]').forEach((btn) => {
|
||||||
btn.addEventListener('click', () => {
|
const inst = btn.dataset.instance === 'A' ? A : B;
|
||||||
const inst = btn.dataset.instance === 'A' ? A : B;
|
const code = Number(btn.dataset.key);
|
||||||
sendKey(inst, Number(btn.dataset.key));
|
let timer = null, longFired = false;
|
||||||
|
btn.addEventListener('pointerdown', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
longFired = false;
|
||||||
|
timer = setTimeout(() => { longFired = true; sendKeyLongPress(inst, code); }, LONG_PRESS_MS);
|
||||||
});
|
});
|
||||||
|
btn.addEventListener('pointerup', () => {
|
||||||
|
if (timer) { clearTimeout(timer); timer = null; }
|
||||||
|
if (!longFired) sendKey(inst, code);
|
||||||
|
});
|
||||||
|
const cancel = () => { if (timer) { clearTimeout(timer); timer = null; } };
|
||||||
|
btn.addEventListener('pointerleave', cancel);
|
||||||
|
btn.addEventListener('pointercancel', cancel);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('btn-advert-a').addEventListener('click', () => {
|
document.getElementById('btn-advert-a').addEventListener('click', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user