Fix for bug #95828.
authorbillh <billh@e2bd861d-eb25-0410-b326-f6ed22b6b98c>
Mon, 21 Oct 2002 19:41:46 +0000 (19:41 +0000)
committerbillh <billh@e2bd861d-eb25-0410-b326-f6ed22b6b98c>
Mon, 21 Oct 2002 19:41:46 +0000 (19:41 +0000)
git-svn-id: http://svn.gnome.org/svn/at-spi/trunk@356 e2bd861d-eb25-0410-b326-f6ed22b6b98c

ChangeLog
acconfig.h
configure.in
libspi/listener.c
registryd/deviceeventcontroller.c
registryd/deviceeventcontroller.h
test/Makefile.am
test/key-listener-test.c
test/visual-bell.c [new file with mode: 0755]

index 68b9528..3dbc936 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
+2002-10-18  Bill Haneman  <bill.haneman@sun.com>
+
+       BUGFIX for #95828.
+
+       * acconfig.h:
+       Added template for HAVE_XKB.
+       
+       * configure.in:
+       Changes to check for XKB, and to set the HAVE_XKB #define if it 
+       is available.
+       
+       * libspi/listener.c:
+       ()impl_notify_event): Make failure to get event source name
+       nonfatal.
+
+       * registryd/deviceeventcontroller.c:
+       (#include): include X11/XKBlib.h.
+       (DEControllerPrivateData) : New struct.
+       (dec_xkb_get_slowkeys_delay) (dec_xkb_get_boucekeys_delay) : 
+       New methods.
+       (dec_synth_keycode_press) (dec_synth_keycode_release):
+       New methods; split the key synthesis code into these methods.
+       They check XKB settings before determining the "time" values to
+       pass to Xtest; this fixes bug #95828.
+       (impl_generate_keyboard_event): Changed to use methods above,
+       instead of callng Xtest directly.
+       (spi_device_event_controller_init): Initialize new privae struct s
+       above.
+       (spi_device_event_controllr_object_finalize):
+       Free the (new) private data.
+
+       * registryd/deviceeventcontroller.h:
+       Add new gpointer to end of struct data.
+
 2002-10-16  Bill Haneman  <bill.haneman@sun.com>
 
        * configure.in:
index 94f82b5..2ebb98a 100644 (file)
@@ -5,3 +5,4 @@
 #undef HAVE_GET_TEXT
 #undef HAVE_LC_MESSAGES
 #undef HAVE_STPCPY
+#undef HAVE_XKB
index 5179e83..47353c3 100644 (file)
@@ -160,6 +160,16 @@ if test "x$XTST_LIBS" = x; then
 fi
 AC_SUBST(XTST_LIBS)
 
+have_xkb=
+AC_CHECK_LIB(X11, XkbQueryExtension, have_xkb="maybe",,$X_LIBS)
+if test "x$have_xkb" = "xmaybe"; then 
+  AC_CHECK_HEADER(X11/XKBlib.h, have_xkb=yes)
+fi
+if test "x$have_xkb" = "xyes"; then
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(HAVE_XKB)
+fi
+
 AC_SUBST(CFLAGS)
 AC_SUBST(CPPFLAGS)
 AC_SUBST(LDFLAGS)
index e0288e8..7f10f5c 100644 (file)
@@ -67,7 +67,6 @@ impl_notify_event (PortableServer_Servant     servant,
     fprintf(stderr,
             ("Accessibility app error: exception during event notification: %s\n"),
             CORBA_exception_id(ev));
-    exit(-1);
   }
   /*
   fprintf (stderr, "source is component ? : %s\n",
index ca2aff4..919f2d1 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <config.h>
 
+#undef  SPI_XKB_DEBUG
 #undef  SPI_DEBUG
 #undef  SPI_KEYEVENT_DEBUG
 
@@ -35,6 +36,7 @@
 
 #include <X11/Xlib.h>
 #include <X11/extensions/XTest.h>
+#include <X11/XKBlib.h>
 #define XK_MISCELLANY
 #include <X11/keysymdef.h>
 #include <gdk/gdk.h>
@@ -58,6 +60,8 @@ static unsigned int mouse_button_mask =
 static unsigned int key_modifier_mask =
   Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask | ShiftMask | LockMask | ControlMask;
 
+static GQuark spi_dec_private_quark = 0;
+
 int (*x_default_error_handler) (Display *display, XErrorEvent *error_event);
 
 typedef enum {
@@ -89,6 +93,13 @@ typedef struct {
   Accessibility_EventListenerMode  *mode;      
 } DEControllerKeyListener;
 
+typedef struct {
+       unsigned int last_press_keycode;
+       unsigned int last_release_keycode;
+       struct timeval last_press_time;
+       struct timeval last_release_time;
+} DEControllerPrivateData;
+
 static void     spi_controller_register_with_devices          (SpiDEController           *controller);
 static gboolean spi_controller_update_key_grabs               (SpiDEController           *controller,
                                                               Accessibility_DeviceEvent *recv);
@@ -212,7 +223,9 @@ spi_dec_poll_mouse_moved (gpointer data)
          }
          if ((mask_return & key_modifier_mask) !=
              (mouse_mask_state & key_modifier_mask)) {
-                 fprintf (stderr, "MODIFIER CHANGE EVENT!\n");  
+#ifdef SPI_DEBUG
+                 fprintf (stderr, "MODIFIER CHANGE EVENT!\n");
+#endif
                  e.type = "keyboard:modifiers";  
                  e.source = BONOBO_OBJREF (registry->desktop);
                  e.detail1 = mouse_mask_state;
@@ -307,7 +320,9 @@ spi_dec_init_mouse_listener (SpiRegistry *registry)
                   True, ButtonPressMask | ButtonReleaseMask,
                   GrabModeSync, GrabModeAsync, None, None);
       XSync (display, False);
+#ifdef SPI_DEBUG
       fprintf (stderr, "mouse buttons grabbed\n");
+#endif
     }
 }
 
@@ -1051,7 +1066,8 @@ spi_device_event_controller_object_finalize (GObject *object)
 #endif
   /* disconnect any special listeners, get rid of outstanding keygrabs */
   XUngrabKey (spi_get_display (), AnyKey, AnyModifier, DefaultRootWindow (spi_get_display ()));
-       
+
+  g_free (g_object_get_data (G_OBJECT (controller), "spi-dec-private"));
   spi_device_event_controller_parent_class->finalize (object);
 }
 
