diff --git a/Cargo.lock b/Cargo.lock index d23409de..d0379de7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2878,6 +2878,7 @@ dependencies = [ "parking_lot", "poll-promise", "rfd", + "ringbuf", "steamworks", "strum", "tempfile", @@ -4408,6 +4409,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "ringbuf" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79abed428d1fd2a128201cec72c5f6938e2da607c6f3745f769fabea399d950a" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "rodio" version = "0.17.3" diff --git a/Cargo.toml b/Cargo.toml index 6c3cc0a2..95cfe544 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -201,6 +201,7 @@ tokio = { version = "1.33", features = [ ] } # *sigh* tempfile.workspace = true luminol-term.workspace = true +ringbuf = "0.3.3" # Set poll promise features here based on the target # I'd much rather do it in the workspace, but cargo doesn't support that yet diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 950a1174..027d3ff4 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -168,6 +168,7 @@ impl<'res> UpdateState<'res> { &'this mut self, edit_windows: &'this mut window::EditWindows, ) -> UpdateState<'this> { + tracing::info!("dvasacjkfsadcdfngaxjnfdsa"); UpdateState { ctx: self.ctx, audio: self.audio, diff --git a/src/app/log_window.rs b/src/app/log_window.rs index a2893ddd..bf22761f 100644 --- a/src/app/log_window.rs +++ b/src/app/log_window.rs @@ -22,25 +22,58 @@ // terms of the Steamworks API by Valve Corporation, the licensors of this // Program grant you additional permission to convey the resulting work. +use std::sync::mpsc::{Receiver, Sender}; + +use ringbuf::{ring_buffer::RbBase, Rb}; + pub struct LogWindow { pub(super) term_shown: bool, + byte_rx: Receiver, + byte_tx: Sender, + ringbuf: RingBuf, // Should we use a temporary file instead of a ringbuffer? term: luminol_term::widget::ChannelTerminal, save_promise: Option>>, } +const MAX_BUFFER_CAPACITY: usize = 1 << 24; +type RingBuf = ringbuf::LocalRb>>; impl LogWindow { pub fn new( config: &luminol_config::terminal::Config, byte_rx: std::sync::mpsc::Receiver, ) -> Self { + let (byte_tx, term_byte_rx) = std::sync::mpsc::channel(); + let term = luminol_term::widget::Terminal::channel(config, term_byte_rx); + let ringbuf = RingBuf::new(2 << 12); + Self { + byte_rx, + byte_tx, + ringbuf, + term, term_shown: false, save_promise: None, - term: luminol_term::widget::Terminal::channel(config, byte_rx), } } pub fn ui(&mut self, ui: &mut egui::Ui, update_state: &mut luminol_core::UpdateState<'_>) { + for byte in self.byte_rx.try_iter() { + let _ = self.byte_tx.send(byte); + + // resize ringbuffer if it is full + let capacity = self.ringbuf.capacity(); + if self.ringbuf.is_full() && capacity < MAX_BUFFER_CAPACITY { + let mut new_ringbuf = RingBuf::new(capacity << 1); + + let (front, back) = self.ringbuf.as_slices(); + new_ringbuf.push_slice(front); + new_ringbuf.push_slice(back); + + self.ringbuf = new_ringbuf; + } + self.ringbuf.push_overwrite(byte); + } + // We update the log terminal even if it's not open so that we don't encounter // performance problems when the terminal has to parse all the new input at once self.term.update(); @@ -79,19 +112,12 @@ impl LogWindow { Err(p) => self.save_promise = Some(p), } } else if ui.button("Save to file").clicked() { - // self.buffer.make_contiguous(); - // let buffer = self.buffer.clone(); - - // self.save_promise = Some(luminol_core::spawn_future(async move { - // use futures_lite::AsyncWriteExt; - - // let mut tmp = luminol_filesystem::host::File::new()?; - // let mut cursor = async_std::io::Cursor::new(buffer.as_slices().0); - // async_std::io::copy(&mut cursor, &mut tmp).await?; - // tmp.flush().await?; - // tmp.save("luminol.log", "Log files").await?; - // Ok(()) - // })); + let (left, right) = self.ringbuf.as_slices(); + let mut buffer = left.to_vec(); + buffer.extend_from_slice(right); + + self.save_promise = + Some(luminol_core::spawn_future(Self::save_promise(buffer))); } }); @@ -105,4 +131,15 @@ impl LogWindow { } }); } + + async fn save_promise(buffer: Vec) -> luminol_filesystem::Result<()> { + use futures_lite::AsyncWriteExt; + + let mut tmp = luminol_filesystem::host::File::new()?; + let mut cursor = async_std::io::Cursor::new(buffer); + async_std::io::copy(&mut cursor, &mut tmp).await?; + tmp.flush().await?; + tmp.save("luminol.log", "Log files").await?; + Ok(()) + } } diff --git a/src/log.rs b/src/log.rs index 19c6f7bb..0e23f129 100644 --- a/src/log.rs +++ b/src/log.rs @@ -24,11 +24,14 @@ use std::io::Write; use std::sync::mpsc::Sender; +use std::sync::Arc; + +use once_cell::sync::OnceCell; #[derive(Clone)] struct LogWriter { sender: Sender, - context: egui::Context, + context: Arc>, } impl std::io::Write for LogWriter { @@ -40,7 +43,9 @@ impl std::io::Write for LogWriter { let _ = self.sender.send(byte); } - self.context.request_repaint(); + if let Some(ctx) = self.context.get() { + ctx.request_repaint(); + } Ok(buf.len()) } @@ -68,7 +73,7 @@ where } } -pub fn initialize_log(sender: Sender, context: egui::Context) { +pub fn initialize_log(sender: Sender, context: Arc>) { let log_writer = LogWriter { sender, context }; tracing_subscriber::fmt() // we clone + move the log_writer so this closure impls Fn() diff --git a/src/main.rs b/src/main.rs index a3214292..192531bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -205,6 +205,10 @@ fn main() { } })); + let (log_byte_tx, log_byte_rx) = std::sync::mpsc::channel(); + let ctx_cell = std::sync::Arc::new(once_cell::sync::OnceCell::new()); + log::initialize_log(log_byte_tx, ctx_cell.clone()); + let image = image::load_from_memory(ICON).expect("Failed to load Icon data."); let native_options = luminol_eframe::NativeOptions { @@ -239,9 +243,10 @@ fn main() { luminol_eframe::run_native( "Luminol", native_options, - Box::new(|cc| { - let (log_byte_tx, log_byte_rx) = std::sync::mpsc::channel(); - log::initialize_log(log_byte_tx, cc.egui_ctx.clone()); + Box::new(move |cc| { + ctx_cell + .set(cc.egui_ctx.clone()) + .expect("egui context cell already set (this shouldn't happen!)"); Box::new(app::App::new( cc,