''' Filename events.py Author: Bryan "ObsidianBlk" Miller Date Created: 8/1/2018 Python Version: 3.7 ''' import time import weakref import pygame def _getWeakRef(fn): if not hasattr(fn, "__call__"): return None if hasattr(fn, "__self__"): return weakref.WeakMethod(fn) return weakref.ref(fn) class EventError(Exception): pass class _Events: def __init__(self): self._signals = {} def _ClearUnreferenced(self): for signal in self._signals: removal = [s for s in self._signals[signal] if s() is None] for r in removal: self._signals[signal].remove(r) def listen(self, signal, fn): ref = _getWeakRef(fn) if ref is None or ref() is None: raise EventError("Expected a function callback.") if not signal in self._signals: self._signals[signal] = [] if not ref in self._signals[signal]: self._signals[signal].append(ref) def unlisten(self, signal, fn): ref = _getWeakRef(fn) if ref is None or ref() is None: return # Not a function. Nothing to do. if signal in self._signals: if ref in self._signals[signal]: self._signals[signal].remove(ref) def unlisten_all(self, signal): if signal in self._signals: del self._signals[signal] def emit(self, signal, data): if signal in self._signals: for r in self._signals[signal]: fn = r() if fn is not None: fn(signal, data) self._ClearUnreferenced() # Create the actual Events instance :) Events = _Events() _ClickDelayMax=500 # in Milliseconds _DOWNKEYS=[] _DOWNMBUTTONS=[] _DOWNJBUTTONS=[] def _WatchKey(key): global _DOWNKEYS for k in _DOWNKEYS: if k[0] == key: return # Already watching. Technically, this should never happen. _DOWNKEYS.append((key, int(round(time.time()*1000)))) def _ReleaseKey(key): global _DOWNKEYS, _ClickDelayMax tick = int(round(time.time()*1000)) for k in _DOWNKEYS: if k[0] == key: lastTick = k[1] _DOWNKEYS.remove(k) if tick - lastTick <= _ClickDelayMax: Events.emit("KEYPRESSED", {"key":key, "mod":pygame.key.get_mods(), "key_name":pygame.key.name(key)}) return # Done. # We found nothing, boss. def _WatchButton(device, button): global _DOWNMBUTTONS, _DOWNJBUTTONS tick = int(round(time.time()*1000)) btnsrc = _DOWNMBUTTONS if device >= 0: btnsrc = _DOWNJBUTTONS for b in btnsrc: if b[0] == device and b[1] == button: return # Already Watching... btnsrc.append((device, button, tick)) def _ReleaseButton(device, button): global _DOWNMBUTTONS, _DOWNJBUTTONS, _ClickDelayMax tick = int(round(time.time()*1000)) btnsrc = _DOWNMBUTTONS if device >= 0: btnsrc = _DOWNJBUTTONS for b in btnsrc: if b[0] == device and b[1] == button: lastTick = b[2] btnsrc.remove(b) if tick - lastTick <= _ClickDelayMax: if device >= 0: Events.emit("JOYBUTTONPRESSED", {"joy":device, "button":button}) else: Events.emit("MOUSEBUTTONPRESSED", {"pos":pygame.mouse.get_pos(), "button":button}) return # Done. # We found nothing, boss. def pollEmitter(): global Events, _WatchKey, _ReleaseKey, _WatchButton, _ReleaseButton for event in pygame.event.get(): if event.type == pygame.QUIT: Events.emit("QUIT", {}) elif event.type == pygame.KEYDOWN: _WatchKey(event.key) Events.emit("KEYDOWN", {"unicode":event.unicode, "key":event.key, "mod":event.mod, "key_name":pygame.key.name(event.key)}) elif event.type == pygame.KEYUP: Events.emit("KEYUP", {"key":event.key, "mod":event.mod, "key_name":pygame.key.name(event.key)}) _ReleaseKey(event.key) elif event.type == pygame.MOUSEMOTION: Events.emit("MOUSEMOTION", {"pos":event.pos, "rel":event.rel, "buttons":event.buttons}) elif event.type == pygame.MOUSEBUTTONUP: Events.emit("MOUSEBUTTONUP", {"pos":event.pos, "button":event.button}) _ReleaseButton(-1, event.button) elif event.type == pygame.MOUSEBUTTONDOWN: _WatchButton(-1, event.button) Events.emit("MOUSEBUTTONDOWN", {"pos":event.pos, "button":event.button}) elif event.type == pygame.VIDEORESIZE: # NOTE: There is a resize bug in Linux. This will stop working after a short time. Grrr Events.emit("VIDEORESIZE", {"size":event.size, "w":event.w, "h":event.h}) elif event.type == pygame.VIDEOEXPOSE: Events.emit("VIDEOEXPOSE", {}) elif event.type == pygame.JOYAXISMOTION: Events.emit("JOYAXISMOTION", {"joy":event.joy, "axis":event.axis, "value":event.value}) elif event.type == pygame.JOYBALLMOTION: Events.emit("JOYBALLMOTION", {"joy":event.joy, "ball":event.ball, "res":event.rel}) elif event.type == pygame.JOYHATMOTION: Events.emit("JOYHATMOTION", {"joy":event.joy, "hat":event.hat, "value":event.value}) elif event.type == pygame.JOYBUTTONUP: Events.emit("JOYBUTTONUP", {"joy":event.joy, "button":event.button}) _ReleaseButton(event.joy, event.button) elif event.type == pygame.JOYBUTTONDOWN: _WatchButton(event.joy, event.button) Events.emit("JOYBUTTONDOWN", {"joy":event.joy, "button":event.button}) elif event.type == pygame.ACTIVEEVENT: if event.state == 1: if event.gain == 0: Events.emit("FOCUSLOST", {}) elif event.gain == 1: Events.emit("FOCUSGAINED", {}) else: if hasattr(event, "code"): Events.emit("PYGUSER_{}".format(event.code), {}) else: print("Unkown pygame event type '{}'".format(pygame.event.event_name(event.type)))