Skip to main content

No project description provided

Project description

libTerm

Lightweight Python library for direct terminal control on POSIX systems (Linux/macOS). Works with raw ANSI escape codes, terminal modes, cursor positioning, input handling, and color management — without heavy abstractions.

Quick Start

git clone https://github.com/hoefkensj/libTerm
cd libTerm
python -m venv .venv && source .venv/bin/activate
pip install -e .
python -m libTerm.examples.ex_arrowkeys

Or install directly into your project:

pip install libTerm

What is libTerm?

A direct interface to terminal I/O and ANSI control codes. Useful for:

  • Terminal games (snake, roguelikes, real-time animations)
  • Interactive CLI tools with custom key handling
  • Live dashboards that update without clearing the screen
  • Learning ANSI sequences and POSIX terminal control

Features

  • Terminal modes: Switch between NORMAL (echo, canonical) and CONTROL (raw input, no echo, cursor hidden)
  • Cursor control: Position, move, show/hide, save/restore positions on a stack
  • Raw keyboard input: Non-blocking event polling, read all available input at once
  • Color detection: Query current foreground/background colors from terminal
  • Terminal size: Track terminal width and height, detect resize events
  • Direct ANSI access: Use raw escape codes alongside the API

Basic Usage

from libTerm import Term, Mode, Coord

term = Term()

# Query terminal state
print(f"Size: {term.size.xy}")          # (width, height)
print(f"Cursor at: {term.cursor.xy}")   # (x, y)
print(f"BG color: {term.color.bg}")     # Current background color

Terminal Modes

Switch between normal line-editing and raw control input:

from libTerm import Term, Mode

term = Term()

# Enter raw input mode (no echo, no line buffering, cursor hidden)
term.mode = Mode.CONTROL

# Poll for keyboard events without blocking
if term.stdin.event:
    key = term.stdin.read()
    if key == 'q':
        break

# Restore normal mode (echo on, canonical input, cursor visible)
term.mode = Mode.NORMAL

Keyboard Input (Non-Blocking)

Read raw input one event at a time:

# Check if input is available (non-blocking)
if term.stdin.event:
    key_bytes = term.stdin.read()
    
    # Arrow keys come as ANSI sequences
    if key_bytes == '\x1b[A':  # Up
        print("UP")
    elif key_bytes == '\x1b[B':  # Down
        print("DOWN")
    elif key_bytes == '\x1b[C':  # Right
        print("RIGHT")
    elif key_bytes == '\x1b[D':  # Left
        print("LEFT")

vs. using input():

  • input() blocks and echoes, waits for Enter
  • term.stdin.event + term.stdin.read() is non-blocking, captures individual bytes, allows arrow keys and Ctrl sequences

Cursor Control

Position the cursor, move it, or hide it:

from libTerm import Coord

# Set cursor position (1-indexed)
term.cursor.xy = Coord(10, 5)
print("*")

# Move relative to current position
term.cursor.move.down(3)
term.cursor.move.right(5)
print("*")

# Show / hide cursor
term.cursor.hide()
term.cursor.show()

vs. raw ANSI:

# Without libTerm (raw ANSI):
print("\x1b[5;10H*")  # Move to (10, 5), print *
print("\x1b[3B\x1b[5C*")  # Move down 3, right 5, print *
print("\x1b[?25l")  # Hide cursor
print("\x1b[?25h")  # Show cursor

libTerm makes this discoverable and less error-prone.

Cursor Position Stack

Save and restore cursor positions easily:

term.cursor.store.save()   # Push current position
print("Hello at position 1")

term.cursor.xy = Coord(1, 10)
print("Hello at position 2")

term.cursor.store.undo()   # Pop back to saved position
print("Back to position 1")

Coordinates (Coord)

Immutable coordinate type:

c = Coord(5, 10)
print(c.x, c.y)           # 5, 10
print(c[0], c[1])         # 5, 10 (indexing)
print(c + Coord(2, 3))    # Coord(7, 13)

