2008-11-13 Mark Doffman <mark.doffman@codethink.co.uk>
authorMark Doffman <mdoff@silver-wind.(none)>
Fri, 7 Nov 2008 17:26:28 +0000 (17:26 +0000)
committerMark Doffman <mdoff@silver-wind.(none)>
Thu, 13 Nov 2008 17:19:27 +0000 (17:19 +0000)
        * pyatspi/*
          Add initial device event functionality to
          the python bindings.

        * registryd/*
          Re-integrate the device event controller
          into the registry daemon.

        * xml/org.freedesktop.atspi.DeviceEventController.xml
          Fix an error in the translation of the
          registerKeystrokeListener method to D-Bus XML

14 files changed:
pyatspi/Accessibility.py
pyatspi/Makefile.am
pyatspi/accessiblecache.py
pyatspi/applicationcache.py
pyatspi/deviceevent.py [new file with mode: 0644]
pyatspi/interfaces.py
pyatspi/registry.py
registryd/Makefile.am
registryd/deviceeventcontroller.c
registryd/deviceeventcontroller.h
registryd/reentrant-list.c [new file with mode: 0644]
registryd/reentrant-list.h [new file with mode: 0644]
registryd/registry-main.c
xml/org.freedesktop.atspi.DeviceEventController.xml

index 7c15dc9..18558d7 100644 (file)
@@ -22,8 +22,10 @@ from collection import *
 from component import *
 from constants import *
 from desktop import *
+from deviceevent import *
 from document import *
 from editabletext import *
+from event import *
 from hyperlink import *
 from hypertext import *
 from image import *
index f7bf95a..0520b38 100644 (file)
@@ -11,6 +11,7 @@ pyatspi_PYTHON = \
                component.py            \
                constants.py            \
                desktop.py              \
+               deviceevent.py          \
                document.py             \
                editabletext.py         \
                event.py                \
index 5729f71..15a3353 100644 (file)
@@ -135,7 +135,7 @@ class AccessibleCache(object):
                                        ("parent", 0, 0, ""))
                         self._registry._notifyParentChange(event)
 
-                added, removed = _list_items_added_removed (olddata.children, newdata.children):
+                added, removed = _list_items_added_removed (olddata.children, newdata.children)
 
                 if added:
                         event = _Event(self._registry._cache,
index ada9dbb..a50c51b 100644 (file)
@@ -19,7 +19,7 @@ from desktop import Desktop
 from factory import accessible_factory
 from event import Event as _Event
 
-import interfaces
+from interfaces import *
 
 __all__ = [
            "ApplicationCache",
@@ -63,8 +63,8 @@ class TestApplicationCache(object):
                 Creates an accessible object for the root of the application
                 available at the given D-Bus name.
                 """
-                cls = accessible_factory.get_accessible_class(interfaces.ATSPI_APPLICATION)
-                return cls(app_name, self.application_cache[app_name].root, self, interfaces.ATSPI_APPLICATION)
+                cls = accessible_factory.get_accessible_class(ATSPI_APPLICATION)
+                return cls(app_name, self.application_cache[app_name].root, self, ATSPI_APPLICATION)
 
         def create_accessible(self, app_name, acc_path, interface, dbus_object=None):
                 """
@@ -114,10 +114,6 @@ class ApplicationCache(object):
                      D-Bus path.
         """
 
-        _REGISTRY_PATH = '/org/freedesktop/atspi/registry'
-        _REGISTRY_INTERFACE = 'org.freedesktop.atspi.Registry'
-        _REGISTRY_NAME = 'org.freedesktop.atspi.Registry'
-
         # An accessible path of '/' implies the desktop object, whatever the application name.
         _DESKTOP_PATH = '/'
 
@@ -132,13 +128,13 @@ class ApplicationCache(object):
                 self.application_cache = {}
 
                 self._regsig = connection.add_signal_receiver(self.update_handler,
-                                                              dbus_interface=ApplicationCache._REGISTRY_INTERFACE,
+                                                              dbus_interface=ATSPI_REGISTRY_INTERFACE,
                                                               signal_name="updateApplications")
 
-                obj = connection.get_object(ApplicationCache._REGISTRY_NAME,
-                                            ApplicationCache._REGISTRY_PATH,
+                obj = connection.get_object(ATSPI_REGISTRY_NAME,
+                                            ATSPI_REGISTRY_PATH,
                                             introspect=False)
-                self._app_register = dbus.Interface(obj, ApplicationCache._REGISTRY_INTERFACE)
+                self._app_register = dbus.Interface(obj, ATSPI_REGISTRY_INTERFACE)
 
                 self.application_list.extend(self._app_register.getApplications())
                 for bus_name in self.application_list:
@@ -151,7 +147,7 @@ class ApplicationCache(object):
                         self.application_cache[bus_name] = AccessibleCache(self._registry, self._connection, bus_name)
                         event = _Event(self,
                                        ApplicationCache._DESKTOP_PATH,
-                                       ApplicationCache._REGISTRY_NAME,
+                                       ATSPI_REGISTRY_NAME,
                                        "org.freedesktop.atspi.Event.Object",
                                        "children-changed",
                                        ("add", 0, 0, ""))
@@ -161,7 +157,7 @@ class ApplicationCache(object):
                         del(self.application_cache[bus_name])
                         event = _Event(self,
                                        ApplicationCache._DESKTOP_PATH,
-                                       ApplicationCache._REGISTRY_NAME,
+                                       ATSPI_REGISTRY_NAME,
                                        "org.freedesktop.atspi.Event.Object",
                                        "children-changed",
                                        ("remove", 0, 0, ""))
@@ -180,11 +176,11 @@ class ApplicationCache(object):
                 Creates an accessible object for the root of the application
                 available at the given D-Bus name.
                 """
-                if app_name == ApplicationCache._REGISTRY_NAME:
+                if app_name == ATSPI_REGISTRY_NAME:
                         return Desktop(self)
                 else:
-                        cls = accessible_factory.get_accessible_class(interfaces.ATSPI_APPLICATION)
-                        return cls(app_name, self.application_cache[app_name].root, self, interfaces.ATSPI_APPLICATION)
+                        cls = accessible_factory.get_accessible_class(ATSPI_APPLICATION)
+                        return cls(app_name, self.application_cache[app_name].root, self, ATSPI_APPLICATION)
 
         def create_accessible(self, app_name, acc_path, interface, dbus_object=None):
                 """
diff --git a/pyatspi/deviceevent.py b/pyatspi/deviceevent.py
new file mode 100644 (file)
index 0000000..b644224
--- /dev/null
@@ -0,0 +1,541 @@
+#Copyright (C) 2008 Codethink Ltd
+
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Lesser General Public
+#License version 2 as published by the Free Software Foundation.
+
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#GNU General Public License for more details.
+#You should have received a copy of the GNU Lesser General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import dbus as _dbus
+import dbus.service as _service
+import interfaces
+
+from base import Enum as _Enum
+
+#------------------------------------------------------------------------------
+
+class PressedEventType(_Enum):
+        _enum_lookup = {
+                0:'KEY_PRESSED_EVENT',
+                1:'KEY_RELEASED_EVENT',
+                2:'BUTTON_PRESSED_EVENT',
+                3:'BUTTON_RELEASED_EVENT',
+        }
+
+KEY_PRESSED_EVENT = PressedEventType(0)
+KEY_RELEASED_EVENT = PressedEventType(1)
+BUTTON_PRESSED_EVENT = PressedEventType(2)
+BUTTON_RELEASED_EVENT = PressedEventType(3)
+
+#------------------------------------------------------------------------------
+
+class ControllerEventMask(_Enum):
+        _enum_lookup = {
+                1:'KEY_PRESSED_EVENT_MASK',
+                2:'KEY_RELEASED_EVENT_MASK',
+                4:'BUTTON_PRESSED_EVENT_MASK',
+                8:'BUTTON_RELEASED_EVENT_MASK',
+        }
+
+KEY_PRESSED_EVENT_MASK = ControllerEventMask(1)
+KEY_RELEASED_EVENT_MASK = ControllerEventMask(2)
+BUTTON_PRESSED_EVENT_MASK = ControllerEventMask(4)
+BUTTON_RELEASED_EVENT_MASK = ControllerEventMask(8)
+
+#------------------------------------------------------------------------------
+
+class KeyEventType(_Enum):
+        _enum_lookup = {
+                0:'KEY_PRESSED',
+                1:'KEY_RELEASED',
+        }
+KEY_PRESSED = KeyEventType(0)
+KEY_RELEASED = KeyEventType(1)
+
+#------------------------------------------------------------------------------
+
+class KeySynthType(_Enum):
+        _enum_lookup = {
+                0:'KEY_PRESS',
+                1:'KEY_RELEASE',
+                2:'KEY_PRESSRELEASE',
+                3:'KEY_SYM',
+                4:'KEY_STRING',
+        }
+
+KEY_PRESS = KeySynthType(0)
+KEY_PRESSRELEASE = KeySynthType(2)
+KEY_RELEASE = KeySynthType(1)
+KEY_STRING = KeySynthType(4)
+KEY_SYM = KeySynthType(3)
+
+#------------------------------------------------------------------------------
+
+class ModifierType(_Enum):
+        _enum_lookup = {
+                0:'MODIFIER_SHIFT',
+                1:'MODIFIER_SHIFTLOCK',
+                2:'MODIFIER_CONTROL',
+                3:'MODIFIER_ALT',
+                4:'MODIFIER_META',
+                5:'MODIFIER_META2',
+                6:'MODIFIER_META3',
+                7:'MODIFIER_NUMLOCK',
+        }
+
+MODIFIER_ALT = ModifierType(3)
+MODIFIER_CONTROL = ModifierType(2)
+MODIFIER_META = ModifierType(4)
+MODIFIER_META2 = ModifierType(5)
+MODIFIER_META3 = ModifierType(6)
+MODIFIER_NUMLOCK = ModifierType(7)
+MODIFIER_SHIFT = ModifierType(0)
+MODIFIER_SHIFTLOCK = ModifierType(1)
+
+#------------------------------------------------------------------------------
+
+class DeviceEvent(list):
+        """
+        Wraps an AT-SPI device event with a more Pythonic interface. Primarily adds
+        a consume attribute which can be used to cease propagation of a device event.
+
+        @ivar consume: Should this event be consumed and not allowed to pass on to
+                observers further down the dispatch chain in this process or possibly
+                system wide?
+        @type consume: boolean
+        @ivar type: Kind of event, KEY_PRESSED_EVENT or KEY_RELEASED_EVENT
+        @type type: Accessibility.EventType
+        @ivar id: Serial identifier for this key event
+        @type id: integer
+        @ivar hw_code: Hardware scan code for the key
+        @type hw_code: integer
+        @ivar modifiers: Modifiers held at the time of the key event
+        @type modifiers: integer
+        @ivar timestamp: Time at which the event occurred relative to some platform
+                dependent starting point (e.g. XWindows start time)
+        @type timestamp: integer
+        @ivar event_string: String describing the key pressed (e.g. keysym)
+        @type event_string: string
+        @ivar is_text: Is the event representative of text to be inserted (True), or 
+                of a control key (False)?
+        @type is_text: boolean
+        """
+        def __new__(cls, type, id, hw_code, modifiers, timestamp, event_string, is_text):
+                return list.__new__(cls, (type, id, hw_code, modifiers, timestamp, event_string, is_text))
+        def __init__(self, type, id, hw_code, modifiers, timestamp, event_string, is_text):
+                list.__init__(self, (type, id, hw_code, modifiers, timestamp, event_string, is_text))
+                self.consume = False
+        def _get_type(self):
+                return self[0]
+        def _set_type(self, val):
+                self[0] = val
+        type = property(fget=_get_type, fset=_set_type)
+        def _get_id(self):
+                return self[1]
+        def _set_id(self, val):
+                self[1] = val
+        id = property(fget=_get_id, fset=_set_id)
+        def _get_hw_code(self):
+                return self[2]
+        def _set_hw_code(self, val):
+                self[2] = val
+        hw_code = property(fget=_get_hw_code, fset=_set_hw_code)
+        def _get_modifiers(self):
+                return self[3]
+        def _set_modifiers(self, val):
+                self[3] = val
+        modifiers = property(fget=_get_modifiers, fset=_set_modifiers)
+        def _get_timestamp(self):
+                return self[4]
+        def _set_timestamp(self, val):
+                self[4] = val
+        timestamp = property(fget=_get_timestamp, fset=_set_timestamp)
+        def _get_event_string(self):
+                return self[5]
+        def _set_event_string(self, val):
+                self[5] = val
+        event_string = property(fget=_get_event_string, fset=_set_event_string)
+        def _get_is_text(self):
+                return self[6]
+        def _set_is_text(self, val):
+                self[6] = val
+        is_text = property(fget=_get_is_text, fset=_set_is_text)
+
+        def __str__(self):
+                """
+                Builds a human readable representation of the event.
+
+                @return: Event description
+                @rtype: string
+                """
+                if self.type == constants.KEY_PRESSED_EVENT:
+                        kind = 'pressed'
+                elif self.type == constants.KEY_RELEASED_EVENT:
+                        kind = 'released'
+                return """\
+%s
+\thw_code: %d
+\tevent_string: %s
+\tmodifiers: %d
+\tid: %d
+\ttimestamp: %d
+\tis_text: %s""" % (kind, self.hw_code, self.event_string, self.modifiers,
+                self.id, self.timestamp, self.is_text)
+
+#------------------------------------------------------------------------------
+
+class EventListenerMode(list):
+        def __new__(cls, synchronous, preemptive, global_):
+                return list.__new__(cls, (synchronous, preemptive, global_))
+        def __init__(self, synchronous, preemptive, global_):
+                list.__init__(self, (synchronous, preemptive, global_))
+        def _get_synchronous(self):
+                return self[0]
+        def _set_synchronous(self, val):
+                self[0] = val
+        synchronous = property(fget=_get_synchronous, fset=_set_synchronous)
+        def _get_preemptive(self):
+                return self[1]
+        def _set_preemptive(self, val):
+                self[1] = val
+        preemptive = property(fget=_get_preemptive, fset=_set_preemptive)
+        def _get_global_(self):
+                return self[2]
+        def _set_global_(self, val):
+                self[2] = val
+        global_ = property(fget=_get_global_, fset=_set_global_)
+
+#------------------------------------------------------------------------------
+
+class KeyDefinition(list):
+        def __new__(cls, keycode, keysym, keystring, unused):
+                return list.__new__(cls, (keycode, keysym, keystring, unused))
+        def __init__(self, keycode, keysym, keystring, unused):
+                list.__init__(self, (keycode, keysym, keystring, unused))
+        def _get_keycode(self):
+                return self[0]
+        def _set_keycode(self, val):
+                self[0] = val
+        keycode = property(fget=_get_keycode, fset=_set_keycode)
+        def _get_keysym(self):
+                return self[1]
+        def _set_keysym(self, val):
+                self[1] = val
+        keysym = property(fget=_get_keysym, fset=_set_keysym)
+        def _get_keystring(self):
+                return self[2]
+        def _set_keystring(self, val):
+                self[2] = val
+        keystring = property(fget=_get_keystring, fset=_set_keystring)
+        def _get_unused(self):
+                return self[3]
+        def _set_unused(self, val):
+                self[3] = val
+        unused = property(fget=_get_unused, fset=_set_unused)
+
+#------------------------------------------------------------------------------
+
+class DeviceEventController(object):
+        """
+        The interface via which clients request notification of device
+        events, and through which device events may be simulated.
+        """
+
+        def __init__ (self, connection):
+                dec_object = connection.get_object(interfaces.ATSPI_REGISTRY_NAME,
+                                                   interfaces.ATSPI_DEVICE_EVENT_CONTROLLER_PATH,
+                                                   introspect=True)
+                self._dec = _dbus.Interface(dec_object, interfaces.ATSPI_DEVICE_EVENT_CONTROLLER_INTERFACE)
+
+        def registerKeystrokeListener(self,
+                                      event_listener,
+                                      keys,
+                                      event_mask,
+                                      key_event_types,
+                                      event_listener_mode):
+                """
+                Register to intercept keyboard events, and either pass them on
+                or consume them.
+                @param : listener
+                A DeviceEventListener which will intercept key events. 
+                @param : keys
+                A list of KeyDefinition indicating which keys to intercept, or KEYSET_ALL_KEYS.
+                @param : mask
+                A ControllerEventMask bitmask for filtering the intercepted key events.
+                @param : type
+                A list of KeyEventType
+                @param : mode
+                An EventListenerMode indicating whether the listener should receive
+                the events synchronously, potentially consuming them, or just
+                be notified asynchronously of those events that have been generated.
+
+                @return True if the DeviceEventListener was successfully registered
+                for the requested KeySet, ControllerEventMask, event types, and
+                EventListenerMode; otherwise returns False.
+                """
+                func = self._dec.get_dbus_method("registerKeystrokeListener")
+                return func(event_listener,
+                            keys,
+                            event_mask,
+                            key_event_types,
+                            event_listener_mode)
+
+        def deregisterKeystrokeListener(self,
+                                        event_listener,
+                                        keys,
+                                        event_mask,
+                                        key_event_types):
+                """
+                De-register a previously registered keyboard eventlistener.
+                @param : listener
+                A DeviceEventListener which will intercept key events.
+                @param : keys
+                A list of KeyDefinition indicating which keys to intercept, or KEYSET_ALL_KEYS.
+                @param : mask
+                A ControllerEventMask filtering the intercepted key events.
+                @param : type
+                A list of KeyEventType
+                """
+                func = self._dec.get_dbus_method("deregisterKeystrokeListener")
+                return func(event_listener,
+                            keys,
+                            event_mask,
+                            key_event_types)
+
+        def registerDeviceEventListener(self,
+                                        event_listener,
+                                        event_types):
+                """
+                Register to intercept events, and either pass them on or consume
+                them. To listen to keyboard events use registerKeystrokeListener
+                instead. 
+                @param : listener
+                A DeviceEventListener which will intercept events.
+                @param : typeseq
+                A list of EventType indicating which event types to listen for.
+                @return True if successful, False if not
+                """
+                func = self._dec.get_dbus_method("registerDeviceEventListener")
+                return func(event_listener, event_types)
+
+        def deregisterDeviceEventListener(self,
+                                          event_listener,
+                                          event_types):
+                """
+                De-register a previously registered keyboard eventlistener.
+                @param : listener
+                A DeviceEventListener which will intercept events.
+                @param : typeseq
+                A List of EventType indicating which event types to stop listening
+                for.
+                """
+                func = self._dec.get_dbus_method("deregisterDeviceEventListener")
+                return func(event_listener, event_types)
+
+        def notifyListenersSync(self, event):
+                """
+                Notify the Registry instance that a device event has taken place,
+                and allow pre-emptive listeners the opportunity to 'consume'
+                the event and thus prevent its further issuance/forwarding. This
+                is the method used by accessibility bridges to forward "toolkit
+                dependent" device events to the Registry from the application's
+                process space.
+                @return True if the event was consumed by a (pre-emptive) listener,
+                False if not (in which case the device event will be forwarded
+                as normal to any application which would normally receive it,
+                e.g. the currently active application in the case of mouse or
+                keyboard events).
+                """
+                func = self._dec.get_dbus_method("notifyListenersSync")
+                return func(event)
+
+        def notifyListenersAsync(self, event):
+                """
+                Notify the Registry instance that a device event has taken place
+                in an asynchronous manner. This is the method used by accessibility
+                bridges to forward "toolkit dependent" device events to the Registry
+                from the application's process space. If the event in question
+                is potentially pre-emptible. notifyListenersSync should be used
+                instead.
+                """
+                func = self._dec.get_dbus_method("notifyListenersAsync")
+                return func(event)
+
+        def generateKeyboardEvent(self, keycode, keystring, type):
+                """
+                Synthesize a keyboard event.
+                @param : keycode
+                A long integer indicating the keycode of the keypress to be synthesized.
+                @param : keystring
+                an optional UTF-8 string indicating a complex keyboard input
+                event.
+                @param : type
+                A KeySynthType indicating the type of event(s) to be synthesized:
+                a key press, release, press-release pair, or a complex input
+                string (for instance from an internationalized or complex text
+                input method, or a composed character).
+                """
+                func = self._dec.get_dbus_method("generateKeyboardEvent")
+                return func(keycode, keystring, type)
+
+        def generateMouseEvent(self, x, y, name):
+                """
+                Synthesize a mouse event.
+                @param : x
+                A long integer indicating the screen x coord for the mouse event.
+                @param : y
+                A long integer indicating the screen y coord for the mouse event.
+                @param : name
+                A string indicating the type of mouse event, e.g. "button1up"
+                """
+                func = self._dec.get_dbus_method("generateMouseEvent")
+                return func(x, y, name)
+
+#------------------------------------------------------------------------------
+
+class _TestDeviceEventController(object):
+        """
+        Used for testing when no Registry daemon is present.
+        """
+
+        def registerKeystrokeListener(self, event_listener, keys, event_mask, key_event_types, event_listener_mode):
+                return True
+
+        def deregisterKeystrokeListener(self, event_listener, keys, event_mask, key_event_types):
+                pass
+
+        def registerDeviceEventListener(self, event_listener, event_types):
+                return True
+
+        def deregisterDeviceEventListener(self, event_listener, event_types):
+                pass
+
+        def notifyListenersSync(self, event):
+                return False
+
+        def notifyListenersAsync(self, event):
+                pass
+
+        def generateKeyboardEvent(self, keycode, keystring, type):
+                pass
+
+        def generateMouseEvent(self, x, y, name):
+                pass
+
+#------------------------------------------------------------------------------
+
+class KeyboardDeviceEventListener(_service.Object):
+        """
+        Observes keyboard press and release events.
+
+        @ivar registry: The L{Registry} that created this observer
+        @type registry: L{Registry}
+        @ivar key_set: Set of keys to monitor
+        @type key_set: list of integer
+        @ivar mask: Watch for key events while these modifiers are held
+        @type mask: integer
+        @ivar kind: Kind of events to monitor
+        @type kind: integer
+        @ivar mode: Keyboard event mode
+        @type mode: Accessibility.EventListenerMode
+        """
+
+        _next_listener_id = 0
+
+        def _get_unique_path (self):
+                KeyboardDeviceEventListener._next_listener_id += 1
+                return "/org/freedesktop/atspi/keyeventlistener/%d" % (KeyboardDeviceEventListener._next_listener_id,)
+
+        def __init__(self, registry, synchronous, preemptive, global_):
+                """
+                Creates a mode object that defines when key events will be received from 
+                the system. Stores all other information for later registration.
+
+                @param registry: The L{Registry} that created this observer
+                @type registry: L{Registry}
+                @param synchronous: Handle the key event synchronously?
+                @type synchronous: boolean
+                @param preemptive: Allow event to be consumed?
+                @type preemptive: boolean
+                @param global_: Watch for events on inaccessible applications too?
+                @type global_: boolean
+                """
+                self._upath = self._get_unique_path()
+                _service.Object.__init__(self, registry._bus, self._upath)
+                self.mode = EventListenerMode(synchronous, preemptive, global_)
+
+        def register(self, dc, key_set, mask, kind):
+                """
+                Starts keyboard event monitoring.
+
+                @param dc: Reference to a device controller
+                @type dc: Accessibility.DeviceEventController
+                @param key_set: Set of keys to monitor
+                @type key_set: list of integer
+                @param mask: Integer modifier mask or an iterable over multiple masks to
+                        unapply all at once
+                @type mask: integer, iterable, or None
+                @param kind: Kind of events to monitor
+                @type kind: integer
+                """
+                try:
+                        # check if the mask is iterable
+                        iter(mask)
+                except TypeError:
+                        # register a single integer if not
+                        dc.registerKeystrokeListener(self._upath, key_set, mask, kind, self.mode)
+                else:
+                        for m in mask:
+                                dc.registerKeystrokeListener(self._upath, key_set, m, kind, self.mode)
+
+        def unregister(self, dc, key_set, mask, kind):
+                """
+                Stops keyboard event monitoring.
+
+                @param dc: Reference to a device controller
+                @type dc: Accessibility.DeviceEventController
+                @param key_set: Set of keys to monitor
+                @type key_set: list of integer
+                @param mask: Integer modifier mask or an iterable over multiple masks to
+                        unapply all at once
+                @type mask: integer, iterable, or None
+                @param kind: Kind of events to monitor
+                @type kind: integer
+                """
+                try:
+                        # check if the mask is iterable
+                        iter(mask)
+                except TypeError:
+                        # unregister a single integer if not
+                        dc.deregisterKeystrokeListener(self._upath, key_set, mask, kind)
+                else:
+                        for m in mask:
+                                dc.deregisterKeystrokeListener(self._upath, key_set, m, kind)
+
+        @_service.method(dbus_interface=interfaces.ATSPI_DEVICE_EVENT_LISTENER_INTERFACE,
+                         in_signature="(uinnisb)",
+                         out_signature="b")
+        def notifyEvent(self, ev):
+                """
+                Notifies the L{Registry} that an event has occurred. Wraps the raw event 
+                object in our L{Event} class to support automatic ref and unref calls. An
+                observer can return True to indicate this event should not be allowed to pass 
+                to other AT-SPI observers or the underlying application.
+
+                @param ev: Keyboard event
+                @type ev: Accessibility.DeviceEvent
+                @return: Should the event be consumed (True) or allowed to pass on to other
+                        AT-SPI observers (False)?
+                @rtype: boolean
+                """
+                # wrap the device event
+                ev = event.DeviceEvent(ev)
+                return self.registry.handleDeviceEvent(ev, self)
+
+#END---------------------------------------------------------------------------
index 2fad9ec..0883fb9 100644 (file)
@@ -30,3 +30,11 @@ ATSPI_STREAMABLE_CONTENT = 'org.freedesktop.atspi.Content'
 ATSPI_TABLE = 'org.freedesktop.atspi.Table'
 ATSPI_TEXT = 'org.freedesktop.atspi.Text'
 ATSPI_VALUE = 'org.freedesktop.atspi.Value'
+
+ATSPI_REGISTRY_INTERFACE = 'org.freedesktop.atspi.Registry'
+ATSPI_REGISTRY_PATH = '/org/freedesktop/atspi/registry'
+ATSPI_REGISTRY_NAME = 'org.freedesktop.atspi.Registry'
+
+ATSPI_DEVICE_EVENT_CONTROLLER_INTERFACE = 'org.freedesktop.atspi.DeviceEventController'
+ATSPI_DEVICE_EVENT_CONTROLLER_PATH = '/org/freedesktop/atspi/registry/deviceeventcontroller'
+ATSPI_DEVICE_EVENT_LISTENER_INTERFACE = 'org.freedesktop.atspi.DeviceEventListener'
index 477f932..2c73cf1 100644 (file)
@@ -23,78 +23,20 @@ import os as _os
 import dbus as _dbus
 import gobject as _gobject
 
-from base import Enum as _Enum
 from desktop import Desktop as _Desktop
+
 from event import EventType as _EventType
 from event import event_type_to_signal_reciever as _event_type_to_signal_reciever
+
 from applicationcache import TestApplicationCache, ApplicationCache
 
 from dbus.mainloop.glib import DBusGMainLoop as _DBusGMainLoop
-_DBusGMainLoop(set_as_default=True)
-
-#------------------------------------------------------------------------------
-
-class PressedEventType(_Enum):
-        _enum_lookup = {
-                0:'KEY_PRESSED_EVENT',
-                1:'KEY_RELEASED_EVENT',
-                2:'BUTTON_PRESSED_EVENT',
-                3:'BUTTON_RELEASED_EVENT',
-        }
-
-KEY_PRESSED_EVENT = PressedEventType(0)
-KEY_RELEASED_EVENT = PressedEventType(1)
-BUTTON_PRESSED_EVENT = PressedEventType(2)
-BUTTON_RELEASED_EVENT = PressedEventType(3)
-#------------------------------------------------------------------------------
-
-class KeyEventType(_Enum):
-        _enum_lookup = {
-                0:'KEY_PRESSED',
-                1:'KEY_RELEASED',
-        }
-KEY_PRESSED = KeyEventType(0)
-KEY_RELEASED = KeyEventType(1)
 
-#------------------------------------------------------------------------------
-
-class KeySynthType(_Enum):
-        _enum_lookup = {
-                0:'KEY_PRESS',
-                1:'KEY_RELEASE',
-                2:'KEY_PRESSRELEASE',
-                3:'KEY_SYM',
-                4:'KEY_STRING',
-        }
-
-KEY_PRESS = KeySynthType(0)
-KEY_PRESSRELEASE = KeySynthType(2)
-KEY_RELEASE = KeySynthType(1)
-KEY_STRING = KeySynthType(4)
-KEY_SYM = KeySynthType(3)
-
-#------------------------------------------------------------------------------
+from Queue import Queue
+from deviceevent import *
+from deviceevent import _TestDeviceEventController
 
-class ModifierType(_Enum):
-        _enum_lookup = {
-                0:'MODIFIER_SHIFT',
-                1:'MODIFIER_SHIFTLOCK',
-                2:'MODIFIER_CONTROL',
-                3:'MODIFIER_ALT',
-                4:'MODIFIER_META',
-                5:'MODIFIER_META2',
-                6:'MODIFIER_META3',
-                7:'MODIFIER_NUMLOCK',
-        }
-
-MODIFIER_ALT = ModifierType(3)
-MODIFIER_CONTROL = ModifierType(2)
-MODIFIER_META = ModifierType(4)
-MODIFIER_META2 = ModifierType(5)
-MODIFIER_META3 = ModifierType(6)
-MODIFIER_NUMLOCK = ModifierType(7)
-MODIFIER_SHIFT = ModifierType(0)
-MODIFIER_SHIFTLOCK = ModifierType(1)
+_DBusGMainLoop(set_as_default=True)
 
 #------------------------------------------------------------------------------
 
@@ -136,11 +78,13 @@ class _Registry(object):
                 app_name = None
                 if "ATSPI_TEST_APP_NAME" in _os.environ.keys():
                         app_name = _os.environ["ATSPI_TEST_APP_NAME"]
+
                 if app_name:
-                        self._app_name = app_name
                         self._appcache = TestApplicationCache(self, self._bus, app_name)
+                        self.dev = _TestDeviceEventController()
                 else:
                         self._appcache = ApplicationCache(self, self._bus)
+                        self.dev = DeviceEventController(self._bus)
 
                 self._event_listeners = {}
 
@@ -156,6 +100,9 @@ class _Registry(object):
                 self._children_changed_type = _EventType("object:children-changed")
                 self._children_changed_listeners = {}
 
+                self.queue = Queue()
+                self.clients = {}
+
         def __call__(self):
                 """
                 @return: This instance of the registry
@@ -384,7 +331,20 @@ class _Registry(object):
                         AT-SPI is in the foreground? (requires xevie)
                 @type global_: boolean
                 """
-                pass
+                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 = KeyboardDeviceEventListener(self, synchronous, preemptive, global_)
+                        # store the observer to client mapping, and the inverse
+                        self.clients[ob] = client
+                        self.clients[client] = ob
+                if mask is None:
+                        # None means all modifier combinations
+                        mask = utils.allModifiers()
+                # register for new keystrokes on the observer
+                ob.register(self.dev, key_set, mask, kind)
 
         def deregisterKeystrokeListener(self,
                                         client,
@@ -411,7 +371,13 @@ class _Registry(object):
                 @type kind: list
                 @raise KeyError: When the client isn't already registered for events
                 """
-                pass
+                # see if we already have an observer for this client
+                ob = self.clients[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):
                 """
@@ -427,7 +393,10 @@ class _Registry(object):
                 @param kind: Kind of event to synthesize
                 @type kind: integer
                 """
-                pass
+                if keysym is None:
+                        self.dev.generateKeyboardEvent(keycode, '', kind)
+                else:
+                        self.dev.generateKeyboardEvent(None, keysym, kind)
 
         def generateMouseEvent(self, x, y, name):
                 """
@@ -443,7 +412,7 @@ class _Registry(object):
                 @param name: Name of the event to generate
                 @type name: string
                 """
-                pass
+                self.dev.generateMouseEvent(x, y, name)
 
         def handleDeviceEvent(self, event, ob):
                 """
@@ -459,29 +428,86 @@ class _Registry(object):
                 @param event: AT-SPI device event
                 @type event: L{event.DeviceEvent}
                 @param ob: Observer that received the event
-                @type ob: L{_DeviceObserver}
+                @type ob: L{KeyboardDeviceEventListener}
 
                 @return: Should the event be consumed (True) or allowed to pass on to other
                         AT-SPI observers (False)?
                 @rtype: boolean
                 """
-                return True
+                try:
+                        # try to get the client registered for this event type
+                        client = self.clients[ob]
+                except KeyError:
+                        # client may have unregistered recently, ignore event
+                        return False
+                # make the call to the client
+                try:
+                        return client(event) or event.consume
+                except Exception:
+                        # print the exception, but don't let it stop notification
+                        traceback.print_exc()
  
         def handleEvent(self, event):
-                """                
+                """
                 Handles an AT-SPI event by either queuing it for later dispatch when the
                 L{Registry.async} flag is set, or dispatching it immediately.
 
                 @param event: AT-SPI event
                 @type event: L{event.Event}
                 """
-                pass
+                if self.async:
+                        # queue for now
+                        self.queue.put_nowait(event)
+                else:
+                        # dispatch immediately
+                        self._dispatchEvent(event)
+
+        def _dispatchEvent(self, event):
+                """
+                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.
+
+                @param event: AT-SPI event
+                @type event: L{event.Event}
+                """
+                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.
                 """
-                pass
+                self.queue = Queue()
 
         def pumpQueuedEvents(self, num=-1):
                 """
@@ -495,4 +521,15 @@ class _Registry(object):
                 @return: True if queue is not empty after events were pumped.
                 @rtype: boolean
                 """
-                return False
+                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()
index 9fecd7d..fe657e0 100644 (file)
@@ -20,7 +20,8 @@ at_spi_registryd_SOURCES =    \
        registry-main.c         \
        registry.c              \
        registry.h              \
+       deviceeventcontroller.c \
+       deviceeventcontroller.h \
+       reentrant-list.c        \
+       reentrant-list.h        \
        ucs2keysym.c
-
-#      deviceeventcontroller.c
-#      deviceeventcontroller.h
index 4b0ba0d..bfc8258 100644 (file)
@@ -56,6 +56,7 @@
 #include <atk-adaptor/spi-private.h>
 #include <spi-common/keymasks.h>
 #include <droute/droute.h>
+#include <droute/introspect-loader.h>
 #include <spi-common/spi-dbus.h>
 #include <spi-common/spi-types.h>
 
@@ -339,7 +340,7 @@ spi_dec_clear_unlatch_pending (SpiDEController *controller)
 {
   DEControllerPrivateData *priv = 
     g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);
-  priv->xkb_latch_mask = 0; 
+  priv->xkb_latch_mask = 0;
 }
  
 static void emit(SpiDEController *controller, const char *name, int first_type, ...)
@@ -347,12 +348,12 @@ static void emit(SpiDEController *controller, const char *name, int first_type,
   va_list arg;
 
   va_start(arg, first_type);
-  spi_dbus_emit_valist(controller->registry->droute.bus, SPI_DBUS_PATH_DEC, SPI_DBUS_INTERFACE_DEC, name, first_type, arg);
+  spi_dbus_emit_valist(controller->droute->bus, SPI_DBUS_PATH_DEC, SPI_DBUS_INTERFACE_DEC, name, first_type, arg);
   va_end(arg);
 }
 
 static gboolean
-spi_dec_button_update_and_emit (SpiDEController *controller, 
+spi_dec_button_update_and_emit (SpiDEController *controller,
                                guint mask_return)
 {
   Accessibility_DeviceEvent mouse_e;
@@ -540,14 +541,13 @@ spi_dec_emit_modifier_event (SpiDEController *controller, guint prev_mask,
 static gboolean
 spi_dec_poll_mouse_moved (gpointer data)
 {
-  SpiRegistry *registry = SPI_REGISTRY (data);
-  SpiDEController *controller = registry->de_controller;
-  int x, y;  
+  SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER(data);
+  int x, y;
   gboolean moved;
   guint mask_return;
 
   mask_return = spi_dec_mouse_check (controller, &x, &y, &moved);
-  
+
   if ((mask_return & key_modifier_mask) !=
       (mouse_mask_state & key_modifier_mask)) 
     {
@@ -561,7 +561,7 @@ spi_dec_poll_mouse_moved (gpointer data)
 static gboolean
 spi_dec_poll_mouse_idle (gpointer data)
 {
-  if (! spi_dec_poll_mouse_moved (data)) 
+  if (! spi_dec_poll_mouse_moved (data))
     return TRUE;
   else
     {
@@ -597,12 +597,12 @@ spi_dec_ungrab_mouse (gpointer data)
 #endif
 
 static void
-spi_dec_init_mouse_listener (SpiRegistry *registry)
+spi_dec_init_mouse_listener (SpiDEController *dec)
 {
 #ifdef GRAB_BUTTON
   Display *display = spi_get_display ();
 #endif
-  g_timeout_add (100, spi_dec_poll_mouse_idle, registry);
+  g_timeout_add (100, spi_dec_poll_mouse_idle, dec);
 
 #ifdef GRAB_BUTTON
   if (display)
@@ -835,10 +835,6 @@ _deregister_keygrab (SpiDEController      *controller,
           cur_mask->pending_remove = TRUE;
        }
     }
-  else
-    {
-      DBG (1, g_warning ("De-registering non-existant grab"));
-    }
 }
 
 static void
@@ -917,7 +913,7 @@ spi_controller_register_device_listener (SpiDEController      *controller,
 
       controller->key_listeners = g_list_prepend (controller->key_listeners,
                                                  key_listener);
-      spi_dbus_add_disconnect_match (controller->registry->droute.bus, key_listener->listener.bus_name);
+      spi_dbus_add_disconnect_match (controller->droute->bus, key_listener->listener.bus_name);
       if (key_listener->mode->global)
         {
          return spi_controller_register_global_keygrabs (controller, key_listener);    
@@ -927,16 +923,19 @@ spi_controller_register_device_listener (SpiDEController      *controller,
       break;
   case SPI_DEVICE_TYPE_MOUSE:
       controller->mouse_listeners = g_list_prepend (controller->mouse_listeners, listener);
-      spi_dbus_add_disconnect_match (controller->registry->droute.bus, listener->bus_name);
+      spi_dbus_add_disconnect_match (controller->droute->bus, listener->bus_name);
       break;
   default:
-      DBG (1, g_warning ("listener registration for unknown device type.\n"));
       break;
   }
-  return FALSE; 
+  return FALSE;
 }
 
-static gboolean Accessibility_DeviceEventListener_notifyEvent(SpiRegistry *registry, DEControllerListener *listener, const Accessibility_DeviceEvent *key_event)
+static gboolean
+Accessibility_DeviceEventListener_notifyEvent(SpiDEController *controller,
+                                              SpiRegistry *registry,
+                                              DEControllerListener *listener,
+                                              const Accessibility_DeviceEvent *key_event)
 {
   DBusMessage *message = dbus_message_new_method_call(listener->bus_name, listener->path, "org.freedesktop.atspi.Registry", "notifyEvent");
   DBusError error;
@@ -947,7 +946,7 @@ static gboolean Accessibility_DeviceEventListener_notifyEvent(SpiRegistry *regis
   {
     // TODO: Evaluate performance: perhaps rework this whole architecture
     // to avoid blocking calls
-    DBusMessage *reply = dbus_connection_send_with_reply_and_block(registry->droute.bus, message, 1000, &error);
+    DBusMessage *reply = dbus_connection_send_with_reply_and_block(controller->droute->bus, message, 1000, &error);
     if (reply)
     {
       DBusError error;
@@ -1001,16 +1000,16 @@ spi_controller_notify_mouselisteners (SpiDEController                 *controlle
   is_consumed = FALSE;
   for (l2 = notify; l2 && !is_consumed; l2 = l2->next)
     {
-      DEControllerListener *listener = l2->data;           
+      DEControllerListener *listener = l2->data;
+
+      is_consumed = Accessibility_DeviceEventListener_notifyEvent (controller, controller->registry, listener, event);
 
-      is_consumed = Accessibility_DeviceEventListener_notifyEvent (controller->registry, listener, event);
-      
       spi_listener_clone_free ((DEControllerListener *) l2->data);
     }
 
   for (; l2; l2 = l2->next)
     {
-      DEControllerListener *listener = l2->data;           
+      DEControllerListener *listener = l2->data;
       spi_listener_clone_free (listener);
       /* clone doesn't have its own ref, so don't use spi_device_listener_free */
     }
