Index: pyatspi/registry.py
===================================================================
--- pyatspi/registry.py	(revision 1017)
+++ pyatspi/registry.py	(working copy)
@@ -136,7 +136,7 @@
     self.mode.synchronous = synchronous
     self.mode._global = global_    
    
-  def register(self, dc, key_set, mask, kind):
+  def registerKeystroke(self, dc, key_set, mask, kind):
     '''
     Starts keyboard event monitoring.
     
@@ -161,7 +161,7 @@
       for m in mask:
         dc.registerKeystrokeListener(self._this(), key_set, m, kind, self.mode)
 
-  def unregister(self, dc, key_set, mask, kind):
+  def unregisterKeystroke(self, dc, key_set, mask, kind):
     '''
     Stops keyboard event monitoring.
     
@@ -185,6 +185,28 @@
       for m in mask:
         dc.deregisterKeystrokeListener(self._this(), key_set, m, kind)
       
+  def registerDeviceEvent(self, dc, kind):
+    '''
+    Starts device event monitoring.
+    
+    @param dc: Reference to a device controller
+    @type dc: Accessibility.DeviceEventController
+    @param kind: Kind of events to monitor
+    @type kind: integer
+    '''
+    dc.registerDeviceEventListener(self._this(), kind)
+
+  def unregisterDeviceEvent(self, dc, kind):
+    '''
+    Stops device event monitoring.
+    
+    @param dc: Reference to a device controller
+    @type dc: Accessibility.DeviceEventController
+    @param kind: Kind of events to monitor
+    @type kind: integer
+    '''
+    dc.deregisterDeviceEventListener(self._this(), kind)
+
   def queryInterface(self, repo_id):
     '''
     Reports that this class only implements the AT-SPI DeviceEventListener 
@@ -502,7 +524,7 @@
       # None means all modifier combinations
       mask = utils.allModifiers()
     # register for new keystrokes on the observer
-    ob.register(self.dev, key_set, mask, kind)
+    ob.registerKeystroke(self.dev, key_set, mask, kind)
 
   def deregisterKeystrokeListener(self, client, key_set=[], mask=0, 
                                   kind=(constants.KEY_PRESSED_EVENT, 
@@ -533,8 +555,61 @@
       # None means all modifier combinations
       mask = utils.allModifiers()
     # register for new keystrokes on the observer
-    ob.unregister(self.dev, key_set, mask, kind)
+    ob.unregisterKeystroke(self.dev, key_set, mask, kind)
 
+  def registerDeviceEventListener(self, client,
+                                  kind=(constants.BUTTON_PRESSED_EVENT, 
+                                        constants.BUTTON_RELEASED_EVENT),
+                                  synchronous=True, preemptive=True, 
+                                  global_=False):
+    '''
+    Registers a listener for device events.
+    
+    @param client: Callable to be invoked when the event occurs
+    @type client: callable
+    @param kind: Kind of events to watch, BUTTON_PRESSED_EVENT or 
+      BUTTON_RELEASED_EVENT.
+    @type kind: list
+    @param synchronous: Should the callback notification be synchronous, giving
+      the client the chance to consume the event?
+    @type synchronous: boolean
+    @param preemptive: Should the callback be allowed to preempt / consume the
+      event?
+    @type preemptive: boolean
+    @param global_: Should callback occur even if an application not supporting
+      AT-SPI is in the foreground? (requires xevie)
+    @type global_: boolean
+    '''
+    try:
+      # see if we already have an observer for this client
+      ob = self.clients[client]
+    except KeyError:
+      # create a new device observer for this client
+      ob = _DeviceObserver(self, synchronous, preemptive, global_)
+      # store the observer to client mapping, and the inverse
+      self.clients[ob] = client
+      self.clients[client] = ob
+    # register for new device event on the observer
+    ob.registerDeviceEvent(self.dev, kind)
+
+  def deregisterDeviceEventListener(self, client,
+                                  kind=(constants.BUTTON_PRESSED_EVENT, 
+                                        constants.BUTTON_RELEASED_EVENT)):
+    '''
+    Deregisters a listener for device events.
+    
+    @param client: Callable to be invoked when the event occurs
+    @type client: callable
+    @param kind: Kind of events to stop watching, KEY_PRESSED_EVENT or 
+      KEY_RELEASED_EVENT.
+    @type kind: list
+    @raise KeyError: When the client isn't already registered for events
+    '''
+    # see if we already have an observer for this client
+    ob = self.clients[client]
+    # unregister device event.
+    ob.unregisterDeviceEvent(self.dev, key_set, mask, kind)
+
   def generateKeyboardEvent(self, keycode, keysym, kind):
     '''
     Generates a keyboard event. One of the keycode or the keysym parameters
Index: pyatspi/constants.py
===================================================================
--- pyatspi/constants.py	(revision 1017)
+++ pyatspi/constants.py	(working copy)
@@ -188,7 +188,7 @@
 # these are the prefixes for the variable names we want to pull out of the 
 # Accessibility module
 prefixes = ['ROLE_', 'STATE_', 'TEXT_', 'MODIFIER_', 'LOCALE_', 'RELATION_',
-            'KEY_', 'MATCH_', 'SORT_', 'LAYER_']
+            'KEY_', 'MATCH_', 'SORT_', 'LAYER_', 'BUTTON_']
 # for each variable name in the Accessibility namespace, check if it starts
 # with at least one of the prefixes above; if it does, add a 2-tuple of 
 # variable name and value to the values list
Index: pyatspi/event.py
===================================================================
--- pyatspi/event.py	(revision 1017)
+++ pyatspi/event.py	(working copy)
@@ -77,9 +77,13 @@
     @rtype: string
     '''
     if self.type == constants.KEY_PRESSED_EVENT:
-      kind = 'pressed'
+      kind = 'key pressed'
     elif self.type == constants.KEY_RELEASED_EVENT:
-      kind = 'released'
+      kind = 'key released'
+    elif self.type == constants.BUTTON_PRESSED_EVENT:
+      kind = 'button pressed'
+    elif self.type == constants.BUTTON_RELEASED_EVENT:
+      kind = 'button released'
     return '''\
 %s
 \thw_code: %d
Index: registryd/deviceeventcontroller.c
===================================================================
--- registryd/deviceeventcontroller.c	(revision 1017)
+++ registryd/deviceeventcontroller.c	(working copy)
@@ -340,7 +340,8 @@
  
 static gboolean
 spi_dec_button_update_and_emit (SpiDEController *controller, 
-				guint mask_return)
+				guint mask_return,
+                                gboolean *consumed)
 {
   CORBA_Environment ev;
   Accessibility_Event e;
@@ -458,7 +459,10 @@
 						&ev);  
 	  }
 	else
-	  spi_dec_set_unlatch_pending (controller, mask_return);
+          {
+	    spi_dec_set_unlatch_pending (controller, mask_return);
+            *consumed = TRUE;
+          }
 
         CORBA_free (e.any_data._value);
       }
@@ -473,7 +477,7 @@
 
 static guint
 spi_dec_mouse_check (SpiDEController *controller, 
-		     int *x, int *y, gboolean *moved)
+		     int *x, int *y, gboolean *moved, gboolean *consumed)
 {
   Accessibility_Event e;
   Accessibility_EventDetails *details;
@@ -497,7 +501,7 @@
    */
   if (mask_return != mouse_mask_state) 
     {
-      while (spi_dec_button_update_and_emit (controller, mask_return));
+      while (spi_dec_button_update_and_emit (controller, mask_return, consumed));
     }
 
   if (*x != last_mouse_pos->x || *y != last_mouse_pos->y) 
@@ -583,8 +587,9 @@
   int x, y;  
   gboolean moved;
   guint mask_return;
+  gboolean consumed;
 
-  mask_return = spi_dec_mouse_check (controller, &x, &y, &moved);
+  mask_return = spi_dec_mouse_check (controller, &x, &y, &moved, &consumed);
   
   if ((mask_return & key_modifier_mask) !=
       (mouse_mask_state & key_modifier_mask)) 
@@ -1119,6 +1124,8 @@
   controller = SPI_DEVICE_EVENT_CONTROLLER (data);
   priv = (DEControllerPrivateData *)
 	  g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);  
+  GdkFilterReturn return_val = GDK_FILTER_CONTINUE;
+  gboolean is_consumed = FALSE;
 
   if (xevent->type == MappingNotify)
     xmkeymap = NULL;
@@ -1127,8 +1134,6 @@
     {
       if (controller->xevie_display == NULL)
         {
-          gboolean is_consumed;
-
           is_consumed =
             spi_device_event_controller_forward_key_event (controller, xevent);
 
@@ -1153,6 +1158,7 @@
               XAllowEvents (display, AsyncKeyboard, CurrentTime);
               if (n_events)
                 XUngrabKeyboard (display, CurrentTime);
+              return_val = GDK_FILTER_REMOVE;
             }
           else
             {
@@ -1162,7 +1168,7 @@
             }
         }
 
-      return GDK_FILTER_CONTINUE;
+      return return_val;
     }
   if (xevent->type == ButtonPress || xevent->type == ButtonRelease)
     {
@@ -1185,7 +1191,9 @@
 	    {
 	      int x, y;
 	      gboolean moved;
-	      spi_dec_mouse_check (controller, &x, &y, &moved);
+	      spi_dec_mouse_check (controller, &x, &y, &moved, &is_consumed);
+              if (is_consumed)
+                return_val = GDK_FILTER_REMOVE;
 	    }
 	  /* we check again, since the previous call may have 
 	     changed this flag */
@@ -1237,7 +1245,7 @@
       XSynchronize (display, FALSE);
     }
   
-  return GDK_FILTER_CONTINUE;
+  return return_val;
 }
 
 static int
