Initial commit: Chatter — assistive-writing app for reMarkable Paper Pro Move
Direct-framebuffer ink pipeline (stock-quality strokes), finger-wipe erase, growable scrolling canvas with color-ghost cleanup, bidirectional toggle with a persistent 4-finger return launcher, instant button feedback. Includes prebuilt aarch64 binaries (dist/), build/deploy/install scripts, a user guide, and a complete technical reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
56
tools/fbdump.cpp
Normal file
56
tools/fbdump.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
// Framebuffer snapshot shim: preloaded into xochitl, it captures the e-paper
|
||||
// framebuffer (via setBuffers) and, when the trigger file /tmp/fbdump appears,
|
||||
// saves the current framebuffer to /home/root/fbdump.png. Used to recover the
|
||||
// real Calligraphy nib angle by measuring xochitl's actual rendered strokes.
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
#include <QImage>
|
||||
#include <tuple>
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <cstdio>
|
||||
|
||||
namespace {
|
||||
uchar *g_bits = nullptr;
|
||||
int g_w = 0, g_h = 0, g_bpl = 0, g_fmt = 0;
|
||||
bool g_started = false;
|
||||
|
||||
void *dumper(void *)
|
||||
{
|
||||
fprintf(stderr, "FBSHIM dumper thread started\n");
|
||||
for (;;) {
|
||||
usleep(400000);
|
||||
struct stat st;
|
||||
if (g_bits && stat("/tmp/fbdump", &st) == 0) {
|
||||
QImage snap = QImage(g_bits, g_w, g_h, g_bpl, QImage::Format(g_fmt)).copy();
|
||||
bool ok = snap.save("/home/root/fbdump.png");
|
||||
if (!ok) ok = snap.save("/home/root/fbdump.bmp", "BMP");
|
||||
::unlink("/tmp/fbdump");
|
||||
fprintf(stderr, "FBDUMP %dx%d saved ok=%d\n", g_w, g_h, int(ok));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void _ZN13EPFramebuffer10setBuffersESt5tupleIJ6QImageS1_EEPS1_(
|
||||
void *self, void *tuplePtr, void *imgPtr)
|
||||
{
|
||||
static void (*real)(void *, void *, void *) = nullptr;
|
||||
if (!real)
|
||||
real = (void (*)(void *, void *, void *))dlsym(
|
||||
RTLD_NEXT, "_ZN13EPFramebuffer10setBuffersESt5tupleIJ6QImageS1_EEPS1_");
|
||||
auto *t = reinterpret_cast<std::tuple<QImage, QImage> *>(tuplePtr);
|
||||
const QImage &a = std::get<0>(*t);
|
||||
g_bits = const_cast<uchar *>(a.constBits());
|
||||
g_w = a.width(); g_h = a.height(); g_bpl = a.bytesPerLine(); g_fmt = int(a.format());
|
||||
fprintf(stderr, "FBSHIM setBuffers %dx%d fmt=%d\n", g_w, g_h, g_fmt);
|
||||
if (!g_started) {
|
||||
g_started = true;
|
||||
pthread_t th;
|
||||
pthread_create(&th, nullptr, dumper, nullptr);
|
||||
}
|
||||
if (real) real(self, tuplePtr, imgPtr);
|
||||
}
|
||||
Reference in New Issue
Block a user