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",
|
"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"
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@ -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"
|
||||||
|
@ -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()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
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>>{
|
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(())
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user