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)
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 :*)
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?)
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
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.
51 * -fixed a stupid string bug in key sending.
52 * -updated irxevent.README to be up to date with the .lircrc format.
55 * -changed DEBUG functions to actually produce some output :)
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
83 #include <sys/socket.h>
86 #include <sys/types.h>
89 #include <X11/Xutil.h>
93 #include "lirc_client.h"
95 static int bDaemon
= 0;
96 static int bInError
= 0;
99 static void debugprintf(char *format_str
, ...)
102 va_start(ap
,format_str
);
104 vfprintf(stderr
,format_str
,ap
);
108 static void debugprintf(char *format_str
, ...)
113 struct keymodlist_t
{
117 static struct keymodlist_t keymodlist
[]=
119 {"SHIFT", ShiftMask
},
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
},
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";
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 */
148 struct timezone tz
; /* is not used since ages */
149 gettimeofday(&tv
,&tz
);
150 tint
=(int)tv
.tv_sec
*1000;
152 tint
=tint
+tv
.tv_usec
/1000;
156 static Window
find_window(Window top
,char *name
)
160 Window
*children
,foo
;
161 int revert_to_return
;
163 if (!strcmp(active_window_name
,name
)){
164 XGetInputFocus(dpy
, &foo
, &revert_to_return
);
166 } else if (!strcmp(root_window_name
,name
)){
169 /* First the base case */
170 if (XFetchName(dpy
,top
,&wname
)){
171 if (!strncmp(wname
,name
,strlen(name
))) {
173 debugprintf("found it by wname 0x%x \n",top
);
174 return(top
); /* found it! */
179 if(XGetIconName(dpy
,top
,&iname
)){
180 if (!strncmp(iname
,name
,strlen(name
))) {
182 debugprintf("found it by iname 0x%x \n",top
);
183 return(top
); /* found it! */
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 */
208 top
= find_window(children
[nc
-1],name
);
209 if(top
) break; /* we found it somewhere */
211 if(children
!=NULL
) XFree(children
);
215 static Window
find_sub_sub_window(Window top
,int *x
, int *y
)
218 Window
*children
,foo
,target
=0;
220 rel_x
,rel_y
,width
,height
,border
,depth
,
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 */
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
;
243 target
=find_sub_sub_window(target
,&new_x
,&new_y
);
248 if(children
!=NULL
) XFree(children
);
259 static Window
find_sub_window(Window top
,char *name
,int *x
, int *y
)
262 Window
*children
,foo
,target
=0;
264 rel_x
,rel_y
,width
,height
,border
,depth
,
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 */
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
;
287 target
=find_sub_sub_window(target
,&new_x
,&new_y
);
292 if(children
!=NULL
) XFree(children
);
302 static Window
find_window_focused(Window top
,char *name
)
305 Window w
, cur
, *children
, foo
;
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
);
320 else if(XQueryTree(dpy
,w
,&foo
,&foo
,&children
,&n
) && children
!=NULL
) {
321 /* check all the sub windows of named window */
323 if(children
[n
-1] == cur
) {
335 static void make_button(int button
,int x
,int y
,XButtonEvent
*xev
)
337 xev
->type
= ButtonPress
;
341 xev
->time
=fake_timestamp();
343 xev
->x_root
=1; xev
->y_root
=1;
346 xev
->same_screen
=True
;
351 static void make_key(char *keyname
,int x
, int y
,XKeyEvent
*xev
)
353 char *part
, *part2
, *sep_part
;
354 struct keymodlist_t
*kmlptr
;
360 xev
->type
= KeyPress
;
363 xev
->subwindow
= None
;
364 xev
->time
=fake_timestamp();
366 xev
->x_root
=1; xev
->y_root
=1;
367 xev
->same_screen
= True
;
371 while ((part
=strsep(&keyname
, key_delimiter
)))
373 while ((part
=strtok(keyname
, key_delimiter
)) && ((keyname
=NULL
)==NULL
))
376 part2
=strncpy(part2
,part
,128);
377 // debugprintf("- %s \n",part);
381 // debugprintf("-- %s %s \n", kmlptr->name, part);
382 if (!strcasecmp(kmlptr
->name
, part
))
383 xev
->state
|=kmlptr
->mask
;
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
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
);
416 debugprintf("state 0x%x, keycode 0x%x\n",xev
->state
, xev
->keycode
);
421 static void sendfocus(Window w
,int in_out
)
423 XFocusChangeEvent focev
;
428 focev
.mode
=NotifyNormal
;
429 focev
.detail
=NotifyPointer
;
430 XSendEvent(dpy
,w
,True
,FocusChangeMask
,(XEvent
*)&focev
);
436 static void sendpointer_enter_or_leave(Window w
,int in_out
)
438 XCrossingEvent crossev
;
443 crossev
.subwindow
=None
;
444 crossev
.time
=fake_timestamp();
449 crossev
.mode
=NotifyNormal
;
450 crossev
.detail
=NotifyNonlinear
;
451 crossev
.same_screen
=True
;
454 XSendEvent(dpy
,w
,True
,EnterWindowMask
|LeaveWindowMask
,(XEvent
*)&crossev
);
459 static void sendkey(char *keyname
,int x
,int y
,Window w
,Window s
)
461 make_key(keyname
,x
,y
,(XKeyEvent
*)&xev
);
463 xev
.xkey
.subwindow
=s
;
465 if (s
) sendfocus(s
,FocusIn
);
467 XSendEvent(dpy
,w
,True
,KeyPressMask
,&xev
);
468 xev
.type
= KeyRelease
;
470 xev
.xkey
.time
= fake_timestamp();
471 if (s
) sendfocus(s
,FocusOut
);
472 XSendEvent(dpy
,w
,True
,KeyReleaseMask
,&xev
);
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
);
487 xev
.type
= ButtonRelease
;
488 xev
.xkey
.state
|=0x100;
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
);
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);
505 if(!bDaemon
) fprintf(stderr
, "X11 error: %s\n", buff
);
516 buffer
=malloc(strlen(s
));
519 fprintf(stderr
,"%s: out of memory\n",progname
);
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
);
544 static struct option long_options
[] =
546 {"daemon", no_argument
, NULL
, 'd'},
547 {"help", no_argument
, NULL
, 'h'},
548 {"version", no_argument
, NULL
, 'V'},
552 int main(int argc
, char *argv
[])
555 int pointer_button
,pointer_x
,pointer_y
;
557 struct lirc_config
*config
;
558 char *config_file
=NULL
;
562 while ((c
= getopt_long(argc
, argv
, "dhV", long_options
, NULL
)) != EOF
) {
565 bDaemon
= 1; continue;
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
);
573 printf("%s %s\n", progname
, VERSION
);
574 return(EXIT_SUCCESS
);
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
);
592 fprintf(stderr
,"Can't open DISPLAY.\n");
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)
609 if(daemon(1, 0) < 0) {
610 perror("Failed to run as daemon");
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
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");
634 WindowID
=find_window_focused(root
,windowname
);
636 debugprintf("target window '%s' doesn't have focus\n",windowname
);
639 debugprintf("focused: %s\n",windowname
);
644 XGetInputFocus(dpy
, &cur
, &tmp
);
645 if(WindowID
!= cur
) {
646 debugprintf("target window '0x%x' doesn't have focus\n",WindowID
);
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
);
663 debugprintf("target window '%s' not found\n", windowname
);
671 debugprintf("keyname: %s \t WindowID: 0x%x\n",keyname
,WindowID
);
672 debugprintf("%s\n",c
);
673 sendkey(keyname
,1,1,(Window
)WindowID
,0);
678 subw
=find_sub_window(root
,windowname
,&pointer_x
,&pointer_y
);
680 if (WindowID
==subw
) subw
=0;
681 debugprintf("%s\n",c
);
685 sendbutton(pointer_button
,pointer_x
,pointer_y
,WindowID
,subw
);
688 sendkey(keyname
,pointer_x
,pointer_y
,WindowID
,subw
);
699 lirc_freeconfig(config
);