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:
89
src/main.cpp
Normal file
89
src/main.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// Chatter — Qt Quick app with a direct-framebuffer ink pipeline.
|
||||
// Pen strokes are drawn straight into the e-paper framebuffer (captured by
|
||||
// FbCapture) and refreshed with an explicit solid swap (InkEngine). Qt Quick is
|
||||
// used only for the static UI (Back / Clear). This is what gives stock-quality
|
||||
// solid, low-latency ink — the Qt scene graph can't.
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
#include <QScreen>
|
||||
#include <QSettings>
|
||||
#include <QVariantMap>
|
||||
#include <QColor>
|
||||
#include <QRegion>
|
||||
#include <QRect>
|
||||
|
||||
#include "PenDevice.h"
|
||||
#include "AppControl.h"
|
||||
#include "InkEngine.h"
|
||||
#include "FbCapture.h" // links the setBuffers interposer into the executable
|
||||
|
||||
struct PenStyle {
|
||||
int type = 2;
|
||||
qreal size = 2.0;
|
||||
QColor color = Qt::black;
|
||||
};
|
||||
|
||||
// Read the pen the user last selected in the standard GUI (xochitl.conf,
|
||||
// [General]/LastWritingTool — flushed when xochitl stops, i.e. on Back).
|
||||
static PenStyle readPenStyle()
|
||||
{
|
||||
PenStyle ps;
|
||||
QSettings xs(QStringLiteral("/home/root/.config/remarkable/xochitl.conf"),
|
||||
QSettings::IniFormat);
|
||||
const QVariantMap t = xs.value(QStringLiteral("LastWritingTool")).toMap();
|
||||
if (t.isEmpty()) {
|
||||
qWarning("readPenStyle: LastWritingTool unreadable; using defaults");
|
||||
return ps;
|
||||
}
|
||||
ps.type = t.value("LastPen").toInt();
|
||||
ps.size = t.value("LastPenSize").toDouble();
|
||||
const uint code = t.value("LastPenColorCode").toUInt();
|
||||
if (code != 0)
|
||||
ps.color = QColor::fromRgba(QRgb(code));
|
||||
qInfo("readPenStyle: pen=%d size=%.2f color=%s", ps.type, ps.size,
|
||||
qPrintable(ps.color.name(QColor::HexArgb)));
|
||||
return ps;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
const PenStyle penStyle = readPenStyle();
|
||||
|
||||
const QSize screen = app.primaryScreen()->geometry().size();
|
||||
PenDevice pen(screen.width(), screen.height());
|
||||
AppControl appControl;
|
||||
|
||||
InkEngine ink;
|
||||
ink.setInkColor(penStyle.color);
|
||||
// reMarkable exposes only the size CATEGORY (LastPenSize 1/2/3 =
|
||||
// thin/thicker/thickest). Calibrate to measured widths on this 264-PPI panel:
|
||||
// thicker ~1mm, thickest ~2mm, thinnest ~2px. px/mm = 264/25.4 ~= 10.4.
|
||||
constexpr double pxPerMm = 264.0 / 25.4;
|
||||
const double maxMm = qMax(0.2, (penStyle.size - 1.0) * 1.0); // sz3->2mm, sz2->1mm
|
||||
ink.setWidthRange(2.0, maxMm * pxPerMm);
|
||||
ink.setPenType(penStyle.type);
|
||||
|
||||
// Back button (finger or stylus) → return to the standard GUI.
|
||||
QObject::connect(&ink, &InkEngine::backRequested, &appControl, &AppControl::returnToStandard);
|
||||
|
||||
// Pen drives the ink engine directly (no QML in the ink loop).
|
||||
QObject::connect(&pen, &PenDevice::strokeStart, &ink, &InkEngine::strokeStart);
|
||||
QObject::connect(&pen, &PenDevice::strokeMove, &ink, &InkEngine::strokeMove);
|
||||
QObject::connect(&pen, &PenDevice::strokeEnd, &ink, &InkEngine::strokeEnd);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
engine.rootContext()->setContextProperty("appControl", &appControl);
|
||||
engine.rootContext()->setContextProperty("ink", &ink);
|
||||
|
||||
QObject::connect(
|
||||
&engine, &QQmlApplicationEngine::objectCreationFailed,
|
||||
&app, []() { QCoreApplication::exit(-1); },
|
||||
Qt::QueuedConnection);
|
||||
|
||||
engine.loadFromModule("Chatter", "Main");
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
Reference in New Issue
Block a user