Skip to content
59 changes: 59 additions & 0 deletions examples/termbox/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
API of `termbox` Python module implemented in `tld`.

The code here are modified files from
[termbox repository](https://github.com/nsf/termbox/), so please consult
it for the license and other info.


The code consists of two part - `termbox.py` module with API, translation
of official binding form the description below into `tld`:

https://github.com/nsf/termbox/blob/b20c0a11/src/python/termboxmodule.pyx

And the example `termboxtest.py` which is copied verbatim from:

https://github.com/nsf/termbox/blob/b20c0a11/test_termboxmodule.py


### API Mapping Notes

Notes taken while mapping the Termbox class:

tb_init() // initialization console = tdl.init(132, 60)
tb_shutdown() // shutdown

tb_width() // width of the terminal screen console.width
tb_height() // height of the terminal screen console.height

tb_clear() // clear buffer console.clear()
tb_present() // sync internal buffer with terminal tdl.flush()

tb_put_cell()
tb_change_cell() console.draw_char(x, y, ch, fg, bg)
tb_blit() // drawing functions

tb_select_input_mode() // change input mode
tb_peek_event() // peek a keyboard event
tb_poll_event() // wait for a keyboard event * tdl.event.get()


* - means the translation is not direct



init...
tdl doesn't allow to resize window (or rather libtcod)
tb works in existing terminal window and queries it rather than making own

colors...
tdl uses RGB values
tb uses it own constants

event...
tb returns event one by one
tdl return an event iterator


tb Event tdl Event
.type .type
EVENT_KEY KEYDOWN
290 changes: 290 additions & 0 deletions examples/termbox/termbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
"""
Implementation of Termbox Python API in tdl.

See README.md for details.
"""

import tdl

"""
Implementation status:
[ ] tdl.init() needs a window, made 132x60
[ ] Termbox.close() is not implemented, does nothing
[ ] poll_event needs review, because it does not
completely follows the original logic
[ ] peek is stubbed, but not implemented
[ ] not all keys/events are mapped
"""

class TermboxException(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg

_instance = None

# keys ----------------------------------
KEY_F1 = (0xFFFF-0)
KEY_F2 = (0xFFFF-1)
KEY_F3 = (0xFFFF-2)
KEY_F4 = (0xFFFF-3)
KEY_F5 = (0xFFFF-4)
KEY_F6 = (0xFFFF-5)
KEY_F7 = (0xFFFF-6)
KEY_F8 = (0xFFFF-7)
KEY_F9 = (0xFFFF-8)
KEY_F10 = (0xFFFF-9)
KEY_F11 = (0xFFFF-10)
KEY_F12 = (0xFFFF-11)
KEY_INSERT = (0xFFFF-12)
KEY_DELETE = (0xFFFF-13)

KEY_PGUP = (0xFFFF-16)
KEY_PGDN = (0xFFFF-17)

KEY_MOUSE_LEFT =(0xFFFF-22)
KEY_MOUSE_RIGHT =(0xFFFF-23)
KEY_MOUSE_MIDDLE =(0xFFFF-24)
KEY_MOUSE_RELEASE =(0xFFFF-25)
KEY_MOUSE_WHEEL_UP =(0xFFFF-26)
KEY_MOUSE_WHEEL_DOWN =(0xFFFF-27)

KEY_CTRL_TILDE = 0x00
KEY_CTRL_2 = 0x00
KEY_CTRL_A = 0x01
KEY_CTRL_B = 0x02
KEY_CTRL_C = 0x03
KEY_CTRL_D = 0x04
KEY_CTRL_E = 0x05
KEY_CTRL_F = 0x06
KEY_CTRL_G = 0x07
KEY_BACKSPACE = 0x08
KEY_CTRL_H = 0x08
KEY_TAB = 0x09
KEY_CTRL_I = 0x09
KEY_CTRL_J = 0x0A
KEY_CTRL_K = 0x0B
KEY_CTRL_L = 0x0C
KEY_ENTER = 0x0D
KEY_CTRL_M = 0x0D
KEY_CTRL_N = 0x0E
KEY_CTRL_O = 0x0F
KEY_CTRL_P = 0x10
KEY_CTRL_Q = 0x11
KEY_CTRL_R = 0x12
KEY_CTRL_S = 0x13
KEY_CTRL_T = 0x14
KEY_CTRL_U = 0x15
KEY_CTRL_V = 0x16
KEY_CTRL_W = 0x17
KEY_CTRL_X = 0x18
KEY_CTRL_Y = 0x19
KEY_CTRL_Z = 0x1A


# -- mapped to tdl
KEY_HOME = 'HOME'
KEY_END = 'END'
KEY_ARROW_UP = 'UP'
KEY_ARROW_DOWN = 'DOWN'
KEY_ARROW_LEFT = 'LEFT'
KEY_ARROW_RIGHT = 'RIGHT'
KEY_ESC = 'ESCAPE'
# /--


KEY_CTRL_LSQ_BRACKET = 0x1B
KEY_CTRL_3 = 0x1B
KEY_CTRL_4 = 0x1C
KEY_CTRL_BACKSLASH = 0x1C
KEY_CTRL_5 = 0x1D
KEY_CTRL_RSQ_BRACKET = 0x1D
KEY_CTRL_6 = 0x1E
KEY_CTRL_7 = 0x1F
KEY_CTRL_SLASH = 0x1F
KEY_CTRL_UNDERSCORE = 0x1F
KEY_SPACE = 0x20
KEY_BACKSPACE2 = 0x7F
KEY_CTRL_8 = 0x7F

MOD_ALT = 0x01

# attributes ----------------------

#-- mapped to tdl
DEFAULT = Ellipsis

BLACK = 0x000000
RED = 0xFF0000
GREEN = 0x00FF00
YELLOW = 0xFFFF00
BLUE = 0x0000FF
MAGENTA = 0xFF00FF
CYAN = 0x00FFFF
WHITE = 0xFFFFFF
#/--

BOLD = 0x10
UNDERLINE = 0x20
REVERSE = 0x40

# misc ----------------------------

HIDE_CURSOR = -1
INPUT_CURRENT = 0
INPUT_ESC = 1
INPUT_ALT = 2
OUTPUT_CURRENT = 0
OUTPUT_NORMAL = 1
OUTPUT_256 = 2
OUTPUT_216 = 3
OUTPUT_GRAYSCALE = 4


# -- mapped to tdl
EVENT_KEY = 'KEYDOWN'
# /--
EVENT_RESIZE = 2
EVENT_MOUSE = 3

class Event:
""" Aggregate for Termbox Event structure """
type = None
ch = None
key = None
mod = None
width = None
height = None
mousex = None
mousey = None

def gettuple(self):
return (self.type, self.ch, self.key, self.mod, self.width, self.height, self.mousex, self.mousey)

class Termbox:
def __init__(self, width=132, height=60):
global _instance
if _instance:
raise TermboxException("It is possible to create only one instance of Termbox")

try:
self.console = tdl.init(width, height)
except tdl.TDLException as e:
raise TermboxException(e)

self.e = Event() # cache for event data

_instance = self

def __del__(self):
self.close()

def __exit__(self, *args):#t, value, traceback):
self.close()

def __enter__(self):
return self

def close(self):
global _instance
# tb_shutdown()
_instance = None
# TBD, does nothing

def present(self):
"""Sync state of the internal cell buffer with the terminal.
"""
tdl.flush()

def change_cell(self, x, y, ch, fg, bg):
"""Change cell in position (x;y).
"""
self.console.draw_char(x, y, ch, fg, bg)

def width(self):
"""Returns width of the terminal screen.
"""
return self.console.width

def height(self):
"""Return height of the terminal screen.
"""
return self.console.height

def clear(self):
"""Clear the internal cell buffer.
"""
self.console.clear()

def set_cursor(self, x, y):
"""Set cursor position to (x;y).

Set both arguments to HIDE_CURSOR or use 'hide_cursor' function to hide it.
"""
tb_set_cursor(x, y)

def hide_cursor(self):
"""Hide cursor.
"""
tb_set_cursor(-1, -1)

def select_input_mode(self, mode):
"""Select preferred input mode: INPUT_ESC or INPUT_ALT.

INPUT_CURRENT returns the selected mode without changing anything.
"""
return int(tb_select_input_mode(mode))

def select_output_mode(self, mode):
"""Select preferred output mode: one of OUTPUT_* constants.

OUTPUT_CURRENT returns the selected mode without changing anything.
"""
return int(tb_select_output_mode(mode))

def peek_event(self, timeout=0):
"""Wait for an event up to 'timeout' milliseconds and return it.

Returns None if there was no event and timeout is expired.
Returns a tuple otherwise: (type, unicode character, key, mod, width, height, mousex, mousey).
"""
"""
cdef tb_event e
with self._poll_lock:
with nogil:
result = tb_peek_event(&e, timeout)
assert(result >= 0)
if result == 0:
return None
if e.ch:
uch = unichr(e.ch)
else:
uch = None
"""
pass #return (e.type, uch, e.key, e.mod, e.w, e.h, e.x, e.y)

def poll_event(self):
"""Wait for an event and return it.

Returns a tuple: (type, unicode character, key, mod, width, height, mousex, mousey).
"""
"""
cdef tb_event e
with self._poll_lock:
with nogil:
result = tb_poll_event(&e)
assert(result >= 0)
if e.ch:
uch = unichr(e.ch)
else:
uch = None
"""
for e in tdl.event.get():
# [ ] not all events are passed thru
self.e.type = e.type
if e.type == 'KEYDOWN':
self.e.key = e.key
return self.e.gettuple()

#return (e.type, uch, e.key, e.mod, e.w, e.h, e.x, e.y)
Loading