Added python bindings.
This commit is contained in:
parent
01612abb5a
commit
a41bce136a
8
Makefile
8
Makefile
@ -35,4 +35,10 @@ lua:
|
|||||||
testlua:
|
testlua:
|
||||||
@cd Lua; make test
|
@cd Lua; make test
|
||||||
|
|
||||||
.PHONY: all, checksyntax, clean, c, testc, lua, testlua
|
python:
|
||||||
|
@cd Python; make
|
||||||
|
|
||||||
|
testpython:
|
||||||
|
@cd Python; make test
|
||||||
|
|
||||||
|
.PHONY: all, checksyntax, clean, c, testc, lua, testlua, python, testpython
|
||||||
|
24
Python/Makefile
Normal file
24
Python/Makefile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
PREFIX=
|
||||||
|
CC ?= gcc
|
||||||
|
O ?= -O2
|
||||||
|
CFLAGS=-std=c99 -D_XOPEN_SOURCE=500 -D_GNU_SOURCE -D_POSIX_C_SOURCE=200809L
|
||||||
|
CWARN=-Wall -Wpedantic -Wextra -Wno-unknown-pragmas -Wno-missing-field-initializers\
|
||||||
|
-Wno-padded -Wsign-conversion -Wno-missing-noreturn -Wno-cast-qual -Wtype-limits
|
||||||
|
#CFLAGS += -fsanitize=address -fno-omit-frame-pointer
|
||||||
|
G=
|
||||||
|
|
||||||
|
all: libbtui.so
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f btui.o libbtui.so
|
||||||
|
|
||||||
|
libbtui.so: btui.o
|
||||||
|
$(CC) $(CFLAGS) $(CWARN) $(G) $(O) -shared $< -o $@
|
||||||
|
|
||||||
|
btui.o: btui.c ../btui.h
|
||||||
|
$(CC) $(CFLAGS) $(CWARN) $(G) $(O) -fPIC -c $< -o $@
|
||||||
|
|
||||||
|
test: libbtui.so
|
||||||
|
python3 test.py
|
||||||
|
|
||||||
|
.PHONY: all, clean, test
|
1
Python/btui.c
Normal file
1
Python/btui.c
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "../btui.h"
|
234
Python/btui.py
Normal file
234
Python/btui.py
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
import ctypes
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
__all__ = ['btui']
|
||||||
|
|
||||||
|
# Load the shared library into c types.
|
||||||
|
libbtui = ctypes.CDLL("./libbtui.so")
|
||||||
|
|
||||||
|
class FILE(ctypes.Structure):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class BTUI_struct(ctypes.Structure):
|
||||||
|
_fields_ = [
|
||||||
|
('in', ctypes.POINTER(FILE)),
|
||||||
|
('out', ctypes.POINTER(FILE)),
|
||||||
|
('width', ctypes.c_int),
|
||||||
|
('height', ctypes.c_int),
|
||||||
|
('size_changed', ctypes.c_int),
|
||||||
|
]
|
||||||
|
|
||||||
|
libbtui.btui_enable.restype = ctypes.POINTER(BTUI_struct)
|
||||||
|
|
||||||
|
attr_t = ctypes.c_longlong
|
||||||
|
BTUI_ATTRS = {
|
||||||
|
"normal": attr_t.in_dll(libbtui, 'BTUI_NORMAL'),
|
||||||
|
"bold": attr_t.in_dll(libbtui, 'BTUI_BOLD'),
|
||||||
|
"faint": attr_t.in_dll(libbtui, 'BTUI_FAINT'),
|
||||||
|
"dim": attr_t.in_dll(libbtui, 'BTUI_FAINT'),
|
||||||
|
"italic": attr_t.in_dll(libbtui, 'BTUI_ITALIC'),
|
||||||
|
"underline": attr_t.in_dll(libbtui, 'BTUI_UNDERLINE'),
|
||||||
|
"blink_slow": attr_t.in_dll(libbtui, 'BTUI_BLINK_SLOW'),
|
||||||
|
"blink_fast": attr_t.in_dll(libbtui, 'BTUI_BLINK_FAST'),
|
||||||
|
"reverse": attr_t.in_dll(libbtui, 'BTUI_REVERSE'),
|
||||||
|
"conceal": attr_t.in_dll(libbtui, 'BTUI_CONCEAL'),
|
||||||
|
"strikethrough": attr_t.in_dll(libbtui, 'BTUI_STRIKETHROUGH'),
|
||||||
|
"fraktur": attr_t.in_dll(libbtui, 'BTUI_FRAKTUR'),
|
||||||
|
"double_underline": attr_t.in_dll(libbtui, 'BTUI_DOUBLE_UNDERLINE'),
|
||||||
|
"no_bold_or_faint": attr_t.in_dll(libbtui, 'BTUI_NO_BOLD_OR_FAINT'),
|
||||||
|
"no_italic_or_fraktur": attr_t.in_dll(libbtui, 'BTUI_NO_ITALIC_OR_FRAKTUR'),
|
||||||
|
"no_underline": attr_t.in_dll(libbtui, 'BTUI_NO_UNDERLINE'),
|
||||||
|
"no_blink": attr_t.in_dll(libbtui, 'BTUI_NO_BLINK'),
|
||||||
|
"no_reverse": attr_t.in_dll(libbtui, 'BTUI_NO_REVERSE'),
|
||||||
|
"no_conceal": attr_t.in_dll(libbtui, 'BTUI_NO_CONCEAL'),
|
||||||
|
"no_strikethrough": attr_t.in_dll(libbtui, 'BTUI_NO_STRIKETHROUGH'),
|
||||||
|
"fg_black": attr_t.in_dll(libbtui, 'BTUI_FG_BLACK'),
|
||||||
|
"fg_red": attr_t.in_dll(libbtui, 'BTUI_FG_RED'),
|
||||||
|
"fg_green": attr_t.in_dll(libbtui, 'BTUI_FG_GREEN'),
|
||||||
|
"fg_yellow": attr_t.in_dll(libbtui, 'BTUI_FG_YELLOW'),
|
||||||
|
"fg_blue": attr_t.in_dll(libbtui, 'BTUI_FG_BLUE'),
|
||||||
|
"fg_magenta": attr_t.in_dll(libbtui, 'BTUI_FG_MAGENTA'),
|
||||||
|
"fg_cyan": attr_t.in_dll(libbtui, 'BTUI_FG_CYAN'),
|
||||||
|
"fg_white": attr_t.in_dll(libbtui, 'BTUI_FG_WHITE'),
|
||||||
|
"fg_normal": attr_t.in_dll(libbtui, 'BTUI_FG_NORMAL'),
|
||||||
|
"bg_black": attr_t.in_dll(libbtui, 'BTUI_BG_BLACK'),
|
||||||
|
"bg_red": attr_t.in_dll(libbtui, 'BTUI_BG_RED'),
|
||||||
|
"bg_green": attr_t.in_dll(libbtui, 'BTUI_BG_GREEN'),
|
||||||
|
"bg_yellow": attr_t.in_dll(libbtui, 'BTUI_BG_YELLOW'),
|
||||||
|
"bg_blue": attr_t.in_dll(libbtui, 'BTUI_BG_BLUE'),
|
||||||
|
"bg_magenta": attr_t.in_dll(libbtui, 'BTUI_BG_MAGENTA'),
|
||||||
|
"bg_cyan": attr_t.in_dll(libbtui, 'BTUI_BG_CYAN'),
|
||||||
|
"bg_white": attr_t.in_dll(libbtui, 'BTUI_BG_WHITE'),
|
||||||
|
"bg_normal": attr_t.in_dll(libbtui, 'BTUI_BG_NORMAL'),
|
||||||
|
"framed": attr_t.in_dll(libbtui, 'BTUI_FRAMED'),
|
||||||
|
"encircled": attr_t.in_dll(libbtui, 'BTUI_ENCIRCLED'),
|
||||||
|
"overlined": attr_t.in_dll(libbtui, 'BTUI_OVERLINED'),
|
||||||
|
}
|
||||||
|
|
||||||
|
BTUI_INVERSE_ATTRS = {
|
||||||
|
"normal": attr_t.in_dll(libbtui, 'BTUI_NORMAL'),
|
||||||
|
"bold": attr_t.in_dll(libbtui, 'BTUI_NO_BOLD_OR_FAINT'),
|
||||||
|
"faint": attr_t.in_dll(libbtui, 'BTUI_NO_BOLD_OR_FAINT'),
|
||||||
|
"dim": attr_t.in_dll(libbtui, 'BTUI_NO_BOLD_OR_FAINT'),
|
||||||
|
"italic": attr_t.in_dll(libbtui, 'BTUI_NO_ITALIC_OR_FRAKTUR'),
|
||||||
|
"underline": attr_t.in_dll(libbtui, 'BTUI_NO_UNDERLINE'),
|
||||||
|
"blink_slow": attr_t.in_dll(libbtui, 'BTUI_NO_BLINK'),
|
||||||
|
"blink_fast": attr_t.in_dll(libbtui, 'BTUI_NO_BLINK'),
|
||||||
|
"reverse": attr_t.in_dll(libbtui, 'BTUI_NO_REVERSE'),
|
||||||
|
"conceal": attr_t.in_dll(libbtui, 'BTUI_NO_CONCEAL'),
|
||||||
|
"strikethrough": attr_t.in_dll(libbtui, 'BTUI_NO_STRIKETHROUGH'),
|
||||||
|
"fraktur": attr_t.in_dll(libbtui, 'BTUI_NO_ITALIC_OR_FRAKTUR'),
|
||||||
|
"double_underline": attr_t.in_dll(libbtui, 'BTUI_NO_UNDERLINE'),
|
||||||
|
"fg_black": attr_t.in_dll(libbtui, 'BTUI_FG_NORMAL'),
|
||||||
|
"fg_red": attr_t.in_dll(libbtui, 'BTUI_FG_NORMAL'),
|
||||||
|
"fg_green": attr_t.in_dll(libbtui, 'BTUI_FG_NORMAL'),
|
||||||
|
"fg_yellow": attr_t.in_dll(libbtui, 'BTUI_FG_NORMAL'),
|
||||||
|
"fg_blue": attr_t.in_dll(libbtui, 'BTUI_FG_NORMAL'),
|
||||||
|
"fg_magenta": attr_t.in_dll(libbtui, 'BTUI_FG_NORMAL'),
|
||||||
|
"fg_cyan": attr_t.in_dll(libbtui, 'BTUI_FG_NORMAL'),
|
||||||
|
"fg_white": attr_t.in_dll(libbtui, 'BTUI_FG_NORMAL'),
|
||||||
|
"fg_normal": attr_t.in_dll(libbtui, 'BTUI_FG_NORMAL'),
|
||||||
|
"bg_black": attr_t.in_dll(libbtui, 'BTUI_BG_NORMAL'),
|
||||||
|
"bg_red": attr_t.in_dll(libbtui, 'BTUI_BG_NORMAL'),
|
||||||
|
"bg_green": attr_t.in_dll(libbtui, 'BTUI_BG_NORMAL'),
|
||||||
|
"bg_yellow": attr_t.in_dll(libbtui, 'BTUI_BG_NORMAL'),
|
||||||
|
"bg_blue": attr_t.in_dll(libbtui, 'BTUI_BG_NORMAL'),
|
||||||
|
"bg_magenta": attr_t.in_dll(libbtui, 'BTUI_BG_NORMAL'),
|
||||||
|
"bg_cyan": attr_t.in_dll(libbtui, 'BTUI_BG_NORMAL'),
|
||||||
|
"bg_white": attr_t.in_dll(libbtui, 'BTUI_BG_NORMAL'),
|
||||||
|
"bg_normal": attr_t.in_dll(libbtui, 'BTUI_BG_NORMAL'),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class BTUI:
|
||||||
|
def __enter__(self):
|
||||||
|
self.enable()
|
||||||
|
|
||||||
|
def __exit__(self, *exc):
|
||||||
|
self.disable()
|
||||||
|
|
||||||
|
def enable(self):
|
||||||
|
self._btui = libbtui.btui_enable()
|
||||||
|
|
||||||
|
def disable(self):
|
||||||
|
libbtui.btui_disable(self._btui)
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def disabled(self):
|
||||||
|
self.disable()
|
||||||
|
try: yield self
|
||||||
|
finally: self.enable()
|
||||||
|
|
||||||
|
def getkey(self, timeout=None):
|
||||||
|
assert self._btui
|
||||||
|
timeout = -1 if timeout is None else int(timeout)
|
||||||
|
mouse_x, mouse_y = ctypes.c_int(-1), ctypes.c_int(-1)
|
||||||
|
key = libbtui.btui_getkey(self._btui, timeout,
|
||||||
|
ctypes.byref(mouse_x), ctypes.byref(mouse_y))
|
||||||
|
buf = ctypes.create_string_buffer(64)
|
||||||
|
libbtui.btui_keyname(key, buf)
|
||||||
|
if mouse_x.value == -1:
|
||||||
|
return buf.value.decode('utf8'), None, None
|
||||||
|
else:
|
||||||
|
return buf.value.decode('utf8'), mouse_x.value, mouse_y.value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def width(self):
|
||||||
|
assert self._btui
|
||||||
|
return self._btui.contents.width
|
||||||
|
|
||||||
|
@property
|
||||||
|
def height(self):
|
||||||
|
assert self._btui
|
||||||
|
return self._btui.contents.height
|
||||||
|
|
||||||
|
def write(self, *args, sep=''):
|
||||||
|
assert self._btui
|
||||||
|
s = sep.join(args)
|
||||||
|
self.write_bytes(bytes(s, 'utf8'))
|
||||||
|
|
||||||
|
def write_bytes(self, b):
|
||||||
|
assert self._btui
|
||||||
|
libbtui.btui_puts(self._btui, b)
|
||||||
|
|
||||||
|
def move(self, x, y):
|
||||||
|
assert self._btui
|
||||||
|
libbtui.btui_move_cursor(self._btui, int(x), int(y))
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
assert self._btui
|
||||||
|
libbtui.btui_flush(self._btui)
|
||||||
|
|
||||||
|
def suspend(self):
|
||||||
|
assert self._btui
|
||||||
|
libbtui.btui_suspend(self._btui)
|
||||||
|
|
||||||
|
def clear(self, mode='screen'):
|
||||||
|
if mode == 'below':
|
||||||
|
self.write_bytes(b'\033[J')
|
||||||
|
elif mode == 'above':
|
||||||
|
self.write_bytes(b'\033[1J')
|
||||||
|
elif mode == 'screen':
|
||||||
|
self.write_bytes(b'\033[2J')
|
||||||
|
elif mode == 'right':
|
||||||
|
self.write_bytes(b'\033[K')
|
||||||
|
elif mode == 'left':
|
||||||
|
self.write_bytes(b'\033[1K')
|
||||||
|
elif mode == 'line':
|
||||||
|
self.write_bytes(b'\033[2K')
|
||||||
|
libbtui.btui_flush(self._btui)
|
||||||
|
|
||||||
|
def set_attributes(self, *attrs):
|
||||||
|
assert self._btui
|
||||||
|
attr_long = ctypes.c_longlong(0)
|
||||||
|
for a in attrs:
|
||||||
|
attr_long.value |= BTUI_ATTRS[a].value
|
||||||
|
libbtui.btui_set_attributes(self._btui, attr_long)
|
||||||
|
|
||||||
|
def unset_attributes(self, *attrs):
|
||||||
|
assert self._btui
|
||||||
|
attr_long = ctypes.c_longlong(0)
|
||||||
|
for a in attrs:
|
||||||
|
attr_long.value |= BTUI_INVERSE_ATTRS[a].value
|
||||||
|
libbtui.btui_set_attributes(self._btui, attr_long)
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def attributes(self, *attrs):
|
||||||
|
self.set_attributes(*attrs)
|
||||||
|
try: yield
|
||||||
|
finally: self.unset_attributes(*attrs)
|
||||||
|
|
||||||
|
def set_fg(self, r, g, b):
|
||||||
|
assert self._btui
|
||||||
|
libbtui.btui_set_fg(self._btui, int(r*255), int(g*255), int(b*255))
|
||||||
|
|
||||||
|
def set_bg(self, r, g, b):
|
||||||
|
assert self._btui
|
||||||
|
libbtui.btui_set_bg(self._btui, int(r*255), int(g*255), int(b*255))
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def fg(self, r, g, b):
|
||||||
|
self.set_fg(r, g, b)
|
||||||
|
try: yield
|
||||||
|
finally: self.set_attributes("fg_normal")
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def bg(self, r, g, b):
|
||||||
|
self.set_bg(r, g, b)
|
||||||
|
try: yield
|
||||||
|
finally: self.set_attributes("bg_normal")
|
||||||
|
|
||||||
|
def outline_box(self, x, y, w, h):
|
||||||
|
assert self._btui
|
||||||
|
libbtui.btui_draw_linebox(self._btui, int(x), int(y), int(w), int(h))
|
||||||
|
|
||||||
|
def fill_box(self, x, y, w, h):
|
||||||
|
assert self._btui
|
||||||
|
libbtui.btui_fill_box(self._btui, int(x), int(y), int(w), int(h))
|
||||||
|
|
||||||
|
def draw_shadow(self, x, y, w, h):
|
||||||
|
assert self._btui
|
||||||
|
libbtui.btui_draw_shadow(self._btui, int(x), int(y), int(w), int(h))
|
||||||
|
|
||||||
|
|
||||||
|
btui = BTUI()
|
39
Python/test.py
Normal file
39
Python/test.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from btui import btui
|
||||||
|
|
||||||
|
with btui:
|
||||||
|
key = None
|
||||||
|
x, y = 1, 1
|
||||||
|
while key != 'q' and key != 'Ctrl-c':
|
||||||
|
if key == '?':
|
||||||
|
with btui.disabled():
|
||||||
|
input("Press enter to continue.")
|
||||||
|
elif key == 'Ctrl-z':
|
||||||
|
btui.suspend()
|
||||||
|
|
||||||
|
btui.clear()
|
||||||
|
|
||||||
|
with btui.fg(.8, .95, .2):
|
||||||
|
btui.outline_box(x, y, 30, 1)
|
||||||
|
btui.move(x, y)
|
||||||
|
btui.write(f"Pressed: {key}")
|
||||||
|
|
||||||
|
with btui.attributes("bg_blue", "fg_black"):
|
||||||
|
title = "Python BTUI Demo"
|
||||||
|
w = len(title)
|
||||||
|
center = (btui.width - w) // 2
|
||||||
|
btui.fill_box(center - 2, 0, w + 4, 3)
|
||||||
|
btui.draw_shadow(center - 2, 0, w + 4, 3)
|
||||||
|
btui.move(center, 1)
|
||||||
|
btui.write(title)
|
||||||
|
|
||||||
|
with btui.attributes("faint"):
|
||||||
|
s = f"Size: ({btui.width}x{btui.height})"
|
||||||
|
btui.move(btui.width-len(s), btui.height-1)
|
||||||
|
btui.write(s)
|
||||||
|
|
||||||
|
key, mx, my = btui.getkey()
|
||||||
|
if mx:
|
||||||
|
x, y = mx, my
|
||||||
|
|
||||||
|
if key == 'Ctrl-c':
|
||||||
|
raise KeyboardInterrupt
|
Loading…
Reference in New Issue
Block a user