Add build action
Some checks failed
Build / Build (push) Failing after 43s

This commit is contained in:
Silas Bartha 2025-03-19 07:24:15 -04:00
parent ac1589f2b9
commit ddfd4a7422
Signed by: soaos
GPG Key ID: 9BD3DCC0D56A09B2
8 changed files with 114 additions and 52 deletions

5
.cargo/config.toml Normal file
View File

@ -0,0 +1,5 @@
[profile.dev.package."*"]
opt-level = 2
[patch.'https://git.soaos.dev/bevy_dither_post_process'.bevy_dither_post_process]
path = "../bevy_dither_post_process"

View File

@ -0,0 +1,25 @@
name: Build
on: [push]
jobs:
Build:
env:
RUNNER_TOOL_CACHE: /toolcache
container: rust:alpine
steps:
- name: Install node
run: apk add nodejs gcc libc-dev pkgconf libx11-dev alsa-lib-dev eudev-dev tar
- name: Check out repository code
uses: actions/checkout@v4
- name: Restore cache
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Build
run: cargo build --release

18
Cargo.lock generated
View File

@ -332,7 +332,7 @@ dependencies = [
"console_error_panic_hook", "console_error_panic_hook",
"ctrlc", "ctrlc",
"derive_more 1.0.0", "derive_more 1.0.0",
"downcast-rs", "downcast-rs 1.2.1",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
] ]
@ -359,7 +359,7 @@ dependencies = [
"crossbeam-channel", "crossbeam-channel",
"derive_more 1.0.0", "derive_more 1.0.0",
"disqualified", "disqualified",
"downcast-rs", "downcast-rs 1.2.1",
"either", "either",
"futures-io", "futures-io",
"futures-lite", "futures-lite",
@ -472,8 +472,6 @@ dependencies = [
[[package]] [[package]]
name = "bevy_dither_post_process" name = "bevy_dither_post_process"
version = "0.3.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33f3318ee0fd8667a911def5aedec7c7e4a36b0d4d7c78de6a8be357c1658132"
dependencies = [ dependencies = [
"bevy", "bevy",
] ]
@ -819,7 +817,7 @@ dependencies = [
"bevy_utils", "bevy_utils",
"derive_more 1.0.0", "derive_more 1.0.0",
"disqualified", "disqualified",
"downcast-rs", "downcast-rs 1.2.1",
"erased-serde", "erased-serde",
"glam", "glam",
"serde", "serde",
@ -870,7 +868,7 @@ dependencies = [
"bytemuck", "bytemuck",
"codespan-reporting", "codespan-reporting",
"derive_more 1.0.0", "derive_more 1.0.0",
"downcast-rs", "downcast-rs 1.2.1",
"encase", "encase",
"futures-lite", "futures-lite",
"image", "image",
@ -973,7 +971,7 @@ dependencies = [
"color-eyre", "color-eyre",
"crossbeam-channel", "crossbeam-channel",
"crossterm", "crossterm",
"downcast-rs", "downcast-rs 2.0.1",
"leafwing-input-manager", "leafwing-input-manager",
"once_cell", "once_cell",
"ratatui", "ratatui",
@ -1793,6 +1791,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "downcast-rs"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
[[package]] [[package]]
name = "dpi" name = "dpi"
version = "0.1.1" version = "0.1.1"

View File

@ -1,17 +1,16 @@
[package] [package]
name = "bevy_terminal_display" name = "bevy_terminal_display"
version = "0.6.0" version = "0.7.0"
edition = "2021" edition = "2021"
license = "0BSD OR MIT OR Apache-2.0" license = "0BSD OR MIT OR Apache-2.0"
description = "A plugin for the Bevy game engine which enables rendering to a terminal using unicode braille characters." description = "A plugin for the Bevy game engine which enables rendering to a terminal using unicode braille characters."
repository = "https://git.exvacuum.dev/bevy_terminal_display" repository = "https://git.soaos.dev/bevy_terminal_display"
[dependencies] [dependencies]
crossbeam-channel = "0.5" crossbeam-channel = "0.5"
downcast-rs = "1.2" downcast-rs = "2.0"
once_cell = "1.19" once_cell = "1.19"
bevy_headless_render = "0.2" bevy_headless_render = "0.2"
bevy_dither_post_process = "0.3"
ratatui = "0.29" ratatui = "0.29"
color-eyre = "0.6" color-eyre = "0.6"
leafwing-input-manager = "0.16" leafwing-input-manager = "0.16"
@ -26,3 +25,6 @@ features = ["bevy_render"]
[dependencies.crossterm] [dependencies.crossterm]
version = "0.28" version = "0.28"
features = ["serde"] features = ["serde"]
[dependencies.bevy_dither_post_process]
git = "https://git.soaos.dev/bevy_dither_post_process"

View File

@ -1,8 +1,5 @@
use bevy::{ use bevy::{
ecs::{ ecs::{component::ComponentId, world::DeferredWorld},
component::ComponentId,
world::DeferredWorld,
},
prelude::*, prelude::*,
render::render_resource::{ render::render_resource::{
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
@ -10,15 +7,41 @@ use bevy::{
}; };
use bevy_dither_post_process::components::DitherPostProcessSettings; use bevy_dither_post_process::components::DitherPostProcessSettings;
use bevy_headless_render::components::HeadlessRenderSource; use bevy_headless_render::components::HeadlessRenderSource;
use ratatui::style::Style;
// TODO: MULTIPLE WINDOWS (probably behind feature flag)
// INFO: need abstraction for launching terminal emulators
//
// /// Structure to refer to a terminal window entity
// #[derive(Clone, Debug)]
// pub enum TerminalWindowRef {
// /// Refers to the primary window created by default in the terminal the command is run in
// Primary,
// /// Direct reference to an terminal window entity
// Entity(Entity),
// }
//
// #[derive(Component, Debug)]
// pub struct TerminalWindow;
/// Marker component for terminal display /// Marker component for terminal display
#[derive(Component, Debug)] #[derive(Component, Debug)]
#[component(on_add = on_add_terminal_display)] #[component(on_add = on_add_terminal_display)]
pub struct TerminalDisplay(pub u32); pub struct TerminalDisplay {
/// Level of dithering performed on image
pub dither_level: u32,
/// Style applied to rendered text
pub style: Style,
}
fn on_add_terminal_display(mut world: DeferredWorld, entity: Entity, _id: ComponentId) { fn on_add_terminal_display(mut world: DeferredWorld, entity: Entity, _id: ComponentId) {
let asset_server = world.get_resource::<AssetServer>().unwrap(); let asset_server = world.get_resource::<AssetServer>().unwrap();
let dither_level = world.entity(entity).get::<TerminalDisplay>().unwrap().0; let dither_level = world
.entity(entity)
.get::<TerminalDisplay>()
.unwrap()
.dither_level;
let terminal_size = crossterm::terminal::size().unwrap(); let terminal_size = crossterm::terminal::size().unwrap();
let size = Extent3d { let size = Extent3d {
@ -52,11 +75,13 @@ fn on_add_terminal_display(mut world: DeferredWorld, entity: Entity, _id: Compon
.commands() .commands()
.entity(entity) .entity(entity)
.insert((headless_render_source, post_process_settings)); .insert((headless_render_source, post_process_settings));
if let Some(mut camera) = world.entity_mut(entity).get_mut::<Camera>() { if let Some(mut camera) = world.entity_mut(entity).get_mut::<Camera>() {
camera.target = image_handle.into(); camera.target = image_handle.into();
} else { } else {
world.commands().entity(entity).insert(Camera { world.commands().entity(entity).insert(Camera {
target: image_handle.into(), target: image_handle.into(),
hdr: true,
clear_color: ClearColorConfig::Custom(Color::LinearRgba(LinearRgba::BLACK)),
..Default::default() ..Default::default()
}); });
} }

View File

@ -21,7 +21,7 @@ impl Default for Terminal {
stdout().execute(EnableMouseCapture).unwrap(); stdout().execute(EnableMouseCapture).unwrap();
stdout() stdout()
.execute(PushKeyboardEnhancementFlags( .execute(PushKeyboardEnhancementFlags(
KeyboardEnhancementFlags::REPORT_EVENT_TYPES, KeyboardEnhancementFlags::all(),
)) ))
.unwrap(); .unwrap();
enable_raw_mode().unwrap(); enable_raw_mode().unwrap();

View File

@ -2,16 +2,15 @@ use bevy::{
prelude::*, prelude::*,
render::render_resource::{Extent3d, TextureFormat}, render::render_resource::{Extent3d, TextureFormat},
}; };
use bevy_headless_render::{components::HeadlessRenderDestination, render_assets::HeadlessRenderSource}; use bevy_headless_render::{
use crossterm::event::Event; components::HeadlessRenderDestination, render_assets::HeadlessRenderSource,
use ratatui::{
style::Stylize,
widgets::{Paragraph, Wrap},
}; };
use crossterm::event::Event;
use ratatui::widgets::{Paragraph, Wrap};
use crate::{input::events::TerminalInputEvent, widgets::components::Widget}; use crate::{input::events::TerminalInputEvent, widgets::components::Widget};
use super::resources::Terminal; use super::{components::TerminalDisplay, resources::Terminal};
const BRAILLE_CODE_MIN: u16 = 0x2800; const BRAILLE_CODE_MIN: u16 = 0x2800;
const BRAILLE_CODE_MAX: u16 = 0x28FF; const BRAILLE_CODE_MAX: u16 = 0x28FF;
@ -25,11 +24,13 @@ const BRAILLE_DOT_BIT_POSITIONS: [u8; 8] = [0, 1, 2, 6, 3, 4, 5, 7];
/// Prints out the contents of a render image to the terminal as braille characters /// Prints out the contents of a render image to the terminal as braille characters
pub fn print_to_terminal( pub fn print_to_terminal(
mut terminal: ResMut<Terminal>, mut terminal: ResMut<Terminal>,
image_exports: Query<&HeadlessRenderDestination>, image_exports: Query<(&TerminalDisplay, &HeadlessRenderDestination)>,
mut widgets: Query<&mut Widget>, mut widgets: Query<&mut Widget>,
) { ) {
for image_export in image_exports.iter() { let display = image_exports.get_single();
let mut image = image_export let mut output_buffer = Vec::<char>::new();
if let Ok((_, image)) = display {
let mut image = image
.0 .0
.lock() .lock()
.expect("Failed to get lock on output texture"); .expect("Failed to get lock on output texture");
@ -44,7 +45,6 @@ pub fn print_to_terminal(
}; };
} }
let mut output_buffer = Vec::<char>::new();
let width = image.width(); let width = image.width();
let height = image.height(); let height = image.height();
let data = &image.data; let data = &image.data;
@ -64,29 +64,31 @@ pub fn print_to_terminal(
output_buffer.push(braille_char(mask)); output_buffer.push(braille_char(mask));
} }
} }
}
let string = output_buffer.into_iter().collect::<String>(); let string = output_buffer.into_iter().collect::<String>();
terminal terminal
.0 .0
.draw(|frame| { .draw(|frame| {
if !string.is_empty() {
frame.render_widget( frame.render_widget(
Paragraph::new(string) Paragraph::new(string)
.white() .style(display.unwrap().0.style)
.wrap(Wrap { trim: true }), .wrap(Wrap { trim: true }),
frame.area(), frame.area(),
); );
}
let mut active_widgets = widgets let mut active_widgets = widgets
.iter_mut() .iter_mut()
.filter(|widget| widget.enabled) .filter(|widget| widget.enabled)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
active_widgets.sort_by(|a, b| a.depth.cmp(&b.depth)); active_widgets.sort_by(|a, b| a.depth.cmp(&b.depth));
for mut widget in active_widgets { for mut widget in active_widgets {
widget.widget.render(frame, frame.area()); widget.widget.render(frame, frame.area());
} }
}) })
.expect("Failed to draw terminal frame"); .expect("Failed to draw terminal frame");
}
} }
/// Utility function to convert a u8 into the corresponding braille character /// Utility function to convert a u8 into the corresponding braille character

View File

@ -104,12 +104,11 @@ impl Plugin for TerminalDisplayPlugin {
} }
} }
fn restore_terminal() -> Result<(), Box<dyn std::error::Error>>{ fn restore_terminal() {
disable_raw_mode()?; let _ = disable_raw_mode();
let mut stdout = stdout(); let mut stdout = stdout();
stdout.execute(PopKeyboardEnhancementFlags)? let _ = stdout.execute(PopKeyboardEnhancementFlags).unwrap()
.execute(DisableMouseCapture)? .execute(DisableMouseCapture).unwrap()
.execute(LeaveAlternateScreen)? .execute(LeaveAlternateScreen).unwrap()
.flush()?; .flush();
Ok(())
} }