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.key variable and that enables things like:
53 if (event.key == '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 when printed.
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', 'keychar', 'shift', 'alt', 'control',
96 'leftAlt', 'leftCtrl', 'rightAlt', 'rightCtrl')
97
98 - def __init__(self, key, char, lalt, lctrl, ralt, rctrl, shift):
99
100 self.key = key if isinstance(key, str) else _keyNames[key]
101 """Human readable names of the key pressed.
102 Non special characters will show up as 'CHAR'.
103
104 Can be one of
105 'NONE', 'ESCAPE', 'BACKSPACE', 'TAB', 'ENTER', 'SHIFT', 'CONTROL',
106 'ALT', 'PAUSE', 'CAPSLOCK', 'PAGEUP', 'PAGEDOWN', 'END', 'HOME', 'UP',
107 'LEFT', 'RIGHT', 'DOWN', 'PRINTSCREEN', 'INSERT', 'DELETE', 'LWIN',
108 'RWIN', 'APPS', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
109 'KP0', 'KP1', 'KP2', 'KP3', 'KP4', 'KP5', 'KP6', 'KP7', 'KP8', 'KP9',
110 'KPADD', 'KPSUB', 'KPDIV', 'KPMUL', 'KPDEC', 'KPENTER', 'F1', 'F2',
111 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12',
112 'NUMLOCK', 'SCROLLLOCK', 'SPACE', 'CHAR'
113
114 For the actual character instead of 'CHAR' use L{keychar}.
115 @type: string"""
116 char = char if isinstance(char, str) else char.decode()
117 self.char = char.replace('\x00', '')
118 """A single character string of the letter or symbol pressed.
119
120 Special characters like delete and return are not cross-platform.
121 L{key} or L{keychar} should be used instead for special keys.
122 Characters are also case sensitive.
123 @type: string"""
124
125 self.keychar = self.char if self.key == 'CHAR' else self.key
126 """Similar to L{key} but returns a case sensitive letter or symbol
127 instead of 'CHAR'.
128
129 This variable makes available the widest variety of symbols and should
130 be used for key-mappings or anywhere where a narrower sample of keys
131 isn't needed.
132 """
133 self.leftAlt = bool(lalt)
134 """@type: boolean"""
135 self.rightAlt = bool(ralt)
136 """@type: boolean"""
137 self.leftCtrl = bool(lctrl)
138 """@type: boolean"""
139 self.rightCtrl = bool(rctrl)
140 """@type: boolean"""
141 self.shift = bool(shift)
142 """True if shift was held down during this event.
143 @type: boolean"""
144 self.alt = bool(lalt or ralt)
145 """True if alt was held down during this event.
146 @type: boolean"""
147 self.control = bool(lctrl or rctrl)
148 """True if control was held down during this event.
149 @type: boolean"""
150
152 """Fired when the user presses a key on the keyboard or a key repeats.
153 """
154 __slots__ = ()
155 type = 'KEYDOWN'
156
158 """Fired when the user releases a key on the keyboard.
159 """
160 __slots__ = ()
161 type = 'KEYUP'
162
163 _mouseNames = {1: 'LEFT', 2: 'MIDDLE', 3: 'RIGHT', 4: 'SCROLLUP', 5: 'SCROLLDOWN'}
178
180 """Fired when a mouse button is pressed."""
181 __slots__ = ()
182 type = 'MOUSEDOWN'
183
185 """Fired when a mouse button is released."""
186 __slots__ = ()
187 type = 'MOUSEUP'
188
190 """Fired when the mouse is moved."""
191 __slots__ = ('pos', 'motion', 'cell', 'cellmotion')
192 type = 'MOUSEMOTION'
193
194 - def __init__(self, pos, cell, motion, cellmotion):
195 self.pos = pos
196 """(x, y) position of the mouse on the screen.
197 type: (int, int)"""
198 self.cell = cell
199 """(x, y) position of the mouse snapped to a cell on the root console.
200 type: (int, int)"""
201 self.motion = motion
202 """(x, y) motion of the mouse on the screen.
203 type: (int, int)"""
204 self.cellmotion = cellmotion
205 """(x, y) mostion of the mouse moving over cells on the root console.
206 type: (int, int)"""
207
209 """
210 Application framework.
211
212 - ev_*: Events are passed to methods based on their L{Event.type} attribute.
213 If an event type is 'KEYDOWN' the ev_KEYDOWN method will be called
214 with the event instance as a parameter.
215
216 - key_*: When a key is pressed another method will be called based on the
217 L{KeyEvent.key} attribute. For example the 'ENTER' key will call key_ENTER
218 with the associated L{KeyDown} event as its parameter.
219
220 - L{update}: This method is called every loop. It is passed a single
221 parameter detailing the time in seconds since the last update
222 (often known as deltaTime.)
223
224 You may want to call drawing routines in this method followed by
225 L{tdl.flush}.
226 """
227 __slots__ = ('__running', '__prevTime')
228 __running = False
229 __prevTime = None
230
232 """Unless overridden this method raises a SystemExit exception closing
233 the program."""
234 raise SystemExit()
235
237 """Override this method to handle a L{KeyDown} event."""
238
240 """Override this method to handle a L{KeyUp} event."""
241
243 """Override this method to handle a L{MouseDown} event."""
244
246 """Override this method to handle a L{MouseUp} event."""
247
249 """Override this method to handle a L{MouseMotion} event."""
250
252 """Override this method to handle per frame logic and drawing.
253
254 @type deltaTime: float
255 @param deltaTime: This parameter tells the amount of time passed since
256 the last call measured in seconds as a floating point
257 number.
258
259 You can use this variable to make your program
260 frame rate independent.
261 Use this parameter to adjust the speed of motion,
262 timers, and other game logic.
263 """
264 pass
265
267 """When called the App will begin to return control to where
268 L{App.run} was called.
269
270 Some further events are processed and the L{App.update} method will be
271 called one last time before exiting
272 (unless suspended during a call to L{App.update}.)
273 """
274 self.__running = False
275
277 """Delegate control over to this App instance. This function will
278 process all events and send them to the special methods ev_* and key_*.
279
280 A call to L{App.suspend} will return the control flow back to where
281 this function is called. And then the App can be run again.
282 But a single App instance can not be run multiple times simultaneously.
283 """
284 if self.__running:
285 raise _tdl.TDLError('An App can not be run multiple times simultaneously')
286 self.__running = True
287 while self.__running:
288 self.runOnce()
289
291 """Pump events to this App instance and then return.
292
293 This works in the way described in L{App.run} except it immediately
294 returns after the first L{update} call.
295
296 Having multiple L{App} instances and selectively calling runOnce on
297 them is a decent way to create a state machine.
298 """
299 if self.__prevTime is None:
300 self.__prevTime = time.clock()
301 for event in get():
302 if event.type:
303
304 method = 'ev_%s' % event.type
305 getattr(self, method)(event)
306 if event.type == 'KEYDOWN':
307
308 method = 'key_%s' % event.key
309 if hasattr(self, method):
310 getattr(self, method)(event)
311 newTime = time.clock()
312 self.update(newTime - self.__prevTime)
313 self.__prevTime = newTime
314
315
317 """Flushes the event queue from libtcod into the global list _eventQueue"""
318 global _mousel, _mousem, _mouser, _eventsflushed, _pushedEvents
319 _eventsflushed = True
320 events = _pushedEvents
321 _pushedEvents = []
322
323 mouse = _Mouse()
324 libkey = _Key()
325 while 1:
326 libevent = _lib.TCOD_sys_check_for_event(_tcod.TCOD_EVENT_ANY, libkey, mouse)
327 if not libevent:
328 break
329
330
331 if libevent & _tcod.TCOD_EVENT_MOUSE_MOVE:
332 events.append(MouseMotion(*mouse.motion))
333
334 mousepos = ((mouse.x, mouse.y), (mouse.cx, mouse.cy))
335
336 for oldstate, newstate, released, button in zip((_mousel, _mousem, _mouser),
337 mouse.button, mouse.button_pressed, (1, 2, 3)):
338 if released:
339 if not oldstate:
340 events.append(MouseDown(button, *mousepos))
341 events.append(MouseUp(button, *mousepos))
342 if newstate:
343 events.append(MouseDown(button, *mousepos))
344 elif newstate and not oldstate:
345 events.append(MouseDown(button, *mousepos))
346
347 if mouse.wheel_up:
348 events.append(MouseDown(4, *mousepos))
349 if mouse.wheel_down:
350 events.append(MouseDown(5, *mousepos))
351
352 _mousel = mouse.lbutton
353 _mousem = mouse.mbutton
354 _mouser = mouse.rbutton
355
356 if libkey.vk == _tcod.K_NONE:
357 break
358 if libkey.pressed:
359 keyevent = KeyDown
360 else:
361 keyevent = KeyUp
362 events.append(keyevent(*tuple(libkey)))
363
364 if _lib.TCOD_console_is_window_closed():
365 events.append(Quit())
366
367 _eventQueue.extend(events)
368
370 """Flushes the event queue and returns the list of events.
371
372 This function returns L{Event} objects that can be indentified by their
373 type attribute or their class.
374
375 @rtype: iterator
376 @return: Returns an iterable of objects derived from L{Event} or anything
377 put in a L{push} call. If the iterator is deleted or otherwise
378 interrupted before finishing the excess items are preserved for the
379 next call.
380 """
381 def eventGenerator():
382 while _eventQueue:
383
384
385
386 yield(_eventQueue.pop(0))
387 raise StopIteration()
388 _processEvents()
389 return eventGenerator()
390
392 """Push an event into the event buffer.
393
394 @type event: L{Event}-like object
395 @param event: The event will be available on the next call to L{event.get}.
396 An event pushed in the middle of a L{get} will not show until
397 the next time L{get} called preventing push related
398 infinite loops.
399 """
400 _pushedEvents.append(event)
401
403 """Waits until the user presses a key.
404 Then returns a L{KeyDown} event.
405
406 Key events will repeat if held down.
407
408 A click to close the window will be converted into an Alt+F4 KeyDown event.
409
410 @rtype: L{KeyDown}
411 """
412 while 1:
413 for event in get():
414 if event.type == 'KEYDOWN':
415 return event
416 if event.type == 'QUIT':
417
418 return KeyDown('F4', '', True, False, True, False, False)
419 time.sleep(.001)
420
422 """Returns True if the exit button on the window has been clicked and
423 stays True afterwards.
424
425 @rtype: boolean
426 """
427 return _lib.TCOD_console_is_window_closed()
428
429 __all__ = [_var for _var in locals().keys() if _var[0] != '_' and _var not in ['time', 'ctypes']]
430