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