|
1
|
|
|
""" |
|
2
|
|
|
This module handles user input. |
|
3
|
|
|
|
|
4
|
|
|
To handle user input you will likely want to use the :any:`event.get` |
|
5
|
|
|
function or create a subclass of :any:`event.App`. |
|
6
|
|
|
|
|
7
|
|
|
- :any:`tdl.event.get` iterates over recent events. |
|
8
|
|
|
- :any:`tdl.event.App` passes events to the overridable methods: ``ev_*`` |
|
9
|
|
|
and ``key_*``. |
|
10
|
|
|
|
|
11
|
|
|
But there are other options such as :any:`event.key_wait` and |
|
12
|
|
|
:any:`event.is_window_closed`. |
|
13
|
|
|
|
|
14
|
|
|
A few event attributes are actually string constants. |
|
15
|
|
|
Here's a reference for those: |
|
16
|
|
|
|
|
17
|
|
|
- :any:`Event.type`: |
|
18
|
|
|
'QUIT', 'KEYDOWN', 'KEYUP', 'MOUSEDOWN', 'MOUSEUP', or 'MOUSEMOTION.' |
|
19
|
|
|
- :any:`MouseButtonEvent.button` (found in :any:`MouseDown` and |
|
20
|
|
|
:any:`MouseUp` events): |
|
21
|
|
|
'LEFT', 'MIDDLE', 'RIGHT', 'SCROLLUP', 'SCROLLDOWN' |
|
22
|
|
|
- :any:`KeyEvent.key` (found in :any:`KeyDown` and :any:`KeyUp` events): |
|
23
|
|
|
'NONE', 'ESCAPE', 'BACKSPACE', 'TAB', 'ENTER', 'SHIFT', 'CONTROL', |
|
24
|
|
|
'ALT', 'PAUSE', 'CAPSLOCK', 'PAGEUP', 'PAGEDOWN', 'END', 'HOME', 'UP', |
|
25
|
|
|
'LEFT', 'RIGHT', 'DOWN', 'PRINTSCREEN', 'INSERT', 'DELETE', 'LWIN', |
|
26
|
|
|
'RWIN', 'APPS', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', |
|
27
|
|
|
'KP0', 'KP1', 'KP2', 'KP3', 'KP4', 'KP5', 'KP6', 'KP7', 'KP8', 'KP9', |
|
28
|
|
|
'KPADD', 'KPSUB', 'KPDIV', 'KPMUL', 'KPDEC', 'KPENTER', 'F1', 'F2', |
|
29
|
|
|
'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', |
|
30
|
|
|
'NUMLOCK', 'SCROLLLOCK', 'SPACE', 'CHAR' |
|
31
|
|
|
""" |
|
32
|
|
|
|
|
33
|
|
|
import time as _time |
|
34
|
|
|
|
|
35
|
|
|
from tcod import ffi as _ffi |
|
36
|
|
|
from tcod import lib as _lib |
|
37
|
|
|
|
|
38
|
|
|
import tdl as _tdl |
|
39
|
|
|
from . import style as _style |
|
40
|
|
|
|
|
41
|
|
|
_eventQueue = [] |
|
|
|
|
|
|
42
|
|
|
_pushedEvents = [] |
|
|
|
|
|
|
43
|
|
|
_eventsflushed = False |
|
|
|
|
|
|
44
|
|
|
|
|
45
|
|
|
_mousel = 0 |
|
|
|
|
|
|
46
|
|
|
_mousem = 0 |
|
|
|
|
|
|
47
|
|
|
_mouser = 0 |
|
|
|
|
|
|
48
|
|
|
|
|
49
|
|
|
# this interprets the constants from libtcod and makes a key -> keyname dictionary |
|
|
|
|
|
|
50
|
|
|
def _parseKeyNames(lib): |
|
|
|
|
|
|
51
|
|
|
""" |
|
52
|
|
|
returns a dictionary mapping of human readable key names to their keycodes |
|
53
|
|
|
this parses constants with the names of K_* and makes code=name pairs |
|
54
|
|
|
this is for KeyEvent.key variable and that enables things like: |
|
55
|
|
|
if (event.key == 'PAGEUP'): |
|
56
|
|
|
""" |
|
57
|
|
|
_keyNames = {} |
|
|
|
|
|
|
58
|
|
|
for attr in dir(lib): # from the modules variables |
|
59
|
|
|
if attr[:6] == 'TCODK_': # get the K_* constants |
|
60
|
|
|
_keyNames[getattr(lib, attr)] = attr[6:] # and make CODE=NAME pairs |
|
61
|
|
|
return _keyNames |
|
62
|
|
|
|
|
63
|
|
|
_keyNames = _parseKeyNames(_lib) |
|
|
|
|
|
|
64
|
|
|
|
|
65
|
|
|
class Event(object): |
|
66
|
|
|
"""Base Event class. |
|
67
|
|
|
|
|
68
|
|
|
You can easily subclass this to make your own events. Be sure to set |
|
69
|
|
|
the class attribute L{Event.type} for it to be passed to a custom |
|
70
|
|
|
:any:`App` ev_* method. |
|
71
|
|
|
""" |
|
72
|
|
|
type = None |
|
73
|
|
|
"""String constant representing the type of event. |
|
74
|
|
|
|
|
75
|
|
|
The :any:`App` ev_* methods depend on this attribute. |
|
76
|
|
|
|
|
77
|
|
|
Can be: 'QUIT', 'KEYDOWN', 'KEYUP', 'MOUSEDOWN', 'MOUSEUP', |
|
78
|
|
|
or 'MOUSEMOTION.' |
|
79
|
|
|
""" |
|
80
|
|
|
|
|
81
|
|
|
def __repr__(self): |
|
82
|
|
|
"""List an events public attributes when printed. |
|
83
|
|
|
""" |
|
84
|
|
|
attrdict = {} |
|
85
|
|
|
for varname in dir(self): |
|
86
|
|
|
if '_' == varname[0]: |
|
87
|
|
|
continue |
|
88
|
|
|
attrdict[varname] = self.__getattribute__(varname) |
|
89
|
|
|
return '%s Event %s' % (self.__class__.__name__, repr(attrdict)) |
|
90
|
|
|
|
|
91
|
|
|
class Quit(Event): |
|
92
|
|
|
"""Fired when the window is closed by the user. |
|
93
|
|
|
""" |
|
94
|
|
|
__slots__ = () |
|
95
|
|
|
type = 'QUIT' |
|
96
|
|
|
|
|
97
|
|
|
class KeyEvent(Event): |
|
98
|
|
|
"""Base class for key events.""" |
|
99
|
|
|
|
|
100
|
|
|
def __init__(self, key='', char='', text='', shift=False, |
|
101
|
|
|
left_alt=False, right_alt=False, |
|
102
|
|
|
left_control=False, right_control=False, |
|
103
|
|
|
left_meta=False, right_meta=False): |
|
104
|
|
|
# Convert keycodes into string, but use string if passed |
|
105
|
|
|
self.key = key if isinstance(key, str) else _keyNames[key] |
|
106
|
|
|
"""Text: Human readable names of the key pressed. |
|
107
|
|
|
Non special characters will show up as 'CHAR'. |
|
108
|
|
|
|
|
109
|
|
|
Can be one of |
|
110
|
|
|
'NONE', 'ESCAPE', 'BACKSPACE', 'TAB', 'ENTER', 'SHIFT', 'CONTROL', |
|
111
|
|
|
'ALT', 'PAUSE', 'CAPSLOCK', 'PAGEUP', 'PAGEDOWN', 'END', 'HOME', 'UP', |
|
112
|
|
|
'LEFT', 'RIGHT', 'DOWN', 'PRINTSCREEN', 'INSERT', 'DELETE', 'LWIN', |
|
113
|
|
|
'RWIN', 'APPS', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', |
|
114
|
|
|
'KP0', 'KP1', 'KP2', 'KP3', 'KP4', 'KP5', 'KP6', 'KP7', 'KP8', 'KP9', |
|
115
|
|
|
'KPADD', 'KPSUB', 'KPDIV', 'KPMUL', 'KPDEC', 'KPENTER', 'F1', 'F2', |
|
116
|
|
|
'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', |
|
117
|
|
|
'NUMLOCK', 'SCROLLLOCK', 'SPACE', 'CHAR' |
|
118
|
|
|
|
|
119
|
|
|
For the actual character instead of 'CHAR' use :any:`keychar`. |
|
120
|
|
|
""" |
|
121
|
|
|
self.char = char.replace('\x00', '') # change null to empty string |
|
122
|
|
|
"""Text: A single character string of the letter or symbol pressed. |
|
123
|
|
|
|
|
124
|
|
|
Special characters like delete and return are not cross-platform. |
|
125
|
|
|
L{key} or L{keychar} should be used instead for special keys. |
|
126
|
|
|
Characters are also case sensitive. |
|
127
|
|
|
""" |
|
128
|
|
|
# get the best out of self.key and self.char |
|
129
|
|
|
self.keychar = self.char if self.key == 'CHAR' else self.key |
|
130
|
|
|
"""Similar to L{key} but returns a case sensitive letter or symbol |
|
131
|
|
|
instead of 'CHAR'. |
|
132
|
|
|
|
|
133
|
|
|
This variable makes available the widest variety of symbols and should |
|
134
|
|
|
be used for key-mappings or anywhere where a narrower sample of keys |
|
135
|
|
|
isn't needed. |
|
136
|
|
|
""" |
|
137
|
|
|
self.text = text |
|
138
|
|
|
|
|
139
|
|
|
self.left_alt = self.leftAlt = bool(left_alt) |
|
|
|
|
|
|
140
|
|
|
"""bool:""" |
|
141
|
|
|
self.right_alt = self.rightAlt = bool(right_alt) |
|
|
|
|
|
|
142
|
|
|
"""bool:""" |
|
143
|
|
|
self.left_control = self.leftCtrl = bool(left_control) |
|
|
|
|
|
|
144
|
|
|
"""bool:""" |
|
145
|
|
|
self.right_control = self.rightCtrl = bool(right_control) |
|
|
|
|
|
|
146
|
|
|
"""bool:""" |
|
147
|
|
|
self.shift = bool(shift) |
|
148
|
|
|
"""bool: True if shift was held down during this event.""" |
|
149
|
|
|
self.alt = self.left_alt or self.right_alt |
|
150
|
|
|
"""bool: True if alt was held down during this event.""" |
|
151
|
|
|
self.control = self.left_control or self.right_control |
|
152
|
|
|
"""bool: True if control was held down during this event.""" |
|
153
|
|
|
self.left_meta = bool(left_meta) |
|
154
|
|
|
self.right_meta = bool(right_meta) |
|
155
|
|
|
self.meta = self.left_meta or self.right_meta |
|
156
|
|
|
|
|
157
|
|
|
def __repr__(self): |
|
158
|
|
|
parameters = [] |
|
159
|
|
|
for attr in ('key', 'char', 'text', 'shift', |
|
160
|
|
|
'left_alt', 'right_alt', |
|
161
|
|
|
'left_control', 'right_control', |
|
162
|
|
|
'left_meta', 'right_meta'): |
|
163
|
|
|
value = getattr(self, attr) |
|
164
|
|
|
if value: |
|
165
|
|
|
parameters.append('%s=%r' % (attr, value)) |
|
166
|
|
|
return '%s(%s)' % (self.__class__.__name__, ', '.join(parameters)) |
|
167
|
|
|
|
|
168
|
|
|
class KeyDown(KeyEvent): |
|
169
|
|
|
"""Fired when the user presses a key on the keyboard or a key repeats. |
|
170
|
|
|
""" |
|
171
|
|
|
type = 'KEYDOWN' |
|
172
|
|
|
|
|
173
|
|
|
class KeyUp(KeyEvent): |
|
174
|
|
|
"""Fired when the user releases a key on the keyboard. |
|
175
|
|
|
""" |
|
176
|
|
|
type = 'KEYUP' |
|
177
|
|
|
|
|
178
|
|
|
_mouseNames = {1: 'LEFT', 2: 'MIDDLE', 3: 'RIGHT', 4: 'SCROLLUP', 5: 'SCROLLDOWN'} |
|
|
|
|
|
|
179
|
|
|
class MouseButtonEvent(Event): |
|
180
|
|
|
"""Base class for mouse button events.""" |
|
181
|
|
|
|
|
182
|
|
|
def __init__(self, button, pos, cell): |
|
183
|
|
|
self.button = _mouseNames[button] |
|
184
|
|
|
"""Text: Can be one of |
|
185
|
|
|
'LEFT', 'MIDDLE', 'RIGHT', 'SCROLLUP', 'SCROLLDOWN' |
|
186
|
|
|
""" |
|
187
|
|
|
self.pos = pos |
|
188
|
|
|
"""Tuple[int, int]: (x, y) position of the mouse on the screen.""" |
|
189
|
|
|
self.cell = cell |
|
190
|
|
|
"""Tuple[int, int]: (x, y) position of the mouse snapped to a cell on |
|
191
|
|
|
the root console |
|
192
|
|
|
""" |
|
193
|
|
|
|
|
194
|
|
|
class MouseDown(MouseButtonEvent): |
|
195
|
|
|
"""Fired when a mouse button is pressed.""" |
|
196
|
|
|
__slots__ = () |
|
197
|
|
|
type = 'MOUSEDOWN' |
|
198
|
|
|
|
|
199
|
|
|
class MouseUp(MouseButtonEvent): |
|
200
|
|
|
"""Fired when a mouse button is released.""" |
|
201
|
|
|
__slots__ = () |
|
202
|
|
|
type = 'MOUSEUP' |
|
203
|
|
|
|
|
204
|
|
|
class MouseMotion(Event): |
|
205
|
|
|
"""Fired when the mouse is moved.""" |
|
206
|
|
|
type = 'MOUSEMOTION' |
|
207
|
|
|
|
|
208
|
|
|
def __init__(self, pos, cell, motion, cellmotion): |
|
209
|
|
|
self.pos = pos |
|
210
|
|
|
"""(x, y) position of the mouse on the screen. |
|
211
|
|
|
type: (int, int)""" |
|
212
|
|
|
self.cell = cell |
|
213
|
|
|
"""(x, y) position of the mouse snapped to a cell on the root console. |
|
214
|
|
|
type: (int, int)""" |
|
215
|
|
|
self.motion = motion |
|
216
|
|
|
"""(x, y) motion of the mouse on the screen. |
|
217
|
|
|
type: (int, int)""" |
|
218
|
|
|
self.cellmotion = cellmotion |
|
219
|
|
|
"""(x, y) mostion of the mouse moving over cells on the root console. |
|
220
|
|
|
type: (int, int)""" |
|
221
|
|
|
|
|
222
|
|
|
class App(object): |
|
223
|
|
|
""" |
|
224
|
|
|
Application framework. |
|
225
|
|
|
|
|
226
|
|
|
- ev_*: Events are passed to methods based on their :any:`Event.type` |
|
227
|
|
|
attribute. |
|
228
|
|
|
If an event type is 'KEYDOWN' the ev_KEYDOWN method will be called |
|
229
|
|
|
with the event instance as a parameter. |
|
230
|
|
|
|
|
231
|
|
|
- key_*: When a key is pressed another method will be called based on the |
|
232
|
|
|
:any:`KeyEvent.key` attribute. For example the 'ENTER' key will call |
|
233
|
|
|
key_ENTER with the associated :any:`KeyDown` event as its parameter. |
|
234
|
|
|
|
|
235
|
|
|
- :any:`update`: This method is called every loop. It is passed a single |
|
236
|
|
|
parameter detailing the time in seconds since the last update |
|
237
|
|
|
(often known as deltaTime.) |
|
238
|
|
|
|
|
239
|
|
|
You may want to call drawing routines in this method followed by |
|
240
|
|
|
:any:`tdl.flush`. |
|
241
|
|
|
|
|
242
|
|
|
""" |
|
243
|
|
|
__slots__ = ('__running', '__prevTime') |
|
244
|
|
|
|
|
245
|
|
|
def ev_QUIT(self, event): |
|
|
|
|
|
|
246
|
|
|
"""Unless overridden this method raises a SystemExit exception closing |
|
247
|
|
|
the program.""" |
|
248
|
|
|
raise SystemExit() |
|
249
|
|
|
|
|
250
|
|
|
def ev_KEYDOWN(self, event): |
|
|
|
|
|
|
251
|
|
|
"""Override this method to handle a :any:`KeyDown` event.""" |
|
252
|
|
|
|
|
253
|
|
|
def ev_KEYUP(self, event): |
|
|
|
|
|
|
254
|
|
|
"""Override this method to handle a :any:`KeyUp` event.""" |
|
255
|
|
|
|
|
256
|
|
|
def ev_MOUSEDOWN(self, event): |
|
|
|
|
|
|
257
|
|
|
"""Override this method to handle a :any:`MouseDown` event.""" |
|
258
|
|
|
|
|
259
|
|
|
def ev_MOUSEUP(self, event): |
|
|
|
|
|
|
260
|
|
|
"""Override this method to handle a :any:`MouseUp` event.""" |
|
261
|
|
|
|
|
262
|
|
|
def ev_MOUSEMOTION(self, event): |
|
|
|
|
|
|
263
|
|
|
"""Override this method to handle a :any:`MouseMotion` event.""" |
|
264
|
|
|
|
|
265
|
|
|
def update(self, deltaTime): |
|
|
|
|
|
|
266
|
|
|
"""Override this method to handle per frame logic and drawing. |
|
267
|
|
|
|
|
268
|
|
|
Args: |
|
269
|
|
|
deltaTime (float): |
|
270
|
|
|
This parameter tells the amount of time passed since |
|
271
|
|
|
the last call measured in seconds as a floating point |
|
272
|
|
|
number. |
|
273
|
|
|
|
|
274
|
|
|
You can use this variable to make your program |
|
275
|
|
|
frame rate independent. |
|
276
|
|
|
Use this parameter to adjust the speed of motion, |
|
277
|
|
|
timers, and other game logic. |
|
278
|
|
|
""" |
|
279
|
|
|
pass |
|
280
|
|
|
|
|
281
|
|
|
def suspend(self): |
|
282
|
|
|
"""When called the App will begin to return control to where |
|
283
|
|
|
:any:`App.run` was called. |
|
284
|
|
|
|
|
285
|
|
|
Some further events are processed and the :any:`App.update` method |
|
286
|
|
|
will be called one last time before exiting |
|
287
|
|
|
(unless suspended during a call to :any:`App.update`.) |
|
288
|
|
|
""" |
|
289
|
|
|
self.__running = False |
|
|
|
|
|
|
290
|
|
|
|
|
291
|
|
|
def run(self): |
|
292
|
|
|
"""Delegate control over to this App instance. This function will |
|
293
|
|
|
process all events and send them to the special methods ev_* and key_*. |
|
294
|
|
|
|
|
295
|
|
|
A call to :any:`App.suspend` will return the control flow back to where |
|
296
|
|
|
this function is called. And then the App can be run again. |
|
297
|
|
|
But a single App instance can not be run multiple times simultaneously. |
|
298
|
|
|
""" |
|
299
|
|
|
if getattr(self, '_App__running', False): |
|
300
|
|
|
raise _tdl.TDLError('An App can not be run multiple times simultaneously') |
|
|
|
|
|
|
301
|
|
|
self.__running = True |
|
|
|
|
|
|
302
|
|
|
while self.__running: |
|
303
|
|
|
self.runOnce() |
|
304
|
|
|
|
|
305
|
|
|
def run_once(self): |
|
306
|
|
|
"""Pump events to this App instance and then return. |
|
307
|
|
|
|
|
308
|
|
|
This works in the way described in :any:`App.run` except it immediately |
|
309
|
|
|
returns after the first :any:`update` call. |
|
310
|
|
|
|
|
311
|
|
|
Having multiple :any:`App` instances and selectively calling runOnce on |
|
312
|
|
|
them is a decent way to create a state machine. |
|
313
|
|
|
""" |
|
314
|
|
|
if not hasattr(self, '_App__prevTime'): |
|
315
|
|
|
self.__prevTime = _time.clock() # initiate __prevTime |
|
|
|
|
|
|
316
|
|
|
for event in get(): |
|
317
|
|
|
if event.type: # exclude custom events with a blank type variable |
|
318
|
|
|
# call the ev_* methods |
|
319
|
|
|
method = 'ev_%s' % event.type # ev_TYPE |
|
320
|
|
|
getattr(self, method)(event) |
|
321
|
|
|
if event.type == 'KEYDOWN': |
|
322
|
|
|
# call the key_* methods |
|
323
|
|
|
method = 'key_%s' % event.key # key_KEYNAME |
|
324
|
|
|
if hasattr(self, method): # silently exclude undefined methods |
|
325
|
|
|
getattr(self, method)(event) |
|
326
|
|
|
newTime = _time.clock() |
|
|
|
|
|
|
327
|
|
|
self.update(newTime - self.__prevTime) |
|
328
|
|
|
self.__prevTime = newTime |
|
|
|
|
|
|
329
|
|
|
#_tdl.flush() |
|
330
|
|
|
|
|
331
|
|
|
def _processEvents(): |
|
|
|
|
|
|
332
|
|
|
"""Flushes the event queue from libtcod into the global list _eventQueue""" |
|
333
|
|
|
global _mousel, _mousem, _mouser, _eventsflushed, _pushedEvents |
|
|
|
|
|
|
334
|
|
|
_eventsflushed = True |
|
335
|
|
|
events = _pushedEvents # get events from event.push |
|
336
|
|
|
_pushedEvents = [] # then clear the pushed events queue |
|
337
|
|
|
|
|
338
|
|
|
mouse = _ffi.new('TCOD_mouse_t *') |
|
339
|
|
|
libkey = _ffi.new('TCOD_key_t *') |
|
340
|
|
|
while 1: |
|
341
|
|
|
libevent = _lib.TCOD_sys_check_for_event(_lib.TCOD_EVENT_ANY, libkey, mouse) |
|
|
|
|
|
|
342
|
|
|
if not libevent: # no more events from libtcod |
|
343
|
|
|
break |
|
344
|
|
|
|
|
345
|
|
|
#if mouse.dx or mouse.dy: |
|
346
|
|
|
if libevent & _lib.TCOD_EVENT_MOUSE_MOVE: |
|
347
|
|
|
events.append(MouseMotion((mouse.x, mouse.y), |
|
348
|
|
|
(mouse.cx, mouse.cy), |
|
349
|
|
|
(mouse.dx, mouse.dy), |
|
350
|
|
|
(mouse.dcx, mouse.dcy))) |
|
351
|
|
|
|
|
352
|
|
|
mousepos = ((mouse.x, mouse.y), (mouse.cx, mouse.cy)) |
|
353
|
|
|
|
|
354
|
|
|
for oldstate, newstate, released, button in \ |
|
355
|
|
|
zip((_mousel, _mousem, _mouser), |
|
356
|
|
|
(mouse.lbutton, mouse.mbutton, mouse.rbutton), |
|
357
|
|
|
(mouse.lbutton_pressed, mouse.mbutton_pressed, |
|
358
|
|
|
mouse.rbutton_pressed), |
|
359
|
|
|
(1, 2, 3)): |
|
360
|
|
|
if released: |
|
361
|
|
|
if not oldstate: |
|
362
|
|
|
events.append(MouseDown(button, *mousepos)) |
|
363
|
|
|
events.append(MouseUp(button, *mousepos)) |
|
364
|
|
|
if newstate: |
|
365
|
|
|
events.append(MouseDown(button, *mousepos)) |
|
366
|
|
|
elif newstate and not oldstate: |
|
367
|
|
|
events.append(MouseDown(button, *mousepos)) |
|
368
|
|
|
|
|
369
|
|
|
if mouse.wheel_up: |
|
370
|
|
|
events.append(MouseDown(4, *mousepos)) |
|
371
|
|
|
if mouse.wheel_down: |
|
372
|
|
|
events.append(MouseDown(5, *mousepos)) |
|
373
|
|
|
|
|
374
|
|
|
_mousel = mouse.lbutton |
|
375
|
|
|
_mousem = mouse.mbutton |
|
376
|
|
|
_mouser = mouse.rbutton |
|
377
|
|
|
|
|
378
|
|
|
if libkey.vk == _lib.TCODK_NONE: |
|
379
|
|
|
break |
|
380
|
|
|
if libkey.pressed: |
|
381
|
|
|
keyevent = KeyDown |
|
382
|
|
|
else: |
|
383
|
|
|
keyevent = KeyUp |
|
384
|
|
|
|
|
385
|
|
|
events.append( |
|
386
|
|
|
keyevent( |
|
387
|
|
|
libkey.vk, |
|
388
|
|
|
libkey.c.decode('ascii', errors='ignore'), |
|
389
|
|
|
_ffi.string(libkey.text).decode('utf-8'), |
|
390
|
|
|
libkey.shift, |
|
391
|
|
|
libkey.lalt, |
|
392
|
|
|
libkey.ralt, |
|
393
|
|
|
libkey.lctrl, |
|
394
|
|
|
libkey.rctrl, |
|
395
|
|
|
libkey.lmeta, |
|
396
|
|
|
libkey.rmeta, |
|
397
|
|
|
) |
|
398
|
|
|
) |
|
399
|
|
|
|
|
400
|
|
|
if _lib.TCOD_console_is_window_closed(): |
|
401
|
|
|
events.append(Quit()) |
|
402
|
|
|
|
|
403
|
|
|
_eventQueue.extend(events) |
|
404
|
|
|
|
|
405
|
|
|
def get(): |
|
406
|
|
|
"""Flushes the event queue and returns the list of events. |
|
407
|
|
|
|
|
408
|
|
|
This function returns :any:`Event` objects that can be identified by their |
|
409
|
|
|
type attribute or their class. |
|
410
|
|
|
|
|
411
|
|
|
Returns: Iterator[Type[Event]]: An iterable of Events or anything |
|
412
|
|
|
put in a :any:`push` call. |
|
413
|
|
|
|
|
414
|
|
|
If the iterator is deleted or otherwise interrupted before finishing |
|
415
|
|
|
the excess items are preserved for the next call. |
|
416
|
|
|
""" |
|
417
|
|
|
_processEvents() |
|
418
|
|
|
return _event_generator() |
|
419
|
|
|
|
|
420
|
|
|
def _event_generator(): |
|
|
|
|
|
|
421
|
|
|
while _eventQueue: |
|
422
|
|
|
# if there is an interruption the rest of the events stay untouched |
|
423
|
|
|
# this means you can break out of a event.get loop without losing |
|
424
|
|
|
# the leftover events |
|
425
|
|
|
yield(_eventQueue.pop(0)) |
|
|
|
|
|
|
426
|
|
|
raise StopIteration() |
|
427
|
|
|
|
|
428
|
|
|
|
|
429
|
|
|
def wait(timeout=None, flush=True): |
|
430
|
|
|
"""Wait for an event. |
|
431
|
|
|
|
|
432
|
|
|
Args: |
|
433
|
|
|
timeout (Optional[int]): The time in seconds that this function will |
|
434
|
|
|
wait before giving up and returning None. |
|
435
|
|
|
|
|
436
|
|
|
With the default value of None, this will block forever. |
|
437
|
|
|
flush (bool): If True a call to :any:`tdl.flush` will be made before |
|
438
|
|
|
listening for events. |
|
439
|
|
|
|
|
440
|
|
|
Returns: Type[Event]: An event, or None if the function |
|
441
|
|
|
has timed out. |
|
442
|
|
|
Anything added via :any:`push` will also be returned. |
|
443
|
|
|
""" |
|
444
|
|
|
if timeout is not None: |
|
445
|
|
|
timeout = timeout + _time.clock() # timeout at this time |
|
446
|
|
|
while True: |
|
447
|
|
|
if _eventQueue: |
|
448
|
|
|
return _eventQueue.pop(0) |
|
449
|
|
|
if flush: |
|
450
|
|
|
# a full 'round' of events need to be processed before flushing |
|
451
|
|
|
_tdl.flush() |
|
452
|
|
|
if timeout and _time.clock() >= timeout: |
|
453
|
|
|
return None # return None on timeout |
|
454
|
|
|
_time.sleep(0.001) # sleep 1ms |
|
455
|
|
|
_processEvents() |
|
456
|
|
|
|
|
457
|
|
|
|
|
458
|
|
|
def push(event): |
|
459
|
|
|
"""Push an event into the event buffer. |
|
460
|
|
|
|
|
461
|
|
|
Args: |
|
462
|
|
|
event (Any): This event will be available on the next call to |
|
463
|
|
|
:any:`event.get`. |
|
464
|
|
|
|
|
465
|
|
|
An event pushed in the middle of a :any:`get` will not show until |
|
466
|
|
|
the next time :any:`get` called preventing push related |
|
467
|
|
|
infinite loops. |
|
468
|
|
|
|
|
469
|
|
|
This object should at least have a 'type' attribute. |
|
470
|
|
|
""" |
|
471
|
|
|
_pushedEvents.append(event) |
|
472
|
|
|
|
|
473
|
|
|
def key_wait(): |
|
474
|
|
|
"""Waits until the user presses a key. |
|
475
|
|
|
Then returns a :any:`KeyDown` event. |
|
476
|
|
|
|
|
477
|
|
|
Key events will repeat if held down. |
|
478
|
|
|
|
|
479
|
|
|
A click to close the window will be converted into an Alt+F4 KeyDown event. |
|
480
|
|
|
|
|
481
|
|
|
Returns: |
|
482
|
|
|
tdl.event.KeyDown: The pressed key. |
|
483
|
|
|
""" |
|
484
|
|
|
while 1: |
|
485
|
|
|
for event in get(): |
|
486
|
|
|
if event.type == 'KEYDOWN': |
|
487
|
|
|
return event |
|
488
|
|
|
if event.type == 'QUIT': |
|
489
|
|
|
# convert QUIT into alt+F4 |
|
490
|
|
|
return KeyDown('F4', '', True, False, True, False, False) |
|
491
|
|
|
_time.sleep(.001) |
|
492
|
|
|
|
|
493
|
|
|
def set_key_repeat(delay=500, interval=0): |
|
|
|
|
|
|
494
|
|
|
"""Does nothing. |
|
495
|
|
|
""" |
|
496
|
|
|
pass |
|
497
|
|
|
|
|
498
|
|
|
def is_window_closed(): |
|
499
|
|
|
"""Returns True if the exit button on the window has been clicked and |
|
500
|
|
|
stays True afterwards. |
|
501
|
|
|
|
|
502
|
|
|
Returns: bool: |
|
503
|
|
|
""" |
|
504
|
|
|
return _lib.TCOD_console_is_window_closed() |
|
505
|
|
|
|
|
506
|
|
|
__all__ = [_var for _var in locals().keys() if _var[0] != '_'] |
|
507
|
|
|
|
|
508
|
|
|
App.runOnce = _style.backport(App.run_once) |
|
509
|
|
|
keyWait = _style.backport(key_wait) |
|
|
|
|
|
|
510
|
|
|
setKeyRepeat = _style.backport(set_key_repeat) |
|
|
|
|
|
|
511
|
|
|
isWindowClosed = _style.backport(is_window_closed) |
|
|
|
|
|
|
512
|
|
|
|
|
513
|
|
|
|
This check looks for invalid names for a range of different identifiers.
You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.
If your project includes a Pylint configuration file, the settings contained in that file take precedence.
To find out more about Pylint, please refer to their site.