1 /*
2 * console TV application. Uses a framebuffer device.
3 *
4 * (c) 1998-2001 Gerd Knorr <kraxel@goldbach.in-berlin.de>
5 *
6 */
7
8 #include "config.h"
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <time.h>
15 #include <fcntl.h>
16 #include <termios.h>
17 #include <signal.h>
18 #include <ctype.h>
19 #include <errno.h>
20 #include <sys/time.h>
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
23 #include <sys/mman.h>
24 #include <curses.h>
25 #include <math.h>
26 #include <pthread.h>
27
28 #include <linux/kd.h>
29 #include <linux/fb.h>
30
31 #include "grab-ng.h"
32 #include "writefile.h"
33 #include "sound.h"
34 #include "channel.h"
35 #include "frequencies.h"
36 #include "commands.h"
37 #include "capture.h"
38 #include "lirc.h"
39 #include "joystick.h"
40 #include "midictrl.h"
41 #include "event.h"
42
43 #include "fbtools.h"
44 #include "fs.h"
45 #include "matrox.h"
46
47 #define MAX(x,y) ((x)>(y)?(x):(y))
48 #define MIN(x,y) ((x)<(y)?(x):(y))
49
50 /* ---------------------------------------------------------------------- */
51 /* framebuffer */
52
53 static char *fbdev = NULL;
54 static char *fontfile = NULL;
55 static char *mode = NULL;
56 static char *joydev = NULL;
57 static struct fs_font *f;
58 #ifndef X_DISPLAY_MISSING
59 static char *x11_font = "10x20";
60 #endif
61
62 static unsigned short red[256], green[256], blue[256];
63 static struct fb_cmap cmap = { 0, 256, red, green, blue };
64
65 static int switch_last,fb;
66 static int keep_dma_on = 0;
67
68 static int sig,quiet,matrox;
69 static int ww,hh;
70 static float fbgamma = 1.0;
71
72 static struct ng_video_buf *buf;
73 static struct ng_video_fmt fmt,gfmt;
74 static struct ng_video_conv *conv;
75 static struct ng_convert_handle *ch;
76 static int dx,dy;
77
78 int have_config;
79 int x11_native_format,have_dga=1,debug;
80
81 /*--- channels ------------------------------------------------------------*/
82
83 struct event_entry kbd_events[] = {
84 {
85 .event = "kbd-key-+",
86 .action = "volume inc",
87 },{
88 .event = "kbd-key--",
89 .action = "volume dec",
90 },{
91 .event = "kbd-key-enter",
92 .action = "volume mute",
93 },{
94 .event = "kbd-key-space",
95 .action = "setstation next",
96 },{
97 .event = "kbd-key-backspace",
98 .action = "setstation back",
99 },{
100 .event = "kbd-key-pgup",
101 .action = "setstation next",
102 },{
103 .event = "kbd-key-pgdown",
104 .action = "setstation prev",
105 },{
106 .event = "kbd-key-right",
107 .action = "setchannel fine_up",
108 },{
109 .event = "kbd-key-left",
110 .action = "setchannel fine_down",
111 },{
112 .event = "kbd-key-up",
113 .action = "setchannel next",
114 },{
115 .event = "kbd-key-down",
116 .action = "setchannel prev",
117 },{
118 .event = "kbd-key-g",
119 .action = "snap ppm",
120 },{
121 .event = "kbd-key-j",
122 .action = "snap jpeg",
123 },{
124 .event = "kbd-key-v",
125 .action = "capture toggle",
126 },{
127 .event = "kbd-key-f",
128 .action = "fullscreen toggle",
129 },{
130 .event = "kbd-key-0",
131 .action = "keypad 0",
132 },{
133 .event = "kbd-key-1",
134 .action = "keypad 1",
135 },{
136 .event = "kbd-key-2",
137 .action = "keypad 2",
138 },{
139 .event = "kbd-key-3",
140 .action = "keypad 3",
141 },{
142 .event = "kbd-key-4",
143 .action = "keypad 4",
144 },{
145 .event = "kbd-key-5",
146 .action = "keypad 5",
147 },{
148 .event = "kbd-key-6",
149 .action = "keypad 6",
150 },{
151 .event = "kbd-key-7",
152 .action = "keypad 7",
153 },{
154 .event = "kbd-key-8",
155 .action = "keypad 8",
156 },{
157 .event = "kbd-key-9",
158 .action = "keypad 9",
159 },{
160
161 /* end of list */
162 }
163 };
164 struct KEYTAB {
165 int key;
166 char *name;
167 };
168
169 static struct KEYTAB keytab[] = {
170 { 9, "tab" },
171 { 10, "enter" },
172 { 13, "enter" },
173 { KEY_ENTER, "enter" },
174
175 { ' ', "space" },
176 { KEY_BACKSPACE, "backspace" },
177
178 { KEY_RIGHT, "right" },
179 { KEY_LEFT, "left" },
180 { KEY_UP, "up" },
181 { KEY_DOWN, "down" },
182 { KEY_PPAGE, "pgup" },
183 { KEY_NPAGE, "pgdown" },
184 { KEY_HOME, "home" },
185 { KEY_END, "end" },
186 };
187
188 #define NKEYTAB (sizeof(keytab)/sizeof(struct KEYTAB))
189
190 static char *snapbase;
191 static char default_title[128] = "???";
192 static char message[128] = "";
193
194 /* ---------------------------------------------------------------------- */
195 /* framebuffer stuff */
196
197 static void
198 linear_palette(int bit)
199 {
200 int i, size = 256 >> (8 - bit);
201
202 for (i = 0; i < size; i++)
203 red[i] = green[i] = blue[i] = (unsigned short)(65535.0
204 * pow(i/(size - 1.0), fbgamma));
205 }
206
207 static void
208 dither_palette(int r, int g, int b)
209 {
210 int rs, gs, bs, i;
211
212 rs = 256 / (r - 1);
213 gs = 256 / (g - 1);
214 bs = 256 / (b - 1);
215 for (i = 0; i < r*g*b; i++) {
216 green[i+16] = (gs * ((i / (r * b)) % g)) * 255;
217 red[i+16] = (rs * ((i / b) % r)) * 255;
218 blue[i+16] = (bs * ((i) % b)) * 255;
219 }
220 }
221
222 static void
223 fb_initcolors(int fd, int gray)
224 {
225 /* get colormap */
226 if (fb_var.bits_per_pixel == 8 ||
227 fb_fix.visual == FB_VISUAL_DIRECTCOLOR) {
228 if (-1 == ioctl(fd,FBIOGETCMAP,&cmap))
229 perror("ioctl FBIOGETCMAP");
230 }
231
232 switch (fb_var.bits_per_pixel) {
233 case 8:
234 if (gray) {
235 linear_palette(8);
236 x11_native_format = VIDEO_GRAY;
237 } else {
238 dither_palette(5,9,5);
239 x11_native_format = VIDEO_RGB08;
240 }
241 break;
242 case 15:
243 case 16:
244 if (fb_fix.visual == FB_VISUAL_DIRECTCOLOR)
245 linear_palette(5);
246 #if BYTE_ORDER == BIG_ENDIAN
247 x11_native_format = (fb_var.green.length == 6) ?
248 VIDEO_RGB16_BE : VIDEO_RGB15_BE;
249 #else
250 x11_native_format = (fb_var.green.length == 6) ?
251 VIDEO_RGB16_LE : VIDEO_RGB15_LE;
252 #endif
253 break;
254 case 24:
255 if (fb_fix.visual == FB_VISUAL_DIRECTCOLOR)
256 linear_palette(8);
257 #if BYTE_ORDER == BIG_ENDIAN
258 x11_native_format = VIDEO_RGB24;
259 #else
260 x11_native_format = VIDEO_BGR24;
261 #endif
262 break;
263 case 32:
264 if (fb_fix.visual == FB_VISUAL_DIRECTCOLOR)
265 linear_palette(8);
266 #if BYTE_ORDER == BIG_ENDIAN
267 x11_native_format = VIDEO_RGB32;
268 #else
269 x11_native_format = VIDEO_BGR32;
270 #endif
271 break;
272 default:
273 fprintf(stderr, "Oops: %i bit/pixel ???\n",
274 fb_var.bits_per_pixel);
275 exit(1);
276 }
277
278 /* set colormap */
279 if (fb_var.bits_per_pixel == 8 ||
280 fb_fix.visual == FB_VISUAL_DIRECTCOLOR) {
281 if (-1 == ioctl(fd,FBIOPUTCMAP,&cmap))
282 perror("ioctl FBIOPUTCMAP");
283 }
284 }
285
286 static void
287 tty_init(void)
288 {
289 /* we use curses just for kbd input */
290 initscr();
291 cbreak();
292 noecho();
293 keypad(stdscr,1);
294 }
295
296 static void
297 tty_cleanup(void)
298 {
299 clear();
300 refresh();
301 endwin();
302 }
303
304
305 /* ---------------------------------------------------------------------- */
306
307 static void
308 text_init(char *font)
309 {
310 char *fonts[2] = { font, NULL };
311
312 if (NULL == f)
313 f = fs_consolefont(font ? fonts : NULL);
314 #ifndef X_DISPLAY_MISSING
315 if (NULL == f && 0 == fs_connect(NULL))
316 f = fs_open(font ? font : x11_font);
317 #endif
318 if (NULL == f) {
319 fprintf(stderr,"no font available\n");
320 exit(1);
321 }
322 }
323
324 static void
325 text_out(int x, int y, char *str)
326 {
327 y *= f->height;
328 y -= f->fontHeader.max_bounds.descent;
329 fs_puts(f,x,y,str);
330 }
331
332 static int
333 text_width(char *str)
334 {
335 return fs_textwidth(f,str);
336 }
337
338 /* ---------------------------------------------------------------------- */
339
340 #ifdef HAVE_ALSA
341 static struct midi_handle fb_midi;
342 #endif
343
344 /* ---------------------------------------------------------------------- */
345
346 static void
347 ctrlc(int signal)
348 {
349 sig=1;
350 }
351
352 #if 0
353 void
354 change_audio(int mode)
355 {
356 if (grabber->grab_audio)
357 grabber->grab_audio(-1,-1,&mode);
358 }
359 #endif
360
361 static void do_capture(int from, int to, int tmp_switch)
362 {
363 /* off */
364 switch (from) {
365 case CAPTURE_GRABDISPLAY:
366 if (f_drv & CAN_CAPTURE)
367 drv->stopvideo(h_drv);
368 break;
369 case CAPTURE_OVERLAY:
370 if (f_drv & CAN_CAPTURE)
371 drv->overlay(h_drv,NULL,0,0,NULL,0,0);
372 if (matrox && !tmp_switch)
373 gfx_scaler_off();
374 break;
375 }
376
377 /* on */
378 memset(&buf,0,sizeof(buf));
379 switch (to) {
380 case CAPTURE_GRABDISPLAY:
381 if (ww && hh) {
382 dx = fb_var.xres-fmt.width;
383 dy = 0;
384 fmt.fmtid = x11_native_format;
385 fmt.width = ww;
386 fmt.height = hh;
387 fmt.bytesperline = fb_fix.line_length;
388 } else {
389 if (quiet) {
390 dx = 0;
391 dy = 0;
392 } else {
393 dx = f->height*3/2;
394 dy = f->height;
395 }
396 fmt.fmtid = x11_native_format;
397 fmt.width = fb_var.xres-dx;
398 fmt.height = fb_var.yres-dy;
399 fmt.bytesperline = fb_fix.line_length;
400 }
401 if (0 != ng_grabber_setformat(&fmt,1)) {
402 gfmt = fmt;
403 if (NULL == (conv = ng_grabber_findconv(&gfmt,0))) {
404 fprintf(stderr,"can't fint useful capture format\n");
405 exit(1);
406 }
407 ch = ng_convert_alloc(conv,&gfmt,&fmt);
408 ng_convert_init(ch);
409 }
410 dx += (fb_var.xres-24-fmt.width)/2;
411 dy += (fb_var.yres-16-fmt.height)/2;
412
413 if (f_drv & CAN_CAPTURE)
414 drv->startvideo(h_drv,-1,2);
415 break;
416 case CAPTURE_OVERLAY:
417 fmt.fmtid = x11_native_format;
418 if (ww && hh) {
419 fmt.width = ww;
420 fmt.height = hh;
421 dx = fb_var.xres-fmt.width;
422 dy = 0;
423 } else if (quiet) {
424 fmt.width = fb_var.xres;
425 fmt.height = fb_var.yres;
426 dx = 0;
427 dy = 0;
428 } else {
429 fmt.width = fb_var.xres-24;
430 fmt.height = fb_var.yres-16;
431 dx = f->height*3/2;
432 dy = f->height;
433 }
434 if (matrox) {
435 struct ng_video_fmt off;
436 int starty;
437 #if 1
438 /* FIXME: need some kind of size negotiation */
439 /* hardcoded: PAL, half height (want no interleace) */
440 off.width = 768;
441 off.height = 288;
442 starty = fb_var.yres;
443 #else
444 /* settings for debugging */
445 off.width = 320;
446 off.height = 240;
447 starty = fb_var.yres-off.height;
448 #endif
449 off.bytesperline = fb_fix.line_length;
450 if (off.width*2 > off.bytesperline)
451 off.width = off.bytesperline/2;
452 off.fmtid = VIDEO_YUYV;
453 drv->overlay(h_drv,&off,0,starty,NULL,0,0);
454 gfx_scaler_on(starty*off.bytesperline,off.bytesperline,
455 off.width,off.height,
456 dx,dx+fmt.width,
457 dy,dy+fmt.height);
458 } else {
459 drv->overlay(h_drv,&fmt,dx,dy,NULL,0,1);
460 }
461 break;
462 }
463 }
464
465 static void
466 do_exit(void)
467 {
468 sig = 1;
469 }
470
471 static void
472 new_title(char *txt)
473 {
474 strcpy(default_title,txt);
475 }
476
477 static void
478 new_message(char *txt)
479 {
480 strcpy(message,txt);
481 }
482
483 static void
484 channel_menu(void)
485 {
486 char key[32],ctrl[16],event[64],action[128];
487 int i;
488
489 for (i = 0; i < count; i++) {
490 if (channels[i]->key) {
491 if (2 != sscanf(channels[i]->key,"%15[A-Za-z0-9_]+%31[A-Za-z0-9_]",
492 ctrl,key))
493 strcpy(key,channels[i]->key);
494 sprintf(event,"kbd-key-%s",key);
495 sprintf(action,"setstation \"%s\"",channels[i]->name);
496 event_register(event,action);
497 }
498 }
499 }
500
501 static void
502 do_fullscreen(void)
503 {
504 do_va_cmd(2,"capture","off");
505 quiet = !quiet;
506 fb_memset(fb_mem+fb_mem_offset,0,fb_fix.smem_len);
507 do_va_cmd(2,"capture","on");
508 }
509
510 /*--- main ---------------------------------------------------------------*/
511
512 static void
513 grabber_init(void)
514 {
515 struct ng_video_fmt screen;
516
517 memset(&screen,0,sizeof(screen));
518 screen.fmtid = x11_native_format,
519 screen.width = fb_var.xres_virtual;
520 screen.height = fb_var.yres_virtual;
521 screen.bytesperline = fb_fix.line_length;
522 drv = ng_vid_open(ng_dev.video,NULL,&screen,0,&h_drv);
523 if (NULL == drv) {
524 fprintf(stderr,"no grabber device available\n");
525 exit(1);
526 }
527 f_drv = drv->capabilities(h_drv);
528 add_attrs(drv->list_attrs(h_drv));
529 }
530
531 static void
532 console_switch(void)
533 {
534 switch (fb_switch_state) {
535 case FB_REL_REQ:
536 if (!keep_dma_on)
537 do_va_cmd(2,"capture","off");
538 switch_last = fb_switch_state;
539 fb_switch_release();
540 break;
541 case FB_ACQ_REQ:
542 switch_last = fb_switch_state;
543 fb_switch_acquire();
544 fb_memset(fb_mem+fb_mem_offset,0,fb_fix.smem_len);
545 ioctl(fb,FBIOPAN_DISPLAY,&fb_var);
546 do_va_cmd(2,"capture","on");
547 break;
548 case FB_ACTIVE:
549 case FB_INACTIVE:
550 default:
551 switch_last = fb_switch_state;
552 break;
553 }
554 }
555
556 #if 0
557 /* just a hook for some test code */
558 static void
559 scaler_test(int off)
560 {
561 if (!matrox) {
562 matrox=1;
563 if (-1 == gfx_init(fb))
564 matrox = 0;
565 }
566
567 if (matrox) {
568 gfx_scaler_on(0,fb_fix.line_length,320,240,
569 fb_var.xres-320,fb_var.xres,0,240);
570 sleep(2);
571 }
572 }
573 #endif
574
575 int
576 main(int argc, char *argv[])
577 {
578 int i,key,c,gray=0,rc,vt=0,fps=0,t1,t2,lirc,js,err,mute=1,fdmax;
579 unsigned int ui;
580 unsigned long freq;
581 struct timeval tv;
582 time_t t;
583 char text[80],event[64],*env,*dst;
584 fd_set set;
585 struct sigaction act,old;
586
587
588 if (0 == geteuid() && 0 != getuid()) {
589 fprintf(stderr,"fbtv /must not/ be installed suid root\n");
590 exit(1);
591 }
592
593 if (NULL != (env = getenv("FBFONT")))
594 fontfile = env;
595 ng_init();
596 for (;;) {
597 double val;
598 c = getopt(argc, argv, "Mgvqxkd:o:s:c:f:m:z:t:j:");
599 if (c == -1)
600 break;
601 switch (c) {
602 case 'z':
603 if(sscanf(optarg, "%lf", &val) == 1) {
604 if(val < 0.1 || val > 10)
605 fprintf(stderr, "gamma value is out of range. must be "
606 "0.1 < value < 10.0\n");
607 else
608 fbgamma = 1.0 / val;
609 }
610 break;
611 case 'f':
612 fontfile = optarg;
613 break;
614 case 'm':
615 mode = optarg;
616 break;
617 case 'g':
618 gray = 1;
619 break;
620 case 'M':
621 matrox = 1;
622 break;
623 case 'k':
624 keep_dma_on = 1;
625 break;
626 case 'v':
627 debug++;
628 ng_debug++;
629 break;
630 case 'q':
631 quiet = 1;
632 break;
633 case 'd':
634 fbdev = optarg;
635 break;
636 case 'o':
637 snapbase = strdup(optarg);
638 break;
639 case 's':
640 sscanf(optarg,"%dx%d",&ww,&hh);
641 break;
642 case 'c':
643 ng_dev.video = optarg;
644 /* v4l-conf needs this too */
645 strcat(ng_v4l_conf," -c ");
646 strcat(ng_v4l_conf,ng_dev.video);
647 break;
648 case 't':
649 if (optarg)
650 vt = strtoul(optarg, 0, 0);
651 else
652 vt = 0;
653 break;
654 case 'j':
655 joydev = optarg;
656 break;
657 default:
658 exit(1);
659 }
660 }
661
662 do_overlay = 1;
663 text_init(fontfile);
664 fb = fb_init(fbdev,mode,vt);
665 fb_catch_exit_signals();
666 fb_initcolors(fb,gray);
667 fb_switch_init();
668 switch_last = fb_switch_state;
669 fs_init_fb(15);
670
671 if (matrox)
672 if (-1 == gfx_init(fb))
673 matrox = 0;
674 if (matrox)
675 strcat(ng_v4l_conf," -y ");
676
677 grabber_init();
678 freq_init();
679 read_config(NULL,NULL,NULL);
680 if (0 != strlen(mixerdev)) {
681 struct ng_attribute *attr;
682 if (NULL != (attr = ng_mix_init(mixerdev,mixerctl)))
683 add_attrs(attr);
684 }
685
686 /* set hooks (command.c) */
687 update_title = new_title;
688 display_message = new_message;
689 set_capture_hook = do_capture;
690 exit_hook = do_exit;
691 fullscreen_hook = do_fullscreen;
692
693 tty_init();
694 memset(&act,0,sizeof(act));
695 act.sa_handler = ctrlc;
696 sigemptyset(&act.sa_mask);
697 sigaction(SIGINT,&act,&old);
698
699 /* init hardware */
700 attr_init();
701 audio_on();
702 audio_init();
703
704 /* build channel list */
705 parse_config();
706 channel_menu();
707
708 init_overlay();
709 if (optind+1 == argc) {
710 do_va_cmd(2,"setstation",argv[optind]);
711 } else {
712 if ((f_drv & CAN_TUNE) && 0 != (freq = drv->getfreq(h_drv))) {
713 for (i = 0; i < chancount; i++)
714 if (chanlist[i].freq == freq*1000/16) {
715 do_va_cmd(2,"setchannel",chanlist[i].name);
716 break;
717 }
718 }
719 if (-1 == cur_channel) {
720 if (count > 0)
721 do_va_cmd(2,"setstation","");
722 else
723 set_defaults();
724 }
725 }
726
727 /* keyboard, lirc + midi + joystick input support */
728 event_register_list(kbd_events);
729 lirc = lirc_tv_init();
730 js = joystick_tv_init(joydev);
731 #ifdef HAVE_ALSA
732 fb_midi.fd = -1;
733 if (midi) {
734 if (-1 != midi_open(&fb_midi, "fbtv"))
735 midi_connect(&fb_midi,midi);
736 }
737 #endif
738
739 fb_memset(fb_mem+fb_mem_offset,0,fb_fix.smem_len);
740 for (;!sig;) {
741 if ((fb_switch_state == FB_ACTIVE || keep_dma_on) && !quiet) {
742 /* clear first lines */
743 fb_memset(fb_mem+fb_mem_offset,0,f->height*fb_fix.line_length);
744 if (message[0] != '\0') {
745 strcpy(text,message);
746 } else {
747 sprintf(text,"Framebuffer TV - %s",default_title);
748 }
749 /* debugging + preformance monitoring */
750 switch (cur_capture) {
751 case CAPTURE_GRABDISPLAY:
752 sprintf(text+strlen(text), " - grab %d.%d fps",fps/5,(fps*2)%10);
753 break;
754 }
755 text_out(0,0,text);
756
757 if (dy > 0) {
758 /* display time */
759 time(&t);
760 strftime(text,16,"%H:%M",localtime(&t));
761 text_out(fb_var.xres - text_width(text) - f->width, 0, text);
762 }
763 }
764 if (switch_last != fb_switch_state)
765 console_switch();
766
767 t1 = time(NULL);
768 fps = 0;
769 message[0] = '\0';
770 for (;!sig;) {
771 FD_ZERO(&set);
772 FD_SET(0,&set);
773 fdmax = 1;
774 if (lirc != -1) {
775 FD_SET(lirc,&set);
776 fdmax = MAX(fdmax,lirc+1);
777 }
778 if (js != -1) {
779 FD_SET(js,&set);
780 fdmax = MAX(fdmax,js+1);
781 }
782 #ifdef HAVE_ALSA
783 if (fb_midi.fd != -1) {
784 FD_SET(fb_midi.fd,&set);
785 fdmax = MAX(fdmax,fb_midi.fd+1);
786 }
787 #endif
788 if (cur_capture == CAPTURE_GRABDISPLAY &&
789 (fb_switch_state == FB_ACTIVE || keep_dma_on)) {
790 fps++;
791 /* grab + convert frame */
792 if (NULL == (buf = ng_grabber_grab_image(0))) {
793 fprintf(stderr,"capturing image failed\n");
794 exit(1);
795 }
796 if (ch)
797 buf = ng_convert_frame(ch,NULL,buf);
798 /* blit frame */
799 dst = fb_mem +
800 dy * fb_fix.line_length +
801 dx * ((fb_var.bits_per_pixel+7)/8);
802 for (ui = 0; ui < buf->fmt.height; ui++) {
803 memcpy(dst, buf->data + ui*buf->fmt.bytesperline,
804 buf->fmt.bytesperline);
805 dst += fb_fix.line_length;
806 }
807 ng_release_video_buf(buf);
808 tv.tv_sec = 0;
809 tv.tv_usec = 0;
810 rc = select(fdmax,&set,NULL,NULL,&tv);
811 } else {
812 tv.tv_sec = 6;
813 tv.tv_usec = 0;
814 rc = select(fdmax,&set,NULL,NULL,&tv);
815 }
816 err = errno;
817 if (switch_last != fb_switch_state)
818 console_switch();
819 if (-1 == rc && EINTR == err) {
820 FD_ZERO(&set);
821 continue;
822 }
823 if (rc > 0)
824 break;
825 t2 = time(NULL);
826 if (t2 - t1 >= 5) {
827 keypad_timeout();
828 break;
829 }
830 }
831
832 if (FD_ISSET(0,&set)) {
833 /* keyboard input */
834 switch (key = getch()) {
835 case 27: /* ESC */
836 case 'q':
837 case 'Q':
838 sig=1;
839 break;
840 case 'x':
841 case 'X':
842 sig=1;
843 mute=0;
844 break;
845 case -1:
846 break;
847
848 #if 0 /* debug */
849 case 'y':
850 /* scaler_test(1); */
851 do_va_cmd(2,"capture","off");
852 do_va_cmd(2,"capture","grab");
853 break;
854 #endif
855
856 default:
857 event[0] = 0;
858 if (key > ' ' && key < 127) {
859 /* as is */
860 sprintf(event,"kbd-key-%c",key);
861 } else if (key >= KEY_F(0) && key <= KEY_F(12)) {
862 /* function keys */
863 sprintf(event,"kbd-key-f%d",key - KEY_F(0));
864 } else {
865 /* other special keys */
866 for (ui = 0; ui < NKEYTAB; ui++) {
867 if (keytab[ui].key == key)
868 break;
869 }
870 if (ui != NKEYTAB)
871 sprintf(event,"kbd-key-%s",keytab[ui].name);
872 }
873 if (0 != event[0]) {
874 event_dispatch(event);
875 } else {
876 sprintf(message,"unknown key: %d 0x%x ",key,key);
877 }
878
879 }
880 } /* if (FD_ISSET(0,&set)) */
881
882 if (lirc != -1 && FD_ISSET(lirc,&set)) {
883 /* lirc input */
884 if (-1 == lirc_tv_havedata()) {
885 fprintf(stderr,"lirc: connection lost\n");
886 close(lirc);
887 lirc = -1;
888 }
889 }
890
891 if (js != -1 && FD_ISSET(js,&set)) {
892 /* joystick input */
893 joystick_tv_havedata(js);
894 }
895
896 #ifdef HAVE_ALSA
897 if (fb_midi.fd != -1 && FD_ISSET(fb_midi.fd,&set)) {
898 /* midi input */
899 midi_read(&fb_midi);
900 midi_translate(&fb_midi);
901 }
902 #endif
903 }
904 do_va_cmd(2,"capture","off");
905 if (mute)
906 audio_off();
907 drv->close(h_drv);
908 if (fb_switch_state == FB_ACTIVE)
909 fb_memset(fb_mem+fb_mem_offset,0,fb_fix.smem_len);
910 tty_cleanup();
911 fb_cleanup();
912 exit(0);
913 }
914
|
This page was automatically generated by the
LXR engine.
|