Skip to main content

test0/
test0.rs

1//! Test colour and unicode handling.  Uses direct ANSI output via
2//! `Output`.
3use stakker::{ActorOwn, CX, Stakker, StopCause, actor, fwd_to, ret_shutdown, ret_some_to, stop};
4use stakker_mio::MioPoll;
5use stakker_mio::mio::{Events, Poll};
6use stakker_tui::{Key, Output, TermShare, Terminal, sizer::SimpleSizer};
7use std::io::Write;
8use std::time::{Duration, Instant};
9
10// Let main loop dump setup I/O errors to console.  You could catch
11// them instead if you want.
12fn main() -> std::io::Result<()> {
13    let mut stakker = Stakker::new(Instant::now());
14    let s = &mut stakker;
15    let miopoll = MioPoll::new(s, Poll::new()?, Events::with_capacity(1024), 0)?;
16
17    let _app = actor!(s, App::init(), ret_shutdown!(s));
18
19    const MAXDELAY: Duration = Duration::from_secs(60);
20    let mut idle_pending = s.run(Instant::now(), false);
21    let mut io_pending = false;
22    let mut activity;
23    while s.not_shutdown() {
24        let maxdur = s.next_wait_max(Instant::now(), MAXDELAY, idle_pending || io_pending);
25        (activity, io_pending) = miopoll.poll(maxdur)?;
26        idle_pending = s.run(Instant::now(), !activity);
27    }
28    if let Some(reason) = s.shutdown_reason() {
29        if reason.has_error() {
30            println!("{reason}");
31        }
32    }
33
34    Ok(())
35}
36
37struct App {
38    _terminal: ActorOwn<Terminal>,
39    tshare: Option<TermShare>,
40}
41
42impl App {
43    fn init(cx: CX![]) -> Option<Self> {
44        let _terminal = actor!(
45            cx,
46            Terminal::init(
47                SimpleSizer::new(),
48                fwd_to!([cx], resize() as (Option<TermShare>)),
49                fwd_to!([cx], input() as (Key))
50            ),
51            ret_some_to!([cx], |_, cx, cause: StopCause| {
52                println!("Terminal actor failed: {cause}");
53                stop!(cx);
54            })
55        );
56        Some(Self {
57            _terminal,
58            tshare: None,
59        })
60    }
61
62    fn resize(&mut self, cx: CX![], tshare: Option<TermShare>) {
63        self.tshare = tshare;
64        if self.tshare.is_some() {
65            self.redraw(cx);
66        }
67    }
68
69    fn redraw(&mut self, cx: CX![]) {
70        if let Some(ref tshare) = self.tshare {
71            let o = tshare.output(cx);
72            o.attr_99().cursor_show().scroll_up().save_cleanup();
73            o.cursor_hide();
74            Self::draw(o);
75        }
76    }
77
78    fn input(&mut self, cx: CX![], key: Key) {
79        if key == Key::Ctrl('L') {
80            self.redraw(cx);
81        } else {
82            stop!(cx);
83        }
84    }
85
86    fn draw(o: &mut Output) {
87        o.clear_all_99();
88        if o.sy() < 24 || o.sx() < 80 {
89            o.hfb(162);
90            o.at(o.sy() >> 1, (o.sx() - 30) >> 1);
91            write!(o, " Terminal too small: {} x {} ", o.sy(), o.sx()).unwrap();
92            o.hfb(99);
93            o.flush();
94            return;
95        }
96
97        let x0 = (o.sx() - 80) >> 1;
98        let y0 = (o.sy() - 24) >> 1;
99        let mut yy = y0;
100        let c256 = o.features().colour_256;
101        o.at(yy, x0).hfb(99).text("Colour mode:");
102        o.at(yy, x0 + 15)
103            .hfb(199)
104            .text(if c256 { "256-colour" } else { "8-colour" });
105        yy += 2;
106
107        o.at(yy, x0).hfb(99).text("Attributes:");
108        yy += 1;
109        const TEXT: [&str; 4] = ["White:", "Brown:", "Blue:", "Black:"];
110        const BASE_HFB: [u16; 4] = [70, 60, 10, 3];
111        for i in 0..4 {
112            let hfb = BASE_HFB[i];
113            o.at(yy, x0 + 2).hfb(99).text(TEXT[i]);
114            o.at(yy, x0 + 16).hfb(hfb).text(" norm ");
115            o.at(yy, x0 + 22).hfb(100 + hfb).text("bold ");
116            o.at(yy, x0 + 27).hfb(200 + hfb).text("undl ");
117            o.at(yy, x0 + 32).hfb(300 + hfb).text("both ");
118            yy += 1;
119        }
120        o.at(yy + 1, x0)
121            .hfb(60)
122            .text(" (On 8-colour expect bold/ul to");
123        o.at(yy + 2, x0)
124            .hfb(60)
125            .text("  be emulated as colour changes.)");
126        let y1a = yy + 3;
127
128        yy = y0;
129        o.at(yy, x0 + 40)
130            .hfb(99)
131            .text("Base, bold and background colours:");
132        o.at(yy + 1, x0 + 42).hfb(7).text("  black   ");
133        o.at(yy + 2, x0 + 42).hfb(10).text("  blue    ");
134        o.at(yy + 3, x0 + 42).hfb(20).text("  red     ");
135        o.at(yy + 4, x0 + 42).hfb(30).text("  magenta ");
136        o.at(yy + 5, x0 + 42).hfb(40).text("  green   ");
137        o.at(yy + 6, x0 + 42).hfb(50).text("  cyan    ");
138        o.at(yy + 7, x0 + 42).hfb(60).text("  yellow  ");
139        o.at(yy + 8, x0 + 42).hfb(70).text("  white   ");
140        o.at(yy + 1, x0 + 54).hfb(100).text("  black   ");
141        o.at(yy + 2, x0 + 54).hfb(110).text("  blue    ");
142        o.at(yy + 3, x0 + 54).hfb(120).text("  red     ");
143        o.at(yy + 4, x0 + 54).hfb(130).text("  magenta ");
144        o.at(yy + 5, x0 + 54).hfb(140).text("  green   ");
145        o.at(yy + 6, x0 + 54).hfb(150).text("  cyan    ");
146        o.at(yy + 7, x0 + 54).hfb(160).text("  yellow  ");
147        o.at(yy + 8, x0 + 54).hfb(170).text("  white   ");
148        o.at(yy + 1, x0 + 66).hfb(70).text("  black   ");
149        o.at(yy + 2, x0 + 66).hfb(71).text("  blue    ");
150        o.at(yy + 3, x0 + 66).hfb(72).text("  red     ");
151        o.at(yy + 4, x0 + 66).hfb(73).text("  magenta ");
152        o.at(yy + 5, x0 + 66).hfb(4).text("  green   ");
153        o.at(yy + 6, x0 + 66).hfb(5).text("  cyan    ");
154        o.at(yy + 7, x0 + 66).hfb(6).text("  yellow  ");
155        o.at(yy + 8, x0 + 66).hfb(7).text("  white   ");
156        o.at(yy + 10, x0 + 40)
157            .hfb(60)
158            .text(" (Expect all 16 colours to be");
159        o.at(yy + 11, x0 + 40)
160            .hfb(60)
161            .text("  visible in all cases.)");
162        let y1b = yy + 12;
163        let end = y1a.max(y1b);
164
165        yy = end + 10;
166        o.at(yy - 1, x0).hfb(99).text("Hue wheel");
167        o.at(yy, x0);
168        if o.features().colour_256 {
169            const RED: [i32; 7] = [255, 255, 0, 0, 0, 255, 255];
170            const GREEN: [i32; 7] = [0, 255, 255, 255, 0, 0, 0];
171            const BLUE: [i32; 7] = [0, 0, 0, 255, 255, 255, 0];
172            for x in 0..78 {
173                let i = x / 13;
174                let off = (x - i * 13) as i32;
175                let r = RED[i] + (RED[i + 1] - RED[i]) * off / 13;
176                let g = GREEN[i] + (GREEN[i + 1] - GREEN[i]) * off / 13;
177                let b = BLUE[i] + (BLUE[i + 1] - BLUE[i]) * off / 13;
178                let rgb = (r << 16) + (g << 8) + b;
179                let hfb = o.hfb_alloc(false, false, 0xFFFFFF, rgb as u32);
180                o.hfb(hfb).spaces(1);
181            }
182        } else {
183            o.hfb(2).spaces(13);
184            o.hfb(6).spaces(13);
185            o.hfb(4).spaces(13);
186            o.hfb(5).spaces(13);
187            o.hfb(1).spaces(13);
188            o.hfb(3).spaces(13);
189        }
190
191        yy = end + 1;
192        let mut xx = x0 + 40;
193
194        o.at(yy, xx).hfb(99).text("ASCII");
195        for a in 0..96 {
196            o.at(yy + 1 + (a >> 4), xx + 1 + (a & 15))
197                .char(char::from_u32((a + 32) as u32).unwrap());
198        }
199
200        xx += 20;
201        o.at(yy, xx).hfb(99).text("Latin-1");
202        for a in 0..96 {
203            o.at(yy + 1 + (a >> 4), xx + 1 + (a & 15))
204                .char(char::from_u32((a + 160) as u32).unwrap());
205        }
206
207        const BOXTITLE1: &str = "\
208Single  Single  Double  Thick";
209        const BOXTITLE2: &str = "\
210+Double +Thick  +Single +Single";
211
212        const BOXTEST: &str = "\
213+-+-+-+ +-+-+-+ +=+=+=+ +*+*+*+
214| | H | | | $ | H H | H $ $ | $
215+-+-+-+ +-+-+-+ +=+=+=+ +*+*+*+
216| | H | | | $ | H H | H $ $ | $
217+=+=+=+ +*+*+*+ +-+-+-+ +-+-+-+
218| | H | | | $ | H H | H $ $ | $
219+-+-+-+ +-+-+-+ +=+=+=+ +*+*+*+";
220
221        let boxtest = Output::box_from_ascii_art(BOXTEST);
222        yy = end + 1;
223        xx = x0 + 2;
224        o.at(yy - 2, xx).text(BOXTITLE1);
225        o.at(yy - 1, xx).text(BOXTITLE2);
226        for line in boxtest.split('\n') {
227            o.at(yy, xx).text(line);
228            yy += 1;
229        }
230
231        o.flush();
232    }
233}