-2001-11-09 Bill Haneman <bill.haneman@sun.com
+2001-11-11 Bill Haneman <bill.haneman@sun.com>
+
+ * test/Makefile.am:
+ * test/keysynth-demo.c:
+ Added new test of key synthesis, which creates a simple
+ (mouse-operated) onscreen keyboard. It inserts key events into
+ the currently-focused window, thus it does not grab keyboard focus
+ itself.
+
+ * cspi/spi_registry.c:
+ Added C binding for AT-SPI generateKeyEvent.
+
+ * libspi/deviceeventcontroller.c:
+ Added call to XFilterEvent so that key listener works with XIM (we
+ hope). Added event_synth_type to generateKeyEvent, so that we can
+ produce KEY_PRESS, KEY_RELEASE, KEY_PRESSRELEASE (pair), or
+ synthesize a press/release pair for KeySyms.
+
+2001-11-09 Bill Haneman <bill.haneman@sun.com>
* libspi/Makefile.am:
* registryd/Makefile.am:
AC_SUBST(REGISTRYD_LIBS)
AC_SUBST(REGISTRYD_CFLAGS)
-PKG_CHECK_MODULES(TESTS, bonobo-activation-2.0 >= 0.9.1 libbonobo-2.0 >= 1.97.0 ORBit-2.0 >= 2.3.94 atk >= 0.2)
+PKG_CHECK_MODULES(TESTS, bonobo-activation-2.0 >= 0.9.1 libbonobo-2.0 >= 1.97.0 ORBit-2.0 >= 2.3.94 atk >= 0.2 gtk+-2.0 >= 1.3.2)
AC_SUBST(TESTS_LIBS)
AC_SUBST(TESTS_CFLAGS)
KEY_RELEASED
} KeyEventType;
+typedef enum _KeySynthType {
+ KEY_PRESS,
+ KEY_RELEASE,
+ KEY_PRESSRELEASE,
+ KEY_SYM
+} KeySynthType;
+
typedef enum _KeyListenerSyncType {
KEYLISTENER_SYNCHRONOUS = 1,
KEYLISTENER_CANCONSUME = 2,
* generateKeyEvent:
* @keycode: a #long indicating the keycode of the key event
* being synthesized.
- * @meta: a #long indicating the key modifiers to be sent
- * with the event, if any.
+ * @synth_type: a #KeySynthType indicating whether this should be a
+ * KEY_PRESS, KEY_RELEASE, both (KEY_PRESSRELEASE), or
+ * a press/release pair for a KEYSYM.
*
* Synthesize a keyboard event (as if a hardware keyboard event occurred in the
* current UI context).
- * Not Yet Implemented.
*
**/
void
-generateKeyEvent (long keyCode, long meta);
+generateKeyEvent (long keyCode, KeySynthType synth_type);
/**
* generateMouseEvent:
*
**/
void
-generateKeyEvent (long keyCode, long meta)
+generateKeyEvent (long keyval, KeySynthType type)
{
- ;
+/* TODO: check current modifier status and
+ * send keycode to alter, if necessary
+ */
+ Accessibility_DeviceEventController device_event_controller =
+ Accessibility_Registry_getDeviceEventController (registry, &ev);
+ Accessibility_DeviceEventController_generateKeyEvent (device_event_controller,
+ keyval,
+ (unsigned long) type,
+ &ev);
}
/**
KEY_RELEASED
};
+ enum KeySynthType {
+ KEY_PRESS,
+ KEY_RELEASE,
+ KEY_PRESSRELEASE,
+ KEY_SYM
+ };
+
enum ModifierType {
MODIFIER_SHIFT,
MODIFIER_ALT,
/**
* generateKeyEvent:
- * @keyEventID: a long integer indicating which keypress is synthesized.
+ * @keycode: a long integer indicating the keycode of
+ * the keypress to be synthesized.
+ *
+ * Note that this long may be truncated before being
+ * processed, as keycode length may be platform-dependent
+ * and keycode ranges are generally much smaller than
+ * CORBA_long.
+ *
* Returns: void
*
* Synthesize a keypress event.
*
**/
- void generateKeyEvent (in long keyEventID);
+ void generateKeyEvent (in long keycode, in KeySynthType type);
/**
* generateMouseEvent:
KEY_RELEASED
};
+ enum KeySynthType {
+ KEY_PRESS,
+ KEY_RELEASE,
+ KEY_PRESSRELEASE,
+ KEY_SYM
+ };
+
enum ModifierType {
MODIFIER_SHIFT,
MODIFIER_ALT,
/**
* generateKeyEvent:
- * @keyEventID: a long integer indicating which keypress is synthesized.
+ * @keycode: a long integer indicating the keycode of
+ * the keypress to be synthesized.
+ *
+ * Note that this long may be truncated before being
+ * processed, as keycode length may be platform-dependent
+ * and keycode ranges are generally much smaller than
+ * CORBA_long.
+ *
* Returns: void
*
* Synthesize a keypress event.
*
**/
- void generateKeyEvent (in long keyEventID);
+ void generateKeyEvent (in long keycode, in KeySynthType type);
/**
* generateMouseEvent:
while (XPending(display))
{
XNextEvent (display, x_event);
+ if (XFilterEvent (x_event, None)) continue;
if (x_event->type == KeyPress)
{
x_key_event = (XKeyEvent *)x_event;
}
*/
+static KeyCode
+keycode_for_keysym (long keysym)
+{
+ return XKeysymToKeycode (display, (KeySym) keysym);
+}
+
/*
* CORBA Accessibility::DeviceEventController::registerKeystrokeListener
* method implementation
*/
static void
impl_generate_key_event (PortableServer_Servant servant,
- const CORBA_long keyEventID,
- CORBA_Environment *ev)
+ const CORBA_long keycode,
+ const CORBA_long synth_type,
+ CORBA_Environment *ev)
{
+ long key_synth_code;
#ifdef SPI_DEBUG
- fprintf (stderr, "synthesizing keystroke %ld\n", (long) keyEventID);
+ fprintf (stderr, "synthesizing keystroke %ld\n", (long) keycode);
#endif
/* TODO: hide/wrap/remove X dependency */
- XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keyEventID, True, CurrentTime);
- XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keyEventID, False, CurrentTime);
+
+ /* 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.
+ */
+
+ switch (synth_type)
+ {
+ case Accessibility_KEY_PRESS:
+ XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, True, CurrentTime);
+ break;
+ case Accessibility_KEY_PRESSRELEASE:
+ XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, True, CurrentTime);
+ case Accessibility_KEY_RELEASE:
+ XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, False, CurrentTime);
+ break;
+ case Accessibility_KEY_SYM:
+ key_synth_code = keycode_for_keysym (keycode);
+ XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) key_synth_code, True, CurrentTime);
+ XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) key_synth_code, False, CurrentTime);
+ break;
+ }
}
/*
while (XPending(display))
{
XNextEvent (display, x_event);
+ if (XFilterEvent (x_event, None)) continue;
if (x_event->type == KeyPress)
{
x_key_event = (XKeyEvent *)x_event;
}
*/
+static KeyCode
+keycode_for_keysym (long keysym)
+{
+ return XKeysymToKeycode (display, (KeySym) keysym);
+}
+
/*
* CORBA Accessibility::DeviceEventController::registerKeystrokeListener
* method implementation
*/
static void
impl_generate_key_event (PortableServer_Servant servant,
- const CORBA_long keyEventID,
- CORBA_Environment *ev)
+ const CORBA_long keycode,
+ const CORBA_long synth_type,
+ CORBA_Environment *ev)
{
+ long key_synth_code;
#ifdef SPI_DEBUG
- fprintf (stderr, "synthesizing keystroke %ld\n", (long) keyEventID);
+ fprintf (stderr, "synthesizing keystroke %ld\n", (long) keycode);
#endif
/* TODO: hide/wrap/remove X dependency */
- XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keyEventID, True, CurrentTime);
- XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keyEventID, False, CurrentTime);
+
+ /* 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.
+ */
+
+ switch (synth_type)
+ {
+ case Accessibility_KEY_PRESS:
+ XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, True, CurrentTime);
+ break;
+ case Accessibility_KEY_PRESSRELEASE:
+ XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, True, CurrentTime);
+ case Accessibility_KEY_RELEASE:
+ XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, False, CurrentTime);
+ break;
+ case Accessibility_KEY_SYM:
+ key_synth_code = keycode_for_keysym (keycode);
+ XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) key_synth_code, True, CurrentTime);
+ XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) key_synth_code, False, CurrentTime);
+ break;
+ }
}
/*
NULL=
-noinst_PROGRAMS = at app simple-at
+noinst_PROGRAMS = at app simple-at keysynth-demo
at_SOURCES = at.c
simple_at_SOURCES = simple-at.c
+keysynth_demo_SOURCES = keysynth-demo.c
+
INCLUDES = -I$(top_srcdir) \
-I$(top_builddir) \
-I$(top_srcdir)/libspi \
--- /dev/null
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2001 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 <stdlib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include "spi.h"
+
+#define LABELMAXLEN 20
+#define MIN_KEYCODE 9
+#define CAPSLOCK_KEYCODE 66
+
+/* these can be increased to access more keys */
+#define MAX_ROWS 5
+#define MAX_COLUMNS 14
+
+static KeystrokeListener *key_listener;
+
+static boolean shift_latched = False;
+static boolean caps_lock = False;
+static GtkButton **buttons[MAX_ROWS];
+
+static void
+label_buttons()
+{
+ int i, j;
+ KeySym keysym;
+ KeyCode keycode = MIN_KEYCODE;
+ char label[LABELMAXLEN] = " ";
+ char *button_label;
+ char *keysymstring;
+ boolean shifted;
+
+ for (i=0; i<MAX_ROWS; ++i)
+ {
+ for (j=0; j<MAX_COLUMNS; ++j, ++keycode)
+ {
+ shifted = caps_lock || shift_latched;
+ keysym = (KeySym) XKeycodeToKeysym (GDK_DISPLAY(), keycode, shifted ? 1 : 0);
+ /* Note: these routines are not i18n-savvy, we need to use XIM, other methods here */
+ if (keysym && g_ascii_isprint((int)keysym))
+ {
+ snprintf (label, 2, "%c", (int) keysym);
+ }
+ else
+ {
+ keysymstring = XKeysymToString (keysym);
+ if (keysymstring)
+ {
+ /* KP_ means keypad... we won't expose this difference */
+ if (!strncmp (keysymstring, "KP_", 3))
+ strncpy (label, (char *)(keysymstring+3), LABELMAXLEN);
+ else strncpy (label, keysymstring, LABELMAXLEN);
+ }
+ else *label = 0;
+ }
+ button_label =
+ *label==' ' ? " space " : label;
+ gtk_button_set_label (buttons[i][j], button_label);
+ }
+ }
+}
+
+static void
+do_shift (GtkButton *button)
+{
+ static KeyCode shift_keycode = 0;
+ if (!shift_keycode) shift_keycode = XKeysymToKeycode(GDK_DISPLAY(), (KeySym) 0xFFE1);
+ /* Note: in a real onscreen keyboard shift keycode should not be hard-coded! */
+ shift_latched = !shift_latched;
+ generateKeyEvent (shift_keycode, shift_latched ? KEY_PRESS : KEY_RELEASE);
+ if (buttons) label_buttons (buttons);
+}
+
+static void
+keysynth_exit()
+{
+ if (shift_latched) do_shift (NULL);
+ deregisterKeystrokeListener (key_listener, KEYMASK_ALT );
+ SPI_exit ();
+}
+
+static void
+button_exit(GtkButton *notused, void *alsonotused)
+{
+ keysynth_exit();
+}
+
+static boolean
+is_command_key (void *p)
+{
+ KeyStroke *key = (KeyStroke *)p;
+ switch (key->keyID)
+ {
+ case 'Q':
+ case 'q':
+ keysynth_exit();
+ return TRUE; /* not reached */
+ }
+}
+
+static void
+synth_keycode (GtkButton *button, KeyCode *keycode)
+{
+ if (*keycode) generateKeyEvent ((long) *keycode, KEY_PRESSRELEASE);
+ if (shift_latched) do_shift (button);
+ if (*keycode == CAPSLOCK_KEYCODE)
+ {
+ caps_lock = !caps_lock;
+ label_buttons ();
+ }
+}
+
+static void
+create_vkbd()
+{
+ GtkWidget *window, *button, *container, *hbox;
+ int i, j;
+ KeyCode *keycodeptr, keycode = MIN_KEYCODE;
+
+ window = g_object_connect (gtk_widget_new (gtk_window_get_type (),
+ "user_data", NULL,
+ "can_focus", FALSE,
+ "type", GTK_WINDOW_POPUP,
+ "window-position", GTK_WIN_POS_CENTER,
+ "title", "test",
+ "allow_grow", FALSE,
+ "allow_shrink", FALSE,
+ "border_width", 10,
+ NULL),
+ "signal::destroy", keysynth_exit, NULL,
+ NULL);
+
+ container = gtk_widget_new (GTK_TYPE_VBOX,
+ "GtkWidget::parent", window,
+ "GtkWidget::visible", TRUE,
+ NULL);
+ for (i=0; i<MAX_ROWS; ++i)
+ {
+ hbox = gtk_widget_new (gtk_hbox_get_type(),
+ "GtkWidget::parent", container,
+ "GtkWidget::visible", TRUE,
+ NULL);
+ buttons[i] = g_new0 (GtkButton*, MAX_COLUMNS);
+ for (j=0; j<MAX_COLUMNS; ++j)
+ {
+ keycodeptr = (KeyCode *) g_new0 (KeyCode, 1);
+ *keycodeptr = keycode;
+ ++keycode;
+ buttons[i][j] = g_object_connect (gtk_widget_new (gtk_button_get_type (),
+ "GtkWidget::parent", hbox,
+ "GtkWidget::visible", TRUE,
+ NULL),
+ "signal::clicked",
+ synth_keycode, keycodeptr,
+ NULL);
+ }
+ }
+ hbox = gtk_widget_new (gtk_hbox_get_type(),
+ "GtkWidget::parent", container,
+ "GtkWidget::visible", TRUE,
+ NULL);
+ button = g_object_connect (gtk_widget_new (gtk_button_get_type (),
+ "GtkButton::label", "Quit",
+ "GtkWidget::parent", hbox,
+ "GtkWidget::visible", TRUE,
+ NULL),
+ "signal::clicked",
+ button_exit, NULL,
+ NULL);
+ button = g_object_connect (gtk_widget_new (gtk_button_get_type (),
+ "GtkButton::label", "ShiftLatch",
+ "GtkWidget::parent", hbox,
+ "GtkWidget::visible", TRUE,
+ NULL),
+ "signal::clicked",
+ do_shift, NULL,
+ NULL);
+ label_buttons ();
+ gtk_widget_show (window);
+
+}
+
+int
+main(int argc, char **argv)
+{
+ if ((argc > 1) && (!strncmp(argv[1],"-h",2)))
+ {
+ printf ("Usage: keysynth-demo\n");
+ exit(0);
+ }
+
+ gtk_init (&argc, &argv); /* must call, because this program uses GTK+ */
+
+ SPI_init();
+
+ key_listener = createKeystrokeListener(is_command_key);
+ /* will listen only to Alt-key combinations */
+ registerKeystrokeListener(key_listener,
+ (KeySet *) ALL_KEYS,
+ KEYMASK_ALT,
+ (unsigned long) ( KeyPress | KeyRelease),
+ KEYLISTENER_CANCONSUME);
+ create_vkbd();
+
+ SPI_event_main(TRUE);
+}