#include <config.h>
+#undef SPI_XKB_DEBUG
#undef SPI_DEBUG
#undef SPI_KEYEVENT_DEBUG
#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>
+#include <X11/XKBlib.h>
#define XK_MISCELLANY
#include <X11/keysymdef.h>
#include <gdk/gdk.h>
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 {
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);
}
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;
True, ButtonPressMask | ButtonReleaseMask,
GrabModeSync, GrabModeAsync, None, None);
XSync (display, False);
+#ifdef SPI_DEBUG
fprintf (stderr, "mouse buttons grabbed\n");
+#endif
}
}
#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);
}
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
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.
/* 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 ())
{
{
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;
}
--- /dev/null
+/*
+ * 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:
+ }
+ }
+ }
+}
+