@@ -1175,6 +1191,150 @@ impl_deregister_keystroke_listener (PortableServer_Servant                  serv
   spi_dec_key_listener_free (key_listener, ev);
 }
 
+static unsigned int dec_xkb_get_slowkeys_delay (SpiDEController *controller)
+{
+       unsigned int retval = 0;
+#ifdef HAVE_XKB
+#ifdef XKB_HAS_GET_SLOW_KEYS_DELAY     
+       retval = XkbGetSlowKeysDelay (spi_get_display (),
+                                     XkbUseCoreKbd, &bounce_delay);
+#else
+       XkbDescPtr xkb = XkbGetMap (spi_get_display (), 0, XkbUseCoreKbd);
+       if (!(xkb == (XkbDescPtr) BadAlloc || xkb == NULL))
+       {
+               Status s = XkbGetControls (spi_get_display (),
+                                          XkbAllControlsMask, xkb);
+               if (s == Success)
+               {
+                       if (xkb->ctrls->enabled_ctrls & XkbSlowKeysMask)
+                               retval = xkb->ctrls->slow_keys_delay;
+               }
+               XkbFreeKeyboard (xkb, XkbAllControlsMask, True);
+       }
+#endif
+#endif
+#ifdef SPI_XKB_DEBUG
+       fprintf (stderr, "SlowKeys delay: %d\n", (int) retval);
+#endif
+        return retval;
+}
+
+static unsigned int dec_xkb_get_bouncekeys_delay (SpiDEController *controller)
+{
+       unsigned int retval = 0;
+#ifdef HAVE_XKB
+#ifdef XKB_HAS_GET_BOUNCE_KEYS_DELAY   
+       retval = XkbGetBounceKeysDelay (spi_get_display (),
+                                       XkbUseCoreKbd, &bounce_delay);
+#else
+       XkbDescPtr xkb = XkbGetMap (spi_get_display (), 0, XkbUseCoreKbd);
+       if (!(xkb == (XkbDescPtr) BadAlloc || xkb == NULL))
+       {
+               Status s = XkbGetControls (spi_get_display (),
+                                          XkbAllControlsMask, xkb);
+               if (s == Success)
+               {
+                       if (xkb->ctrls->enabled_ctrls & XkbBounceKeysMask)
+                               retval = xkb->ctrls->debounce_delay;
+               }
+               XkbFreeKeyboard (xkb, XkbAllControlsMask, True);
+       }
+#endif
+#endif
+#ifdef SPI_XKB_DEBUG
+       fprintf (stderr, "BounceKeys delay: %d\n", (int) retval);
+#endif
+       return retval;
+}
+
+static gboolean
+dec_synth_keycode_press (SpiDEController *controller,
+                        unsigned int keycode)
+{
+       unsigned int time = CurrentTime;
+       unsigned int bounce_delay;
+       unsigned int elapsed_msec;
+       struct timeval tv;
+       DEControllerPrivateData *priv =
+               (DEControllerPrivateData *) g_object_get_qdata (G_OBJECT (controller),
+                                                               spi_dec_private_quark);
+       if (keycode == priv->last_release_keycode)
+       {
+               bounce_delay = dec_xkb_get_bouncekeys_delay (controller); 
+                if (bounce_delay)
+               {
+                       gettimeofday (&tv);
+                       elapsed_msec =
+                               (tv.tv_sec - priv->last_release_time.tv_sec) * 1000
+                               + (tv.tv_usec - priv->last_release_time.tv_usec) / 1000;
+#ifdef SPI_XKB_DEBUG                   
+                       fprintf (stderr, "%d ms elapsed (%ld usec)\n", elapsed_msec,
+                                (long) (tv.tv_usec - priv->last_release_time.tv_usec));
+#endif
+#ifdef THIS_IS_BROKEN
+                       if (elapsed_msec < bounce_delay)
+                               time = bounce_delay - elapsed_msec + 1;
+#else
+                       time = bounce_delay + 10;
+                       /* fudge for broken XTest */
+#endif
+#ifdef SPI_XKB_DEBUG                   
+                       fprintf (stderr, "waiting %d ms\n", time);
+#endif
+               }
+       }
+       fprintf (stderr, "press %d\n", (int) keycode);
+        XTestFakeKeyEvent (spi_get_display (), keycode, True, time);
+       priv->last_press_keycode = keycode;
+       XSync (spi_get_display (), False);
+       gettimeofday (&priv->last_press_time, NULL);
+       return TRUE;
+}
+
+static gboolean
+dec_synth_keycode_release (SpiDEController *controller,
+                          unsigned int keycode)
+{
+       unsigned int time = CurrentTime;
+       unsigned int slow_delay;
+       unsigned int elapsed_msec;
+       struct timeval tv;
+       DEControllerPrivateData *priv =
+               (DEControllerPrivateData *) g_object_get_qdata (G_OBJECT (controller),
+                                                               spi_dec_private_quark);
+       if (keycode == priv->last_press_keycode)
+       {
+               slow_delay = dec_xkb_get_slowkeys_delay (controller);
+               if (slow_delay)
+               {
+                       gettimeofday (&tv);
+                       elapsed_msec =
+                               (tv.tv_sec - priv->last_press_time.tv_sec) * 1000
+                               + (tv.tv_usec - priv->last_press_time.tv_usec) / 1000;
+#ifdef SPI_XKB_DEBUG                   
+                       fprintf (stderr, "%d ms elapsed (%ld usec)\n", elapsed_msec,
+                                (long) (tv.tv_usec - priv->last_press_time.tv_usec));
+#endif
+#ifdef THIS_IS_BROKEN_DUNNO_WHY
+                       if (elapsed_msec < slow_delay)
+                               time = slow_delay - elapsed_msec + 1;
+#else
+                       time = slow_delay + 10;
+                       /* our XTest seems broken, we have to add slop as above */
+#endif
+#ifdef SPI_XKB_DEBUG                   
+                       fprintf (stderr, "waiting %d ms\n", time);
+#endif
+               }
+       }
+       fprintf (stderr, "release %d\n", (int) keycode);
+        XTestFakeKeyEvent (spi_get_display (), keycode, False, time);
+       priv->last_release_keycode = keycode;
+       XSync (spi_get_display (), False);
+       gettimeofday (&priv->last_release_time, NULL);
+       return TRUE;
+}
+
 /*
  * CORBA Accessibility::DEController::registerKeystrokeListener
  *     method implementation
@@ -1186,16 +1346,19 @@ impl_generate_keyboard_event (PortableServer_Servant           servant,
                              const Accessibility_KeySynthType synth_type,
                              CORBA_Environment               *ev)
 {
+  SpiDEController *controller =
+       SPI_DEVICE_EVENT_CONTROLLER (bonobo_object (servant));
   long key_synth_code;
+  unsigned int slow_keys_delay;
+  unsigned int press_time;
+  unsigned int release_time;
 
 #ifdef SPI_DEBUG
-  fprintf (stderr, "synthesizing keystroke %ld, type %d\n",
-          (long) keycode, (int) synth_type);
+       fprintf (stderr, "synthesizing keystroke %ld, type %d\n",
+                (long) keycode, (int) synth_type);
 #endif
   /* TODO: hide/wrap/remove X dependency */
 
