1 /*
2 * common X11 stuff (mostly libXt level) moved here from main.c
3 *
4 * (c) 1997-2003 Gerd Knorr <kraxel@bytesex.org>
5 *
6 */
7
8 #define _GNU_SOURCE
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <string.h>
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <signal.h>
17 #include <sys/socket.h>
18 #include <sys/wait.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/utsname.h>
22 #include <netinet/in.h>
23 #include <netdb.h>
24 #include <pthread.h>
25
26 #if defined(__linux__)
27 # include <sys/ioctl.h>
28 # include "videodev.h"
29 #endif
30
31 #include "config.h"
32
33 #include <X11/Xlib.h>
34 #include <X11/Xproto.h>
35 #include <X11/Xatom.h>
36 #include <X11/Intrinsic.h>
37 #include <X11/Shell.h>
38 #include <X11/StringDefs.h>
39 #include <X11/cursorfont.h>
40 #include <X11/extensions/XShm.h>
41
42 #include "grab-ng.h"
43 #include "commands.h"
44 #include "sound.h"
45 #include "toolbox.h"
46 #include "xv.h"
47 #include "atoms.h"
48 #include "xt.h"
49 #include "x11.h"
50 #include "wmhooks.h"
51 #include "channel.h"
52 #include "capture.h"
53 #include "midictrl.h"
54 #include "lirc.h"
55 #include "joystick.h"
56 #include "vbi-data.h"
57 #include "blit.h"
58 #include "parseconfig.h"
59 #include "event.h"
60
61 /* jwz */
62 #include "remote.h"
63
64 /*----------------------------------------------------------------------*/
65
66 XtAppContext app_context;
67 Widget app_shell, tv;
68 Widget on_shell;
69 Display *dpy;
70 int stay_on_top = 0;
71
72 XVisualInfo vinfo;
73 Colormap colormap;
74
75 int have_dga = 0;
76 int have_vm = 0;
77 int have_randr = 0;
78 int fs = 0;
79
80 void *movie_state;
81 int movie_blit;
82
83 XtIntervalId zap_timer,scan_timer;
84
85 #ifdef HAVE_LIBXXF86VM
86 int vm_count;
87 XF86VidModeModeInfo **vm_modelines;
88 XF86VidModeModeLine vm_line;
89 int vm_dot;
90 #endif
91 #ifdef HAVE_LIBXINERAMA
92 XineramaScreenInfo *xinerama;
93 int nxinerama;
94 #endif
95 #ifdef HAVE_LIBXRANDR
96 XRRScreenSize *randr;
97 int nrandr;
98 int randr_evbase;
99 #endif
100
101 static Widget on_label;
102 static XtIntervalId title_timer, on_timer;
103 static char default_title[256] = "???";
104
105 static int zap_start,zap_fast;
106
107 /*--- args ----------------------------------------------------------------*/
108
109 struct ARGS args;
110
111 XtResource args_desc[] = {
112 /* name, class, type, size, offset, default_type, default_addr */
113 {
114 /* Strings */
115 "device",
116 XtCString, XtRString, sizeof(char*),
117 XtOffset(struct ARGS*,device),
118 XtRString, NULL
119 },{
120 "driver",
121 XtCString, XtRString, sizeof(char*),
122 XtOffset(struct ARGS*,driver),
123 XtRString, NULL
124 },{
125 "dspdev",
126 XtCString, XtRString, sizeof(char*),
127 XtOffset(struct ARGS*,dspdev),
128 XtRString, NULL
129 },{
130 "vbidev",
131 XtCString, XtRString, sizeof(char*),
132 XtOffset(struct ARGS*,vbidev),
133 XtRString, NULL
134 },{
135 "joydev",
136 XtCString, XtRString, sizeof(char*),
137 XtOffset(struct ARGS*,joydev),
138 XtRString, NULL
139 },{
140 "basename",
141 XtCString, XtRString, sizeof(char*),
142 XtOffset(struct ARGS*,basename),
143 XtRString, "snap"
144 },{
145 "conffile",
146 XtCString, XtRString, sizeof(char*),
147 XtOffset(struct ARGS*,conffile),
148 XtRString, NULL
149 },{
150 /* Integer */
151 "debug",
152 XtCValue, XtRInt, sizeof(int),
153 XtOffset(struct ARGS*,debug),
154 XtRString, ""
155 },{
156 "bpp",
157 XtCValue, XtRInt, sizeof(int),
158 XtOffset(struct ARGS*,bpp),
159 XtRString, ""
160 },{
161 "shift",
162 XtCValue, XtRInt, sizeof(int),
163 XtOffset(struct ARGS*,shift),
164 XtRString, ""
165 },{
166 "xvport",
167 XtCValue, XtRInt, sizeof(int),
168 XtOffset(struct ARGS*,xv_port),
169 XtRString, ""
170 },{
171 "parallel",
172 XtCValue, XtRInt, sizeof(int),
173 XtOffset(struct ARGS*,parallel),
174 XtRString, "1"
175 },{
176 "bufcount",
177 XtCValue, XtRInt, sizeof(int),
178 XtOffset(struct ARGS*,bufcount),
179 XtRString, "16"
180 },{
181 /* Boolean */
182 "remote",
183 XtCBoolean, XtRBoolean, sizeof(int),
184 XtOffset(struct ARGS*,remote),
185 XtRString, ""
186 },{
187 "readconfig",
188 XtCBoolean, XtRBoolean, sizeof(int),
189 XtOffset(struct ARGS*,readconfig),
190 XtRString, "1"
191 },{
192 "fullscreen",
193 XtCBoolean, XtRBoolean, sizeof(int),
194 XtOffset(struct ARGS*,fullscreen),
195 XtRString, ""
196 },{
197 "fbdev",
198 XtCBoolean, XtRBoolean, sizeof(int),
199 XtOffset(struct ARGS*,fbdev),
200 XtRString, ""
201 },{
202 "xv",
203 XtCBoolean, XtRBoolean, sizeof(int),
204 XtOffset(struct ARGS*,xv),
205 XtRString, "1"
206 },{
207 "xvVideo",
208 XtCBoolean, XtRBoolean, sizeof(int),
209 XtOffset(struct ARGS*,xv_video),
210 XtRString, "1"
211 },{
212 "xvImage",
213 XtCBoolean, XtRBoolean, sizeof(int),
214 XtOffset(struct ARGS*,xv_image),
215 XtRString, "1"
216 },{
217 "gl",
218 XtCBoolean, XtRBoolean, sizeof(int),
219 XtOffset(struct ARGS*,gl),
220 XtRString, "1"
221 },{
222 "vidmode",
223 XtCBoolean, XtRBoolean, sizeof(int),
224 XtOffset(struct ARGS*,vidmode),
225 XtRString, "1"
226 },{
227 "dga",
228 XtCBoolean, XtRBoolean, sizeof(int),
229 XtOffset(struct ARGS*,dga),
230 XtRString, "1"
231 },{
232 "randr",
233 XtCBoolean, XtRBoolean, sizeof(int),
234 XtOffset(struct ARGS*,randr),
235 XtRString, "1"
236 },{
237 "help",
238 XtCBoolean, XtRBoolean, sizeof(int),
239 XtOffset(struct ARGS*,help),
240 XtRString, ""
241 },{
242 "hwscan",
243 XtCBoolean, XtRBoolean, sizeof(int),
244 XtOffset(struct ARGS*,hwscan),
245 XtRString, ""
246 }
247 };
248
249 const int args_count = XtNumber(args_desc);
250
251 XrmOptionDescRec opt_desc[] = {
252 { "-c", "device", XrmoptionSepArg, NULL },
253 { "-device", "device", XrmoptionSepArg, NULL },
254 { "-driver", "driver", XrmoptionSepArg, NULL },
255 { "-C", "dspdev", XrmoptionSepArg, NULL },
256 { "-dspdev", "dspdev", XrmoptionSepArg, NULL },
257 { "-vbidev", "vbidev", XrmoptionSepArg, NULL },
258 { "-joydev", "joydev", XrmoptionSepArg, NULL },
259 { "-o", "basename", XrmoptionSepArg, NULL },
260 { "-outfile", "basename", XrmoptionSepArg, NULL },
261 { "-conffile", "conffile", XrmoptionSepArg, NULL },
262
263 { "-v", "debug", XrmoptionSepArg, NULL },
264 { "-debug", "debug", XrmoptionSepArg, NULL },
265 { "-b", "bpp", XrmoptionSepArg, NULL },
266 { "-bpp", "bpp", XrmoptionSepArg, NULL },
267 { "-shift", "shift", XrmoptionSepArg, NULL },
268 { "-xvport", "xvport", XrmoptionSepArg, NULL },
269 { "-parallel", "parallel", XrmoptionSepArg, NULL },
270 { "-bufcount", "bufcount", XrmoptionSepArg, NULL },
271
272 { "-remote", "remote", XrmoptionNoArg, "1" },
273 { "-n", "readconfig", XrmoptionNoArg, "" },
274 { "-noconf", "readconfig", XrmoptionNoArg, "" },
275 { "-f", "fullscreen", XrmoptionNoArg, "1" },
276 { "-fullscreen", "fullscreen", XrmoptionNoArg, "1" },
277 { "-hwscan", "hwscan", XrmoptionNoArg, "1" },
278 { "-fb", "fbdev", XrmoptionNoArg, "1" },
279
280 { "-xv", "xv", XrmoptionNoArg, "1" },
281 { "-noxv", "xv", XrmoptionNoArg, "" },
282 { "-xv-video", "xvVideo", XrmoptionNoArg, "1" },
283 { "-noxv-video", "xvVideo", XrmoptionNoArg, "" },
284 { "-xv-image", "xvImage", XrmoptionNoArg, "1" },
285 { "-noxv-image", "xvImage", XrmoptionNoArg, "" },
286 { "-gl", "gl", XrmoptionNoArg, "1" },
287 { "-nogl", "gl", XrmoptionNoArg, "" },
288
289 { "-vm", "vidmode", XrmoptionNoArg, "1" },
290 { "-novm", "vidmode", XrmoptionNoArg, "" },
291 { "-dga", "dga", XrmoptionNoArg, "1" },
292 { "-nodga", "dga", XrmoptionNoArg, "" },
293 { "-randr", "randr", XrmoptionNoArg, "1" },
294 { "-norandr", "randr", XrmoptionNoArg, "" },
295
296 { "-h", "help", XrmoptionNoArg, "1" },
297 { "-help", "help", XrmoptionNoArg, "1" },
298 { "--help", "help", XrmoptionNoArg, "1" },
299 };
300
301 const int opt_count = (sizeof(opt_desc)/sizeof(XrmOptionDescRec));
302 /*----------------------------------------------------------------------*/
303
304 Boolean
305 ExitWP(XtPointer client_data)
306 {
307 /* exit if the application is idle,
308 * i.e. all the DestroyCallback's are called.
309 */
310 exit(0);
311 }
312
313 void
314 ExitCB(Widget widget, XtPointer client_data, XtPointer calldata)
315 {
316 audio_off();
317 video_overlay(0);
318 video_close();
319 do_va_cmd(2,"capture", "off");
320 if (fs)
321 do_va_cmd(1,"fullscreen");
322 XSync(dpy,False);
323 drv->close(h_drv);
324 XtAppAddWorkProc(app_context,ExitWP, NULL);
325 XtDestroyWidget(app_shell);
326 }
327
328 void
329 do_exit(void)
330 {
331 ExitCB(NULL,NULL,NULL);
332 }
333
334 void
335 CloseMainAction(Widget widget, XEvent *event,
336 String *params, Cardinal *num_params)
337 {
338 if (NULL != event && event->type == ClientMessage) {
339 if (debug)
340 fprintf(stderr,"CloseMainAction: received %s message\n",
341 XGetAtomName(dpy,event->xclient.data.l[0]));
342 if ((Atom)event->xclient.data.l[0] == WM_DELETE_WINDOW) {
343 /* fall throuth -- popdown window */
344 } else {
345 /* whats this ?? */
346 return;
347 }
348 }
349 ExitCB(widget,NULL,NULL);
350 }
351
352 void
353 RemoteAction(Widget widget, XEvent * event,
354 String * params, Cardinal * num_params)
355 {
356 Atom type;
357 int format, argc;
358 unsigned int i;
359 char *argv[32];
360 unsigned long nitems, bytesafter;
361 unsigned char *args = NULL;
362
363 if (event->type == PropertyNotify) {
364 if (debug > 1)
365 fprintf(stderr,"PropertyNotify %s\n",
366 XGetAtomName(dpy,event->xproperty.atom));
367 if (event->xproperty.atom == _XAWTV_REMOTE &&
368 Success == XGetWindowProperty(dpy,
369 event->xproperty.window,
370 event->xproperty.atom,
371 0, (65536 / sizeof(long)),
372 True, XA_STRING,
373 &type, &format, &nitems, &bytesafter,
374 &args) &&
375 nitems != 0) {
376 for (i = 0, argc = 0; i <= nitems; i += strlen(args + i) + 1) {
377 if (i == nitems || args[i] == '\0') {
378 argv[argc] = NULL;
379 do_command(argc,argv);
380 argc = 0;
381 } else {
382 argv[argc++] = args+i;
383 }
384 }
385 XFree(args);
386 }
387 }
388 }
389
390 static void
391 zap_timeout(XtPointer client_data, XtIntervalId *id)
392 {
393 static int muted = 0;
394
395 if (zap_fast && !cur_attrs[ATTR_ID_MUTE]) {
396 /* mute for fast channel scan */
397 muted = 1;
398 do_va_cmd(2,"volume","mute","on");
399 }
400 /* pixit(); */
401 do_va_cmd(2,"setstation","next");
402 if (cur_sender != zap_start) {
403 zap_timer = XtAppAddTimeOut
404 (app_context, zap_fast ? CAP_TIME : ZAP_TIME, zap_timeout,NULL);
405 } else {
406 if(muted) {
407 /* unmute */
408 muted = 0;
409 do_va_cmd(2,"volume","mute","off");
410 }
411 }
412 }
413
414 void
415 ZapAction(Widget widget, XEvent *event,
416 String *params, Cardinal *num_params)
417 {
418 if (zap_timer) {
419 XtRemoveTimeOut(zap_timer);
420 zap_timer = 0;
421 #if 0
422 strcpy(title,"channel hopping off");
423 set_timer_title();
424 #endif
425 } else {
426 zap_start = (cur_sender == -1) ? 0 : cur_sender;
427 zap_fast = 0;
428 if (*num_params > 0) {
429 if (0 == strcasecmp(params[0],"fast"))
430 zap_fast = 1;
431 }
432 if (count)
433 zap_timer = XtAppAddTimeOut
434 (app_context, CAP_TIME, zap_timeout,NULL);
435 }
436 }
437
438 static void
439 scan_timeout(XtPointer client_data, XtIntervalId *id)
440 {
441 scan_timer = 0;
442
443 /* check */
444 if (!(f_drv & CAN_TUNE))
445 return;
446 if (drv->is_tuned(h_drv))
447 return;
448
449 do_va_cmd(2,"setchannel","next");
450 scan_timer = XtAppAddTimeOut
451 (app_context, SCAN_TIME, scan_timeout, NULL);
452 }
453
454 void
455 ScanAction(Widget widget, XEvent *event,
456 String *params, Cardinal *num_params)
457 {
458 if (channel_switch_hook)
459 channel_switch_hook();
460 do_va_cmd(2,"setchannel","next");
461 scan_timer = XtAppAddTimeOut
462 (app_context, SCAN_TIME, scan_timeout,NULL);
463 }
464
465 void
466 RatioAction(Widget widget, XEvent *event,
467 String *params, Cardinal *num_params)
468 {
469 int w,h;
470
471 if (2 != *num_params)
472 return;
473 w = atoi(params[0]);
474 h = atoi(params[1]);
475 ng_ratio_x = w;
476 ng_ratio_y = h;
477 do_va_cmd(2,"capture","off");
478 do_va_cmd(2,"capture","on");
479 }
480
481 /*--- onscreen display (fullscreen) --------------------------------------*/
482
483 void
484 create_onscreen(WidgetClass class)
485 {
486 on_shell = XtVaCreateWidget("onscreen",transientShellWidgetClass,
487 app_shell,
488 XtNoverrideRedirect,True,
489 XtNvisual,vinfo.visual,
490 XtNcolormap,colormap,
491 XtNdepth,vinfo.depth,
492 NULL);
493 on_label = XtVaCreateManagedWidget("label", class, on_shell,
494 NULL);
495 }
496
497 static void
498 popdown_onscreen(XtPointer client_data, XtIntervalId *id)
499 {
500 if (debug)
501 fprintf(stderr,"osd: hide\n");
502 XtPopdown(on_shell);
503 on_timer = 0;
504 }
505
506 static void
507 display_onscreen(char *title)
508 {
509 static int first = 1;
510 Dimension x,y;
511
512 if (!fs)
513 return;
514 if (!use_osd)
515 return;
516
517 if (debug)
518 fprintf(stderr,"osd: show (%s)\n",title);
519 XtVaGetValues(app_shell,XtNx,&x,XtNy,&y,NULL);
520 XtVaSetValues(on_shell,XtNx,x+osd_x,XtNy,y+osd_y,NULL);
521 toolkit_set_label(on_label,title);
522 XtPopup(on_shell, XtGrabNone);
523 if (wm_stay_on_top && stay_on_top > 0)
524 wm_stay_on_top(dpy,XtWindow(on_shell),1);
525 if (on_timer)
526 XtRemoveTimeOut(on_timer);
527 on_timer = XtAppAddTimeOut
528 (app_context, ONSCREEN_TIME, popdown_onscreen,NULL);
529
530 if (first) {
531 first = 0;
532 XDefineCursor(dpy, XtWindow(on_shell), no_ptr);
533 XDefineCursor(dpy, XtWindow(on_label), no_ptr);
534 }
535 }
536
537 /*----------------------------------------------------------------------*/
538
539 Boolean
540 rec_work(XtPointer client_data)
541 {
542 struct ng_video_buf *buf;
543
544 if (movie_blit) {
545 buf = NULL;
546 movie_grab_put_video(movie_state, &buf);
547 if (buf)
548 video_gd_blitframe(&vh,buf);
549 } else {
550 movie_grab_put_video(movie_state, NULL);
551 }
552 return False;
553 }
554
555 void
556 exec_done(int signal)
557 {
558 int pid,stat;
559
560 if (debug)
561 fprintf(stderr,"got sigchild\n");
562 pid = waitpid(-1,&stat,WUNTRACED|WNOHANG);
563 if (debug) {
564 if (-1 == pid) {
565 perror("waitpid");
566 } else if (0 == pid) {
567 fprintf(stderr,"oops: got sigchild and waitpid returns 0 ???\n");
568 } else if (WIFEXITED(stat)){
569 fprintf(stderr,"[%d]: normal exit (%d)\n",pid,WEXITSTATUS(stat));
570 } else if (WIFSIGNALED(stat)){
571 fprintf(stderr,"[%d]: %s\n",pid,strsignal(WTERMSIG(stat)));
572 } else if (WIFSTOPPED(stat)){
573 fprintf(stderr,"[%d]: %s\n",pid,strsignal(WSTOPSIG(stat)));
574 }
575 }
576 }
577
578 static void
579 exec_output(XtPointer data, int *fd, XtInputId * iproc)
580 {
581 char buffer[81];
582 int len;
583
584 switch (len = read(*fd,buffer,80)) {
585 case -1: /* error */
586 perror("read pipe");
587 /* fall */
588 case 0: /* EOF */
589 close(*fd);
590 XtRemoveInput(*iproc);
591 break;
592 default: /* got some bytes */
593 buffer[len] = 0;
594 fprintf(stderr,"%s",buffer);
595 break;
596 }
597 }
598
599 int
600 exec_x11(char **argv)
601 {
602 int p[2],pid,i;
603
604 if (debug) {
605 fprintf(stderr,"exec: \"%s\"",argv[0]);
606 for (i = 1; argv[i] != NULL; i++)
607 fprintf(stderr,", \"%s\"",argv[i]);
608 fprintf(stderr,"\n");
609 }
610 pipe(p);
611 switch (pid = fork()) {
612 case -1:
613 perror("fork");
614 return -1;
615 case 0:
616 /* child */
617 dup2(p[1],1);
618 dup2(p[1],2);
619 close(p[0]);
620 close(p[1]);
621 close(ConnectionNumber(dpy));
622 execvp(argv[0],argv);
623 fprintf(stderr,"exec %s: %s\n",argv[0],strerror(errno));
624 exit(1);
625 default:
626 /* parent */
627 close(p[1]);
628 XtAppAddInput(app_context, p[0], (XtPointer) XtInputReadMask,
629 exec_output, NULL);
630 break;
631 }
632 return pid;
633 }
634
635 void
636 exec_player(char *moviefile)
637 {
638 //static char *command = "xanim +f +Sr +Ze -Zr";
639 static char *command = "pia";
640 char *cmd;
641 char **argv;
642 int argc;
643
644 /* go! */
645 cmd = malloc(strlen(command)+strlen(moviefile)+5);
646 sprintf(cmd,"%s %s",command,moviefile);
647 argv = split_cmdline(cmd,&argc);
648 exec_x11(argv);
649 }
650
651 void
652 LaunchAction(Widget widget, XEvent *event,
653 String *params, Cardinal *num_params)
654 {
655 char **argv;
656 int i,argc;
657
658 if (*num_params != 1)
659 return;
660 for (i = 0; i < nlaunch; i++) {
661 if (0 == strcasecmp(params[0],launch[i].name))
662 break;
663 }
664 if (i == nlaunch)
665 return;
666
667 argv = split_cmdline(launch[i].cmdline,&argc);
668
669 switch (fork()) {
670 case -1:
671 perror("fork");
672 break;
673 case 0:
674 if (debug) {
675 fprintf(stderr,"[%d]: exec ",getpid());
676 for (i = 0; i < argc; i++) {
677 fprintf(stderr,"\"%s\" ",argv[i]);
678 }
679 fprintf(stderr,"\n");
680 }
681 execvp(argv[0],argv);
682 fprintf(stderr,"execvp %s: %s",argv[0],strerror(errno));
683 exit(1);
684 break;
685 default:
686 break;
687 }
688 }
689
690 /*------------------------------------------------------------------------*/
691
692 XtSignalId sig_id;
693
694 static void termsig_handler(XtPointer data, XtSignalId *id)
695 {
696 ExitCB(NULL,NULL,NULL);
697 }
698
699 static void
700 termsig(int signal)
701 {
702 if (debug)
703 fprintf(stderr,"received signal %d [%s]\n",signal,strsignal(signal));
704 XtNoticeSignal(sig_id);
705 }
706
707 static void
708 segfault(int signal)
709 {
710 fprintf(stderr,"[pid=%d] segfault catched, aborting\n",getpid());
711 abort();
712 }
713
714 void
715 xt_siginit(void)
716 {
717 struct sigaction act,old;
718
719 memset(&act,0,sizeof(act));
720 sigemptyset(&act.sa_mask);
721 act.sa_handler = exec_done;
722 sigaction(SIGCHLD,&act,&old);
723
724 sig_id = XtAppAddSignal(app_context,termsig_handler,NULL);
725 act.sa_handler = termsig;
726 sigaction(SIGINT,&act,&old);
727 sigaction(SIGTERM,&act,&old);
728
729 act.sa_handler = SIG_IGN;
730 sigaction(SIGPIPE,&act,&old);
731
732 if (debug) {
733 act.sa_handler = segfault;
734 sigaction(SIGSEGV,&act,&old);
735 fprintf(stderr,"main thread [pid=%d]\n",getpid());
736 }
737 }
738
739 /*----------------------------------------------------------------------*/
740
741 static XtIntervalId xscreensaver_timer;
742
743 static void
744 xscreensaver_timefunc(XtPointer clientData, XtIntervalId *id)
745 {
746 static int first = 1;
747 int status;
748 char *err;
749
750 if (debug)
751 fprintf(stderr,"xscreensaver_timefunc\n");
752 xscreensaver_timer = 0;
753 if (first) {
754 xscreensaver_init(dpy);
755 first = 0;
756 }
757 status = xscreensaver_command(dpy,XA_DEACTIVATE,0,debug,&err);
758 if (0 != status) {
759 if (debug)
760 fprintf(stderr,"xscreensaver_command: %s\n",err);
761 return;
762 }
763 xscreensaver_timer = XtAppAddTimeOut(app_context,60000,
764 xscreensaver_timefunc,NULL);
765 }
766
767 /*----------------------------------------------------------------------*/
768
769 #ifdef HAVE_LIBXXF86VM
770 static void
771 vidmode_timer(XtPointer clientData, XtIntervalId *id)
772 {
773 do_va_cmd(2,"capture", "on");
774 }
775
776 static void
777 set_vidmode(XF86VidModeModeInfo *mode)
778 {
779 if (CAPTURE_OVERLAY == cur_capture) {
780 do_va_cmd(2,"capture", "off");
781 XtAppAddTimeOut(app_context,VIDMODE_DELAY,vidmode_timer,NULL);
782 }
783 /* usleep(VIDMODE_DELAY*1000); */
784 if (debug)
785 fprintf(stderr,"switching mode: %d %d %d %d %d %d %d %d %d %d\n",
786 mode->dotclock,
787 mode->hdisplay,
788 mode->hsyncstart,
789 mode->hsyncend,
790 mode->htotal,
791 mode->vdisplay,
792 mode->vsyncstart,
793 mode->vsyncend,
794 mode->vtotal,
795 mode->flags);
796 XF86VidModeSwitchToMode(dpy,XDefaultScreen(dpy),mode);
797 XSync(dpy,False);
798 }
799
800 static void
801 do_vidmode_modeswitch(int fs_state, int *vp_width, int *vp_height)
802 {
803 static int vm_switched;
804 static XF86VidModeModeInfo *vm_current = NULL;
805 static XF86VidModeModeInfo *vm_fullscreen = NULL;
806 int i;
807
808 if (fs_state) {
809 /* enter fullscreen mode */
810 XF86VidModeGetModeLine(dpy,XDefaultScreen(dpy),&vm_dot,&vm_line);
811 XF86VidModeGetAllModeLines(dpy,XDefaultScreen(dpy),
812 &vm_count,&vm_modelines);
813 vm_fullscreen = NULL;
814 for (i = 0; i < vm_count; i++) {
815 if (fs_width == vm_modelines[i]->hdisplay &&
816 fs_height == vm_modelines[i]->vdisplay &&
817 vm_fullscreen == NULL)
818 vm_fullscreen = vm_modelines[i];
819 if (vm_line.hdisplay == vm_modelines[i]->hdisplay &&
820 vm_line.vdisplay == vm_modelines[i]->vdisplay &&
821 vm_current == NULL)
822 vm_current = vm_modelines[i];
823 }
824 if (debug) {
825 fprintf(stderr,"vm: current=%dx%d",
826 vm_current->hdisplay,vm_current->vdisplay);
827 if (vm_fullscreen)
828 fprintf(stderr,"fullscreen=%dx%d",
829 vm_fullscreen->hdisplay,vm_fullscreen->vdisplay);
830 fprintf(stderr,"\n");
831 }
832 if (vm_current && vm_fullscreen &&
833 vm_fullscreen->hdisplay != vm_current->hdisplay &&
834 vm_fullscreen->vdisplay != vm_current->vdisplay) {
835 set_vidmode(vm_fullscreen);
836 vm_switched = 1;
837 *vp_width = vm_fullscreen->hdisplay;
838 *vp_height = vm_fullscreen->vdisplay;
839 } else {
840 vm_switched = 0;
841 *vp_width = vm_current->hdisplay;
842 *vp_height = vm_current->vdisplay;
843 }
844 } else {
845 /* leave fullscreen mode */
846 if (vm_switched) {
847 set_vidmode(vm_current);
848 vm_switched = 0;
849 }
850 }
851 }
852 #endif
853
854 #ifdef HAVE_LIBXRANDR
855 static void
856 do_randr_modeswitch(int fs_state, int *vp_width, int *vp_height)
857 {
858 static SizeID normal;
859 Window root = RootWindow(dpy, DefaultScreen(dpy));
860 XRRScreenConfiguration *sc;
861 Rotation rotation;
862 SizeID current, new, i;
863
864 sc = XRRGetScreenInfo(dpy, root);
865 current = XRRConfigCurrentConfiguration(sc, &rotation);
866 new = current;
867 if (fs_state) {
868 /* enter fullscreen mode */
869 normal = current;
870 new = current;
871 for (i = 0; i < nrandr; i++) {
872 if (randr[i].width == fs_width &&
873 randr[i].height == fs_height) {
874 new = i;
875 break;
876 }
877 }
878 } else {
879 /* leave fullscreen mode */
880 new = normal;
881 }
882
883 if (new != current) {
884 /* switch mode */
885 if (debug)
886 fprintf(stderr, "randr: switch to %dx%d\n",
887 randr[new].width, randr[new].height);
888 XRRSetScreenConfig(dpy, sc, root, new, rotation, CurrentTime);
889 }
890 XRRFreeScreenConfigInfo(sc);
891
892 /* FIXME: change swidth / sheight instead */
893 *vp_width = randr[new].width;
894 *vp_height = randr[new].height;
895 }
896 #endif
897
898 static void
899 do_modeswitch(int fs_state, int *vp_width, int *vp_height)
900 {
901 *vp_width = swidth;
902 *vp_height = sheight;
903
904 #ifdef HAVE_LIBXXF86VM
905 if (have_vm)
906 do_vidmode_modeswitch(fs_state,vp_width,vp_height);
907 #endif
908 #ifdef HAVE_LIBXRANDR
909 if (!have_vm && have_randr)
910 do_randr_modeswitch(fs_state,vp_width,vp_height);
911 #endif
912 }
913
914 /*----------------------------------------------------------------------*/
915
916 Boolean
917 MyResize(XtPointer client_data)
918 {
919 /* needed for program-triggered resizes (fullscreen mode) */
920 video_new_size();
921 return TRUE;
922 }
923
924 static void
925 do_screensaver(int fs_state)
926 {
927 static int timeout,interval,prefer_blanking,allow_exposures;
928 #ifdef HAVE_LIBXDPMS
929 static BOOL dpms_on;
930 CARD16 dpms_state;
931 int dpms_dummy;
932 #endif
933
934 if (fs_state) {
935 /* fullscreen on -- disable screensaver */
936 XGetScreenSaver(dpy,&timeout,&interval,
937 &prefer_blanking,&allow_exposures);
938 XSetScreenSaver(dpy,0,0,DefaultBlanking,DefaultExposures);
939 #ifdef HAVE_LIBXDPMS
940 if ((DPMSQueryExtension(dpy, &dpms_dummy, &dpms_dummy)) &&
941 (DPMSCapable(dpy))) {
942 DPMSInfo(dpy, &dpms_state, &dpms_on);
943 DPMSDisable(dpy);
944 }
945 #endif
946 xscreensaver_timer = XtAppAddTimeOut(app_context,60000,
947 xscreensaver_timefunc,NULL);
948 } else {
949 /* fullscreen off -- enable screensaver */
950 XSetScreenSaver(dpy,timeout,interval,prefer_blanking,allow_exposures);
951 #ifdef HAVE_LIBXDPMS
952 if ((DPMSQueryExtension(dpy, &dpms_dummy, &dpms_dummy)) &&
953 (DPMSCapable(dpy)) && (dpms_on)) {
954 DPMSEnable(dpy);
955 }
956 #endif
957 if (xscreensaver_timer)
958 XtRemoveTimeOut(xscreensaver_timer);
959 }
960 }
961
962 void
963 do_fullscreen(void)
964 {
965 static Dimension x,y,w,h;
966 static int rpx,rpy;
967 static int warp_pointer;
968
969 Window root,child;
970 int wpx,wpy,mask;
971 unsigned int vp_width, vp_height;
972
973 if (use_wm_fullscreen && wm_fullscreen) {
974 /* full service for us, next to nothing to do */
975 fs = !fs;
976 if (debug)
977 fprintf(stderr,"fullscreen %s via netwm\n", fs ? "on" : "off");
978
979 do_modeswitch(fs,&vp_width,&vp_height);
980 XSync(dpy,False);
981 wm_fullscreen(dpy,XtWindow(app_shell),fs);
982
983 if (0 == fs && on_timer) {
984 XtPopdown(on_shell);
985 XtRemoveTimeOut(on_timer);
986 on_timer = 0;
987 }
988 do_screensaver(fs);
989 return;
990 }
991
992 if (fs) {
993 if (debug)
994 fprintf(stderr,"turning fs off (%dx%d+%d+%d)\n",w,h,x,y);
995 do_modeswitch(0,&vp_width,&vp_height);
996
997 if (on_timer) {
998 XtPopdown(on_shell);
999 XtRemoveTimeOut(on_timer);
1000 on_timer = 0;
1001 }
1002
1003 XtVaSetValues(app_shell,
1004 XtNwidthInc, WIDTH_INC,
1005 XtNheightInc,HEIGHT_INC,
1006 XtNx, x + fs_xoff,
1007 XtNy, y + fs_yoff,
1008 XtNwidth, w,
1009 XtNheight, h,
1010 NULL);
1011
1012 do_screensaver(0);
1013 if (warp_pointer)
1014 XWarpPointer(dpy, None, RootWindowOfScreen(XtScreen(tv)),
1015 0, 0, 0, 0, rpx, rpy);
1016 fs = 0;
1017 } else {
1018 int vp_x, vp_y;
1019
1020 if (debug)
1021 fprintf(stderr,"turning fs on\n");
1022 XQueryPointer(dpy, RootWindowOfScreen(XtScreen(tv)),
1023 &root, &child, &rpx, &rpy, &wpx, &wpy, &mask);
1024
1025 vp_x = 0;
1026 vp_y = 0;
1027 do_modeswitch(1,&vp_width,&vp_height);
1028 if (vp_width < sheight || vp_width < swidth) {
1029 /* move viewpoint, make sure the pointer is in there */
1030 warp_pointer = 1;
1031 XWarpPointer(dpy, None, RootWindowOfScreen(XtScreen(tv)),
1032 0, 0, 0, 0, vp_width/2, vp_height/2);
1033 #ifdef HAVE_LIBXXF86VM
1034 XF86VidModeSetViewPort(dpy,XDefaultScreen(dpy),0,0);
1035 #endif
1036 }
1037 XtVaGetValues(app_shell,
1038 XtNx, &x,
1039 XtNy, &y,
1040 XtNwidth, &w,
1041 XtNheight, &h,
1042 NULL);
1043
1044 #ifdef HAVE_LIBXINERAMA
1045 if (nxinerama) {
1046 /* check which physical screen we are visible on */
1047 int i;
1048 for (i = 0; i < nxinerama; i++) {
1049 if (x >= xinerama[i].x_org &&
1050 y >= xinerama[i].y_org &&
1051 x < xinerama[i].x_org + xinerama[i].width &&
1052 y < xinerama[i].y_org + xinerama[i].height) {
1053 vp_x = xinerama[i].x_org;
1054 vp_y = xinerama[i].y_org;
1055 vp_width = xinerama[i].width;
1056 vp_height = xinerama[i].height;
1057 break;
1058 }
1059 }
1060 }
1061 #endif
1062 if (debug)
1063 fprintf(stderr,"viewport: %dx%d+%d+%d\n",
1064 vp_width,vp_height,vp_x,vp_y);
1065
1066 XtVaSetValues(app_shell,
1067 XtNwidthInc, 1,
1068 XtNheightInc, 1,
1069 NULL);
1070 XtVaSetValues(app_shell,
1071 XtNx, (vp_x & 0xfffc) + fs_xoff,
1072 XtNy, vp_y + fs_yoff,
1073 XtNwidth, vp_width,
1074 XtNheight, vp_height,
1075 NULL);
1076
1077 XRaiseWindow(dpy, XtWindow(app_shell));
1078 do_screensaver(1);
1079 if (warp_pointer)
1080 XWarpPointer(dpy, None, XtWindow(tv), 0, 0, 0, 0, 30, 15);
1081 fs = 1;
1082 }
1083 XtAppAddWorkProc (app_context, MyResize, NULL);
1084 }
1085
1086 /*----------------------------------------------------------------------*/
1087
1088 static void
1089 title_timeout(XtPointer client_data, XtIntervalId *id)
1090 {
1091 keypad_timeout();
1092 XtVaSetValues(app_shell,XtNtitle,default_title,NULL);
1093 title_timer = 0;
1094 }
1095
1096 void
1097 new_title(char *txt)
1098 {
1099 strcpy(default_title,txt);
1100 XtVaSetValues(app_shell,
1101 XtNtitle,default_title,
1102 XtNiconName,default_title,
1103 NULL);
1104 display_onscreen(default_title);
1105
1106 if (title_timer) {
1107 XtRemoveTimeOut(title_timer);
1108 title_timer = 0;
1109 }
1110 }
1111
1112 void
1113 new_message(char *txt)
1114 {
1115 XtVaSetValues(app_shell,XtNtitle,txt,NULL);
1116 display_onscreen(txt);
1117 if (title_timer)
1118 XtRemoveTimeOut(title_timer);
1119 title_timer = XtAppAddTimeOut
1120 (app_context, TITLE_TIME, title_timeout,NULL);
1121 }
1122
1123 /*
1124 * mode = -1: check mode (just update the title)
1125 * mode = 0: set autodetect (and read back result)
1126 * mode > 0: set some mode
1127 */
1128 void
1129 change_audio(int mode)
1130 {
1131 struct ng_attribute *attr;
1132 const char *mname;
1133 char label[64];
1134
1135 attr = ng_attr_byid(attrs,ATTR_ID_AUDIO_MODE);
1136 if (NULL == attr)
1137 return;
1138
1139 if (-1 != mode)
1140 attr->write(attr,mode);
1141 if (-1 == mode || 0 == mode)
1142 mode = attr->read(attr);
1143
1144 mname = ng_attr_getstr(attr,mode);
1145 if (NULL == mname)
1146 mname = "???";
1147
1148 if (attr_notify)
1149 attr_notify(attr,mode);
1150
1151 sprintf(label,"%s (%s)",default_title,mname);
1152 XtVaSetValues(app_shell,XtNtitle,label,NULL);
1153 }
1154
1155 /*----------------------------------------------------------------------*/
1156
1157 void
1158 CommandAction(Widget widget, XEvent *event,
1159 String *params, Cardinal *num_params)
1160 {
1161 do_command(*num_params,params);
1162 }
1163
1164 void
1165 set_property(int freq, char *channel, char *name)
1166 {
1167 int len;
1168 char line[80];
1169
1170 len = sprintf(line,"%.3f",(float)freq/16)+1;
1171 len += sprintf(line+len,"%s",channel ? channel : "?") +1;
1172 len += sprintf(line+len,"%s",name ? name : "?") +1;
1173 XChangeProperty(dpy, XtWindow(app_shell),
1174 _XAWTV_STATION, XA_STRING,
1175 8, PropModeReplace,
1176 line, len);
1177 }
1178
1179 void command_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
1180 {
1181 struct DO_CMD *cmd = clientdata;
1182 do_command(cmd->argc,cmd->argv);
1183 }
1184
1185 void tv_expose_event(Widget widget, XtPointer client_data,
1186 XEvent *event, Boolean *d)
1187 {
1188 static GC gc;
1189 XGCValues values;
1190
1191 switch(event->type) {
1192 case Expose:
1193 if (debug)
1194 fprintf(stderr,"expose count=%d\n",
1195 event->xexpose.count);
1196 if (0 == event->xexpose.count && CAPTURE_OVERLAY == cur_capture) {
1197 if (f_drv & NEEDS_CHROMAKEY) {
1198 Dimension win_width, win_height;
1199 if (debug)
1200 fprintf(stderr,"expose: chromakey [%dx%d]\n",
1201 cur_tv_width, cur_tv_height);
1202 if (0 == gc) {
1203 XColor color;
1204 color.red = (ng_chromakey & 0x00ff0000) >> 8;
1205 color.green = (ng_chromakey & 0x0000ff00);
1206 color.blue = (ng_chromakey & 0x000000ff) << 8;
1207 XAllocColor(dpy,colormap,&color);
1208 values.foreground = color.pixel;
1209 gc = XCreateGC(dpy, XtWindow(widget), GCForeground,
1210 &values);
1211 }
1212 /* draw background for chroma keying */
1213 XtVaGetValues(widget, XtNwidth, &win_width,
1214 XtNheight, &win_height, NULL);
1215 XFillRectangle(dpy,XtWindow(widget),gc,
1216 (win_width - cur_tv_width) >> 1,
1217 (win_height - cur_tv_height) >> 1,
1218 cur_tv_width, cur_tv_height);
1219 }
1220 if (have_xv) {
1221 if (debug)
1222 fprintf(stderr,"expose: xv reblit\n");
1223 video_new_size();
1224 }
1225 }
1226 break;
1227 }
1228 }
1229
1230 void
1231 FilterAction(Widget widget, XEvent *event,
1232 String *params, Cardinal *num_params)
1233 {
1234 struct list_head *item;
1235 struct ng_filter *filter;
1236
1237 cur_filter = NULL;
1238 if (0 == *num_params)
1239 return;
1240 list_for_each(item,&ng_filters) {
1241 filter = list_entry(item, struct ng_filter, list);
1242 if (0 == strcasecmp(filter->name,params[0])) {
1243 cur_filter = filter;
1244 break;
1245 }
1246 }
1247 }
1248
1249 /*----------------------------------------------------------------------*/
1250
1251 void
1252 xfree_dga_init(Display *dpy)
1253 {
1254 #ifdef HAVE_LIBXXF86DGA
1255 int flags,foo,bar,ma,mi;
1256
1257 if (!do_overlay)
1258 return;
1259
1260 if (args.dga) {
1261 if (XF86DGAQueryExtension(dpy,&foo,&bar)) {
1262 XF86DGAQueryDirectVideo(dpy,XDefaultScreen(dpy),&flags);
1263 if (flags & XF86DGADirectPresent) {
1264 XF86DGAQueryVersion(dpy,&ma,&mi);
1265 if (debug)
1266 fprintf(stderr,"DGA version %d.%d\n",ma,mi);
1267 have_dga = 1;
1268 }
1269 }
1270 }
1271 #endif
1272 }
1273
1274 void
1275 xfree_vm_init(Display *dpy)
1276 {
1277 #ifdef HAVE_LIBXXF86VM
1278 int foo,bar,i,ma,mi;
1279
1280 if (!do_overlay)
1281 return;
1282
1283 if (args.vidmode) {
1284 if (XF86VidModeQueryExtension(dpy,&foo,&bar)) {
1285 XF86VidModeQueryVersion(dpy,&ma,&mi);
1286 if (debug)
1287 fprintf(stderr,"VidMode version %d.%d\n",ma,mi);
1288 have_vm = 1;
1289 XF86VidModeGetAllModeLines(dpy,XDefaultScreen(dpy),
1290 &vm_count,&vm_modelines);
1291 if (debug) {
1292 fprintf(stderr," available video mode(s):");
1293 for (i = 0; i < vm_count; i++) {
1294 fprintf(stderr," %dx%d",
1295 vm_modelines[i]->hdisplay,
1296 vm_modelines[i]->vdisplay);
1297 }
1298 fprintf(stderr,"\n");
1299 }
1300 }
1301 }
1302 #endif
1303 }
1304
1305 void
1306 xfree_randr_init(Display *dpy)
1307 {
1308 #ifdef HAVE_LIBXRANDR
1309 int bar,i;
1310
1311 if (args.randr) {
1312 if (XRRQueryExtension(dpy,&randr_evbase,&bar)) {
1313 randr = XRRSizes(dpy,DefaultScreen(dpy),&nrandr);
1314 if (nrandr > 0) {
1315 have_randr = 1;
1316 if (debug) {
1317 fprintf(stderr,"xrandr:");
1318 for (i = 0; i < nrandr; i++) {
1319 fprintf(stderr, " %dx%d",
1320 randr[i].width, randr[i].height);
1321 }
1322 fprintf(stderr,"\n");
1323 }
1324 }
1325 }
1326 }
1327 #endif
1328 }
1329
1330 void
1331 xfree_xinerama_init(Display *dpy)
1332 {
1333 #ifdef HAVE_LIBXINERAMA
1334 int foo,bar,i;
1335
1336 if (XineramaQueryExtension(dpy,&foo,&bar) &&
1337 XineramaIsActive(dpy)) {
1338 xinerama = XineramaQueryScreens(dpy,&nxinerama);
1339 for (i = 0; i < nxinerama; i++) {
1340 fprintf(stderr,"xinerama %d: %dx%d+%d+%d\n",
1341 xinerama[i].screen_number,
1342 xinerama[i].width,
1343 xinerama[i].height,
1344 xinerama[i].x_org,
1345 xinerama[i].y_org);
1346 }
1347 }
1348 #endif
1349 }
1350
1351 void
1352 grabber_init()
1353 {
1354 struct ng_video_fmt screen;
1355 void *base = NULL;
1356
1357 memset(&screen,0,sizeof(screen));
1358 #ifdef HAVE_LIBXXF86DGA
1359 if (have_dga) {
1360 int bar,fred;
1361 XF86DGAGetVideoLL(dpy,XDefaultScreen(dpy),(void*)&base,
1362 &screen.bytesperline,&bar,&fred);
1363 }
1364 #endif
1365 if (!do_overlay) {
1366 if (debug)
1367 fprintf(stderr,"x11: remote display (overlay disabled)\n");
1368 drv = ng_vid_open(args.device, args.driver, NULL, base, &h_drv);
1369 } else {
1370 screen.width = XtScreen(app_shell)->width;
1371 screen.height = XtScreen(app_shell)->height;
1372 screen.fmtid = x11_dpy_fmtid;
1373 screen.bytesperline *= ng_vfmt_to_depth[x11_dpy_fmtid]/8;
1374 if (debug)
1375 fprintf(stderr,"x11: %dx%d, %d bit/pixel, %d byte/scanline%s%s\n",
1376 screen.width,screen.height,
1377 ng_vfmt_to_depth[screen.fmtid],
1378 screen.bytesperline,
1379 have_dga ? ", DGA" : "",
1380 have_vm ? ", VidMode" : "");
1381 drv = ng_vid_open(args.device, args.driver, &screen, base, &h_drv);
1382 }
1383 if (NULL == drv) {
1384 fprintf(stderr,"no video grabber device available\n");
1385 exit(1);
1386 }
1387 f_drv = drv->capabilities(h_drv);
1388 add_attrs(drv->list_attrs(h_drv));
1389 }
1390
1391 void
1392 grabber_scan(void)
1393 {
1394 const struct ng_vid_driver *driver;
1395 void *handle;
1396 struct stat st;
1397 int n,i,fh,flags;
1398
1399 for (i = 0; ng_dev.video_scan[i] != NULL; i++) {
1400 if (-1 == lstat(ng_dev.video_scan[i],&st)) {
1401 if (ENOENT == errno)
1402 continue;
1403 fprintf(stderr,"%s: %s\n",ng_dev.video_scan[i],strerror(errno));
1404 continue;
1405 }
1406 fh = open(ng_dev.video_scan[i],O_RDWR);
1407 if (-1 == fh) {
1408 if (ENODEV == errno)
1409 continue;
1410 fprintf(stderr,"%s: %s\n",ng_dev.video_scan[i],strerror(errno));
1411 continue;
1412 }
1413 close(fh);
1414
1415 driver = ng_vid_open(ng_dev.video_scan[i], args.driver,
1416 NULL, NULL, &handle);
1417 if (NULL == driver) {
1418 fprintf(stderr,"%s: initialization failed\n",ng_dev.video_scan[i]);
1419 continue;
1420 }
1421 flags = driver->capabilities(handle);
1422 n = fprintf(stderr,"%s: OK",ng_dev.video_scan[i]);
1423 fprintf(stderr,"%*s[ -device %s ]\n",40-n,"",ng_dev.video_scan[i]);
1424 fprintf(stderr," type : %s\n",driver->name);
1425 if (driver->get_devname)
1426 fprintf(stderr," name : %s\n",driver->get_devname(handle));
1427 fprintf(stderr," flags: %s %s %s %s\n",
1428 (flags & CAN_OVERLAY) ? "overlay" : "",
1429 (flags & CAN_CAPTURE) ? "capture" : "",
1430 (flags & CAN_TUNE) ? "tuner" : "",
1431 (flags & NEEDS_CHROMAKEY) ? "chromakey" : "");
1432 driver->close(handle);
1433 fprintf(stderr,"\n");
1434 }
1435 exit(0);
1436 }
1437
1438 void
1439 x11_check_remote()
1440 {
1441 #if defined(HAVE_GETNAMEINFO) && defined(HAVE_SOCKADDR_STORAGE)
1442 int fd = ConnectionNumber(dpy);
1443 struct sockaddr_storage ss;
1444 char me[INET6_ADDRSTRLEN+1];
1445 char peer[INET6_ADDRSTRLEN+1];
1446 char port[17];
1447 int length;
1448
1449 if (debug)
1450 fprintf(stderr, "check if the X-Server is local ... ");
1451
1452 /* me */
1453 length = sizeof(ss);
1454 if (-1 == getsockname(fd,(struct sockaddr*)&ss,&length)) {
1455 perror("getsockname");
1456 return;
1457 }
1458 if (debug)
1459 fprintf(stderr,"*");
1460
1461 /* catch unix sockets on FreeBSD */
1462 if (0 == length || ss.ss_family == AF_UNIX) {
1463 if (debug)
1464 fprintf(stderr, " ok (unix socket)\n");
1465 return;
1466 }
1467
1468 getnameinfo((struct sockaddr*)&ss,length,
1469 me,INET6_ADDRSTRLEN,port,16,
1470 NI_NUMERICHOST | NI_NUMERICSERV);
1471 if (debug)
1472 fprintf(stderr,"*");
1473
1474 /* peer */
1475 length = sizeof(ss);
1476 if (-1 == getpeername(fd,(struct sockaddr*)&ss,&length)) {
1477 perror("getsockname");
1478 return;
1479 }
1480 if (debug)
1481 fprintf(stderr,"*");
1482
1483 getnameinfo((struct sockaddr*)&ss,length,
1484 peer,INET6_ADDRSTRLEN,port,16,
1485 NI_NUMERICHOST | NI_NUMERICSERV);
1486 if (debug)
1487 fprintf(stderr,"*");
1488
1489 if (debug)
1490 fprintf(stderr," ok\nx11 socket: me=%s, server=%s\n",me,peer);
1491 if (0 != strcmp(me,peer))
1492 /* different hosts => assume remote display */
1493 do_overlay = 0;
1494 #endif
1495 return;
1496 }
1497
1498 void x11_misc_init(Display *dpy)
1499 {
1500 fcntl(ConnectionNumber(dpy),F_SETFD,FD_CLOEXEC);
1501 }
1502
1503 /*----------------------------------------------------------------------*/
1504
1505 void
1506 visual_init(char *n1, char *n2)
1507 {
1508 Visual *visual;
1509 XVisualInfo *vinfo_list;
1510 int n;
1511
1512 /* look for a useful visual */
1513 visual = x11_find_visual(XtDisplay(app_shell));
1514 vinfo.visualid = XVisualIDFromVisual(visual);
1515 vinfo_list = XGetVisualInfo(dpy, VisualIDMask, &vinfo, &n);
1516 vinfo = vinfo_list[0];
1517 XFree(vinfo_list);
1518 if (visual != DefaultVisualOfScreen(XtScreen(app_shell))) {
1519 fprintf(stderr,"switching visual (0x%lx)\n",vinfo.visualid);
1520 colormap = XCreateColormap(dpy,RootWindowOfScreen(XtScreen(app_shell)),
1521 vinfo.visual,AllocNone);
1522 XtDestroyWidget(app_shell);
1523 app_shell = XtVaAppCreateShell(n1,n2,
1524 applicationShellWidgetClass, dpy,
1525 XtNvisual,vinfo.visual,
1526 XtNcolormap,colormap,
1527 XtNdepth, vinfo.depth,
1528 NULL);
1529 } else {
1530 colormap = DefaultColormapOfScreen(XtScreen(app_shell));
1531 }
1532 x11_init_visual(XtDisplay(app_shell),&vinfo);
1533 }
1534
1535 void
1536 v4lconf_init()
1537 {
1538 if (!do_overlay)
1539 return;
1540
1541 strcpy(ng_v4l_conf,"v4l-conf");
1542 if (!args.debug)
1543 strcat(ng_v4l_conf," -q");
1544 if (args.fbdev)
1545 strcat(ng_v4l_conf," -f");
1546 if (args.shift)
1547 sprintf(ng_v4l_conf+strlen(ng_v4l_conf)," -s %d",args.shift);
1548 if (args.bpp)
1549 sprintf(ng_v4l_conf+strlen(ng_v4l_conf)," -b %d",args.bpp);
1550 if (args.device)
1551 sprintf(ng_v4l_conf+strlen(ng_v4l_conf)," -c %s",args.device);
1552 }
1553
1554 static void
1555 usage(void)
1556 {
1557 fprintf(stderr,
1558 "\n"
1559 "usage: xawtv [ options ] [ station ]\n"
1560 "options:\n"
1561 " -h -help print this text\n"
1562 " -v -debug n debug level n, n = [0..2]\n"
1563 " -remote assume remote display\n"
1564 " -n -noconf don't read the config file\n"
1565 " -m -nomouse startup with mouse pointer disabled\n"
1566 " -f -fullscreen startup in fullscreen mode\n"
1567 #ifdef HAVE_LIBXXF86DGA
1568 " -(no)dga enable/disable DGA extention\n"
1569 #endif
1570 #ifdef HAVE_LIBXXF86VM
1571 " -(no)vm enable/disable VidMode extention\n"
1572 #endif
1573 #ifdef HAVE_LIBXRANDR
1574 " -(no)randr enable/disable Xrandr extention\n"
1575 #endif
1576 #ifdef HAVE_LIBXV
1577 " -(no)xv enable/disable Xvideo extention altogether\n"
1578 " -(no)xv-video enable/disable Xvideo extention (for video only,\n"
1579 " i.e. XvPutVideo() calls)\n"
1580 " -(no)xv-image enable/disable Xvideo extention (for image scaling\n"
1581 " only, i.e. XvPutImage() calls)\n"
1582 #endif
1583 #ifdef HAVE_GL
1584 " -(no)gl enable/disable OpenGL\n"
1585 #endif
1586 " -b -bpp n color depth of the display is n (n=24,32)\n"
1587 " -o -outfile file filename base for snapshots\n"
1588 " -c -device file use <file> as video4linux device\n"
1589 " -C -dspdev file use <file> as audio (oss) device\n"
1590 " -vbidev file use <file> as vbi device\n"
1591 " -joydev file use <file> as joystick device\n"
1592 " -shift x shift display by x bytes\n"
1593 " -fb let fb (not X) set up v4l device\n"
1594 " -parallel n use n compression threads\n"
1595 " -bufcount n use n video buffers\n"
1596 " -hwscan print a list of available devices.\n"
1597 "station:\n"
1598 " this is one of the stations listed in $HOME/.xawtv\n"
1599 "\n"
1600 "Check the manual page for a more detailed description.\n"
1601 "\n"
1602 "--\n"
1603 "Gerd Knorr <kraxel@bytesex.org>\n");
1604 }
1605
1606 void
1607 hello_world(char *name)
1608 {
1609 struct utsname uts;
1610
1611 if (0 == geteuid() && 0 != getuid()) {
1612 fprintf(stderr,"%s *must not* be installed suid root\n",name);
1613 exit(1);
1614 }
1615
1616 uname(&uts);
1617 fprintf(stderr,"This is %s-%s, running on %s/%s (%s)\n",
1618 name,VERSION,uts.sysname,uts.machine,uts.release);
1619 }
1620
1621 void
1622 handle_cmdline_args(void)
1623 {
1624 XtGetApplicationResources(app_shell,&args,
1625 args_desc,args_count,
1626 NULL,0);
1627 if (args.help) {
1628 usage();
1629 exit(0);
1630 }
1631 snapbase = args.basename;
1632 debug = args.debug;
1633 ng_debug = args.debug;
1634
1635 if (0 == args.xv) {
1636 args.xv_video = 0;
1637 args.xv_image = 0;
1638 }
1639 if (NULL == args.dspdev)
1640 args.dspdev = ng_dev.dsp;
1641 if (NULL == args.vbidev)
1642 args.vbidev = ng_dev.vbi;
1643 if (NULL == args.device) {
1644 args.device = ng_dev.video;
1645 } else {
1646 args.xv_video = 0;
1647 }
1648 if (0 != args.xv_port)
1649 args.xv_video = 1;
1650 }
1651
1652 int
1653 x11_ctrl_alt_backspace(Display *dpy)
1654 {
1655 fprintf(stderr,"game over\n");
1656 if (debug)
1657 abort();
1658 audio_off();
1659 video_overlay(0);
1660 video_close();
1661 drv->close(h_drv);
1662 exit(0);
1663 }
1664
1665 /*----------------------------------------------------------------------*/
1666
1667 static int mouse_visible;
1668 static XtIntervalId mouse_timer;
1669
1670 static void
1671 mouse_timeout(XtPointer clientData, XtIntervalId *id)
1672 {
1673 Widget widget = clientData;
1674 if (debug > 1)
1675 fprintf(stderr,"xt: pointer hide\n");
1676 if (XtWindow(widget))
1677 XDefineCursor(dpy, XtWindow(widget), no_ptr);
1678 mouse_visible = 0;
1679 mouse_timer = 0;
1680 }
1681
1682 void
1683 mouse_event(Widget widget, XtPointer client_data, XEvent *event, Boolean *d)
1684 {
1685 if (!mouse_visible) {
1686 if (debug > 1)
1687 fprintf(stderr,"xt: pointer show\n");
1688 if (XtWindow(widget))
1689 XDefineCursor(dpy, XtWindow(widget), left_ptr);
1690 mouse_visible = 1;
1691 }
1692 if (mouse_timer)
1693 XtRemoveTimeOut(mouse_timer);
1694 mouse_timer = XtAppAddTimeOut(app_context, 1000, mouse_timeout,widget);
1695 }
1696
1697 /*----------------------------------------------------------------------*/
1698
1699 #if TT
1700 static char *vtx_colors[8] = { "black", "red", "green", "yellow",
1701 "blue", "magenta", "cyan", "white" };
1702 #endif
1703
1704 #ifdef HAVE_ZVBI
1705 static XtInputId x11_vbi_input;
1706 static struct vbi_state *x11_vbi;
1707 static int x11_vbi_page;
1708 char x11_vbi_station[64];
1709
1710 #if 0
1711 static struct TEXTELEM*
1712 vtx_to_tt(struct vt_page *vtp)
1713 {
1714 static struct TEXTELEM tt[VTX_COUNT];
1715 int t,x,y,color,lcolor;
1716 struct fmt_page pg[1];
1717 struct fmt_char l[W+2];
1718 #define L (l+1)
1719
1720 t = 0;
1721 fmt_page(fmt,pg,vtp);
1722 memset(tt,0,sizeof(tt));
1723 for (y = 0; y < H; y++) {
1724 for (x = 0; x < W; x++) {
1725 struct fmt_char c = pg->data[y][x];
1726 switch (c.ch) {
1727 case 0x00:
1728 case 0xa0:
1729 c.ch = ' ';
1730 break;
1731 case 0x7f:
1732 c.ch = '*';
1733 break;
1734 case BAD_CHAR:
1735 c.ch = '?';
1736 break;
1737 default:
1738 if (c.attr & EA_GRAPHIC)
1739 c.ch = '#';
1740 break;
1741 }
1742 L[x] = c;
1743 }
1744
1745 /* delay fg and attr changes as far as possible */
1746 for (x = 0; x < W; ++x)
1747 if (L[x].ch == ' ') {
1748 L[x].fg = L[x-1].fg;
1749 L[x].attr = L[x-1].attr;
1750 }
1751
1752 /* move fg and attr changes to prev bg change point */
1753 for (x = W-1; x >= 0; x--)
1754 if (L[x].ch == ' ' && L[x].bg == L[x+1].bg) {
1755 L[x].fg = L[x+1].fg;
1756 L[x].attr = L[x+1].attr;
1757 }
1758
1759 /* now render the line */
1760 lcolor = -1;
1761 tt[t].line = y;
1762 tt[t].len = 0;
1763 for (x = 0; x < W; x++) {
1764 color = (L[x].fg&0x0f) * 10 + (L[x].bg&0x0f);
1765 if (color != lcolor) {
1766 if (-1 != lcolor)
1767 if (tt[t].len) {
1768 t++;
1769 tt[t].line = y;
1770 }
1771 lcolor = color;
1772 }
1773 tt[t].str[tt[t].len++] = L[x].ch;
1774 tt[t].fg = vtx_colors[L[x].fg&0x0f];
1775 tt[t].bg = vtx_colors[L[x].bg&0x0f];
1776 }
1777 if (tt[t].len)
1778 t++;
1779 }
1780 return tt;
1781 }
1782
1783 #define EMPTY_VBI_LINE " "
1784 static struct TEXTELEM*
1785 tt_pick_subtitle(struct TEXTELEM *tt)
1786 {
1787 int i;
1788
1789 /* skip header line */
1790 while (tt->len && 0 == tt->line)
1791 tt++;
1792 /* rm empty lines (from top) */
1793 while (tt->len &&
1794 0 == strcmp(tt->str,EMPTY_VBI_LINE))
1795 tt++;
1796
1797 if (tt->len) {
1798 /* seek to end */
1799 for (i = 0; tt[i].len != 0; i++)
1800 ;
1801 /* rm empty lines (from bottom) */
1802 while (0 == strcmp(tt[i-1].str,EMPTY_VBI_LINE))
1803 i--;
1804 tt[i].len = 0;
1805 }
1806
1807 return tt;
1808 }
1809
1810 static void
1811 dump_tt(struct TEXTELEM *tt)
1812 {
1813 int i,lastline = 0;
1814
1815 lastline = tt[0].line;
1816 for (i = 0; tt[i].len > 0; i++) {
1817 if (tt[i].line != lastline) {
1818 lastline = tt[i].line;
1819 fprintf(stderr,"\n");
1820 }
1821 fprintf(stderr,"[%s,%s,%d]%s",
1822 tt[i].fg ? tt[i].fg : "def",
1823 tt[i].bg ? tt[i].bg : "def",
1824 tt[i].line,tt[i].str);
1825 }
1826 fprintf(stderr,"\n");
1827 }
1828 #endif
1829
1830 static void
1831 x11_vbi_event(struct vbi_event *ev, void *user)
1832 {
1833 struct vbi_page pg;
1834 struct vbi_rect rect;
1835
1836 switch (ev->type) {
1837 case VBI_EVENT_NETWORK:
1838 strcpy(x11_vbi_station,ev->ev.network.name);
1839 break;
1840 case VBI_EVENT_TTX_PAGE:
1841 if (ev->ev.ttx_page.pgno == x11_vbi_page) {
1842 if (debug)
1843 fprintf(stderr,"got vtx page %03x\n",ev->ev.ttx_page.pgno);
1844 if (vtx_subtitle) {
1845 vbi_fetch_vt_page(x11_vbi->dec,&pg,
1846 ev->ev.ttx_page.pgno,
1847 ev->ev.ttx_page.subno,
1848 VBI_WST_LEVEL_1p5,25,1);
1849 vbi_find_subtitle(&pg,&rect);
1850 if (25 == rect.y1)
1851 vtx_subtitle(NULL,NULL);
1852 else
1853 vtx_subtitle(&pg,&rect);
1854 }
1855 }
1856 break;
1857 }
1858 }
1859
1860 static void x11_vbi_data(XtPointer data, int *fd, XtInputId *iproc)
1861 {
1862 struct vbi_state *vbi = data;
1863 vbi_hasdata(vbi);
1864 }
1865
1866 int
1867 x11_vbi_start(char *device)
1868 {
1869 if (NULL != x11_vbi)
1870 return 0;
1871
1872 if (NULL == device)
1873 device = ng_dev.vbi;
1874
1875 x11_vbi = vbi_open(device, debug, 0);
1876 if (NULL == x11_vbi) {
1877 fprintf(stderr,"open %s: %s\n",device,strerror(errno));
1878 return -1;
1879 }
1880 vbi_event_handler_add(x11_vbi->dec,~0,x11_vbi_event,x11_vbi);
1881 x11_vbi_input = XtAppAddInput(app_context,x11_vbi->fd,
1882 (XtPointer) XtInputReadMask,
1883 x11_vbi_data,x11_vbi);
1884 if (debug)
1885 fprintf(stderr,"x11_vbi_start\n");
1886 return 0;
1887 }
1888
1889 /* try to use the vbi device to see whenever we have a station or not.
1890 * failing that, fallback to the libng grabber driver. */
1891 int
1892 x11_vbi_tuned(void)
1893 {
1894 #if defined(__linux__)
1895 struct video_tuner tuner;
1896
1897 if (NULL == x11_vbi)
1898 return drv->is_tuned(h_drv);
1899 memset(&tuner,0,sizeof(tuner));
1900 if (-1 != ioctl(x11_vbi->fd,VIDIOCGTUNER,&tuner))
1901 return tuner.signal ? 1 : 0;
1902 #endif
1903 return drv->is_tuned(h_drv);
1904 }
1905
1906 void
1907 x11_vbi_stop(void)
1908 {
1909 if (NULL == x11_vbi)
1910 return;
1911 XtRemoveInput(x11_vbi_input);
1912 vbi_event_handler_remove(x11_vbi->dec,x11_vbi_event);
1913 vbi_close(x11_vbi);
1914 x11_vbi = NULL;
1915 if (debug)
1916 fprintf(stderr,"x11_vbi_stop\n");
1917 }
1918
1919 void
1920 VtxAction(Widget widget, XEvent *event,
1921 String *params, Cardinal *num_params)
1922 {
1923 if (0 == *num_params)
1924 return;
1925 if (0 == strcasecmp(params[0],"start")) {
1926 if (2 >= *num_params)
1927 sscanf(params[1],"%x",&x11_vbi_page);
1928 if (debug)
1929 fprintf(stderr,"subtitles page: %x\n",x11_vbi_page);
1930 x11_vbi_start(args.vbidev);
1931 }
1932 if (0 == strcasecmp(params[0],"stop")) {
1933 x11_vbi_page = 0;
1934 x11_vbi_stop();
1935 #if TT
1936 if (vtx_message)
1937 vtx_message(NULL);
1938 #endif
1939 #ifdef HAVE_ZVBI
1940 if (vtx_subtitle)
1941 vtx_subtitle(NULL,NULL);
1942 #endif
1943 }
1944 }
1945 #endif
1946
1947 /* ---------------------------------------------------------------------- */
1948 /* control via lirc / midi / joystick */
1949
1950 static int xt_lirc;
1951
1952 static void
1953 xt_lirc_data(XtPointer data, int *fd, XtInputId *iproc)
1954 {
1955 if (debug)
1956 fprintf(stderr,"lirc_input triggered\n");
1957 if (-1 == lirc_tv_havedata()) {
1958 fprintf(stderr,"lirc: connection lost\n");
1959 XtRemoveInput(*iproc);
1960 close(*fd);
1961 }
1962 }
1963
1964 int xt_lirc_init(void)
1965 {
1966 if (-1 != (xt_lirc = lirc_tv_init()))
1967 XtAppAddInput(app_context,xt_lirc,(XtPointer)XtInputReadMask,
1968 xt_lirc_data,NULL);
1969 return 0;
1970 }
1971
1972 #ifdef HAVE_ALSA
1973 static struct midi_handle xt_midi;
1974
1975 static void
1976 xt_midi_data(XtPointer data, int *fd, XtInputId *iproc)
1977 {
1978 midi_read(&xt_midi);
1979 midi_translate(&xt_midi);
1980 }
1981 #endif
1982
1983 int xt_midi_init(char *dev)
1984 {
1985 if (NULL == dev)
1986 return -1;
1987
1988 #ifdef HAVE_ALSA
1989 memset(&xt_midi,0,sizeof(xt_midi));
1990 if (-1 == midi_open(&xt_midi, "xawtv"))
1991 return -1;
1992 midi_connect(&xt_midi,dev);
1993 XtAppAddInput(app_context,xt_midi.fd,(XtPointer) XtInputReadMask,
1994 xt_midi_data,NULL);
1995 return 0;
1996 #else
1997 fprintf(stderr,"midi: not compiled in, sorry\n");
1998 return -1;
1999 #endif
2000 }
2001
2002 static int xt_joystick;
2003
2004 static void
2005 xt_joystick_data(XtPointer data, int *fd, XtInputId *iproc)
2006 {
2007 joystick_tv_havedata(xt_joystick);
2008 }
2009
2010 int xt_joystick_init(void)
2011 {
2012 if (-1 != (xt_joystick = joystick_tv_init(args.joydev)))
2013 XtAppAddInput(app_context,xt_joystick,(XtPointer)XtInputReadMask,
2014 xt_joystick_data,NULL);
2015 return 0;
2016 }
2017
2018 /* ---------------------------------------------------------------------- */
2019
2020 void xt_kbd_init(Widget tv)
2021 {
2022 char **list,key[32],str[128];
2023
2024 list = cfg_list_entries("eventmap");
2025 if (NULL == list)
2026 return;
2027
2028 for (; *list != NULL; list++) {
2029 if (1 != sscanf(*list,"kbd-key-%31s",key))
2030 continue;
2031 sprintf(str,"<Key>%s: Event(%s)",key,*list);
2032 XtOverrideTranslations(tv,XtParseTranslationTable(str));
2033 }
2034 }
2035
2036 void
2037 EventAction(Widget widget, XEvent *event,
2038 String *params, Cardinal *num_params)
2039 {
2040 if (0 != *num_params)
2041 event_dispatch(params[0]);
2042 }
2043
2044 /* ---------------------------------------------------------------------- */
2045
2046 Cursor left_ptr;
2047 Cursor menu_ptr;
2048 Cursor qu_ptr;
2049 Cursor no_ptr;
2050
2051 Pixmap bm_yes;
2052 Pixmap bm_no;
2053
2054 static unsigned char bm_yes_data[] = {
2055 /* -------- -------- */ 0x00,
2056 /* -------- -------- */ 0x00,
2057 /* ------xx xx------ */ 0x18,
2058 /* ----xxxx xxxx---- */ 0x3c,
2059 /* ----xxxx xxxx---- */ 0x3c,
2060 /* ------xx xx------ */ 0x18,
2061 /* -------- -------- */ 0x00,
2062 /* -------- -------- */ 0x00
2063 };
2064
2065 static unsigned char bm_no_data[] = { 0,0,0,0, 0,0,0,0 };
2066
2067 void
2068 create_pointers(Widget app_shell)
2069 {
2070 XColor white,red,dummy;
2071 Screen *scr;
2072
2073 left_ptr = XCreateFontCursor(dpy,XC_left_ptr);
2074 menu_ptr = XCreateFontCursor(dpy,XC_right_ptr);
2075 qu_ptr = XCreateFontCursor(dpy,XC_question_arrow);
2076 scr = DefaultScreenOfDisplay(dpy);
2077 if (vinfo.depth > 1) {
2078 if (XAllocNamedColor(dpy,colormap,"white",&white,&dummy) &&
2079 XAllocNamedColor(dpy,colormap,"red",&red,&dummy)) {
2080 XRecolorCursor(dpy,left_ptr,&red,&white);
2081 XRecolorCursor(dpy,menu_ptr,&red,&white);
2082 XRecolorCursor(dpy,qu_ptr,&red,&white);
2083 }
2084 }
2085 }
2086
2087 void
2088 create_bitmaps(Widget app_shell)
2089 {
2090 XColor black, dummy;
2091
2092 bm_yes = XCreateBitmapFromData(dpy, XtWindow(app_shell),
2093 bm_yes_data, 8,8);
2094 bm_no = XCreateBitmapFromData(dpy, XtWindow(app_shell),
2095 bm_no_data, 8,8);
2096
2097 XAllocNamedColor(dpy,colormap,"black",&black,&dummy);
2098 no_ptr = XCreatePixmapCursor(dpy, bm_no, bm_no,
2099 &black, &black,
2100 0, 0);
2101 }
2102
2103 /* ---------------------------------------------------------------------- */
2104
2105 int xt_handle_pending(Display *dpy)
2106 {
2107 XEvent event;
2108
2109 if (debug)
2110 fprintf(stderr,"xt: handle_pending: start ...\n");
2111 XFlush(dpy);
2112 while (True == XCheckMaskEvent(dpy, ~0, &event))
2113 XtDispatchEvent(&event);
2114 if (debug)
2115 fprintf(stderr,"xt: handle_pending: ... done\n");
2116 return 0;
2117 }
2118
2119 /* ---------------------------------------------------------------------- */
2120
2121 int xt_vm_randr_input_init(Display *dpy)
2122 {
2123 /* vidmode / randr */
2124 if (debug)
2125 fprintf(stderr,"xt: checking for randr extention ...\n");
2126 xfree_randr_init(dpy);
2127 if (debug)
2128 fprintf(stderr,"xt: checking for vidmode extention ...\n");
2129 xfree_vm_init(dpy);
2130
2131 /* input */
2132 if (debug)
2133 fprintf(stderr,"xt: checking for lirc ...\n");
2134 xt_lirc_init();
2135 if (debug)
2136 fprintf(stderr,"xt: checking for joystick ...\n");
2137 xt_joystick_init();
2138 if (debug)
2139 fprintf(stderr,"xt: checking for midi ...\n");
2140 xt_midi_init(midi);
2141 if (debug)
2142 fprintf(stderr,"xt: adding kbd hooks ...\n");
2143 xt_kbd_init(tv);
2144
2145 return 0;
2146 }
2147
2148 int xt_main_loop()
2149 {
2150 XEvent event;
2151
2152 if (debug)
2153 fprintf(stderr,"xt: enter main event loop... \n");
2154 signal(SIGHUP,SIG_IGN); /* don't really need a tty ... */
2155
2156 for (;;) {
2157 if (XtAppGetExitFlag(app_context))
2158 break;
2159 XtAppNextEvent(app_context, &event);
2160 if (True == XtDispatchEvent(&event))
2161 continue;
2162 }
2163 return 0;
2164 }
2165
|
This page was automatically generated by the
LXR engine.
|