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 import time
34
35
36
37 from .__tcod import _lib, _Mouse, _Key
38 from . import __tcod as _tcod
39 import untdl as _tdl
40
41 _eventQueue = []
42 _pushedEvents = []
43
44 _mouse_l = 0
45 _mouse_m = 0
46 _mouse_r = 0
47
48
49
50
52 """
53 returns a dictionary mapping of human readable key names to their keycodes
54 this parses constants with the names of K_* and makes code=name pairs
55 this is for KeyEvent.key variable and that enables things like:
56 if (event.key == 'PAGEUP'):
57 """
58 _key_names = {}
59 for attr in dir(module):
60 if attr[:2] == 'K_':
61 _key_names[getattr(_tcod, attr)] = attr[2:]
62 return _key_names
63
64
65 _keyNames = _parse_key_names(_tcod)
66
67
69 """Base Event class.
70
71 You can easily subclass this to make your own events. Be sure to set
72 the class attribute L{Event.type} for it to be passed to a custom L{App}
73 ev_* method."""
74 __slots__ = ('__weakref__',)
75 type = None
76 """String constant representing the type of event.
77
78 The L{App} ev_* methods depend on this attribute.
79
80 Can be: 'QUIT', 'KEYDOWN', 'KEYUP', 'MOUSEDOWN', 'MOUSEUP', or 'MOUSEMOTION.'
81 """
82
84 """List an events public attributes when printed.
85 """
86 attr_dict = {}
87 for var_name in dir(self):
88 if '_' == var_name[0]:
89 continue
90 attr_dict[var_name] = self.__getattribute__(var_name)
91 return '%s Event %s' % (self.__class__.__name__, repr(attr_dict))
92
93
95 """Fired when the window is closed by the user.
96 """
97 __slots__ = ()
98 type = 'QUIT'
99
100
102 __slots__ = ('key', 'char', 'keychar', 'shift', 'alt', 'control',
103 'leftAlt', 'leftCtrl', 'rightAlt', 'rightCtrl')
104
105 - def __init__(self, key, char, l_alt, l_ctrl, r_alt, r_ctrl, shift):
106
107 self.key = key if isinstance(key, str) else _keyNames[key]
108 """Human readable names of the key pressed.
109 Non special characters will show up as 'CHAR'.
110
111 Can be one of
112 'NONE', 'ESCAPE', 'BACKSPACE', 'TAB', 'ENTER', 'SHIFT', 'CONTROL',
113 'ALT', 'PAUSE', 'CAPSLOCK', 'PAGEUP', 'PAGEDOWN', 'END', 'HOME', 'UP',
114 'LEFT', 'RIGHT', 'DOWN', 'PRINTSCREEN', 'INSERT', 'DELETE', 'LWIN',
115 'RWIN', 'APPS', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
116 'KP0', 'KP1', 'KP2', 'KP3', 'KP4', 'KP5', 'KP6', 'KP7', 'KP8', 'KP9',
117 'KPADD', 'KPSUB', 'KPDIV', 'KPMUL', 'KPDEC', 'KPENTER', 'F1', 'F2',
118 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12',
119 'NUMLOCK', 'SCROLLLOCK', 'SPACE', 'CHAR'
120
121 For the actual character instead of 'CHAR' use L{keychar}.
122 @type: string"""
123 char = char if isinstance(char, str) else char.decode()
124 self.char = char.replace('\x00', '')
125 """A single character string of the letter or symbol pressed.
126
127 Special characters like delete and return are not cross-platform.
128 L{key} or L{keychar} should be used instead for special keys.
129 Characters are also case sensitive.
130 @type: string"""
131
132 self.keychar = self.char if self.key == 'CHAR' else self.key
133 """Similar to L{key} but returns a case sensitive letter or symbol
134 instead of 'CHAR'.
135
136 This variable makes available the widest variety of symbols and should
137 be used for key-mappings or anywhere where a narrower sample of keys
138 isn't needed.
139 """
140 self.leftAlt = bool(l_alt)
141 """@type: boolean"""
142 self.rightAlt = bool(r_alt)
143 """@type: boolean"""
144 self.leftCtrl = bool(l_ctrl)
145 """@type: boolean"""
146 self.rightCtrl = bool(r_ctrl)
147 """@type: boolean"""
148 self.shift = bool(shift)
149 """True if shift was held down during this event.
150 @type: boolean"""
151 self.alt = bool(l_alt or r_alt)
152 """True if alt was held down during this event.
153 @type: boolean"""
154 self.control = bool(l_ctrl or r_ctrl)
155 """True if control was held down during this event.
156 @type: boolean"""
157
158
160 """Fired when the user presses a key on the keyboard or a key repeats.
161 """
162 __slots__ = ()
163 type = 'KEYDOWN'
164
165
167 """Fired when the user releases a key on the keyboard.
168 """
169 __slots__ = ()
170 type = 'KEYUP'
171
172
173 _mouseNames = {1: 'LEFT', 2: 'MIDDLE', 3: 'RIGHT', 4: 'SCROLLUP', 5: 'SCROLLDOWN'}
174
175
190
191
193 """Fired when a mouse button is pressed."""
194 __slots__ = ()
195 type = 'MOUSEDOWN'
196
197
199 """Fired when a mouse button is released."""
200 __slots__ = ()
201 type = 'MOUSEUP'
202
203
205 """Fired when the mouse is moved."""
206 __slots__ = ('pos', 'motion', 'cell', 'cellmotion')
207 type = 'MOUSEMOTION'
208
209 - def __init__(self, pos, cell, motion, cellmotion):
210 self.pos = pos
211 """(x, y) position of the mouse on the screen.
212 type: (int, int)"""
213 self.cell = cell
214 """(x, y) position of the mouse snapped to a cell on the root console.
215 type: (int, int)"""
216 self.motion = motion
217 """(x, y) motion of the mouse on the screen.
218 type: (int, int)"""
219 self.cellmotion = cellmotion
220 """(x, y) motion of the mouse moving over cells on the root console.
221 type: (int, int)"""
222
223
224
226 """
227 Application framework.
228
229 - ev_*: Events are passed to methods based on their L{Event.type} attribute.
230 If an event type is 'KEYDOWN' the ev_KEYDOWN method will be called
231 with the event instance as a parameter.
232
233 - key_*: When a key is pressed another method will be called based on the
234 L{KeyEvent.key} attribute. For example the 'ENTER' key will call key_ENTER
235 with the associated L{KeyDown} event as its parameter.
236
237 - L{update}: This method is called every loop. It is passed a single
238 parameter detailing the time in seconds since the last update
239 (often known as deltaTime.)
240
241 You may want to call drawing routines in this method followed by
242 L{untdl.flush}.
243 """
244 __slots__ = ('__running', '__prevTime')
245
247 """Unless overridden this method raises a SystemExit exception closing
248 the program."""
249 raise SystemExit()
250
252 """Override this method to handle a L{KeyDown} event."""
253
255 """Override this method to handle a L{KeyUp} event."""
256
258 """Override this method to handle a L{MouseDown} event."""
259
261 """Override this method to handle a L{MouseUp} event."""
262
264 """Override this method to handle a L{MouseMotion} event."""
265
266 - def update(self, delta_time):
267 """Override this method to handle per frame logic and drawing.
268
269 @type delta_time: float
270 @param delta_time: 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
282 """When called the App will begin to return control to where
283 L{App.run} was called.
284
285 Some further events are processed and the L{App.update} method will be
286 called one last time before exiting
287 (unless suspended during a call to L{App.update}.)
288 """
289 self.__running = False
290
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 L{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.run_once()
304
306 """Pump events to this App instance and then return.
307
308 This works in the way described in L{App.run} except it immediately
309 returns after the first L{update} call.
310
311 Having multiple L{App} instances and selectively calling run_once 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()
316 for event in get():
317 if event.type:
318
319 method = 'ev_%s' % event.type
320 getattr(self, method)(event)
321 if event.type == 'KEYDOWN':
322
323 method = 'key_%s' % event.key
324 if hasattr(self, method):
325 getattr(self, method)(event)
326 new_time = time.clock()
327 self.update(new_time - self.__prevTime)
328 self.__prevTime = new_time
329
330
331
333 """Flushes the event queue from libtcod into the global list _eventQueue"""
334 global _mouse_l, _mouse_m, _mouse_r, _eventsflushed, _pushedEvents
335 _eventsflushed = True
336 events = _pushedEvents
337 _pushedEvents = []
338
339 mouse = _Mouse()
340 lib_key = _Key()
341 while 1:
342 lib_event = _lib.TCOD_sys_check_for_event(_tcod.TCOD_EVENT_ANY, lib_key, mouse)
343 if not lib_event:
344 break
345
346
347 if lib_event & _tcod.TCOD_EVENT_MOUSE_MOVE:
348 events.append(MouseMotion(*mouse.motion))
349
350 mouse_pos = ((mouse.x, mouse.y), (mouse.cx, mouse.cy))
351
352 for old_state, new_state, released, button in zip((_mouse_l, _mouse_m, _mouse_r),
353 mouse.button, mouse.button_pressed, (1, 2, 3)):
354 if released:
355 if not old_state:
356 events.append(MouseDown(button, *mouse_pos))
357 events.append(MouseUp(button, *mouse_pos))
358 if new_state:
359 events.append(MouseDown(button, *mouse_pos))
360 elif new_state and not old_state:
361 events.append(MouseDown(button, *mouse_pos))
362
363 if mouse.wheel_up:
364 events.append(MouseDown(4, *mouse_pos))
365 if mouse.wheel_down:
366 events.append(MouseDown(5, *mouse_pos))
367
368 _mouse_l = mouse.lbutton
369 _mouse_m = mouse.mbutton
370 _mouse_r = mouse.rbutton
371
372 if lib_key.vk == _tcod.K_NONE:
373 break
374 if lib_key.pressed:
375 keyevent = KeyDown
376 else:
377 keyevent = KeyUp
378 events.append(keyevent(*tuple(lib_key)))
379
380 if _lib.TCOD_console_is_window_closed():
381 events.append(Quit())
382
383 _eventQueue.extend(events)
384
385
387 """Flushes the event queue and returns the list of events.
388
389 This function returns L{Event} objects that can be identified by their
390 type attribute or their class.
391
392 @rtype: iterator
393 @return: Returns an iterable of objects derived from L{Event} or anything
394 put in a L{push} call. If the iterator is deleted or otherwise
395 interrupted before finishing the excess items are preserved for the
396 next call.
397 """
398
399 def event_generator():
400 while _eventQueue:
401
402
403
404 yield (_eventQueue.pop(0))
405 raise StopIteration()
406
407 _process_events()
408 return event_generator()
409
410
412 """Push an event into the event buffer.
413
414 @type event: L{Event}-like object
415 @param event: The event will be available on the next call to L{event.get}.
416 An event pushed in the middle of a L{get} will not show until
417 the next time L{get} called preventing push related
418 infinite loops.
419 """
420 _pushedEvents.append(event)
421
422
424 """Waits until the user presses a key.
425 Then returns a L{KeyDown} event.
426
427 Key events will repeat if held down.
428
429 A click to close the window will be converted into an Alt+F4 KeyDown event.
430
431 @rtype: L{KeyDown}
432 """
433 while 1:
434 for event in get():
435 if event.type == 'KEYDOWN':
436 return event
437 if event.type == 'QUIT':
438
439 return KeyDown('F4', '', True, False, True, False, False)
440 time.sleep(.001)
441
442
444 """Change or disable key repeat.
445
446 @type delay: int
447 @param delay: Milliseconds before a held key begins to repeat.
448
449 Key repeat can be disabled entirely by setting a delay of zero.
450
451 @type interval: int
452 @param interval: Milliseconds between key repeats.
453
454 An interval of zero will repeat every frame.
455 """
456 _lib.TCOD_console_set_keyboard_repeat(delay, interval)
457
458
460 """Returns True if the exit button on the window has been clicked and
461 stays True afterwards.
462
463 @rtype: boolean
464 """
465 return _lib.TCOD_console_is_window_closed()
466
467
468 __all__ = [_var for _var in locals().keys() if _var[0] != '_' and _var not in ['time', 'ctypes']]
469