Compare commits
	
		
			3 commits
		
	
	
		
			master
			...
			some-buffe
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						c9d179c33f | ||
| 
							 | 
						57880ef70a | ||
| 
							 | 
						970bb5187a | 
					 6 changed files with 196 additions and 602 deletions
				
			
		
							
								
								
									
										33
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										33
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -73,7 +73,6 @@ dependencies = [
 | 
			
		|||
 "libc",
 | 
			
		||||
 "nix",
 | 
			
		||||
 "png",
 | 
			
		||||
 "socket2",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
| 
						 | 
				
			
			@ -132,35 +131,3 @@ name = "simd-adler32"
 | 
			
		|||
version = "0.3.7"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "socket2"
 | 
			
		||||
version = "0.4.10"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "libc",
 | 
			
		||||
 "winapi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[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-x86_64-pc-windows-gnu"
 | 
			
		||||
version = "0.4.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,4 +7,3 @@ edition = "2024"
 | 
			
		|||
png = "0.17"
 | 
			
		||||
libc = "0.2"
 | 
			
		||||
nix = { version = "0.29", features = ["socket", "uio"] }
 | 
			
		||||
socket2 = "0.4.7"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										132
									
								
								src/buffer.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/buffer.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,132 @@
 | 
			
		|||
use std::sync::{Arc, Condvar, Mutex};
 | 
			
		||||
use std::thread;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
// Using MaybeUninit is the idiomatic way to handle a buffer
 | 
			
		||||
// of potentially uninitialized items without requiring a Default trait.
 | 
			
		||||
use std::mem::MaybeUninit;
 | 
			
		||||
 | 
			
		||||
/// A fixed-capacity, overwriting ring buffer.
 | 
			
		||||
pub struct RingBuffer<T, const N: usize> {
 | 
			
		||||
    buffer: [MaybeUninit<T>; N],
 | 
			
		||||
    head: usize,
 | 
			
		||||
    tail: usize,
 | 
			
		||||
    size: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T, const N: usize> RingBuffer<T, N> {
 | 
			
		||||
    /// Creates a new, empty RingBuffer.
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            // This is a safe way to create an array of uninitialized data.
 | 
			
		||||
            buffer: unsafe { MaybeUninit::uninit().assume_init() },
 | 
			
		||||
            head: 0,
 | 
			
		||||
            tail: 0,
 | 
			
		||||
            size: 0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Checks if the buffer is empty.
 | 
			
		||||
    pub fn is_empty(&self) -> bool {
 | 
			
		||||
        self.size == 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Pushes an item into the buffer.
 | 
			
		||||
    /// If the buffer is full, the oldest item is overwritten.
 | 
			
		||||
    pub fn push(&mut self, item: T) {
 | 
			
		||||
        // Write the item to the tail position.
 | 
			
		||||
        // This is safe because we manage initialization with `size`.
 | 
			
		||||
        self.buffer[self.tail].write(item);
 | 
			
		||||
        self.tail = (self.tail + 1) % N;
 | 
			
		||||
 | 
			
		||||
        if self.size < N {
 | 
			
		||||
            // Buffer is not full, just increment size.
 | 
			
		||||
            self.size += 1;
 | 
			
		||||
        } else {
 | 
			
		||||
            // Buffer was full. The push overwrote the oldest item.
 | 
			
		||||
            // The head must also advance to the new oldest item.
 | 
			
		||||
            self.head = (self.head + 1) % N;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Pops an item from the buffer.
 | 
			
		||||
    /// Returns None if the buffer is empty.
 | 
			
		||||
    pub fn pop(&mut self) -> Option<T> {
 | 
			
		||||
        if self.is_empty() {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Read the item from the head, leaving that slot uninitialized.
 | 
			
		||||
        // This is safe because is_empty() check ensures `head` points to valid data.
 | 
			
		||||
        let item = unsafe { self.buffer[self.head].assume_init_read() };
 | 
			
		||||
        self.head = (self.head + 1) % N;
 | 
			
		||||
        self.size -= 1;
 | 
			
		||||
        Some(item)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    const CAPACITY: usize = 5;
 | 
			
		||||
    println!("Ring Buffer Capacity: {}", CAPACITY);
 | 
			
		||||
    println!("Producer will produce 15 items, so overwrites are expected.");
 | 
			
		||||
 | 
			
		||||
    // The state is shared between threads using an Arc (Atomic Reference Counter).
 | 
			
		||||
    // The Mutex ensures exclusive access, and the Condvar allows threads to wait efficiently.
 | 
			
		||||
    let pair = Arc::new((
 | 
			
		||||
        Mutex::new(RingBuffer::<i32, CAPACITY>::new()),
 | 
			
		||||
        Condvar::new(),
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    // --- Producer Thread ---
 | 
			
		||||
    let producer_pair = Arc::clone(&pair);
 | 
			
		||||
    let producer_handle = thread::spawn(move || {
 | 
			
		||||
        for i in 0..15 {
 | 
			
		||||
            // Lock the mutex to get exclusive access to the buffer.
 | 
			
		||||
            let (lock, cvar) = &*producer_pair;
 | 
			
		||||
            let mut buffer = lock.lock().unwrap();
 | 
			
		||||
 | 
			
		||||
            buffer.push(i);
 | 
			
		||||
            println!("➡️ Produced: {}", i);
 | 
			
		||||
 | 
			
		||||
            // This is crucial: after adding an item, we notify one waiting thread.
 | 
			
		||||
            cvar.notify_one();
 | 
			
		||||
            
 | 
			
		||||
            // We don't need the lock anymore, so we can drop it explicitly
 | 
			
		||||
            // before sleeping to allow the consumer to work.
 | 
			
		||||
            drop(buffer);
 | 
			
		||||
            thread::sleep(Duration::from_millis(100)); // Producer is fast
 | 
			
		||||
        }
 | 
			
		||||
        println!("✅ Producer finished.");
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // --- Consumer Thread ---
 | 
			
		||||
    let consumer_pair = Arc::clone(&pair);
 | 
			
		||||
    let consumer_handle = thread::spawn(move || {
 | 
			
		||||
        let mut items_processed = 0;
 | 
			
		||||
        while items_processed < 15 {
 | 
			
		||||
            let (lock, cvar) = &*consumer_pair;
 | 
			
		||||
            let mut buffer = lock.lock().unwrap();
 | 
			
		||||
 | 
			
		||||
            // Use a while loop to handle spurious wakeups.
 | 
			
		||||
            // The thread will sleep until the buffer is no longer empty.
 | 
			
		||||
            while buffer.is_empty() {
 | 
			
		||||
                // `cvar.wait` atomically unlocks the mutex and waits.
 | 
			
		||||
                // When woken, it re-locks the mutex before returning.
 | 
			
		||||
                buffer = cvar.wait(buffer).unwrap();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // At this point, the buffer is not empty.
 | 
			
		||||
            if let Some(item) = buffer.pop() {
 | 
			
		||||
                println!("   Consumed: {}", item);
 | 
			
		||||
                items_processed += 1;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            drop(buffer);
 | 
			
		||||
            thread::sleep(Duration::from_millis(300)); // Consumer is slow
 | 
			
		||||
        }
 | 
			
		||||
        println!("✅ Consumer finished.");
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    producer_handle.join().unwrap();
 | 
			
		||||
    consumer_handle.join().unwrap();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										69
									
								
								src/color.rs
									
										
									
									
									
								
							
							
						
						
									
										69
									
								
								src/color.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,69 +0,0 @@
 | 
			
		|||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
/// Represents a color in the RGB (Red, Green, Blue) model.
 | 
			
		||||
/// Values range from 0 to 255.
 | 
			
		||||
pub struct Rgb {
 | 
			
		||||
    pub r: u8,
 | 
			
		||||
    pub g: u8,
 | 
			
		||||
    pub b: u8,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a color in the HSV (Hue, Saturation, Value) model.
 | 
			
		||||
/// - `h` (hue): 0-359 degrees
 | 
			
		||||
/// - `s` (saturation): 0.0-1.0
 | 
			
		||||
/// - `v` (value/brightness): 0.0-1.0
 | 
			
		||||
pub struct Hsv {
 | 
			
		||||
    pub h: u16,
 | 
			
		||||
    pub s: f32,
 | 
			
		||||
    pub v: f32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implement the `Debug` trait for Rgb to allow pretty-printing.
 | 
			
		||||
impl fmt::Debug for Rgb {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(f, "Rgb({}, {}, {})", self.r, self.g, self.b)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Provides the conversion logic from HSV to RGB.
 | 
			
		||||
/// This uses the standard mathematical formula for the conversion.
 | 
			
		||||
impl From<Hsv> for Rgb {
 | 
			
		||||
    fn from(hsv: Hsv) -> Self {
 | 
			
		||||
        // Ensure saturation and value are within the valid range [0.0, 1.0]
 | 
			
		||||
        let s = hsv.s.clamp(0.0, 1.0);
 | 
			
		||||
        let v = hsv.v.clamp(0.0, 1.0);
 | 
			
		||||
        
 | 
			
		||||
        // When saturation is 0, the color is a shade of gray.
 | 
			
		||||
        if s == 0.0 {
 | 
			
		||||
            let val = (v * 255.0) as u8;
 | 
			
		||||
            return Rgb { r: val, g: val, b: val };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // The hue is treated as a sector on a color wheel.
 | 
			
		||||
        let h = hsv.h as f32 / 60.0; // Sector 0-5
 | 
			
		||||
        let i = h.floor() as i32;
 | 
			
		||||
        let f = h - i as f32; // Fractional part of h
 | 
			
		||||
 | 
			
		||||
        let p = v * (1.0 - s);
 | 
			
		||||
        let q = v * (1.0 - f * s);
 | 
			
		||||
        let t = v * (1.0 - (1.0 - f) * s);
 | 
			
		||||
 | 
			
		||||
        // Determine the RGB values based on the hue sector.
 | 
			
		||||
        let (r, g, b) = match i {
 | 
			
		||||
            0 => (v, t, p),
 | 
			
		||||
            1 => (q, v, p),
 | 
			
		||||
            2 => (p, v, t),
 | 
			
		||||
            3 => (p, q, v),
 | 
			
		||||
            4 => (t, p, v),
 | 
			
		||||
            _ => (v, p, q), // Default case for sector 5
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Convert the float RGB values (0.0-1.0) to u8 values (0-255).
 | 
			
		||||
        Rgb {
 | 
			
		||||
            r: (r * 255.0).round() as u8,
 | 
			
		||||
            g: (g * 255.0).round() as u8,
 | 
			
		||||
            b: (b * 255.0).round() as u8,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										272
									
								
								src/main-new.rs
									
										
									
									
									
								
							
							
						
						
									
										272
									
								
								src/main-new.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,272 +0,0 @@
 | 
			
		|||
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
use std::io::BufReader;
 | 
			
		||||
use std::net::{ToSocketAddrs, UdpSocket};
 | 
			
		||||
use std::os::unix::io::{AsRawFd, RawFd};
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
// Nix crate for sendmmsg
 | 
			
		||||
use nix::sys::socket::{sendmmsg, MsgFlags, SendMmsgData};
 | 
			
		||||
use nix::sys::uio::IoVec;
 | 
			
		||||
 | 
			
		||||
// Constants from the C code
 | 
			
		||||
const QUEUE_LEN: usize = 1000;
 | 
			
		||||
const MSG_PAYLOAD_SIZE: usize = 7 * 160;
 | 
			
		||||
const MSGSIZE: usize = 2 + MSG_PAYLOAD_SIZE;
 | 
			
		||||
 | 
			
		||||
const DISPLAY_HOST: &str = "100.65.0.2";
 | 
			
		||||
const DISPLAY_PORT: u16 = 5005;
 | 
			
		||||
const DISPLAY_WIDTH: i32 = 1920;
 | 
			
		||||
const DISPLAY_HEIGHT: i32 = 1080;
 | 
			
		||||
 | 
			
		||||
/// Represents the data decoded from a PNG file.
 | 
			
		||||
struct PngData {
 | 
			
		||||
    width: u32,
 | 
			
		||||
    height: u32,
 | 
			
		||||
    pixels: Vec<u8>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PngData {
 | 
			
		||||
    /// Loads and decodes a PNG image from the given path.
 | 
			
		||||
    fn open(path: &str) -> Result<Self, png::DecodingError> {
 | 
			
		||||
        let file = File::open(path).expect("Failed to open PNG file");
 | 
			
		||||
        let decoder = png::Decoder::new(BufReader::new(file));
 | 
			
		||||
        let mut reader = decoder.read_info()?;
 | 
			
		||||
        let mut buf = vec![0; reader.output_buffer_size()];
 | 
			
		||||
        let info = reader.next_frame(&mut buf)?;
 | 
			
		||||
 | 
			
		||||
        Ok(PngData {
 | 
			
		||||
            width: info.width,
 | 
			
		||||
            height: info.height,
 | 
			
		||||
            pixels: buf,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a bouncing image on the screen.
 | 
			
		||||
struct BouncingImage {
 | 
			
		||||
    img: PngData,
 | 
			
		||||
    x: i32,
 | 
			
		||||
    y: i32,
 | 
			
		||||
    x1: i32,
 | 
			
		||||
    y1: i32,
 | 
			
		||||
    x2: i32,
 | 
			
		||||
    y2: i32,
 | 
			
		||||
    move_x: i32,
 | 
			
		||||
    move_y: i32,
 | 
			
		||||
    rate: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BouncingImage {
 | 
			
		||||
    /// Initializes a new BouncingImage.
 | 
			
		||||
    fn new(img_file: &str, move_x: i32, move_y: i32, rate: u32, start_x: i32, start_y: i32) -> Self {
 | 
			
		||||
        let img = PngData::open(img_file).expect("Could not load image");
 | 
			
		||||
        let mut bb = BouncingImage {
 | 
			
		||||
            x2: DISPLAY_WIDTH - img.width as i32,
 | 
			
		||||
            y2: DISPLAY_HEIGHT - img.height as i32,
 | 
			
		||||
            img,
 | 
			
		||||
            x: start_x,
 | 
			
		||||
            y: start_y,
 | 
			
		||||
            x1: 0,
 | 
			
		||||
            y1: 0,
 | 
			
		||||
            move_x,
 | 
			
		||||
            move_y,
 | 
			
		||||
            rate,
 | 
			
		||||
        };
 | 
			
		||||
        if bb.x == -1 {
 | 
			
		||||
            bb.x = (bb.x1 + bb.x2) / 2;
 | 
			
		||||
        }
 | 
			
		||||
        if bb.y == -1 {
 | 
			
		||||
            bb.y = (bb.y1 + bb.y2) / 2;
 | 
			
		||||
        }
 | 
			
		||||
        bb
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Draws the image and updates its position.
 | 
			
		||||
    fn draw_and_move(&mut self, display: &mut Display) {
 | 
			
		||||
        display.draw_png(&self.img, self.x, self.y);
 | 
			
		||||
 | 
			
		||||
        self.x += self.move_x;
 | 
			
		||||
        self.y += self.move_y;
 | 
			
		||||
 | 
			
		||||
        if self.x < self.x1 || self.x > self.x2 {
 | 
			
		||||
            self.move_x *= -1;
 | 
			
		||||
        }
 | 
			
		||||
        if self.y < self.y1 || self.y > self.y2 {
 | 
			
		||||
            self.move_y *= -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Manages the connection and data sent to the display.
 | 
			
		||||
struct Display {
 | 
			
		||||
    fd: RawFd,
 | 
			
		||||
    bufs: Vec<[u8; MSGSIZE]>,
 | 
			
		||||
    lens: Vec<usize>, // Stores the actual length of data in each buffer
 | 
			
		||||
    next_buf: usize,   // The next buffer in the queue to be filled
 | 
			
		||||
    send_next: usize, // The next buffer in the queue to be sent
 | 
			
		||||
    pos_in_buf: usize,
 | 
			
		||||
    // Keep the socket alive to keep the file descriptor valid
 | 
			
		||||
    _socket: UdpSocket,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display {
 | 
			
		||||
    /// Creates a new Display and connects to the specified host and port.
 | 
			
		||||
    fn new(host: &str, port: u16) -> Self {
 | 
			
		||||
        let remote_addr = (host, port)
 | 
			
		||||
            .to_socket_addrs()
 | 
			
		||||
            .expect("Invalid remote address")
 | 
			
		||||
            .next()
 | 
			
		||||
            .expect("Could not resolve host");
 | 
			
		||||
 | 
			
		||||
        let socket = UdpSocket::bind("0.0.0.0:0").expect("Could not bind to local port");
 | 
			
		||||
        socket.connect(remote_addr).expect("Could not connect to remote");
 | 
			
		||||
        let fd = socket.as_raw_fd();
 | 
			
		||||
 | 
			
		||||
        let mut bufs = vec![[0; MSGSIZE]; QUEUE_LEN];
 | 
			
		||||
        for buf in bufs.iter_mut() {
 | 
			
		||||
            buf[0] = 0x00;
 | 
			
		||||
            buf[1] = 0x01;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Display {
 | 
			
		||||
            fd,
 | 
			
		||||
            bufs,
 | 
			
		||||
            lens: vec![0; QUEUE_LEN],
 | 
			
		||||
            next_buf: 0,
 | 
			
		||||
            send_next: 0,
 | 
			
		||||
            pos_in_buf: 0,
 | 
			
		||||
            _socket: socket,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Marks the current buffer as ready to be sent and moves to the next one.
 | 
			
		||||
    fn mark_buffer_ready(&mut self) {
 | 
			
		||||
        if self.pos_in_buf > 0 {
 | 
			
		||||
            self.lens[self.next_buf] = 2 + self.pos_in_buf * 7;
 | 
			
		||||
            self.next_buf = (self.next_buf + 1) % QUEUE_LEN;
 | 
			
		||||
            self.pos_in_buf = 0;
 | 
			
		||||
 | 
			
		||||
            // If we've wrapped around and caught up to the send queue,
 | 
			
		||||
            // we must flush to avoid overwriting data that hasn't been sent.
 | 
			
		||||
            if self.next_buf == self.send_next {
 | 
			
		||||
                eprintln!("Warning: Buffer queue full. Forcing a flush.");
 | 
			
		||||
                self.flush_all_pending();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sends all queued packets using the efficient `sendmmsg` syscall.
 | 
			
		||||
    fn flush_all_pending(&mut self) {
 | 
			
		||||
        // First, ensure the current, partially-filled buffer is marked as ready.
 | 
			
		||||
        self.mark_buffer_ready();
 | 
			
		||||
 | 
			
		||||
        if self.send_next == self.next_buf {
 | 
			
		||||
            return; // Nothing to send.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // We build a temporary list of message headers to pass to sendmmsg.
 | 
			
		||||
        // This is the cleanest way to handle the circular buffer.
 | 
			
		||||
        let mut iovecs_storage = Vec::new();
 | 
			
		||||
        let mut messages_to_send = Vec::new();
 | 
			
		||||
 | 
			
		||||
        let mut current_idx = self.send_next;
 | 
			
		||||
        while current_idx != self.next_buf {
 | 
			
		||||
            let data_slice = &self.bufs[current_idx][..self.lens[current_idx]];
 | 
			
		||||
            iovecs_storage.push(IoVec::from_slice(data_slice));
 | 
			
		||||
            current_idx = (current_idx + 1) % QUEUE_LEN;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Since we used `connect()`, the kernel knows the destination address,
 | 
			
		||||
        // so we can pass `None` for the address in `SendMmsgData`.
 | 
			
		||||
        for iov in &iovecs_storage {
 | 
			
		||||
            messages_to_send.push(SendMmsgData {
 | 
			
		||||
                iov: &[*iov],
 | 
			
		||||
                addr: None,
 | 
			
		||||
                cmsgs: &[],
 | 
			
		||||
                _phantom: std::marker::PhantomData,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if messages_to_send.is_empty() {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Perform the `sendmmsg` syscall
 | 
			
		||||
        match sendmmsg(self.fd, &messages_to_send, MsgFlags::empty()) {
 | 
			
		||||
            Ok(num_sent) => {
 | 
			
		||||
                // Advance the send queue by the number of packets actually sent.
 | 
			
		||||
                self.send_next = (self.send_next + num_sent) % QUEUE_LEN;
 | 
			
		||||
            }
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                // Non-blocking sockets might return an error indicating to try again.
 | 
			
		||||
                // For this example, we'll just log other errors.
 | 
			
		||||
                if e != nix::errno::Errno::EAGAIN && e != nix::errno::Errno::EWOULDBLOCK {
 | 
			
		||||
                    eprintln!("Failed to send messages with sendmmsg: {}", e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets a pixel color at a specific coordinate.
 | 
			
		||||
    fn set_pixel(&mut self, x: u16, y: u16, r: u8, g: u8, b: u8) {
 | 
			
		||||
        let offset = 2 + self.pos_in_buf * 7;
 | 
			
		||||
        let buf = &mut self.bufs[self.next_buf][offset..offset + 7];
 | 
			
		||||
        buf[0] = x as u8;
 | 
			
		||||
        buf[1] = (x >> 8) as u8;
 | 
			
		||||
        buf[2] = y as u8;
 | 
			
		||||
        buf[3] = (y >> 8) as u8;
 | 
			
		||||
        buf[4] = r;
 | 
			
		||||
        buf[5] = g;
 | 
			
		||||
        buf[6] = b;
 | 
			
		||||
 | 
			
		||||
        self.pos_in_buf += 1;
 | 
			
		||||
        if self.pos_in_buf == 160 {
 | 
			
		||||
            self.mark_buffer_ready();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Draws a PNG image at the given coordinates.
 | 
			
		||||
    fn draw_png(&mut self, png: &PngData, x: i32, y: i32) {
 | 
			
		||||
        for sy in 0..png.height {
 | 
			
		||||
            for sx in 0..png.width {
 | 
			
		||||
                let index = (sy * png.width + sx) as usize * 4;
 | 
			
		||||
                let rgba = &png.pixels[index..index + 4];
 | 
			
		||||
                if rgba[3] > 0 { // Check alpha channel
 | 
			
		||||
                    self.set_pixel((x + sx as i32) as u16, (y + sy as i32) as u16, rgba[0], rgba[1], rgba[2]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    let mut images = vec![
 | 
			
		||||
        BouncingImage::new("images/unicorn_cc.png", 13, -10, 1, -1, -1),
 | 
			
		||||
        BouncingImage::new("images/windows_logo.png", -8, 3, 2, -1, -1),
 | 
			
		||||
        BouncingImage::new("images/spade.png", 32, -12, 1, 0, 0),
 | 
			
		||||
        BouncingImage::new("images/dvdvideo.png", 20, 6, 5, 1000, 800),
 | 
			
		||||
        BouncingImage::new("images/hackaday.png", 40, 18, 3, 500, 800),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    let mut display = Display::new(DISPLAY_HOST, DISPLAY_PORT);
 | 
			
		||||
    let mut frame_counter: u32 = 0;
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        for bb in images.iter_mut() {
 | 
			
		||||
            if bb.rate > 0 && frame_counter % bb.rate != 0 {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            bb.draw_and_move(&mut display);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Send all queued packets for this frame in a single batch.
 | 
			
		||||
        display.flush_all_pending();
 | 
			
		||||
 | 
			
		||||
        frame_counter += 1;
 | 
			
		||||
        
 | 
			
		||||
        // A small delay to control the frame rate (approx 60 FPS).
 | 
			
		||||
        std::thread::sleep(Duration::from_millis(16));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										291
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										291
									
								
								src/main.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,15 +1,15 @@
 | 
			
		|||
mod buffer;
 | 
			
		||||
use crate::buffer::{RingBuffer};
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
use std::io::BufReader;
 | 
			
		||||
use std::net::{ToSocketAddrs, UdpSocket};
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use std::sync::{Arc, Condvar, Mutex};
 | 
			
		||||
use std::thread;
 | 
			
		||||
use std::sync::{Mutex,Arc};
 | 
			
		||||
use socket2::{Domain, Socket, Type};
 | 
			
		||||
use std::net::{Ipv4Addr, SocketAddr};
 | 
			
		||||
mod color;
 | 
			
		||||
 | 
			
		||||
// Constants from the C code
 | 
			
		||||
const QUEUE_LEN: usize = 1000;
 | 
			
		||||
const MSG_PAYLOAD_SIZE: usize = 7 * 211;
 | 
			
		||||
const MSG_PAYLOAD_SIZE: usize = 7 * 160;
 | 
			
		||||
const MSGSIZE: usize = 2 + MSG_PAYLOAD_SIZE;
 | 
			
		||||
 | 
			
		||||
const DISPLAY_HOST: &str = "100.65.0.2";
 | 
			
		||||
| 
						 | 
				
			
			@ -55,12 +55,6 @@ struct BouncingImage {
 | 
			
		|||
    rate: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
trait Drawable {
 | 
			
		||||
    // Associated function signature; `Self` refers to the implementor type.
 | 
			
		||||
    fn rate(&self) -> u32;
 | 
			
		||||
    fn draw_and_move(&mut self, display: &mut Display,tick:u32);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
impl BouncingImage {
 | 
			
		||||
    /// Initializes a new BouncingImage.
 | 
			
		||||
    fn new(img_file: &str, move_x: i32, move_y: i32, rate: u32, start_x: i32, start_y: i32) -> Self {
 | 
			
		||||
| 
						 | 
				
			
			@ -86,30 +80,9 @@ impl BouncingImage {
 | 
			
		|||
        bb
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Draws a PNG image at the given coordinates.
 | 
			
		||||
    fn draw_png(&mut self, display: &mut Display, x: i32, y: i32) {
 | 
			
		||||
        
 | 
			
		||||
        for sy in 0..self.img.height {
 | 
			
		||||
            for sx in 0..self.img.width {
 | 
			
		||||
                let index = (sy * self.img.width + sx) as usize * 4;
 | 
			
		||||
                let rgba = &self.img.pixels[index..index + 4];
 | 
			
		||||
                if rgba[3] > 0 { // Check alpha channel
 | 
			
		||||
                    //display.set_pixel((x + sx as i32) as u16, (y + sy as i32) as u16, rgba[0], rgba[1], rgba[2]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }    
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
impl Drawable for BouncingImage {
 | 
			
		||||
    fn rate(&self) -> u32 {
 | 
			
		||||
        return self.rate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Draws the image and updates its position.
 | 
			
		||||
    fn draw_and_move(&mut self, display: &mut Display,_: u32) {
 | 
			
		||||
        self.draw_png(display, self.x, self.y);
 | 
			
		||||
    fn draw_and_move(&mut self, display: &mut Display) {
 | 
			
		||||
        display.draw_png(&self.img, self.x, self.y);
 | 
			
		||||
 | 
			
		||||
        self.x += self.move_x;
 | 
			
		||||
        self.y += self.move_y;
 | 
			
		||||
| 
						 | 
				
			
			@ -123,91 +96,11 @@ impl Drawable for BouncingImage {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Circle {
 | 
			
		||||
   x: Arc<Mutex<u32>>,
 | 
			
		||||
   y: Arc<Mutex<u32>>,
 | 
			
		||||
}
 | 
			
		||||
impl Circle {
 | 
			
		||||
 | 
			
		||||
    fn new(x:Arc<Mutex<u32>>, y: Arc<Mutex<u32>>) -> Self {
 | 
			
		||||
        Circle {
 | 
			
		||||
         x,
 | 
			
		||||
         y
 | 
			
		||||
        }
 | 
			
		||||
    }    
 | 
			
		||||
 | 
			
		||||
    /// This exploits the eight-way symmetry of a circle.
 | 
			
		||||
    fn draw_circle_octants(&mut self, display: &mut Display, cx: i32, cy: i32, x: i32, y: i32, r: u8, g: u8, b: u8) {
 | 
			
		||||
        display.set_pixel(cx + x, cy + y, r, g, b);
 | 
			
		||||
        display.set_pixel(cx - x, cy + y, r, g, b);
 | 
			
		||||
        display.set_pixel(cx + x, cy - y, r, g, b);
 | 
			
		||||
        display.set_pixel(cx - x, cy - y, r, g, b);
 | 
			
		||||
        display.set_pixel(cx + y, cy + x, r, g, b);
 | 
			
		||||
        display.set_pixel(cx - y, cy + x, r, g, b);
 | 
			
		||||
        display.set_pixel(cx + y, cy - x, r, g, b);
 | 
			
		||||
        display.set_pixel(cx - y, cy - x, r, g, b);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Draws a circle using the Midpoint Circle Algorithm.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Arguments
 | 
			
		||||
    /// * `center_x`: The x-coordinate of the circle's center.
 | 
			
		||||
    /// * `center_y`: The y-coordinate of the circle's center.
 | 
			
		||||
    /// * `radius`: The radius of the circle. Must be non-negative.
 | 
			
		||||
    /// * `r`, `g`, `b`: The RGB color components for the circle.
 | 
			
		||||
    pub fn draw_circle(&mut self, display: &mut Display, center_x: u32, center_y: u32, radius: u32, r: u8, g: u8, b: u8) {
 | 
			
		||||
        if radius < 0 {
 | 
			
		||||
            // Or return an error: Err("Radius cannot be negative".into())
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let radius_i32:i32 = radius.try_into().unwrap();
 | 
			
		||||
        let mut x:i32 = 0;
 | 
			
		||||
        let mut y:i32 = radius_i32;
 | 
			
		||||
        // Initial decision parameter
 | 
			
		||||
        let mut d:i32 = 3 - 2 * radius_i32;
 | 
			
		||||
 | 
			
		||||
        // Iterate through the first octant and draw points in all 8 octants
 | 
			
		||||
        while y >= x {
 | 
			
		||||
            self.draw_circle_octants(display,center_x.try_into().unwrap(), center_y.try_into().unwrap(), x, y, r, g, b);
 | 
			
		||||
 | 
			
		||||
            x += 1;
 | 
			
		||||
 | 
			
		||||
            // Update the decision parameter
 | 
			
		||||
            if d > 0 {
 | 
			
		||||
                y -= 1;
 | 
			
		||||
                d = d + 4 * (x - y) + 10;
 | 
			
		||||
            } else {
 | 
			
		||||
                d = d + 4 * x + 6;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
impl Drawable for Circle {
 | 
			
		||||
 | 
			
		||||
    fn rate(&self) -> u32 {
 | 
			
		||||
        1
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
       /// Helper method to draw the 8 symmetric points for a given (x, y) offset.
 | 
			
		||||
    fn draw_and_move(&mut self, display: &mut Display,tick:u32) {
 | 
			
		||||
    let hsv_color = color::Hsv {
 | 
			
		||||
            h: ((tick/200)%360).try_into().unwrap(),
 | 
			
		||||
            s: 1.0,
 | 
			
		||||
            v: 1.0,
 | 
			
		||||
    };
 | 
			
		||||
    let rgb: color::Rgb = hsv_color.into();
 | 
			
		||||
    let draw_y =*self.x.lock().unwrap();
 | 
			
		||||
    let draw_x = *self.y.lock().unwrap();
 | 
			
		||||
    let radius = (tick/30) % (300/2);
 | 
			
		||||
    self.draw_circle(display,draw_y,draw_x,radius.try_into().unwrap(),rgb.r,rgb.g,rgb.b);
 | 
			
		||||
    }   
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Manages the connection and data sent to the display.
 | 
			
		||||
struct Display {
 | 
			
		||||
    socket: UdpSocket,
 | 
			
		||||
    bufs: Vec<[u8; MSGSIZE]>,
 | 
			
		||||
    pair: Arc<(Mutex<RingBuffer::<i32, QUEUE_LEN>>,Condvar)>,
 | 
			
		||||
    next_buf: usize,
 | 
			
		||||
    pos_in_buf: usize,
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -229,10 +122,14 @@ impl Display {
 | 
			
		|||
            buf[0] = 0x00;
 | 
			
		||||
            buf[1] = 0x01;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    let pair = Arc::new((
 | 
			
		||||
        Mutex::new(RingBuffer::<i32, QUEUE_LEN>::new()),
 | 
			
		||||
        Condvar::new(),
 | 
			
		||||
    ));
 | 
			
		||||
        Display {
 | 
			
		||||
            socket,
 | 
			
		||||
            bufs,
 | 
			
		||||
            pair,
 | 
			
		||||
            next_buf: 0,
 | 
			
		||||
            pos_in_buf: 0,
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -250,147 +147,87 @@ impl Display {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets a pixel color at a specific coordinate.
 | 
			
		||||
    fn set_pixel(&mut self, x: i32, y: i32, r: u8, g: u8, b: u8) {
 | 
			
		||||
        if let (Ok(output_x),Ok(output_y)) = (u16::try_from(x), u16::try_from(y)) {
 | 
			
		||||
         let offset = 2 + self.pos_in_buf * 7;
 | 
			
		||||
    fn set_pixel(&mut self, x: u16, y: u16, r: u8, g: u8, b: u8) {
 | 
			
		||||
        let offset = 2 + self.pos_in_buf * 7;
 | 
			
		||||
        let buf = &mut self.bufs[self.next_buf][offset..offset + 7];
 | 
			
		||||
        buf[0] = output_x as u8;
 | 
			
		||||
        buf[1] = (output_x >> 8) as u8;
 | 
			
		||||
        buf[2] = output_y as u8;
 | 
			
		||||
        buf[3] = (output_y >> 8) as u8;
 | 
			
		||||
        buf[0] = x as u8;
 | 
			
		||||
        buf[1] = (x >> 8) as u8;
 | 
			
		||||
        buf[2] = y as u8;
 | 
			
		||||
        buf[3] = (y >> 8) as u8;
 | 
			
		||||
        buf[4] = r;
 | 
			
		||||
        buf[5] = g;
 | 
			
		||||
        buf[6] = b;
 | 
			
		||||
 | 
			
		||||
        self.pos_in_buf += 1;
 | 
			
		||||
        if self.pos_in_buf == 211 {
 | 
			
		||||
        if self.pos_in_buf == 160 {
 | 
			
		||||
            self.flush_frame();
 | 
			
		||||
        }               
 | 
			
		||||
            }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Clears the entire screen to black.
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    fn blank_screen(&mut self) {
 | 
			
		||||
        for x in 0..DISPLAY_WIDTH {
 | 
			
		||||
            for y in 0..DISPLAY_HEIGHT {
 | 
			
		||||
                //self.set_pixel(x as u16, y as u16, 0, 0, 0);
 | 
			
		||||
    
 | 
			
		||||
    /// Draws a PNG image at the given coordinates.
 | 
			
		||||
    fn draw_png(&mut self, png: &PngData, x: i32, y: i32) {
 | 
			
		||||
        for sy in 0..png.height {
 | 
			
		||||
            for sx in 0..png.width {
 | 
			
		||||
                let index = (sy * png.width + sx) as usize * 4;
 | 
			
		||||
                let rgba = &png.pixels[index..index + 4];
 | 
			
		||||
                if rgba[3] > 0 { // Check alpha channel
 | 
			
		||||
                    self.set_pixel((x + sx as i32) as u16, (y + sy as i32) as u16, rgba[0], rgba[1], rgba[2]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        self.flush_frame();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    fn send_thread(&mut self) {
 | 
			
		||||
        let consumer_pair = Arc::clone(&self.pair);
 | 
			
		||||
        let consumer_handle = thread::spawn(move || {
 | 
			
		||||
        let mut items_processed = 0;
 | 
			
		||||
        loop {
 | 
			
		||||
            let (lock, cvar) = &*consumer_pair;
 | 
			
		||||
            let mut buffer = lock.lock().unwrap();
 | 
			
		||||
 | 
			
		||||
            // Use a while loop to handle spurious wakeups.
 | 
			
		||||
            // The thread will sleep until the buffer is no longer empty.
 | 
			
		||||
            while buffer.is_empty() {
 | 
			
		||||
                // `cvar.wait` atomically unlocks the mutex and waits.
 | 
			
		||||
                // When woken, it re-locks the mutex before returning.
 | 
			
		||||
                buffer = cvar.wait(buffer).unwrap();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
/// Unpacks a 4-byte slice into two u16 values (little-endian).
 | 
			
		||||
fn unpack_coordinates(buffer: &[u8]) -> Option<(u16, u16)> {
 | 
			
		||||
    if buffer.len() != 4 {
 | 
			
		||||
        return None;
 | 
			
		||||
            // At this point, the buffer is not empty.
 | 
			
		||||
            if let Some(item) = buffer.pop() {
 | 
			
		||||
                println!("   Consumed: {}", item);
 | 
			
		||||
                items_processed += 1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            drop(buffer);
 | 
			
		||||
            thread::sleep(Duration::from_millis(300)); // Consumer is slow
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
    // Try to convert the first 2 bytes to a u16 for x.
 | 
			
		||||
    let x_bytes: [u8; 2] = buffer[0..2].try_into().ok()?;
 | 
			
		||||
    // Try to convert the next 2 bytes to a u16 for y.
 | 
			
		||||
    let y_bytes: [u8; 2] = buffer[2..4].try_into().ok()?;
 | 
			
		||||
 | 
			
		||||
    // Reconstruct the u16 values from their little-endian byte representation.
 | 
			
		||||
    let x = u16::from_le_bytes(x_bytes);
 | 
			
		||||
    let y = u16::from_le_bytes(y_bytes);
 | 
			
		||||
 | 
			
		||||
    Some((x, y))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    let x:Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
 | 
			
		||||
    let x_thread = x.clone();
 | 
			
		||||
    
 | 
			
		||||
    let y:Arc<Mutex<u32>>= Arc::new(Mutex::new(0));
 | 
			
		||||
    let y_thread = y.clone();
 | 
			
		||||
 | 
			
		||||
    let circle = Box::new(Circle::new(x,y));
 | 
			
		||||
    let mut images:Vec<Box<dyn Drawable>> = vec![
 | 
			
		||||
//        Box::new(BouncingImage::new("images/unicorn_cc.png", 13, -10, 1, -1, -1)),
 | 
			
		||||
//        Box::new(BouncingImage::new("images/windows_logo.png", -8, 3, 2, -1, -1)),
 | 
			
		||||
//        Box::new(BouncingImage::new("images/spade.png", 32, -12, 1, 0, 0)),
 | 
			
		||||
//        Box::new(BouncingImage::new("images/dvdvideo.png", 20, 6, 5, 1000, 800)),
 | 
			
		||||
//        Box::new(BouncingImage::new("images/hackaday.png", 40, 18, 3, 500, 800)),
 | 
			
		||||
        circle
 | 
			
		||||
    let mut images = vec![
 | 
			
		||||
        BouncingImage::new("images/unicorn_cc.png", 13, -10, 1, -1, -1),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    let mut display = Display::new(DISPLAY_HOST, DISPLAY_PORT);
 | 
			
		||||
    let mut frame_counter: u32 = 0;
 | 
			
		||||
    thread::spawn(move || {
 | 
			
		||||
    let bind_address = format!("0.0.0.0:12345");
 | 
			
		||||
    let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap();
 | 
			
		||||
    socket.set_reuse_address(true).unwrap();
 | 
			
		||||
    //socket.set_nonblocking(true).unwrap();
 | 
			
		||||
    socket.join_multicast_v4(&Ipv4Addr::new(239, 1, 1, 1), &Ipv4Addr::new(0, 0, 0, 0)).unwrap();
 | 
			
		||||
    socket.bind(&"0.0.0.0:1234".parse::<SocketAddr>().unwrap().into()).unwrap();
 | 
			
		||||
    // Bind the UDP socket to the specified address and port.
 | 
			
		||||
    let socket: UdpSocket = socket.into();
 | 
			
		||||
 | 
			
		||||
    println!("Listening for UDP packets on {}", bind_address);
 | 
			
		||||
    display.send_thread();
 | 
			
		||||
 | 
			
		||||
    // Create a buffer to hold incoming data. 4 bytes for two u16 values.
 | 
			
		||||
    let mut buf = [0u8; 4];
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        // Wait for a packet to arrive.
 | 
			
		||||
        match socket.recv_from(&mut buf) {
 | 
			
		||||
            Ok((number_of_bytes, src_addr)) => {
 | 
			
		||||
                println!("\nReceived {} bytes from {}", number_of_bytes, src_addr);
 | 
			
		||||
 | 
			
		||||
                // Ensure we received the correct number of bytes.
 | 
			
		||||
                if number_of_bytes == 4 {
 | 
			
		||||
                    // Unpack the buffer into coordinates.
 | 
			
		||||
                    if let Some((x_rev, y_rev)) = unpack_coordinates(&buf) {
 | 
			
		||||
 | 
			
		||||
                        println!("Received Coordinates: X = {}, Y = {}", x_rev, y_rev);                        let x_32:u32 = x_rev.into();
 | 
			
		||||
                        let y_32:u32 = y_rev.into();
 | 
			
		||||
                        *x_thread.lock().unwrap() = x_32;
 | 
			
		||||
                        *y_thread.lock().unwrap() = y_32;
 | 
			
		||||
 | 
			
		||||
                        
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // This case should ideally not be reached if number_of_bytes is 4.
 | 
			
		||||
                        eprintln!("Error: Failed to unpack coordinate data.");
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    eprintln!(
 | 
			
		||||
                        "Warning: Received packet with incorrect size ({} bytes). Expected 4.",
 | 
			
		||||
                        number_of_bytes
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                eprintln!("Error receiving data: {}", e);
 | 
			
		||||
                // Decide if you want to break the loop on an error.
 | 
			
		||||
                // For a continuous server, you might just log and continue.
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // display.blank_screen();
 | 
			
		||||
    let mut tick:u32 = 0;
 | 
			
		||||
    loop {
 | 
			
		||||
        for (i, bb) in images.iter_mut().enumerate() {
 | 
			
		||||
            if bb.rate() > 0 && frame_counter % bb.rate() != 0 {
 | 
			
		||||
            if bb.rate > 0 && frame_counter % bb.rate != 0 {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            bb.draw_and_move(&mut display,tick);
 | 
			
		||||
                    }
 | 
			
		||||
            bb.draw_and_move(&mut display);
 | 
			
		||||
        }
 | 
			
		||||
        display.flush_frame();
 | 
			
		||||
        tick+=1;
 | 
			
		||||
        frame_counter += 1;
 | 
			
		||||
        
 | 
			
		||||
        println!("test");
 | 
			
		||||
        // A small delay to control the frame rate
 | 
			
		||||
        //std::thread::sleep(Duration::from_millis(16));
 | 
			
		||||
        std::thread::sleep(Duration::from_millis(16));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue