diff --git a/Cargo.lock b/Cargo.lock index 09fb07b..68f3f8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 5afe95d..2a394d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,3 @@ edition = "2024" png = "0.17" libc = "0.2" nix = { version = "0.29", features = ["socket", "uio"] } -socket2 = "0.4.7" diff --git a/src/buffer.rs b/src/buffer.rs new file mode 100644 index 0000000..33f3877 --- /dev/null +++ b/src/buffer.rs @@ -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 { + buffer: [MaybeUninit; N], + head: usize, + tail: usize, + size: usize, +} + +impl RingBuffer { + /// 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 { + 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::::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(); +} + diff --git a/src/color.rs b/src/color.rs deleted file mode 100644 index 2b3b9dd..0000000 --- a/src/color.rs +++ /dev/null @@ -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 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, - } - } -} diff --git a/src/main-new.rs b/src/main-new.rs deleted file mode 100644 index 7af1867..0000000 --- a/src/main-new.rs +++ /dev/null @@ -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, -} - -impl PngData { - /// Loads and decodes a PNG image from the given path. - fn open(path: &str) -> Result { - 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, // 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)); - } -} - diff --git a/src/main.rs b/src/main.rs index 8585c0b..3e56a03 100644 --- a/src/main.rs +++ b/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>, - y: Arc>, -} -impl Circle { - - fn new(x:Arc>, y: Arc>) -> 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>,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::::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> = Arc::new(Mutex::new(0)); - let x_thread = x.clone(); - - let y:Arc>= Arc::new(Mutex::new(0)); - let y_thread = y.clone(); - - let circle = Box::new(Circle::new(x,y)); - let mut images:Vec> = 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::().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)); } }