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