2002-08-12 Michael Meeks <michael@ximian.com>
[platform/core/uifw/at-spi2-atk.git] / registryd / deviceeventcontroller.c
index f8e4982..6009c05 100644 (file)
@@ -20,7 +20,7 @@
  * Boston, MA 02111-1307, USA.
  */
 
-/* deviceeventcontroler.c: implement the DeviceEventController interface */
+/* deviceeventcontroller.c: implement the DeviceEventController interface */
 
 #include <config.h>
 
 /* A pointer to our parent object class */
 static GObjectClass *spi_device_event_controller_parent_class;
 static int spi_error_code = 0;
+static GdkPoint *last_mouse_pos = NULL; 
+static unsigned int mouse_button_state = 0;
+static unsigned int mouse_button_mask =
+  Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask;
 
 int (*x_default_error_handler) (Display *display, XErrorEvent *error_event);
 
@@ -90,7 +94,14 @@ static gboolean spi_controller_register_device_listener       (SpiDEController
                                                               CORBA_Environment         *ev);
 static void     spi_device_event_controller_forward_key_event (SpiDEController           *controller,
                                                               const XEvent              *event);
+static void     spi_deregister_controller_key_listener (SpiDEController         *controller,
+                                                       DEControllerKeyListener *key_listener,
+                                                       CORBA_Environment       *ev);
+
 static gboolean spi_clear_error_state (void);
+static gboolean spi_dec_poll_mouse_moved (gpointer data);
+static gboolean spi_dec_poll_mouse_moving (gpointer data);
+static gboolean spi_dec_poll_mouse_idle (gpointer data);
 
 #define spi_get_display() GDK_DISPLAY()
 
@@ -138,6 +149,138 @@ spi_grab_mask_compare_values (gconstpointer p1, gconstpointer p2)
     }
 }
 
