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