1 /* $Id: lircmd.c,v 5.19 2006/10/09 07:22:14 lirc Exp $ */
3 /****************************************************************************
4 ** lircmd.c ****************************************************************
5 ****************************************************************************
7 * lircmd - LIRC Mouse Daemon
9 * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
11 * Wheel support based on lirc-imps2 by
12 * Ryan Gammon <rggammon@engmail.uwaterloo.ca>
30 #include <sys/types.h>
32 #include <sys/socket.h>
35 #define CLICK_DELAY 50000 /* usecs */
36 #define PACKET_SIZE 256
37 #define WHITE_SPACE " \t"
38 #define ALL ((char *) (-1))
41 #define BUTTONS 3 /* 3 buttons supported */
43 /* buttons chosen to match MouseSystem protocol*/
52 inline int map_buttons(int b
)
65 enum directive
{move_n
,move_ne
,move_e
,move_se
,
66 move_s
,move_sw
,move_w
,move_nw
,
68 button1_down
,button1_up
,button1_toggle
,button1_click
,
69 button2_down
,button2_up
,button2_toggle
,button2_click
,
70 button3_down
,button3_up
,button3_toggle
,button3_click
,
71 mouse_activate
,mouse_toggle_activate
,ignore
78 int x
,y
,z
,down
,up
,toggle
;
81 struct config_mouse config_table
[]=
83 {"MOVE_N" ,move_n
, 0, 1, 0, 0, 0, 0},
84 {"MOVE_NE" ,move_ne
, 1, 1, 0, 0, 0, 0},
85 {"MOVE_E" ,move_e
, 1, 0, 0, 0, 0, 0},
86 {"MOVE_SE" ,move_se
, 1,-1, 0, 0, 0, 0},
87 {"MOVE_S" ,move_s
, 0,-1, 0, 0, 0, 0},
88 {"MOVE_SW" ,move_sw
,-1,-1, 0, 0, 0, 0},
89 {"MOVE_W" ,move_w
,-1, 0, 0, 0, 0, 0},
90 {"MOVE_NW" ,move_nw
,-1, 1, 0, 0, 0, 0},
91 {"MOVE_IN" ,move_in
, 0, 0,-1, 0, 0, 0},
92 {"MOVE_OUT" ,move_out
, 0, 0, 1, 0, 0, 0},
93 {"BUTTON1_DOWN" ,button1_down
, 0, 0, 0,BUTTON1
, 0, 0},
94 {"BUTTON1_UP" ,button1_up
, 0, 0, 0, 0,BUTTON1
, 0},
95 {"BUTTON1_TOGGLE" ,button1_toggle
, 0, 0, 0,BUTTON1
,BUTTON1
, 1},
96 {"BUTTON1_CLICK" ,button1_click
, 0, 0, 0,BUTTON1
,BUTTON1
, 0},
97 {"BUTTON2_DOWN" ,button2_down
, 0, 0, 0,BUTTON2
, 0, 0},
98 {"BUTTON2_UP" ,button2_up
, 0, 0, 0, 0,BUTTON2
, 0},
99 {"BUTTON2_TOGGLE" ,button2_toggle
, 0, 0, 0,BUTTON2
,BUTTON2
, 1},
100 {"BUTTON2_CLICK" ,button2_click
, 0, 0, 0,BUTTON2
,BUTTON2
, 0},
101 {"BUTTON3_DOWN" ,button3_down
, 0, 0, 0,BUTTON3
, 0, 0},
102 {"BUTTON3_UP" ,button3_up
, 0, 0, 0, 0,BUTTON3
, 0},
103 {"BUTTON3_TOGGLE" ,button3_toggle
, 0, 0, 0,BUTTON3
,BUTTON3
, 1},
104 {"BUTTON3_CLICK" ,button3_click
, 0, 0, 0,BUTTON3
,BUTTON3
, 0},
105 {"IGNORE" ,ignore
, 0, 0, 0, 0, 0, 0},
106 {NULL
,ignore
, 0, 0, 0, 0, 0, 0}
109 enum protocol
{mouse_systems
,imps_2
,im_serial
};
113 struct trans_mouse
*tm_next
;
116 enum directive tm_directive
;
119 enum state_button
{ button_up
, button_down
};
120 enum state_axis
{ axis_none
, axis_up
, axis_down
};
125 int always_active
,toggle_active
,active
;
126 int acc_start
,acc_max
,acc_fak
; /* defaults, acc_fak == acc_factor */
127 enum state_button buttons
[BUTTONS
];
130 struct state_mouse new_ms
,ms
=
135 {button_up
,button_up
,button_up
}
138 char *progname
="lircmd";
139 static const char *syslogident
= "lircmd-" VERSION
;
140 char *configfile
=LIRCMDCFGFILE
;
146 struct trans_mouse
*read_config(FILE *fd
);
148 void freetm(struct trans_mouse
*tm_all
)
150 struct trans_mouse
*tm
;
154 if(tm_all
->tm_remote
!=ALL
&& tm_all
->tm_remote
!=NULL
)
155 free(tm_all
->tm_remote
);
156 if(tm_all
->tm_button
!=ALL
&& tm_all
->tm_button
!=NULL
)
157 free(tm_all
->tm_button
);
164 void sigterm(int sig
)
166 /* not safe in a signal handler *//*freetm(tm_first);*/
185 struct trans_mouse
*tm_list
;
187 fd
=fopen(configfile
,"r");
190 syslog(LOG_WARNING
,"could not open config file: %m");
193 tm_list
=read_config(fd
);
195 if(tm_list
==(void *) -1)
197 syslog(LOG_WARNING
,"reading of config file failed");
212 fprintf(stderr
,"%s: daemon() failed\n",progname
);
218 #endif /* DAEMONIZE */
220 void msend(int dx
,int dy
,int dz
,int rep
,int buttp
,int buttr
)
222 static int buttons
=0;
226 if(rep
>=ms
.acc_start
)
228 if(rep
*ms
.acc_fak
>=ms
.acc_max
)
244 buffer
[0]=~(buttons
|0x78);
248 buffer
[3]=buffer
[4]=0;
253 write(lircm
,buffer
,5);
257 buffer
[0] = ((buttons
&BUTTON1
) ? 0x01:0x00)
258 |((buttons
&BUTTON3
) ? 0x02:0x00)
259 |((buttons
&BUTTON2
) ? 0x04:0x00)
263 buffer
[1]=dx
+(dx
>=0 ? 0:256);
264 buffer
[2]=dy
+(dy
>=0 ? 0:256);
270 write(lircm
,buffer
,4);
275 buffer
[0] = ((buttons
&BUTTON1
) ? 0x20:0x00)
276 |((buttons
&BUTTON3
) ? 0x10:0x00)
280 buffer
[1]=dx
& ~0xC0;
281 buffer
[2]=dy
& ~0xC0;
282 buffer
[3] = ((dz
< 0) ? 0x0f:0x00)
283 |((dz
> 0) ? 0x01:0x00)
284 |((buttons
&BUTTON2
) ? 0x10:0x00);
289 write(lircm
,buffer
,4);
295 void mouse_move(int dx
,int dy
,int dz
,int rep
)
297 msend(dx
,dy
,dz
,rep
,0,0);
300 void mouse_button(int down
,int up
,int rep
)
304 msend(0,0,0,rep
,down
,up
);
305 if(down
&BUTTON1
) ms
.buttons
[map_buttons(BUTTON1
)]=button_down
;
306 if(down
&BUTTON2
) ms
.buttons
[map_buttons(BUTTON2
)]=button_down
;
307 if(down
&BUTTON3
) ms
.buttons
[map_buttons(BUTTON3
)]=button_down
;
308 if(up
&BUTTON1
) ms
.buttons
[map_buttons(BUTTON1
)]=button_up
;
309 if(up
&BUTTON2
) ms
.buttons
[map_buttons(BUTTON2
)]=button_up
;
310 if(up
&BUTTON3
) ms
.buttons
[map_buttons(BUTTON3
)]=button_up
;
315 You don't understand this funktion?
316 Never mind, I don't understand it, too.
319 void mouse_circle(int r
,int dirx
,int diry
)
321 int i
,d
,incX
,incY
,x
,y
;
324 1, 0,-1,-1,-1, 0, 1, 1
341 mouse_move(dirx
*dd
[i
],
342 diry
*dd
[(i
+8-6)%8],0,0);
346 mouse_move(dirx
*dd
[(i
+8-1)%8],
347 diry
*dd
[(i
+8-7)%8],0,0);
360 mouse_circle(CIRCLE
,1,1);
366 mouse_button(0,BUTTON1
|BUTTON2
|BUTTON3
,0);
368 mouse_circle(CIRCLE
,-1,1);
372 void mouse_conv(int rep
,char *button
,char *remote
)
374 struct trans_mouse
*tm
;
380 if(tm
->tm_remote
!=ALL
)
382 if(strcasecmp(remote
,tm
->tm_remote
)!=0)
388 if(tm
->tm_button
!=ALL
)
390 if(strcasecmp(button
,tm
->tm_button
)!=0)
396 if(tm
->tm_directive
==mouse_activate
)
398 if(ms
.active
==0 && ms
.always_active
==0)
403 else if(tm
->tm_directive
==mouse_toggle_activate
&& rep
==0)
405 if(ms
.always_active
==0)
419 if(tm
->tm_directive
!=ignore
&&
420 (ms
.active
|| ms
.always_active
))
423 for(i
=0;config_table
[i
].string
!=NULL
;i
++)
425 if(tm
->tm_directive
==config_table
[i
].d
)
427 int x
,y
,z
,up
,down
,toggle
;
432 down
=config_table
[i
].down
;
433 up
=config_table
[i
].up
;
434 toggle
=config_table
[i
].toggle
;
438 mouse_move(x
,y
,z
,rep
);
448 if(ms
.buttons
[map_buttons(up
)]==button_up
)
449 mouse_button(down
,0,rep
);
451 mouse_button(0,up
,rep
);
455 if(down
&& up
) /* click */
457 mouse_button(down
,0,rep
);
461 mouse_button(0,up
,rep
);
465 mouse_button(down
,up
,rep
);
479 ms
.always_active
==0 &&
487 struct trans_mouse
*read_config(FILE *fd
)
489 char buffer
[PACKET_SIZE
];
490 char *directives
,*remote
,*button
;
494 struct trans_mouse
*tm_new
,*tm_list
,*tm_last
=NULL
;
498 new_ms
.always_active
=1;
499 new_ms
.toggle_active
=0;
501 while(fgets(buffer
,PACKET_SIZE
,fd
)!=NULL
)
505 if(len
==PACKET_SIZE
-1 && buffer
[len
-1]!='\n')
507 syslog(LOG_ERR
,"line %d too long in config file",
513 if(buffer
[len
]=='\n') buffer
[len
]=0;
515 /* ignore comments */
516 if(buffer
[0]=='#') continue;
518 directives
=strtok(buffer
,WHITE_SPACE
);
519 /* ignore empty lines */
520 if(directives
==NULL
) continue;
522 if(strcasecmp("PROTOCOL",directives
)==0)
526 name
=strtok(NULL
,WHITE_SPACE
);
529 if(strcasecmp("MouseSystems",name
)==0)
531 new_ms
.protocol
=mouse_systems
;
533 else if(strcasecmp("IMPS/2",name
)==0)
535 new_ms
.protocol
=imps_2
;
537 else if(strcasecmp("IntelliMouse",name
)==0)
539 new_ms
.protocol
=im_serial
;
544 "unknown protocol %s",name
);
547 if(name
==NULL
|| strtok(NULL
,WHITE_SPACE
)!=NULL
)
550 "invalid line %d in config file "
557 if(strcasecmp("ACCELERATOR",directives
)==0)
561 number
=strtok(NULL
,WHITE_SPACE
);
563 new_ms
.acc_start
=atoi(number
);
564 number
=strtok(NULL
,WHITE_SPACE
);
566 new_ms
.acc_max
=atoi(number
);
567 number
=strtok(NULL
,WHITE_SPACE
);
569 new_ms
.acc_fak
=atoi(number
);
570 if(strtok(NULL
,WHITE_SPACE
)!=NULL
)
573 "invalid line %d in config file "
575 new_ms
.acc_start
=ms
.acc_start
;
576 new_ms
.acc_max
=ms
.acc_max
;
577 new_ms
.acc_fak
=ms
.acc_fak
;
583 remote
=strtok(NULL
,WHITE_SPACE
);
584 button
=strtok(NULL
,WHITE_SPACE
);
585 if(remote
==NULL
|| button
==NULL
||
586 strtok(NULL
,WHITE_SPACE
)!=NULL
)
589 "invalid line %d in config file ignored",
594 if(strcasecmp("ACTIVATE",directives
)==0)
597 new_ms
.always_active
=0;
599 else if(strcasecmp("TOGGLE_ACTIVATE",directives
)==0)
601 d
=mouse_toggle_activate
;
602 new_ms
.always_active
=0;
608 d
=mouse_activate
; /* make compiler happy */
609 for(i
=0;config_table
[i
].string
!=NULL
;i
++)
611 if(strcasecmp(config_table
[i
].string
,
618 if(config_table
[i
].string
==NULL
)
621 "unknown directive \"%s\" ignored",
627 if(strcmp("*",remote
)==0) remote
=ALL
;
628 else remote
=strdup(remote
);
629 if(strcmp("*",button
)==0) button
=ALL
;
630 else button
=strdup(button
);
632 tm_new
=malloc(sizeof(struct trans_mouse
));
633 if(remote
==NULL
|| button
==NULL
|| tm_new
==NULL
)
635 syslog(LOG_ERR
,"out of memory");
636 if(remote
!=NULL
) free(remote
);
637 if(button
!=NULL
) free(button
);
638 if(tm_new
!=NULL
) free(tm_new
);
642 tm_new
->tm_next
=NULL
;
643 tm_new
->tm_remote
=remote
;
644 tm_new
->tm_button
=button
;
645 tm_new
->tm_directive
=d
;
653 tm_last
->tm_next
=tm_new
;
663 char buffer
[PACKET_SIZE
+1];
665 char button
[PACKET_SIZE
+1];
666 char remote
[PACKET_SIZE
+1];
672 sigaddset(&block
,SIGHUP
);
681 if(strchr(buffer
,'\n')==NULL
)
684 sigprocmask(SIG_UNBLOCK
,&block
,NULL
);
685 len
=read(lircd
,buffer
+end_len
,PACKET_SIZE
-end_len
);
686 sigprocmask(SIG_BLOCK
,&block
,NULL
);
689 if(len
==-1 && errno
==EINTR
) continue;
693 buffer
[len
+end_len
]=0;
694 ret
=sscanf(buffer
,"%*llx %x %s %s\n",&rep
,button
,remote
);
695 end
=strchr(buffer
,'\n');
703 memmove(buffer
,end
,end_len
+1);
706 mouse_conv(rep
,button
,remote
);
712 int main(int argc
,char **argv
)
715 struct sigaction act
;
716 struct sockaddr_un addr
;
723 static struct option long_options
[] =
725 {"help",no_argument
,NULL
,'h'},
726 {"version",no_argument
,NULL
,'v'},
727 {"nodaemon",no_argument
,NULL
,'n'},
730 c
= getopt_long(argc
,argv
,"hvn",long_options
,NULL
);
736 printf("Usage: %s [options] [config-file]\n",progname
);
737 printf("\t -h --help\t\tdisplay this message\n");
738 printf("\t -v --version\t\tdisplay version\n");
739 printf("\t -n --nodaemon\t\tdon't fork to background\n");
740 return(EXIT_SUCCESS
);
742 printf("%s %s\n",progname
,VERSION
);
743 return(EXIT_SUCCESS
);
748 printf("Usage: %s [options] [config-file]\n",progname
);
749 return(EXIT_FAILURE
);
754 configfile
=argv
[optind
];
756 else if(optind
!=argc
)
758 fprintf(stderr
,"%s: invalid argument count\n",progname
);
759 return(EXIT_FAILURE
);
762 /* connect to lircd */
764 addr
.sun_family
=AF_UNIX
;
765 strcpy(addr
.sun_path
,LIRCD
);
766 lircd
=socket(AF_UNIX
,SOCK_STREAM
,0);
769 fprintf(stderr
,"%s: could not open socket\n",progname
);
773 if(connect(lircd
,(struct sockaddr
*)&addr
,sizeof(addr
))==-1)
775 fprintf(stderr
,"%s: could not connect to socket\n",progname
);
782 if(mkfifo(LIRCM
,0644)==-1)
786 fprintf(stderr
,"%s: could not create fifo\n",progname
);
792 lircm
=open(LIRCM
,O_RDWR
|O_NONBLOCK
);
795 fprintf(stderr
,"%s: could not open fifo\n",progname
);
800 /* read config file */
802 fd
=fopen(configfile
,"r");
805 fprintf(stderr
,"%s: could not open config file\n",progname
);
809 tm_first
=read_config(fd
);
811 if(tm_first
==(void *) -1)
813 fprintf(stderr
,"%s: reading of config file failed\n",progname
);
821 if(!nodaemon
) daemonize();
823 openlog(syslogident
,LOG_CONS
,LOG_DAEMON
);
825 signal(SIGPIPE
,SIG_IGN
);
827 act
.sa_handler
=sigterm
;
828 sigfillset(&act
.sa_mask
);
829 act
.sa_flags
=SA_RESTART
; /* don't fiddle with EINTR */
830 sigaction(SIGTERM
,&act
,NULL
);
831 sigaction(SIGINT
,&act
,NULL
);
833 /* block SIGHUP first */
835 sigaddset(&block
,SIGHUP
);
836 sigprocmask(SIG_BLOCK
,&block
,NULL
);
838 act
.sa_handler
=sighup
;
839 sigemptyset(&act
.sa_mask
);
840 act
.sa_flags
=0; /* need EINTR in loop() */
841 sigaction(SIGHUP
,&act
,NULL
);
846 return(EXIT_SUCCESS
);