Implement XTest extension and xte demo
[free-sw/xcb/demo] / app / xte / xte.c
1 /*
2  *  
3  *  Copyright (c) 2002 Steve Slaven, All Rights Reserved.
4  *  
5  *  This program is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU General Public License as
7  *  published by the Free Software Foundation; either version 2 of
8  *  the License, or (at your option) any later version.
9  *  
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *  
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
18  *  MA 02111-1307 USA
19  *  
20 */
21
22 /* Generates an X event, like keypress/mouseclick/move/etc
23    like a little man in your computer.  :) */
24
25 #include <stdio.h>
26 #define X_H   /* make sure we aren't using symbols from X.h */
27 #include <X11/XCB/xcb.h>
28 #include <X11/XCB/xcb_keysyms.h>
29 #include <X11/XCB/xtest.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <stdlib.h>
33
34 /*#include "debug.h"*/
35 #define dmsg(a,b,...)
36
37 #define IS_CMD( x, y ) strncmp( x, y, strlen( y ) ) == 0
38
39 /* NOTE: demo only supports US keyboards, but could also support German; */
40 /*  see original configure.in */
41 /*#include "kbd.h"*/
42 #define KBDMAP "us"
43
44 /* TODO: this is normally defined in configure.in */
45 #define VERSION "0.97"
46
47 #define XK_Shift_L                       0xffe1  /* Left shift */
48
49 XCBKeySymbols *syms = NULL;
50
51 BYTE thing_to_keycode( XCBConnection *c, char *thing ) {
52   XCBKEYCODE kc;
53   XCBKEYSYM ks;
54   
55 #if 0   /* There is no XCB equivalent to XStringToKeysym */
56   ks = XStringToKeysym( thing );
57   if( ks == NoSymbol ){
58     fprintf( stderr, "Unable to resolve keysym for '%s'\n", thing );
59     return( thing_to_keycode( c, "space" ) );
60   }
61 #else
62   /* For now, assume thing[0] == Latin-1 keysym */
63   ks.id = (BYTE)thing[0];
64 #endif  
65
66   kc = XCBKeySymbolsGetKeycode( syms, ks );
67
68   dmsg( 1, "String '%s' maps to keysym '%d'\n", thing, ks );
69   dmsg( 1, "String '%s' maps to keycode '%d'\n", thing, kc );
70
71   return( kc.id );
72 }
73
74 /* XCBTestFakeInput(type,detail,time,window,x,y,device) */
75
76 static void
77 fake_input(XCBConnection *c, BYTE type, BYTE detail)
78 {
79   XCBWINDOW none = { XCBNone };
80
81   XCBTestFakeInput( c, type, detail, 0, none, 0, 0, 0 );
82 }
83
84 static void
85 fake_motion(XCBConnection *c, BOOL relative, CARD16 x, CARD16 y)
86 {
87   XCBWINDOW window = { XCBNone };
88
89   if (!relative) {
90     window = XCBConnSetupSuccessRepRootsIter(XCBGetSetup(c)).data->root;
91   }
92   XCBTestFakeInput( c, XCBMotionNotify, relative, 0, window, x, y, 0 );
93 }
94
95 void send_key( XCBConnection *c, char *thing ) {
96   static XCBKEYSYM shift = { XK_Shift_L };
97   BYTE code, wrap_code = 0;
98
99   dmsg( 1, "Sending key '%s'\n", thing );
100
101 #if 0
102   int probidx;
103   /* Catch some common problem characters (thanks Martin Pirker) */
104   for( probidx = 0; problems[ probidx ] != NULL; probidx += 3 ) {
105     if( strcmp( thing, problems[ probidx ] ) == 0 ) {
106       /*wrap_key = problems[ probidx + 1 ]; */
107       if (problems[ probidx + 1 ] != NULL) {
108         wrap_code = XCBKeySymbolsGetKeycode( syms, shift ).id;
109       }
110       thing = problems[ probidx + 2 ];
111       break;
112     }
113   }
114 #else
115   /* no XStringToKeysym support: do by hand */
116 /*const char *low = "`1234567890-=[]\\;',./";*/
117   const char *cap = "~!@#$%^&*()_+{}|:\"<>?";
118   
119   if (thing[0] >= 'A' && thing[0] <= 'Z')
120     wrap_code = XCBKeySymbolsGetKeycode( syms, shift ).id;
121   else if (strchr(cap, thing[0]) != NULL)
122     wrap_code = XCBKeySymbolsGetKeycode( syms, shift ).id;
123 #endif
124   code = thing_to_keycode( c, thing );
125
126   if( wrap_code )
127     fake_input( c, XCBKeyPress, wrap_code );
128
129   fake_input( c, XCBKeyPress, code );
130   fake_input( c, XCBKeyRelease, code );
131
132   if( wrap_code )
133     fake_input( c, XCBKeyRelease, wrap_code );
134 }
135
136 void mouse_click( XCBConnection *c, int button ) {
137   dmsg( 1, "Clicking mouse button %d\n", button );
138   fake_input( c, XCBButtonPress, button );
139   fake_input( c, XCBButtonRelease, button );
140 }
141
142 void mouse_move( XCBConnection *c, int x, int y ) {
143   dmsg( 1, "Moving mouse to %c,%d\n", x, y );
144   fake_motion( c, 0, x, y );
145 }
146
147 void mouse_rel_move( XCBConnection *c, int x, int y ) {
148   dmsg( 1, "Moving mouse relatively by %c,%d\n", x, y );
149   fake_motion( c, 1, x, y );
150 }
151
152 void process_command( XCBConnection *c, const char *cmd ) {
153   /* Process a command */
154   int tmpx,tmpy;
155   char str[ 128 ];
156
157   bzero( str, 128 );
158   if( IS_CMD( cmd, "mouseclick " ) ) {
159     sscanf( cmd, "mouseclick %d", &tmpx );
160     tmpx = tmpx<1 ? 1 : (tmpx>5 ? 5 : tmpx);
161     mouse_click( c, tmpx );
162   }else if( IS_CMD( cmd, "key " ) ) {
163     strncpy( str, &cmd[ 4 ], 128 );
164     send_key( c, str );
165   }else if( IS_CMD( cmd, "keydown " ) ) {
166     strncpy( str, &cmd[ 8 ], 128 );
167     fake_input( c, XCBKeyPress, thing_to_keycode( c, str ) );
168   }else if( IS_CMD( cmd, "keyup " ) ) {
169     strncpy( str, &cmd[ 6 ], 128 );
170     fake_input( c, XCBKeyRelease, thing_to_keycode( c, str ) );
171   }else if( IS_CMD( cmd, "mousemove " ) ) {
172     sscanf( cmd, "mousemove %d %d", &tmpx, &tmpy );
173     mouse_move( c, tmpx, tmpy );
174   }else if( IS_CMD( cmd, "mousermove " ) ) {
175     sscanf( cmd, "mousermove %d %d", &tmpx, &tmpy );
176     mouse_rel_move( c, tmpx, tmpy );
177   }else if( IS_CMD( cmd, "sleep " ) ) {
178     sscanf( cmd, "sleep %d", &tmpx );
179     dmsg( 1, "sleep %d\n", tmpx );
180     sleep( tmpx );
181   }else if( IS_CMD( cmd, "usleep " ) ) {
182     sscanf( cmd, "usleep %d", &tmpx );
183     dmsg( 1, "usleep %d\n", tmpx );
184     usleep( tmpx );
185   }else if( IS_CMD( cmd, "mousedown " ) ) {
186     sscanf( cmd, "mousedown %d", &tmpx );
187     tmpx = tmpx<1 ? 1 : (tmpx>5 ? 5 : tmpx);
188     fake_input( c, XCBButtonPress, tmpx );
189   }else if( IS_CMD( cmd, "mouseup " ) ) {
190     sscanf( cmd, "mouseup %d", &tmpx );
191     tmpx = tmpx<1 ? 1 : (tmpx>5 ? 5 : tmpx);
192     fake_input( c, XCBButtonRelease, tmpx );
193   }else if( IS_CMD( cmd, "str " ) ) {
194     cmd += 4;
195     while( cmd[ 0 ] != 0 ) {
196       str[ 0 ] = cmd[ 0 ];
197       send_key( c, str );
198       cmd++;
199     }
200   /* in the absence of XStringToKeysym, allow sending hex syms directly */
201   }else if( IS_CMD( cmd, "sym " ) ) {
202     XCBKEYSYM sym;
203     XCBKEYCODE code;
204     sscanf( str, "sym %x", &sym.id );
205     code = XCBKeySymbolsGetKeycode( syms, sym );
206     fake_input( c, XCBKeyPress, code.id );
207     fake_input( c, XCBKeyRelease, code.id );
208   }else if( IS_CMD( cmd, "symdown " ) ) {
209     XCBKEYSYM sym;
210     sscanf( str, "symdown %x", &sym.id );
211     fake_input( c, XCBKeyPress, XCBKeySymbolsGetKeycode( syms, sym ).id );
212   }else if( IS_CMD( cmd, "symup " ) ) {
213     XCBKEYSYM sym;
214     sscanf( str, "symup %x", &sym.id );
215     fake_input( c, XCBKeyRelease, XCBKeySymbolsGetKeycode( syms, sym ).id );
216   }else{
217     fprintf( stderr, "Unknown command '%s'\n", cmd );
218   }
219
220   XCBFlush( c );
221 }
222
223 int main( int argc, char *argv[] ) {
224   XCBConnection *c = NULL;
225   int cnt;  /*, tmp_i; */
226   char *buf, *display = NULL;
227   int opt;
228  
229   while( ( opt = getopt( argc, argv, "hx:" ) ) != EOF ) {  /* "hd:x: */
230     switch( opt ) {
231     case 'h':
232       printf( "xte v" VERSION "\n"
233               "Generates fake input using the XTest extension, more reliable than xse\n"
234               "Author: Steve Slaven - http://hoopajoo.net\n"
235               "Ported to XCB: Ian Osgood\n"
236               "Current keyboard map: " KBDMAP "\n"
237               "\n"
238               "usage: %s [-h] [-x display] [arg ..]\n"
239               "\n"
240               "  -h  this help\n"
241               "  -x  send commands to remote X server.  Note that some commands\n"
242               "      may not work correctly unless the display is on the console,\n"
243               "      e.g. the display is currently controlled by the keyboard and\n"
244               "      mouse and not in the background.  This seems to be a limitation\n"
245               "      of the XTest extension.\n"
246               "  arg args instructing the little man on what to do (see below)\n"
247               "      if no args are passec, commands are read from stdin separated\n"
248               "      by newlines, to allow a batch mode\n"
249               "\n"
250               " Commands:\n"
251               "  key k          Press and release key k\n"
252               "  keydown k      Press key k down\n"
253               "  keyup k        Release key k\n"
254               "  str string     Do a bunch of key X events for each char in string\n"
255               "  mouseclick i   Click mouse button i\n"
256               "  mousemove x y  Move mouse to screen position x,y\n"
257               "  mousermove x y Move mouse relative from current location by x,y\n"
258               "  mousedown i    Press mouse button i down\n"
259               "  mouseup i      Release mouse button i\n"
260               "  sleep x        Sleep x seconds\n"
261               "  usleep x       uSleep x microseconds\n"
262               "\n"
263               "Some useful keys (case sensitive)\n"
264               "  Home\n"
265               "  Left\n"
266               "  Up\n"
267               "  Right\n"
268               "  Down\n"
269               "  Page_Up\n"
270               "  Page_Down\n"
271               "  End\n"
272               "  Return\n"
273               "  Backspace\n"
274               "  Tab\n"
275               "  Escape\n"
276               "  Delete\n"
277               "  Shift_L\n"
278               "  Shift_R\n"
279               "  Control_L\n"
280               "  Control_R\n"
281               "  Meta_L\n"
282               "  Meta_R\n"
283               "  Alt_L\n"
284               "  Alt_R\n"
285               "\n"
286               "Sample, drag from 100,100 to 200,200 using mouse1:\n"
287               "  xte 'mousemove 100 100' 'mousedown 1' 'mousemove 200 200' 'mouseup 1'\n"
288               "\n"
289               , argv[ 0 ] );
290       exit( 0 );
291       break;
292 #if 0      
293     case 'd':
294       sscanf( optarg, "%d", &tmp_i );
295       dmsg( 2, "Debug set to %d\n", tmp_i );
296       debug_level( tmp_i );
297       break;
298 #endif
299     case 'x':
300       display = optarg;
301       break;
302
303     case '?':
304       fprintf( stderr, "Unknown option '%c'\n", optopt );
305       break;
306       
307     default:
308       fprintf( stderr, "Unhandled option '%c'\n", opt );
309       break;
310     }
311   }
312
313   c = XCBConnect( display, NULL );
314   if( c == NULL ) {
315     fprintf( stderr, "Unable to open display '%s'\n", display == NULL ? "default" : display );
316     exit( 1 );
317   }
318   
319   /* do XTest init and version check (need 2.1) */
320   /* XCBTestInit( c );   required? none of the other extension demos do this */
321   
322   XCBTestGetVersionCookie cookie = XCBTestGetVersion( c, 2, 1 );
323
324   XCBGenericError *e = NULL;
325   XCBTestGetVersionRep *xtest_reply = XCBTestGetVersionReply ( c, cookie, &e );
326   if (xtest_reply) {
327     fprintf( stderr, "XTest version %u.%u\n",
328       (unsigned int)xtest_reply->major_version,
329       (unsigned int)xtest_reply->minor_version );
330     free(xtest_reply);
331   }
332   if (e) {
333     fprintf( stderr, "XTest version error: %d", (int)e->error_code );
334     free(e);
335   }
336   
337   
338   /* prep for keysym-->keycode conversion */
339   syms = XCBKeySymbolsAlloc( c );
340
341   if( argc - optind >= 1 ) {
342     /* Arg mode */
343     for( cnt = optind; cnt < argc; cnt++ ) {
344       process_command( c, argv[ cnt ] );
345     }
346   }else{
347     /* STDIN mode */
348     buf = (char *)malloc( 128 );
349     while( fgets( buf, 128, stdin ) ) {
350       buf[ strlen( buf ) - 1 ] = 0; /* Chop \n */
351       process_command( c, buf );
352     }
353   }
354   
355   XCBKeySymbolsFree( syms );
356
357   XCBDisconnect( c );
358   exit( 0 );
359 }