from __future__ import annotations
import os
import sys
import threading
import queue
if os.name == 'nt':
from ConsoleM.Core.windows_driver import WindowsDriver as Driver
else:
from ConsoleM.Core.linux_driver import LinuxDriver as Driver
from ConsoleM.Core.const.keys import Keys
[docs]
class Terminal:
"""A high-level interface for terminal manipulation and input handling.
This class provides methods for:
- Screen manipulation (clear, move cursor)
- Input handling
- Cursor control
- Terminal size detection
Example:
>>> term = Terminal()
>>> term.clear()
>>> term.move_cursor(1, 1)
>>> width, height = term.get_terminal_size()
"""
[docs]
def __init__(self):
"""Initialize a new Terminal instance.
This creates a terminal interface that supports:
- Screen manipulation (clear, move cursor)
- Input handling
- Cursor control
- Terminal size detection
Raises:
NotImplementedError: If running on Windows (not supported yet) or unsupported OS
"""
self.keys = queue.Queue()
self._tr_key_input: threading.Thread | None = None
self.driver = Driver()
[docs]
@staticmethod
def clear_line():
"""Clear the current line from cursor position to the end.
This is useful for updating progress or status lines.
"""
sys.stdout.write("\033[2K")
sys.stdout.flush()
[docs]
@staticmethod
def clear_lines_above(n: int):
"""Clear n lines above the current cursor position.
Args:
n (int): Number of lines to clear above the current position
"""
for _ in range(n):
sys.stdout.write("\033[F")
sys.stdout.write("\033[2K")
sys.stdout.flush()
[docs]
@staticmethod
def create_alternate_screen():
"""Create an alternate screen buffer.
This is useful for full-screen applications like text editors.
The original screen content is preserved and can be restored.
"""
sys.stdout.write("\033[?1049h")
sys.stdout.flush()
[docs]
@staticmethod
def restore_alternate_screen():
"""Restore the original screen after using alternate screen.
This restores the terminal to its previous state.
"""
sys.stdout.write("\033[?1049l")
sys.stdout.flush()
[docs]
@staticmethod
def clear():
"""Clear the entire terminal screen.
This is equivalent to the 'clear' command in the terminal.
"""
sys.stdout.write("\033[2J")
[docs]
@staticmethod
def clear_end_of_line():
"""Clear from cursor position to the end of the current line."""
sys.stdout.write("\033[K")
[docs]
@staticmethod
def move_cursor(x: int, y: int):
"""Move the cursor to the specified position.
Args:
x (int): Column position (1-based)
y (int): Row position (1-based)
Example:
>>> term.move_cursor(1, 1) # Move to top-left corner
"""
sys.stdout.write(f"\033[{y};{x}H")
sys.stdout.flush()
[docs]
@staticmethod
def move_cursor_relative(x: int, y: int):
"""Move the cursor relative to its current position.
Args:
x (int): Number of columns to move (positive = right, negative = left)
y (int): Number of rows to move (positive = down, negative = up)
Example:
>>> term.move_cursor_relative(1, 0) # Move one column right
>>> term.move_cursor_relative(-1, 0) # Move one column left
"""
if x > 0:
sys.stdout.write(f"\033[{x}C")
elif x < 0:
sys.stdout.write(f"\033[{abs(x)}D")
if y > 0:
sys.stdout.write(f"\033[{y}B")
elif y < 0:
sys.stdout.write(f"\033[{abs(y)}A")
sys.stdout.flush()
[docs]
@staticmethod
def write(text: str):
"""Write text to the terminal.
Args:
text (str): Text to write
"""
sys.stdout.write(text)
sys.stdout.flush()
[docs]
def get_key_from_str(self, key: str) -> Keys | None:
"""Get the key objectfrom a string.
Args:
key (str): The key to get
Returns:
Keys: The key or None if not found
"""
return Keys.get(key)
[docs]
def hide_cursor(self):
"""Hide the terminal cursor.
Useful for full-screen applications or when you don't want the cursor visible.
"""
self.driver.hide_cursor()
[docs]
def show_cursor(self):
"""Show the terminal cursor.
Restores cursor visibility after hiding it.
"""
self.driver.show_cursor()
[docs]
def get_cursor_position(self) -> tuple[int, int]:
"""Get the current cursor position.
Returns:
tuple[int, int]: Current (x, y) position of the cursor
"""
return self.driver.get_cursor_position()
[docs]
def get_terminal_size(self) -> tuple[int, int]:
"""Get the current terminal dimensions.
Returns:
tuple[int, int]: Current (width, height) of the terminal
"""
return self.driver.get_terminal_size()
[docs]
def get_key_from_queue(self) -> str:
"""Get the next key press from the input queue.
Returns:
str: The pressed key or special key name (e.g., 'UP_ARROW', 'ENTER')
"""
key: str = self.keys.get()
if key in Keys:
return Keys(key).name
return key