mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-04-27 07:59:59 -07:00
Add build features for multiple device types
The bin crate now has two features, one for each supported device. * The IOCTL change from #142 is compiled in conditionally. * Tp-link display is supported & tested for HW rev 3 and HW rev 5. The release tarballs now contain two rayhunter-daemon binaries, for orbic and tplink. An installer for tplink is not yet included. Co-authored-by: m0veax <m0veax@chaospott.de>
This commit is contained in:
196
bin/src/display/generic_framebuffer.rs
Normal file
196
bin/src/display/generic_framebuffer.rs
Normal file
@@ -0,0 +1,196 @@
|
||||
use image::{codecs::gif::GifDecoder, imageops::FilterType, AnimationDecoder, DynamicImage};
|
||||
use std::time::Duration;
|
||||
use std::io::Cursor;
|
||||
|
||||
use crate::config;
|
||||
use crate::display::DisplayState;
|
||||
|
||||
use log::{error, info};
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tokio_util::task::TaskTracker;
|
||||
use tokio::sync::oneshot::error::TryRecvError;
|
||||
|
||||
use std::thread::sleep;
|
||||
|
||||
use include_dir::{include_dir, Dir};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Dimensions {
|
||||
pub height: u32,
|
||||
pub width: u32,
|
||||
}
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Color {
|
||||
Red ,
|
||||
Green ,
|
||||
Blue ,
|
||||
White ,
|
||||
Black ,
|
||||
Cyan ,
|
||||
Yellow ,
|
||||
Pink,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
fn rgb(self) -> (u8, u8, u8) {
|
||||
match self {
|
||||
Color::Red => (0xff, 0, 0),
|
||||
Color::Green => (0, 0xff, 0),
|
||||
Color::Blue => (0, 0, 0xff),
|
||||
Color::White => (0xff, 0xff, 0xff),
|
||||
Color::Black => (0, 0, 0),
|
||||
Color::Cyan => (0, 0xff, 0xff),
|
||||
Color::Yellow => (0xff, 0xff, 0),
|
||||
Color::Pink => (0xfe, 0x24, 0xff),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DisplayState> for Color{
|
||||
fn from(state: DisplayState) -> Self {
|
||||
match state {
|
||||
DisplayState::Paused => Color::White,
|
||||
DisplayState::Recording => Color::Green,
|
||||
DisplayState::RecordingCBM => Color::Blue,
|
||||
DisplayState::WarningDetected => Color::Red,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GenericFramebuffer: Send + 'static {
|
||||
fn dimensions(&self) -> Dimensions;
|
||||
|
||||
fn write_buffer(
|
||||
&mut self,
|
||||
buffer: &[(u8, u8, u8)], // rgb, row-wise, left-to-right, top-to-bottom
|
||||
);
|
||||
|
||||
fn write_dynamic_image(&mut self, img: DynamicImage) {
|
||||
let dimensions = self.dimensions();
|
||||
let mut width = img.width();
|
||||
let mut height = img.height();
|
||||
let resized_img: DynamicImage;
|
||||
if height > dimensions.height ||
|
||||
width > dimensions.width {
|
||||
resized_img = img.resize( dimensions.width, dimensions.height, FilterType::CatmullRom);
|
||||
width = dimensions.width.min(resized_img.width());
|
||||
height = dimensions.height.min(resized_img.height());
|
||||
} else {
|
||||
resized_img = img;
|
||||
}
|
||||
let img_rgba8 = resized_img.as_rgba8().unwrap();
|
||||
let mut buf = Vec::new();
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
let px = img_rgba8.get_pixel(x, y);
|
||||
buf.push((px[0], px[1], px[2]));
|
||||
}
|
||||
}
|
||||
|
||||
self.write_buffer(
|
||||
&buf,
|
||||
);
|
||||
}
|
||||
|
||||
fn draw_gif(&mut self, img_buffer: &[u8]) {
|
||||
// this is dumb and i'm sure there's a better way to loop this
|
||||
let cursor = Cursor::new(img_buffer);
|
||||
let decoder = GifDecoder::new(cursor).unwrap();
|
||||
for maybe_frame in decoder.into_frames() {
|
||||
let frame = maybe_frame.unwrap();
|
||||
let (numerator, _) = frame.delay().numer_denom_ms();
|
||||
let img = DynamicImage::from(frame.into_buffer());
|
||||
self.write_dynamic_image(img);
|
||||
std::thread::sleep(Duration::from_millis(numerator as u64));
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_img(&mut self, img_buffer: &[u8]) {
|
||||
let img = image::load_from_memory(img_buffer).unwrap();
|
||||
self.write_dynamic_image(img);
|
||||
}
|
||||
|
||||
fn draw_line(&mut self, color: Color, height: u32) {
|
||||
let width = self.dimensions().width;
|
||||
let px_num = height * width;
|
||||
let mut buffer = Vec::new();
|
||||
for _ in 0..px_num {
|
||||
buffer.push(color.rgb());
|
||||
}
|
||||
|
||||
self.write_buffer(&buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_ui(
|
||||
task_tracker: &TaskTracker,
|
||||
config: &config::Config,
|
||||
mut fb: impl GenericFramebuffer,
|
||||
mut ui_shutdown_rx: oneshot::Receiver<()>,
|
||||
mut ui_update_rx: Receiver<DisplayState>
|
||||
) {
|
||||
static IMAGE_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/static/images/");
|
||||
let mut display_color: Color;
|
||||
let display_level = config.ui_level;
|
||||
if display_level == 0 {
|
||||
info!("Invisible mode, not spawning UI.");
|
||||
}
|
||||
|
||||
if config.colorblind_mode {
|
||||
display_color = Color::Blue;
|
||||
} else {
|
||||
display_color = Color::Green;
|
||||
}
|
||||
|
||||
task_tracker.spawn_blocking(move || {
|
||||
// this feels wrong, is there a more rusty way to do this?
|
||||
let mut img: Option<&[u8]> = None;
|
||||
if display_level == 2 {
|
||||
img = Some(IMAGE_DIR.get_file("orca.gif").expect("failed to read orca.gif").contents());
|
||||
} else if display_level == 3 {
|
||||
img = Some(IMAGE_DIR.get_file("eff.png").expect("failed to read eff.png").contents());
|
||||
}
|
||||
loop {
|
||||
match ui_shutdown_rx.try_recv() {
|
||||
Ok(_) => {
|
||||
info!("received UI shutdown");
|
||||
break;
|
||||
},
|
||||
Err(TryRecvError::Empty) => {},
|
||||
Err(e) => panic!("error receiving shutdown message: {e}")
|
||||
}
|
||||
match ui_update_rx.try_recv() {
|
||||
Ok(state) => {
|
||||
display_color = state.into();
|
||||
},
|
||||
Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {},
|
||||
Err(e) => error!("error receiving framebuffer update message: {e}")
|
||||
}
|
||||
|
||||
match display_level {
|
||||
2 => {
|
||||
fb.draw_gif(img.unwrap());
|
||||
},
|
||||
3 => {
|
||||
fb.draw_img(img.unwrap())
|
||||
},
|
||||
128 => {
|
||||
fb.draw_line(Color::Cyan, 128);
|
||||
fb.draw_line(Color::Pink, 102);
|
||||
fb.draw_line(Color::White, 76);
|
||||
fb.draw_line(Color::Pink, 50);
|
||||
fb.draw_line(Color::Cyan, 25);
|
||||
},
|
||||
_ => { // this branch id for ui_level 1, which is also the default if an
|
||||
// unknown value is used
|
||||
fb.draw_line(display_color, 2);
|
||||
},
|
||||
};
|
||||
sleep(Duration::from_millis(1000));
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user