use async_channel::Sender;
use endbasic_std::Signal;
use endbasic_std::console::{Console, ConsoleFactory, ConsoleHost, ConsoleSpec, Resolution};
use endbasic_std::gfx::lcd::fonts::{FONT_VGA8X16, Font, Fonts};
use std::cell::RefCell;
use std::io;
use std::num::NonZeroU32;
use std::rc::Rc;
use std::sync::mpsc::{self, Receiver, SyncSender};
mod console;
use console::SdlConsole;
#[cfg(feature = "testutils")]
pub use console::testutils;
mod host;
use host::SdlConsoleHost;
const DEFAULT_RESOLUTION_PIXELS: (u32, u32) = (800, 600);
fn string_error_to_io_error(e: String) -> io::Error {
io::Error::other(e)
}
struct SdlConsoleFactory {
request_tx: SyncSender<host::Request>,
response_rx: Receiver<host::Response>,
on_key_rx: Receiver<endbasic_std::console::Key>,
}
impl SdlConsoleFactory {
fn new(
request_tx: SyncSender<host::Request>,
response_rx: Receiver<host::Response>,
on_key_rx: Receiver<endbasic_std::console::Key>,
) -> Self {
Self { request_tx, response_rx, on_key_rx }
}
fn build_console(self) -> io::Result<SdlConsole> {
SdlConsole::new(self.request_tx, self.response_rx, self.on_key_rx)
}
}
impl ConsoleFactory for SdlConsoleFactory {
fn build(self: Box<Self>) -> io::Result<Rc<RefCell<dyn Console>>> {
let console = self.build_console()?;
Ok(Rc::from(RefCell::from(console)))
}
}
pub(crate) fn new_console_pair(
resolution: Resolution,
default_fg_color: Option<u8>,
default_bg_color: Option<u8>,
font: &'static Font,
signals_tx: Sender<Signal>,
) -> (SdlConsoleHost, SdlConsoleFactory) {
let (request_tx, request_rx) = mpsc::sync_channel(1);
let (response_tx, response_rx) = mpsc::sync_channel(1);
let (on_key_tx, on_key_rx) = mpsc::channel();
let host = SdlConsoleHost {
resolution,
default_fg_color,
default_bg_color,
font,
request_rx,
response_tx,
on_key_tx,
signals_tx,
};
let factory = SdlConsoleFactory::new(request_tx, response_rx, on_key_rx);
(host, factory)
}
pub fn setup(
spec: &mut ConsoleSpec<'_>,
signals_tx: Sender<Signal>,
fonts: &Fonts,
) -> io::Result<(Box<dyn ConsoleHost>, Box<dyn ConsoleFactory>)> {
let resolution: Resolution = spec.take_keyed_flag("resolution")?.unwrap_or_else(|| {
let width = NonZeroU32::new(DEFAULT_RESOLUTION_PIXELS.0).unwrap();
let height = NonZeroU32::new(DEFAULT_RESOLUTION_PIXELS.1).unwrap();
Resolution::Windowed((width, height))
});
let default_fg_color = spec.take_keyed_flag::<u8>("fg_color")?;
let default_bg_color = spec.take_keyed_flag::<u8>("bg_color")?;
let font_name = spec.take_keyed_flag_str("font").unwrap_or(FONT_VGA8X16.name);
let font = fonts.get(font_name)?;
let (host, factory) =
new_console_pair(resolution, default_fg_color, default_bg_color, font, signals_tx);
Ok((Box::from(host), Box::from(factory)))
}