[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: