2009-03-03 Mark Doffman <mark.doffman@codethink.co.uk>
authorMark Doffman <mdoff@silver-wind.(none)>
Tue, 3 Mar 2009 15:49:26 +0000 (15:49 +0000)
committerMark Doffman <mdoff@silver-wind.(none)>
Tue, 3 Mar 2009 15:57:02 +0000 (15:57 +0000)
        * pyatspi/*
          Fix event printing without an accessible source.
          Fix event deregistration.
        * registryd/deviceeventcontroller.c
          Fix the mouse and keyboard events from the DEC.

pyatspi/applicationcache.py
pyatspi/base.py
pyatspi/event.py
pyatspi/registry.py
registryd/deviceeventcontroller.c

index 6381188..a03f1ca 100644 (file)
@@ -18,6 +18,7 @@ from accessiblecache import AccessibleCache
 from desktop import Desktop
 from factory import accessible_factory
 from event import Event as _Event
+from base import AccessibleObjectNotAvailable
 
 from interfaces import *
 
@@ -66,7 +67,10 @@ class TestApplicationCache(object):
                 available at the given D-Bus name.
                 """
                 cls = accessible_factory.get_accessible_class(ATSPI_APPLICATION)
-                return cls(app_name, self.application_cache[app_name].root, self, ATSPI_APPLICATION)
+                try:
+                        return cls(app_name, self.application_cache[app_name].root, self, ATSPI_APPLICATION)
+                except KeyError:
+                        raise AccessibleObjectNotAvailable ()
 
         def create_accessible(self, app_name, acc_path, interface, dbus_object=None):
                 """
@@ -88,7 +92,10 @@ class TestApplicationCache(object):
                         return Desktop(self)
                 else:
                         cls = accessible_factory.get_accessible_class(interface)
-                        return cls(app_name, acc_path, self, interface, dbus_object=dbus_object)
+                        try:
+                                return cls(app_name, acc_path, self, interface, dbus_object=dbus_object)
+                        except KeyError:
+                                raise AccessibleObjectNotAvailable ()
 
         @property
         def connection(self):
@@ -182,7 +189,10 @@ class ApplicationCache(object):
                         return Desktop(self)
                 else:
                         cls = accessible_factory.get_accessible_class(ATSPI_APPLICATION)
-                        return cls(app_name, self.application_cache[app_name].root, self, ATSPI_APPLICATION)
+                        try:
+                                return cls(app_name, self.application_cache[app_name].root, self, ATSPI_APPLICATION)
+                        except KeyError:
+                                raise AccessibleObjectNotAvailable ()
 
         def create_accessible(self, app_name, acc_path, interface, dbus_object=None):
                 """
@@ -203,7 +213,10 @@ class ApplicationCache(object):
                         return Desktop(self)
                 else:
                         cls = accessible_factory.get_accessible_class(interface)
-                        return cls(app_name, acc_path, self, interface, dbus_object=dbus_object)
+                        try:
+                                return cls(app_name, acc_path, self, interface, dbus_object=dbus_object)
+                        except KeyError:
+                                raise AccessibleObjectNotAvailable ()
 
         @property
         def connection(self):
index ae097d1..7adafbb 100644 (file)
@@ -20,6 +20,7 @@ import interfaces
 
 __all__ = [
            "AccessibleObjectNoLongerExists",
+           "AccessibleObjectNotAvailable",
            "Enum",
            "BaseProxy",
           ]
@@ -27,6 +28,9 @@ __all__ = [
 class AccessibleObjectNoLongerExists(Exception):
         pass
 
+class AccessibleObjectNotAvailable(Exception):
+        pass
+
 #------------------------------------------------------------------------------
 
 class Enum(int):
index 1065f20..ea416cb 100644 (file)
@@ -14,6 +14,7 @@
 
 import interfaces
 from accessible import BoundingBox
+from base import AccessibleObjectNotAvailable
 
 __all__ = [
                 "Event",
@@ -27,6 +28,7 @@ _interface_to_klass = {
                 "org.freedesktop.atspi.Event.Object":"object",
                 "org.freedesktop.atspi.Event.Window":"window",
                 "org.freedesktop.atspi.Event.Mouse":"mouse",
+                "org.freedesktop.atspi.Event.Keyboard":"keyboard",
                 "org.freedesktop.atspi.Event.Terminal":"terminal",
                 "org.freedesktop.atspi.Event.Document":"document",
                 "org.freedesktop.atspi.Event.Focus":"focus",
@@ -36,6 +38,7 @@ _klass_to_interface = {
                 "object":"org.freedesktop.atspi.Event.Object",
                 "window":"org.freedesktop.atspi.Event.Window",
                 "mouse":"org.freedesktop.atspi.Event.Mouse",
+                "keyboard":"org.freedesktop.atspi.Event.Keyboard",
                 "terminal":"org.freedesktop.atspi.Event.Terminal",
                 "document":"org.freedesktop.atspi.Event.Document",
                 "focus":"org.freedesktop.atspi.Event.Focus",
@@ -134,12 +137,12 @@ def event_type_to_signal_reciever(bus, cache, event_handler, event_type):
         if event_type.minor:
                 kwargs['arg0'] = event_type.minor
 
-        def handler_wrapper(minor, detail1, detail2, any_data, 
+        def handler_wrapper(minor, detail1, detail2, any_data,
                             sender=None, interface=None, member=None, path=None):
                 event = Event(cache, path, sender, interface, member, (minor, detail1, detail2, any_data))
                 return event_handler(event)
 
-        return bus.add_signal_receiver(handler_wrapper, **kwargs) 
+        return bus.add_signal_receiver(handler_wrapper, **kwargs)
 
 #------------------------------------------------------------------------------
 
@@ -219,15 +222,21 @@ class Event(object):
         @property
         def host_application(self):
                 if not self._application:
-                        return self._cache.create_application(self._source_application)
+                        try:
+                                return self._cache.create_application(self._source_application)
+                        except AccessibleObjectNotAvailable:
+                                pass
                 return self._application
 
         @property
         def source(self):
                 if not self._source:
-                        self._source = self._cache.create_accessible(self._source_application,
-                                                                     self._source_path,
-                                                                     interfaces.ATSPI_ACCESSIBLE)
+                        try:
+                                self._source = self._cache.create_accessible(self._source_application,
+                                                                             self._source_path,
+                                                                             interfaces.ATSPI_ACCESSIBLE)
+                        except AccessibleObjectNotAvailable:
+                                pass
                 return self._source
 
         @property
index 71ab550..3f7088f 100644 (file)
@@ -100,8 +100,8 @@ class _Registry(object):
                 self._children_changed_type = _EventType("object:children-changed")
                 self._children_changed_listeners = {}
 
-                self.queue = Queue()
                 self.clients = {}
+                self.deviceClients = {}
 
         def __call__(self):
                 """
@@ -162,6 +162,8 @@ class _Registry(object):
                 """
                 return _Desktop(self._app_cache)
 
+        # -------------------------------------------------------------------------------
+
         def _callClients(self, register, event):
                 for client in register.keys():
                         client(event)
@@ -217,6 +219,8 @@ class _Registry(object):
                 if registered == []:
                         del(register[client])
 
+        # -------------------------------------------------------------------------------
+
         def registerEventListener(self, client, *names):
                 """
                 Registers a new client callback for the given event names. Supports 
@@ -281,9 +285,8 @@ class _Registry(object):
 
                 for name in names:
                         remove_type = _EventType(name)
-
-                        for i in range(0, len(registered) - 1):
-                                (type_name, signal_match) = registered[i]
+                        for i in range (0, len(registered)):
+                                type_name, signal_match = registered[i]
                                 registered_type = _EventType(type_name)
 
                                 if remove_type.is_subtype(registered_type):
@@ -303,6 +306,8 @@ class _Registry(object):
 
                 return missing
 
+        # -------------------------------------------------------------------------------
+
         def registerKeystrokeListener(self,
                                       client,
                                       key_set=[],
@@ -341,13 +346,13 @@ class _Registry(object):
                 """
                 try:
                         # see if we already have an observer for this client
-                        ob = self.clients[client]
+                        ob = self.deviceClients[client]
                 except KeyError:
                         # create a new device observer for this client
                         ob = KeyboardDeviceEventListener(self, synchronous, preemptive, global_)
                         # store the observer to client mapping, and the inverse
-                        self.clients[ob] = client
-                        self.clients[client] = ob
+                        self.deviceClients[ob] = client
+                        self.deviceClients[client] = ob
                 if mask is None:
                         # None means all modifier combinations
                         mask = utils.allModifiers()
@@ -380,48 +385,13 @@ class _Registry(object):
                 @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]
+                ob = self.deviceClients[client]
                 if mask is None:
                         # None means all modifier combinations
                         mask = utils.allModifiers()
                 # register for new keystrokes on the observer
                 ob.unregister(self.dev, key_set, mask, kind)
 
-        def generateKeyboardEvent(self, keycode, keysym, kind):
-                """
-                Generates a keyboard event. One of the keycode or the keysym parameters
-                should be specified and the other should be None. The kind parameter is 
-                required and should be one of the KEY_PRESS, KEY_RELEASE, KEY_PRESSRELEASE,
-                KEY_SYM, or KEY_STRING.
-
-                @param keycode: Hardware keycode or None
-                @type keycode: integer
-                @param keysym: Symbolic key string or None
-                @type keysym: string
-                @param kind: Kind of event to synthesize
-                @type kind: integer
-                """
-                if keysym is None:
-                        self.dev.generateKeyboardEvent(keycode, '', kind)
-                else:
-                        self.dev.generateKeyboardEvent(None, keysym, kind)
-
-        def generateMouseEvent(self, x, y, name):
-                """
-                Generates a mouse event at the given absolute x and y coordinate. The kind
-                of event generated is specified by the name. For example, MOUSE_B1P 
-                (button 1 press), MOUSE_REL (relative motion), MOUSE_B3D (butten 3 
-                double-click).
-
-                @param x: Horizontal coordinate, usually left-hand oriented
-                @type x: integer
-                @param y: Vertical coordinate, usually left-hand oriented
-                @type y: integer
-                @param name: Name of the event to generate
-                @type name: string
-                """
-                self.dev.generateMouseEvent(x, y, name)
-
         def handleDeviceEvent(self, event, ob):
                 """
                 Dispatches L{event.DeviceEvent}s to registered clients. Clients are called
@@ -454,90 +424,54 @@ class _Registry(object):
                 except Exception:
                         # print the exception, but don't let it stop notification
                         traceback.print_exc()
-        def handleEvent(self, event):
+
+        # -------------------------------------------------------------------------------
+
+        def pumpQueuedEvents (self):
+                """
+                No Longer needed all application events are asyncronous.
                 """
-                Handles an AT-SPI event by either queuing it for later dispatch when the
-                L{Registry.async} flag is set, or dispatching it immediately.
+                pass
 
-                @param event: AT-SPI event
-                @type event: L{event.Event}
+        def flushEvents (self):
                 """
-                if self.async:
-                        # queue for now
-                        self.queue.put_nowait(event)
-                else:
-                        # dispatch immediately
-                        self._dispatchEvent(event)
+                No Longer needed all application events are asyncronous.
+                """
+                pass
 
-        def _dispatchEvent(self, event):
+        # -------------------------------------------------------------------------------
+
+        def generateKeyboardEvent(self, keycode, keysym, kind):
                 """
-                Dispatches L{event.Event}s to registered clients. Clients are called in
-                the order they were registered for the given AT-SPI event. If any client
-                returns True, callbacks cease for the event for clients of this registry 
-                instance. Clients of other registry instances and clients in other processes 
-                are unaffected.
+                Generates a keyboard event. One of the keycode or the keysym parameters
+                should be specified and the other should be None. The kind parameter is 
+                required and should be one of the KEY_PRESS, KEY_RELEASE, KEY_PRESSRELEASE,
+                KEY_SYM, or KEY_STRING.
 
-                @param event: AT-SPI event
-                @type event: L{event.Event}
+                @param keycode: Hardware keycode or None
+                @type keycode: integer
+                @param keysym: Symbolic key string or None
+                @type keysym: string
+                @param kind: Kind of event to synthesize
+                @type kind: integer
                 """
-                et = event.type
-                try:
-                        # try to get the client registered for this event type
-                        clients = self.clients[et.name]
-                except KeyError:
-                        try:
-                                # we may not have registered for the complete subtree of events
-                                # if our tree does not list all of a certain type (e.g.
-                                # object:state-changed:*); try again with klass and major only
-                                if et.detail is not None:
-                                        # Strip the 'detail' field.
-                                        clients = self.clients['%s:%s:%s' % (et.klass, et.major, et.minor)]
-                                elif et.minor is not None:
-                                        # The event could possibly be object:state-changed:*.
-                                        clients = self.clients['%s:%s' % (et.klass, et.major)]
-                        except KeyError:
-                                # client may have unregistered recently, ignore event
-                                return
-                # make the call to each client
-                consume = False
-                for client in clients:
-                        try:
-                                consume = client(event) or False
-                        except Exception:
-                                # print the exception, but don't let it stop notification
-                                traceback.print_exc()
-                        if consume or event.consume:
-                                # don't allow further processing if a client returns True
-                                break
-
-        def flushEvents(self):
-                """
-                Flushes the event queue by destroying it and recreating it.
-                """
-                self.queue = Queue()
-
-        def pumpQueuedEvents(self, num=-1):
-                """
-                Provides asynch processing of events in the queue by executeing them with 
-                _dispatchEvent() (as is done immediately when synch processing). 
-                This method would normally be called from a main loop or idle function.
-
-                @param num: Number of events to pump. If number is negative it pumps
-                the entire queue. Default is -1.
-                @type num: integer
-                @return: True if queue is not empty after events were pumped.
-                @rtype: boolean
+                if keysym is None:
+                        self.dev.generateKeyboardEvent(keycode, '', kind)
+                else:
+                        self.dev.generateKeyboardEvent(None, keysym, kind)
+
+        def generateMouseEvent(self, x, y, name):
+                """
+                Generates a mouse event at the given absolute x and y coordinate. The kind
+                of event generated is specified by the name. For example, MOUSE_B1P 
+                (button 1 press), MOUSE_REL (relative motion), MOUSE_B3D (butten 3 
+                double-click).
+
+                @param x: Horizontal coordinate, usually left-hand oriented
+                @type x: integer
+                @param y: Vertical coordinate, usually left-hand oriented
+                @type y: integer
+                @param name: Name of the event to generate
+                @type name: string
                 """
-                if num < 0:
-                        # Dequeue as many events as currently in the queue.
-                        num = self.queue.qsize()
-                for i in xrange(num):
-                        try:
-                                # get next waiting event
-                                event = self.queue.get_nowait()
-                        except Queue.Empty:
-                                break
-                        self._dispatchEvent(event)
-
-                return not self.queue.empty()
+                self.dev.generateMouseEvent(x, y, name)
index 2d41ef4..d12f412 100644 (file)
@@ -371,13 +371,26 @@ spi_dec_clear_unlatch_pending (SpiDEController *controller)
   priv->xkb_latch_mask = 0;
 }
 
-static void emit(SpiDEController *controller, const char *name, int first_type, ...)
+static void emit(SpiDEController *controller, const char *interface, const char *name, int a1, int a2)
 {
-  va_list arg;
+  DBusMessage *signal = NULL;
+  DBusMessageIter iter, iter_variant;
+  int nil = 0;
+  const char *minor = "";
+  const char *path = SPI_DBUS_PATH_DEC;
 
-  va_start(arg, first_type);
-  spi_dbus_emit_valist(controller->bus, SPI_DBUS_PATH_DEC, SPI_DBUS_INTERFACE_DEC, name, first_type, arg);
-  va_end(arg);
+  signal = dbus_message_new_signal (path, interface, name);
+
+  dbus_message_iter_init_append (signal, &iter);
+
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &minor);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &a1);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &a2);
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "i", &iter_variant);
+      dbus_message_iter_append_basic (&iter_variant, DBUS_TYPE_INT32, &nil);
+  dbus_message_iter_close_container (&iter, &iter_variant);
+
+  dbus_connection_send (controller->bus, signal, NULL);
 }
 
 static gboolean
@@ -484,7 +497,7 @@ spi_dec_button_update_and_emit (SpiDEController *controller,
        if (!is_consumed)
          {
            dbus_uint32_t x = last_mouse_pos->x, y = last_mouse_pos->y;
-           emit(controller, SPI_DBUS_INTERFACE_EVENT_MOUSE, "button", event_detail, x, y);
+           emit(controller, SPI_DBUS_INTERFACE_EVENT_MOUSE, "button", x, y);
          }
        else
          spi_dec_set_unlatch_pending (controller, mask_return);
@@ -528,10 +541,10 @@ spi_dec_mouse_check (SpiDEController *controller,
     {
       // TODO: combine these two signals?
       dbus_uint32_t ix = *x, iy = *y;
-      emit(controller, SPI_DBUS_INTERFACE_EVENT_MOUSE, "abs", NULL, ix, iy);
+      emit(controller, SPI_DBUS_INTERFACE_EVENT_MOUSE, "abs", ix, iy);
       ix -= last_mouse_pos->x;
       iy -= last_mouse_pos->y;
-      emit(controller, SPI_DBUS_INTERFACE_EVENT_MOUSE, "rel", NULL, ix, iy);
+      emit(controller, SPI_DBUS_INTERFACE_EVENT_MOUSE, "rel", ix, iy);
       last_mouse_pos->x = *x;
       last_mouse_pos->y = *y;
       *moved = True;
@@ -563,7 +576,7 @@ spi_dec_emit_modifier_event (SpiDEController *controller, guint prev_mask,
 
   d1 = prev_mask & key_modifier_mask;
   d2 = current_mask & key_modifier_mask;
-      emit(controller, SPI_DBUS_INTERFACE_EVENT_KEYBOARD, "modifiers", NULL, d1, d2);
+      emit(controller, SPI_DBUS_INTERFACE_EVENT_KEYBOARD, "modifiers", d1, d2);
 }
 
 static gboolean
@@ -965,7 +978,10 @@ Accessibility_DeviceEventListener_notifyEvent(SpiDEController *controller,
                                               DEControllerListener *listener,
                                               const Accessibility_DeviceEvent *key_event)
 {
-  DBusMessage *message = dbus_message_new_method_call(listener->bus_name, listener->path, "org.freedesktop.atspi.Registry", "notifyEvent");
+  DBusMessage *message = dbus_message_new_method_call(listener->bus_name,
+                                                      listener->path,
+                                                      SPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER,
+                                                      "notifyEvent");
   DBusError error;
   dbus_bool_t consumed = FALSE;