diff --git a/reStream.sh b/reStream.sh index 6cbce9a..aaeb195 100755 --- a/reStream.sh +++ b/reStream.sh @@ -9,11 +9,10 @@ version="1.5.0" rm2_firmware_version_3_7="3.7.0.1930" rm2_firmware_version_3_24="3.24" -rm2_firmware_version_3_27="3.27.1.0" # default values for arguments remarkable="${REMARKABLE_IP:-10.11.99.1}" # remarkable IP address -landscape=true # rotate 90 degrees to the right +portrait=false # 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 @@ -34,7 +33,7 @@ while [ $# -gt 0 ]; do exit ;; -p | --portrait) - landscape=false + portrait=true shift ;; -c | --cursor) @@ -140,7 +139,7 @@ is_current_rm_firmware_version_ge() { exit_rm() { ssh_cmd 'kill $(pidof restream)' } -trap exit_rm EXIT INT HUP +trap exit_rm EXIT INT HUP TERM # SSH_CONNECTION is a variable on reMarkable => ssh '' instead of ssh "" # shellcheck disable=SC2016 @@ -157,6 +156,7 @@ if ! ssh_cmd true; then exit 1 fi +pen_orientation_opt="" rm_version="$(ssh_cmd cat /sys/devices/soc0/machine)" case "$rm_version" in @@ -182,8 +182,8 @@ case "$rm_version" in fb_file=":mem:" # Use updated video settings? - if is_current_rm_firmware_version_ge $rm2_firmware_version_3_27; then - echo "Using the 3.27+ :mem: video settings." + if is_current_rm_firmware_version_ge $rm2_firmware_version_3_24; then + echo "Using the newer :mem: video settings." bytes_per_pixel=4 pixel_format="bgra" video_filters="$video_filters,transpose=1" @@ -192,18 +192,8 @@ case "$rm_version" in height=$width width=$tmp - skip_offset=4705256 - elif is_current_rm_firmware_version_ge $rm2_firmware_version_3_24; then - echo "Using the 3.24+ :mem: video settings." - bytes_per_pixel=4 - pixel_format="bgra" - video_filters="$video_filters,transpose=2" - - tmp=$height - height=$width - width=$tmp - skip_offset=2629636 + # Use the previous video settings. elif is_current_rm_firmware_version_ge $rm2_firmware_version_3_7; then echo "Using the older :mem: video settings." @@ -270,8 +260,8 @@ fi # store extra ffmpeg arguments in $@ set -- -# rotate 90 degrees if landscape=true -$landscape && video_filters="$video_filters,transpose=1" +# rotate 90 degrees if portrait=true +$portrait && video_filters="$video_filters,transpose=2" # Scale and add padding if we are targeting a webcam because a lot of services # expect a size of exactly 1280x720 (tested in Firefox, MS Teams, and Skype for @@ -314,11 +304,16 @@ set -e # stop if an error occurs restream_options="-h $height -w $width -b $bytes_per_pixel -f $fb_file -s $skip_offset" if "$cursor"; then - restream_options="$restream_options -c" + if [ -n "$pen_orientation_opt" ] && ssh_cmd "PATH=\"\$PATH:/opt/bin/:.\" restream --help 2>&1 | grep -q pen-orientation"; then + restream_options="$restream_options -c $pen_orientation_opt" + else + [ -n "$pen_orientation_opt" ] && echo "[reStream] Warning: restream binary on device does not support --pen-orientation; cursor will be misaligned on fw 3.27+. Reinstall restream.arm.static to fix." >&2 + restream_options="$restream_options -c" + fi fi # shellcheck disable=SC2089 -restream_rs="PATH=\"\$PATH:/opt/bin/:/home/root/.vellum/bin:.\" restream $restream_options" +restream_rs="PATH=\"\$PATH:/opt/bin/:.\" restream $restream_options" if $unsecure_connection; then listen_port=16789 ssh_cmd "$restream_rs --listen $listen_port" & diff --git a/restream.arm.static b/restream.arm.static index 1fe76b1..9a1d552 100755 Binary files a/restream.arm.static and b/restream.arm.static differ diff --git a/src/main.rs b/src/main.rs index b9f78eb..5025152 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,6 +48,22 @@ pub struct Opts { /// Memory offset #[arg(long, name = "skip", short = 's')] skip: usize, + + /// Orientation of the framebuffer content, used to draw the pen cursor at the right + /// position. Inferred from width/height when omitted (landscape if width > height). + #[arg(long, value_enum)] + pen_orientation: Option, +} + +#[derive(Clone, Copy, Debug, PartialEq, clap::ValueEnum)] +pub enum PenOrientation { + /// Landscape buffer (rM2 firmware before 3.24 with :mem:) + Landscape, + /// Upright portrait buffer (rM1, rm2fb, rM2 firmware 3.24+) + Portrait, + /// Portrait buffer rotated by 180 degrees (rM2 firmware 3.27+) + #[value(name = "portrait180")] + Portrait180, } fn main() -> Result<()> { @@ -62,6 +78,12 @@ fn main() -> Result<()> { (opts.file.to_owned(), 0) }; + let pen_orientation = opts.pen_orientation.unwrap_or(if opts.width > opts.height { + PenOrientation::Landscape + } else { + PenOrientation::Portrait + }); + let streamer = ReStreamer::init( &file, offset, @@ -69,6 +91,7 @@ fn main() -> Result<()> { opts.height, opts.bytes_per_pixel, opts.show_cursor, + pen_orientation, )?; let stdout = std::io::stdout(); @@ -148,6 +171,7 @@ pub struct ReStreamer { bytes_per_pixel: usize, show_cursor: bool, + pen_orientation: PenOrientation, input_rx: Receiver, pen_pos: Option<(usize, usize)>, drawing: bool, @@ -161,6 +185,7 @@ impl ReStreamer { height: usize, bytes_per_pixel: usize, show_cursor: bool, + pen_orientation: PenOrientation, ) -> Result { let start = offset as u64; let size = width * height * bytes_per_pixel; @@ -181,6 +206,7 @@ impl ReStreamer { height, bytes_per_pixel, show_cursor, + pen_orientation, input_rx, pen_pos: None, drawing: false, @@ -234,37 +260,48 @@ impl ReStreamer { /// 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) }; + if let (false, Some((ev_x, ev_y))) = (self.drawing, self.pen_pos) { + // The framebuffer is row-major with a stride of `width` pixels, + // both for the old landscape layout (1872x1404) and the 3.24+ + // portrait layout (1404x1872). // 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 width = self.width as isize; + let height = 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 { + + // Pen events are in display coordinates (1404x1872 portrait); + // map them to a (row, column) position in the framebuffer. + let (pen_row, pen_col) = match self.pen_orientation { + PenOrientation::Landscape => (ev_x as isize, width - ev_y as isize), + PenOrientation::Portrait => (ev_y as isize, ev_x as isize), + PenOrientation::Portrait180 => { + (height - ev_y as isize, width - ev_x as isize) + } + }; + + for (i, (coff, no)) in PEN_IMAGE.iter().enumerate() { + // each entry of PEN_IMAGE is one row of the dot + let row = pen_row + i as isize - (PEN_IMAGE.len() as isize / 2); + // row outside of canvas? + if row < 0 || row >= height { continue; } - let mut ystart = (height - y as isize) + yoff; + let mut col = pen_col + coff; let mut no = *no; // cut-off at sides - if ystart < 0 { - no += ystart; - ystart = 0; + if col < 0 { + no += col; + col = 0; } - if ystart + no > height { - no = height - ystart; + if col + no > width { + no = width - col; } if no <= 0 { continue; } // translate to buf indexes, check bounds and draw - let mut px_start = (xstart * height + ystart) * bpp; + let mut px_start = (row * width + col) * bpp; let mut px_end = px_start + no * bpp; // outside current buf? if px_end < cursor || px_start >= cursor + buf.len() as isize { @@ -277,7 +314,9 @@ impl ReStreamer { if px_end > cursor + buf.len() as isize { px_end = cursor + buf.len() as isize; } - // invert pixel (on RM2) + // invert pixels: byte-wise NOT inverts every supported pixel + // format (gray8, gray16, rgb565, bgra); players ignore the + // inverted alpha byte on bgra rawvideo // 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;