Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ oracle.env
riddle/dist/
drawlab/dist/
home/dist/
riddle/riddle-rm2
.DS_Store
49 changes: 49 additions & 0 deletions README-RM2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# riddle for reMarkable 2 — no XOVI or AppLoad

This build runs beside the stock reMarkable UI on RM2 firmware 3.28. It reads
real Wacom strokes without grabbing the device, sends a private reconstruction
of the writing to the configured vision model, and replays the reply through
the Wacom input device. xochitl records Tom's handwriting in the open notebook
as ordinary ink.

## Install

Copy the extracted `riddle-rm2` directory to `/home/root/riddle-rm2`, then:

```sh
cd /home/root/riddle-rm2
cp oracle.env.example oracle.env
vi oracle.env # set RIDDLE_OPENAI_KEY
chmod +x riddle-rm2 start-rm2.sh stop-rm2.sh
```

No root filesystem files, startup hooks, XOVI, AppLoad, or systemd units are
installed.

## Use

Open a blank notebook in the stock UI and write in its upper third. From SSH:

```sh
/home/root/riddle-rm2/start-rm2.sh
```

After a 2.8 second pause, Tom writes back through xochitl. Stop it before using
other parts of the UI:

```sh
/home/root/riddle-rm2/stop-rm2.sh
```

This companion build intentionally does not erase/fade existing notebook ink
and disables the takeover-only help and memory-page animations. The original
notebook remains a normal reMarkable document and syncs normally.

## Remove

```sh
/home/root/riddle-rm2/stop-rm2.sh
rm -rf /home/root/riddle-rm2 /home/root/riddle-data
```

Rebooting also stops it because no autostart component is installed.
2 changes: 1 addition & 1 deletion riddle/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "riddle"
version = "0.3.0"
edition = "2021"
license = "MIT"
description = "The diary of Tom Riddle, for the reMarkable Paper Pro"
description = "The diary of Tom Riddle, ported to the reMarkable 2"

[dependencies]
libc = "0.2"
Expand Down
9 changes: 9 additions & 0 deletions riddle/appload-rm2.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh
set -eu
cd "$(dirname "$0")"
if [ -f oracle.env ]; then
set -a
. ./oracle.env
set +a
fi
exec ./riddle-rm2
16 changes: 16 additions & 0 deletions riddle/build-rm2.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh
# Build the windowed reMarkable 2 AppLoad binary.
set -eu
cd "$(dirname "$0")"

TARGET=armv7-unknown-linux-gnueabihf
if command -v cross >/dev/null 2>&1; then
cross build --release --target "$TARGET"
else
: "${CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER:=arm-linux-gnueabihf-gcc}"
export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER
cargo build --release --target "$TARGET"
fi

cp "target/$TARGET/release/riddle" riddle-rm2
echo "built: $(pwd)/riddle-rm2"
7 changes: 4 additions & 3 deletions riddle/external.manifest.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"id": "riddle",
"name": "The Diary",
"version": "0.3.0",
"version": "0.3.0-rm2.2",
"description": "An enchanted diary: write to it with the pen — it writes back, and it remembers.",
"application": "appload-launch.sh",
"qtfb": false
"application": "appload-rm2.sh",
"qtfb": true,
"aspectRatio": "original"
}
Binary file added riddle/fonts/NotoSansTC-VF.ttf
Binary file not shown.
37 changes: 29 additions & 8 deletions riddle/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use std::io;

pub enum Display {
Qtfb(crate::qtfb::QtfbClient),
/// Headless companion mode: xochitl owns the real panel while riddle
/// renders into this private RGB565 page for OCR and layout.
Xochitl(Box<[u8]>),
#[allow(dead_code)]
Quill,
}
Expand All @@ -31,18 +34,36 @@ impl Display {
let key: i32 = key.parse().map_err(io::Error::other)?;
let mut client = crate::qtfb::QtfbClient::connect(
key,
crate::qtfb::FBFMT_RMPP_RGB565,
1620,
2160,
crate::qtfb::FBFMT_RM2FB,
crate::fb::SCREEN_W,
crate::fb::SCREEN_H,
2,
)?;
let _ = client.set_refresh_mode(crate::qtfb::REFRESH_MODE_UFAST);
let buf = client.framebuffer();
let (ptr, len) = (buf.as_mut_ptr(), buf.len());
let surface = Surface::new(ptr, len, 1620, 2160, 1620 * 2, PixFmt::Rgb565);
let surface = Surface::new(
ptr, len, crate::fb::SCREEN_W, crate::fb::SCREEN_H,
crate::fb::SCREEN_W * 2, PixFmt::Rgb565,
);
return Ok((Display::Qtfb(client), surface));
}

// reMarkable 2 needs no launcher: keep the stock UI running and use
// its Wacom input path to replay Tom's generated handwriting.
#[cfg(not(feature = "takeover"))]
{
let mut page = vec![0xff; crate::fb::SCREEN_W * crate::fb::SCREEN_H * 2]
.into_boxed_slice();
let ptr = page.as_mut_ptr();
let len = page.len();
let surface = Surface::new(
ptr, len, crate::fb::SCREEN_W, crate::fb::SCREEN_H,
crate::fb::SCREEN_W * 2, PixFmt::Rgb565,
);
return Ok((Display::Xochitl(page), surface));
}

#[cfg(feature = "takeover")]
{
unsafe {
Expand All @@ -60,10 +81,6 @@ impl Display {
Ok((Display::Quill, surface))
}
}
#[cfg(not(feature = "takeover"))]
Err(io::Error::other(
"QTFB_KEY not set and this build has no takeover backend",
))
}

/// Push a region to the panel. `fast` selects the low-latency waveform.
Expand All @@ -72,6 +89,7 @@ impl Display {
Display::Qtfb(c) => {
let _ = c.update_partial(x, y, w, h);
}
Display::Xochitl(_) => {}
#[allow(unused_variables)]
Display::Quill => {
#[cfg(feature = "takeover")]
Expand All @@ -89,6 +107,7 @@ impl Display {
Display::Qtfb(c) => {
let _ = c.update_all();
}
Display::Xochitl(_) => {}
#[allow(unused_variables)]
Display::Quill => {
#[cfg(feature = "takeover")]
Expand All @@ -107,6 +126,7 @@ impl Display {
Display::Qtfb(c) => {
let _ = c.request_full_refresh();
}
Display::Xochitl(_) => {}
#[allow(unused_variables)]
Display::Quill => {
#[cfg(feature = "takeover")]
Expand All @@ -124,6 +144,7 @@ impl Display {
pub fn pump(&self) -> io::Result<Vec<crate::qtfb::InputEvent>> {
match self {
Display::Qtfb(c) => c.drain_events(),
Display::Xochitl(_) => Ok(Vec::new()),
Display::Quill => {
#[cfg(feature = "takeover")]
unsafe {
Expand Down
5 changes: 3 additions & 2 deletions riddle/src/fb.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Geometry helpers. Drawing lives in surface.rs.

pub const SCREEN_W: usize = 1620;
pub const SCREEN_H: usize = 2160;
// reMarkable 1/2 native portrait framebuffer.
pub const SCREEN_W: usize = 1404;
pub const SCREEN_H: usize = 1872;

/// Grow-only pixel bounding box, used to build update/dissolve regions.
#[derive(Clone, Copy, Debug)]
Expand Down
Loading