+static gboolean
+spi_dec_poll_mouse_moved (gpointer data)
+{
+  SpiRegistry *registry = SPI_REGISTRY (data);
+  CORBA_Environment ev;
+  Accessibility_Event e;
+  Window root_return, child_return;
+  int win_x_return,win_y_return;
+  int x, y;
+  unsigned int mask_return;
+  gchar event_name[24];
+  Display *display = spi_get_display ();
+  if (last_mouse_pos == NULL) {
+         last_mouse_pos = g_new0 (GdkPoint, 1);
+         last_mouse_pos->x = 0;
+         last_mouse_pos->y = 0;
+         e.type = g_strdup ("mouse:abs");
+  } else {
+         e.type = g_strdup ("mouse:rel");
+  }
+  if (display != NULL)
+         XQueryPointer(display, DefaultRootWindow (display),
+               &root_return, &child_return,
+               &x, &y,
+               &win_x_return, &win_y_return, &mask_return);
+  if ((mask_return & mouse_button_mask) != mouse_button_state) {
+         int button_number = 0;
+         if (!(mask_return & Button1Mask) &&
+             (mouse_button_state & Button1Mask)) {
+                 button_number = 1;
+         } else if (!(mask_return & Button2Mask) &&
+                    (mouse_button_state & Button2Mask)) {
+                 button_number = 2;
+         } else if (!(mask_return & Button3Mask) &&
+                    (mouse_button_state & Button3Mask)) {
+                 button_number = 3;
+         } else if (!(mask_return & Button4Mask) &&
+                    (mouse_button_state & Button1Mask)) {
+                 button_number = 4;
+         } else if (!(mask_return & Button5Mask) &&
+                    (mouse_button_state & Button5Mask)) {
+                 button_number = 5;
+         }
+         if (button_number) {
+#ifdef SPI_DEBUG                 
+                 fprintf (stderr, "Button %d Released\n",
+                          button_number);
+#endif
+                 snprintf (event_name, 22, "mouse:button:%dr", button_number);
+                 e.type = CORBA_string_dup (event_name);
+                 e.source = BONOBO_OBJREF (registry->desktop);
+                 e.detail1 = last_mouse_pos->x;
+                 e.detail2 = last_mouse_pos->y;
+                 CORBA_exception_init (&ev);
+                 Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
+                                                     &e,
+                                                     &ev);
+         }
+         mouse_button_state = mask_return & mouse_button_mask;
+  }
+  if (x != last_mouse_pos->x || y != last_mouse_pos->y) {
+         e.source = BONOBO_OBJREF (registry->desktop);
+         e.detail1 = x - last_mouse_pos->x;
+         e.detail2 = y - last_mouse_pos->y;
+         CORBA_exception_init (&ev);
+         if (last_mouse_pos == NULL)
+                 last_mouse_pos = g_new0 (GdkPoint, 1);
+         last_mouse_pos->x = x;
+         last_mouse_pos->y = y;
+         Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
+                                             &e,
+                                             &ev);
+         return TRUE;
+  }
+  return FALSE;
+}
+
+static gboolean
+spi_dec_poll_mouse_idle (gpointer data)
+{
+  if (! spi_dec_poll_mouse_moved (data)) 
+    return TRUE;
+  else
+    {
+      g_timeout_add (20, spi_dec_poll_mouse_moving, data);         
+      return FALSE;        
+    }
+}
+
+static gboolean
+spi_dec_poll_mouse_moving (gpointer data)
+{
+  if (spi_dec_poll_mouse_moved (data))
+    return TRUE;
+  else
+    {
+      g_timeout_add (100, spi_dec_poll_mouse_idle, data);          
+      return FALSE;
+    }
+}
+
+static int
+spi_dec_ungrab_mouse (gpointer data)
+{
+       Display *display = spi_get_display ();
+       fprintf (stderr, "mouse ungrab : display = %p\n", display);
+       if (display)
+         {
+           XUngrabButton (spi_get_display (), AnyButton, AnyModifier,
+                          XDefaultRootWindow (spi_get_display ()));
+           fprintf (stderr, "mouse grab released\n");
+         }
+       return FALSE;
+}
+
+static void
+spi_dec_init_mouse_listener (SpiRegistry *registry)
+{
+  Display *display = spi_get_display ();
+  g_timeout_add (100, spi_dec_poll_mouse_idle, registry);
+
+  if (display)
+    {
+      XGrabButton (display, AnyButton, 0,
+                  gdk_x11_get_default_root_xwindow (),
+                  True, ButtonPressMask | ButtonReleaseMask,
+                  GrabModeSync, GrabModeAsync, None, None);
+      XSync (display, False);
+      fprintf (stderr, "mouse buttons grabbed\n");
+    }
+}
+
 static DEControllerKeyListener *
 spi_dec_key_listener_new (CORBA_Object                            l,
                          const Accessibility_KeySet             *keys,
@@ -168,17 +311,47 @@ spi_dec_key_listener_new (CORBA_Object                            l,
   return key_listener; 
 }
 
+static DEControllerKeyListener *
+spi_key_listener_clone (DEControllerKeyListener *key_listener, CORBA_Environment *ev)
+{
+  DEControllerKeyListener *clone = g_new0 (DEControllerKeyListener, 1);
+  clone->listener.object =
+         CORBA_Object_duplicate (key_listener->listener.object, ev);
+  clone->listener.type = SPI_DEVICE_TYPE_KBD;
+  clone->keys = ORBit_copy_value (key_listener->keys, TC_Accessibility_KeySet);
+  clone->mask = key_listener->mask;
+  clone->typeseq = ORBit_copy_value (key_listener->typeseq, TC_Accessibility_KeyEventTypeSeq);
+  if (key_listener->mode)
+    clone->mode = ORBit_copy_value (key_listener->mode, TC_Accessibility_EventListenerMode);
+  else
+    clone->mode = NULL;
+  return clone;
+}
+
 static void
-spi_dec_key_listener_free (DEControllerKeyListener *key_listener,
-                          CORBA_Environment       *ev)
+spi_key_listener_data_free (DEControllerKeyListener *key_listener, CORBA_Environment *ev)
 {
-  bonobo_object_release_unref (key_listener->listener.object, ev);
   CORBA_free (key_listener->typeseq);
   CORBA_free (key_listener->keys);
   g_free (key_listener);
 }
 
 static void
+spi_key_listener_clone_free (DEControllerKeyListener *clone, CORBA_Environment *ev)
+{
+  CORBA_Object_release (clone->listener.object, ev);
+  spi_key_listener_data_free (clone, ev);
+}
+
+static void
+spi_dec_key_listener_free (DEControllerKeyListener *key_listener,
+                          CORBA_Environment       *ev)
+{
+  bonobo_object_release_unref (key_listener->listener.object, ev);
+  spi_key_listener_data_free (key_listener, ev);
+}
+
+static void
 _register_keygrab (SpiDEController      *controller,
                   DEControllerGrabMask *grab_mask)
 {
@@ -315,22 +488,78 @@ spi_controller_register_device_listener (SpiDEController      *controller,
   return FALSE; 
 }
 
+static void
+spi_device_event_controller_forward_mouse_event (SpiDEController *controller,
+                                                XEvent *xevent)
+{
+  Accessibility_Event e;
+  CORBA_Environment ev;
+  gchar event_name[24];
+  int button = ((XButtonEvent *) xevent)->button;
+  
+  mouse_button_state = ((XButtonEvent *) xevent)->state;
+
+  switch (button)
+    {
+    case 1:
+           mouse_button_state |= Button1Mask;
+           break;
+    case 2:
+           mouse_button_state |= Button2Mask;
+           break;
+    case 3:
+           mouse_button_state |= Button3Mask;
+           break;
+    case 4:
+           mouse_button_state |= Button4Mask;
+           break;
+    case 5:
+           mouse_button_state |= Button5Mask;
+           break;
+    }
+  last_mouse_pos->x = ((XButtonEvent *) xevent)->x_root;
+  last_mouse_pos->y = ((XButtonEvent *) xevent)->y_root;
+
+#ifdef SPI_DEBUG  
+  fprintf (stderr, "mouse button %d %s (%x)\n",
+          ((XButtonEvent *) xevent)->button, 
+          (xevent->type == ButtonPress) ? "Press" : "Release",
+          mouse_button_state);
+#endif
+  snprintf (event_name, 22, "mouse:button:%d%c", button,
+           (xevent->type == ButtonPress) ? 'p' : 'r');
+
+  e.type = CORBA_string_dup (event_name);
+  e.source = BONOBO_OBJREF (controller->registry->desktop);
+  e.detail1 = last_mouse_pos->x;
+  e.detail2 = last_mouse_pos->y;
+  CORBA_exception_init (&ev);
+  Accessibility_Registry_notifyEvent (BONOBO_OBJREF (controller->registry),
+                                     &e,
+                                     &ev);
+  
+  XAllowEvents (spi_get_display (), ReplayPointer, CurrentTime);
+}
+
 static GdkFilterReturn
 global_filter_fn (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
 {
   XEvent *xevent = gdk_xevent;
   SpiDEController *controller;
 
-  if (xevent->type != KeyPress && xevent->type != KeyRelease)
+  if (xevent->type == KeyPress && xevent->type == KeyRelease)
     {
+      controller = SPI_DEVICE_EVENT_CONTROLLER (data);
+      spi_device_event_controller_forward_key_event (controller, xevent);
+      /* FIXME: is this right ? */
       return GDK_FILTER_CONTINUE;
     }
-
-  controller = SPI_DEVICE_EVENT_CONTROLLER (data);
-
-  spi_device_event_controller_forward_key_event (controller, xevent);
-
-  /* FIXME: is this right ? */
+  if (xevent->type == ButtonPress || xevent->type == ButtonRelease)
+    {
+      controller = SPI_DEVICE_EVENT_CONTROLLER (data);
+      spi_device_event_controller_forward_mouse_event (controller, xevent);
+    }
+  
   return GDK_FILTER_CONTINUE;
 }
 
@@ -467,13 +696,14 @@ spi_key_event_matches_listener (const Accessibility_DeviceEvent *key_event,
 }
 
 static gboolean
-spi_notify_keylisteners (GList                          **key_listeners,
-                        const Accessibility_DeviceEvent *key_event,
-                        CORBA_boolean                    is_system_global,
-                        CORBA_Environment               *ev)
+spi_controller_notify_keylisteners (SpiDEController                 *controller,
+                                   const Accessibility_DeviceEvent *key_event,
+                                   CORBA_boolean                    is_system_global,
+                                   CORBA_Environment               *ev)
 {
   GList   *l;
   GSList  *notify = NULL, *l2;
+  GList  **key_listeners = &controller->key_listeners;
   gboolean is_consumed;
 
   if (!key_listeners)
@@ -491,7 +721,9 @@ spi_notify_keylisteners (GList                          **key_listeners,
 
           if (ls != CORBA_OBJECT_NIL)
             {
-               notify = g_slist_prepend (notify, CORBA_Object_duplicate (ls, ev));
+              /* we clone (don't dup) the listener, to avoid refcount inc. */
+              notify = g_slist_prepend (notify,
+                                        spi_key_listener_clone (key_listener, ev));
             }
          }
     }
@@ -506,13 +738,16 @@ spi_notify_keylisteners (GList                          **key_listeners,
   is_consumed = FALSE;
   for (l2 = notify; l2 && !is_consumed; l2 = l2->next)
     {
-      Accessibility_DeviceEventListener ls = l2->data;
+      DEControllerKeyListener *key_listener = l2->data;            
+      Accessibility_DeviceEventListener ls = key_listener->listener.object;
 
       is_consumed = Accessibility_DeviceEventListener_notifyEvent (ls, key_event, ev);
 
       if (BONOBO_EX (ev))
         {
           is_consumed = FALSE;
+         spi_deregister_controller_key_listener (controller, key_listener,
+                                                 ev);
          CORBA_exception_free (ev);
         }
 
@@ -521,7 +756,9 @@ spi_notify_keylisteners (GList                          **key_listeners,
 
   for (; l2; l2 = l2->next)
     {
-      CORBA_Object_release (l2->data, ev);
+      DEControllerKeyListener *key_listener = l2->data;            
+      spi_key_listener_clone_free (key_listener, ev);
+      /* clone doesn't have its own ref, so don't use spi_key_listener_free */
     }
 
   g_slist_free (notify);
@@ -855,6 +1092,31 @@ copy_key_listener_cb (GList * const *list,
   return SPI_RE_ENTRANT_CONTINUE;
 }
 
+
+static void
+spi_deregister_controller_key_listener (SpiDEController            *controller,
+                                       DEControllerKeyListener    *key_listener,
+                                       CORBA_Environment          *ev)
+{
+  RemoveKeyListenerClosure  ctx;
+
+  ctx.ev = ev;
+  ctx.key_listener = key_listener;
+
+  /* special case, copy keyset from existing controller list entry */
+  if (key_listener->keys->_length == 0) 
+    {
+      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,
+                               remove_key_listener_cb, &ctx);
+
+}
+
 /*
  * CORBA Accessibility::DEController::deregisterKeystrokeListener
  *     method implementation
@@ -868,7 +1130,6 @@ impl_deregister_keystroke_listener (PortableServer_Servant                  serv
                                    CORBA_Environment                      *ev)
 {
   DEControllerKeyListener  *key_listener;
-  RemoveKeyListenerClosure  ctx;
   SpiDEController *controller;
 
   controller = SPI_DEVICE_EVENT_CONTROLLER (bonobo_object (servant));
@@ -880,20 +1141,7 @@ impl_deregister_keystroke_listener (PortableServer_Servant                  serv
           (void *) l, (unsigned long) mask->value);
 #endif
 
-  ctx.ev = ev;
-  ctx.key_listener = key_listener;
-
-  /* special case, copy keyset from existing controller list entry */
-  if (keys->_length == 0) 
-    {
-      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,
-                               remove_key_listener_cb, &ctx);
+  spi_deregister_controller_key_listener (controller, key_listener, ev);
 
   spi_dec_key_listener_free (key_listener, ev);
 }
@@ -1026,7 +1274,7 @@ impl_notify_listeners_sync (PortableServer_Servant           servant,
   g_print ("notifylistening listeners synchronously: controller %p, event id %d\n",
           controller, (int) event->id);
 #endif
-  return spi_notify_keylisteners (&controller->key_listeners, event, CORBA_FALSE, ev) ?
+  return spi_controller_notify_keylisteners (controller, event, CORBA_FALSE, ev) ?
          CORBA_TRUE : CORBA_FALSE; 
 }
 
@@ -1041,7 +1289,7 @@ impl_notify_listeners_async (PortableServer_Servant           servant,
 #ifdef SPI_DEBUG
   fprintf (stderr, "notifying listeners asynchronously\n");
 #endif
-  spi_notify_keylisteners (&controller->key_listeners, event, CORBA_FALSE, ev); 
+  spi_controller_notify_keylisteners (controller, event, CORBA_FALSE, ev); 
 }
 
 static void
@@ -1096,8 +1344,8 @@ spi_device_event_controller_forward_key_event (SpiDEController *controller,
   spi_controller_update_key_grabs (controller, &key_event);
 
   /* relay to listeners, and decide whether to consume it or not */
-  is_consumed = spi_notify_keylisteners (
-    &controller->key_listeners, &key_event, CORBA_TRUE, &ev);
+  is_consumed = spi_controller_notify_keylisteners (
+         controller, &key_event, CORBA_TRUE, &ev);
 
   CORBA_exception_free (&ev);
 
@@ -1120,6 +1368,9 @@ spi_device_event_controller_new (SpiRegistry *registry)
   retval->registry = SPI_REGISTRY (bonobo_object_ref (
          BONOBO_OBJECT (registry)));
 
+  spi_dec_init_mouse_listener (registry);
+  /* TODO: kill mouse listener on finalize */
+  
   return retval;
 }