From e30f7b7a51a42a19765deedc3de17f489fca1d48 Mon Sep 17 00:00:00 2001 From: billh Date: Sun, 11 Nov 2001 00:28:07 +0000 Subject: [PATCH] Added C binding for key synthesis, and a new test/demo program that creates a simple onscreen keyboard (mouse-operated) and injects events into the currently focussed window. Added support for several types of key synthesis: KEY_PRESS, KEY_RELEASE, KEY_PRESSRELEASE (pair), KEY_SYM (pair). git-svn-id: http://svn.gnome.org/svn/at-spi/trunk@84 e2bd861d-eb25-0410-b326-f6ed22b6b98c --- ChangeLog | 20 +++- configure.in | 2 +- cspi/spi.h | 15 ++- cspi/spi_registry.c | 12 +- idl/Accessibility_Registry.idl | 18 ++- idl/Registry.idl | 18 ++- libspi/deviceeventcontroller.c | 41 ++++++- registryd/deviceeventcontroller.c | 41 ++++++- test/Makefile.am | 4 +- test/keysynth-demo.c | 226 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 374 insertions(+), 23 deletions(-) create mode 100644 test/keysynth-demo.c diff --git a/ChangeLog b/ChangeLog index 40ffba9..19db2dd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,22 @@ -2001-11-09 Bill Haneman + + * 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 * libspi/Makefile.am: * registryd/Makefile.am: diff --git a/configure.in b/configure.in index 9a30f0d..55383b0 100644 --- a/configure.in +++ b/configure.in @@ -91,7 +91,7 @@ PKG_CHECK_MODULES(REGISTRYD, bonobo-activation-2.0 >= 0.9.1 libbonobo-2.0 >= 1.9 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) diff --git a/cspi/spi.h b/cspi/spi.h index f83d20c..b9c559f 100644 --- a/cspi/spi.h +++ b/cspi/spi.h @@ -62,6 +62,13 @@ typedef enum _KeyEventType { KEY_RELEASED } KeyEventType; +typedef enum _KeySynthType { + KEY_PRESS, + KEY_RELEASE, + KEY_PRESSRELEASE, + KEY_SYM +} KeySynthType; + typedef enum _KeyListenerSyncType { KEYLISTENER_SYNCHRONOUS = 1, KEYLISTENER_CANCONSUME = 2, @@ -332,16 +339,16 @@ registerKeystrokeListener (KeystrokeListener *listener, * 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: diff --git a/cspi/spi_registry.c b/cspi/spi_registry.c index 6be863a..aa294c1 100644 --- a/cspi/spi_registry.c +++ b/cspi/spi_registry.c @@ -266,9 +266,17 @@ deregisterKeystrokeListener (KeystrokeListener *listener, KeyMaskType keymask) * **/ 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); } /** diff --git a/idl/Accessibility_Registry.idl b/idl/Accessibility_Registry.idl index 3d519ff..5908106 100644 --- a/idl/Accessibility_Registry.idl +++ b/idl/Accessibility_Registry.idl @@ -157,6 +157,13 @@ module Accessibility { KEY_RELEASED }; + enum KeySynthType { + KEY_PRESS, + KEY_RELEASE, + KEY_PRESSRELEASE, + KEY_SYM + }; + enum ModifierType { MODIFIER_SHIFT, MODIFIER_ALT, @@ -234,13 +241,20 @@ module Accessibility { /** * 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: diff --git a/idl/Registry.idl b/idl/Registry.idl index 3d519ff..5908106 100644 --- a/idl/Registry.idl +++ b/idl/Registry.idl @@ -157,6 +157,13 @@ module Accessibility { KEY_RELEASED }; + enum KeySynthType { + KEY_PRESS, + KEY_RELEASE, + KEY_PRESSRELEASE, + KEY_SYM + }; + enum ModifierType { MODIFIER_SHIFT, MODIFIER_ALT, @@ -234,13 +241,20 @@ module Accessibility { /** * 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: diff --git a/libspi/deviceeventcontroller.c b/libspi/deviceeventcontroller.c index f96d7b0..e3271bc 100644 --- a/libspi/deviceeventcontroller.c +++ b/libspi/deviceeventcontroller.c @@ -235,6 +235,7 @@ _check_key_event (DeviceEventController *controller) while (XPending(display)) { XNextEvent (display, x_event); + if (XFilterEvent (x_event, None)) continue; if (x_event->type == KeyPress) { x_key_event = (XKeyEvent *)x_event; @@ -410,21 +411,51 @@ impl_register_mouse_listener (PortableServer_Servant servant, } */ +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; + } } /* diff --git a/registryd/deviceeventcontroller.c b/registryd/deviceeventcontroller.c index f96d7b0..e3271bc 100644 --- a/registryd/deviceeventcontroller.c +++ b/registryd/deviceeventcontroller.c @@ -235,6 +235,7 @@ _check_key_event (DeviceEventController *controller) while (XPending(display)) { XNextEvent (display, x_event); + if (XFilterEvent (x_event, None)) continue; if (x_event->type == KeyPress) { x_key_event = (XKeyEvent *)x_event; @@ -410,21 +411,51 @@ impl_register_mouse_listener (PortableServer_Servant servant, } */ +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; + } } /* diff --git a/test/Makefile.am b/test/Makefile.am index d9a3ade..09e8d6a 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,6 @@ NULL= -noinst_PROGRAMS = at app simple-at +noinst_PROGRAMS = at app simple-at keysynth-demo at_SOURCES = at.c @@ -8,6 +8,8 @@ app_SOURCES = app.c simple_at_SOURCES = simple-at.c +keysynth_demo_SOURCES = keysynth-demo.c + INCLUDES = -I$(top_srcdir) \ -I$(top_builddir) \ -I$(top_srcdir)/libspi \ diff --git a/test/keysynth-demo.c b/test/keysynth-demo.c new file mode 100644 index 0000000..9bf7130 --- /dev/null +++ b/test/keysynth-demo.c @@ -0,0 +1,226 @@ +/* + * 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 +#include +#include +#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; ikeyID) + { + 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 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); +} -- 2.7.4