Commits

Michael Ludwig committed 289b425

Document and clean up core input interfaces.

  • Participants
  • Parent commits 4de1656

Comments (0)

Files changed (14)

ferox-input/src/main/java/com/ferox/input/AWTEventAdapter.java

 import java.awt.event.MouseMotionListener;
 import java.awt.event.MouseWheelEvent;
 import java.awt.event.MouseWheelListener;
-import java.util.ArrayList;
-import java.util.List;
 
 import com.ferox.input.KeyEvent.KeyCode;
 import com.ferox.input.MouseEvent.MouseButton;
 
-
-public class AWTEventAdapter implements MouseKeyEventSource, java.awt.event.KeyListener, java.awt.event.MouseListener, MouseMotionListener, MouseWheelListener {
+/**
+ * AWTEventAdapter is a utility for converting events produced by Java's
+ * Abstract Windowing Toolkit to the appropriate events for this framework. An
+ * EventSource that wraps an AWT component can attach its component to an
+ * AWTEventAdapter and it will automatically dispatch events.
+ * 
+ * @author Michael Ludwig
+ */
+public class AWTEventAdapter implements java.awt.event.KeyListener, java.awt.event.MouseListener, MouseMotionListener, MouseWheelListener {
     private Component component;
-    private boolean transformY;
     
-    private final MouseKeyEventSource realSource;
-    private final EventDispatcher dispatcher;
+    private final MouseKeyEventDispatcher dispatcher;
     
-    private final List<KeyListener> keyListeners;
-    private final List<MouseListener> mouseListeners;
-    
-    public AWTEventAdapter(MouseKeyEventSource realSource) {
-        if (realSource == null)
-            throw new NullPointerException("EventSource cannot be null");
+    /**
+     * Create a new AWTEventAdapter that will convert AWT events and dispatch
+     * them to the given MouseKeyEventDispatcher.
+     * 
+     * @param dispatcher The dispatcher to use
+     */
+    public AWTEventAdapter(MouseKeyEventDispatcher dispatcher) {
+        if (dispatcher == null)
+            throw new NullPointerException("Dispatcher cannot be null");
         
-        this.realSource = realSource;
-        
-        dispatcher = new AWTEventDispatcher();
-        keyListeners = new ArrayList<KeyListener>();
-        mouseListeners = new ArrayList<MouseListener>();
+        this.dispatcher = dispatcher;
     }
     
-    public void attach(Component component, boolean transformY) {
+    /**
+     * <p>
+     * Attach the adapter to the given component. The adapter can only be
+     * attached to a single component at a time and must be detached before
+     * listening on another component.
+     * <p>
+     * To produce meaningful events, the attached component must be related to
+     * the EventSource implementation used by this adapter's dispatcher.
+     * <p>
+     * The adapter cannot send events to its dispatcher until its been attached
+     * to an alive component that is producing the relevant AWT events.
+     * 
+     * @param component The component to attach to
+     * @throws NullPointerException if component is null
+     * @throws IllegalStateException if the adapter is currently attached to
+     *             another component
+     */
+    public void attach(Component component) {
         if (component == null)
             throw new NullPointerException("Component cannot be null");
         
             component.addMouseWheelListener(this);
             
             this.component = component;
-            this.transformY = transformY;
         }
     }
     
+    /**
+     * Detach this adapter from the component it's currently attached to. If the
+     * adapter is not attached to a component, nothing happens. After detaching,
+     * the adapter will not convert and dispatch AWT events.
+     */
     public void detach() {
         synchronized(this) {
             if (component != null) {
             }
         }
     }
-
-    @Override
-    public void addKeyListener(KeyListener listener) {
-        if (listener == null)
-            throw new NullPointerException("KeyListener cannot be null");
-        synchronized(this) {
-            if (!keyListeners.contains(listener))
-                keyListeners.add(listener);
-        }
-    }
-
-    @Override
-    public void removeKeyListener(KeyListener listener) {
-        if (listener == null)
-            throw new NullPointerException("KeyListener cannot be null");
-        synchronized(this) {
-            keyListeners.remove(listener);
-        }
-    }
-
-    @Override
-    public EventQueue getQueue() {
-        return realSource.getQueue();
-    }
-
-    @Override
-    public void addMouseListener(MouseListener listener) {
-        if (listener == null)
-            throw new NullPointerException("MouseListener cannot be null");
-        synchronized(this) {
-            if (!mouseListeners.contains(listener))
-                mouseListeners.add(listener);
-        }
-    }
-
-    @Override
-    public void removeMouseListener(MouseListener listener) {
-        if (listener == null)
-            throw new NullPointerException("MouseListener cannot be null");
-        synchronized(this) {
-            mouseListeners.remove(listener);
-        }
-    }
     
     /* AWT event listener methods */
     
     }
     
     private int getY(java.awt.event.MouseEvent e) {
-        if (transformY)
-            return component.getHeight() - e.getY();
-        else
-            return e.getY();
+        return component.getHeight() - e.getY();
     }
 
     @Override
     public void keyPressed(java.awt.event.KeyEvent e) {
-        KeyEvent event = new KeyEvent(KeyEvent.Type.PRESS, (KeyEventSource) realSource, 
+        KeyEvent event = new KeyEvent(KeyEvent.Type.PRESS, dispatcher.getSource(), 
                                       getKeyCode(e), getCharacter(e));
-        realSource.getQueue().postEvent(event, dispatcher);
+        dispatcher.dispatchEvent(event);
     }
 
     @Override
     public void keyReleased(java.awt.event.KeyEvent e) {
-        KeyEvent event = new KeyEvent(KeyEvent.Type.RELEASE, (KeyEventSource) realSource,
+        KeyEvent event = new KeyEvent(KeyEvent.Type.RELEASE, dispatcher.getSource(),
                                       getKeyCode(e), getCharacter(e));
-        realSource.getQueue().postEvent(event, dispatcher);
+        dispatcher.dispatchEvent(event);
     }
 
     @Override
     @Override
     public void mouseClicked(java.awt.event.MouseEvent e) {
         // ignore this event
-        
     }
 
     @Override
 
     @Override
     public void mousePressed(java.awt.event.MouseEvent e) {
-        MouseEvent event = new MouseEvent(MouseEvent.Type.PRESS, (MouseEventSource) realSource, 
+        MouseEvent event = new MouseEvent(MouseEvent.Type.PRESS, dispatcher.getSource(), 
                                           e.getX(), getY(e), 0, getButton(e));
-        realSource.getQueue().postEvent(event, dispatcher);
+        dispatcher.dispatchEvent(event);
     }
 
     @Override
     public void mouseReleased(java.awt.event.MouseEvent e) {
-        MouseEvent event = new MouseEvent(MouseEvent.Type.RELEASE, (MouseEventSource) realSource, 
+        MouseEvent event = new MouseEvent(MouseEvent.Type.RELEASE, dispatcher.getSource(), 
                                           e.getX(), getY(e), 0, getButton(e));
-        realSource.getQueue().postEvent(event, dispatcher);
+        dispatcher.dispatchEvent(event);
     }
 
     @Override
 
     @Override
     public void mouseMoved(java.awt.event.MouseEvent e) {
-        MouseEvent event = new MouseEvent(MouseEvent.Type.MOVE, (MouseEventSource) realSource, 
+        MouseEvent event = new MouseEvent(MouseEvent.Type.MOVE, dispatcher.getSource(), 
                                           e.getX(), getY(e), 0, MouseButton.NONE);
-        realSource.getQueue().postEvent(event, dispatcher);
+        dispatcher.dispatchEvent(event);
     }
     
     @Override
     public void mouseWheelMoved(MouseWheelEvent e) {
-        MouseEvent event = new MouseEvent(MouseEvent.Type.SCROLL, (MouseEventSource) realSource, 
+        MouseEvent event = new MouseEvent(MouseEvent.Type.SCROLL, dispatcher.getSource(), 
                                           e.getX(), getY(e), e.getWheelRotation(), MouseButton.NONE);
-        realSource.getQueue().postEvent(event, dispatcher);
-    }
-    
-    /* 
-     * Internal class handling the event dispatching to the
-     * added Key and MouseListeners.
-     */
-    private class AWTEventDispatcher implements EventDispatcher {
-        @Override
-        public void dispatchEvent(Event e) {
-            if (e instanceof KeyEvent) {
-                synchronized(this) {
-                    KeyEvent ke = (KeyEvent) e;
-                    int size = keyListeners.size();
-                    for (int i = 0; i < size; i++)
-                        keyListeners.get(i).handleEvent(ke);
-                }
-            } else if (e instanceof MouseEvent) {
-                synchronized(this) {
-                    MouseEvent me = (MouseEvent) e;
-                    int size = mouseListeners.size();
-                    for (int i = 0; i < size; i++)
-                        mouseListeners.get(i).handleEvent(me);
-                }
-            } // else someone is tampering with events, just ignore it
-        }
+        dispatcher.dispatchEvent(event);
     }
 }

ferox-input/src/main/java/com/ferox/input/Event.java

 package com.ferox.input;
 
+/**
+ * <p>
+ * Event is a generic interface to signify a type as an "event" of some kind.
+ * Events are produced by {@link EventSource sources}. Event instances must be
+ * immutable objects. The produced events are then passed to
+ * {@link EventListener listeners} registered with the sources.
+ * 
+ * @see MouseEvent
+ * @see KeyEvent
+ * @author Michael Ludwig
+ */
 public interface Event {
+    /**
+     * @return The EventSource that produced this event
+     */
     public EventSource getSource();
 }

ferox-input/src/main/java/com/ferox/input/EventDispatcher.java

-package com.ferox.input;
-
-public interface EventDispatcher {
-    public void dispatchEvent(Event e);
-}

ferox-input/src/main/java/com/ferox/input/EventListener.java

 package com.ferox.input;
 
+/**
+ * EventListener is a tag super-interface for interfaces that receive events.
+ * There are two primary event listeners in this package: {@link KeyListener}
+ * for handling keyboard input events, and {@link MouseListener} for handling
+ * mouse input events.
+ * 
+ * @author Michael Ludwig
+ */
 public interface EventListener {
 }

ferox-input/src/main/java/com/ferox/input/EventQueue.java

-package com.ferox.input;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.RejectedExecutionException;
-
-public class EventQueue {
-    private final ExecutorService executor;
-    
-    public EventQueue() {
-        executor = Executors.newFixedThreadPool(1);
-    }
-
-    public void shutdown() {
-        executor.shutdownNow();
-    }
-    
-    public void postEvent(Event e, EventDispatcher dispatcher) {
-        try {
-            executor.submit(new EventTask(dispatcher, e));
-        } catch(RejectedExecutionException ree) {
-            // ignore
-        }
-    }
-    
-    private static class EventTask implements Runnable {
-        private final Event e;
-        private final EventDispatcher dispatcher;
-        
-        public EventTask(EventDispatcher dispatcher, Event e) {
-            this.dispatcher = dispatcher;
-            this.e = e;
-        }
-        
-        @Override
-        public void run() {
-            dispatcher.dispatchEvent(e);
-        }
-    }
-}

ferox-input/src/main/java/com/ferox/input/EventSource.java

 package com.ferox.input;
 
-public interface EventSource {
-    
-    public EventQueue getQueue();
-}
+/**
+ * EventSource is a tag interface to mark a type as a source or producer of
+ * events. More specific interfaces are defined that allow you to register event
+ * listeners for particular types of events: {@link KeyEventSource} and
+ * {@link MouseEventSource}.
+ * 
+ * @author Michael Ludwig
+ */
+public interface EventSource { }

ferox-input/src/main/java/com/ferox/input/KeyEvent.java

 package com.ferox.input;
 
+/**
+ * <p>
+ * KeyEvent is the concrete event type representing a user's interactions with a
+ * keyboard. KeyEvent's represent presses and releases of the keys on a
+ * keyboard.
+ * 
+ * @author Michael Ludwig
+ */
 public class KeyEvent implements Event {
+    /**
+     * Type represents the possible types of KeyEvents produced when a user
+     * interacts with a keyboard.
+     */
     public static enum Type {
-        PRESS, RELEASE
+        /**
+         * A key is pressed. Some OS's might continue to send PRESS events even
+         * if there's been no subsequent RELEASE.
+         */
+        PRESS,
+        /**
+         * A key that was pressed has been released. These events will only be
+         * sent for keys that have already produced at least one PRESS event.
+         */
+        RELEASE
     }
 
     /**
      */
     public static final int CHAR_UNKNOWN = '\0';
 
+    /**
+     * KeyCode represents all supported keyboard buttons that can be identified
+     * in a press or release event. The key code can represent keys that are
+     * modifiers that would not produce characters by themselves.
+     */
     public static enum KeyCode {
         UNKNOWN("Unknown"), 
         
     private final Type type;
     private final KeyEventSource source;
     
+    /**
+     * Create a new KeyEvent with the given parameters.
+     * 
+     * @param type The type of KeyEvent
+     * @param source The source of the event
+     * @param keyCode The KeyCode representing the keyboard button pressed or
+     *            released
+     * @param charValue The final character that would be produced by this event
+     * @throws NullPointerException if type, source, or keyCode are null
+     */
     public KeyEvent(Type type, KeyEventSource source, KeyCode keyCode, char charValue) {
         if (source == null)
             throw new NullPointerException("Event source cannot be null");
         this.charValue = charValue;
     }
     
+    /**
+     * Get the character typed by this event. This will correctly have any
+     * previously pressed modifier keys or language layouts applied to produce
+     * the expected character suitable for a text editor.
+     * 
+     * @return The character typed by this event
+     */
     public char getCharacter() {
         return charValue;
     }
     
+    /**
+     * @return The KeyCode representing the physical key that was pressed or
+     *         released for this event
+     */
     public KeyCode getKeyCode() {
         return keyCode;
     }
     
+    /**
+     * @return The type of key event
+     */
     public Type getEventType() {
         return type;
     }

ferox-input/src/main/java/com/ferox/input/KeyEventSource.java

 package com.ferox.input;
 
+/**
+ * <p>
+ * KeyEventSource is an event source for mouse events. Most often it is a window
+ * of some kind that can obtain focus from the OS and receive events either
+ * through AWT, JInput, or LWJGL's native input library.
+ * <p>
+ * The source is then responsible for converting those low-level events into
+ * {@link KeyEvent KeyEvents} and dispatching them to registered
+ * {@link KeyListener KeyListeners}. There is no guarantee about the order in
+ * which registered listeners are invoked when an event occurs.
+ * 
+ * @author Michael Ludwig
+ */
 public interface KeyEventSource extends EventSource {
+    /**
+     * Register the given KeyListener with this KeyEventSource. Nothing is
+     * done if the given listener has already been added.
+     * 
+     * @param listener The listener to add
+     * @throws NullPointerException if listener is null
+     */
     public void addKeyListener(KeyListener listener);
     
+    /**
+     * Remove the given KeyListener from this KeyEventSource. Nothing is
+     * done if the given listener has never been added, or was already removed.
+     * 
+     * @param listener The listener to remove
+     * @throws NullPointerException if listener is null
+     */
     public void removeKeyListener(KeyListener listener);
 }

ferox-input/src/main/java/com/ferox/input/KeyListener.java

 package com.ferox.input;
 
+/**
+ * <p>
+ * KeyListener is the event listener interface for handling keyboard input
+ * events. Before a listener can receive events, it must be added to a
+ * {@link KeyEventSource}. Its event handler, {@link #handleEvent(KeyEvent)}, is
+ * invoked every time the user presses a key on the keyboard while the
+ * listener's associated source has focus.
+ * <p>
+ * Depending on the OS, holding a key down might generate repeated key events of
+ * the same key code without any release event until the key is finally
+ * released.
+ * 
+ * @author Michael Ludwig
+ */
 public interface KeyListener extends EventListener {
+    /**
+     * <p>
+     * Process the specified KeyEvent. This will be invoked as soon as possible
+     * after the real-world event occurs but there is obviously some delay.
+     * KeyListeners should strive to quickly return to allow other listeners to
+     * process the event.
+     * <p>
+     * This method will be invoked on an internal event-queue thread managed by
+     * the KeyEventSource this listener was registered to. Because of this,
+     * KeyListener implementations must be thread-safe.
+     * 
+     * @param event The key event that just occurred
+     */
     public void handleEvent(KeyEvent event);
-
 }

ferox-input/src/main/java/com/ferox/input/MouseEvent.java

 package com.ferox.input;
 
+/**
+ * <p>
+ * MouseEvent is the concrete event type representing a user's interactions with
+ * a mouse. MouseEvent can represent mouse movements, button presses, and scroll
+ * wheel changes.
+ * <p>
+ * The specific type of event is represented by the {@link Type} enum. Depending
+ * on the type, the event's active mouse button or scroll wheel delta are not
+ * meaningful.
+ * <p>
+ * <ul>
+ * <li>MOVE and SCROLL events will not have a mouse button active.</li>
+ * <li>PRESS and RELEASE events will have an active button.</li>
+ * <li>SCROLL is the only event type to have a non-zero scroll delta.</li>
+ * <li>All event types have valid x and y positions.</li>
+ * </ul>
+ * 
+ * @author Michael Ludwig
+ */
 public class MouseEvent implements Event {
+    /**
+     * Type represents the various mouse event types. Some other mouse event
+     * systems might support the concept of a 'drag' but this can be reproduced
+     * by tracking when buttons are pressed and then listening to mouse movement
+     * events.
+     */
     public static enum Type {
-        MOVE, PRESS, RELEASE, SCROLL
+        /**
+         * Only the mouse's position changed. There was no button or scroll
+         * wheel interaction.
+         */
+        MOVE,
+        /**
+         * One of the mouse's buttons was pressed down and has not been released
+         * yet. Use {@link MouseEvent#getButton()} to determine which button was
+         * pressed.
+         */
+        PRESS, 
+        /**
+         * One of the mouse's buttons was released that has previously been
+         * pressed. Use {@link MouseEvent#getButton()} to determine which button
+         * was released.
+         */
+        RELEASE,
+        /**
+         * The scroll wheel was rolled forwards or backwards. Use
+         * {@link MouseEvent#getScrollDelta()} to determine the magnitude and
+         * direction.
+         */
+        SCROLL
     }
     
+    /**
+     * MouseButton is an enum representing all supported buttons in this event
+     * framework. Some mice might not be capable of using the right or center
+     * buttons.
+     */
     public static enum MouseButton {
-        NONE, LEFT, RIGHT, CENTER
+        /**
+         * Does not represent an actual button, but is used when an event has no
+         * button interaction.
+         */
+        NONE,
+        /**
+         * The left button.
+         */
+        LEFT,
+        /**
+         * The right button.
+         */
+        RIGHT,
+        /**
+         * The center button, often built into the scroll wheel.
+         */
+        CENTER
     }
     
     private final MouseEventSource source;
     
     private final MouseButton button;
     
+    /**
+     * Create a new MouseEvent with the given arguments.
+     * 
+     * @param type The type of event
+     * @param source The event's source
+     * @param x The mouse's current x position as of the event
+     * @param y The mouse's current y position as of the event
+     * @param scrollDelta The change in wheel position
+     * @param button Any mouse button pressed
+     * @throws NullPointerException if source, type, or button are null
+     * @throws IllegalArgumentException if type, scrollDelta and button are
+     *             incompatible
+     */
     public MouseEvent(Type type, MouseEventSource source, int x, int y, int scrollDelta, MouseButton button) {
         if (source == null)
             throw new NullPointerException("Event source cannot be null");
         if (type == null)
             throw new NullPointerException("Type cannot be null");
         if (button == null)
-            throw new IllegalArgumentException("MouseButton cannot be null");
+            throw new IllegalArgumentException("Mouse button cannot be null");
+        
+        // verify state
+        if (type == Type.PRESS || type == Type.RELEASE) {
+            if (button == MouseButton.NONE)
+                throw new IllegalArgumentException("Button cannot be NONE for a " + type + " event");
+        } else {
+            if (button != MouseButton.NONE)
+                throw new IllegalArgumentException("Button must be NONE for a " + type + " event");
+        }
+        if (type == Type.SCROLL) {
+            if (scrollDelta == 0)
+                throw new IllegalArgumentException("Scroll delta must be non-zero for a " + type + " event");
+        } else {
+            if (scrollDelta != 0)
+                throw new IllegalArgumentException("Scroll delta must be 0 for a " + type + " event");
+        }
         
         this.source = source;
         this.type = type;
         this.button = button;
     }
     
+    /**
+     * @return The type of mouse event
+     */
     public Type getEventType() {
         return type;
     }
     
+    /**
+     * <p>
+     * Get the x position of the mouse at the time of the event. Given that most
+     * event sources are windows, the x position's 0 value is defined as the
+     * left edge of the window and positive x values extend to the right edge.
+     * <p>
+     * If drags outside of the window's bounds are supported, the x value can be
+     * negative or larger than the width of the window.
+     * 
+     * @return The mouse's x position
+     */
     public int getX() {
         return x;
     }
     
+    /**
+     * <p>
+     * Get the y position of the mouse at the time of the event. Given that most
+     * event sources are windows, the y position's 0 value is defined as the
+     * bottom edge of the window and positive y values extend to the top of the
+     * window.
+     * <p>
+     * If drags outside of the window's bounds are supported, the y value can be
+     * negative or larger than the height of the window.
+     * 
+     * @return The mouse's y position
+     */
     public int getY() {
         return y;
     }
     
+    /**
+     * Get the amount of scroll increments the scroll wheel has been adjusted
+     * by. This value is always 0 if the type is not SCROLL. When the event is a
+     * SCROLL event it will be a positive or negative value. A positive value
+     * represents a backwards scrolling motion; a negative value is a forwards
+     * motion.
+     * 
+     * @return The scroll delta
+     */
     public int getScrollDelta() {
         return scrollDelta;
     }
     
+    /**
+     * Get the mouse button that produced the event if the type is PRESS or
+     * RELEASE. MOVE and SCROLL will always return NONE. PRESS and RELEASE will
+     * always return one of LEFT, RIGHT or CENTER.
+     * 
+     * @return The mouse button of the event
+     */
     public MouseButton getButton() {
         return button;
     }

ferox-input/src/main/java/com/ferox/input/MouseEventSource.java

 package com.ferox.input;
 
+/**
+ * <p>
+ * MouseEventSource is an event source for mouse events. Most often it is a
+ * window of some kind that can obtain focus from the OS and receive events
+ * either through AWT, JInput, or LWJGL's native input library.
+ * <p>
+ * The source is then responsible for converting those low-level events into
+ * {@link MouseEvent MouseEvents} and dispatching them to registered
+ * {@link MouseListener MouseListeners}. There is no guarantee about the order
+ * in which registered listeners are invoked when an event occurs.
+ * 
+ * @author Michael Ludwig
+ */
 public interface MouseEventSource extends EventSource {
+    /**
+     * Register the given MouseListener with this MouseEventSource. Nothing is
+     * done if the given listener has already been added.
+     * 
+     * @param listener The listener to add
+     * @throws NullPointerException if listener is null
+     */
     public void addMouseListener(MouseListener listener);
     
+    /**
+     * Remove the given MouseListener from this MouseEventSource. Nothing is
+     * done if the given listener has never been added, or was already removed.
+     * 
+     * @param listener The listener to remove
+     * @throws NullPointerException if listener is null
+     */
     public void removeMouseListener(MouseListener listener);
 }

ferox-input/src/main/java/com/ferox/input/MouseKeyEventDispatcher.java

+package com.ferox.input;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * <p>
+ * MouseKeyEventDispatcher is a convenience class that implements the necessary
+ * logic to register and unregister listeners as required by the
+ * {@link MouseEventSource} and {@link KeyEventSource} interfaces, and dispatch
+ * events to those listeners.
+ * <p>
+ * Event sources can create a MouseKeyEventDispatcher and delegate their
+ * interface methods to the dispatcher as appropriate. When their low-level
+ * event system or polling produces an event, they can then invoke
+ * {@link #dispatchEvent(Event)} to have their dispatcher invoke all listeners
+ * on an internal thread.
+ * <p>
+ * When an event source is destroyed, they must be sure to call
+ * {@link #shutdown()} to end the internal dispatch thread.
+ * <p>
+ * This class is thread safe.
+ * 
+ * @author Michael Ludwig
+ */
+public class MouseKeyEventDispatcher {
+    private final ExecutorService executor;
+    private final CopyOnWriteArrayList<KeyListener> keyListeners;
+    private final CopyOnWriteArrayList<MouseListener> mouseListeners;
+    
+    private final MouseKeyEventSource source;
+    
+    /**
+     * Create a new MouseKeyEventDispatcher.
+     * 
+     * @param source The event source that produced events dispatched by this
+     *            dispatcher
+     */
+    public MouseKeyEventDispatcher(MouseKeyEventSource source) {
+        if (source == null)
+            throw new NullPointerException("Source cannot be null");
+        this.source = source;
+        executor = Executors.newFixedThreadPool(1);
+        keyListeners = new CopyOnWriteArrayList<KeyListener>();
+        mouseListeners = new CopyOnWriteArrayList<MouseListener>();
+    }
+    
+    /**
+     * @return The source of events that are dispatched by this dispatcher
+     */
+    public MouseKeyEventSource getSource() {
+        return source;
+    }
+    
+    /**
+     * Function compatible with
+     * {@link KeyEventSource#addKeyListener(KeyListener)}.
+     * 
+     * @param listener The listener to register
+     */
+    public void addKeyListener(KeyListener listener) {
+        if (listener == null)
+            throw new NullPointerException("KeyListener cannot be null");
+        keyListeners.addIfAbsent(listener);
+    }
+    
+    /**
+     * Function compatible with
+     * {@link KeyEventSource#removeKeyListener(KeyListener)}.
+     * 
+     * @param listener The listener to unregister
+     */
+    public void removeKeyListener(KeyListener listener) {
+        if (listener == null)
+            throw new NullPointerException("KeyListener cannot be null");
+        keyListeners.remove(listener);
+    }
+
+    /**
+     * Function compatible with
+     * {@link MouseEventSource#addMouseListener(MouseListener)}.
+     * 
+     * @param listener The listener to register
+     */
+    public void addMouseListener(MouseListener listener) {
+        if (listener == null)
+            throw new NullPointerException("MouseListener cannot be null");
+        mouseListeners.addIfAbsent(listener);
+    }
+
+    /**
+     * Function compatible with
+     * {@link MouseEventSource#addMouseListener(MouseListener)}.
+     * 
+     * @param listener The listener to unregister
+     */
+    public void removeMouseListener(MouseListener listener) {
+        if (listener == null)
+            throw new NullPointerException("MouseListener cannot be null");
+        mouseListeners.remove(listener);
+    }
+    
+    /**
+     * Dispatch the given event to all registered listeners that are interested
+     * in the event. This will be invoked on an internal thread managed by this
+     * dispatcher.
+     * 
+     * @param e The event to dispatch to listeners
+     * @throws IllegalArgumentException if e's source is not the same source as
+     *             the dispatcher's
+     */
+    public void dispatchEvent(Event e) {
+        if (e.getSource() != source)
+            throw new IllegalArgumentException("Event's source does not match this dispatcher's source");
+        
+        try {
+            if (e instanceof MouseEvent)
+                executor.submit(new MouseEventTask((MouseEvent) e));
+            else if (e instanceof KeyEvent)
+                executor.submit(new KeyEventTask((KeyEvent) e));
+            else
+                throw new UnsupportedOperationException("Unsupported type of event: " + e.getClass());
+        } catch(RejectedExecutionException ree) {
+            // ignore
+        }
+    }
+
+    /**
+     * Shutdown the internal thread that processes dispatched events. After this
+     * is invoked, calls to {@link #dispatchEvent(Event)} will perform no
+     * action.
+     */
+    public void shutdown() {
+        executor.shutdownNow();
+    }
+    
+    private class KeyEventTask implements Runnable {
+        private final KeyEvent e;
+        
+        public KeyEventTask(KeyEvent e) {
+            this.e = e;
+        }
+        
+        @Override
+        public void run() {
+            for (KeyListener l: keyListeners) {
+                l.handleEvent(e);
+            }
+        }
+    }
+    
+    private class MouseEventTask implements Runnable {
+        private final MouseEvent e;
+        
+        public MouseEventTask(MouseEvent e) {
+            this.e = e;
+        }
+        
+        @Override
+        public void run() {
+            for (MouseListener l: mouseListeners) {
+                l.handleEvent(e);
+            }
+        }
+    }
+}

ferox-input/src/main/java/com/ferox/input/MouseKeyEventSource.java

 package com.ferox.input;
 
+/**
+ * MouseKeyEventSource is a combined interface for both MouseEvents and
+ * KeyEvents. When both types of events are required, or produced from a source,
+ * this interface should be used.
+ * 
+ * @author Michael Ludwig
+ */
 public interface MouseKeyEventSource extends MouseEventSource, KeyEventSource {
 
 }

ferox-input/src/main/java/com/ferox/input/MouseListener.java

 package com.ferox.input;
 
+/**
+ * <p>
+ * MouseListener is the event listener interface for handling mouse input
+ * events. Before a listener can receive events, it must be added to a
+ * {@link MouseEventSource}. Its event handler, {@link #handleEvent(MouseEvent)}
+ * , is invoked every time the user presses or releases a mouse button, scrolls
+ * a mouse wheel, or moves the mouse.
+ * <p>
+ * The MouseEventSource may only be able to produce events while the mouse is
+ * inside the bounds of its window. Some OS's support reporting mouse movement
+ * events if the mouse is being "dragged" outside the window as long as the drag
+ * began within the source.
+ * 
+ * @author Michael Ludwig
+ */
 public interface MouseListener extends EventListener {
+    /**
+     * <p>
+     * Process the specified MouseEvent. This will be invoked as soon as
+     * possible after the real-world event occurs but there is obviously some
+     * delay. MouseListeners should strive to quickly return to allow other
+     * listeners to process the event.
+     * <p>
+     * Because mouse movement generates a new event for each slight movement,
+     * many events can be processed in succession.
+     * <p>
+     * This method will be invoked on an internal event-queue thread managed by
+     * the MouseEventSource this listener was registered to. Because of this,
+     * MouseListener implementations must be thread-safe.
+     * 
+     * @param event The mouse event that just occurred
+     */
     public void handleEvent(MouseEvent event);
-
 }