Merge pull request 'Add build action' (#2) from 1-add-build-action into master
All checks were successful
Build / Build (push) Successful in 4m4s

Reviewed-on: #2
This commit is contained in:
Silas Bartha 2025-03-22 01:09:08 -04:00
commit 81caa78aa3
10 changed files with 103 additions and 4704 deletions

2
.cargo/config.toml Normal file
View File

@ -0,0 +1,2 @@
[profile.dev.package."*"]
opt-level = 2

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

1
.gitignore vendored
View File

@ -1 +1,2 @@
/target
Cargo.lock

4656
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -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/soaos/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/soaos/bevy_dither_post_process"

View File

@ -3,7 +3,6 @@
[![Crates](https://img.shields.io/crates/v/bevy_terminal_display)](https://crates.io/crates/bevy_terminal_display)
![License](https://img.shields.io/badge/license-0BSD%2FMIT%2FApache-blue.svg)
![Tag](https://img.shields.io/github/v/tag/exvacuum/bevy_terminal_display)
[![Docs](https://img.shields.io/website?url=https%3A%2F%2Fexvacuum.github.io%2Fbevy_terminal_display%2F&label=docs)](https://exvacuum.github.io/bevy_terminal_display)
A (very experimental) plugin for the [Bevy](https://bevyengine.org) engine which allows for rendering to a terminal window.
@ -19,7 +18,7 @@ Features Include:
- Log redirection
## Screenshots
![](https://git.exvacuum.dev/bevy_terminal_display/plain/doc/screenshot.png)
![](https://git.soaos.dev/soaos/bevy_terminal_display/plain/doc/screenshot.png)
## Compatibility
| Crate Version | Bevy Version |
@ -39,7 +38,7 @@ bevy_terminal_display = "0.5"
### Using git URL in Cargo.toml
```toml
[dependencies.bevy_terminal_display]
git = "https://git.exvacuum.dev/bevy_terminal_display"
git = "https://git.soaos.dev/soaos/bevy_terminal_display"
```
## Example Usage

View File

@ -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::<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 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::<Camera>() {
if let Some(mut camera) = world.entity_mut(entity).get_mut::<Camera>() {
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()
});
}

View File

@ -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();

View File

@ -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<Terminal>,
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::<char>::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::<char>::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::<String>();
terminal
.0
.draw(|frame| {
let string = output_buffer.into_iter().collect::<String>();
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::<Vec<_>>();
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::<Vec<_>>();
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

View File

@ -104,12 +104,11 @@ impl Plugin for TerminalDisplayPlugin {
}
}
fn restore_terminal() -> Result<(), Box<dyn std::error::Error>>{
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();
}