Merge remote-tracking branch 'abmantis/pen_marker'

This commit is contained in:
Rien Maertens 2024-01-09 17:25:36 +01:00
commit f4d3065a71
No known key found for this signature in database
GPG Key ID: AE66CE42F1AF9DEF
4 changed files with 476 additions and 4 deletions

309
Cargo.lock generated
View File

@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "anstream"
version = "0.6.5"
@ -56,24 +65,93 @@ version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "approx"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278"
dependencies = [
"num-traits",
]
[[package]]
name = "atomic"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "bitvec"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cgmath"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
dependencies = [
"approx",
"num-traits",
]
[[package]]
name = "clap"
version = "4.4.14"
@ -120,6 +198,42 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "env_logger"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "epoll"
version = "4.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
dependencies = [
"bitflags 2.4.1",
"libc",
]
[[package]]
name = "evdev"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bed59fcc8cfd6b190814a509018388462d3b203cf6dd10db5c00087e72a83f3"
dependencies = [
"bitvec",
"cfg-if",
"libc",
"nix",
"thiserror",
]
[[package]]
name = "fehler"
version = "1.0.0"
@ -140,25 +254,127 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "fxhash"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
dependencies = [
"byteorder",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "libc"
version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "libremarkable"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "665568ee80be33f72306e91a1f2513098da90b85d60874bd7ede74bd9262f397"
dependencies = [
"atomic",
"cgmath",
"env_logger",
"epoll",
"evdev",
"fxhash",
"libc",
"log",
"once_cell",
]
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "lz-fear"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06aad1ce45e4ccf7a8d7d43e0c3ad38dc5d2255174a5f29a3c39d961fbc6181d"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"byteorder",
"fehler",
"thiserror",
"twox-hash",
]
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "nix"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
dependencies = [
"bitflags 1.3.2",
"cc",
"cfg-if",
"libc",
"memoffset",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "proc-macro2"
version = "1.0.76"
@ -177,12 +393,48 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "restream"
version = "1.2.0"
dependencies = [
"anyhow",
"clap",
"libremarkable",
"lz-fear",
]
@ -220,6 +472,21 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "termcolor"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.56"
@ -262,6 +529,37 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"
@ -327,3 +625,12 @@ name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]

View File

@ -8,3 +8,5 @@ edition = "2018"
anyhow = "1.0"
lz-fear = "0.1"
clap = { version = "4.4", features = [ "derive" ] }
libremarkable = { version = "0.6", features = [ "input" ], default-features = false }

View File

@ -12,6 +12,7 @@ rm2_old_firmware_version="3.7.0.1930"
# default values for arguments
remarkable="${REMARKABLE_IP:-10.11.99.1}" # remarkable IP address
landscape=true # rotate 90 degrees to the right
cursor=false # show a cursor where the pen is hovering
output_path=- # display output through ffplay
format=- # automatic output format
webcam=false # not to a webcam
@ -32,6 +33,10 @@ while [ $# -gt 0 ]; do
landscape=false
shift
;;
-c | --cursor)
cursor=true
shift
;;
-s | --source)
remarkable="$2"
shift
@ -87,10 +92,11 @@ while [ $# -gt 0 ]; do
shift
;;
-h | --help | *)
echo "Usage: $0 [-p] [-u] [-s <source>] [-o <output>] [-f <format>] [-t <title>] [-m] [-w] [--hflip]"
echo "Usage: $0 [-p] [-c] [-u] [-s <source>] [-o <output>] [-f <format>] [-t <title>] [-m] [-w] [--hflip]"
echo "Examples:"
echo " $0 # live view in landscape"
echo " $0 -p # live view in portrait"
echo " $0 -c # show a cursor where the pen is hovering (rM2 only)"
echo " $0 -s 192.168.0.10 # connect to different IP"
echo " $0 -o remarkable.mp4 # record to a file"
echo " $0 -o udp://dest:1234 -f mpegts # record to a stream"
@ -255,6 +261,10 @@ set -e # stop if an error occurs
restream_options="-h $height -w $width -b $bytes_per_pixel -f $fb_file"
if "$cursor"; then
restream_options="$restream_options -c"
fi
# shellcheck disable=SC2089
restream_rs="PATH=\"\$PATH:/opt/bin/:.\" restream $restream_options"
if $unsecure_connection; then

View File

