Bug#415451: xautomation: xte misinterprets '/' as '7'.
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 );
}
Reply to: