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