add support for ASCII bell escape. Add fruit jam and bell examples

This commit is contained in:
foamyguy 2025-07-09 15:40:34 -05:00
parent 1ea46e46d7
commit dcdca87f70
6 changed files with 173 additions and 5 deletions

View file

@ -5,7 +5,8 @@
`adafruit_color_terminal`
================================================================================
Extension of supports ANSI color escapes for subsets of text
Extension of supports ANSI color escapes for subsets of text and optionally the
ASCII bell escape code.
* Author(s): Tim Cocks
@ -28,6 +29,7 @@ Implementation Notes
__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Color_Terminal.git"
from audiocore import WaveFile
from displayio import Palette, TileGrid
from terminalio import Terminal
from tilepalettemapper import TilePaletteMapper
@ -63,9 +65,20 @@ class ColorTerminal:
:param height: The height of the terminal in characters.
:param custom_palette: A custom palette of colors to use instead of the default ones.
Must contain at least 9 colors.
:param audio_interface: The audio interface to use for playing ASCII bell escape codes.
:param bell_audio_file: The wave audio file to use for the ASCII bell escape codes.
Defaults to beep.wav
"""
def __init__(self, font, width, height, custom_palette=None):
def __init__(
self,
font,
width,
height,
custom_palette=None,
audio_interface=None,
bell_audio_file="/beep.wav",
):
if custom_palette is None:
self.terminal_palette = Palette(9)
self.terminal_palette[0] = 0x000000
@ -99,8 +112,12 @@ class ColorTerminal:
self.cur_color_mapping = [0, 1]
@staticmethod
def parse_ansi_colors(text):
self.audio_interface = audio_interface
if audio_interface is not None:
beep_wave_file = open(bell_audio_file, "rb")
self.beep_wave = WaveFile(beep_wave_file)
def parse_ansi_colors(self, text):
"""
Parse ANSI color escape codes from a string.
@ -188,13 +205,22 @@ class ColorTerminal:
:param s: The string to write.
"""
clean_message, color_map = ColorTerminal.parse_ansi_colors(s)
clean_message, color_map = self.parse_ansi_colors(s)
ordered_color_change_indexes = sorted(color_map.keys())
cur_change_index = 0
if not color_map:
self.terminal.write(s)
if (
"\x07" in s
and self.audio_interface is not None
and not self.audio_interface.playing
):
print("playing beep")
self.audio_interface.play(self.beep_wave)
# while self.audio_interface.playing:
# pass
return
idx = 0
@ -222,6 +248,12 @@ class ColorTerminal:
self.apply_color(cur_slice)
self.terminal.write(cur_slice)
if (
"\x07" in cur_slice
and self.audio_interface is not None
and not self.audio_interface.playing
):
self.audio_interface.play(self.beep_wave)
# index after last can be in the color map if color code is last thing in string
if idx in color_map:

BIN
examples/beep.wav Normal file

Binary file not shown.

View file

@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

View file

@ -0,0 +1,71 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import supervisor
from adafruit_fruitjam import peripherals
from displayio import Group
from terminalio import FONT
from adafruit_color_terminal import ColorTerminal
main_group = Group()
display = supervisor.runtime.display
if display is None or peripherals.get_display_config()[2] < 4:
print(
"To use ColorTerminal there must be an initialized display with a color depth at least 4."
)
print(
"Initializing display to default size 640x480 with color depth 8, ",
"if you do not want this configuration then initialize the display ",
"before running this example.",
)
peripherals.request_display_config(640, 480, 8)
fruitjam_peripherals = peripherals.Peripherals()
fruitjam_peripherals.dac.headphone_output = True
fruitjam_peripherals.dac.configure_clocks(sample_rate=16000, bit_depth=16)
font_bb = FONT.get_bounding_box()
screen_size = (display.width // font_bb[0], display.height // font_bb[1])
terminal = ColorTerminal(
FONT, screen_size[0], screen_size[1], audio_interface=fruitjam_peripherals.audio
)
main_group.append(terminal.tilegrid)
black = chr(27) + "[30m"
red = chr(27) + "[31m"
green = chr(27) + "[32m"
yellow = chr(27) + "[33m"
blue = chr(27) + "[34m"
magenta = chr(27) + "[35m"
cyan = chr(27) + "[36m"
white = chr(27) + "[37m"
reset = chr(27) + "[0m"
message = f"Hello {green}World{reset} {yellow}ANSI\n"
terminal.write(message)
print(message, end="")
message = f"{magenta}Terminal {red}Colors{reset}"
terminal.write(message)
print(message)
display.root_group = main_group
print(terminal.cursor_x, terminal.cursor_y)
move_cursor = chr(27) + "[10;10H"
terminal.write(f" Something {move_cursor}{cyan} Else{reset}")
time.sleep(2)
terminal.write(" beep\x07")
while True:
pass

View file

@ -0,0 +1,59 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import supervisor
from adafruit_fruitjam import peripherals
from displayio import Group
from terminalio import FONT
from adafruit_color_terminal import ColorTerminal
main_group = Group()
display = supervisor.runtime.display
if display is None or peripherals.get_display_config()[2] < 4:
print(
"To use ColorTerminal there must be an initialized display with a color depth at least 4."
)
print(
"Initializing display to default size 640x480 with color depth 8, ",
"if you do not want this configuration then initialize the display ",
"before running this example.",
)
peripherals.request_display_config(640, 480, 8)
font_bb = FONT.get_bounding_box()
screen_size = (display.width // font_bb[0], display.height // font_bb[1])
terminal = ColorTerminal(FONT, screen_size[0], screen_size[1])
main_group.append(terminal.tilegrid)
black = chr(27) + "[30m"
red = chr(27) + "[31m"
green = chr(27) + "[32m"
yellow = chr(27) + "[33m"
blue = chr(27) + "[34m"
magenta = chr(27) + "[35m"
cyan = chr(27) + "[36m"
white = chr(27) + "[37m"
reset = chr(27) + "[0m"
message = f"Hello {green}World{reset} {yellow}ANSI\n"
terminal.write(message)
print(message, end="")
message = f"{magenta}Terminal {red}Colors{reset}"
terminal.write(message)
print(message)
display.root_group = main_group
print(terminal.cursor_x, terminal.cursor_y)
move_cursor = chr(27) + "[10;10H"
terminal.write(f" Something {move_cursor}{cyan} Else{reset}")
while True:
pass

View file

@ -92,6 +92,9 @@ ignore = [
"PLR2004", # magic-value-comparison
"UP030", # format literals
"PLW1514", # unspecified-encoding
"PLR0913", # too many args
"PLR0917", # too many positional args
"PLR6301", # method could be function
]