@@ -1091,7 +1090,8 @@ spi_device_event_controller_forward_mouse_event (SpiDEController *controller,
        spi_controller_notify_mouselisteners (controller, &mouse_e);
       ix = last_mouse_pos->x;
       iy = last_mouse_pos->y;
-      emit(controller, event_name, DBUS_TYPE_UINT32, &ix, DBUS_TYPE_UINT32, &iy, DBUS_TYPE_INVALID);
+      /* TODO - Work out which part of the spec this emit is fulfilling */
+      //emit(controller, event_name, DBUS_TYPE_UINT32, &ix, DBUS_TYPE_UINT32, &iy, DBUS_TYPE_INVALID);
     }
 
   xkb_mod_unlatch_occurred = (xevent->type == ButtonPress ||
@@ -1231,8 +1231,6 @@ global_filter_fn (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
              priv->xkb_latch_mask = xkb_snev->latched_mods;
            }
        }
-        else
-              DBG (2, g_warning ("XKB event %d\n", xkb_ev->xkb_type));
       XSynchronize (display, FALSE);
     }
   
@@ -1257,10 +1255,14 @@ _spi_controller_device_error_handler (Display *display, XErrorEvent *error)
 static void
 spi_controller_register_with_devices (SpiDEController *controller)
 {
-  DEControllerPrivateData *priv = (DEControllerPrivateData *) 
-         g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);     
-  /* FIXME: should check for extension first! */
-  XTestGrabControl (spi_get_display (), True);
+  DEControllerPrivateData *priv;
+  int event_base, error_base, major_version, minor_version;
+
+  priv = (DEControllerPrivateData *) g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);
+  if (XTestQueryExtension (spi_get_display(), &event_base, &error_base, &major_version, &minor_version))
+    {
+      XTestGrabControl (spi_get_display (), True);
+    }
 
   /* calls to device-specific implementations and routines go here */
   /* register with: keyboard hardware code handler */
