1
|
|
|
""" |
2
|
|
|
Implementation of Termbox Python API in tdl. |
3
|
|
|
|
4
|
|
|
See README.md for details. |
5
|
|
|
""" |
6
|
|
|
|
7
|
|
|
import tdl |
8
|
|
|
|
9
|
|
|
""" |
10
|
|
|
Implementation status: |
11
|
|
|
[ ] tdl.init() needs a window, made 132x60 |
12
|
|
|
[ ] Termbox.close() is not implemented, does nothing |
13
|
|
|
[ ] poll_event needs review, because it does not |
14
|
|
|
completely follows the original logic |
15
|
|
|
[ ] peek is stubbed, but not implemented |
16
|
|
|
[ ] not all keys/events are mapped |
17
|
|
|
""" |
18
|
|
|
|
19
|
|
|
class TermboxException(Exception): |
20
|
|
|
def __init__(self, msg): |
21
|
|
|
self.msg = msg |
22
|
|
|
def __str__(self): |
23
|
|
|
return self.msg |
24
|
|
|
|
25
|
|
|
_instance = None |
26
|
|
|
|
27
|
|
|
# keys ---------------------------------- |
28
|
|
|
KEY_F1 = (0xFFFF-0) |
29
|
|
|
KEY_F2 = (0xFFFF-1) |
30
|
|
|
KEY_F3 = (0xFFFF-2) |
31
|
|
|
KEY_F4 = (0xFFFF-3) |
32
|
|
|
KEY_F5 = (0xFFFF-4) |
33
|
|
|
KEY_F6 = (0xFFFF-5) |
34
|
|
|
KEY_F7 = (0xFFFF-6) |
35
|
|
|
KEY_F8 = (0xFFFF-7) |
36
|
|
|
KEY_F9 = (0xFFFF-8) |
37
|
|
|
KEY_F10 = (0xFFFF-9) |
38
|
|
|
KEY_F11 = (0xFFFF-10) |
39
|
|
|
KEY_F12 = (0xFFFF-11) |
40
|
|
|
KEY_INSERT = (0xFFFF-12) |
41
|
|
|
KEY_DELETE = (0xFFFF-13) |
42
|
|
|
|
43
|
|
|
KEY_PGUP = (0xFFFF-16) |
44
|
|
|
KEY_PGDN = (0xFFFF-17) |
45
|
|
|
|
46
|
|
|
KEY_MOUSE_LEFT =(0xFFFF-22) |
47
|
|
|
KEY_MOUSE_RIGHT =(0xFFFF-23) |
48
|
|
|
KEY_MOUSE_MIDDLE =(0xFFFF-24) |
49
|
|
|
KEY_MOUSE_RELEASE =(0xFFFF-25) |
50
|
|
|
KEY_MOUSE_WHEEL_UP =(0xFFFF-26) |
51
|
|
|
KEY_MOUSE_WHEEL_DOWN =(0xFFFF-27) |
52
|
|
|
|
53
|
|
|
KEY_CTRL_TILDE = 0x00 |
54
|
|
|
KEY_CTRL_2 = 0x00 |
55
|
|
|
KEY_CTRL_A = 0x01 |
56
|
|
|
KEY_CTRL_B = 0x02 |
57
|
|
|
KEY_CTRL_C = 0x03 |
58
|
|
|
KEY_CTRL_D = 0x04 |
59
|
|
|
KEY_CTRL_E = 0x05 |
60
|
|
|
KEY_CTRL_F = 0x06 |
61
|
|
|
KEY_CTRL_G = 0x07 |
62
|
|
|
KEY_BACKSPACE = 0x08 |
63
|
|
|
KEY_CTRL_H = 0x08 |
64
|
|
|
KEY_TAB = 0x09 |
65
|
|
|
KEY_CTRL_I = 0x09 |
66
|
|
|
KEY_CTRL_J = 0x0A |
67
|
|
|
KEY_CTRL_K = 0x0B |
68
|
|
|
KEY_CTRL_L = 0x0C |
69
|
|
|
KEY_ENTER = 0x0D |
70
|
|
|
KEY_CTRL_M = 0x0D |
71
|
|
|
KEY_CTRL_N = 0x0E |
72
|
|
|
KEY_CTRL_O = 0x0F |
73
|
|
|
KEY_CTRL_P = 0x10 |
74
|
|
|
KEY_CTRL_Q = 0x11 |
75
|
|
|
KEY_CTRL_R = 0x12 |
76
|
|
|
KEY_CTRL_S = 0x13 |
77
|
|
|
KEY_CTRL_T = 0x14 |
78
|
|
|
KEY_CTRL_U = 0x15 |
79
|
|
|
KEY_CTRL_V = 0x16 |
80
|
|
|
KEY_CTRL_W = 0x17 |
81
|
|
|
KEY_CTRL_X = 0x18 |
82
|
|
|
KEY_CTRL_Y = 0x19 |
83
|
|
|
KEY_CTRL_Z = 0x1A |
84
|
|
|
|
85
|
|
|
|
86
|
|
|
# -- mapped to tdl |
87
|
|
|
KEY_HOME = 'HOME' |
88
|
|
|
KEY_END = 'END' |
89
|
|
|
KEY_ARROW_UP = 'UP' |
90
|
|
|
KEY_ARROW_DOWN = 'DOWN' |
91
|
|
|
KEY_ARROW_LEFT = 'LEFT' |
92
|
|
|
KEY_ARROW_RIGHT = 'RIGHT' |
93
|
|
|
KEY_ESC = 'ESCAPE' |
94
|
|
|
# /-- |
95
|
|
|
|
96
|
|
|
|
97
|
|
|
KEY_CTRL_LSQ_BRACKET = 0x1B |
98
|
|
|
KEY_CTRL_3 = 0x1B |
99
|
|
|
KEY_CTRL_4 = 0x1C |
100
|
|
|
KEY_CTRL_BACKSLASH = 0x1C |
101
|
|
|
KEY_CTRL_5 = 0x1D |
102
|
|
|
KEY_CTRL_RSQ_BRACKET = 0x1D |
103
|
|
|
KEY_CTRL_6 = 0x1E |
104
|
|
|
KEY_CTRL_7 = 0x1F |
105
|
|
|
KEY_CTRL_SLASH = 0x1F |
106
|
|
|
KEY_CTRL_UNDERSCORE = 0x1F |
107
|
|
|
KEY_SPACE = 0x20 |
108
|
|
|
KEY_BACKSPACE2 = 0x7F |
109
|
|
|
KEY_CTRL_8 = 0x7F |
110
|
|
|
|
111
|
|
|
MOD_ALT = 0x01 |
112
|
|
|
|
113
|
|
|
# attributes ---------------------- |
114
|
|
|
|
115
|
|
|
#-- mapped to tdl |
116
|
|
|
DEFAULT = Ellipsis |
117
|
|
|
|
118
|
|
|
BLACK = 0x000000 |
119
|
|
|
RED = 0xFF0000 |
120
|
|
|
GREEN = 0x00FF00 |
121
|
|
|
YELLOW = 0xFFFF00 |
122
|
|
|
BLUE = 0x0000FF |
123
|
|
|
MAGENTA = 0xFF00FF |
124
|
|
|
CYAN = 0x00FFFF |
125
|
|
|
WHITE = 0xFFFFFF |
126
|
|
|
#/-- |
127
|
|
|
|
128
|
|
|
BOLD = 0x10 |
129
|
|
|
UNDERLINE = 0x20 |
130
|
|
|
REVERSE = 0x40 |
131
|
|
|
|
132
|
|
|
# misc ---------------------------- |
133
|
|
|
|
134
|
|
|
HIDE_CURSOR = -1 |
135
|
|
|
INPUT_CURRENT = 0 |
136
|
|
|
INPUT_ESC = 1 |
137
|
|
|
INPUT_ALT = 2 |
138
|
|
|
OUTPUT_CURRENT = 0 |
139
|
|
|
OUTPUT_NORMAL = 1 |
140
|
|
|
OUTPUT_256 = 2 |
141
|
|
|
OUTPUT_216 = 3 |
142
|
|
|
OUTPUT_GRAYSCALE = 4 |
143
|
|
|
|
144
|
|
|
|
145
|
|
|
# -- mapped to tdl |
146
|
|
|
EVENT_KEY = 'KEYDOWN' |
147
|
|
|
# /-- |
148
|
|
|
EVENT_RESIZE = 2 |
149
|
|
|
EVENT_MOUSE = 3 |
150
|
|
|
|
151
|
|
|
class Event: |
152
|
|
|
""" Aggregate for Termbox Event structure """ |
153
|
|
|
type = None |
154
|
|
|
ch = None |
155
|
|
|
key = None |
156
|
|
|
mod = None |
157
|
|
|
width = None |
158
|
|
|
height = None |
159
|
|
|
mousex = None |
160
|
|
|
mousey = None |
161
|
|
|
|
162
|
|
|
def gettuple(self): |
163
|
|
|
return (self.type, self.ch, self.key, self.mod, self.width, self.height, self.mousex, self.mousey) |
164
|
|
|
|
165
|
|
|
class Termbox: |
166
|
|
|
def __init__(self, width=132, height=60): |
167
|
|
|
global _instance |
168
|
|
|
if _instance: |
169
|
|
|
raise TermboxException("It is possible to create only one instance of Termbox") |
170
|
|
|
|
171
|
|
|
try: |
172
|
|
|
self.console = tdl.init(width, height) |
173
|
|
|
except tdl.TDLException as e: |
174
|
|
|
raise TermboxException(e) |
175
|
|
|
|
176
|
|
|
self.e = Event() # cache for event data |
177
|
|
|
|
178
|
|
|
_instance = self |
179
|
|
|
|
180
|
|
|
def __del__(self): |
181
|
|
|
self.close() |
182
|
|
|
|
183
|
|
|
def __exit__(self, *args):#t, value, traceback): |
184
|
|
|
self.close() |
185
|
|
|
|
186
|
|
|
def __enter__(self): |
187
|
|
|
return self |
188
|
|
|
|
189
|
|
|
def close(self): |
190
|
|
|
global _instance |
191
|
|
|
# tb_shutdown() |
192
|
|
|
_instance = None |
193
|
|
|
# TBD, does nothing |
194
|
|
|
|
195
|
|
|
def present(self): |
196
|
|
|
"""Sync state of the internal cell buffer with the terminal. |
197
|
|
|
""" |
198
|
|
|
tdl.flush() |
199
|
|
|
|
200
|
|
|
def change_cell(self, x, y, ch, fg, bg): |
201
|
|
|
"""Change cell in position (x;y). |
202
|
|
|
""" |
203
|
|
|
self.console.draw_char(x, y, ch, fg, bg) |
204
|
|
|
|
205
|
|
|
def width(self): |
206
|
|
|
"""Returns width of the terminal screen. |
207
|
|
|
""" |
208
|
|
|
return self.console.width |
209
|
|
|
|
210
|
|
|
def height(self): |
211
|
|
|
"""Return height of the terminal screen. |
212
|
|
|
""" |
213
|
|
|
return self.console.height |
214
|
|
|
|
215
|
|
|
def clear(self): |
216
|
|
|
"""Clear the internal cell buffer. |
217
|
|
|
""" |
218
|
|
|
self.console.clear() |
219
|
|
|
|
220
|
|
|
def set_cursor(self, x, y): |
221
|
|
|
"""Set cursor position to (x;y). |
222
|
|
|
|
223
|
|
|
Set both arguments to HIDE_CURSOR or use 'hide_cursor' function to hide it. |
224
|
|
|
""" |
225
|
|
|
tb_set_cursor(x, y) |
226
|
|
|
|
227
|
|
|
def hide_cursor(self): |
228
|
|
|
"""Hide cursor. |
229
|
|
|
""" |
230
|
|
|
tb_set_cursor(-1, -1) |
231
|
|
|
|
232
|
|
|
def select_input_mode(self, mode): |
233
|
|
|
"""Select preferred input mode: INPUT_ESC or INPUT_ALT. |
234
|
|
|
|
235
|
|
|
INPUT_CURRENT returns the selected mode without changing anything. |
236
|
|
|
""" |
237
|
|
|
return int(tb_select_input_mode(mode)) |
238
|
|
|
|
239
|
|
|
def select_output_mode(self, mode): |
240
|
|
|
"""Select preferred output mode: one of OUTPUT_* constants. |
241
|
|
|
|
242
|
|
|
OUTPUT_CURRENT returns the selected mode without changing anything. |
243
|
|
|
""" |
244
|
|
|
return int(tb_select_output_mode(mode)) |
245
|
|
|
|
246
|
|
|
def peek_event(self, timeout=0): |
247
|
|
|
"""Wait for an event up to 'timeout' milliseconds and return it. |
248
|
|
|
|
249
|
|
|
Returns None if there was no event and timeout is expired. |
250
|
|
|
Returns a tuple otherwise: (type, unicode character, key, mod, width, height, mousex, mousey). |
251
|
|
|
""" |
252
|
|
|
""" |
253
|
|
|
cdef tb_event e |
254
|
|
|
with self._poll_lock: |
255
|
|
|
with nogil: |
256
|
|
|
result = tb_peek_event(&e, timeout) |
257
|
|
|
assert(result >= 0) |
258
|
|
|
if result == 0: |
259
|
|
|
return None |
260
|
|
|
if e.ch: |
261
|
|
|
uch = unichr(e.ch) |
262
|
|
|
else: |
263
|
|
|
uch = None |
264
|
|
|
""" |
265
|
|
|
pass #return (e.type, uch, e.key, e.mod, e.w, e.h, e.x, e.y) |
266
|
|
|
|
267
|
|
|
def poll_event(self): |
268
|
|
|
"""Wait for an event and return it. |
269
|
|
|
|
270
|
|
|
Returns a tuple: (type, unicode character, key, mod, width, height, mousex, mousey). |
271
|
|
|
""" |
272
|
|
|
""" |
273
|
|
|
cdef tb_event e |
274
|
|
|
with self._poll_lock: |
275
|
|
|
with nogil: |
276
|
|
|
result = tb_poll_event(&e) |
277
|
|
|
assert(result >= 0) |
278
|
|
|
if e.ch: |
279
|
|
|
uch = unichr(e.ch) |
280
|
|
|
else: |
281
|
|
|
uch = None |
282
|
|
|
""" |
283
|
|
|
for e in tdl.event.get(): |
284
|
|
|
# [ ] not all events are passed thru |
285
|
|
|
self.e.type = e.type |
286
|
|
|
if e.type == 'KEYDOWN': |
287
|
|
|
self.e.key = e.key |
288
|
|
|
return self.e.gettuple() |
289
|
|
|
|
290
|
|
|
#return (e.type, uch, e.key, e.mod, e.w, e.h, e.x, e.y) |
291
|
|
|
|