@ -6,12 +6,15 @@ use anyhow::{Context, Result};
use clap::Parser;
use lz_fear::CompressionSettings;
use libremarkable::cgmath;
use libremarkable::input::{ev::EvDevContext, InputDevice, InputEvent, WacomEvent, WacomPen};
use std::default::Default;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
use std::net::{TcpListener, TcpStream};
use std::process::Command;
use std::sync::mpsc::channel;
use std::sync::mpsc::{channel, Receiver};
use std::thread;
use std::time::Duration;
@ -37,6 +40,10 @@ pub struct Opts {
/// File containing the framebuffer data. If this equals the string ':mem:' it will try to read the framebuffer from xochitl's process memory (rM2 only).
#[arg(long, name = "path", short = 'f')]
file: String,
/// Show a cursor where the pen is hovering.
#[arg(long, name = "cursor", short = 'c')]
show_cursor: bool,
}
fn main() -> Result<()> {
@ -51,7 +58,14 @@ fn main() -> Result<()> {
(opts.file.to_owned(), 0)
};
let streamer = ReStreamer::init(&file, offset, opts.width, opts.height, opts.bytes_per_pixel)?;
let streamer = ReStreamer::init(
&file,
offset,
opts.width,
opts.height,
opts.bytes_per_pixel,
opts.show_cursor,
)?;
let stdout = std::io::stdout();
let data_target: Box<dyn Write> = if let Some(port) = opts.listen {
@ -125,6 +139,14 @@ pub struct ReStreamer {
start: u64,
cursor: usize,
size: usize,
width: usize,
height: usize,
bytes_per_pixel: usize,
show_cursor: bool,
input_rx: Receiver<InputEvent>,
pen_pos: Option<(usize, usize)>,
drawing: bool,
}
impl ReStreamer {
@ -134,16 +156,30 @@ impl ReStreamer {
width: usize,
height: usize,
bytes_per_pixel: usize,
show_cursor: bool,
) -> Result<ReStreamer> {
let start = offset as u64;
let size = width * height * bytes_per_pixel;
let cursor = 0;
let file = File::open(path)?;
let (input_tx, input_rx) = channel::<InputEvent>();
if show_cursor {
EvDevContext::new(InputDevice::Wacom, input_tx).start();
}
let mut streamer = ReStreamer {
file,
start: start,
cursor,
size,
width,
height,
bytes_per_pixel,
show_cursor,
input_rx,
pen_pos: None,
drawing: false,
};
streamer.next_frame()?;
Ok(streamer)
@ -152,10 +188,122 @@ impl ReStreamer {
pub fn next_frame(&mut self) -> std::io::Result<()> {
self.file.seek(SeekFrom::Start(self.start))?;
self.cursor = 0;
if self.show_cursor {
self.read_input();
}
Ok(())
}
/// Read input events to figure out pen state and position.
fn read_input(&mut self) {
let mut down = self.pen_pos.is_some();
let mut pos = self.pen_pos.unwrap_or((0, 0));
while let Ok(event) = self.input_rx.try_recv() {
match event {
InputEvent::WacomEvent { event: e } => match e {
WacomEvent::InstrumentChange {
pen: WacomPen::ToolPen,
state: s,
} => {
down = s;
}
WacomEvent::Hover {
position: cgmath::Point2 { x, y },
..
} => {
pos = (x as usize, y as usize);
self.drawing = false;
}
WacomEvent::Draw { .. } => {
// no need to show position while drawing
self.drawing = true;
}
_ => (),
},
_ => (),
}
}
self.pen_pos = if down { Some(pos) } else { None };
}
/// Draw pen position into fb data, if necessary (in hover range and not drawing).
fn draw_pen_position(&mut self, buf: &mut [u8]) {
if let (false, Some((y, x))) = (self.drawing, self.pen_pos) {
let flip = self.width > self.height;
let (x, y) = if flip { (y, x) } else { (x, y) };
// we need negative numbers to calculate offsets correctly
let width = if flip { self.height } else { self.width } as isize;
let height = if flip { self.width } else { self.height } as isize;
let bpp = self.bytes_per_pixel as isize;
let cursor = self.cursor as isize;
for (i, (yoff, no)) in PEN_IMAGE.iter().enumerate() {
// we draw vertically (lines along y)
let xoff = i as isize - (PEN_IMAGE.len() as isize / 2);
let xstart = x as isize + xoff;
// line outside of canvas?
if xstart < 0 || xstart >= width {
continue;
}
let mut ystart = (height - y as isize) + yoff;
let mut no = *no;
// cut-off at sides
if ystart < 0 {
no += ystart;
ystart = 0;
}
if ystart + no > height {
no = height - ystart;
}
if no <= 0 {
continue;
}
// translate to buf indexes, check bounds and draw
let mut px_start = (xstart * height + ystart) * bpp;
let mut px_end = px_start + no * bpp;
// outside current buf?
if px_end < cursor || px_start >= cursor + buf.len() as isize {
continue;
}
// truncate if partially outside
if px_start < cursor {
px_start = cursor;
}
if px_end > cursor + buf.len() as isize {
px_end = cursor + buf.len() as isize;
}
// invert pixel (on RM2)
// TODO: Do something sensible on RM1
for b in buf[(px_start - cursor) as usize..(px_end - cursor) as usize].iter_mut() {
*b = 255 - *b;
}
}
}
}
}
// Image of pen, given as (offset from pen position, number of pixels)
static PEN_IMAGE: [(isize, isize); 17] = [
(0, 1), // 00000000100000000
(0, 1), // 00000000100000000
(0, 1), // 00000000100000000
(-1, 3), // 00000001110000000
(-3, 7), // 00000111111100000
(-4, 9), // 00001111111110000
(-4, 9), // 00001111111110000
(-5, 11), // 00011111111111000
(-8, 17), // 11111111111111111
(-5, 11), // 00011111111111000
(-4, 9), // 00001111111110000
(-4, 9), // 00001111111110000
(-3, 7), // 00000111111100000
(-1, 3), // 00000001110000000
(0, 1), // 00000000100000000
(0, 1), // 00000000100000000
(0, 1), // 00000000100000000
];
impl Read for ReStreamer {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let requested = buf.len();
@ -165,6 +313,11 @@ impl Read for ReStreamer {
let rest = self.size - self.cursor;
self.file.read(&mut buf[0..rest])?
};
if self.show_cursor {
self.draw_pen_position(&mut buf[0..bytes_read]);
}
self.cursor += bytes_read;
if self.cursor == self.size {
self.next_frame()?;