This commit is contained in:
parent
ac1589f2b9
commit
ddfd4a7422
5
.cargo/config.toml
Normal file
5
.cargo/config.toml
Normal 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"
|
25
.gitea/workflows/build.yaml
Normal file
25
.gitea/workflows/build.yaml
Normal 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
18
Cargo.lock
generated
@ -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"
|
||||
|
10
Cargo.toml
10
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"
|
||||
|
@ -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()
|
||||
});
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
13
src/lib.rs
13
src/lib.rs
@ -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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user