diff --git a/Blarg.GameFramework/Blarg.GameFramework.csproj b/Blarg.GameFramework/Blarg.GameFramework.csproj
index de74197..63f1692 100644
--- a/Blarg.GameFramework/Blarg.GameFramework.csproj
+++ b/Blarg.GameFramework/Blarg.GameFramework.csproj
@@ -141,6 +141,10 @@
+
+
+
+
@@ -155,6 +159,7 @@
+
diff --git a/Blarg.GameFramework/Events/Event.cs b/Blarg.GameFramework/Events/Event.cs
new file mode 100644
index 0000000..3f9969c
--- /dev/null
+++ b/Blarg.GameFramework/Events/Event.cs
@@ -0,0 +1,11 @@
+using System;
+using Blarg.GameFramework.Support;
+
+namespace Blarg.GameFramework.Events
+{
+ public abstract class Event : IPoolable
+ {
+ public abstract void Reset();
+ }
+}
+
diff --git a/Blarg.GameFramework/Events/EventHandler.cs b/Blarg.GameFramework/Events/EventHandler.cs
new file mode 100644
index 0000000..76a4112
--- /dev/null
+++ b/Blarg.GameFramework/Events/EventHandler.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace Blarg.GameFramework.Events
+{
+ public abstract class EventHandler : IEventListener
+ {
+ public readonly EventManager EventManager;
+
+ public EventHandler(EventManager eventManager)
+ {
+ if (eventManager == null)
+ throw new ArgumentNullException("eventManager");
+
+ EventManager = eventManager;
+ }
+
+ public bool ListenFor() where T : Event
+ {
+ return EventManager.AddListener(this);
+ }
+
+ public bool StopListeningFor() where T : Event
+ {
+ return EventManager.RemoveListener(this);
+ }
+
+ public abstract bool Handle(Event e);
+ }
+}
+
diff --git a/Blarg.GameFramework/Events/EventManager.cs b/Blarg.GameFramework/Events/EventManager.cs
new file mode 100644
index 0000000..af3be77
--- /dev/null
+++ b/Blarg.GameFramework/Events/EventManager.cs
@@ -0,0 +1,227 @@
+using System;
+using System.Collections.Generic;
+using Blarg.GameFramework.Support;
+
+namespace Blarg.GameFramework.Events
+{
+ using EventListenerList = IList;
+ using EventTypeSet = ISet;
+ using EventListenerTable = IList;
+ using EventListenerMap = IDictionary>;
+ using EventQueue = LinkedList;
+
+ public class EventManager
+ {
+ const int NumEventQueues = 2;
+
+ EventTypeSet _typeList;
+ EventListenerMap _registry;
+ EventQueue[] _queues;
+ int _activeQueue;
+
+ public EventManager()
+ {
+ _typeList = new HashSet();
+ _registry = new Dictionary();
+ _queues = new EventQueue[NumEventQueues];
+ for (int i = 0; i < _queues.Length; ++i)
+ _queues[i] = new LinkedList();
+
+ _activeQueue = 0;
+ }
+
+ public bool AddListener(IEventListener listener) where T : Event
+ {
+ if (listener == null)
+ throw new ArgumentNullException("listener");
+
+ var type = typeof(T);
+ EventListenerTable listenerTable = null;
+
+ _registry.TryGetValue(type, out listenerTable);
+ if (listenerTable == null)
+ {
+ // need to register this listener for the given type
+ listenerTable = new List();
+ _registry.Add(type, listenerTable);
+ }
+
+ // prevent duplicate listeners from being registered
+ if (listenerTable.Contains(listener))
+ throw new InvalidOperationException("Duplicate event listener registration.");
+
+ listenerTable.Add(listener);
+ Platform.Logger.Debug("EventManager", "Added {0} as a listener for event type {1}", listener.GetType().Name, type.Name);
+
+ // also update the list of currently registered event types
+ _typeList.Add(type);
+
+ return true;
+ }
+
+ public bool RemoveListener(IEventListener listener) where T : Event
+ {
+ if (listener == null)
+ throw new ArgumentNullException("listener");
+
+ var type = typeof(T);
+
+ // get the list of listeners for the given event type
+ EventListenerTable listenersForType;
+ _registry.TryGetValue(type, out listenersForType);
+ if (listenersForType == null)
+ return false; // either no listeners for this type, or the listener wasn't registered with us
+
+ if (listenersForType.Contains(listener))
+ {
+ listenersForType.Remove(listener);
+ Platform.Logger.Debug("EventManager", "Removed {0} as a listener for event type {1}", listener.GetType().Name, type.Name);
+
+ // if there are no more listeners for this type, remove the type
+ // from the list of registered event types
+ if (listenersForType.Count == 0)
+ _typeList.Remove(type);
+
+ return true;
+ }
+ else
+ return false;
+ }
+
+ public bool Trigger(Event e)
+ {
+ if (e == null)
+ throw new ArgumentNullException("e");
+
+ var type = e.GetType();
+
+ // find the listeners for the event type provided
+ EventListenerTable listenersForType;
+ _registry.TryGetValue(type, out listenersForType);
+ if (listenersForType == null)
+ return false; // no listeners for this event type have been registered -- we can't handle the event
+
+ bool result = false;
+
+ // trigger the event in each listener
+ foreach (var listener in listenersForType)
+ {
+ if (listener.Handle(e))
+ {
+ // don't let other listeners handle the event if this one signals it handled it
+ result = true;
+ break;
+ }
+ }
+
+ // TODO: maybe, for Trigger() only, it's better to force the calling code
+ // to "putback" the event object being triggered? since we handle the
+ // event immediately, unlike with Queue() where it makes a lot more
+ // sense for us to place it back in the pool ourselves ...
+ Free(e);
+
+ // a result of "false" merely indicates that no listener indicates
+ // it "handled" the event
+ return result;
+ }
+
+ public bool Queue(Event e)
+ {
+ if (e == null)
+ throw new ArgumentNullException("e");
+
+ // validate that there is infact a listener for this event type
+ // (otherwise, we don't queue this event)
+ var type = e.GetType();
+ if (!_typeList.Contains(type))
+ return false;
+
+ _queues[_activeQueue].AddLast(e);
+
+ return true;
+ }
+
+ public bool Abort(bool stopAfterFirstRemoval = true) where T : Event
+ {
+ // validate that there is infact a listener for this event type
+ // (otherwise, we don't queue this event)
+ var type = typeof(T);
+ if (!_typeList.Contains(type))
+ return false;
+
+ bool result = false;
+
+ // walk through the queue and remove matching events
+ // NOTE: foreach not used because we need to remove items while inside the loop
+ var queue = _queues[_activeQueue];
+ var node = queue.First;
+ while (node != null)
+ {
+ // grab the next node first (so we have it before potentially removing
+ // this node and then losing the link to the next one)
+ var nextNode = node.Next;
+
+ if (node.Value.GetType() == type)
+ {
+ // found a match, remove it
+ var e = node.Value;
+ queue.Remove(node);
+ Free(e);
+ result = true;
+
+ if (stopAfterFirstRemoval)
+ break;
+ }
+
+ node = nextNode;
+ }
+
+ return result;
+ }
+
+ public bool ProcessQueue()
+ {
+ // swap active queues and empty the new queue
+ int queueToProcess = _activeQueue;
+ _activeQueue = (_activeQueue + 1) % NumEventQueues;
+ _queues[_activeQueue].Clear();
+
+ // process the queue
+ var queue = _queues[queueToProcess];
+ while (queue.Count > 0)
+ {
+ // pop the next event off the queue
+ var e = queue.First.Value;
+ queue.RemoveFirst();
+
+ var type = e.GetType();
+
+ EventListenerTable listenersForType;
+ _registry.TryGetValue(type, out listenersForType);
+ if (listenersForType != null)
+ {
+ foreach (var listener in listenersForType)
+ {
+ if (listener.Handle(e))
+ break; // don't let other listeners handle the event if this one signals it handled it
+ }
+ }
+
+ Free(e);
+ }
+
+ return true;
+ }
+
+ public T Create() where T : Event
+ {
+ return ObjectPools.Take();
+ }
+
+ public void Free(T e) where T : Event
+ {
+ ObjectPools.Free(e);
+ }
+ }
+}
+
diff --git a/Blarg.GameFramework/Events/IEventListener.cs b/Blarg.GameFramework/Events/IEventListener.cs
new file mode 100644
index 0000000..71b4869
--- /dev/null
+++ b/Blarg.GameFramework/Events/IEventListener.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Blarg.GameFramework.Events
+{
+ public interface IEventListener
+ {
+ bool Handle(Event e);
+ }
+}
+