diff --git a/ctru-rs/src/console.rs b/ctru-rs/src/console.rs index ddd4fe85..b5b60bb9 100644 --- a/ctru-rs/src/console.rs +++ b/ctru-rs/src/console.rs @@ -5,6 +5,8 @@ use ctru_sys::{consoleClear, consoleInit, consoleSelect, consoleSetWindow, Print use crate::gfx::Screen; +mod non_flushing; + static mut EMPTY_CONSOLE: PrintConsole = unsafe { const_zero::const_zero!(PrintConsole) }; pub struct Console<'screen> { @@ -27,6 +29,18 @@ impl<'screen> Console<'screen> { } } + /// Initialize a console that prints to the screen *without* flushing after every newline + /// or carriage return. This can prevent deadlocks when trying to print from multiple + /// threads simultaneously, but [`Gfx::flush_buffers`] + /// must be called for the printed output to be visible + /// + /// [`Gfx::flush_buffers`]: crate::gfx::Gfx::flush_buffers + pub fn non_flushing(screen: RefMut<'screen, dyn Screen>) -> Self { + let mut console = Self::init(screen); + console.context.PrintChar = Some(non_flushing::print_char); + console + } + /// Returns true if a valid Console to print on is selected pub fn exists() -> bool { unsafe { diff --git a/ctru-rs/src/console/non_flushing.rs b/ctru-rs/src/console/non_flushing.rs new file mode 100644 index 00000000..fcdc4a5b --- /dev/null +++ b/ctru-rs/src/console/non_flushing.rs @@ -0,0 +1,90 @@ +//! This is a bit of a hack, but allows us to reuse most of the libctru console +//! implementation without writing one from scratch. This lets the user control +//! when the console characters are flushed to the screen, which can help prevent +//! deadlocks while using the console and graphics simultaneously. + +use ctru_sys::PrintConsole; + +/// Print a character to the console, without flushing the graphics buffers. +/// Falls back to the default implementation for any characters other than +/// `\n` or `\r`, which are the only ones that normally result in a flush. +pub extern "C" fn print_char(console: *mut libc::c_void, c: i32) -> bool { + let console = unsafe { &mut *(console.cast::()) }; + + let ret = match c { + // '\r' + 10 => { + new_row(console); + console.cursorX = 0; + true + } + // '\n' + 13 => { + console.cursorX = 0; + true + } + _ => false, // use default console printer + }; + + if console.cursorX >= console.windowWidth { + console.cursorX = 0; + new_row(console); + true + } else { + ret + } +} + +/// Direct port of +/// +fn new_row(console: &mut PrintConsole) { + console.cursorY += 1; + + if console.cursorY >= console.windowHeight { + console.cursorY -= 1; + let idx = (console.windowX * 8 * 240) + (239 - (console.windowY * 8)); + + unsafe { + let mut dst: *mut u16 = console.frameBuffer.offset(idx as _); + let mut src: *mut u16 = dst.offset(-8); + + for _ in 0..console.windowWidth * 8 { + let mut from: *mut u32 = (src as i32 & !3) as _; + let mut to: *mut u32 = (dst as i32 & !3) as _; + for _ in 0..(((console.windowHeight - 1) * 8) / 2) { + *to = *from; + to = to.offset(-1); + from = from.offset(-1); + } + + dst = dst.offset(240); + src = src.offset(240); + } + } + + clear_line(console); + } +} + +// HACK: this is technically an implementation detail of libctru's console, +// but it has a that we can link to. We use this so we don't need to re-implement +// the entire call stack down from clear_line just to print some spaces characters. +extern "C" { + fn consolePrintChar(c: i32); +} + +/// Direct port of `consoleClearLine('2')`, minus the flush at the end. +/// +fn clear_line(console: &mut PrintConsole) { + let col_tmp = console.cursorX; + + console.cursorX = 0; + + for _ in 0..console.windowWidth { + unsafe { + consolePrintChar(b' ' as i32); + } + } + + console.cursorX = col_tmp; +}