Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ anathema-testutils = { path = "anathema-testutils" }
default = []
profile = ["anathema-runtime/profile", "anathema-widgets/profile", "anathema-backend/profile"]
serde = ["anathema-state/serde", "anathema-store/serde"]
osc = ["anathema-backend/osc"]
# filelog = ["anathema-debug/filelog", "anathema-widgets/filelog", "anathema-runtime/filelog"]

[lints]
Expand Down
1 change: 1 addition & 0 deletions anathema-backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ puffin = { version = "0.19.1", optional = true }
[features]
default = []
profile = ["puffin"]
osc = []

[lints]
workspace = true
54 changes: 54 additions & 0 deletions anathema-backend/src/tui/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use std::fmt;

use anathema_state::Color;
use crossterm::Command;

/// A command that sets the the background color of the entire terminal using OSC 11.
/// This might not work on all terminals. When it is not supported it will do nothing.
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SetTerminalBackground(pub Color);

impl Command for SetTerminalBackground {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
if let Color::Rgb(r, g, b) = self.0 {
// OSC 11 format for RGB is 'rgb:RR/GG/BB' in hexadecimal format
return write!(f, "\x1b]11;rgb:{:02x}/{:02x}/{:02x}\x07", r, g, b);
} else if let Color::AnsiVal(_) = self.0 {
// Ansi values are not supported by OSC 11
Ok(())
} else {
write!(f, "\x1b]11;{}\x07", self.0)
}
}

#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
Ok(())
}
}

/// A command that resets the the background color with OSC 111.
///
/// # Notes
///
/// Commands must be executed/queued for execution otherwise they do nothing.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResetTerminalBackground();

impl Command for ResetTerminalBackground {
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
// Some terminals require a ; at the end of the command
// to reset the background color, while some do not work with it.
let _ = write!(f, "\x1b]111\x07");
write!(f, "\x1b]111;\x07")
}

#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
Ok(())
}
}
11 changes: 11 additions & 0 deletions anathema-backend/src/tui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use std::ops::Add;
use std::time::Duration;

use anathema_geometry::{LocalPos, Pos, Size};
#[cfg(feature = "osc")]
use anathema_state::Color;
use anathema_value_resolver::AttributeStorage;
use anathema_widgets::components::events::Event;
pub use anathema_widgets::{Attributes, Style};
Expand All @@ -22,6 +24,7 @@ use self::events::Events;
use crate::Backend;

mod buffer;
mod commands;
/// Events
pub mod events;
mod screen;
Expand Down Expand Up @@ -144,6 +147,14 @@ impl TuiBackend {
let _ = Screen::disable_raw_mode();
self
}

/// Set the background color of the terminal using OSC 11.
/// Might not work on all terminals. Ansi values (0-255) are not supported.
/// Use RGB or named colors.
#[cfg(feature = "osc")]
pub fn set_background_color(&mut self, color: Color) {
let _ = Screen::set_terminal_background_color(&mut self.output, color);
}
}

impl Backend for TuiBackend {
Expand Down
10 changes: 10 additions & 0 deletions anathema-backend/src/tui/screen.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use std::io::{Result, Write};

use anathema_geometry::{Pos, Size};
use anathema_state::Color;
use anathema_value_resolver::Attributes;
use anathema_widgets::paint::Glyph;
use anathema_widgets::{GlyphMap, Style, WidgetRenderer};
use crossterm::event::EnableMouseCapture;
use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode};
use crossterm::{ExecutableCommand, QueueableCommand, cursor};

use super::commands::{ResetTerminalBackground, SetTerminalBackground};

use super::LocalPos;
use super::buffer::{Buffer, Change, diff, draw_changes};

Expand Down Expand Up @@ -105,6 +108,12 @@ impl Screen {
Ok(())
}

/// Set Terminal background color
pub fn set_terminal_background_color(mut output: impl Write, color: Color) -> Result<()> {
output.queue(SetTerminalBackground(color))?;
Ok(())
}

/// Enter an alternative screen.
/// When using this with stdout it means the output will not persist once the program exits.
pub fn enter_alt_screen(mut output: impl Write) -> Result<()> {
Expand Down Expand Up @@ -134,6 +143,7 @@ impl Screen {
#[cfg(not(target_os = "windows"))]
output.execute(crossterm::event::DisableMouseCapture)?;
output.execute(cursor::Show)?;
output.execute(ResetTerminalBackground())?;
Ok(())
}
}
Expand Down
29 changes: 29 additions & 0 deletions examples/terminal_background.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::fs::read_to_string;

use anathema::backend::Backend;
use anathema::backend::tui::TuiBackend;
use anathema::runtime::Runtime;
use anathema::state::Color;
use anathema::templates::{Document, ToSourceKind};

fn main() {
let template = read_to_string("examples/templates/basic/basic.aml").unwrap();

let doc = Document::new("@index");

let mut backend = TuiBackend::builder()
.enable_alt_screen()
.enable_raw_mode()
.hide_cursor()
.finish()
.unwrap();
backend.finalize();

// This is not set on the builder as it can be called at any time
// to change the background color of the terminal.
backend.set_background_color(Color::Rgb(135, 105, 20));

let mut builder = Runtime::builder(doc, &backend);
builder.template("index", template.to_template()).unwrap();
let _ = builder.finish(&mut backend, |runtime, backend| runtime.run(backend));
}
Loading