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
230 """Unless overridden this method raises a SystemExit exception closing
231 the program."""
232 raise SystemExit()
233
235 """Override this method to handle a L{KeyDown} event."""
236
238 """Override this method to handle a L{KeyUp} event."""
239
241 """Override this method to handle a L{MouseDown} event."""
242
244 """Override this method to handle a L{MouseUp} event."""
245
247 """Override this method to handle a L{MouseMotion} event."""
248
250 """Override this method to handle per frame logic and drawing.
251
252 @type deltaTime: float
253 @param deltaTime: This parameter tells the amount of time passed since
254 the last call measured in seconds as a floating point
255 number.
256
257 You can use this variable to make your program
258 frame rate independent.
259 Use this parameter to adjust the speed of motion,
260 timers, and other game logic.
261 """
262 pass
263
265 """When called the App will begin to return control to where
266 L{App.run} was called.
267
268 Some further events are processed and the L{App.update} method will be
269 called one last time before exiting
270 (unless suspended during a call to L{App.update}.)
271 """
272 self.__running = False
273
275 """Delegate control over to this App instance. This function will
276 process all events and send them to the special methods ev_* and key_*.
277
278 A call to L{App.suspend} will return the control flow back to where
279 this function is called. And then the App can be run again.
280 But a single App instance can not be run multiple times simultaneously.
281 """
282 if getattr(self, '_App__running', False):
283 raise _tdl.TDLError('An App can not be run multiple times simultaneously')
284 self.__running = True
285 while self.__running:
286 self.runOnce()
287
289 """Pump events to this App instance and then return.
290
291 This works in the way described in L{App.run} except it immediately
292 returns after the first L{update} call.
293
294 Having multiple L{App} instances and selectively calling runOnce on
295 them is a decent way to create a state machine.
296 """
297 if not hasattr(self, '_App__prevTime'):
298 self.__prevTime = time.clock()
299 for event in get():
300 if event.type:
301
302 method = 'ev_%s' % event.type
303 getattr(self, method)(event)
304 if event.type == 'KEYDOWN':
305
306 method = 'key_%s' % event.key
307 if hasattr(self, method):
308 getattr(self, method)(event)
309 newTime = time.clock()
310 self.update(newTime - self.__prevTime)
311 self.__prevTime = newTime
312
313
315 """Flushes the event queue from libtcod into the global list _eventQueue"""
316 global _mousel, _mousem, _mouser, _eventsflushed, _pushedEvents
317 _eventsflushed = True
318 events = _pushedEvents
319 _pushedEvents = []
320
321 mouse = _Mouse()
322 libkey = _Key()
323 while 1:
324 libevent = _lib.TCOD_sys_check_for_event(_tcod.TCOD_EVENT_ANY, libkey, mouse)
325 if not libevent:
326 break
327
328
329 if libevent & _tcod.TCOD_EVENT_MOUSE_MOVE:
330 events.append(MouseMotion(*mouse.motion))
331
332 mousepos = ((mouse.x, mouse.y), (mouse.cx, mouse.cy))
333
334 for oldstate, newstate, released, button in zip((_mousel, _mousem, _mouser),
335 mouse.button, mouse.button_pressed, (1, 2, 3)):
336 if released:
337 if not oldstate:
338 events.append(MouseDown(button, *mousepos))
339 events.append(MouseUp(button, *mousepos))
340 if newstate:
341 events.append(MouseDown(button, *mousepos))
342 elif newstate and not oldstate:
343 events.append(MouseDown(button, *mousepos))
344
345 if mouse.wheel_up:
346 events.append(MouseDown(4, *mousepos))
347 if mouse.wheel_down:
348 events.append(MouseDown(5, *mousepos))
349
350 _mousel = mouse.lbutton
351 _mousem = mouse.mbutton
352 _mouser = mouse.rbutton
353
354 if libkey.vk == _tcod.K_NONE:
355 break
356 if libkey.pressed:
357 keyevent = KeyDown
358 else:
359 keyevent = KeyUp
360 events.append(keyevent(*tuple(libkey)))
361
362 if _lib.TCOD_console_is_window_closed():
363 events.append(Quit())
364
365 _eventQueue.extend(events)
366
368 """Flushes the event queue and returns the list of events.
369
370 This function returns L{Event} objects that can be indentified by their
371 type attribute or their class.
372
373 @rtype: iterator
374 @return: Returns an iterable of objects derived from L{Event} or anything
375 put in a L{push} call. If the iterator is deleted or otherwise
376 interrupted before finishing the excess items are preserved for the
377 next call.
378 """
379 def eventGenerator():
380 while _eventQueue:
381
382
383
384 yield(_eventQueue.pop(0))
385 raise StopIteration()
386 _processEvents()
387 return eventGenerator()
388
390 """Push an event into the event buffer.
391
392 @type event: L{Event}-like object
393 @param event: The event will be available on the next call to L{event.get}.
394 An event pushed in the middle of a L{get} will not show until
395 the next time L{get} called preventing push related
396 infinite loops.
397 """
398 _pushedEvents.append(event)
399
401 """Waits until the user presses a key.
402 Then returns a L{KeyDown} event.
403
404 Key events will repeat if held down.
405
406 A click to close the window will be converted into an Alt+F4 KeyDown event.
407
408 @rtype: L{KeyDown}
409 """
410 while 1:
411 for event in get():
412 if event.type == 'KEYDOWN':
413 return event
414 if event.type == 'QUIT':
415
416 return KeyDown('F4', '', True, False, True, False, False)
417 time.sleep(.001)
418
420 """Change or disable key repeat.
421
422 @type delay: int
423 @param delay: Milliseconds before a held key begins to repeat.
424
425 Key repeat can be disabled entirely by setting a delay of zero.
426
427 @type interval: int
428 @param interval: Milliseconds between key repeats.
429
430 An interval of zero will repeat every frame.
431 """
432 _lib.TCOD_console_set_keyboard_repeat(delay, interval)
433
435 """Returns True if the exit button on the window has been clicked and
436 stays True afterwards.
437
438 @rtype: boolean
439 """
440 return _lib.TCOD_console_is_window_closed()
441
442 __all__ = [_var for _var in locals().keys() if _var[0] != '_' and _var not in ['time', 'ctypes']]
443