Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Write a console that does not flush gfx buffers #57

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions ctru-rs/src/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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 {
Expand Down
90 changes: 90 additions & 0 deletions ctru-rs/src/console/non_flushing.rs
Original file line number Diff line number Diff line change
@@ -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::<PrintConsole>()) };

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
/// <https://github.com/devkitPro/libctru/blob/master/libctru/source/console.c#L724>
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.
/// <https://github.com/devkitPro/libctru/blob/master/libctru/source/console.c#L231>
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;
}