@@ -1459,7 +1461,7 @@ spi_controller_notify_keylisteners (SpiDEController                 *controller,
     {
       DEControllerKeyListener *key_listener = l2->data;            
 
-      is_consumed = Accessibility_DeviceEventListener_notifyEvent (controller->registry, &key_listener->listener, key_event) &&
+      is_consumed = Accessibility_DeviceEventListener_notifyEvent (controller, controller->registry, &key_listener->listener, key_event) &&
                    key_listener->mode->preemptive;
 
       spi_key_listener_clone_free (key_listener);
@@ -1780,7 +1782,7 @@ impl_register_keystroke_listener (DBusConnection *bus,
                                  DBusMessage *message,
                                  void *user_data)
 {
-  SpiDEController *controller = SPI_REGISTRY(user_data)->de_controller;
+  SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER(user_data);
   DEControllerKeyListener *dec_listener;
   DBusMessageIter iter, iter_array;
   const char *path;
@@ -1840,7 +1842,7 @@ impl_register_device_listener (DBusConnection *bus,
                                  DBusMessage *message,
                                  void *user_data)
 {
-  SpiDEController *controller = SPI_REGISTRY(user_data)->de_controller;
+  SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER(user_data);
   DEControllerListener *dec_listener;
   DBusError error;
   const char *path;
@@ -1913,7 +1915,7 @@ spi_controller_deregister_device_listener (SpiDEController            *controlle
 {
   RemoveListenerClosure  ctx;
 
-  ctx.bus = controller->registry->droute.bus;
+  ctx.bus = controller->droute->bus;
   ctx.listener = listener;
 
   spi_re_entrant_list_foreach (&controller->mouse_listeners,
@@ -1926,7 +1928,7 @@ spi_deregister_controller_key_listener (SpiDEController            *controller,
 {
   RemoveListenerClosure  ctx;
 
-  ctx.bus = controller->registry->droute.bus;
+  ctx.bus = controller->droute->bus;
   ctx.listener = (DEControllerListener *) key_listener;
 
   /* special case, copy keyset from existing controller list entry */
@@ -1935,7 +1937,7 @@ spi_deregister_controller_key_listener (SpiDEController            *controller,
       spi_re_entrant_list_foreach (&controller->key_listeners,
                                  copy_key_listener_cb, &ctx);
     }
-  
+
   spi_controller_deregister_global_keygrabs (controller, key_listener);
 
   spi_re_entrant_list_foreach (&controller->key_listeners,
@@ -1977,7 +1979,7 @@ impl_deregister_keystroke_listener (DBusConnection *bus,
                                  DBusMessage *message,
                                  void *user_data)
 {
-  SpiDEController *controller = SPI_REGISTRY(user_data)->de_controller;
+  SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER(user_data);
   DEControllerKeyListener *key_listener;
   DBusMessageIter iter, iter_array;
   const char *path;
@@ -2029,7 +2031,7 @@ impl_deregister_device_listener (DBusConnection *bus,
                                  DBusMessage *message,
                                  void *user_data)
 {
-  SpiDEController *controller = SPI_REGISTRY(user_data)->de_controller;
+  SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER(user_data);
   DEControllerListener *listener;
   DBusError error;
   const char *path;
@@ -2337,7 +2339,7 @@ dec_synth_keystring (SpiDEController *controller, const char *keystring)
  */
 static DBusMessage * impl_generate_keyboard_event (DBusConnection *bus, DBusMessage *message, void *user_data)
 {
-  SpiDEController *controller = SPI_REGISTRY(user_data)->de_controller;
+  SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER(user_data);
   DBusError error;
   dbus_int32_t keycode;
   char *keystring;
@@ -2400,10 +2402,6 @@ static DBusMessage * impl_generate_keyboard_event (DBusConnection *bus, DBusMess
                               keystring);
              break;
     }
-  if ((err = gdk_error_trap_pop ()))
-    {
-      DBG (-1, g_warning ("Error [%d] emitting keystroke", err));
-    }
   if (synth_type == Accessibility_KEY_SYM) {
     keysym = keycode;
   }
@@ -2497,7 +2495,7 @@ static DBusMessage * impl_generate_mouse_event (DBusConnection *bus, DBusMessage
 static DBusMessage *
 impl_notify_listeners_sync (DBusConnection *bus, DBusMessage *message, void *user_data)
 {
-  SpiDEController *controller = SPI_REGISTRY(user_data)->de_controller;
+  SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER(user_data);
   Accessibility_DeviceEvent event;
   dbus_bool_t ret;
   DBusMessage *reply;
@@ -2525,7 +2523,7 @@ impl_notify_listeners_sync (DBusConnection *bus, DBusMessage *message, void *use
 static DBusMessage *
 impl_notify_listeners_async (DBusConnection *bus, DBusMessage *message, void *user_data)
 {
-  SpiDEController *controller = SPI_REGISTRY(user_data)->de_controller;
+  SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER(user_data);
   Accessibility_DeviceEvent event;
   DBusMessage *reply;
 
@@ -2549,7 +2547,7 @@ spi_device_event_controller_class_init (SpiDEControllerClass *klass)
   GObjectClass * object_class = (GObjectClass *) klass;
 
   spi_device_event_controller_parent_class = g_type_class_peek_parent (klass);
-  
+
   object_class->finalize = spi_device_event_controller_object_finalize;
 
   if (!spi_dec_private_quark)
@@ -2651,18 +2649,6 @@ spi_device_event_controller_forward_key_event (SpiDEController *controller,
   return ret;
 }
 
-SpiDEController *
-spi_device_event_controller_new (SpiRegistry *registry)
-{
-  SpiDEController *retval = g_object_new (
-    SPI_DEVICE_EVENT_CONTROLLER_TYPE, NULL);
-  
-  retval->registry = g_object_ref (registry);
-
-  spi_dec_init_mouse_listener (registry);
-  /* TODO: kill mouse listener on finalize */  
-  return retval;
-}
 
 static gboolean
 is_key_released (KeyCode code)
@@ -2701,7 +2687,37 @@ static void wait_for_release_event (XEvent          *event,
   check_release_handler = g_timeout_add (CHECK_RELEASE_DELAY, check_release, &pressed_event);
 }
 
-static DRouteMethod methods[] =
+static DBusMessage *
+impl_introspect (DBusConnection *bus, DBusMessage *message,
+                 void *user_data)
+{
+  const char *path;
+  GString *output;
+  char *final;
+
+  DBusMessage *reply;
+
+  path = dbus_message_get_path(message);
+
+  output = g_string_new(spi_introspection_header);
+
+  g_string_append_printf(output, spi_introspection_node_element, path);
+
+  spi_append_interface(output, SPI_DBUS_INTERFACE_DEC);
+
+  g_string_append(output, spi_introspection_footer);
+  final = g_string_free(output, FALSE);
+
+  reply = dbus_message_new_method_return (message);
+  g_assert(reply != NULL);
+  dbus_message_append_args(reply, DBUS_TYPE_STRING, &final,
+                           DBUS_TYPE_INVALID);
+
+  g_free(final);
+  return reply;
+}
+
+static DRouteMethod dev_methods[] =
 {
   { impl_register_keystroke_listener, "registerKeystrokeListener" },
   { impl_register_device_listener, "registerDeviceListener" },
@@ -2714,17 +2730,30 @@ static DRouteMethod methods[] =
   { NULL, NULL }
 };
 
-static void
-spi_registry_initialize_dec_interface (DRouteData * data)
-{
-  droute_add_interface (data, SPI_DBUS_INTERFACE_DEC, methods,
-                       NULL, NULL, NULL);
+static DRouteMethod intro_methods[] = {
+  {impl_introspect, "Introspect"},
+  {NULL, NULL}
 };
 
 SpiDEController *
-spi_registry_dec_new (DRouteData *droute)
+spi_registry_dec_new (SpiRegistry *reg, DRouteData *droute)
 {
-  SpiDEController *dec = g_object_new (SPI_DEVICE_EVENT_CONTROLLER_TYPE, 1);
-  spi_registry_initialize_dec_interface (&droute);
+  SpiDEController *dec = g_object_new (SPI_DEVICE_EVENT_CONTROLLER_TYPE, NULL);
+
+  dec->registry = g_object_ref (reg);
+  dec->droute = droute;
+
+  droute_add_interface (droute,
+                        SPI_DBUS_INTERFACE_DEC,
+                        dev_methods,
+                        NULL, NULL, NULL);
+  droute_add_interface (droute,
+                        "org.freedesktop.DBus.Introspectable",
+                        intro_methods,
+                        NULL, NULL, NULL);
+
+  spi_dec_init_mouse_listener (dec);
+  /* TODO: kill mouse listener on finalize */
+
   return dec;
 }
index fad9be7..c42fe24 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <X11/Xlib.h>
 #include <gdk/gdk.h>
+#include <droute/droute.h>
 
 typedef struct _SpiDEController SpiDEController;
 
@@ -44,6 +45,7 @@ struct _SpiDEController {
        GObject parent;
        
        SpiRegistry *registry;
+        DRouteData  *droute;
        GList       *key_listeners;
        GList       *mouse_listeners;
        GList       *keygrabs_list;
@@ -59,8 +61,6 @@ SpiDEController *spi_device_event_controller_new      (SpiRegistry *registry);
 
 void spi_remove_device_listeners (SpiDEController *controller, const char *bus_name);
 
-void spi_registry_initialize_dec_interface (DRouteData * data);
-
 G_END_DECLS
 
 #endif /* DEVICEEVENTCONTROLLER_H_ */
diff --git a/registryd/reentrant-list.c b/registryd/reentrant-list.c
new file mode 100644 (file)
index 0000000..6e9911e
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2001, 2002 Sun Microsystems Inc.,
+ * Copyright 2001, 2002 Ximian, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <glib.h>
+
+#include "reentrant-list.h"
+
+typedef struct {
+       GList **list;
+       GList  *iterator;
+} Iteration;
+
+static GSList *working_list = NULL; /* of Iteration */
+
+/*
+ *   deletes an element from the list - in a re-entrant
+ * safe fashion; advances the element pointer to the next
+ * element.
+ */
+void
+spi_re_entrant_list_delete_link (GList * const *element_ptr)
+{
+  GSList    *l;
+  GList     *next;
+  GList     *element;
+  gboolean   first_item;
+
+  g_return_if_fail (element_ptr != NULL);
+
+  element = *element_ptr;
+  g_return_if_fail (element != NULL);
+
+  next = element->next;
+  first_item = (element->prev == NULL);
+
+  g_list_remove_link (NULL, element);
+
+  for (l = working_list; l; l = l->next)
+    {
+       Iteration *i = l->data;
+
+       if (i->iterator == element)
+         {
+           i->iterator = next;
+         }
+
+       if (first_item && *(i->list) == element)
+         {
+           *(i->list) = next;
+         }
+    }
+
+  g_list_free_1 (element);
+}
+
+void
+spi_re_entrant_list_foreach (GList         **list,
+                            SpiReEntrantFn  func,
+                            gpointer        user_data)
+{
+       Iteration i;
+
+       if (!list || !*list)
+         {
+            return;
+         }
+
+       i.list = list;
+       i.iterator = *list;
+
+       working_list = g_slist_prepend (working_list, &i);
+
+       while (i.iterator) {
+               GList *l = i.iterator;
+
+               func (&i.iterator, user_data);
+
+               if (i.iterator == l)
+                       i.iterator = i.iterator->next;
+       }
+
+       working_list = g_slist_remove (working_list, &i);
+}
diff --git a/registryd/reentrant-list.h b/registryd/reentrant-list.h
new file mode 100644 (file)
index 0000000..88e37e4
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2001, 2002 Sun Microsystems Inc.,
+ * Copyright 2001, 2002 Ximian, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef REENTRANT_LIST_H_
+#define REENTRANT_LIST_H_
+
+#include <glib/glist.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+       SPI_RE_ENTRANT_CONTINUE = 0,
+       SPI_RE_ENTRANT_TERMINATE
+} SpiReEntrantContinue;
+
+typedef SpiReEntrantContinue (*SpiReEntrantFn) (GList * const *list,
+                                               gpointer       user_data);
+
+void spi_re_entrant_list_delete_link (GList * const  *element_ptr);
+void spi_re_entrant_list_foreach     (GList         **list,
+                                     SpiReEntrantFn  func,
+                                     gpointer        user_data);
+
+G_END_DECLS
+
+#endif /* REENTRANT_LIST_H_ */
index dd42775..ba613c9 100644 (file)
 #include <glib/gmain.h>
 
 #include <spi-common/spi-dbus.h>
+#include <droute/droute.h>
 
 #include "registry.h"
+#include "deviceeventcontroller.h"
 
 static gchar *dbus_name = NULL;
 
@@ -38,11 +40,19 @@ static GOptionEntry optentries[] =
   {NULL}
 };
 
+static DBusObjectPathVTable droute_vtable =
+{
+  NULL,
+  &droute_message,
+  NULL, NULL, NULL, NULL
+};
+
 int
 main (int argc, char **argv)
 {
   SpiRegistry *registry;
-  /*SpiDEController *dec;*/
+  SpiDEController *dec;
+  DRouteData droute;
 
   GMainLoop *mainloop;
   DBusConnection *bus;
@@ -55,6 +65,9 @@ main (int argc, char **argv)
 
   g_type_init();
 
+  /* We depend on GDK as well as XLib for device event processing */
+  gdk_init(&argc, &argv);
+
   /*Parse command options*/
   opt = g_option_context_new(NULL);
   g_option_context_add_main_entries(opt, optentries, NULL);
@@ -67,6 +80,7 @@ main (int argc, char **argv)
 
   dbus_error_init (&error);
   bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
+  droute.bus = bus;
   if (!bus)
   {
     g_warning("Couldn't connect to dbus: %s\n", error.message);
@@ -85,8 +99,19 @@ main (int argc, char **argv)
       g_print ("SpiRegistry daemon is running with well-known name - %s\n", dbus_name);
     }
 
-  /*dec = spi_registry_dec_new (bus);*/
+  /* Set up D-Route for use by the dec */
+  if (!dbus_connection_register_object_path (droute.bus,
+                                             "/org/freedesktop/atspi/registry/deviceeventcontroller",
+                                             &droute_vtable,
+                                             &droute))
+  {
+    g_error("AT-SPI Registry daemon: Couldn't register droute.\n");
+    return 0;
+  }
+
   registry = spi_registry_new (bus);
+  dec = spi_registry_dec_new (registry, &droute);
+  droute.user_data = dec;
 
   g_main_loop_run (mainloop);
   return 0;
index e044f6a..a9e4612 100644 (file)
@@ -25,7 +25,7 @@
       A ControllerEventMask filtering the intercepted key events.
     </tp:docstring>
     </arg>
-    <arg direction="in" name="type" type="u" tp:type="KeyEventTypeSeq">
+    <arg direction="in" name="type" type="au" tp:type="KeyEventTypeSeq">
     <tp:docstring>
       A KeyEventTypeSeq that may created by ORing event types together.
     </tp:docstring>