From ddfd4a742268a61e81df9501a9ae579391a69a80 Mon Sep 17 00:00:00 2001 From: Silas Bartha Date: Wed, 19 Mar 2025 07:24:15 -0400 Subject: [PATCH] Add build action --- .cargo/config.toml | 5 ++++ .gitea/workflows/build.yaml | 25 +++++++++++++++++ Cargo.lock | 18 ++++++++----- Cargo.toml | 10 ++++--- src/display/components.rs | 39 ++++++++++++++++++++++----- src/display/resources.rs | 2 +- src/display/systems.rs | 54 +++++++++++++++++++------------------ src/lib.rs | 13 +++++---- 8 files changed, 114 insertions(+), 52 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 .gitea/workflows/build.yaml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..f1d43fb --- /dev/null +++ b/.cargo/config.toml @@ -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" \ No newline at end of file diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..06dad8b --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -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 diff --git a/Cargo.lock b/Cargo.lock index 5ca7dde..757b425 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -332,7 +332,7 @@ dependencies = [ "console_error_panic_hook", "ctrlc", "derive_more 1.0.0", - "downcast-rs", + "downcast-rs 1.2.1", "wasm-bindgen", "web-sys", ] @@ -359,7 +359,7 @@ dependencies = [ "crossbeam-channel", "derive_more 1.0.0", "disqualified", - "downcast-rs", + "downcast-rs 1.2.1", "either", "futures-io", "futures-lite", @@ -472,8 +472,6 @@ dependencies = [ [[package]] name = "bevy_dither_post_process" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33f3318ee0fd8667a911def5aedec7c7e4a36b0d4d7c78de6a8be357c1658132" dependencies = [ "bevy", ] @@ -819,7 +817,7 @@ dependencies = [ "bevy_utils", "derive_more 1.0.0", "disqualified", - "downcast-rs", + "downcast-rs 1.2.1", "erased-serde", "glam", "serde", @@ -870,7 +868,7 @@ dependencies = [ "bytemuck", "codespan-reporting", "derive_more 1.0.0", - "downcast-rs", + "downcast-rs 1.2.1", "encase", "futures-lite", "image", @@ -973,7 +971,7 @@ dependencies = [ "color-eyre", "crossbeam-channel", "crossterm", - "downcast-rs", + "downcast-rs 2.0.1", "leafwing-input-manager", "once_cell", "ratatui", @@ -1793,6 +1791,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "downcast-rs" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf" + [[package]] name = "dpi" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index 1464f0d..52e415d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,16 @@ [package] name = "bevy_terminal_display" -version = "0.6.0" +version = "0.7.0" edition = "2021" 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." -repository = "https://git.exvacuum.dev/bevy_terminal_display" +repository = "https://git.soaos.dev/bevy_terminal_display" [dependencies] crossbeam-channel = "0.5" -downcast-rs = "1.2" +downcast-rs = "2.0" once_cell = "1.19" bevy_headless_render = "0.2" -bevy_dither_post_process = "0.3" ratatui = "0.29" color-eyre = "0.6" leafwing-input-manager = "0.16" @@ -26,3 +25,6 @@ features = ["bevy_render"] [dependencies.crossterm] version = "0.28" features = ["serde"] + +[dependencies.bevy_dither_post_process] +git = "https://git.soaos.dev/bevy_dither_post_process" diff --git a/src/display/components.rs b/src/display/components.rs index 1346f50..132c2e6 100644 --- a/src/display/components.rs +++ b/src/display/components.rs @@ -1,8 +1,5 @@ use bevy::{ - ecs::{ - component::ComponentId, - world::DeferredWorld, - }, + ecs::{component::ComponentId, world::DeferredWorld}, prelude::*, render::render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, @@ -10,15 +7,41 @@ use bevy::{ }; use bevy_dither_post_process::components::DitherPostProcessSettings; 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 #[derive(Component, Debug)] #[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) { let asset_server = world.get_resource::().unwrap(); - let dither_level = world.entity(entity).get::().unwrap().0; + let dither_level = world + .entity(entity) + .get::() + .unwrap() + .dither_level; let terminal_size = crossterm::terminal::size().unwrap(); let size = Extent3d { @@ -52,11 +75,13 @@ fn on_add_terminal_display(mut world: DeferredWorld, entity: Entity, _id: Compon .commands() .entity(entity) .insert((headless_render_source, post_process_settings)); - if let Some(mut camera) = world.entity_mut(entity).get_mut::() { + if let Some(mut camera) = world.entity_mut(entity).get_mut::() { camera.target = image_handle.into(); } else { world.commands().entity(entity).insert(Camera { target: image_handle.into(), + hdr: true, + clear_color: ClearColorConfig::Custom(Color::LinearRgba(LinearRgba::BLACK)), ..Default::default() }); } diff --git a/src/display/resources.rs b/src/display/resources.rs index 7321aea..5651d85 100644 --- a/src/display/resources.rs +++ b/src/display/resources.rs @@ -21,7 +21,7 @@ impl Default for Terminal { stdout().execute(EnableMouseCapture).unwrap(); stdout() .execute(PushKeyboardEnhancementFlags( - KeyboardEnhancementFlags::REPORT_EVENT_TYPES, + KeyboardEnhancementFlags::all(), )) .unwrap(); enable_raw_mode().unwrap(); diff --git a/src/display/systems.rs b/src/display/systems.rs index 706d296..cca2e06 100644 --- a/src/display/systems.rs +++ b/src/display/systems.rs @@ -2,16 +2,15 @@ use bevy::{ prelude::*, render::render_resource::{Extent3d, TextureFormat}, }; -use bevy_headless_render::{components::HeadlessRenderDestination, render_assets::HeadlessRenderSource}; -use crossterm::event::Event; -use ratatui::{ - style::Stylize, - widgets::{Paragraph, Wrap}, +use bevy_headless_render::{ + components::HeadlessRenderDestination, render_assets::HeadlessRenderSource, }; +use crossterm::event::Event; +use ratatui::widgets::{Paragraph, Wrap}; 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_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 pub fn print_to_terminal( mut terminal: ResMut, - image_exports: Query<&HeadlessRenderDestination>, + image_exports: Query<(&TerminalDisplay, &HeadlessRenderDestination)>, mut widgets: Query<&mut Widget>, ) { - for image_export in image_exports.iter() { - let mut image = image_export + let display = image_exports.get_single(); + let mut output_buffer = Vec::::new(); + if let Ok((_, image)) = display { + let mut image = image .0 .lock() .expect("Failed to get lock on output texture"); @@ -44,7 +45,6 @@ pub fn print_to_terminal( }; } - let mut output_buffer = Vec::::new(); let width = image.width(); let height = image.height(); let data = &image.data; @@ -64,29 +64,31 @@ pub fn print_to_terminal( output_buffer.push(braille_char(mask)); } } + } - let string = output_buffer.into_iter().collect::(); - terminal - .0 - .draw(|frame| { + let string = output_buffer.into_iter().collect::(); + terminal + .0 + .draw(|frame| { + if !string.is_empty() { frame.render_widget( Paragraph::new(string) - .white() + .style(display.unwrap().0.style) .wrap(Wrap { trim: true }), frame.area(), ); + } - let mut active_widgets = widgets - .iter_mut() - .filter(|widget| widget.enabled) - .collect::>(); - active_widgets.sort_by(|a, b| a.depth.cmp(&b.depth)); - for mut widget in active_widgets { - widget.widget.render(frame, frame.area()); - } - }) - .expect("Failed to draw terminal frame"); - } + let mut active_widgets = widgets + .iter_mut() + .filter(|widget| widget.enabled) + .collect::>(); + active_widgets.sort_by(|a, b| a.depth.cmp(&b.depth)); + for mut widget in active_widgets { + widget.widget.render(frame, frame.area()); + } + }) + .expect("Failed to draw terminal frame"); } /// Utility function to convert a u8 into the corresponding braille character diff --git a/src/lib.rs b/src/lib.rs index e17f271..cd6519f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,12 +104,11 @@ impl Plugin for TerminalDisplayPlugin { } } -fn restore_terminal() -> Result<(), Box>{ - disable_raw_mode()?; +fn restore_terminal() { + let _ = disable_raw_mode(); let mut stdout = stdout(); - stdout.execute(PopKeyboardEnhancementFlags)? - .execute(DisableMouseCapture)? - .execute(LeaveAlternateScreen)? - .flush()?; - Ok(()) + let _ = stdout.execute(PopKeyboardEnhancementFlags).unwrap() + .execute(DisableMouseCapture).unwrap() + .execute(LeaveAlternateScreen).unwrap() + .flush(); }