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 import ctypes
35
36 from .__tcod import _lib, _Mouse, _Key
37 from . import __tcod as _tcod
38 import tdl as _tdl
39
40 _eventQueue = []
41 _pushedEvents = []
42
43 _mousel = 0
44 _mousem = 0
45 _mouser = 0
46
47
49 """
50 returns a dictionary mapping of human readable key names to their keycodes
51 this parses constants with the names of K_* and makes code=name pairs
52 this is for KeyEvent.keyname variable and that enables things like:
53 if (event.keyname == 'PAGEUP'):
54 """
55 _keyNames = {}
56 for attr in dir(module):
57 if attr[:2] == 'K_':
58 _keyNames[getattr(_tcod, attr)] = attr[2:]
59 return _keyNames
60
61 _keyNames = _parseKeyNames(_tcod)
62
64 """Base Event class.
65
66 You can easily subclass this to make your own events. Be sure to set
67 the class attribute L{Event.type} for it to be passed to a custom L{App}
68 ev_* method."""
69 __slots__ = ('__weakref__',)
70 type = None
71 """String constant representing the type of event.
72
73 The L{App} ev_* methods depend on this attribute.
74
75 Can be: 'QUIT', 'KEYDOWN', 'KEYUP', 'MOUSEDOWN', 'MOUSEUP', or 'MOUSEMOTION.'
76 """
77
79 """List an events public attributes in print calls.
80 """
81 attrdict = {}
82 for varname in dir(self):
83 if '_' == varname[0]:
84 continue
85 attrdict[varname] = self.__getattribute__(varname)
86 return '%s Event %s' % (self.__class__.__name__, repr(attrdict))
87
89 """Fired when the window is closed by the user.
90 """
91 __slots__ = ()
92 type = 'QUIT'
93
95 __slots__ = ('key', 'char', 'shift', 'alt', 'control',
96 'leftAlt', 'leftCtrl', 'rightAlt', 'rightCtrl')
97
98 - def __init__(self, key, char, lalt, lctrl, ralt, rctrl, shift):
99 self.key = _keyNames[key]
100 """Human readable name of the key pressed.
101
102 Can be one of
103 'NONE', 'ESCAPE', 'BACKSPACE', 'TAB', 'ENTER', 'SHIFT', 'CONTROL',
104 'ALT', 'PAUSE', 'CAPSLOCK', 'PAGEUP', 'PAGEDOWN', 'END', 'HOME', 'UP',
105 'LEFT', 'RIGHT', 'DOWN', 'PRINTSCREEN', 'INSERT', 'DELETE', 'LWIN',
106 'RWIN', 'APPS', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
107 'KP0', 'KP1', 'KP2', 'KP3', 'KP4', 'KP5', 'KP6', 'KP7', 'KP8', 'KP9',
108 'KPADD', 'KPSUB', 'KPDIV', 'KPMUL', 'KPDEC', 'KPENTER', 'F1', 'F2',
109 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12',
110 'NUMLOCK', 'SCROLLLOCK', 'SPACE', 'CHAR'
111 @type: string"""
112 char = char if isinstance(char, str) else char.decode()
113 self.char = char.replace('\x00', '')
114 """A single character string of the letter or symbol pressed.
115
116 Special characters like delete and return are not cross-platform.
117 L{key} should be used instead for special keys.
118 @type: string"""
119 self.leftAlt = bool(lalt)
120 """@type: boolean"""
121 self.rightAlt = bool(ralt)
122 """@type: boolean"""
123 self.leftCtrl = bool(lctrl)
124 """@type: boolean"""
125 self.rightCtrl = bool(rctrl)
126 """@type: boolean"""
127 self.shift = bool(shift)
128 """True if shift was held down during this event.
129 @type: boolean"""
130 self.alt = bool(lalt or ralt)
131 """True if alt was held down during this event.
132 @type: boolean"""
133 self.control = bool(lctrl or rctrl)
134 """True if control was held down during this event.
135 @type: boolean"""
136
138 """Fired when the user presses a key on the keyboard or a key repeats.
139 """
140 __slots__ = ()
141 type = 'KEYDOWN'
142
144 """Fired when the user releases a key on the keyboard.
145 """
146 __slots__ = ()
147 type = 'KEYUP'
148
149 _mouseNames = {1: 'LEFT', 2: 'MIDDLE', 3: 'RIGHT', 4: 'SCROLLUP', 5: 'SCROLLDOWN'}
164
166 """Fired when a mouse button is pressed."""
167 __slots__ = ()
168 type = 'MOUSEDOWN'
169
171 """Fired when a mouse button is released."""
172 __slots__ = ()
173 type = 'MOUSEUP'
174
176 """Fired when the mouse is moved."""
177 __slots__ = ('pos', 'motion', 'cell', 'cellmotion')
178 type = 'MOUSEMOTION'
179
180 - def __init__(self, pos, cell, motion, cellmotion):
181 self.pos = pos
182 """(x, y) position of the mouse on the screen.
183 type: (int, int)"""
184 self.cell = cell
185 """(x, y) position of the mouse snapped to a cell on the root console.
186 type: (int, int)"""
187 self.motion = motion
188 """(x, y) motion of the mouse on the screen.
189 type: (int, int)"""
190 self.cellmotion = cellmotion
191 """(x, y) mostion of the mouse moving over cells on the root console.
192 type: (int, int)"""
193
195 """
196 Application framework.
197
198 - ev_*: Events are passed to methods based on their L{Event.type} attribute.
199 If an event type is 'KEYDOWN' the ev_KEYDOWN method will be called
200 with the event instance as a parameter.
201
202 - key_*: When a key is pressed another method will be called based on the
203 keyname attribute. For example the 'ENTER' keyname will call key_ENTER
204 with the associated L{KeyDown} event as its parameter.
205
206 - L{update}: This method is called every loop before making a call to
207 L{tdl.flush}. It is passed a single parameter detailing the time in
208 seconds since the last update (often known as deltaTime.)
209 """
210 __slots__ = ('__running', '__prevTime')
211 __running = False
212
214 """Unless overridden this method raises a SystemExit exception closing
215 the program."""
216 raise SystemExit()
217
219 """Override this method to handle a L{KeyDown} event."""
220
222 """Override this method to handle a L{KeyUp} event."""
223
225 """Override this method to handle a L{MouseDown} event."""
226
228 """Override this method to handle a L{MouseUp} event."""
229
231 """Override this method to handle a L{MouseMotion} event."""
232
234 """Override this method to handle per frame logic and drawing.
235
236 @type deltaTime: float
237 @param deltaTime: This parameter tells the amount of time passed since
238 the last call measured in seconds as a floating point
239 number.
240
241 You can use this variable to make your program
242 frame rate independent. Use this parameter to adjust
243 the speed of physics, special visual effects,
244 and other game logic.
245 """
246 pass
247
249 """When called the App will begin to return control to where
250 L{App.run} was called.
251
252 Some further events are processed and the L{App.update} method will be
253 called one last time before exiting
254 (unless suspended during a call to L{App.update}.)
255 """
256 self.__running = False
257
259 """Delegate control over to this App instance. This function will
260 process all events and send them to the special methods ev_* and key_*.
261
262 A call to L{App.suspend} will return the control flow back to where
263 this function is called. And then the App can be run again.
264 But a single App instance can not be run multiple times simultaneously.
265 """
266 if self.__running:
267 raise _tdl.TDLError('An App can not be run multiple times simultaneously')
268 self.__running = True
269 while self.__running:
270 self.runOnce()
271
273 """Pump events to this App instance and then return.
274
275 This works in the way described in L{App.run} except it immediately
276 returns after the first L{update} call.
277
278 Having multiple L{App} instances and selectively calling runOnce on
279 them is a decent way to create a state machine.
280 """
281 if not hasattr(self, '__prevTime'):
282 self.__prevTime = time.clock()
283 for event in get():
284 if event.type:
285
286 method = 'ev_%s' % event.type
287 getattr(self, method)(event)
288 if event.type == 'KEYDOWN':
289
290 method = 'key_%s' % event.key
291 if hasattr(self, method):
292 getattr(self, method)(event)
293 newTime = time.clock()
294 self.update(newTime - self.__prevTime)
295 self.__prevTime = newTime
296 _tdl.flush()
297
299 """Flushes the event queue from libtcod into the global list _eventQueue"""
300 global _mousel, _mousem, _mouser, _eventsflushed, _pushedEvents
301 _eventsflushed = True
302 events = _pushedEvents
303 _pushedEvents = []
304
305 mouse = _Mouse()
306 libkey = _Key()
307 while 1:
308 libevent = _lib.TCOD_sys_check_for_event(_tcod.TCOD_EVENT_ANY, libkey, mouse)
309 if not libevent:
310 break
311
312
313 if libevent & _tcod.TCOD_EVENT_MOUSE_MOVE:
314 events.append(MouseMotion(*mouse.motion))
315
316 mousepos = ((mouse.x, mouse.y), (mouse.cx, mouse.cy))
317
318 for oldstate, newstate, released, button in zip((_mousel, _mousem, _mouser),
319 mouse.button, mouse.button_pressed, (1, 2, 3)):
320 if released:
321 if not oldstate:
322 events.append(MouseDown(button, *mousepos))
323 events.append(MouseUp(button, *mousepos))
324 if newstate:
325 events.append(MouseDown(button, *mousepos))
326 elif newstate and not oldstate:
327 events.append(MouseDown(button, *mousepos))
328
329 if mouse.wheel_up:
330 events.append(MouseDown(4, *mousepos))
331 if mouse.wheel_down:
332 events.append(MouseDown(5, *mousepos))
333
334 _mousel = mouse.lbutton
335 _mousem = mouse.mbutton
336 _mouser = mouse.rbutton
337
338 if libkey.vk == _tcod.K_NONE:
339 break
340 if libkey.pressed:
341 keyevent = KeyDown
342 else:
343 keyevent = KeyUp
344 events.append(keyevent(*tuple(libkey)))
345
346 if _lib.TCOD_console_is_window_closed():
347 events.append(Quit())
348
349 _eventQueue.extend(events)
350
352 """Flushes the event queue and returns the list of events.
353
354 This function returns L{Event} objects that can be indentified by their
355 type attribute or their class.
356
357 @rtype: iterator
358 @return: Returns an iterable of objects derived from L{Event} or anything
359 put in a L{push} call. If the iterator is deleted or otherwise
360 interrupted before finishing the excess items are preserved for the
361 next call.
362 """
363 _processEvents()
364 while _eventQueue:
365
366
367
368 yield(_eventQueue.pop(0))
369 raise StopIteration()
370
372 """Push an event into the event buffer.
373
374 @type event: L{Event}-like object
375 @param event: The event will be available on the next call to L{event.get}.
376 An event pushed in the middle of a L{get} will not show until
377 the next time L{get} called preventing push related
378 infinite loops.
379 """
380 _pushedEvents.append(event)
381
383 """Waits until the user presses a key. Then returns a L{KeyDown} event.
384
385 @rtype: L{KeyDown}
386 """
387 global _eventsflushed
388 _eventsflushed = True
389 flush = False
390 libkey = _Key()
391 _lib.TCOD_console_wait_for_keypress_wrapper(libkey, flush)
392 return KeyDown(*libkey)
393
395 """Returns True if the exit button on the window has been clicked and
396 stays True afterwards.
397
398 @rtype: boolean
399 """
400 return _lib.TCOD_console_is_window_closed()
401
402 __all__ = [_var for _var in locals().keys() if _var[0] != '_' and _var not in ['time', 'ctypes']]
403