From 1acf082d6062c7d9042ded6ba0b7c957341913f5 Mon Sep 17 00:00:00 2001 From: Mark Doffman Date: Fri, 7 Nov 2008 17:26:28 +0000 Subject: [PATCH] 2008-11-13 Mark Doffman * 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 --- pyatspi/Accessibility.py | 2 + pyatspi/Makefile.am | 1 + pyatspi/accessiblecache.py | 2 +- pyatspi/applicationcache.py | 28 +- pyatspi/deviceevent.py | 541 +++++++++++++++++++++ pyatspi/interfaces.py | 8 + pyatspi/registry.py | 187 ++++--- registryd/Makefile.am | 7 +- registryd/deviceeventcontroller.c | 165 ++++--- registryd/deviceeventcontroller.h | 4 +- registryd/reentrant-list.c | 104 ++++ registryd/reentrant-list.h | 46 ++ registryd/registry-main.c | 29 +- ...org.freedesktop.atspi.DeviceEventController.xml | 2 +- 14 files changed, 958 insertions(+), 168 deletions(-) create mode 100644 pyatspi/deviceevent.py create mode 100644 registryd/reentrant-list.c create mode 100644 registryd/reentrant-list.h diff --git a/pyatspi/Accessibility.py b/pyatspi/Accessibility.py index 7c15dc9..18558d7 100644 --- a/pyatspi/Accessibility.py +++ b/pyatspi/Accessibility.py @@ -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 * diff --git a/pyatspi/Makefile.am b/pyatspi/Makefile.am index f7bf95a..0520b38 100644 --- a/pyatspi/Makefile.am +++ b/pyatspi/Makefile.am @@ -11,6 +11,7 @@ pyatspi_PYTHON = \ component.py \ constants.py \ desktop.py \ + deviceevent.py \ document.py \ editabletext.py \ event.py \ diff --git a/pyatspi/accessiblecache.py b/pyatspi/accessiblecache.py index 5729f71..15a3353 100644 --- a/pyatspi/accessiblecache.py +++ b/pyatspi/accessiblecache.py @@ -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, diff --git a/pyatspi/applicationcache.py b/pyatspi/applicationcache.py index ada9dbb..a50c51b 100644 --- a/pyatspi/applicationcache.py +++ b/pyatspi/applicationcache.py @@ -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 index 0000000..b644224 --- /dev/null +++ b/pyatspi/deviceevent.py @@ -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--------------------------------------------------------------------------- diff --git a/pyatspi/interfaces.py b/pyatspi/interfaces.py index 2fad9ec..0883fb9 100644 --- a/pyatspi/interfaces.py +++ b/pyatspi/interfaces.py @@ -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' diff --git a/pyatspi/registry.py b/pyatspi/registry.py index 477f932..2c73cf1 100644 --- a/pyatspi/registry.py +++ b/pyatspi/registry.py @@ -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() diff --git a/registryd/Makefile.am b/registryd/Makefile.am index 9fecd7d..fe657e0 100644 --- a/registryd/Makefile.am +++ b/registryd/Makefile.am @@ -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 diff --git a/registryd/deviceeventcontroller.c b/registryd/deviceeventcontroller.c index 4b0ba0d..bfc8258 100644 --- a/registryd/deviceeventcontroller.c +++ b/registryd/deviceeventcontroller.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -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; } diff --git a/registryd/deviceeventcontroller.h b/registryd/deviceeventcontroller.h index fad9be7..c42fe24 100644 --- a/registryd/deviceeventcontroller.h +++ b/registryd/deviceeventcontroller.h @@ -26,6 +26,7 @@ #include #include +#include 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 index 0000000..6e9911e --- /dev/null +++ b/registryd/reentrant-list.c @@ -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 +#include + +#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 index 0000000..88e37e4 --- /dev/null +++ b/registryd/reentrant-list.h @@ -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 + +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_ */ diff --git a/registryd/registry-main.c b/registryd/registry-main.c index dd42775..ba613c9 100644 --- a/registryd/registry-main.c +++ b/registryd/registry-main.c @@ -27,8 +27,10 @@ #include #include +#include #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; diff --git a/xml/org.freedesktop.atspi.DeviceEventController.xml b/xml/org.freedesktop.atspi.DeviceEventController.xml index e044f6a..a9e4612 100644 --- a/xml/org.freedesktop.atspi.DeviceEventController.xml +++ b/xml/org.freedesktop.atspi.DeviceEventController.xml @@ -25,7 +25,7 @@ A ControllerEventMask filtering the intercepted key events. - + A KeyEventTypeSeq that may created by ORing event types together. -- 2.7.4