1 #define _GNU_SOURCE
2
3 #include "config.h"
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <string.h>
9 #include <ctype.h>
10 #include <errno.h>
11 #include <math.h>
12 #include <stdarg.h>
13 #include <fcntl.h>
14 #include <pthread.h>
15 #include <sys/time.h>
16
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <netdb.h>
21
22 #include "grab-ng.h"
23
24 #include "capture.h"
25 #include "commands.h"
26 #include "writefile.h"
27 #include "channel.h"
28 #include "webcam.h"
29 #include "frequencies.h"
30 #include "sound.h"
31
32 /* ----------------------------------------------------------------------- */
33
34 /* feedback for the user */
35 void (*update_title)(char *message);
36 void (*display_message)(char *message);
37 void (*rec_status)(char *message);
38 #if TT
39 void (*vtx_message)(struct TEXTELEM *tt);
40 #endif
41 #ifdef HAVE_ZVBI
42 void (*vtx_subtitle)(struct vbi_page *pg, struct vbi_rect *rect);
43 #endif
44
45 /* for updating GUI elements / whatever */
46 void (*attr_notify)(struct ng_attribute *attr, int val);
47 void (*volume_notify)(void);
48 void (*freqtab_notify)(void);
49 void (*setfreqtab_notify)(void);
50 void (*setstation_notify)(void);
51
52 /* gets called _before_ channel switches */
53 void (*channel_switch_hook)(void);
54
55 /* capture overlay/grab/off */
56 void (*set_capture_hook)(int old, int new, int tmp_switch);
57
58 /* toggle fullscreen */
59 void (*fullscreen_hook)(void);
60 void (*exit_hook)(void);
61 void (*capture_get_hook)(void);
62 void (*capture_rel_hook)(void);
63 void (*movie_hook)(int argc, char **argv);
64
65 int debug;
66 int do_overlay;
67 char *snapbase = "snap";
68 int have_shmem;
69
70 unsigned int cur_tv_width,cur_tv_height;
71 int cur_movie,cur_attrs[256];
72
73 /* current hardware driver */
74 const struct ng_vid_driver *drv;
75 void *h_drv;
76 int f_drv;
77
78 struct ng_attribute *attrs = NULL;
79
80
81 /* ----------------------------------------------------------------------- */
82
83 static int setfreqtab_handler(char *name, int argc, char **argv);
84 static int setstation_handler(char *name, int argc, char **argv);
85 static int setchannel_handler(char *name, int argc, char **argv);
86
87 static int capture_handler(char *name, int argc, char **argv);
88 static int volume_handler(char *name, int argc, char **argv);
89 static int attr_handler(char *name, int argc, char **argv);
90 static int show_handler(char *name, int argc, char **argv);
91 static int list_handler(char *name, int argc, char **argv);
92 static int dattr_handler(char *name, int argc, char **argv);
93
94 static int snap_handler(char *name, int argc, char **argv);
95 static int webcam_handler(char *name, int argc, char **argv);
96 static int movie_handler(char *name, int argc, char **argv);
97 static int fullscreen_handler(char *name, int argc, char **argv);
98 static int msg_handler(char *name, int argc, char **argv);
99 static int showtime_handler(char *name, int argc, char **argv);
100 static int vdr_handler(char *name, int argc, char **argv);
101 #if TT
102 static int vtx_handler(char *name, int argc, char **argv);
103 #endif
104 static int exit_handler(char *name, int argc, char **argv);
105
106 static int keypad_handler(char *name, int argc, char **argv);
107
108 static struct COMMANDS {
109 char *name;
110 int min_args;
111 int (*handler)(char *name, int argc, char **argv);
112 } commands[] = {
113 { "setstation", 0, setstation_handler },
114 { "setchannel", 0, setchannel_handler },
115 { "setfreq", 1, setchannel_handler },
116 { "setfreqtab", 1, setfreqtab_handler },
117
118 { "capture", 1, capture_handler },
119
120 { "setnorm", 1, attr_handler },
121 { "setinput", 1, attr_handler },
122 { "setattr", 1, attr_handler },
123 { "color", 0, attr_handler },
124 { "hue", 0, attr_handler },
125 { "bright", 0, attr_handler },
126 { "contrast", 0, attr_handler },
127 { "show", 0, show_handler },
128 { "list", 0, list_handler },
129
130 { "volume", 0, volume_handler },
131 { "attr", 0, dattr_handler },
132
133 { "snap", 0, snap_handler },
134 { "webcam", 1, webcam_handler },
135 { "movie", 1, movie_handler },
136 { "fullscreen", 0, fullscreen_handler },
137 { "msg", 1, msg_handler },
138 #if 0
139 { "vtx", 0, vtx_handler },
140 #endif
141 { "message", 0, msg_handler },
142 { "exit", 0, exit_handler },
143 { "quit", 0, exit_handler },
144 { "bye", 0, exit_handler },
145
146 { "keypad", 1, keypad_handler },
147 { "showtime", 0, showtime_handler },
148 { "vdr", 1, vdr_handler },
149
150 { NULL, 0, NULL }
151 };
152
153 static int cur_dattr = 0;
154 static int dattr[] = {
155 ATTR_ID_VOLUME,
156 ATTR_ID_BRIGHT,
157 ATTR_ID_CONTRAST,
158 ATTR_ID_COLOR,
159 ATTR_ID_HUE
160 };
161 #define NUM_DATTR (sizeof(dattr)/sizeof(char*))
162
163 static int keypad_state = -1;
164
165 /* ----------------------------------------------------------------------- */
166
167 void add_attrs(struct ng_attribute *new)
168 {
169 struct ng_attribute *all;
170 int nold,nnew;
171
172 if (attrs)
173 for (nold = 0; attrs[nold].name != NULL; nold++)
174 ;
175 else
176 nold = 0;
177 for (nnew = 0; new[nnew].name != NULL; nnew++)
178 ;
179 all = malloc(sizeof(struct ng_attribute) * (nold + nnew + 1));
180 memset(all,0,sizeof(struct ng_attribute) * (nold + nnew + 1));
181 memcpy(all,new,sizeof(struct ng_attribute)*nnew);
182 if (attrs) {
183 memcpy(all+nnew,attrs,sizeof(struct ng_attribute)*nold);
184 free(attrs);
185 }
186 attrs = all;
187
188 #if 0
189 {
190 int i;
191 fprintf(stderr," <attr>\n");
192 for (i = 0; attrs[i].name != NULL; i++) {
193 fprintf(stderr," attr[%p]: %s \n",
194 attrs[i].handle,attrs[i].name);
195 }
196 fprintf(stderr," </attr>\n");
197 }
198 #endif
199 }
200
201 void init_overlay(void)
202 {
203 do_va_cmd(2,"setfreqtab",(-1 != chantab)
204 ? chanlist_names[chantab].str : "europe-west");
205
206 cur_capture = -1;
207 switch (defaults.capture) {
208 case CAPTURE_ON:
209 case CAPTURE_OVERLAY:
210 do_va_cmd(2,"capture","overlay");
211 break;
212 case CAPTURE_GRABDISPLAY:
213 do_va_cmd(2,"capture","grabdisplay");
214 break;
215 default:
216 do_va_cmd(2,"capture","off");
217 break;
218 }
219 }
220
221 /* ----------------------------------------------------------------------- */
222
223 int
224 do_va_cmd(int argc, ...)
225 {
226 va_list ap;
227 int i;
228 char *argv[32];
229
230 va_start(ap,argc);
231 for (i = 0; i < argc; i++)
232 argv[i] = va_arg(ap,char*);
233 argv[i] = NULL;
234 va_end (ap);
235 return do_command(argc,argv);
236 }
237
238 int
239 do_command(int argc, char **argv)
240 {
241 int i;
242
243 if (argc == 0) {
244 fprintf(stderr,"do_command: no argument\n");
245 return -1;
246 }
247 if (debug) {
248 fprintf(stderr,"cmd:");
249 for (i = 0; i < argc; i++) {
250 fprintf(stderr," \"%s\"",argv[i]);
251 }
252 fprintf(stderr,"\n");
253 }
254
255 for (i = 0; commands[i].name != NULL; i++)
256 if (0 == strcasecmp(commands[i].name,argv[0]))
257 break;
258 if (commands[i].name == NULL) {
259 fprintf(stderr,"no handler for %s\n",argv[0]);
260 return -1;
261 }
262 if (argc-1 < commands[i].min_args) {
263 fprintf(stderr,"no enough args for %s\n",argv[0]);
264 return -1;
265 } else {
266 return commands[i].handler(argv[0],argc-1,argv+1);
267 }
268 }
269
270 char**
271 split_cmdline(char *line, int *count)
272 {
273 static char cmdline[1024];
274 static char *argv[32];
275 int argc,i;
276
277 strcpy(cmdline,line);
278 for (argc=0, i=0; argc<31;) {
279 argv[argc++] = cmdline+i;
280 while (cmdline[i] != ' ' &&
281 cmdline[i] != '\t' &&
282 cmdline[i] != '\0')
283 i++;
284 if (cmdline[i] == '\0')
285 break;
286 cmdline[i++] = '\0';
287 while (cmdline[i] == ' ' ||
288 cmdline[i] == '\t')
289 i++;
290 if (cmdline[i] == '\0')
291 break;
292 }
293 argv[argc] = NULL;
294
295 *count = argc;
296 return argv;
297 }
298
299 /* ----------------------------------------------------------------------- */
300
301 /* sharing code does'nt work well for this one ... */
302 static void
303 set_capture(int capture, int tmp_switch)
304 {
305 static int last_on = CAPTURE_OVERLAY;
306
307 if (set_capture_hook) {
308 if (capture == CAPTURE_ON)
309 capture = last_on;
310
311 if (capture == CAPTURE_OVERLAY) {
312 /* can we do overlay ?? */
313 if (!(f_drv & CAN_OVERLAY))
314 capture = CAPTURE_GRABDISPLAY;
315 if (!do_overlay)
316 capture = CAPTURE_GRABDISPLAY;
317 }
318
319 if (cur_capture != capture) {
320 set_capture_hook(cur_capture,capture,tmp_switch);
321 cur_capture = capture;
322 }
323
324 if (cur_capture != CAPTURE_OFF)
325 last_on = cur_capture;
326 }
327 }
328
329 static void
330 set_attr(struct ng_attribute *attr, int val)
331 {
332 if (NULL == attr)
333 return;
334
335 attr->write(attr,val);
336 cur_attrs[attr->id] = val;
337 if (attr_notify)
338 attr_notify(attr,val);
339 }
340
341 static void
342 set_volume(void)
343 {
344 struct ng_attribute *attr;
345
346 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_VOLUME)))
347 attr->write(attr,cur_attrs[ATTR_ID_VOLUME]);
348 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_MUTE)))
349 attr->write(attr,cur_attrs[ATTR_ID_MUTE]);
350
351 if (volume_notify)
352 volume_notify();
353 }
354
355 static void
356 set_freqtab(int j)
357 {
358 freq_newtab(j);
359
360 /* cur_channel might be invalid (>chancount) right now */
361 cur_channel = -1;
362 /* this is valid for (struct CHANNEL*)->channel too */
363 calc_frequencies();
364
365 if (freqtab_notify)
366 freqtab_notify();
367 }
368
369 static void
370 set_title(void)
371 {
372 static char title[256];
373 const char *norm;
374
375 keypad_state = -1;
376 if (update_title) {
377 if (-1 != cur_sender) {
378 sprintf(title,"%s",channels[cur_sender]->name);
379 } else if (-1 != cur_channel) {
380 sprintf(title,"channel %s",chanlist[cur_channel].name);
381 if (cur_fine != 0)
382 sprintf(title+strlen(title)," (%d)",cur_fine);
383 norm = ng_attr_getstr(ng_attr_byid(attrs,ATTR_ID_NORM),
384 cur_attrs[ATTR_ID_NORM]);
385 sprintf(title+strlen(title)," (%s/%s)",
386 norm ? norm : "???", chanlists[chantab].name);
387 } else {
388 sprintf(title,"%.3f MHz",cur_freq/16.0);
389 }
390 update_title(title);
391 }
392 }
393
394 static void
395 set_msg_int(struct ng_attribute *attr, int val)
396 {
397 static char title[256];
398
399 if (display_message) {
400 sprintf(title,"%s: %d%%",attr->name,
401 ng_attr_int2percent(attr,val));
402 display_message(title);
403 }
404 }
405
406 static void
407 set_msg_bool(const char *name, int val)
408 {
409 static char title[256];
410
411 if (display_message) {
412 sprintf(title,"%s: %s",name, val ? "on" : "off");
413 display_message(title);
414 }
415 }
416
417 static void
418 set_msg_str(char *name, char *val)
419 {
420 static char title[256];
421
422 if (display_message) {
423 sprintf(title,"%s: %s",name,val);
424 display_message(title);
425 }
426 }
427
428 /* ----------------------------------------------------------------------- */
429
430 static int update_int(struct ng_attribute *attr, int old, char *new)
431 {
432 int value = old;
433 int step;
434
435 step = (attr->max - attr->min) * 3 / 100;
436 if (step == 0)
437 step = 1;
438
439 if (0 == strcasecmp(new,"inc"))
440 value += step;
441 else if (0 == strcasecmp(new,"dec"))
442 value -= step;
443 else if (0 == strncasecmp(new,"+=",2))
444 value += ng_attr_parse_int(attr,new+2);
445 else if (0 == strncasecmp(new,"-=",2))
446 value -= ng_attr_parse_int(attr,new+2);
447 else if (isdigit(new[0]) || '+' == new[0] || '-' == new[0])
448 value = ng_attr_parse_int(attr,new);
449 else
450 fprintf(stderr,"update_int: can't parse %s\n",new);
451
452 if (value < attr->min)
453 value = attr->min;
454 if (value > attr->max)
455 value = attr->max;
456 return value;
457 }
458
459 /* ----------------------------------------------------------------------- */
460
461 void
462 attr_init(void)
463 {
464 struct ng_attribute *attr;
465 int val;
466
467 for (attr = attrs; attr != NULL && attr->name != NULL; attr++) {
468 if (attr->id == ATTR_ID_VOLUME ||
469 attr->id == ATTR_ID_MUTE)
470 continue;
471 val = attr->read(attr);
472 if (attr_notify)
473 attr_notify(attr,val);
474 cur_attrs[attr->id] = val;
475 }
476 if (-1 == defaults.color &&
477 NULL != ng_attr_byid(attrs,ATTR_ID_COLOR))
478 defaults.color = cur_attrs[ATTR_ID_COLOR];
479 if (-1 == defaults.bright &&
480 NULL != ng_attr_byid(attrs,ATTR_ID_BRIGHT))
481 defaults.bright = cur_attrs[ATTR_ID_BRIGHT];
482 if (-1 == defaults.hue &&
483 NULL != ng_attr_byid(attrs,ATTR_ID_HUE))
484 defaults.hue = cur_attrs[ATTR_ID_HUE];
485 if (-1 == defaults.contrast &&
486 NULL != ng_attr_byid(attrs,ATTR_ID_CONTRAST))
487 defaults.contrast = cur_attrs[ATTR_ID_CONTRAST];
488 }
489
490 void
491 audio_init(void)
492 {
493 struct ng_attribute *attr;
494
495 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_VOLUME)))
496 cur_attrs[ATTR_ID_VOLUME] = attr->read(attr);
497 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_MUTE)))
498 cur_attrs[ATTR_ID_MUTE] = attr->read(attr);
499 if (volume_notify)
500 volume_notify();
501 }
502
503 void
504 audio_on(void)
505 {
506 struct ng_attribute *attr,*list;
507
508 list = drv->list_attrs(h_drv);
509 attr = ng_attr_byid(list,ATTR_ID_MUTE);
510 if (NULL != attr)
511 attr->write(attr,0);
512 }
513
514 void
515 audio_off(void)
516 {
517 struct ng_attribute *attr,*list;
518
519 list = drv->list_attrs(h_drv);
520 attr = ng_attr_byid(list,ATTR_ID_MUTE);
521 if (NULL != attr)
522 attr->write(attr,1);
523 }
524
525 void
526 set_defaults(void)
527 {
528 struct ng_attribute *attr;
529
530 /* image parameters */
531 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_COLOR)))
532 set_attr(attr,defaults.color);
533 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_BRIGHT)))
534 set_attr(attr,defaults.bright);
535 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_HUE)))
536 set_attr(attr,defaults.hue);
537 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_CONTRAST)))
538 set_attr(attr,defaults.contrast);
539 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_INPUT)))
540 set_attr(attr,defaults.input);
541 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_NORM)))
542 set_attr(attr,defaults.norm);
543 set_capture(defaults.capture,0);
544
545 cur_channel = defaults.channel;
546 cur_fine = defaults.fine;
547 cur_freq = defaults.freq;
548 if (f_drv & CAN_TUNE)
549 drv->setfreq(h_drv,defaults.freq);
550 }
551
552 /* ----------------------------------------------------------------------- */
553
554 #ifndef HAVE_STRCASESTR
555 static char* strcasestr(char *haystack, char *needle)
556 {
557 int hlen = strlen(haystack);
558 int nlen = strlen(needle);
559 int offset;
560
561 for (offset = 0; offset <= hlen - nlen; offset++)
562 if (0 == strncasecmp(haystack+offset,needle,nlen))
563 return haystack+offset;
564 return NULL;
565 }
566 #endif
567
568 static int setstation_handler(char *name, int argc, char **argv)
569 {
570 struct ng_attribute *attr,*mute;
571 int i;
572
573 if (0 == argc) {
574 set_title();
575 return 0;
576 }
577
578 if (cur_movie) {
579 if (display_message)
580 display_message("grabber busy");
581 return -1;
582 }
583
584 if (count && 0 == strcasecmp(argv[0],"next")) {
585 i = (cur_sender+1) % count;
586 } else if (count && 0 == strcasecmp(argv[0],"prev")) {
587 i = (cur_sender+count-1) % count;
588 } else if (count && 0 == strcasecmp(argv[0],"back")) {
589 if (-1 == last_sender)
590 return -1;
591 i = last_sender;
592 } else {
593 /* search the configured channels first... */
594 for (i = 0; i < count; i++)
595 if (0 == strcasecmp(channels[i]->name,argv[0]))
596 break;
597 /* ... next try substring matches ... */
598 if (i == count)
599 for (i = 0; i < count; i++)
600 if (NULL != strcasestr(channels[i]->name,argv[0]))
601 break;
602 /* ... next try using the argument as index ... */
603 if (i == count)
604 if (isdigit(argv[0][0]))
605 i = atoi(argv[0]);
606 if (i == count) {
607 /* ... sorry folks */
608 fprintf(stderr,"station \"%s\" not found\n",argv[0]);
609 return -1;
610 }
611 }
612
613 /* ok ?? */
614 if (i < 0 || i >= count)
615 return -1;
616
617 /* switch ... */
618 if (channel_switch_hook)
619 channel_switch_hook();
620 set_capture(CAPTURE_OFF,1);
621
622 last_sender = cur_sender;
623 cur_sender = i;
624
625 mute = ng_attr_byid(attrs,ATTR_ID_MUTE);
626 if (mute && !cur_attrs[ATTR_ID_MUTE])
627 mute->write(mute,1);
628
629 /* image parameters */
630 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_COLOR)))
631 set_attr(attr,channels[i]->color);
632 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_BRIGHT)))
633 set_attr(attr,channels[i]->bright);
634 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_HUE)))
635 set_attr(attr,channels[i]->hue);
636 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_CONTRAST)))
637 set_attr(attr,channels[i]->contrast);
638
639 /* input / norm */
640 if (cur_attrs[ATTR_ID_INPUT] != channels[i]->input)
641 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_INPUT)))
642 set_attr(attr,channels[i]->input);
643 if (cur_attrs[ATTR_ID_NORM] != channels[i]->norm)
644 if (NULL != (attr = ng_attr_byid(attrs,ATTR_ID_NORM)))
645 set_attr(attr,channels[i]->norm);
646
647 /* station */
648 cur_channel = channels[i]->channel;
649 cur_fine = channels[i]->fine;
650 cur_freq = channels[i]->freq;
651 if (f_drv & CAN_TUNE)
652 drv->setfreq(h_drv,channels[i]->freq);
653 set_capture(channels[i]->capture,0);
654
655 set_title();
656 if (setstation_notify)
657 setstation_notify();
658
659 if (mute && !cur_attrs[ATTR_ID_MUTE]) {
660 usleep(20000);
661 mute->write(mute,0);
662 }
663 return 0;
664 }
665
666 static int setchannel_handler(char *name, int argc, char **argv)
667 {
668 struct ng_attribute *mute;
669 int c,i;
670
671 if (0 == argc) {
672 set_title();
673 return 0;
674 }
675
676 if (cur_movie) {
677 if (display_message)
678 display_message("grabber busy");
679 return -1;
680 }
681
682 if (0 == strcasecmp("setfreq",name)) {
683 cur_freq = (unsigned long)(atof(argv[0])*16);
684 cur_sender = -1;
685 cur_channel = -1;
686 cur_fine = 0;
687 } else {
688 if (0 == strcasecmp(argv[0],"next")) {
689 cur_channel = (cur_channel+1) % chancount;
690 cur_fine = defaults.fine;
691 } else if (0 == strcasecmp(argv[0],"prev")) {
692 cur_channel = (cur_channel+chancount-1) % chancount;
693 cur_fine = defaults.fine;
694 } else if (0 == strcasecmp(argv[0],"fine_up")) {
695 cur_fine++;
696 } else if (0 == strcasecmp(argv[0],"fine_down")) {
697 cur_fine--;
698 } else {
699 if (-1 != (c = lookup_channel(argv[0]))) {
700 cur_channel = c;
701 cur_fine = defaults.fine;
702 }
703 }
704
705 if (0 != strncmp(argv[0],"fine",4)) {
706 /* look if there is a known station on that channel */
707 for (i = 0; i < count; i++) {
708 if (cur_channel == channels[i]->channel) {
709 char *argv[2];
710 argv[0] = channels[i]->name;
711 argv[1] = NULL;
712 return setstation_handler("", argc, argv);
713 }
714 }
715 }
716 cur_sender = -1;
717 if (-1 != cur_channel)
718 cur_freq = get_freq(cur_channel)+cur_fine;
719 else {
720 cur_freq += cur_fine;
721 cur_fine = 0;
722 }
723 }
724
725 if (channel_switch_hook)
726 channel_switch_hook();
727 set_capture(CAPTURE_OFF,1);
728
729 mute = ng_attr_byid(attrs,ATTR_ID_MUTE);
730 if (mute && !cur_attrs[ATTR_ID_MUTE])
731 mute->write(mute,1);
732
733 if (f_drv & CAN_TUNE)
734 drv->setfreq(h_drv,cur_freq);
735 set_capture(defaults.capture,0);
736
737 set_title();
738 if (setstation_notify)
739 setstation_notify();
740
741 if (mute && !cur_attrs[ATTR_ID_MUTE]) {
742 usleep(20000);
743 mute->write(mute,0);
744 }
745 return 0;
746 }
747
748 /* ----------------------------------------------------------------------- */
749
750 static void
751 print_choices(char *name, char *value, struct STRTAB *tab)
752 {
753 int i;
754
755 fprintf(stderr,"unknown %s: '%s' (available: ",name,value);
756 for (i = 0; tab[i].str != NULL; i++)
757 fprintf(stderr,"%s'%s'", (0 == i) ? "" : ", ", tab[i].str);
758 fprintf(stderr,")\n");
759 }
760
761 static int setfreqtab_handler(char *name, int argc, char **argv)
762 {
763 int i;
764
765 i = str_to_int(argv[0],chanlist_names);
766 if (i != -1)
767 set_freqtab(i);
768 else
769 print_choices("freqtab",argv[0],chanlist_names);
770 return 0;
771 }
772
773 static int capture_handler(char *name, int argc, char **argv)
774 {
775 int i;
776
777 if (0 == strcasecmp(argv[0],"toggle")) {
778 i = (cur_capture == CAPTURE_OFF) ? CAPTURE_ON : CAPTURE_OFF;
779 } else {
780 i = str_to_int(argv[0],captab);
781 }
782 if (i != -1)
783 set_capture(i,0);
784 return 0;
785 }
786
787 /* ----------------------------------------------------------------------- */
788
789 static int volume_handler(char *name, int argc, char **argv)
790 {
791 struct ng_attribute *vol = ng_attr_byid(attrs,ATTR_ID_VOLUME);
792
793 if (0 == argc)
794 goto display;
795
796 if (0 == strcasecmp(argv[0],"mute")) {
797 /* mute on/off/toggle */
798 if (argc > 1) {
799 switch (str_to_int(argv[1],booltab)) {
800 case 0: cur_attrs[ATTR_ID_MUTE] = 0; break;
801 case 1: cur_attrs[ATTR_ID_MUTE] = 1; break;
802 default: cur_attrs[ATTR_ID_MUTE] = !cur_attrs[ATTR_ID_MUTE]; break;
803 }
804 } else {
805 cur_attrs[ATTR_ID_MUTE] = !cur_attrs[ATTR_ID_MUTE];
806 }
807 } else {
808 /* volume */
809 if (NULL != vol) {
810 cur_attrs[ATTR_ID_VOLUME] = vol->read(vol);
811 cur_attrs[ATTR_ID_VOLUME] =
812 update_int(vol,cur_attrs[ATTR_ID_VOLUME],argv[0]);
813 }
814 }
815 set_volume();
816
817 display:
818 if (cur_attrs[ATTR_ID_MUTE])
819 set_msg_str("volume","muted");
820 else {
821 if (vol)
822 set_msg_int(vol,cur_attrs[ATTR_ID_VOLUME]);
823 else
824 set_msg_str("volume","unmuted");
825 }
826 return 0;
827 }
828
829 static int attr_handler(char *name, int argc, char **argv)
830 {
831 struct ng_attribute *attr;
832 int val,arg=0;
833
834 if (0 == strcasecmp(name,"setnorm")) {
835 attr = ng_attr_byname(attrs,"norm");
836
837 } else if (0 == strcasecmp(name,"setinput")) {
838 attr = ng_attr_byname(attrs,"input");
839
840 } else if (0 == strcasecmp(name,"setattr") &&
841 argc > 0) {
842 attr = ng_attr_byname(attrs,argv[arg++]);
843
844 } else {
845 attr = ng_attr_byname(attrs,name);
846 }
847
848 if (NULL == attr) {
849 fprintf(stderr,"cmd: %s: attribute not found\nvalid choices are:",
850 (arg > 0) ? argv[0] : name);
851 for (attr = attrs; attr->name != NULL; attr++)
852 fprintf(stderr,"%s \"%s\"",
853 (attr != attrs) ? "," : "", attr->name);
854 fprintf(stderr,"\n");
855 return -1;
856 }
857
858 if (!cur_movie && capture_get_hook)
859 capture_get_hook();
860 switch (attr->type) {
861 case ATTR_TYPE_CHOICE:
862 if (argc > arg) {
863 if (0 == strcasecmp("next", argv[arg])) {
864 val = cur_attrs[attr->id];
865 val++;
866 if (NULL == attr->choices[val].str)
867 val = 0;
868 } else {
869 val = ng_attr_getint(attr, argv[arg]);
870 }
871 if (-1 == val) {
872 fprintf(stderr,"invalid value for %s: %s\n",attr->name,argv[arg]);
873 ng_attr_listchoices(attr);
874 } else {
875 set_attr(attr,val);
876 }
877 }
878 break;
879 case ATTR_TYPE_INTEGER:
880 if (argc > arg) {
881 cur_attrs[attr->id] = attr->read(attr);
882 val = update_int(attr,cur_attrs[attr->id],argv[arg]);
883 set_attr(attr,val);
884 }
885 set_msg_int(attr,cur_attrs[attr->id]);
886 break;
887 case ATTR_TYPE_BOOL:
888 if (argc > arg) {
889 val = str_to_int(argv[arg],booltab);
890 if (-1 == val) {
891 if (0 == strcasecmp(argv[arg],"toggle"))
892 val = !cur_attrs[attr->id];
893 }
894 set_attr(attr,val);
895 }
896 set_msg_bool(attr->name,cur_attrs[attr->id]);
897 break;
898 }
899 if (!cur_movie && capture_rel_hook)
900 capture_rel_hook();
901 return 0;
902 }
903
904 static int show_handler(char *name, int argc, char **argv)
905 {
906 struct ng_attribute *attr;
907 char *n[2] = { NULL, NULL };
908 int val;
909
910 if (0 == argc) {
911 for (attr = attrs; attr->name != NULL; attr++) {
912 n[0] = (char*)attr->name;
913 show_handler("show", 1, n);
914 }
915 return 0;
916 }
917
918 attr = ng_attr_byname(attrs,argv[0]);
919 if (NULL == attr) {
920 fprintf(stderr,"fixme: 404 %s\n",argv[0]);
921 return 0;
922 }
923 val = cur_attrs[attr->id];
924 switch (attr->type) {
925 case ATTR_TYPE_CHOICE:
926 printf("%s: %s\n", attr->name, ng_attr_getstr(attr,val));
927 break;
928 case ATTR_TYPE_INTEGER:
929 printf("%s: %d\n", attr->name, val);
930 break;
931 case ATTR_TYPE_BOOL:
932 printf("%s: %s\n", attr->name, val ? "on" : "off");
933 break;
934 }
935 return 0;
936 }
937
938 static int list_handler(char *name, int argc, char **argv)
939 {
940 struct ng_attribute *attr;
941 int val,i;
942
943 printf("%-10.10s | type | %-7.7s | %-7.7s | %s\n",
944 "attribute","current","default","comment");
945 printf("-----------+--------+---------+--------"
946 "-+-------------------------------------\n");
947 for (attr = attrs; attr->name != NULL; attr++) {
948 val = cur_attrs[attr->id];
949 switch (attr->type) {
950 case ATTR_TYPE_CHOICE:
951 printf("%-10.10s | choice | %-7.7s | %-7.7s |",
952 attr->name,
953 ng_attr_getstr(attr,val),
954 ng_attr_getstr(attr,attr->defval));
955 for (i = 0; attr->choices[i].str != NULL; i++)
956 printf(" %s",attr->choices[i].str);
957 printf("\n");
958 break;
959 case ATTR_TYPE_INTEGER:
960 printf("%-10.10s | int | %7d | %7d | range is %d => %d\n",
961 attr->name, val, attr->defval,
962 attr->min, attr->max);
963 break;
964 case ATTR_TYPE_BOOL:
965 printf("%-10.10s | bool | %-7.7s | %-7.7s |\n",
966 attr->name,
967 val ? "on" : "off",
968 attr->defval ? "on" : "off");
969 break;
970 }
971 }
972 return 0;
973 }
974
975 static int dattr_handler(char *name, int argc, char **argv)
976 {
977 struct ng_attribute *attr = NULL;
978 unsigned int i;
979
980 if (argc > 0 && 0 == strcasecmp(argv[0],"next")) {
981 for (i = 0; i < NUM_DATTR; i++) {
982 cur_dattr++;
983 cur_dattr %= NUM_DATTR;
984 attr = ng_attr_byid(attrs,dattr[cur_dattr]);
985 if (NULL != attr)
986 break;
987 }
988 if (NULL == attr)
989 return 0;
990 argc = 0;
991 }
992 if (NULL == attr)
993 attr = ng_attr_byid(attrs,dattr[cur_dattr]);
994 if (NULL == attr)
995 return 0;
996 return attr_handler((char*)attr->name,argc,argv);
997 }
998
999 /* ----------------------------------------------------------------------- */
1000
1001 static int snap_handler(char *hname, int argc, char **argv)
1002 {
1003 char message[512];
1004 char *tmpfilename = NULL;
1005 char *filename = NULL;
1006 char *name;
1007 int jpeg = 0;
1008 int ret = 0;
1009 struct ng_video_fmt fmt;
1010 struct ng_video_buf *buf = NULL;
1011
1012 if (!(f_drv & CAN_CAPTURE)) {
1013 fprintf(stderr,"grabbing: not supported [try -noxv switch?]\n");
1014 return -1;
1015 }
1016
1017 if (cur_movie) {
1018 if (display_message)
1019 display_message("grabber busy");
1020 return -1;
1021 }
1022
1023 if (capture_get_hook)
1024 capture_get_hook();
1025
1026 /* format */
1027 if (argc > 0) {
1028 if (0 == strcasecmp(argv[0],"jpeg"))
1029 jpeg = 1;
1030 if (0 == strcasecmp(argv[0],"ppm"))
1031 jpeg = 0;
1032 }
1033
1034 /* size */
1035 memset(&fmt,0,sizeof(fmt));
1036 fmt.fmtid = VIDEO_RGB24;
1037 fmt.width = 2048;
1038 fmt.height = 1572;
1039 if (argc > 1) {
1040 if (0 == strcasecmp(argv[1],"full")) {
1041 /* nothing */
1042 } else if (0 == strcasecmp(argv[1],"win")) {
1043 fmt.width = cur_tv_width;
1044 fmt.height = cur_tv_height;
1045 } else if (2 == sscanf(argv[1],"%dx%d",&fmt.width,&fmt.height)) {
1046 /* nothing */
1047 } else {
1048 return -1;
1049 }
1050 }
1051
1052 /* filename */
1053 if (argc > 2)
1054 filename = argv[2];
1055
1056 if (NULL == (buf = ng_grabber_get_image(&fmt))) {
1057 if (display_message)
1058 display_message("grabbing failed");
1059 ret = -1;
1060 goto done;
1061 }
1062 buf = ng_filter_single(cur_filter,buf);
1063
1064 if (NULL == filename) {
1065 if (-1 != cur_sender) {
1066 name = channels[cur_sender]->name;
1067 } else if (-1 != cur_channel) {
1068 name = chanlist[cur_channel].name;
1069 } else {
1070 name = "unknown";
1071 }
1072 filename = snap_filename(snapbase, name, jpeg ? "jpeg" : "ppm");
1073 }
1074 tmpfilename = malloc(strlen(filename)+8);
1075 sprintf(tmpfilename,"%s.$$$",filename);
1076
1077 if (jpeg) {
1078 if (-1 == write_jpeg(tmpfilename, buf, ng_jpeg_quality, 0)) {
1079 sprintf(message,"open %s: %s\n",tmpfilename,strerror(errno));
1080 } else {
1081 sprintf(message,"saved jpeg: %s",filename);
1082 }
1083 } else {
1084 if (-1 == write_ppm(tmpfilename, buf)) {
1085 sprintf(message,"open %s: %s\n",tmpfilename,strerror(errno));
1086 } else {
1087 sprintf(message,"saved ppm: %s",filename);
1088 }
1089 }
1090 unlink(filename);
1091 if (-1 == link(tmpfilename,filename)) {
1092 fprintf(stderr,"link(%s,%s): %s\n",
1093 tmpfilename,filename,strerror(errno));
1094 goto done;
1095 }
1096 unlink(tmpfilename);
1097 if (display_message)
1098 display_message(message);
1099
1100 done:
1101 if (tmpfilename)
1102 free(tmpfilename);
1103 if (NULL != buf)
1104 ng_release_video_buf(buf);
1105 if (capture_rel_hook)
1106 capture_rel_hook();
1107 return ret;
1108 }
1109
1110 static int webcam_handler(char *hname, int argc, char **argv)
1111 {
1112 struct ng_video_fmt fmt;
1113 struct ng_video_buf *buf;
1114
1115 if (webcam)
1116 free(webcam);
1117 webcam = strdup(argv[0]);
1118
1119 /* if either avi recording or grabdisplay is active, we do
1120 /not/ stop capture and switch the video format. The next
1121 capture will send a copy of the frame to the webcam thread
1122 and it has to deal with it as-is */
1123 if (cur_movie)
1124 return 0;
1125 if (cur_capture == CAPTURE_GRABDISPLAY)
1126 return 0;
1127
1128 /* if no capture is running we can switch to RGB first to make
1129 the webcam happy */
1130 if (capture_get_hook)
1131 capture_get_hook();
1132 memset(&fmt,0,sizeof(fmt));
1133 fmt.fmtid = VIDEO_RGB24;
1134 fmt.width = cur_tv_width;
1135 fmt.height = cur_tv_height;
1136 buf = ng_grabber_get_image(&fmt);
1137 if (buf)
1138 ng_release_video_buf(buf);
1139 if (capture_rel_hook)
1140 capture_rel_hook();
1141 return 0;
1142 }
1143
1144 static int movie_handler(char *name, int argc, char **argv)
1145 {
1146 if (!movie_hook)
1147 return 0;
1148 movie_hook(argc,argv);
1149 return 0;
1150 }
1151
1152 static int
1153 fullscreen_handler(char *name, int argc, char **argv)
1154 {
1155 if (fullscreen_hook)
1156 fullscreen_hook();
1157 return 0;
1158 }
1159
1160 static int
1161 msg_handler(char *name, int argc, char **argv)
1162 {
1163 if (display_message)
1164 display_message(argv[0]);
1165 return 0;
1166 }
1167
1168 static int
1169 showtime_handler(char *name, int argc, char **argv)
1170 {
1171 char timestr[6];
1172 struct tm *times;
1173 time_t timet;
1174
1175 timet = time(NULL);
1176 times = localtime(&timet);
1177 strftime(timestr, 6, "%k:%M", times);
1178 if (display_message)
1179 display_message(timestr);
1180 return 0;
1181 }
1182
1183 static int
1184 exit_handler(char *name, int argc, char **argv)
1185 {
1186 if (exit_hook)
1187 exit_hook();
1188 return 0;
1189 }
1190
1191 /* ----------------------------------------------------------------------- */
1192
1193 static char *strfamily(int family)
1194 {
1195 switch (family) {
1196 case PF_INET6: return "ipv6";
1197 case PF_INET: return "ipv4";
1198 case PF_UNIX: return "unix";
1199 }
1200 return "????";
1201 }
1202
1203 static int
1204 tcp_connect(struct addrinfo *ai, char *host, char *serv)
1205 {
1206 struct addrinfo *res,*e;
1207 char uhost[INET6_ADDRSTRLEN+1];
1208 char userv[33];
1209 int sock,rc,opt=1;
1210
1211 ai->ai_flags = AI_CANONNAME;
1212 if (debug)
1213 fprintf(stderr,"tcp: lookup %s:%s ... ",host,serv);
1214 if (0 != (rc = getaddrinfo(host, serv, ai, &res))) {
1215 fprintf(stderr,"tcp: getaddrinfo (%s:%s): %s\n",
1216 host,serv,gai_strerror(rc));
1217 return -1;
1218 }
1219 if (debug)
1220 fprintf(stderr,"ok\n");
1221 for (e = res; e != NULL; e = e->ai_next) {
1222 if (0 != getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
1223 uhost,INET6_ADDRSTRLEN,userv,32,
1224 NI_NUMERICHOST | NI_NUMERICSERV)) {
1225 fprintf(stderr,"tcp: getnameinfo (peer): oops\n");
1226 continue;
1227 }
1228 if (debug)
1229 fprintf(stderr,"tcp: trying %s (%s:%s) ... ",
1230 strfamily(e->ai_family),uhost,userv);
1231 if (-1 == (sock = socket(e->ai_family, e->ai_socktype,
1232 e->ai_protocol))) {
1233 fprintf(stderr,"tcp: socket: %s\n",strerror(errno));
1234 continue;
1235 }
1236 setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
1237 if (-1 == connect(sock,e->ai_addr,e->ai_addrlen)) {
1238 fprintf(stderr,"tcp: connect: %s\n",strerror(errno));
1239 close(sock);
1240 continue;
1241 }
1242 if (debug)
1243 fprintf(stderr,"ok\n");
1244 fcntl(sock,F_SETFL,O_NONBLOCK);
1245 fcntl(sock,F_SETFD,FD_CLOEXEC);
1246 return sock;
1247 }
1248 return -1;
1249 }
1250
1251 static int tcp_readbuf(int sock, int timeout, char *dest, char dlen)
1252 {
1253 struct timeval tv;
1254 fd_set set;
1255 int rc;
1256
1257 again:
1258 FD_ZERO(&set);
1259 FD_SET(sock,&set);
1260 tv.tv_sec = timeout;
1261 tv.tv_usec = 0;
1262 rc = select(sock+1,&set,NULL,NULL,&tv);
1263 if (-1 == rc && EINTR == errno)
1264 goto again;
1265 if (-1 == rc) {
1266 if (debug)
1267 perror("tcp: select");
1268 return -1;
1269 }
1270 if (0 == rc) {
1271 if (debug)
1272 fprintf(stderr,"tcp: select timeout\n");
1273 return -1;
1274 }
1275 rc = read(sock,dest,dlen-1);
1276 if (-1 == rc) {
1277 if (debug)
1278 perror("tcp: read");
1279 return -1;
1280 }
1281 dest[rc] = 0;
1282 return rc;
1283 }
1284
1285 static int vdr_sock = -1;
1286
1287 static int
1288 vdr_handler(char *name, int argc, char **argv)
1289 {
1290 char line[80];
1291 struct addrinfo ask;
1292 int i,rc;
1293 unsigned int l,len;
1294
1295 reconnect:
1296 if (-1 == vdr_sock) {
1297 memset(&ask,0,sizeof(ask));
1298 ask.ai_family = PF_UNSPEC;
1299 ask.ai_socktype = SOCK_STREAM;
1300 vdr_sock = tcp_connect(&ask,"localhost","2001");
1301 if (-1 == vdr_sock)
1302 return -1;
1303 if (debug)
1304 fprintf(stderr,"vdr: connected\n");
1305
1306 /* skip greeting line */
1307 if (-1 == tcp_readbuf(vdr_sock,3,line,sizeof(line)))
1308 goto oops;
1309 if (debug)
1310 fprintf(stderr,"vdr: << %s",line);
1311 }
1312
1313 /* send command */
1314 line[0] = 0;
1315 for (i = 0, len = 0; i < argc; i++) {
1316 l = strlen(argv[i]);
1317 if (len+l+4 > sizeof(line))
1318 break;
1319 if (len) {
1320 strcpy(line+len," ");
1321 len++;
1322 }
1323 strcpy(line+len,argv[i]);
1324 len += l;
1325 }
1326 strcpy(line+len,"\r\n");
1327 len += 2;
1328 if (len != (rc = write(vdr_sock,line,len))) {
1329 if (-1 == rc && EPIPE == errno) {
1330 if (debug)
1331 fprintf(stderr,"tcp: write: broken pipe, trying reconnect\n");
1332 close(vdr_sock);
1333 vdr_sock = -1;
1334 goto reconnect;
1335 }
1336 if (debug)
1337 perror("tcp: write");
1338 goto oops;
1339 }
1340 if (debug)
1341 fprintf(stderr,"vdr: >> %s",line);
1342
1343 /* skip answer */
1344 if (-1 == tcp_readbuf(vdr_sock,3,line,sizeof(line)))
1345 goto oops;
1346 if (debug)
1347 fprintf(stderr,"vdr: << %s",line);
1348
1349 #if 0
1350 /* play nicely and close the handle -- vdr can handle only one
1351 * connection at the same time. Drawback is that it increases
1352 * latencies ... */
1353 close(vdr_sock);
1354 vdr_sock = -1;
1355 #endif
1356 return 0;
1357
1358 oops:
1359 close(vdr_sock);
1360 vdr_sock = -1;
1361 return -1;
1362 }
1363
1364 /* ----------------------------------------------------------------------- */
1365
1366 #if TT
1367 static struct TEXTELEM*
1368 parse_vtx(int lines, char **text)
1369 {
1370 static char *names[8] = { "black", "red", "green", "yellow",
1371 "blue", "magenta", "cyan", "white" };
1372 static struct TEXTELEM tt[VTX_COUNT];
1373 int i,n,t,ansi;
1374 char *ansi_fg,*ansi_bg;
1375
1376 /* parse */
1377 t = 0;
1378 memset(tt,0,sizeof(tt));
1379 for (i = 0; i < lines; i++) {
1380 tt[t].line = i;
1381 ansi_fg = NULL; ansi_bg = NULL;
1382 for (n = 0; text[i][n] != 0;) {
1383 if (text[i][n] == '\033') {
1384 if (tt[t].len) {
1385 t++;
1386 if (VTX_COUNT == t)
1387 return tt;
1388 }
1389 n++;
1390 if (text[i][n] == '[') {
1391 /* ANSI color tty sequences */
1392 n++;
1393 for (ansi=1;ansi;) {
1394 switch (text[i][n]) {
1395 case '3':
1396 n++;
1397 if (text[i][n] >= '' && text[i][n] < '8') {
1398 ansi_fg = names[text[i][n]-''];
1399 n++;
1400 }
1401 break;
1402 case '4':
1403 n++;
1404 if (text[i][n] >= '' && text[i][n] < '8') {
1405 ansi_bg = names[text[i][n]-''];
1406 n++;
1407 }
1408 break;
1409 case '1':
1410 case ';':
1411 n++;
1412 break;
1413 case 'm':
1414 n++;
1415 /* ok, commit */
1416 ansi=0;
1417 tt[t].fg = ansi_fg;
1418 tt[t].bg = ansi_bg;
1419 break;
1420 default:
1421 /* error */
1422 ansi=0;
1423 }
1424 }
1425 } else {
1426 /* old way: ESC fg bg */
1427 if (text[i][n] >= '' && text[i][n] < '8') {
1428 tt[t].fg = names[text[i][n]-''];
1429 n++;
1430 }
1431 if (text[i][n] >= '' && text[i][n] < '8') {
1432 tt[t].bg = names[text[i][n]-''];
1433 n++;
1434 }
1435 }
1436 tt[t].line = i;
1437 } else {
1438 tt[t].str[tt[t].len++] = text[i][n];
1439 n++;
1440 if (tt[t].len >= VTX_LEN-1) {
1441 t++;
1442 if (VTX_COUNT == t)
1443 return tt;
1444 tt[t].line = i;
1445 }
1446 }
1447 }
1448 if (tt[t].len) {
1449 t++;
1450 if (VTX_COUNT == t)
1451 break;
1452 }
1453 }
1454 return tt;
1455 }
1456
1457 static int
1458 vtx_handler(char *name, int argc, char **argv)
1459 {
1460 struct TEXTELEM *tt;
1461
1462 if (vtx_message) {
1463 if (argc) {
1464 tt = parse_vtx(argc,argv);
1465 vtx_message(tt);
1466 } else {
1467 vtx_message(NULL);
1468 }
1469 }
1470 return 0;
1471 }
1472 #endif
1473
1474 /* ----------------------------------------------------------------------- */
1475
1476 #define CH_MAX (keypad_ntsc ? 99 : count)
1477
1478 static int
1479 keypad_handler(char *name, int argc, char **argv)
1480 {
1481 int n = atoi(argv[0])%10;
1482 char msg[8],ch[8];
1483
1484 if (debug)
1485 fprintf(stderr,"keypad: key %d\n",n);
1486 if (-1 == keypad_state) {
1487 if ((keypad_partial && n > 0 && n <= CH_MAX) ||
1488 (!keypad_partial && n > 0 && n <= CH_MAX && 10*n > CH_MAX)) {
1489 if (keypad_ntsc) {
1490 sprintf(ch,"%d",n);
1491 do_va_cmd(2,"setchannel",ch,NULL);
1492 } else
1493 do_va_cmd(2,"setstation",channels[n-1]->name,NULL);
1494 }
1495 if (n >= 0 && 10*n <= CH_MAX) {
1496 if (debug)
1497 fprintf(stderr,"keypad: hang: %d\n",n);
1498 keypad_state = n;
1499 if (display_message) {
1500 sprintf(msg,"%d_",n);
1501 display_message(msg);
1502 }
1503 }
1504 } else {
1505 if ((n+keypad_state*10) <= CH_MAX)
1506 n += keypad_state*10;
1507 keypad_state = -1;
1508 if (debug)
1509 fprintf(stderr,"keypad: ok: %d\n",n);
1510 if (n > 0 && n <= CH_MAX) {
1511 if (keypad_ntsc) {
1512 sprintf(ch,"%d",n);
1513 do_va_cmd(2,"setchannel",ch,NULL);
1514 } else
1515 do_va_cmd(2,"setstation",channels[n-1]->name,NULL);
1516 }
1517 }
1518 return 0;
1519 }
1520
1521 void
1522 keypad_timeout(void)
1523 {
1524 if (debug)
1525 fprintf(stderr,"keypad: timeout\n");
1526 if (keypad_state == cur_sender+1)
1527 set_title();
1528 keypad_state = -1;
1529 }
1530
|
This page was automatically generated by the
LXR engine.
|