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>
90 lines
3.3 KiB
C++
90 lines
3.3 KiB
C++
// 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();
|
|
}
|