tags 415451 patch thanks Hi,
The problem is the following: xte needs to convert the user input, e.g. the string '/', to key events. On a German keyboard this would be the following sequence: press the shift key, press the '7'-key, release the '7'-key, release the shift key. The same is the case for a US keyboard with the character '&'. Therefore xte needs to know the current keyboard layout in order to map 'input string' to 'sequence of key events'. Currently, this is implemented using a hard-coded table for the US keyboard layout. So it works for many characters that are generated using the shift key on a US keyboard (e.g. all capital letters,':','?','!' etc.), but it does not for other keyboard layouts. There is also a table for the German keyboard layout, but you have choose one at compile time, and the Debian package is compiled with the US layout. This is not very nice. A workaround would be to send commands with the explicit key sequence. For example instead of str http://foo.com/ the following sequence must be sent: str http: keydown Shift_L key 7 keyup Shift_L keydown Shift_L key 7 keyup Shift_L str foo.com keydown Shift_L key 7 keyup Shift_L However, this is quite ugly. Here I provide a patch to get rid of the hard-coded conversion table. With this patch, xte queries the current keyboard layout from the X-server and generates a table with the current mapping at startup. When processing the 'key' and 'str' commands, it generates the correct key event sequence for each character. Additionally, I implemented Unicode support, which means that now you can send UTF-8 encoded strings with the 'key' and 'str' commands. This is required, for example, to send the character 'รค', which is frequently used in German. PS: Because this package seems the be orphaned, I will try to build a package with this patch and ask a sponsor to upload it to the archive. I'm willing to become the maintainer of this package later, if that's in demand. Kind regards, Marco Steinacher
diff -rupN xautomation-0.96/configure.in xautomation-0.96-new/configure.in --- xautomation-0.96/configure.in 2004-05-13 23:49:02.000000000 +0200 +++ xautomation-0.96-new/configure.in 2007-09-24 17:34:30.000000000 +0200 @@ -9,20 +9,6 @@ AC_ARG_ENABLE(debug, in slower binaries]), AC_DEFINE(DEBUG_A_LOO)) -AC_ARG_ENABLE(keyboard, - AC_HELP_STRING([--enable-keyboard=keyboard], - [Use different keyboard map, default is 'us'. Can be any - of the kbd_*.h]), - ) - -if test "x$enable_keyboard" = "x" ; then - enable_keyboard=us -fi - -AC_MSG_NOTICE([Using '$enable_keyboard' as keyboard map]) -rm -f kbd.h -ln -s kbd_$enable_keyboard.h kbd.h - # Checks for programs. AC_PROG_CC @@ -33,14 +19,14 @@ AC_CHECK_LIB(png, png_read_info) # Checks for header files. AC_PATH_X -AC_CHECK_HEADERS([stdlib.h unistd.h]) +AC_CHECK_HEADERS([stdlib.h unistd.h wchar.h locale.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST # Checks for library functions. AC_FUNC_MALLOC -AC_CHECK_FUNCS([bzero]) +AC_CHECK_FUNCS([bzero wmemset mbstowcs]) if test "1$x_libraries" = "1" then diff -rupN xautomation-0.96/debian/rules xautomation-0.96-new/debian/rules --- xautomation-0.96/debian/rules 2007-09-20 18:51:20.000000000 +0200 +++ xautomation-0.96-new/debian/rules 2007-09-24 17:33:41.000000000 +0200 @@ -45,7 +45,6 @@ clean: dh_testdir dh_testroot -$(MAKE) distclean - -rm -f kbd.h -rm -f *-stamp dh_clean diff -rupN xautomation-0.96/kbd_german.h xautomation-0.96-new/kbd_german.h --- xautomation-0.96/kbd_german.h 2003-02-11 00:54:08.000000000 +0100 +++ xautomation-0.96-new/kbd_german.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,65 +0,0 @@ -/* Contributed by Stefan Nickl */ - -/* Map for German keyboards */ -#define KBDMAP "German" - -char *problems[] = { " ", NULL, "space", - ":", "Shift_L", "colon", - ";", "Shift_L", "semicolon", - "<", NULL, "less", - ">", "Shift_L", "greater", - "?", "Shift_L", "question", - "/", "Shift_L", "slash", - "+", NULL, "plus", - ",", NULL, "comma", - "-", NULL, "minus", - ".", NULL, "period", - "!", "Shift_L", "exclam", - "#", NULL, "number", - "$", "Shift_L", "dollar", - "%", "Shift_L", "percent", - "&", "Shift_L", "ampersand", - "(", "Shift_L", "parenleft", - ")", "Shift_L", "parenright", - "{", "AltGr", "braceleft", - "}", "AltGr", "braceright", - "|", "AltGr", "bar", - "~", "AltGr", "asciitilde", - "'", "Shift_L", "apostrophe", - "*", "Shift_L", "asterisk", - "=", "Shift_L", "equal", - "[", "AltGr", "bracketleft", - "]", "AltGr", "bracketright", - "_", "Shift_L", "underscore", - "`", "Shift_L", "grave", - "@", "AltGr", "at", - "A", "Shift_L", "A", - "B", "Shift_L", "B", - "C", "Shift_L", "C", - "D", "Shift_L", "D", - "E", "Shift_L", "E", - "F", "Shift_L", "F", - "G", "Shift_L", "G", - "H", "Shift_L", "H", - "I", "Shift_L", "I", - "J", "Shift_L", "J", - "K", "Shift_L", "K", - "L", "Shift_L", "L", - "M", "Shift_L", "M", - "N", "Shift_L", "N", - "O", "Shift_L", "O", - "P", "Shift_L", "P", - "Q", "Shift_L", "Q", - "R", "Shift_L", "R", - "S", "Shift_L", "S", - "T", "Shift_L", "T", - "U", "Shift_L", "U", - "V", "Shift_L", "V", - "W", "Shift_L", "W", - "X", "Shift_L", "X", - "Y", "Shift_L", "Y", - "Z", "Shift_L", "Z", - "\"", "Shift_L", "quote", - "\t", NULL, "tab", - "\\", "AltGr", "backslash", - NULL, NULL, NULL }; diff -rupN xautomation-0.96/kbd.h xautomation-0.96-new/kbd.h --- xautomation-0.96/kbd.h 1970-01-01 01:00:00.000000000 +0100 +++ xautomation-0.96-new/kbd.h 2007-09-24 17:11:45.000000000 +0200 @@ -0,0 +1,28 @@ +/* + * + * Copyright (c) 2002 Steve Slaven, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * +*/ + +#define NUM_KEY_MODIFIERS 3 +// ISO_Level3_Shift = AltGr +char *key_modifiers[] = { NULL, "Shift_L", "ISO_Level3_Shift" }; + +#define MAX_KEYSYM 65536 +int keysym_to_modifier_map[MAX_KEYSYM]; +KeyCode keysym_to_keycode_map[MAX_KEYSYM]; diff -rupN xautomation-0.96/kbd_us.h xautomation-0.96-new/kbd_us.h --- xautomation-0.96/kbd_us.h 2003-02-11 00:54:08.000000000 +0100 +++ xautomation-0.96-new/kbd_us.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,86 +0,0 @@ -/* - * - * Copyright (c) 2002 Steve Slaven, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - * -*/ - -/* Map for US keyboards */ -#define KBDMAP "US" - -/* The need shift thing is keymap dependant, it's: - char, needs_shit_key, x_token */ -char *problems[] = { " ", NULL, "space", - ":", "Shift_L", "colon", - ";", NULL, "semicolon", - "<", "Shift_L", "less", - ">", "Shift_L", "greater", - "?", "Shift_L", "question", - "/", NULL, "slash", - "+", "Shift_L", "plus", - ",", NULL, "comma", - "-", NULL, "minus", - ".", NULL, "period", - "!", "Shift_L", "exclam", - "#", "Shift_L", "number", - "$", "Shift_L", "dollar", - "%", "Shift_L", "percent", - "&", "Shift_L", "ampersand", - "(", "Shift_L", "parenleft", - ")", "Shift_L", "parenright", - "{", "Shift_L", "braceleft", - "}", "Shift_L", "braceright", - "|", "Shift_L", "bar", - "~", "Shift_L", "asciitilde", - "'", NULL, "apostrophe", - "*", "Shift_L", "asterisk", - "=", NULL, "equal", - "[", NULL, "bracketleft", - "]", NULL, "bracketright", - "_", "Shift_L", "underscore", - "`", NULL, "grave", - "@", "Shift_L", "at", - "A", "Shift_L", "A", - "B", "Shift_L", "B", - "C", "Shift_L", "C", - "D", "Shift_L", "D", - "E", "Shift_L", "E", - "F", "Shift_L", "F", - "G", "Shift_L", "G", - "H", "Shift_L", "H", - "I", "Shift_L", "I", - "J", "Shift_L", "J", - "K", "Shift_L", "K", - "L", "Shift_L", "L", - "M", "Shift_L", "M", - "N", "Shift_L", "N", - "O", "Shift_L", "O", - "P", "Shift_L", "P", - "Q", "Shift_L", "Q", - "R", "Shift_L", "R", - "S", "Shift_L", "S", - "T", "Shift_L", "T", - "U", "Shift_L", "U", - "V", "Shift_L", "V", - "W", "Shift_L", "W", - "X", "Shift_L", "X", - "Y", "Shift_L", "Y", - "Z", "Shift_L", "Z", - "\"", "Shift_L", "quote", - "\t", NULL, "tab", - "\\", NULL, "backslash", - NULL, NULL, NULL }; diff -rupN xautomation-0.96/Makefile.am xautomation-0.96-new/Makefile.am --- xautomation-0.96/Makefile.am 2003-09-16 22:32:29.000000000 +0200 +++ xautomation-0.96-new/Makefile.am 2007-09-24 17:32:32.000000000 +0200 @@ -1,5 +1,5 @@ bin_PROGRAMS = xte rgb2pat png2pat visgrep pat2ppm patextract -xte_SOURCES = xte.c debug.c kbd_* +xte_SOURCES = xte.c debug.c kbd.h xte_LDADD = -L @x_libraries@ -lX11 -lXtst rgb2pat_SOURCES = rgb2pat.c image.c image.h debug.c debug.h rgb2pat_LDADD = -lpng diff -rupN xautomation-0.96/xte.c xautomation-0.96-new/xte.c --- xautomation-0.96/xte.c 2007-09-20 18:51:20.000000000 +0200 +++ xautomation-0.96-new/xte.c 2007-09-20 19:36:48.000000000 +0200 @@ -25,10 +25,13 @@ #include <unistd.h> #include <string.h> #include <stdlib.h> +#include <wchar.h> +#include <locale.h> #include "debug.h" #define IS_CMD( x, y ) strncmp( x, y, strlen( y ) ) == 0 +#define CMD_STRING_MAXLEN 256 #include "kbd.h" @@ -45,32 +48,46 @@ KeyCode thing_to_keycode( Display *d, ch } kc = XKeysymToKeycode( d, ks ); - dmsg( 1, "String '%s' maps to keysym '%d'\n", thing, ks ); + dmsg( 1, "String '%s' maps to keysym '%lld'\n", thing, (long long int)ks ); dmsg( 1, "String '%s' maps to keycode '%d'\n", thing, kc ); return( kc ); } -void send_key( Display *d, char *thing ) { - int probidx; +/* Simulate pressed key(s) to generate thing character + * Only characters where the KeySym corresponds to the Unicode + * character code and KeySym < MAX_KEYSYM are supported, + * except the special character 'Tab'. */ +void send_key( Display *d, wchar_t *thing ) { char *wrap_key = NULL; + KeyCode keycode; + KeySym keysym; - dmsg( 1, "Sending key '%s'\n", thing ); + /* Special key: tab */ + if (L'\t' == thing[0]) { + dmsg( 1, "Sending special key 'Tab' (keysym=%lld)\n", (long long int)thing[0] ); + XTestFakeKeyEvent( d, thing_to_keycode( d, "Tab" ), True, CurrentTime ); + XTestFakeKeyEvent( d, thing_to_keycode( d, "Tab" ), False, CurrentTime ); + return; + } - /* Catch some common problem characters (thanks Martin Pirker) */ - probidx = 0; - while( problems[ probidx ] != NULL ) { - if( strcmp( thing, problems[ probidx ] ) == 0 ) { - wrap_key = problems[ probidx + 1 ]; - thing = problems[ probidx + 2 ]; - } - probidx += 3; + dmsg( 1, "Sending key '%ls' (keysym=%lld)\n", thing, (long long int)thing[0] ); + + /* keysym = wchar value */ + keysym = thing[0]; + if (keysym >= MAX_KEYSYM) { + fprintf(stderr,"Special character '%ls' is currently not supported.\n",thing); + return; } + /* Keyboard modifier and KeyCode lookup*/ + wrap_key=key_modifiers[keysym_to_modifier_map[keysym]]; + keycode=keysym_to_keycode_map[keysym]; + if( wrap_key != NULL ) XTestFakeKeyEvent( d, thing_to_keycode( d, wrap_key ), True, CurrentTime ); - XTestFakeKeyEvent( d, thing_to_keycode( d, thing ), True, CurrentTime ); - XTestFakeKeyEvent( d, thing_to_keycode( d, thing ), False, CurrentTime ); + XTestFakeKeyEvent( d, keycode, True, CurrentTime ); + XTestFakeKeyEvent( d, keycode, False, CurrentTime ); if( wrap_key != NULL ) XTestFakeKeyEvent( d, thing_to_keycode( d, wrap_key ), False, CurrentTime ); } @@ -97,21 +114,24 @@ void mouse_rel_move( Display *d, int x, void process_command( Display *d, const char *cmd ) { /* Process a command */ - int tmpx,tmpy; - char str[ 128 ]; + int tmpx,tmpy,i; + char str[ CMD_STRING_MAXLEN ]; + wchar_t wc_str[ CMD_STRING_MAXLEN ]; + wchar_t wc_singlechar_str[2]; - bzero( str, 128 ); + bzero( str, CMD_STRING_MAXLEN ); + wmemset(wc_str,L'\0',CMD_STRING_MAXLEN); if( IS_CMD( cmd, "mouseclick " ) ) { sscanf( cmd, "mouseclick %d", &tmpx ); mouse_click( d, tmpx ); }else if( IS_CMD( cmd, "key " ) ) { - strncpy( str, &cmd[ 4 ], 128 ); - send_key( d, str ); + mbstowcs(wc_str,&cmd[ 4 ],CMD_STRING_MAXLEN); + send_key( d, wc_str ); }else if( IS_CMD( cmd, "keydown " ) ) { - strncpy( str, &cmd[ 8 ], 128 ); + strncpy( str, &cmd[ 8 ], CMD_STRING_MAXLEN ); XTestFakeKeyEvent( d, thing_to_keycode( d, str ), True, CurrentTime ); }else if( IS_CMD( cmd, "keyup " ) ) { - strncpy( str, &cmd[ 6 ], 128 ); + strncpy( str, &cmd[ 6 ], CMD_STRING_MAXLEN ); XTestFakeKeyEvent( d, thing_to_keycode( d, str ), False, CurrentTime ); }else if( IS_CMD( cmd, "mousemove " ) ) { sscanf( cmd, "mousemove %d %d", &tmpx, &tmpy ); @@ -134,11 +154,13 @@ void process_command( Display *d, const sscanf( cmd, "mouseup %d", &tmpx ); XTestFakeButtonEvent( d, tmpx, False, CurrentTime ); }else if( IS_CMD( cmd, "str " ) ) { - cmd += 4; - while( cmd[ 0 ] != 0 ) { - str[ 0 ] = cmd[ 0 ]; - send_key( d, str ); - cmd++; + mbstowcs(wc_str,&cmd[ 4 ],CMD_STRING_MAXLEN); + wc_singlechar_str[1]=L'\0'; + i=0; + while ((wc_str[i] != L'\0') && (i < CMD_STRING_MAXLEN)) { + wc_singlechar_str[ 0 ] = wc_str[i]; + send_key( d, wc_singlechar_str ); + i++; } }else{ fprintf( stderr, "Unknown command '%s'\n", cmd ); @@ -146,6 +168,44 @@ void process_command( Display *d, const XFlush( d ); } + +/* Load keycodes and modifiers of current keyboard mapping into arrays */ +void load_keycodes(Display *d) { + int min_keycode,max_keycode,keysyms_per_keycode,keycode_index,wrap_key_index,num_modifiers; + char *str; + KeySym *keysyms,keysym; + KeyCode keycode; + + XDisplayKeycodes(d,&min_keycode,&max_keycode); + keysyms=XGetKeyboardMapping(d,(KeyCode)min_keycode,max_keycode+1-min_keycode,&keysyms_per_keycode); + + for(keysym=0;keysym<MAX_KEYSYM;keysym++) { + keysym_to_modifier_map[keysym]=-1; + keysym_to_keycode_map[keysym]=0; + } + + if (keysyms_per_keycode < NUM_KEY_MODIFIERS) { + num_modifiers = keysyms_per_keycode; + } else { + num_modifiers = NUM_KEY_MODIFIERS; + } + + for(keycode_index=0;keycode_index<(max_keycode+1-min_keycode);keycode_index++) { + for (wrap_key_index=0;wrap_key_index<num_modifiers;wrap_key_index++) { + str = XKeysymToString(keysyms[keycode_index*keysyms_per_keycode+wrap_key_index]); + if (str!=NULL) { + keysym = XStringToKeysym(str); + keycode = XKeysymToKeycode(d,keysym); + //printf("i=%d (keysym %lld), j=%d (keycode %d): %s\n",keycode_index,(long long int)keysym,wrap_key_index,keycode,str); + + if ((keysym<MAX_KEYSYM) && (keysym_to_modifier_map[keysym] == -1)) { + keysym_to_modifier_map[keysym] = wrap_key_index; + keysym_to_keycode_map[keysym] = keycode; + } + } + } + } +} int main( int argc, char *argv[] ) { Display *dpy = NULL; @@ -153,13 +213,14 @@ int main( int argc, char *argv[] ) { char *buf, *display = NULL; int opt; + setlocale(LC_ALL, "" ); + while( ( opt = getopt( argc, argv, "hd:x:" ) ) != EOF ) { switch( opt ) { case 'h': printf( "xte v" VERSION "\n" "Generates fake input using the XTest extension, more reliable than xse\n" "Author: Steve Slaven - http://hoopajoo.net\n" - "Current keyboard map: " KBDMAP "\n" "\n" "usage: %s [-h] [-x display] [arg ..]\n" "\n" @@ -242,6 +303,8 @@ int main( int argc, char *argv[] ) { exit( 1 ); } + load_keycodes(dpy); + if( argc - optind >= 1 ) { /* Arg mode */ for( cnt = optind; cnt < argc; cnt++ ) { @@ -249,8 +312,8 @@ int main( int argc, char *argv[] ) { } }else{ /* STDIN mode */ - buf = (char *)malloc( 128 ); - while( fgets( buf, 128, stdin ) ) { + buf = (char *)malloc( CMD_STRING_MAXLEN ); + while( fgets( buf, CMD_STRING_MAXLEN, stdin ) ) { buf[ strlen( buf ) - 1 ] = 0; /* Chop \n */ process_command( dpy, buf ); }