Little fix.
[irreco.git] / lirc-0.8.4a / daemons / lircmd.c
blob5b606542a1d981129ab7b6a7eaba2a0d11d344c9
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
8 *
9 * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
11 * Wheel support based on lirc-imps2 by
12 * Ryan Gammon <rggammon@engmail.uwaterloo.ca>
14 */
16 #ifdef HAVE_CONFIG_H
17 # include <config.h>
18 #endif
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <signal.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <syslog.h>
27 #include <errno.h>
28 #include <getopt.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
35 #define CLICK_DELAY 50000 /* usecs */
36 #define PACKET_SIZE 256
37 #define WHITE_SPACE " \t"
38 #define ALL ((char *) (-1))
39 #define CIRCLE 10
41 #define BUTTONS 3 /* 3 buttons supported */
43 /* buttons chosen to match MouseSystem protocol*/
44 #define BUTTON1 0x04
45 #define BUTTON2 0x02
46 #define BUTTON3 0x01
48 #define MAP_BUTTON1 0
49 #define MAP_BUTTON2 1
50 #define MAP_BUTTON3 2
52 inline int map_buttons(int b)
54 switch(b)
56 case BUTTON1:
57 return(MAP_BUTTON1);
58 case BUTTON2:
59 return(MAP_BUTTON2);
60 default:
61 return(MAP_BUTTON3);
65 enum directive {move_n,move_ne,move_e,move_se,
66 move_s,move_sw,move_w,move_nw,
67 move_in,move_out,
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
74 struct config_mouse
76 char *string;
77 enum directive d;
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};
111 struct trans_mouse
113 struct trans_mouse *tm_next;
114 char *tm_remote;
115 char *tm_button;
116 enum directive tm_directive;
117 } *tm_first=NULL;
119 enum state_button { button_up, button_down };
120 enum state_axis { axis_none, axis_up, axis_down };
122 struct state_mouse
124 int protocol;
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=
132 mouse_systems,
133 1,0,0,
134 2,20,2,
135 {button_up,button_up,button_up}
138 char *progname="lircmd";
139 static const char *syslogident = "lircmd-" VERSION;
140 char *configfile=LIRCMDCFGFILE;
142 int lircd,lircm;
144 sig_atomic_t hup=0;
146 struct trans_mouse *read_config(FILE *fd);
148 void freetm(struct trans_mouse *tm_all)
150 struct trans_mouse *tm;
152 while(tm_all!=NULL)
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);
158 tm=tm_all;
159 tm_all=tm->tm_next;
160 free(tm);
164 void sigterm(int sig)
166 /* not safe in a signal handler *//*freetm(tm_first);*/
168 shutdown(lircd,2);
169 close(lircd);
170 shutdown(lircm,2);
171 close(lircm);
173 signal(sig,SIG_DFL);
174 raise(sig);
177 void sighup(int sig)
179 hup=1;
182 void dohup(void)
184 FILE *fd;
185 struct trans_mouse *tm_list;
187 fd=fopen(configfile,"r");
188 if(fd==NULL)
190 syslog(LOG_WARNING,"could not open config file: %m");
191 return;
193 tm_list=read_config(fd);
194 fclose(fd);
195 if(tm_list==(void *) -1)
197 syslog(LOG_WARNING,"reading of config file failed");
199 else
201 freetm(tm_first);
202 tm_first=tm_list;
203 ms=new_ms;
207 #ifdef DAEMONIZE
208 void daemonize(void)
210 if(daemon(0,0)==-1)
212 fprintf(stderr,"%s: daemon() failed\n",progname);
213 perror(progname);
214 exit(EXIT_FAILURE);
216 umask(0);
218 #endif /* DAEMONIZE */
220 void msend(int dx,int dy,int dz,int rep,int buttp,int buttr)
222 static int buttons=0;
223 int f=1;
224 char buffer[5];
226 if(rep>=ms.acc_start)
228 if(rep*ms.acc_fak>=ms.acc_max)
230 f=ms.acc_max;
232 else
234 f=rep*ms.acc_fak;
238 buttons|=buttp;
239 buttons&=~buttr;
241 switch(ms.protocol)
243 case mouse_systems:
244 buffer[0]=~(buttons|0x78);
246 buffer[1]=dx;
247 buffer[2]=dy;
248 buffer[3]=buffer[4]=0;
250 while(f>0)
252 f--;
253 write(lircm,buffer,5);
255 break;
256 case imps_2:
257 buffer[0] = ((buttons&BUTTON1) ? 0x01:0x00)
258 |((buttons&BUTTON3) ? 0x02:0x00)
259 |((buttons&BUTTON2) ? 0x04:0x00)
260 | 0x08
261 |(dx<0 ? 0x10:0x00)
262 |(dy<0 ? 0x20:0x00);
263 buffer[1]=dx+(dx>=0 ? 0:256);
264 buffer[2]=dy+(dy>=0 ? 0:256);
265 buffer[3]=dz;
267 while(f>0)
269 f--;
270 write(lircm,buffer,4);
272 break;
273 case im_serial:
274 dy = -dy;
275 buffer[0] = ((buttons&BUTTON1) ? 0x20:0x00)
276 |((buttons&BUTTON3) ? 0x10:0x00)
277 | 0x40
278 |((dx & 0xC0) >> 6)
279 |((dy & 0xC0) >> 4);
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);
286 while(f>0)
288 f--;
289 write(lircm,buffer,4);
291 break;
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)
302 if(rep==0)
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;
322 int dd[8]=
324 1, 0,-1,-1,-1, 0, 1, 1
327 for(i=0;i<8;i++)
329 d=1-r;
330 incX=0;
331 incY=2*r;
332 x=0;
333 y=r;
334 while(x<y)
336 if(d>=0)
338 y--;
339 incY-=2;
340 d-=incY;
341 mouse_move(dirx*dd[i],
342 diry*dd[(i+8-6)%8],0,0);
344 else
346 mouse_move(dirx*dd[(i+8-1)%8],
347 diry*dd[(i+8-7)%8],0,0);
349 x++;
350 incX+=2;
351 d+=incX+1;
352 usleep(1);
357 void activate()
359 ms.active=1;
360 mouse_circle(CIRCLE,1,1);
363 void deactivate()
365 /* all buttons up */
366 mouse_button(0,BUTTON1|BUTTON2|BUTTON3,0);
367 ms.active=0;
368 mouse_circle(CIRCLE,-1,1);
372 void mouse_conv(int rep,char *button,char *remote)
374 struct trans_mouse *tm;
375 int found=0;
377 tm=tm_first;
378 while(tm!=NULL)
380 if(tm->tm_remote!=ALL)
382 if(strcasecmp(remote,tm->tm_remote)!=0)
384 tm=tm->tm_next;
385 continue;
388 if(tm->tm_button!=ALL)
390 if(strcasecmp(button,tm->tm_button)!=0)
392 tm=tm->tm_next;
393 continue;
396 if(tm->tm_directive==mouse_activate)
398 if(ms.active==0 && ms.always_active==0)
400 activate();
403 else if(tm->tm_directive==mouse_toggle_activate && rep==0)
405 if(ms.always_active==0)
407 if(ms.active==0)
409 activate();
410 ms.toggle_active=1;
412 else
414 deactivate();
419 if(tm->tm_directive!=ignore &&
420 (ms.active || ms.always_active))
422 int i;
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;
429 x=config_table[i].x;
430 y=config_table[i].y;
431 z=config_table[i].z;
432 down=config_table[i].down;
433 up=config_table[i].up;
434 toggle=config_table[i].toggle;
436 if(x || y || z)
438 mouse_move(x,y,z,rep);
440 if(toggle)
443 assert(down==up);
444 assert(up==BUTTON1
445 || up==BUTTON2
446 || up==BUTTON3);
448 if(ms.buttons[map_buttons(up)]==button_up)
449 mouse_button(down,0,rep);
450 else
451 mouse_button(0,up,rep);
453 else
455 if(down && up) /* click */
457 mouse_button(down,0,rep);
458 #ifdef CLICK_DELAY
459 usleep(CLICK_DELAY);
460 #endif
461 mouse_button(0,up,rep);
463 else if(down || up)
465 mouse_button(down,up,rep);
468 break;
473 found=1;
474 tm=tm->tm_next;
476 if(found==0)
478 if(ms.active==1 &&
479 ms.always_active==0 &&
480 ms.toggle_active==0)
482 deactivate();
487 struct trans_mouse *read_config(FILE *fd)
489 char buffer[PACKET_SIZE];
490 char *directives,*remote,*button;
491 enum directive d;
492 int len;
493 int line;
494 struct trans_mouse *tm_new,*tm_list,*tm_last=NULL;
496 tm_list=NULL;
497 new_ms=ms;
498 new_ms.always_active=1;
499 new_ms.toggle_active=0;
500 line=0;
501 while(fgets(buffer,PACKET_SIZE,fd)!=NULL)
503 line++;
504 len=strlen(buffer);
505 if(len==PACKET_SIZE-1 && buffer[len-1]!='\n')
507 syslog(LOG_ERR,"line %d too long in config file",
508 line);
509 freetm(tm_list);
510 return((void *) -1);
512 len--;
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)
524 char *name;
526 name=strtok(NULL,WHITE_SPACE);
527 if(name!=NULL)
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;
541 else
543 syslog(LOG_WARNING,
544 "unknown protocol %s",name);
547 if(name==NULL || strtok(NULL,WHITE_SPACE)!=NULL)
549 syslog(LOG_WARNING,
550 "invalid line %d in config file "
551 "ignored",line);
552 continue;
554 continue;
557 if(strcasecmp("ACCELERATOR",directives)==0)
559 char *number;
561 number=strtok(NULL,WHITE_SPACE);
562 if(number!=NULL)
563 new_ms.acc_start=atoi(number);
564 number=strtok(NULL,WHITE_SPACE);
565 if(number!=NULL)
566 new_ms.acc_max=atoi(number);
567 number=strtok(NULL,WHITE_SPACE);
568 if(number!=NULL)
569 new_ms.acc_fak=atoi(number);
570 if(strtok(NULL,WHITE_SPACE)!=NULL)
572 syslog(LOG_WARNING,
573 "invalid line %d in config file "
574 "ignored",line);
575 new_ms.acc_start=ms.acc_start;
576 new_ms.acc_max=ms.acc_max;
577 new_ms.acc_fak=ms.acc_fak;
578 continue;
580 continue;
583 remote=strtok(NULL,WHITE_SPACE);
584 button=strtok(NULL,WHITE_SPACE);
585 if(remote==NULL || button==NULL ||
586 strtok(NULL,WHITE_SPACE)!=NULL)
588 syslog(LOG_WARNING,
589 "invalid line %d in config file ignored",
590 line);
591 continue;
594 if(strcasecmp("ACTIVATE",directives)==0)
596 d=mouse_activate;
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;
604 else
606 int i;
608 d=mouse_activate; /* make compiler happy */
609 for(i=0;config_table[i].string!=NULL;i++)
611 if(strcasecmp(config_table[i].string,
612 directives)==0)
614 d=config_table[i].d;
615 break;
618 if(config_table[i].string==NULL)
620 syslog(LOG_WARNING,
621 "unknown directive \"%s\" ignored",
622 directives);
623 continue;
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);
639 free(tm_list);
640 return((void *) -1);
642 tm_new->tm_next=NULL;
643 tm_new->tm_remote=remote;
644 tm_new->tm_button=button;
645 tm_new->tm_directive=d;
646 if(tm_list==NULL)
648 tm_list=tm_new;
649 tm_last=tm_new;
651 else
653 tm_last->tm_next=tm_new;
654 tm_last=tm_new;
657 return(tm_list);
660 void loop()
662 ssize_t len=0;
663 char buffer[PACKET_SIZE+1];
664 int rep,ret;
665 char button[PACKET_SIZE+1];
666 char remote[PACKET_SIZE+1];
667 char *end;
668 int end_len=0;
669 sigset_t block;
671 sigemptyset(&block);
672 sigaddset(&block,SIGHUP);
673 buffer[0]=0;
674 while(1)
676 if(hup)
678 dohup();
679 hup=0;
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);
687 if(len<=0)
689 if(len==-1 && errno==EINTR) continue;
690 raise(SIGTERM);
693 buffer[len+end_len]=0;
694 ret=sscanf(buffer,"%*llx %x %s %s\n",&rep,button,remote);
695 end=strchr(buffer,'\n');
696 if(end==NULL)
698 end_len=0;
699 continue;
701 end++;
702 end_len=strlen(end);
703 memmove(buffer,end,end_len+1);
704 if(ret==3)
706 mouse_conv(rep,button,remote);
712 int main(int argc,char **argv)
714 FILE *fd;
715 struct sigaction act;
716 struct sockaddr_un addr;
717 sigset_t block;
718 int nodaemon=0;
720 while(1)
722 int c;
723 static struct option long_options[] =
725 {"help",no_argument,NULL,'h'},
726 {"version",no_argument,NULL,'v'},
727 {"nodaemon",no_argument,NULL,'n'},
728 {0, 0, 0, 0}
730 c = getopt_long(argc,argv,"hvn",long_options,NULL);
731 if(c==-1)
732 break;
733 switch (c)
735 case 'h':
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);
741 case 'v':
742 printf("%s %s\n",progname,VERSION);
743 return(EXIT_SUCCESS);
744 case 'n':
745 nodaemon=1;
746 break;
747 default:
748 printf("Usage: %s [options] [config-file]\n",progname);
749 return(EXIT_FAILURE);
752 if(optind==argc-1)
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);
767 if(lircd==-1)
769 fprintf(stderr,"%s: could not open socket\n",progname);
770 perror(progname);
771 exit(EXIT_FAILURE);
773 if(connect(lircd,(struct sockaddr *)&addr,sizeof(addr))==-1)
775 fprintf(stderr,"%s: could not connect to socket\n",progname);
776 perror(progname);
777 exit(EXIT_FAILURE);
780 /* open fifo */
782 if(mkfifo(LIRCM,0644)==-1)
784 if(errno!=EEXIST)
786 fprintf(stderr,"%s: could not create fifo\n",progname);
787 perror(progname);
788 exit(EXIT_FAILURE);
792 lircm=open(LIRCM,O_RDWR|O_NONBLOCK);
793 if(lircm==-1)
795 fprintf(stderr,"%s: could not open fifo\n",progname);
796 perror(progname);
797 exit(EXIT_FAILURE);
800 /* read config file */
802 fd=fopen(configfile,"r");
803 if(fd==NULL)
805 fprintf(stderr,"%s: could not open config file\n",progname);
806 perror(progname);
807 exit(EXIT_FAILURE);
809 tm_first=read_config(fd);
810 fclose(fd);
811 if(tm_first==(void *) -1)
813 fprintf(stderr,"%s: reading of config file failed\n",progname);
814 exit(EXIT_FAILURE);
816 else
818 ms=new_ms;
820 #ifdef DAEMONIZE
821 if(!nodaemon) daemonize();
822 #endif
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 */
834 sigemptyset(&block);
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);
843 loop();
845 /* never reached */
846 return(EXIT_SUCCESS);