Little fix.
[irreco.git] / lirc-0.8.4a / tools / irxevent.c
blobe307ca98be6b145c85ac7e247b148f0c8ea32df5
1 /* $Id: irxevent.c,v 5.19 2007/03/30 07:13:47 lirc Exp $ */
3 /****************************************************************************
4 ** irxevent.c **************************************************************
5 ****************************************************************************
7 * irxevent - infra-red xevent sender
9 * Heinrich Langos <heinrich@null.net>
10 * small modifications by Christoph Bartelmus <lirc@bartelmus.de>
12 * irxevent is based on irexec (Copyright (C) 1998 Trent Piepho)
13 * and irx.c (no copyright notice found)
15 * =======
16 * HISTORY
17 * =======
19 * 0.1
20 * -Initial Release
22 * 0.2
23 * -no more XWarpPointer... sending Buttonclicks to off-screen
24 * applications works becaus i also fake the EnterNotify and LeaveNotify
25 * -support for keysymbols rather than characters... so you can use
26 * Up or Insert or Control_L ... maybe you could play xquake than :*)
28 * 0.3
29 * -bugfix for looking for subwindows of non existing windows
30 * -finaly a README file
32 * 0.3a (done by Christoph Bartelmus)
33 * -read from a shared .lircrc file
34 * -changes to comments
35 * (chris, was that all you changed?)
37 * 0.4
38 * -fake_timestamp() to solve gqmpeg problems
39 * -Shift Control and other mod-keys may work. (can't check it right now)
40 * try ctrl-c or shift-Page_up or whatever ...
41 * modifiers: shift, caps, ctrl, alt, meta, numlock, mod3, mod4, scrlock
42 * -size of 'char *keyname' changed from 64 to 128 to allow all mod-keys.
43 * -updated irxevent.README
45 * 0.4.1
46 * -started to make smaller version steps :-)
47 * -Use "CurrentWindow" as window name to send events to the window
48 * that -you guessed it- currently has the focus.
50 * 0.4.2
51 * -fixed a stupid string bug in key sending.
52 * -updated irxevent.README to be up to date with the .lircrc format.
54 * 0.4.3
55 * -changed DEBUG functions to actually produce some output :)
57 * 0.5.0
58 * -fixed finding subwindows recursively
59 * -added xy_Key (though xterm and xemacs still don't like me)
60 * -added compilation patch from Ben Hochstedler
61 * <benh@eeyore.moneng.mei.com> for compiling on systems
62 * without strsep() (like some solaris)
65 * see http://www.wh9.tu-dresden.de/~heinrich/lirc/irxevent/irxevent.keys
66 * for a the key names. (this one is for you Pablo :-) )
68 * for more information see the irxevent.README file
72 #ifdef HAVE_CONFIG_H
73 # include <config.h>
74 #endif
76 #include <errno.h>
77 #include <unistd.h>
78 #include <getopt.h>
79 #include <stdarg.h>
80 #include <stdio.h>
81 #include <stdlib.h>
82 #include <string.h>
83 #include <sys/socket.h>
84 #include <sys/un.h>
85 #include <sys/stat.h>
86 #include <sys/types.h>
88 #include <X11/Xlib.h>
89 #include <X11/Xutil.h>
90 #include <sys/time.h>
91 #include <unistd.h>
93 #include "lirc_client.h"
95 static int bDaemon = 0;
96 static int bInError = 0;
98 #ifdef DEBUG
99 static void debugprintf(char *format_str, ...)
101 va_list ap;
102 va_start(ap,format_str);
103 if(!bDaemon)
104 vfprintf(stderr,format_str,ap);
105 va_end(ap);
107 #else
108 static void debugprintf(char *format_str, ...)
111 #endif
113 struct keymodlist_t {
114 char *name;
115 Mask mask;
117 static struct keymodlist_t keymodlist[]=
119 {"SHIFT", ShiftMask},
120 {"CAPS", LockMask},
121 {"CTRL", ControlMask},
122 {"ALT", Mod1Mask},{"META", Mod1Mask},
123 {"NUMLOCK", Mod2Mask},
124 {"MOD3", Mod3Mask}, /* I don't have a clue what key maps to this. */
125 {"MOD4", Mod4Mask}, /* I don't have a clue what key maps to this. */
126 {"SCRLOCK", Mod5Mask},
127 {NULL,0},
130 static const char *key_delimiter ="-";
131 static const char *active_window_name ="CurrentWindow";
132 static const char *root_window_name ="RootWindow";
135 static const char *progname="irxevent";
136 static Display *dpy;
137 static Window root;
138 static XEvent xev;
139 static Window w,subw;
141 static Time fake_timestamp()
142 /*seems that xfree86 computes the timestamps like this */
143 /*strange but it relies on the *1000-32bit-wrap-around */
144 /*if anybody knows exactly how to do it, please contact me */
146 int tint;
147 struct timeval tv;
148 struct timezone tz; /* is not used since ages */
149 gettimeofday(&tv,&tz);
150 tint=(int)tv.tv_sec*1000;
151 tint=tint/1000*1000;
152 tint=tint+tv.tv_usec/1000;
153 return (Time)tint;
156 static Window find_window(Window top,char *name)
158 char *wname,*iname;
159 XClassHint xch;
160 Window *children,foo;
161 int revert_to_return;
162 unsigned int nc;
163 if (!strcmp(active_window_name,name)){
164 XGetInputFocus(dpy, &foo, &revert_to_return);
165 return(foo);
166 } else if (!strcmp(root_window_name,name)){
167 return(root);
169 /* First the base case */
170 if (XFetchName(dpy,top,&wname)){
171 if (!strncmp(wname,name,strlen(name))) {
172 XFree(wname);
173 debugprintf("found it by wname 0x%x \n",top);
174 return(top); /* found it! */
176 XFree(wname);
179 if(XGetIconName(dpy,top,&iname)){
180 if (!strncmp(iname,name,strlen(name))) {
181 XFree(iname);
182 debugprintf("found it by iname 0x%x \n",top);
183 return(top); /* found it! */
185 XFree(iname);
188 if(XGetClassHint(dpy,top,&xch)) {
189 if(!strcmp(xch.res_class,name)) {
190 XFree(xch.res_name); XFree(xch.res_class);
191 debugprintf("res_class '%s' res_name '%s' 0x%x \n", xch.res_class,xch.res_name,top);
192 return(top); /* found it! */
194 if(!strcmp(xch.res_name,name)) {
195 XFree(xch.res_name); XFree(xch.res_class);
196 debugprintf("res_class '%s' res_name '%s' 0x%x \n", xch.res_class,xch.res_name,top);
197 return(top); /* found it! */
199 XFree(xch.res_name); XFree(xch.res_class);
202 if(!XQueryTree(dpy,top,&foo,&foo,&children,&nc) || children==NULL) {
203 return(0); /* no more windows here */
206 /* check all the sub windows */
207 for(;nc>0;nc--) {
208 top = find_window(children[nc-1],name);
209 if(top) break; /* we found it somewhere */
211 if(children!=NULL) XFree(children);
212 return(top);
215 static Window find_sub_sub_window(Window top,int *x, int *y)
217 Window base;
218 Window *children,foo,target=0;
219 unsigned int nc,
220 rel_x,rel_y,width,height,border,depth,
221 new_x=1,new_y=1,
222 targetsize=1000000;
224 base=top;
225 if (!base) {return base;};
226 if(!XQueryTree(dpy,base,&foo,&foo,&children,&nc) || children==NULL) {
227 return(base); /* no more windows here */
229 debugprintf("found subwindows %d\n",nc);
231 /* check if we hit a sub window and find the smallest one */
232 for(;nc>0;nc--) {
233 if(XGetGeometry(dpy, children[nc-1], &foo, &rel_x, &rel_y,
234 &width, &height, &border, &depth)){
235 if ((rel_x<=*x)&&(*x<=rel_x+width)&&(rel_y<=*y)&&(*y<=rel_y+height)){
236 debugprintf("found a subwindow 0x%x +%d +%d %d x %d \n",children[nc-1], rel_x,rel_y,width,height);
237 if ((width*height)<targetsize){
238 target=children[nc-1];
239 targetsize=width*height;
240 new_x=*x-rel_x;
241 new_y=*y-rel_y;
242 /*bull's eye ...*/
243 target=find_sub_sub_window(target,&new_x,&new_y);
248 if(children!=NULL) XFree(children);
249 if (target){
250 *x=new_x;
251 *y=new_y;
252 return target;
253 }else
254 return base;
259 static Window find_sub_window(Window top,char *name,int *x, int *y)
261 Window base;
262 Window *children,foo,target=0;
263 unsigned int nc,
264 rel_x,rel_y,width,height,border,depth,
265 new_x=1,new_y=1,
266 targetsize=1000000;
268 base=find_window(top, name);
269 if (!base) {return base;};
270 if(!XQueryTree(dpy,base,&foo,&foo,&children,&nc) || children==NULL) {
271 return(base); /* no more windows here */
273 debugprintf("found subwindows %d\n",nc);
275 /* check if we hit a sub window and find the smallest one */
276 for(;nc>0;nc--) {
277 if(XGetGeometry(dpy, children[nc-1], &foo, &rel_x, &rel_y,
278 &width, &height, &border, &depth)){
279 if ((rel_x<=*x)&&(*x<=rel_x+width)&&(rel_y<=*y)&&(*y<=rel_y+height)){
280 debugprintf("found a subwindow 0x%x +%d +%d %d x %d \n",children[nc-1], rel_x,rel_y,width,height);
281 if ((width*height)<targetsize){
282 target=children[nc-1];
283 targetsize=width*height;
284 new_x=*x-rel_x;
285 new_y=*y-rel_y;
286 /*bull's eye ...*/
287 target=find_sub_sub_window(target,&new_x,&new_y);
292 if(children!=NULL) XFree(children);
293 if (target){
294 *x=new_x;
295 *y=new_y;
296 return target;
297 }else
298 return base;
302 static Window find_window_focused(Window top,char *name)
304 int tmp;
305 Window w, cur, *children, foo;
306 unsigned int n;
309 /* return the currently focused window if it is a direct match or a
310 subwindow of the named window */
312 if((w=find_window(top,name))) {
313 XGetInputFocus(dpy, &cur, &tmp);
314 debugprintf("current window: 0x%x named window: 0x%x\n",cur,w);
316 if( w == cur ) {
317 /* window matched */
318 return(cur);
320 else if(XQueryTree(dpy,w,&foo,&foo,&children,&n) && children!=NULL) {
321 /* check all the sub windows of named window */
322 for(;n>0;n--) {
323 if(children[n-1] == cur ) {
324 XFree(children);
325 return(cur);
328 XFree(children);
332 return(0);
335 static void make_button(int button,int x,int y,XButtonEvent *xev)
337 xev->type = ButtonPress;
338 xev->display=dpy;
339 xev->root=root;
340 xev->subwindow=None;
341 xev->time=fake_timestamp();
342 xev->x=x;xev->y=y;
343 xev->x_root=1; xev->y_root=1;
344 xev->state=0;
345 xev->button=button;
346 xev->same_screen=True;
348 return;
351 static void make_key(char *keyname,int x, int y,XKeyEvent *xev)
353 char *part, *part2, *sep_part;
354 struct keymodlist_t *kmlptr;
355 KeySym ks;
356 KeyCode kc;
358 part2=malloc(128);
360 xev->type = KeyPress;
361 xev->display=dpy;
362 xev->root=root;
363 xev->subwindow = None;
364 xev->time=fake_timestamp();
365 xev->x=x; xev->y=y;
366 xev->x_root=1; xev->y_root=1;
367 xev->same_screen = True;
369 xev->state=0;
370 #ifdef HAVE_STRSEP
371 while ((part=strsep(&keyname, key_delimiter)))
372 #else
373 while ((part=strtok(keyname, key_delimiter)) && ((keyname=NULL)==NULL))
374 #endif
376 part2=strncpy(part2,part,128);
377 // debugprintf("- %s \n",part);
378 kmlptr=keymodlist;
379 while (kmlptr->name)
381 // debugprintf("-- %s %s \n", kmlptr->name, part);
382 if (!strcasecmp(kmlptr->name, part))
383 xev->state|=kmlptr->mask;
384 kmlptr++;
386 // debugprintf("--- %s \n",part);
388 // debugprintf("*** %s \n",part);
389 // debugprintf("*** %s \n",part2);
392 * New code 14-June-2005 by Warren Melnick, C.A.C. Media
393 * Uses the KeySym: and KeyCode: prefixes on the Key lines to allow for
394 * numeric keysym and keycode numbers to be used in place of X keysyms.
395 * Example 1: config = Key KeyCode:127 CurrentWindow
396 * Example 2: config = Key KeySym:0xFFF0 CurrentWindow
398 ks = 0;
399 kc = 0;
400 if (strncmp(part2, "KeySym:", 7) == 0) {
401 sep_part = part2 + 7;
402 ks = strtoul(sep_part, NULL, 0);
403 kc = XKeysymToKeycode(dpy, ks);
404 debugprintf("KeySym String: %s, KeySym: %ld KeyCode: %d\n", part2, ks, kc);
405 } else if (strncmp(part2, "KeyCode:", 8) == 0) {
406 sep_part = part2 + 8;
407 kc = (KeyCode) strtoul(sep_part, NULL, 0);
408 debugprintf("KeyCode String: %s, KeyCode: %d\n", part2, kc);
410 if ((ks == 0) && (kc == 0)) {
411 ks = XStringToKeysym(part2);
412 kc = XKeysymToKeycode(dpy, ks);
413 debugprintf("Unmodified String: %s, KeySym: %d KeyCode: %d\n", part2, ks, kc);
415 xev->keycode=kc;
416 debugprintf("state 0x%x, keycode 0x%x\n",xev->state, xev->keycode);
417 free(part2);
418 return ;
421 static void sendfocus(Window w,int in_out)
423 XFocusChangeEvent focev;
425 focev.display=dpy;
426 focev.type=in_out;
427 focev.window=w;
428 focev.mode=NotifyNormal;
429 focev.detail=NotifyPointer;
430 XSendEvent(dpy,w,True,FocusChangeMask,(XEvent*)&focev);
431 XSync(dpy,True);
433 return;
436 static void sendpointer_enter_or_leave(Window w,int in_out)
438 XCrossingEvent crossev;
439 crossev.type=in_out;
440 crossev.display=dpy;
441 crossev.window=w;
442 crossev.root=root;
443 crossev.subwindow=None;
444 crossev.time=fake_timestamp();
445 crossev.x=1;
446 crossev.y=1;
447 crossev.x_root=1;
448 crossev.y_root=1;
449 crossev.mode=NotifyNormal;
450 crossev.detail=NotifyNonlinear;
451 crossev.same_screen=True;
452 crossev.focus=True;
453 crossev.state=0;
454 XSendEvent(dpy,w,True,EnterWindowMask|LeaveWindowMask,(XEvent*)&crossev);
455 XSync(dpy,True);
456 return;
459 static void sendkey(char *keyname,int x,int y,Window w,Window s)
461 make_key(keyname ,x,y,(XKeyEvent*)&xev);
462 xev.xkey.window=w;
463 xev.xkey.subwindow=s;
465 if (s) sendfocus(s,FocusIn);
467 XSendEvent(dpy,w,True,KeyPressMask,&xev);
468 xev.type = KeyRelease;
469 usleep(2000);
470 xev.xkey.time = fake_timestamp();
471 if (s) sendfocus(s,FocusOut);
472 XSendEvent(dpy,w,True,KeyReleaseMask,&xev);
473 XSync(dpy,True);
474 return;
477 static void sendbutton(int button, int x, int y, Window w,Window s)
479 make_button(button,x,y,(XButtonEvent*)&xev);
480 xev.xbutton.window=w;
481 xev.xbutton.subwindow=s;
482 sendpointer_enter_or_leave(w,EnterNotify);
483 sendpointer_enter_or_leave(s,EnterNotify);
485 XSendEvent(dpy,w,True,ButtonPressMask,&xev);
486 XSync(dpy,True);
487 xev.type = ButtonRelease;
488 xev.xkey.state|=0x100;
489 usleep(1000);
490 xev.xkey.time = fake_timestamp();
491 XSendEvent(dpy,w,True,ButtonReleaseMask,&xev);
492 sendpointer_enter_or_leave(s,LeaveNotify);
493 sendpointer_enter_or_leave(w,LeaveNotify);
494 XSync(dpy,True);
496 return;
499 int errorHandler(Display* di, XErrorEvent* ev)
501 char buff[512]; buff[0] = 0;
502 if(bInError || ev==NULL || di==NULL) return 1; // only 1 msg per key
503 XGetErrorText(di, ev->error_code, buff, sizeof(buff)-1);
504 if(buff[0]) {
505 if(!bDaemon) fprintf(stderr, "X11 error: %s\n", buff);
506 bInError = 1;
508 return 1;
511 int check(char *s)
513 int d;
514 char *buffer;
516 buffer=malloc(strlen(s));
517 if(buffer==NULL)
519 fprintf(stderr,"%s: out of memory\n",progname);
520 return(-1);
523 if(2!=sscanf(s,"Key %s Focus %s %s",buffer,buffer,buffer) &&
524 2!=sscanf(s,"Key %s WindowID %i %s",buffer,&d,buffer) &&
525 2!=sscanf(s,"Key %s Focus WindowID %i %s",buffer,&d,buffer) &&
526 2!=sscanf(s,"Key %s %s %s",buffer,buffer,buffer) &&
527 4!=sscanf(s,"Button %d %d %d Focus %s %s",&d,&d,&d,buffer,buffer) &&
528 4!=sscanf(s,"Button %d %d %d WindowID %i %s",&d,&d,&d,&d,buffer) &&
529 4!=sscanf(s,"Button %d %d %d Focus WindowID %i %s",&d,&d,&d,&d,buffer) &&
530 4!=sscanf(s,"Button %d %d %d %s %s",&d,&d,&d,buffer,buffer) &&
531 4!=sscanf(s,"xy_Key %d %d %s Focus %s %s",&d,&d,buffer,buffer,buffer) &&
532 4!=sscanf(s,"xy_Key %d %d %s WindowID %i %s",&d,&d,buffer,&d,buffer) &&
533 4!=sscanf(s,"xy_Key %d %d %s Focus WindowID %i %s",&d,&d,buffer,&d,buffer) &&
534 4!=sscanf(s,"xy_Key %d %d %s %s",&d,&d,buffer,buffer))
536 fprintf(stderr,"%s: bad config string \"%s\"\n",progname,s);
537 free(buffer);
538 return(-1);
540 free(buffer);
541 return(0);
544 static struct option long_options[] =
546 {"daemon", no_argument, NULL, 'd'},
547 {"help", no_argument, NULL, 'h'},
548 {"version", no_argument, NULL, 'V'},
549 {0, 0, 0, 0}
552 int main(int argc, char *argv[])
554 char keyname[128];
555 int pointer_button,pointer_x,pointer_y;
556 char windowname[64];
557 struct lirc_config *config;
558 char *config_file=NULL;
559 int c;
560 int WindowID;
562 while ((c = getopt_long(argc, argv, "dhV", long_options, NULL)) != EOF) {
563 switch (c) {
564 case 'd':
565 bDaemon = 1; continue;
566 case 'h':
567 printf("Usage: %s [option]... [config file]\n"
568 " -d --daemon fork and run in background\n"
569 " -h --help display usage summary\n"
570 " -V --version display version\n", progname);
571 return(EXIT_SUCCESS);
572 case 'V':
573 printf("%s %s\n", progname, VERSION);
574 return(EXIT_SUCCESS);
575 case '?':
576 fprintf(stderr, "unrecognized option: -%c\n", optopt);
577 fprintf(stderr, "Try `%s --help' for more information.\n", progname);
578 return(EXIT_FAILURE);
582 if (argc == optind+1){
583 config_file = argv[optind];
584 } else if (argc > optind+1){
585 fprintf(stderr, "%s: incorrect number of arguments.\n", progname);
586 fprintf(stderr, "Try `%s --help' for more information.\n", progname);
587 return(EXIT_FAILURE);
590 dpy=XOpenDisplay(NULL);
591 if(dpy==NULL) {
592 fprintf(stderr,"Can't open DISPLAY.\n");
593 exit(1);
595 root=RootWindow(dpy,DefaultScreen(dpy));
597 // windows may get closed at wrong time. Override default error handler...
598 XSetErrorHandler(errorHandler);
600 if(lirc_init("irxevent",1)==-1) exit(EXIT_FAILURE);
602 if(lirc_readconfig(config_file,&config,check)==0)
604 char *ir;
605 char *c;
606 int ret;
608 if(bDaemon) {
609 if(daemon(1, 0) < 0) {
610 perror("Failed to run as daemon");
611 exit(EXIT_FAILURE);
615 while(lirc_nextcode(&ir)==0)
617 if(ir==NULL) continue;
618 while((ret=lirc_code2char(config,ir,&c))==0 && c!=NULL)
620 debugprintf("Received code: %s Sending event: \n",ir);
621 bInError = 0; // reset error state, want to see error msg
623 *windowname=0;
624 if(2==sscanf(c,"Key %s Focus WindowID %i",keyname,&WindowID) ||
625 4==sscanf(c,"Button %d %d %d Focus WindowID %i",&pointer_button,&pointer_x,&pointer_y,&WindowID) ||
626 4==sscanf(c,"xy_Key %d %d %s Focus WindowID %i",&pointer_x,&pointer_y,keyname,&WindowID) ||
627 2==sscanf(c,"Key %s Focus %s",keyname,windowname) ||
628 4==sscanf(c,"Button %d %d %d Focus %s",&pointer_button,&pointer_x,&pointer_y,windowname) ||
629 4==sscanf(c,"xy_Key %d %d %s Focus %s",&pointer_x,&pointer_y,keyname,windowname))
631 debugprintf("Focus\n");
632 /* focussed ? */
633 if(*windowname) {
634 WindowID=find_window_focused(root,windowname);
635 if(!WindowID) {
636 debugprintf("target window '%s' doesn't have focus\n",windowname);
637 continue;
639 debugprintf("focused: %s\n",windowname);
640 } else {
641 Window cur;
642 int tmp;
644 XGetInputFocus(dpy, &cur, &tmp);
645 if(WindowID != cur) {
646 debugprintf("target window '0x%x' doesn't have focus\n",WindowID);
647 continue;
649 debugprintf("focused: 0x%x\n",WindowID);
651 } else if(2==sscanf(c,"Key %s WindowID %i",keyname,&WindowID) ||
652 4==sscanf(c,"Button %d %d %d WindowID %i",&pointer_button,&pointer_x,&pointer_y,&WindowID) ||
653 4==sscanf(c,"xy_Key %d %d %s WindowID %i",&pointer_x,&pointer_y,keyname,&WindowID)) {
654 debugprintf("WindowID: 0x%x\n",WindowID);
655 /* WindowID passed */
656 } else if(2==sscanf(c,"Key %s %s",keyname,windowname) ||
657 4==sscanf(c,"Button %d %d %d %s",&pointer_button,&pointer_x,&pointer_y,windowname) ||
658 4==sscanf(c,"xy_Key %d %d %s %s\n",&pointer_x,&pointer_y,keyname,windowname)) {
659 debugprintf("name: %s\n",windowname);
660 WindowID=find_window(root,windowname);
661 if(WindowID == 0)
663 debugprintf("target window '%s' not found\n", windowname);
664 continue;
668 switch(c[0])
670 case 'K': // Key
671 debugprintf("keyname: %s \t WindowID: 0x%x\n",keyname,WindowID);
672 debugprintf("%s\n",c);
673 sendkey(keyname,1,1,(Window)WindowID,0);
674 break;
676 case 'B': // Button
677 case 'x': // xy_Key
678 subw=find_sub_window(root,windowname,&pointer_x,&pointer_y);
679 if(subw) {
680 if (WindowID==subw) subw=0;
681 debugprintf("%s\n",c);
682 switch(c[0])
684 case 'B':
685 sendbutton(pointer_button,pointer_x,pointer_y,WindowID,subw);
686 break;
687 case 'x':
688 sendkey(keyname,pointer_x,pointer_y,WindowID,subw);
689 break;
692 break;
696 free(ir);
697 if(ret==-1) break;
699 lirc_freeconfig(config);
702 lirc_deinit();
704 exit(0);