Terminal Size & Resize Detection

Query size and detect when terminal is resized:

width, height = term.size.xy
print(f"{width} x {height}")

# Detect resize events
if term.size.changed:
    print("Terminal was resized!")

vs. os.get_terminal_size():

  • libTerm tracks resize events
  • Provides a convenient object interface
  • Integrates with the terminal state system

Colors

Query the current foreground and background colors:

fg = term.color.fg
bg = term.color.bg

# Color has R, G, B components (0-255 per channel)
print(f"Background: RGB({bg.R}, {bg.G}, {bg.B})")

Direct ANSI Access

libTerm doesn't hide ANSI. Mix raw codes with the API:

# Use Ansi enum for common sequences
from libTerm import Ansi

print(Ansi.hide)     # Hide cursor
print(Ansi.show)     # Show cursor
print("\x1b[31mRED text\x1b[m")  # Raw ANSI still works

Why Use libTerm?

vs. Curses / ncurses

  • libTerm: Direct, close to ANSI, minimal setup, no system package dependency
  • Curses: Heavier abstraction, window-based model, more complex for simple tasks

Choose libTerm if you want raw terminal access without heavy abstractions.

vs. Manual ANSI Codes

  • Raw ANSI: Error-prone escape sequences, hard to remember, no structure
  • libTerm: Named enums, methods with clear intent, still lets you write raw ANSI

Choose libTerm if you want safety and discoverability alongside low-level control.

vs. Rich / Textual

  • Rich/Textual: High-level TUI framework, great for dashboards, lots of abstraction
  • libTerm: Low-level, minimal, close to the terminal

Choose libTerm if you want to build your own abstractions or learn terminal internals.

Examples

Located in src/libTerm/examples/:

File Shows
ex_basic.py Terminal state inspection
ex_arrowkeys.py Raw arrow key polling
ex_snake_manual.py Keyboard-controlled game
ex_snake_automatic.py Animation without input
ex_colors.py Color detection
ex_buffers.py Alternate screen buffer
ex_printkeys.py Debug: see all key codes

Run an example:

python -m libTerm.examples.ex_arrowkeys

Platform Support

  • Linux: Full support
  • macOS: Full support
  • Windows: Partial (alternate implementations in progress)

Philosophy

libTerm is intentionally low-level:

  • Stay close to ANSI and POSIX
  • Avoid heavy abstractions
  • Let you control the terminal directly
  • Make common tasks simple, complex tasks possible

If you need a full TUI framework, use Rich or Textual. If you want control and simplicity, use libTerm.

Contributing

Pull requests welcome. Report bugs and experimental ideas.


Happy terminal hacking!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

libterm-0.3.6.tar.gz (89.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

libterm-0.3.6-py3-none-any.whl (46.0 kB view details)

Uploaded Python 3

File details

Details for the file libterm-0.3.6.tar.gz.

File metadata

  • Download URL: libterm-0.3.6.tar.gz
  • Upload date:
  • Size: 89.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for libterm-0.3.6.tar.gz
Algorithm Hash digest
SHA256 213ce3532f794169ae3702dfab462a9891d2016084152100709255c1c43f438b
MD5 c2ddd3e3e2fb92b92ba497288eb42dd7
BLAKE2b-256 a6c0134b38a9565f072d41cbf5cd203175182b078d527b68c822660a15082b45

See more details on using hashes here.

File details

Details for the file libterm-0.3.6-py3-none-any.whl.

File metadata

  • Download URL: libterm-0.3.6-py3-none-any.whl
  • Upload date:
  • Size: 46.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for libterm-0.3.6-py3-none-any.whl
Algorithm Hash digest
SHA256 c85b5ba7e9b69b27145940a724a72f1837ac99aa386abc8ee52b999a0d3ff085
MD5 778ba48b61c8f7db2ba2a8f3376c2066
BLAKE2b-256 803b57138fbd3f5af65a0ddd957d5aed483f0b460695c28f60b02c3d6513f8fc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page