1use async_channel::Sender;
20use endbasic_std::Signal;
21use endbasic_std::console::{Console, ConsoleFactory, ConsoleHost, ConsoleSpec, Resolution};
22use endbasic_std::gfx::lcd::fonts::{FONT_VGA8X16, Font, Fonts};
23use std::cell::RefCell;
24use std::io;
25use std::num::NonZeroU32;
26use std::rc::Rc;
27use std::sync::mpsc::{self, Receiver, SyncSender};
28
29mod console;
30use console::SdlConsole;
31#[cfg(feature = "testutils")]
32pub use console::testutils;
33
34mod host;
35use host::SdlConsoleHost;
36
37const DEFAULT_RESOLUTION_PIXELS: (u32, u32) = (800, 600);
39
40fn string_error_to_io_error(e: String) -> io::Error {
42 io::Error::other(e)
43}
44
45struct SdlConsoleFactory {
47 request_tx: SyncSender<host::Request>,
48 response_rx: Receiver<host::Response>,
49 on_key_rx: Receiver<endbasic_std::console::Key>,
50}
51
52impl SdlConsoleFactory {
53 fn new(
55 request_tx: SyncSender<host::Request>,
56 response_rx: Receiver<host::Response>,
57 on_key_rx: Receiver<endbasic_std::console::Key>,
58 ) -> Self {
59 Self { request_tx, response_rx, on_key_rx }
60 }
61
62 fn build_console(self) -> io::Result<SdlConsole> {
64 SdlConsole::new(self.request_tx, self.response_rx, self.on_key_rx)
65 }
66}
67
68impl ConsoleFactory for SdlConsoleFactory {
69 fn build(self: Box<Self>) -> io::Result<Rc<RefCell<dyn Console>>> {
70 let console = self.build_console()?;
71 Ok(Rc::from(RefCell::from(console)))
72 }
73}
74
75pub(crate) fn new_console_pair(
77 resolution: Resolution,
78 default_fg_color: Option<u8>,
79 default_bg_color: Option<u8>,
80 font: &'static Font,
81 signals_tx: Sender<Signal>,
82) -> (SdlConsoleHost, SdlConsoleFactory) {
83 let (request_tx, request_rx) = mpsc::sync_channel(1);
84 let (response_tx, response_rx) = mpsc::sync_channel(1);
85 let (on_key_tx, on_key_rx) = mpsc::channel();
86 let host = SdlConsoleHost {
87 resolution,
88 default_fg_color,
89 default_bg_color,
90 font,
91 request_rx,
92 response_tx,
93 on_key_tx,
94 signals_tx,
95 };
96 let factory = SdlConsoleFactory::new(request_tx, response_rx, on_key_rx);
97 (host, factory)
98}
99
100pub fn setup(
102 spec: &mut ConsoleSpec<'_>,
103 signals_tx: Sender<Signal>,
104 fonts: &Fonts,
105) -> io::Result<(Box<dyn ConsoleHost>, Box<dyn ConsoleFactory>)> {
106 let resolution: Resolution = spec.take_keyed_flag("resolution")?.unwrap_or_else(|| {
107 let width = NonZeroU32::new(DEFAULT_RESOLUTION_PIXELS.0).unwrap();
108 let height = NonZeroU32::new(DEFAULT_RESOLUTION_PIXELS.1).unwrap();
109 Resolution::Windowed((width, height))
110 });
111
112 let default_fg_color = spec.take_keyed_flag::<u8>("fg_color")?;
113 let default_bg_color = spec.take_keyed_flag::<u8>("bg_color")?;
114
115 let font_name = spec.take_keyed_flag_str("font").unwrap_or(FONT_VGA8X16.name);
116 let font = fonts.get(font_name)?;
117
118 let (host, factory) =
119 new_console_pair(resolution, default_fg_color, default_bg_color, font, signals_tx);
120 Ok((Box::from(host), Box::from(factory)))
121}