-  /* TODO: be accessX-savvy so that keyrelease occurs after sufficient timeout */
-       
   /*
    * TODO: when initializing, query for XTest extension before using,
    * and fall back to XSendEvent() if XTest is not available.
@@ -1203,25 +1366,28 @@ impl_generate_keyboard_event (PortableServer_Servant           servant,
   
   /* TODO: implement keystring mode also */
   gdk_error_trap_push ();
-
+  
   switch (synth_type)
     {
       case Accessibility_KEY_PRESS:
-        XTestFakeKeyEvent (spi_get_display (), (unsigned int) keycode, True, CurrentTime);
-       break;
+             dec_synth_keycode_press (controller, keycode);
+             break;
       case Accessibility_KEY_PRESSRELEASE:
-       XTestFakeKeyEvent (spi_get_display (), (unsigned int) keycode, True, CurrentTime);
+             dec_synth_keycode_press (controller, keycode);
       case Accessibility_KEY_RELEASE:
-       XTestFakeKeyEvent (spi_get_display (), (unsigned int) keycode, False, CurrentTime);
-       break;
+             dec_synth_keycode_release (controller, keycode);
+             break;
       case Accessibility_KEY_SYM:
-       key_synth_code = keycode_for_keysym (keycode);
-       XTestFakeKeyEvent (spi_get_display (), (unsigned int) key_synth_code, True, CurrentTime);
-       XTestFakeKeyEvent (spi_get_display (), (unsigned int) key_synth_code, False, CurrentTime);
-       break;
+#ifdef SPI_XKB_DEBUG         
+             fprintf (stderr, "KeySym synthesis\n");
+#endif
+             key_synth_code = keycode_for_keysym (keycode);
+             dec_synth_keycode_press (controller, key_synth_code);
+             dec_synth_keycode_release (controller, key_synth_code);
+             break;
       case Accessibility_KEY_STRING:
-       fprintf (stderr, "Not yet implemented\n");
-       break;
+             fprintf (stderr, "Not yet implemented\n");
+             break;
     }
   if (gdk_error_trap_pop ())
     {
@@ -1393,13 +1559,21 @@ spi_device_event_controller_new (SpiRegistry *registry)
 {
   SpiDEController *retval = g_object_new (
     SPI_DEVICE_EVENT_CONTROLLER_TYPE, NULL);
-
+  DEControllerPrivateData *private;
+  
   retval->registry = SPI_REGISTRY (bonobo_object_ref (
          BONOBO_OBJECT (registry)));
 
+  private = g_new0 (DEControllerPrivateData, 1);
+  gettimeofday (&private->last_press_time, NULL);
+  gettimeofday (&private->last_release_time, NULL);
+  if (!spi_dec_private_quark)
+         spi_dec_private_quark = g_quark_from_static_string ("spi-dec-private");
+  g_object_set_qdata (G_OBJECT (retval),
+                     spi_dec_private_quark,
+                     private);
   spi_dec_init_mouse_listener (registry);
-  /* TODO: kill mouse listener on finalize */
-  
+  /* TODO: kill mouse listener on finalize */  
   return retval;
 }
 
index 3d366d0..90ebb0d 100644 (file)
@@ -42,12 +42,12 @@ G_BEGIN_DECLS
 #define SPI_DEVICE_EVENT_CONTROLLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SPI_DEVICE_EVENT_CONTROLLER_TYPE, SpiDEControllerClass))
 
 struct _SpiDEController {
-  BonoboObject parent;
-
-  SpiRegistry *registry;
-  GList       *key_listeners;
-  GList       *mouse_listeners;
-  GList       *keygrabs_list;
+       BonoboObject parent;
+       
+       SpiRegistry *registry;
+       GList       *key_listeners;
+       GList       *mouse_listeners;
+       GList       *keygrabs_list;
 };
 
 typedef struct {
index 6e3bb6a..5a06fda 100644 (file)
@@ -1,11 +1,13 @@
 NULL=
 
-noinst_PROGRAMS = test-simple at app simple-at stress-test keysynth-demo accessx-gui key-listener-test event-listener-test screen-review-test
+noinst_PROGRAMS = test-simple at app simple-at stress-test keysynth-demo accessx-gui key-listener-test event-listener-test screen-review-test visual-bell
 
 key_listener_test_SOURCES = key-listener-test.c
 
 stress_test_SOURCES = stress-test.c
 
+visual_bell_SOURCES = visual-bell.c
+
 event_listener_test_SOURCES = event-listener-test.c
 
 screen_review_test_SOURCES = screen-review-test.c
index 71900a6..dc0186a 100644 (file)
@@ -103,9 +103,9 @@ main (int argc, char **argv)
 
   SPI_registerAccessibleKeystrokeListener(all_key_listener,
                                         SPI_KEYSET_ALL_KEYS,
-                                        SPI_KEYMASK_UNMODIFIED,
-                                        (unsigned long) ( SPI_KEY_PRESSED | SPI_KEY_RELEASED ),
-                                        SPI_KEYLISTENER_NOSYNC);
+                                        SPI_KEYMASK_SHIFT,
+                                        (unsigned long) ( SPI_KEY_PRESSED ),
+                                        SPI_KEYLISTENER_NOSYNC | SPI_KEYLISTENER_CANCONSUME);
 
   SPI_event_main ();
 
diff --git a/test/visual-bell.c b/test/visual-bell.c
new file mode 100755 (executable)
index 0000000..874a4bc
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2001, 2002 Sun Microsystems 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/XKBlib.h>
+
+static Display *display;
+static  Window flash_window = None;
+
+static void
+visual_bell_notify (XkbAnyEvent *xkb_ev)
+{
+       Window root;
+       int width, height;
+       root = RootWindow (display, DefaultScreen (display));
+       width = DisplayWidth (display, DefaultScreen (display));
+       height = DisplayHeight (display, DefaultScreen (display));
+       if (flash_window == None)
+       {
+               Visual *visual = CopyFromParent;
+               XVisualInfo info_return;
+               XSetWindowAttributes xswa;
+               int depth = CopyFromParent;
+               xswa.save_under = True;
+               xswa.override_redirect = True;
+               /* TODO: use XGetVisualInfo and determine which is an
+                  overlay, if one is present.  Not sure how to tell
+                  this yet... */
+               if (XMatchVisualInfo (display,
+                                     DefaultScreen (display),
+                                     8,
+                                     PseudoColor,
+                                     &info_return)) {
+                       depth = 8;
+                       visual = info_return.visual;
+               }
+               else
+               {
+                       fprintf (stderr, "could not create overlay visual, using default root visual type\n");
+               }
+               flash_window = XCreateWindow (display, root,
+                                             0, 0, width, height,
+                                             0, depth,
+                                             InputOutput,
+                                             visual,
+                                             CWSaveUnder | CWOverrideRedirect,
+                                             &xswa);
+               XSelectInput (display, flash_window, ExposureMask);
+               XMapWindow (display, flash_window);
+       }
+       else
+       {
+               /* just draw something in the window */
+               GC gc = XCreateGC (display, flash_window, 0, NULL);
+               XMapWindow (display, flash_window);
+               XSetForeground (display, gc,
+                               WhitePixel (display, DefaultScreen (display)));
+               XFillRectangle (display, flash_window, gc,
+                               0, 0, width, height);
+               XSetForeground (display, gc,
+                               BlackPixel (display, DefaultScreen (display)));
+               XFillRectangle (display, flash_window, gc,
+                               0, 0, width, height);
+       }
+       XFlush (display);
+}
+
+int main (int argc, char **argv)
+{
+       XEvent xev;
+       int ir, xkb_base_event_type, reason_return;
+       char *display_name = getenv ("DISPLAY");
+
+       if (!display_name) display_name = ":0.0";
+       
+       display = XkbOpenDisplay (display_name,
+                                 &xkb_base_event_type,
+                                 &ir, NULL, NULL, &reason_return);
+       if (!display)
+       {
+               fprintf (stderr, "Could not connect to display! (%d)\n",
+                        reason_return);
+               exit (-1);
+       }
+       
+       XkbSelectEvents (display,
+                        XkbUseCoreKbd,
+                        XkbBellNotifyMask,
+                        XkbBellNotifyMask);
+
+       /* comment this out to prevent bell on startup */
+       XkbBell (display, None, 100, None);
+
+       while (1)
+       {
+               XNextEvent (display, &xev);
+               if (xev.type == Expose)
+               {
+                       XExposeEvent *exev = (XExposeEvent *) &xev;
+                       if (exev->window == flash_window)
+                       {
+                               XUnmapWindow (display, flash_window);
+                                /* discard pending bells */
+                               XSync (display, True); 
+                               XFlush (display);
+                       }
+               }
+               else if (xev.type == xkb_base_event_type)
+               {
+                       XkbAnyEvent *xkb_ev = (XkbAnyEvent *) &xev;
+                       
+                       switch (xkb_ev->xkb_type)
+                       {
+                       case XkbBellNotify:
+                               /* flash something */
+                               visual_bell_notify (xkb_ev);
+                               break;
+                       default:
+                       }
+               }
+       }
+}
+