aboutsummaryrefslogtreecommitdiff
path: root/src/systems.rs
blob: dedef654ea3959ef98ac59090892f9360ed6bea7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
use bevy::{
    prelude::*,
    render::render_resource::{Extent3d, TextureFormat},
};
use crossterm::event::{read, Event, KeyEventKind};
use grex_framebuffer_extract::{
    components::FramebufferExtractDestination, render_assets::FramebufferExtractSource,
};

use crate::{
    components::Widget, events::TerminalInputEvent, resources::{EventQueue, Terminal, TerminalInput}
};

use ratatui::{
    prelude::*,
    widgets::{Paragraph, Wrap},
};

const BRAILLE_CODE_MIN: u16 = 0x2800;
const BRAILLE_CODE_MAX: u16 = 0x28FF;
const BRAILLE_DOT_BIT_POSITIONS: [u8; 8] = [0, 1, 2, 6, 3, 4, 5, 7];

pub fn setup(event_queue: Res<EventQueue>) {
    let event_queue = event_queue.0.clone();
    std::thread::spawn(move || {
        loop {
            // `read()` blocks until an `Event` is available
            match read() {
                Ok(event) => {
                    event_queue.lock().unwrap().push(event);
                }
                Err(err) => {
                    panic!("Error reading input events: {:?}", err);
                }
            }
        }
    });
}

pub fn print_to_terminal(
    mut terminal: ResMut<Terminal>,
    mut widgets: Query<&mut Widget>,
    image_exports: Query<&FramebufferExtractDestination>,
) {
    for image_export in image_exports.iter() {
        let mut image = image_export
            .0
            .lock()
            .expect("Failed to get lock on output texture");
        //TODO: Find a better way of preventing first frame
        if image.size() == UVec2::ONE {
            continue;
        }
        if image.texture_descriptor.format != TextureFormat::R8Unorm {
            warn_once!("Extracted framebuffer texture is not R8Unorm format. Will attempt conversion, but consider changing your render texture's format.");
            info_once!("{:?}", image);
            match image.convert(TextureFormat::R8Unorm) {
                Some(img) => *image = img,
                None => error_once!(
                    "Could not convert to R8Unorm texture format. Unexpected output may occur."
                ),
            };
        }

        let mut output_buffer = Vec::<char>::new();
        let width = image.width();
        let height = image.height();
        let data = &image.data;
        for character_y in (0..height).step_by(4) {
            for character_x in (0..width).step_by(2) {
                let mut mask: u8 = 0;
                for offset_x in 0..2 {
                    for offset_y in 0..4 {
                        let x = character_x + offset_x;
                        let y = character_y + offset_y;
                        if x < width && y < height && data[(y * width + x) as usize] == 0xFF {
                            mask |= 1
                                << (BRAILLE_DOT_BIT_POSITIONS[(offset_x * 4 + offset_y) as usize]);
                        }
                    }
                }
                output_buffer.push(braille_char(mask));
            }
        }

        let string = output_buffer.into_iter().collect::<String>();
        terminal
            .0
            .draw(|frame| {
                let area = frame.size();
                frame.render_widget(
                    Paragraph::new(string)
                        .white()
                        .bold()
                        .wrap(Wrap { trim: true }),
                    area,
                );
                let mut active_widgets = widgets.iter_mut().filter(|widget| widget.enabled).collect::<Vec<_>>();
                active_widgets.sort_by(|a, b| a.depth.cmp(&b.depth));
                for mut widget in active_widgets {
                    widget.widget.render(frame, area);
                }
            })
            .expect("Failed to draw terminal frame");
    }
}

fn braille_char(mask: u8) -> char {
    match char::from_u32((BRAILLE_CODE_MIN + mask as u16) as u32) {
        Some(character) => {
            if character as u16 > BRAILLE_CODE_MAX {
                panic!("Number too big!")
            }
            character
        }
        None => panic!("Error converting character!"),
    }
}

pub fn widget_input_handling(
    mut widgets: Query<&mut Widget>,
    mut event_reader: EventReader<TerminalInputEvent>,
    mut commands: Commands,
) {
    for event in event_reader.read() {
        for mut widget in widgets.iter_mut().filter(|widget| widget.enabled) {
            widget.widget.handle_events(event, &mut commands);
        }
    }
}

pub fn input_handling(
    event_queue: Res<EventQueue>,
    mut input: ResMut<TerminalInput>,
    mut event_writer: EventWriter<TerminalInputEvent>,
) {
    let mut event_queue = event_queue.0.lock().unwrap();
    while let Some(event) = event_queue.pop() {
        if let Event::Key(event) = event {
            match event.kind {
                KeyEventKind::Press => {
                    input.press(event.code);
                }
                KeyEventKind::Release => {
                    input.release(event.code);
                }
                _ => (),
            }
        }
        event_writer.send(TerminalInputEvent(event));
    }
}

pub fn resize_handling(
    mut images: ResMut<Assets<Image>>,
    mut sources: ResMut<Assets<FramebufferExtractSource>>,
    mut event_reader: EventReader<TerminalInputEvent>,
) {
    for event in event_reader.read() {
        if let Event::Resize(w, h) = event.0 {
            for source in sources.iter_mut() {
                let image = images.get_mut(&source.1 .0).unwrap();
                image.resize(Extent3d {
                    width: w as u32 * 2,
                    height: h as u32 * 4,
                    depth_or_array_layers: 1,
                });
            }
        }
    }
}