1 #include "config.h"
2 #ifndef HAVE_LIBXV
3 #include "stdio.h"
4 #include "stdlib.h"
main(void)5 int main(void)
6 {puts("Compiled without Xvideo extention support, sorry.");exit(0);}
7 #else
8 /*
9  * put a TV image to the root window - requires Xvideo
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <signal.h>
16 #include <string.h>
17 #include <sys/types.h>
18 #include <sys/wait.h>
19 
20 #include <X11/Xlib.h>
21 #include <X11/Xutil.h>
22 #include <X11/StringDefs.h>
23 #include <X11/Xatom.h>
24 #include <X11/extensions/Xv.h>
25 #include <X11/extensions/Xvlib.h>
26 
27 #include "vroot.h"
28 #include "atoms.h"
29 #include "parseconfig.h"
30 
31 #define SDIMOF(array)  ((signed int)(sizeof(array)/sizeof(array[0])))
32 
33 int     port=-1,bye=0,termsig=0,verbose=0;
34 GC      gc;
35 
36 XvAdaptorInfo        *ai;
37 XvEncodingInfo       *ei;
38 XvAttribute          *at;
39 XvImageFormatValues  *fo;
40 
41 static char *reasons[] = {
42     "XvStarted",
43     "XvStopped",
44     "XvBusy",
45     "XvPreempted",
46     "XvHardError",
47 };
48 
49 static char *events[] = {
50     "0", "1",
51     "KeyPress",
52     "KeyRelease",
53     "ButtonPress",
54     "ButtonRelease",
55     "MotionNotify",
56     "EnterNotify",
57     "LeaveNotify",
58     "FocusIn",
59     "FocusOut",
60     "KeymapNotify",
61     "Expose",
62     "GraphicsExpose",
63     "NoExpose",
64     "VisibilityNotify",
65     "CreateNotify",
66     "DestroyNotify",
67     "UnmapNotify",
68     "MapNotify",
69     "MapRequest",
70     "ReparentNotify",
71     "ConfigureNotify",
72     "ConfigureRequest",
73     "GravityNotify",
74     "ResizeRequest",
75     "CirculateNotify",
76     "CirculateRequest",
77     "PropertyNotify",
78     "SelectionClear",
79     "SelectionRequest",
80     "SelectionNotify",
81     "ColormapNotify",
82     "ClientMessage",
83     "MappingNotify"
84 };
85 
station_list(FILE * fp)86 static void station_list(FILE *fp)
87 {
88     char filename[100];
89     char **list;
90 
91     sprintf(filename,"%.*s/%s",(int)sizeof(filename)-8,
92 	    getenv("HOME"),".xawtv");
93     cfg_parse_file(CONFIGFILE);
94     cfg_parse_file(filename);
95 
96     for (list = cfg_list_sections(); *list != NULL; list++) {
97 	if (0 == strcmp(*list,"defaults")) continue;
98 	if (0 == strcmp(*list,"global"))   continue;
99 	if (0 == strcmp(*list,"launch"))   continue;
100 	if (0 == strcmp(*list,"eventmap")) continue;
101 	fprintf(fp,"\t\"%s\" EXEC v4lctl setstation \"%s\"\n",
102 		*list,*list);
103     }
104 }
105 
wm_menu(FILE * fp)106 static void wm_menu(FILE *fp)
107 {
108     fprintf(fp,"\"TV stations\" MENU\n");
109     station_list(fp);
110     fprintf(fp,"\"TV stations\" END\n");
111 }
112 
video_blit(Display * dpy,Window win)113 static void video_blit(Display *dpy, Window win)
114 {
115     XWindowAttributes wts;
116 
117     XGetWindowAttributes(dpy, win, &wts);
118 #if 0
119     XClearArea(dpy,win,0,0,0,0,False);
120     XvStopVideo(dpy,port,win);
121 #endif
122     XvPutVideo(dpy,port,win,gc,
123 	       0,0,wts.width,wts.height,
124 	       0,0,wts.width,wts.height);
125     if (verbose)
126 	fprintf(stderr,"XvPutVideo(win=0x%lx,w=%d,h=%d)\n",
127 		win,wts.width,wts.height);
128 }
129 
130 static void
catch_sig(int signal)131 catch_sig(int signal)
132 {
133     termsig = signal;
134     if (verbose)
135 	fprintf(stderr,"received signal %d [%s]\n",
136 		termsig,sys_siglist[termsig]);
137 }
138 
usage(FILE * fp)139 static void usage(FILE *fp)
140 {
141     fprintf(fp,
142 	    "rootv is a simple TV application.  Uses and requires Xvideo.\n"
143 	    "Most settings must be tweaked done using some other tool,\n"
144 	    "v4lctl for example.\n"
145 	    "\n"
146 	    "options:\n"
147 	    "  -help          print this text\n"
148 	    "  -verbose       be verbose\n"
149 	    "  -root          put video onto the root window instead of\n"
150 	    "                 creating a new window.\n"
151 	    "  -id <win>      put video into the window <win> instead of\n"
152 	    "                 creating a new window.\n"
153 	    "  -station <st>  tune station <st> (just calls v4lctl)\n"
154 	    "  -no-mute       don't toggle mute on start/exit.\n"
155 	    "  -port <n>      use Xvideo port <n>.\n"
156 	    "  -bg            fork into background.\n"
157 	    "  -wm            print WindowMaker menu, to set all stations\n"
158 	    "                 listed in ~/.xawtv using v4lctl.\n"
159 	    "\n");
160 }
161 
162 int
main(int argc,char * argv[])163 main(int argc, char *argv[])
164 {
165     struct sigaction act,old;
166     Display *dpy;
167     Screen  *scr;
168     Window win = -1;
169     XWindowAttributes wts;
170     KeySym keysym;
171     char c;
172 
173     int ver, rel, req, ev, err, dummy;
174     int adaptors,attributes;
175     int i,bg,newwin,do_mute,have_mute,grab;
176 
177     dpy = XOpenDisplay(NULL);
178     scr = DefaultScreenOfDisplay(dpy);
179     init_atoms(dpy);
180 
181     bg = 0;
182     do_mute = 1;
183     have_mute = 0;
184     newwin = 1;
185 
186     while (argc > 1) {
187 	if (0 == strcmp(argv[1],"-wm")) {
188 	    /* windowmaker menu */
189 	    wm_menu(stdout);
190 	    exit(0);
191 	}
192 	if (0 == strcmp(argv[1],"-h") ||
193 	    0 == strcmp(argv[1],"-help") ||
194 	    0 == strcmp(argv[1],"--help")) {
195 	    usage(stdout);
196 	    exit(0);
197 	}
198 	if (argc > 2  &&  (0 == strcmp(argv[1],"-id") ||
199 			   0 == strcmp(argv[1],"-window-id"))) {
200 	    sscanf(argv[2],"%li",&win);
201 	    newwin = 0;
202 	    if (verbose)
203 		fprintf(stderr,"using window id 0x%lx\n",win);
204 	    argc-=2;
205 	    argv+=2;
206 	} else if (argc > 2 && 0 == strcmp(argv[1],"-port")) {
207 	    sscanf(argv[2],"%i",&port);
208 	    argc-=2;
209 	    argv+=2;
210 	} else if (argc > 2 && 0 == strcmp(argv[1],"-station")) {
211 	    if (0 == fork()) {
212 		execlp("v4lctl","v4lctl","setstation",argv[2],NULL);
213 		exit(1);
214 	    } else {
215 		wait(&dummy);
216 	    }
217 	    argc-=2;
218 	    argv+=2;
219 	} else if (0 == strcmp(argv[1],"-root")) {
220 	    win = RootWindowOfScreen(scr);
221 	    newwin = 0;
222 	    if (verbose)
223 		fprintf(stderr,"using root window [0x%lx]\n",win);
224 	    argc--;
225 	    argv++;
226 	} else if (0 == strcmp(argv[1],"-bg")) {
227 	    bg = 1;
228 	    argc--;
229 	    argv++;
230 	} else if (0 == strcmp(argv[1],"-verbose")) {
231 	    verbose = 1;
232 	    argc--;
233 	    argv++;
234 	} else if (0 == strcmp(argv[1],"-no-mute")) {
235 	    do_mute = 0;
236 	    argc--;
237 	    argv++;
238 	} else {
239 	    fprintf(stderr,"unknown arg: \"%s\"\n",argv[1]);
240 	    exit(1);
241 	}
242     }
243 
244     /* init X11 */
245     if (newwin) {
246 	win = XCreateSimpleWindow(dpy,RootWindowOfScreen(scr),
247 				  0,0,640,480,1,
248 				  BlackPixelOfScreen(scr),
249 				  BlackPixelOfScreen(scr));
250 	XChangeProperty(dpy, win, WM_PROTOCOLS, XA_ATOM, 32,
251 			PropModeReplace,
252 			(unsigned char *) &WM_DELETE_WINDOW, 1);
253     }
254     XGetWindowAttributes (dpy, win, &wts);
255     XSelectInput(dpy, win, wts.your_event_mask |
256 		 KeyPressMask | StructureNotifyMask | ExposureMask);
257 
258     /* query+print Xvideo properties */
259     if (Success != XvQueryExtension(dpy,&ver,&rel,&req,&ev,&err)) {
260 	puts("Server does'nt support Xvideo");
261 	exit(1);
262     }
263     if (Success != XvQueryAdaptors(dpy,DefaultRootWindow(dpy),&adaptors,&ai)) {
264 	puts("Oops: XvQueryAdaptors failed");
265 	exit(1);
266     }
267     if (verbose)
268 	fprintf(stderr,"%d adaptors available.\n",adaptors);
269     for (i = 0; i < adaptors; i++) {
270 	if (verbose)
271 	    fprintf(stderr,"  port=%ld name=\"%s\"\n",ai[i].base_id,ai[i].name);
272 
273 	/* video adaptor ? */
274 	if ((ai[i].type & XvInputMask) &&
275 	    (ai[i].type & XvVideoMask) &&
276 	    (port == -1)) {
277 	    port = ai[i].base_id;
278 	}
279     }
280     if (adaptors > 0)
281 	XvFreeAdaptorInfo(ai);
282     if (-1 == port) {
283 	fprintf(stderr,"no Xvideo port found\n");
284 	exit(1);
285     }
286 
287     /* grab Xvideo port */
288     grab = 0;
289     for (i = 0; !grab && i < 3; i++) {
290 	switch (XvGrabPort(dpy,port,CurrentTime)) {
291 	case Success:
292 	    if (verbose)
293 		fprintf(stderr,"grabbed Xv port\n");
294 	    grab=1;
295 	    break;
296 	case XvAlreadyGrabbed:
297 	    if (verbose)
298 		fprintf(stderr,"Xv port already grabbed\n");
299 	    sleep(1);
300 	    break;
301 	default:
302 	    fprintf(stderr,"Xv port grab: Huh?\n");
303 	    exit(1);
304 	}
305     }
306     if (!grab) {
307 	fprintf(stderr,"can't grab Xv port\n");
308 	exit(1);
309     }
310 
311     at = XvQueryPortAttributes(dpy,port,&attributes);
312     for (i = 0; i < attributes; i++) {
313 	if (0 == strcmp("XV_MUTE",at[i].name))
314 	    have_mute = 1;
315     }
316     gc = XCreateGC(dpy,win,0,NULL);
317 
318     /* fork into background, but keep tty */
319     if (bg)
320 	if (fork())
321 	    exit(0);
322 
323     /* catch signals */
324     memset(&act,0,sizeof(act));
325     sigemptyset(&act.sa_mask);
326     act.sa_handler = catch_sig;
327     sigaction(SIGINT,&act,&old);
328     sigaction(SIGHUP,&act,&old);
329     sigaction(SIGTERM,&act,&old);
330 
331     /* put video to the window */
332     if (newwin)
333 	XMapWindow(dpy,win);
334     if (verbose)
335 	fprintf(stderr,"starting video\n");
336     video_blit(dpy,win);
337     if (do_mute && have_mute)
338 	XvSetPortAttribute(dpy,port,XV_MUTE,0);
339 
340     /* receive events */
341     XvSelectPortNotify(dpy, port, 1);
342     XvSelectVideoNotify(dpy, win, 1);
343 
344     /* main loop */
345     for (;!bye && !termsig;) {
346 	int rc;
347 	fd_set set;
348 	XEvent event;
349 
350 	if (False == XCheckMaskEvent(dpy, ~0, &event)) {
351 	    /* wait for x11 events, make sure that signals are catched */
352 	    XFlush(dpy);
353 	    FD_ZERO(&set);
354 	    FD_SET(ConnectionNumber(dpy),&set);
355 	    rc = select(ConnectionNumber(dpy)+1,&set,NULL,NULL,NULL);
356 	    if (-1 == rc)
357 		if (verbose)
358 		    perror("... select");
359 	    continue;
360 	}
361 
362 	if (event.type > ev) {
363 	    /* Xvideo extention event */
364 	    switch (event.type-ev) {
365 	    case XvVideoNotify:
366 	    {
367 		XvVideoNotifyEvent *xve = (XvVideoNotifyEvent*)&event;
368 		if (verbose)
369 		    fprintf(stderr,"XvVideoNotify, reason=%s, exiting\n",
370 			    reasons[xve->reason]);
371 #if 0
372 		bye=1;
373 #endif
374 		break;
375 	    }
376 	    case XvPortNotify:
377 	    {
378 		XvPortNotifyEvent *xpe = (XvPortNotifyEvent*)&event;
379 		if (verbose)
380 		    fprintf(stderr,"XvPortNotify: %s=%ld\n",
381 			    XGetAtomName(dpy,xpe->attribute),xpe->value);
382 		break;
383 	    }
384 	    }
385 	} else {
386 	    /* other event */
387 	    switch (event.type) {
388 	    case KeyPress:
389 		c = 0;
390 		XLookupString (&event.xkey, &c, 1, &keysym, 0);
391 		if (verbose)
392 		    fprintf(stderr,"key: %c\n",c);
393 		switch (c) {
394 		case 'q':
395 		case 'Q':
396 		case 3:   /* ^C  */
397 		case 27:  /* ESC */
398 		    bye = 1;
399 		}
400 		break;
401 	    case ClientMessage:
402 		if (event.xclient.message_type    == WM_PROTOCOLS &&
403 		    (Atom)event.xclient.data.l[0] == WM_DELETE_WINDOW)
404 		    bye = 1;
405 		break;
406 	    case Expose:
407 		if (event.xexpose.count)
408 		    break;
409 		/* fall througth */
410 	    case ConfigureNotify:
411 		video_blit(dpy,win);
412 		break;
413 	    default:
414 		if (verbose) {
415 		    if (event.type < SDIMOF(events))
416 			fprintf(stderr,"ev: %s\n",events[event.type]);
417 		    else
418 			fprintf(stderr,"ev: #%d\n",event.type);
419 		}
420 	    }
421 	}
422     }
423     if (verbose && termsig)
424 	fprintf(stderr,"exiting on signal %d [%s]\n",
425 		termsig,sys_siglist[termsig]);
426     if (do_mute && have_mute)
427 	XvSetPortAttribute(dpy,port,XV_MUTE,1);
428     XvStopVideo(dpy,port,win);
429     XvUngrabPort(dpy,port,CurrentTime);
430     XClearArea(dpy,win,0,0,0,0,True);
431     XCloseDisplay(dpy);
432     if (verbose)
433 	fprintf(stderr,"bye\n");
434 
435     /* keep compiler happy */
436     exit(0);
437 }
438 
439 #endif
440