Source code for Karana.KUtils.MultibodyTUI.terminal

# Copyright (c) 2024-2025 Karana Dynamics Pty Ltd. All rights reserved.
#
# NOTICE TO USER:
#
# This source code and/or documentation (the "Licensed Materials") is
# the confidential and proprietary information of Karana Dynamics Inc.
# Use of these Licensed Materials is governed by the terms and conditions
# of a separate software license agreement between Karana Dynamics and the
# Licensee ("License Agreement"). Unless expressly permitted under that
# agreement, any reproduction, modification, distribution, or disclosure
# of the Licensed Materials, in whole or in part, to any third party
# without the prior written consent of Karana Dynamics is strictly prohibited.
#
# THE LICENSED MATERIALS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
# KARANA DYNAMICS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
# BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND
# FITNESS FOR A PARTICULAR PURPOSE.
#
# IN NO EVENT SHALL KARANA DYNAMICS BE LIABLE FOR ANY DAMAGES WHATSOEVER,
# INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA, OR USE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, WHETHER IN CONTRACT, TORT,
# OR OTHERWISE ARISING OUT OF OR IN CONNECTION WITH THE LICENSED MATERIALS.
#
# U.S. Government End Users: The Licensed Materials are a "commercial item"
# as defined at 48 C.F.R. 2.101, and are provided to the U.S. Government
# only as a commercial end item under the terms of this license.
#
# Any use of the Licensed Materials in individual or commercial software must
# include, in the user documentation and internal source code comments,
# this Notice, Disclaimer, and U.S. Government Use Provision.

from contextlib import contextmanager
import sys
import select
import os
import subprocess
import termios
import signal
import tty
from collections.abc import Callable


[docs] def bold(text: str) -> str: """Apply the bold effect Parameters ---------- text: str The given text Returns ------- str The bolded text """ return f"\033[1m{text}\033[0m"
[docs] def invert(text: str) -> str: """Swap background and foreground colors Parameters ---------- text: str The given text Returns ------- str The inverted text """ return f"\033[7m{text}\033[0m"
[docs] def critical(text: str) -> str: """Style the text for a critical error Parameters ---------- text: str The given text Returns ------- str The stylized text """ # Bold, inverted background and foreground return f"\033[1;7;31m{text}\033[0m"
[docs] def error(text: str) -> str: """Style the text for an error Parameters ---------- text: str The given text Returns ------- str The stylized text """ # Bold red return f"\033[1;31m{text}\033[0m"
[docs] def warn(text: str) -> str: """Style the text for a warning Parameters ---------- text: str The given text Returns ------- str The stylized text """ # Yellow return f"\033[33m{text}\033[0m"
[docs] def info(text: str) -> str: """Style the text for important info Parameters ---------- text: str The given text Returns ------- str The stylized text """ # Cyan return f"\033[36m{text}\033[0m"
[docs] @contextmanager def tui_mode(on_resume: Callable | None = None): """Context manager to configure the terminal for a TUI This primarily handles setting cbreak mode for immediate key handling and restoring the previous mode upon exiting the context. Multiple tui_mode contexts may be nested. Parameters ---------- on_resume: collections.abc.Callable | None Optional callable triggered upon SIGCONT (eg when resuming from Ctrl-Z). """ fd = sys.stdin.fileno() def _handle_sigcont(signum, frame): tty.setcbreak(fd) if on_resume: on_resume() old_settings = termios.tcgetattr(fd) old_handler = signal.getsignal(signal.SIGCONT) try: signal.signal(signal.SIGCONT, _handle_sigcont) tty.setcbreak(fd) yield finally: # Restore previous settings signal.signal(signal.SIGCONT, old_handler) termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
[docs] @contextmanager def normal_mode(on_resume: Callable | None = None): """Context manager to configure the terminal for regular input This can be used to return the terminal to regular interaction. This is could be used for example to return to normal behavior when entering a Python REPL from within a TUI. Parameters ---------- on_resume: collections.abc.Callable | None Optional callable triggered upon SIGCONT (eg when resuming from Ctrl-Z). """ fd = sys.stdin.fileno() def _handle_sigcont(signum, frame): tty.setcbreak(fd) if on_resume: on_resume() old_settings = termios.tcgetattr(fd) old_handler = signal.getsignal(signal.SIGCONT) try: signal.signal(signal.SIGCONT, _handle_sigcont) attrs = termios.tcgetattr(fd) attrs[3] |= termios.ICANON | termios.ECHO termios.tcsetattr(fd, termios.TCSADRAIN, attrs) yield finally: # Restore previous settings signal.signal(signal.SIGCONT, old_handler) termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
[docs] def clear_screen(): # ANSI: Clear screen and move cursor to top-left sys.stdout.write("\x1b[2J\x1b[H") sys.stdout.flush()
[docs] def poll_key(timeout: float = 0.1) -> str: """Poll for a pending key press Parameters ---------- timeout: float Amount of time in seconds to wait for a key press Returns ------- str The pressed key or the empty string if no key was pressed. """ rlist, _, _ = select.select([sys.stdin], [], [], timeout) if rlist: ch1 = sys.stdin.read(1) if ch1 == "\x1b": # Possibly an escape sequence (e.g., arrow key) ch2 = sys.stdin.read(1) if ch2 == "[": ch3 = sys.stdin.read(1) return f"\x1b[{ch3}" return ch1 return ""