1 /*
2 * main.c for xawtv -- a TV application
3 *
4 * (c) 1997-2002 Gerd Knorr <kraxel@bytesex.org>
5 *
6 */
7
8 #define _GNU_SOURCE
9
10 #include "config.h"
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <errno.h>
18 #include <math.h>
19 #include <signal.h>
20 #include <fcntl.h>
21 #include <pthread.h>
22 #include <sys/time.h>
23 #include <sys/socket.h>
24 #include <sys/wait.h>
25 #include <sys/stat.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
31 #include <X11/Xproto.h>
32 #include <X11/Xmd.h>
33 #include <X11/Intrinsic.h>
34 #include <X11/StringDefs.h>
35 #include <X11/Xatom.h>
36 #include <X11/Shell.h>
37 #include <X11/Xaw/XawInit.h>
38 #include <X11/Xaw/Paned.h>
39 #include <X11/Xaw/Command.h>
40 #include <X11/Xaw/Scrollbar.h>
41 #include <X11/Xaw/Viewport.h>
42 #include <X11/Xaw/Box.h>
43 #include <X11/Xaw/Dialog.h>
44 #include <X11/Xaw/AsciiText.h>
45 #include <X11/extensions/XShm.h>
46
47 #include "grab-ng.h"
48 #include "writefile.h"
49
50 #include "sound.h"
51 #include "channel.h"
52 #include "commands.h"
53 #include "frequencies.h"
54 #include "xv.h"
55 #include "capture.h"
56 #include "atoms.h"
57 #include "xt.h"
58 #include "x11.h"
59 #include "toolbox.h"
60 #include "complete.h"
61 #include "wmhooks.h"
62 #include "conf.h"
63 #include "blit.h"
64 #include "vbi-data.h"
65 #include "vbi-x11.h"
66
67 #define LABEL_WIDTH "16"
68 #define BOOL_WIDTH "24"
69
70 /*--- public variables ----------------------------------------------------*/
71
72 static String fallback_ressources[] = {
73 #include "Xawtv.h"
74 NULL
75 };
76
77 Widget opt_shell, opt_paned, chan_shell, conf_shell, str_shell;
78 Widget launch_shell, launch_paned;
79 Widget c_freq,c_cap;
80 Widget s_bright,s_color,s_hue,s_contrast,s_volume;
81 Widget chan_viewport, chan_box;
82 Pixmap tv_pix;
83 struct vbi_window *vtx;
84
85 int have_config = 0;
86 XtIntervalId audio_timer;
87 int debug = 0;
88
89 char modename[64];
90 char *progname;
91
92 XtWorkProcId rec_work_id;
93
94 /* movie params / setup */
95 Widget w_movie_status;
96 Widget w_movie_driver;
97
98 Widget w_movie_fvideo;
99 Widget w_movie_video;
100 Widget w_movie_fps;
101 Widget w_movie_size;
102
103 Widget w_movie_flabel;
104 Widget w_movie_faudio;
105 Widget w_movie_audio;
106 Widget w_movie_rate;
107
108 struct STRTAB *m_movie_driver;
109 struct STRTAB *m_movie_audio;
110 struct STRTAB *m_movie_video;
111
112 struct ng_writer *movie_driver = NULL;
113 unsigned int i_movie_driver = 0;
114 unsigned int movie_audio = 0;
115 unsigned int movie_video = 0;
116 unsigned int movie_fps = 12000;
117 unsigned int movie_rate = 44100;
118
119 static struct STRTAB m_movie_fps[] = {
120 { 2000, " 2.0 fps" },
121 { 3000, " 3.0 fps" },
122 { 5000, " 5.0 fps" },
123 { 8000, " 8.0 fps" },
124 { 10000, "10.0 fps" },
125 { 12000, "12.0 fps" },
126 { 15000, "15.0 fps" },
127 { 18000, "18.0 fps" },
128 { 20000, "20.0 fps" },
129 { 23976, "23.976 fps" },
130 { 24000, "24.0 fps" },
131 { 25000, "25.0 fps" },
132 { 29970, "29.970 fps" },
133 { 30000, "30.0 fps" },
134 { -1, NULL },
135 };
136 static struct STRTAB m_movie_rate[] = {
137 { 8000, " 8000" },
138 { 11025, "11025" },
139 { 22050, "22050" },
140 { 44100, "44100" },
141 { 48000, "48000" },
142 { -1, NULL },
143 };
144
145 struct xaw_attribute {
146 struct ng_attribute *attr;
147 Widget cmd,scroll;
148 struct xaw_attribute *next;
149 };
150 static struct xaw_attribute *xaw_attrs;
151
152 #define MOVIE_DRIVER "movie driver"
153 #define MOVIE_AUDIO "audio format"
154 #define MOVIE_VIDEO "video format"
155 #define MOVIE_FPS "frames/sec"
156 #define MOVIE_RATE "sample rate"
157 #define MOVIE_SIZE "video size"
158
159 /* fwd decl */
160 void change_audio(int mode);
161 void watch_audio(XtPointer data, XtIntervalId *id);
162
163 /*-------------------------------------------------------------------------*/
164
165 static struct MY_TOPLEVELS {
166 char *name;
167 Widget *shell;
168 int *check;
169 int first;
170 int mapped;
171 } my_toplevels [] = {
172 { "options", &opt_shell },
173 { "channels", &chan_shell, &count },
174 { "config", &conf_shell, },
175 { "streamer", &str_shell },
176 { "launcher", &launch_shell, &nlaunch }
177 };
178 #define TOPLEVELS (sizeof(my_toplevels)/sizeof(struct MY_TOPLEVELS))
179
180 struct STRTAB *cmenu = NULL;
181
182 struct DO_AC {
183 int argc;
184 char *name;
185 char *argv[8];
186 };
187
188 /*--- actions -------------------------------------------------------------*/
189
190 /* conf.c */
191 extern void create_confwin(void);
192 extern void conf_station_switched(void);
193 extern void conf_list_update(void);
194
195 void CloseMainAction(Widget, XEvent*, String*, Cardinal*);
196 void ScanAction(Widget, XEvent*, String*, Cardinal*);
197 void ChannelAction(Widget, XEvent*, String*, Cardinal*);
198 void StayOnTop(Widget, XEvent*, String*, Cardinal*);
199 void PopupAction(Widget, XEvent*, String*, Cardinal*);
200
201 static XtActionsRec actionTable[] = {
202 { "CloseMain", CloseMainAction },
203 { "Scan", ScanAction },
204 { "Channel", ChannelAction },
205 { "Remote", RemoteAction },
206 { "Zap", ZapAction },
207 { "Complete", CompleteAction },
208 { "Help", help_AC },
209 { "StayOnTop", StayOnTop },
210 { "Launch", LaunchAction },
211 { "Popup", PopupAction },
212 { "Command", CommandAction },
213 { "Autoscroll", offscreen_scroll_AC },
214 { "Ratio", RatioAction },
215 #ifdef HAVE_ZVBI
216 { "Vtx", VtxAction },
217 #endif
218 { "Event", EventAction },
219 };
220
221 static struct STRTAB cap_list[] = {
222 { CAPTURE_OFF, "off" },
223 { CAPTURE_OVERLAY, "overlay" },
224 { CAPTURE_GRABDISPLAY, "grabdisplay" },
225 { -1, NULL, },
226 };
227
228 /*--- exit ----------------------------------------------------------------*/
229
230 void
231 PopupAction(Widget widget, XEvent *event,
232 String *params, Cardinal *num_params)
233 {
234 Dimension h;
235 unsigned int i;
236 int mh;
237
238 /* which window we are talking about ? */
239 if (*num_params > 0) {
240 for (i = 0; i < TOPLEVELS; i++) {
241 if (0 == strcasecmp(my_toplevels[i].name,params[0]))
242 break;
243 }
244 } else {
245 for (i = 0; i < TOPLEVELS; i++) {
246 if (*(my_toplevels[i].shell) == widget)
247 break;
248 }
249 }
250 if (i == TOPLEVELS) {
251 fprintf(stderr,"PopupAction: oops: shell widget not found (%s)\n",
252 (*num_params > 0) ? params[0] : "-");
253 return;
254 }
255
256 /* Message from WM ??? */
257 if (NULL != event && event->type == ClientMessage) {
258 if (debug)
259 fprintf(stderr,"%s: received %s message\n",
260 my_toplevels[i].name,
261 XGetAtomName(dpy,event->xclient.data.l[0]));
262 if ((Atom)event->xclient.data.l[0] == WM_DELETE_WINDOW) {
263 /* fall throuth -- popdown window */
264 } else {
265 /* whats this ?? */
266 return;
267 }
268 }
269
270 /* check if window should be displayed */
271 if (NULL != my_toplevels[i].check)
272 if (0 == *(my_toplevels[i].check))
273 return;
274
275 /* popup/down window */
276 if (my_toplevels[i].mapped) {
277 XtPopdown(*(my_toplevels[i].shell));
278 my_toplevels[i].mapped = 0;
279 } else {
280 XtPopup(*(my_toplevels[i].shell), XtGrabNone);
281 if (wm_stay_on_top && stay_on_top > 0)
282 wm_stay_on_top(dpy,XtWindow(*(my_toplevels[i].shell)),1);
283 my_toplevels[i].mapped = 1;
284 if (!my_toplevels[i].first) {
285 XSetWMProtocols(XtDisplay(*(my_toplevels[i].shell)),
286 XtWindow(*(my_toplevels[i].shell)),
287 &WM_DELETE_WINDOW, 1);
288 mh = h = 0;
289 XtVaGetValues(*(my_toplevels[i].shell),
290 XtNmaxHeight,&mh,
291 XtNheight,&h,
292 NULL);
293 if (mh > 0 && h > mh) {
294 if (debug)
295 fprintf(stderr,"height fixup: %d => %d\n",h,mh);
296 XtVaSetValues(*(my_toplevels[i].shell),XtNheight,mh,NULL);
297 }
298 my_toplevels[i].first = 1;
299 }
300 }
301 }
302
303 static void
304 action_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
305 {
306 struct DO_AC *ca = clientdata;
307 XtCallActionProc(widget,ca->name,NULL,ca->argv,ca->argc);
308 }
309
310 void toolkit_set_label(Widget widget, char *str)
311 {
312 XtVaSetValues(widget,XtNlabel,str,NULL);
313 }
314
315 /*--- videotext ----------------------------------------------------------*/
316
317 static void create_vtx(void)
318 {
319 Widget shell,label;
320
321 shell = XtVaCreateWidget("vtx",transientShellWidgetClass,
322 app_shell,
323 XtNoverrideRedirect,True,
324 XtNvisual,vinfo.visual,
325 XtNcolormap,colormap,
326 XtNdepth,vinfo.depth,
327 NULL);
328 label = XtVaCreateManagedWidget("label", labelWidgetClass, shell,
329 NULL);
330 #ifdef HAVE_ZVBI
331 vtx = vbi_render_init(shell,label,NULL);
332 #endif
333 }
334
335 #if TT
336 static void
337 display_vtx(struct TEXTELEM *tt)
338 {
339 static Pixel fg, bg;
340 static XFontStruct *font;
341 static Pixmap pix;
342 static GC gc;
343 static int first = 1;
344 XColor color, dummy;
345 XGCValues values;
346 Dimension x,y,w,h,sw,sh;
347 int maxwidth,width,height,direction,ascent,descent,lastline,i;
348 XCharStruct cs;
349
350 if (NULL == tt) {
351 XtPopdown(vtx_shell);
352 return;
353 }
354
355 if (NULL == font) {
356 XtVaGetValues(vtx_label,
357 XtNfont,&font,
358 XtNbackground,&bg,
359 XtNforeground,&fg,
360 NULL);
361 values.font = font->fid;
362 gc = XCreateGC(dpy, XtWindow(vtx_label), GCFont, &values);
363 }
364
365 /* calc size + positions */
366 width = 0; height = 0; maxwidth = 0;
367 lastline = -1;
368 for (i = 0; tt[i].len; i++) {
369 XTextExtents(font,tt[i].str,tt[i].len,
370 &direction,&ascent,&descent,&cs);
371 if (lastline != tt[i].line) {
372 if (maxwidth < width)
373 maxwidth = width;
374 width = 0;
375 height += ascent+descent;
376 lastline = tt[i].line;
377 }
378 tt[i].x = width;
379 tt[i].y = height - descent;
380 width += cs.width;
381 }
382 if (maxwidth < width)
383 maxwidth = width;
384
385 /* alloc pixmap + draw text */
386 if (pix)
387 XFreePixmap(dpy,pix);
388 fprintf(stderr,"pix: %dx%d\n",maxwidth, height);
389 pix = XCreatePixmap(dpy, RootWindowOfScreen(XtScreen(vtx_label)),
390 maxwidth, height,
391 DefaultDepthOfScreen(XtScreen(vtx_label)));
392 values.foreground = bg;
393 values.background = bg;
394 XChangeGC(dpy, gc, GCForeground | GCBackground, &values);
395 XFillRectangle(dpy,pix,gc,0,0,maxwidth,height);
396 for (i = 0; tt[i].len; i++) {
397 if (tt[i].fg) {
398 XAllocNamedColor(dpy,colormap,tt[i].fg,
399 &color,&dummy);
400 values.foreground = color.pixel;
401 } else {
402 values.foreground = fg;
403 }
404 if (tt[i].bg) {
405 XAllocNamedColor(dpy,colormap,tt[i].bg,
406 &color,&dummy);
407 values.background = color.pixel;
408 } else {
409 values.background = bg;
410 }
411 XChangeGC(dpy, gc, GCForeground | GCBackground, &values);
412 XDrawImageString(dpy,pix,gc,tt[i].x,tt[i].y,tt[i].str,tt[i].len);
413 }
414 XtVaSetValues(vtx_label,XtNbitmap,pix,XtNlabel,NULL,NULL);
415
416 XtVaGetValues(app_shell,XtNx,&x,XtNy,&y,XtNwidth,&w,XtNheight,&h,NULL);
417 XtVaGetValues(vtx_shell,XtNwidth,&sw,XtNheight,&sh,NULL);
418 XtVaSetValues(vtx_shell,XtNx,x+(w-sw)/2,XtNy,y+h-10-sh,NULL);
419 XtPopup(vtx_shell, XtGrabNone);
420 if (wm_stay_on_top && stay_on_top > 0)
421 wm_stay_on_top(dpy,XtWindow(vtx_shell),1);
422
423 if (first) {
424 first = 0;
425 XDefineCursor(dpy, XtWindow(vtx_shell), left_ptr);
426 XDefineCursor(dpy, XtWindow(vtx_label), left_ptr);
427 }
428 }
429 #endif
430
431 #ifdef HAVE_ZVBI
432 static void
433 display_subtitle(struct vbi_page *pg, struct vbi_rect *rect)
434 {
435 static int first = 1;
436 static Pixmap pix;
437 Dimension x,y,w,h,sw,sh;
438
439 if (NULL == pg) {
440 XtPopdown(vtx->shell);
441 return;
442 }
443
444 if (pix)
445 XFreePixmap(dpy,pix);
446 pix = vbi_export_pixmap(vtx,pg,rect);
447 XtVaSetValues(vtx->tt,XtNbitmap,pix,XtNlabel,NULL,NULL);
448
449 XtVaGetValues(app_shell,XtNx,&x,XtNy,&y,XtNwidth,&w,XtNheight,&h,NULL);
450 XtVaGetValues(vtx->shell,XtNwidth,&sw,XtNheight,&sh,NULL);
451 XtVaSetValues(vtx->shell,XtNx,x+(w-sw)/2,XtNy,y+h-10-sh,NULL);
452 XtPopup(vtx->shell, XtGrabNone);
453 if (wm_stay_on_top && stay_on_top > 0)
454 wm_stay_on_top(dpy,XtWindow(vtx->shell),1);
455
456 if (first) {
457 first = 0;
458 XDefineCursor(dpy, XtWindow(vtx->shell), left_ptr);
459 XDefineCursor(dpy, XtWindow(vtx->tt), left_ptr);
460 }
461 }
462 #endif
463
464 /*--- tv -----------------------------------------------------------------*/
465
466 static void
467 resize_event(Widget widget, XtPointer client_data, XEvent *event, Boolean *d)
468 {
469 static int width = 0, height = 0, first = 1;
470 char label[64];
471
472 switch(event->type) {
473 case ConfigureNotify:
474 if (first) {
475 video_gd_init(tv,args.gl);
476 first = 0;
477 }
478 if (width != event->xconfigure.width ||
479 height != event->xconfigure.height) {
480 width = event->xconfigure.width;
481 height = event->xconfigure.height;
482 video_gd_configure(width, height);
483 XClearWindow(XtDisplay(tv),XtWindow(tv));
484 sprintf(label,"%-" LABEL_WIDTH "s: %dx%d",MOVIE_SIZE,width,height);
485 if (w_movie_size)
486 XtVaSetValues(w_movie_size,XtNlabel,label,NULL);
487 }
488 break;
489 }
490 }
491
492 /*------------------------------------------------------------------------*/
493
494 /* the RightWay[tm] to set float resources (copyed from Xaw specs) */
495 static void
496 set_float(Widget widget, char *name, float value)
497 {
498 Arg args[1];
499
500 if (sizeof(float) > sizeof(XtArgVal)) {
501 /*
502 * If a float is larger than an XtArgVal then pass this
503 * resource value by reference.
504 */
505 XtSetArg(args[0], name, &value);
506 } else {
507 /*
508 * Convince C not to perform an automatic conversion, which
509 * would truncate 0.5 to 0.
510 *
511 * switched from pointer tricks to the union to fix alignment
512 * problems on ia64 (Stephane Eranian <eranian@cello.hpl.hp.com>)
513 */
514 union {
515 XtArgVal xt;
516 float fp;
517 } foo;
518 foo.fp = value;
519 XtSetArg(args[0], name, foo.xt);
520 }
521 XtSetValues(widget,args,1);
522 }
523
524 static void
525 new_freqtab(void)
526 {
527 char label[64];
528
529 if (c_freq) {
530 sprintf(label,"%-" LABEL_WIDTH "s: %s","Frequency table",
531 chanlists[chantab].name);
532 XtVaSetValues(c_freq,XtNlabel,label,NULL);
533 }
534 }
535
536 static void
537 new_attr(struct ng_attribute *attr, int val)
538 {
539 struct xaw_attribute *a;
540 char label[64],*olabel;
541 const char *valstr;
542
543 for (a = xaw_attrs; NULL != a; a = a->next) {
544 if (a->attr->id == attr->id)
545 break;
546 }
547 if (NULL != a) {
548 switch (attr->type) {
549 case ATTR_TYPE_CHOICE:
550 XtVaGetValues(a->cmd,XtNlabel,&olabel,NULL);
551 valstr = ng_attr_getstr(attr,val);
552 sprintf(label,"%-" LABEL_WIDTH "." LABEL_WIDTH "s: %s",
553 olabel,valstr ? valstr : "unknown");
554 XtVaSetValues(a->cmd,XtNlabel,label,NULL);
555 break;
556 case ATTR_TYPE_BOOL:
557 XtVaGetValues(a->cmd,XtNlabel,&olabel,NULL);
558 sprintf(label,"%-" BOOL_WIDTH "." BOOL_WIDTH "s %s",
559 olabel,val ? "on" : "off");
560 XtVaSetValues(a->cmd,XtNlabel,label,NULL);
561 break;
562 case ATTR_TYPE_INTEGER:
563 set_float(a->scroll,XtNtopOfThumb,
564 (float)(val-attr->min) / (attr->max - attr->min));
565 break;
566 }
567 return;
568 }
569 }
570
571 static void
572 new_volume(void)
573 {
574 struct ng_attribute *attr;
575
576 attr = ng_attr_byid(attrs,ATTR_ID_VOLUME);
577 if (NULL != attr)
578 new_attr(attr,cur_attrs[ATTR_ID_VOLUME]);
579 }
580
581 static void
582 new_channel(void)
583 {
584 set_property(cur_freq,
585 (cur_channel == -1) ? NULL : chanlist[cur_channel].name,
586 (cur_sender == -1) ? NULL : channels[cur_sender]->name);
587 conf_station_switched();
588
589 if (zap_timer) {
590 XtRemoveTimeOut(zap_timer);
591 zap_timer = 0;
592 }
593 if (scan_timer) {
594 XtRemoveTimeOut(scan_timer);
595 scan_timer = 0;
596 }
597 if (audio_timer) {
598 XtRemoveTimeOut(audio_timer);
599 audio_timer = 0;
600 }
601 audio_timer = XtAppAddTimeOut(app_context, 5000, watch_audio, NULL);
602 }
603
604 void
605 watch_audio(XtPointer data, XtIntervalId *id)
606 {
607 if (-1 != cur_sender)
608 change_audio(channels[cur_sender]->audio);
609 audio_timer = 0;
610 }
611
612 /*------------------------------------------------------------------------*/
613
614 static void
615 do_capture(int from, int to, int tmp_switch)
616 {
617 static int niced = 0;
618 char label[64];
619
620 /* off */
621 switch (from) {
622 case CAPTURE_OFF:
623 XtVaSetValues(tv,XtNbackgroundPixmap,XtUnspecifiedPixmap,NULL);
624 if (tv_pix)
625 XFreePixmap(dpy,tv_pix);
626 tv_pix = 0;
627 break;
628 case CAPTURE_GRABDISPLAY:
629 video_gd_stop();
630 XClearArea(XtDisplay(tv), XtWindow(tv), 0,0,0,0, True);
631 break;
632 case CAPTURE_OVERLAY:
633 video_overlay(0);
634 break;
635 }
636
637 /* on */
638 switch (to) {
639 case CAPTURE_OFF:
640 sprintf(label,"%-" LABEL_WIDTH "s: %s","Capture","off");
641 if (!tmp_switch) {
642 tv_pix = x11_capture_pixmap(dpy, &vinfo, colormap, 0, 0);
643 if (tv_pix)
644 XtVaSetValues(tv,XtNbackgroundPixmap,tv_pix,NULL);
645 }
646 break;
647 case CAPTURE_GRABDISPLAY:
648 sprintf(label,"%-" LABEL_WIDTH "s: %s","Capture","grabdisplay");
649 if (!niced)
650 nice(niced = 10);
651 video_gd_start();
652 break;
653 case CAPTURE_OVERLAY:
654 sprintf(label,"%-" LABEL_WIDTH "s: %s","Capture","overlay");
655 video_overlay(1);
656 break;
657 }
658 if (c_cap)
659 XtVaSetValues(c_cap,XtNlabel,label,NULL);
660 }
661
662 /* gets called before switching away from a channel */
663 static void
664 pixit(void)
665 {
666 Pixmap pix;
667 struct ng_video_fmt fmt;
668 struct ng_video_buf *buf;
669
670 if (cur_sender == -1)
671 return;
672
673 /* save picture settings */
674 channels[cur_sender]->color = cur_attrs[ATTR_ID_COLOR];
675 channels[cur_sender]->bright = cur_attrs[ATTR_ID_BRIGHT];
676 channels[cur_sender]->hue = cur_attrs[ATTR_ID_HUE];
677 channels[cur_sender]->contrast = cur_attrs[ATTR_ID_CONTRAST];
678 channels[cur_sender]->input = cur_attrs[ATTR_ID_INPUT];
679 channels[cur_sender]->norm = cur_attrs[ATTR_ID_NORM];
680
681 if (0 == pix_width || 0 == pix_height)
682 return;
683
684 /* capture mini picture */
685 if (!(f_drv & CAN_CAPTURE))
686 return;
687
688 video_gd_suspend();
689 memset(&fmt,0,sizeof(fmt));
690 fmt.fmtid = x11_dpy_fmtid;
691 fmt.width = pix_width;
692 fmt.height = pix_height;
693 if (NULL == (buf = ng_grabber_get_image(&fmt)))
694 goto done1;
695 buf = ng_filter_single(cur_filter,buf);
696 if (0 == (pix = x11_create_pixmap(dpy,&vinfo,buf)))
697 goto done2;
698 x11_label_pixmap(dpy,colormap,pix,buf->fmt.height,
699 channels[cur_sender]->name);
700 XtVaSetValues(channels[cur_sender]->button,
701 XtNbackgroundPixmap,pix,
702 XtNlabel,"",
703 XtNwidth,pix_width,
704 XtNheight,pix_height,
705 NULL);
706 if (channels[cur_sender]->pixmap)
707 XFreePixmap(dpy,channels[cur_sender]->pixmap);
708 channels[cur_sender]->pixmap = pix;
709
710 done2:
711 ng_release_video_buf(buf);
712 done1:
713 video_gd_restart();
714 }
715
716 static void
717 set_menu_val(Widget widget, char *name, struct STRTAB *tab, int val)
718 {
719 char label[64];
720 int i;
721
722 for (i = 0; tab[i].str != NULL; i++) {
723 if (tab[i].nr == val)
724 break;
725 }
726 sprintf(label,"%-15s : %s",name,
727 (tab[i].str != NULL) ? tab[i].str : "invalid");
728 XtVaSetValues(widget,XtNlabel,label,NULL);
729 }
730
731 void
732 ChannelAction(Widget widget, XEvent *event,
733 String *params, Cardinal *num_params)
734 {
735 int i;
736
737 if (0 == count)
738 return;
739 i = popup_menu(widget,"Stations",cmenu);
740
741 if (i != -1)
742 do_va_cmd(2,"setstation",channels[i]->name);
743 }
744
745 static void create_chanwin(void)
746 {
747 chan_shell = XtVaAppCreateShell("Channels", "Xawtv",
748 topLevelShellWidgetClass,
749 dpy,
750 XtNclientLeader,app_shell,
751 XtNvisual,vinfo.visual,
752 XtNcolormap,colormap,
753 XtNdepth,vinfo.depth,
754 XtNheight,XtScreen(app_shell)->height/2,
755 XtNmaxHeight,XtScreen(app_shell)->height-50,
756 NULL);
757 XtOverrideTranslations(chan_shell, XtParseTranslationTable
758 ("<Message>WM_PROTOCOLS: Popup()"));
759 chan_viewport = XtVaCreateManagedWidget("viewport",
760 viewportWidgetClass, chan_shell,
761 XtNallowHoriz, False,
762 XtNallowVert, True,
763 NULL);
764 chan_box = XtVaCreateManagedWidget("channelbox",
765 boxWidgetClass, chan_viewport,
766 XtNsensitive, True,
767 NULL);
768 }
769
770 void channel_menu(void); /* FIXME */
771 void channel_menu(void)
772 {
773 int i,max,len;
774 char str[100];
775
776 if (cmenu)
777 free(cmenu);
778 cmenu = malloc((count+1)*sizeof(struct STRTAB));
779 memset(cmenu,0,(count+1)*sizeof(struct STRTAB));
780 for (i = 0, max = 0; i < count; i++) {
781 len = strlen(channels[i]->name);
782 if (max < len)
783 max = len;
784 }
785 for (i = 0; i < count; i++) {
786 cmenu[i].nr = i;
787 cmenu[i].str = channels[i]->name;
788 if (channels[i]->key) {
789 sprintf(str,"%2d %-*s %s",i+1,
790 max+2,channels[i]->name,channels[i]->key);
791 } else {
792 sprintf(str,"%2d %-*s",i+1,max+2,channels[i]->name);
793 }
794 cmenu[i].str=strdup(str);
795 }
796 conf_list_update();
797 calc_frequencies();
798 }
799
800 void
801 StayOnTop(Widget widget, XEvent *event,
802 String *params, Cardinal *num_params)
803 {
804 unsigned int i;
805
806 if (!wm_stay_on_top)
807 return;
808
809 stay_on_top = stay_on_top ? 0 : 1;
810 if (debug)
811 fprintf(stderr,"stay_on_top: %d\n",stay_on_top);
812
813 wm_stay_on_top(dpy,XtWindow(app_shell),stay_on_top);
814 wm_stay_on_top(dpy,XtWindow(on_shell),stay_on_top);
815 for (i = 0; i < TOPLEVELS; i++)
816 wm_stay_on_top(dpy,XtWindow(*(my_toplevels[i].shell)),
817 (stay_on_top == -1) ? 0 : stay_on_top);
818 }
819
820 /*--- option window ------------------------------------------------------*/
821
822 static void
823 update_movie_menus(void)
824 {
825 struct list_head *item;
826 struct ng_writer *writer;
827 Boolean sensitive;
828 unsigned int i;
829
830 /* drivers */
831 if (NULL == m_movie_driver) {
832 i = 0;
833 list_for_each(item,&ng_writers)
834 i++;
835 m_movie_driver = malloc(sizeof(struct STRTAB)*(i+1));
836 memset(m_movie_driver,0,sizeof(struct STRTAB)*(i+1));
837 i = 0;
838 list_for_each(item,&ng_writers) {
839 writer = list_entry(item, struct ng_writer, list);
840 m_movie_driver[i].nr = i;
841 m_movie_driver[i].str = writer->desc;
842 if (NULL == movie_driver ||
843 (NULL != mov_driver && 0 == strcasecmp(mov_driver,writer->name))) {
844 movie_driver = writer;
845 i_movie_driver = i;
846 }
847 i++;
848 }
849 m_movie_driver[i].nr = i;
850 m_movie_driver[i].str = NULL;
851 }
852
853 /* audio formats */
854 for (i = 0; NULL != movie_driver->audio[i].name; i++)
855 ;
856 if (m_movie_audio)
857 free(m_movie_audio);
858 movie_audio = 0;
859 m_movie_audio = malloc(sizeof(struct STRTAB)*(i+2));
860 memset(m_movie_audio,0,sizeof(struct STRTAB)*(i+2));
861 for (i = 0; NULL != movie_driver->audio[i].name; i++) {
862 m_movie_audio[i].nr = i;
863 m_movie_audio[i].str = movie_driver->audio[i].desc ?
864 movie_driver->audio[i].desc :
865 ng_afmt_to_desc[movie_driver->audio[i].fmtid];
866 if (NULL != mov_audio)
867 if (0 == strcasecmp(mov_audio,movie_driver->audio[i].name))
868 movie_audio = i;
869 }
870 m_movie_audio[i].nr = i;
871 m_movie_audio[i].str = "no sound";
872
873 /* video formats */
874 for (i = 0; NULL != movie_driver->video[i].name; i++)
875 ;
876 if (m_movie_video)
877 free(m_movie_video);
878 movie_video = 0;
879 m_movie_video = malloc(sizeof(struct STRTAB)*(i+2));
880 memset(m_movie_video,0,sizeof(struct STRTAB)*(i+2));
881 for (i = 0; NULL != movie_driver->video[i].name; i++) {
882 m_movie_video[i].nr = i;
883 m_movie_video[i].str = movie_driver->video[i].desc ?
884 movie_driver->video[i].desc :
885 ng_vfmt_to_desc[movie_driver->video[i].fmtid];
886 if (NULL != mov_video)
887 if (0 == strcasecmp(mov_video,movie_driver->video[i].name))
888 movie_video = i;
889 }
890
891 /* need audio filename? */
892 sensitive = movie_driver->combined ? False : True;
893 XtVaSetValues(w_movie_flabel,
894 XtNsensitive,sensitive,
895 NULL);
896 XtVaSetValues(w_movie_faudio,
897 XtNsensitive,sensitive,
898 NULL);
899 }
900
901 static void
902 init_movie_menus(void)
903 {
904 update_movie_menus();
905
906 if (mov_rate)
907 do_va_cmd(3,"movie","rate",mov_rate);
908 if (mov_fps)
909 do_va_cmd(3,"movie","fps",mov_fps);
910 set_menu_val(w_movie_driver,MOVIE_DRIVER,m_movie_driver,i_movie_driver);
911 set_menu_val(w_movie_audio,MOVIE_AUDIO,m_movie_audio,movie_audio);
912 set_menu_val(w_movie_rate,MOVIE_RATE,m_movie_rate,movie_rate);
913 set_menu_val(w_movie_video,MOVIE_VIDEO,m_movie_video,movie_video);
914 set_menu_val(w_movie_fps,MOVIE_FPS,m_movie_fps,movie_fps);
915 }
916
917 #define PANED_FIX \
918 XtNallowResize, False, \
919 XtNshowGrip, False, \
920 XtNskipAdjust, True
921
922 struct DO_CMD cmd_fs = { 1, { "fullscreen", NULL }};
923 struct DO_CMD cmd_mute = { 2, { "volume", "mute", NULL }};
924 struct DO_CMD cmd_cap = { 2, { "capture", "toggle", NULL }};
925 struct DO_CMD cmd_jpeg = { 2, { "snap", "jpeg", NULL }};
926 struct DO_CMD cmd_ppm = { 2, { "snap", "ppm", NULL }};
927
928 struct DO_AC ac_fs = { 0, "FullScreen", { NULL }};
929 struct DO_AC ac_top = { 0, "StayOnTop", { NULL }};
930
931 struct DO_AC ac_avi = { 1, "Popup", { "streamer", NULL }};
932 struct DO_AC ac_chan = { 1, "Popup", { "channels", NULL }};
933 struct DO_AC ac_conf = { 1, "Popup", { "config", NULL }};
934 struct DO_AC ac_launch = { 1, "Popup", { "launcher", NULL }};
935 struct DO_AC ac_zap = { 0, "Zap", { NULL }};
936
937 static void
938 menu_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
939 {
940 struct ng_attribute *attr;
941 long cd = (long)clientdata;
942 int j;
943
944 switch (cd) {
945 #if 0
946 case 10:
947 attr = ng_attr_byid(a_drv,ATTR_ID_NORM);
948 if (-1 != (j=popup_menu(widget,"TV Norm",attr->choices)))
949 do_va_cmd(2,"setnorm",ng_attr_getstr(attr,j));
950 break;
951 case 11:
952 attr = ng_attr_byid(a_drv,ATTR_ID_INPUT);
953 if (-1 != (j=popup_menu(widget,"Video Source",attr->choices)))
954 do_va_cmd(2,"setinput",ng_attr_getstr(attr,j));
955 break;
956 #endif
957 case 12:
958 if (-1 != (j=popup_menu(widget,"Freq table",chanlist_names)))
959 do_va_cmd(2,"setfreqtab",chanlist_names[j].str);
960 break;
961 case 13:
962 attr = ng_attr_byid(attrs,ATTR_ID_AUDIO_MODE);
963 if (NULL != attr) {
964 int i,mode = attr->read(attr);
965 for (i = 1; attr->choices[i].str != NULL; i++) {
966 attr->choices[i].nr =
967 (1 << (i-1)) & mode ? (1 << (i-1)) : -1;
968 }
969 if (-1 != (j=popup_menu(widget,"Audio",attr->choices)))
970 change_audio(attr->choices[j].nr);
971 }
972 break;
973 case 14:
974 if (-1 != (j=popup_menu(widget,"Capture",cap_list)))
975 do_va_cmd(2,"capture",cap_list[j].str);
976 break;
977
978 case 20:
979 if (-1 != (j=popup_menu(widget,MOVIE_DRIVER,m_movie_driver))) {
980 int i = 0;
981 struct list_head *item;
982 struct ng_writer *writer = NULL;
983
984 list_for_each(item,&ng_writers) {
985 if (i++ == j)
986 writer = list_entry(item,struct ng_writer, list);
987 }
988 do_va_cmd(3,"movie","driver",writer->name);
989 }
990 break;
991 case 21:
992 if (-1 != (j=popup_menu(widget,MOVIE_AUDIO,m_movie_audio)))
993 do_va_cmd(3,"movie","audio",
994 movie_driver->audio[j].name ?
995 movie_driver->audio[j].name :
996 "none");
997 break;
998 case 22:
999 if (-1 != (j=popup_menu(widget,MOVIE_RATE,m_movie_rate)))
1000 do_va_cmd(3,"movie","rate",m_movie_rate[j].str);
1001 break;
1002 case 23:
1003 if (-1 != (j=popup_menu(widget,MOVIE_VIDEO,m_movie_video)))
1004 do_va_cmd(3,"movie","video",movie_driver->video[j].name);
1005 break;
1006 case 24:
1007 if (-1 != (j=popup_menu(widget,MOVIE_FPS,m_movie_fps)))
1008 do_va_cmd(3,"movie","fps",m_movie_fps[j].str);
1009 break;
1010 default:
1011 /* nothing */
1012 break;
1013 }
1014 }
1015
1016 static void
1017 jump_scb(Widget widget, XtPointer clientdata, XtPointer call_data)
1018 {
1019 struct xaw_attribute *a = clientdata;
1020 struct ng_attribute *attr = a->attr;
1021 const char *name;
1022 char val[16];
1023 int value,range;
1024
1025 range = attr->max - attr->min;
1026 value = (int)(*(float*)call_data * range) + attr->min;
1027 if (debug)
1028 fprintf(stderr,"scroll: value is %d\n",value);
1029 if (value < attr->min)
1030 value = attr->min;
1031 if (value > attr->max)
1032 value = attr->max;
1033 sprintf(val,"%d",value);
1034
1035 if (a) {
1036 name = a->attr->name;
1037 do_va_cmd(3,"setattr",name,val);
1038 } else {
1039 name = XtName(XtParent(widget));
1040 do_va_cmd(2,name,val);
1041 }
1042 }
1043
1044 static void
1045 scroll_scb(Widget widget, XtPointer clientdata, XtPointer call_data)
1046 {
1047 long move = (long)call_data;
1048 Dimension length;
1049 float shown,top1,top2;
1050
1051 XtVaGetValues(widget,
1052 XtNlength, &length,
1053 XtNshown, &shown,
1054 XtNtopOfThumb, &top1,
1055 NULL);
1056
1057 top2 = top1 + (float)move/length/5;
1058 if (top2 < 0) top2 = 0;
1059 if (top2 > 1) top2 = 1;
1060 #if 1
1061 fprintf(stderr,"scroll by %ld\tlength %d\tshown %f\ttop %f => %f\n",
1062 move,length,shown,top1,top2);
1063 #endif
1064 jump_scb(widget,clientdata,&top2);
1065 }
1066
1067 static void
1068 attr_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
1069 {
1070 struct xaw_attribute *a = clientdata;
1071 int j;
1072
1073 switch (a->attr->type) {
1074 case ATTR_TYPE_CHOICE:
1075 j=popup_menu(widget,a->attr->name,a->attr->choices);
1076 if (-1 != j)
1077 do_va_cmd(3,"setattr",a->attr->name,a->attr->choices[j].str);
1078 break;
1079 case ATTR_TYPE_BOOL:
1080 do_va_cmd(3,"setattr",a->attr->name,"toggle");
1081 break;
1082 }
1083 }
1084
1085 static void
1086 add_attr_option(Widget paned, struct ng_attribute *attr)
1087 {
1088 struct xaw_attribute *a;
1089 Widget p,l;
1090
1091 a = malloc(sizeof(*a));
1092 memset(a,0,sizeof(*a));
1093 a->attr = attr;
1094
1095 switch (attr->type) {
1096 case ATTR_TYPE_BOOL:
1097 case ATTR_TYPE_CHOICE:
1098 a->cmd = XtVaCreateManagedWidget(attr->name,
1099 commandWidgetClass, paned,
1100 PANED_FIX,
1101 NULL);
1102 XtAddCallback(a->cmd,XtNcallback,attr_cb,a);
1103 break;
1104 case ATTR_TYPE_INTEGER:
1105 p = XtVaCreateManagedWidget(attr->name,
1106 panedWidgetClass, paned,
1107 XtNorientation, XtEvertical,
1108 PANED_FIX,
1109 NULL);
1110 l = XtVaCreateManagedWidget("l",labelWidgetClass, p,
1111 XtNshowGrip, False,
1112 NULL);
1113 a->scroll = XtVaCreateManagedWidget("s",scrollbarWidgetClass,p,
1114 PANED_FIX,
1115 NULL);
1116 XtAddCallback(a->scroll, XtNjumpProc, jump_scb, a);
1117 XtAddCallback(a->scroll, XtNscrollProc, scroll_scb, a);
1118 if (attr->id >= ATTR_ID_COUNT)
1119 XtVaSetValues(l,XtNlabel,attr->name,NULL);
1120 break;
1121 }
1122 a->next = xaw_attrs;
1123 xaw_attrs = a;
1124 }
1125
1126 static void
1127 create_optwin(void)
1128 {
1129 Widget c;
1130
1131 opt_shell = XtVaAppCreateShell("Options", "Xawtv",
1132 topLevelShellWidgetClass,
1133 dpy,
1134 XtNclientLeader,app_shell,
1135 XtNvisual,vinfo.visual,
1136 XtNcolormap,colormap,
1137 XtNdepth,vinfo.depth,
1138 NULL);
1139 XtOverrideTranslations(opt_shell, XtParseTranslationTable
1140 ("<Message>WM_PROTOCOLS: Popup()"));
1141 opt_paned = XtVaCreateManagedWidget("paned", panedWidgetClass, opt_shell,
1142 NULL);
1143
1144 c = XtVaCreateManagedWidget("mute", commandWidgetClass, opt_paned,
1145 PANED_FIX, NULL);
1146 XtAddCallback(c,XtNcallback,command_cb,(XtPointer)&cmd_mute);
1147
1148 c = XtVaCreateManagedWidget("fs", commandWidgetClass, opt_paned,
1149 PANED_FIX, NULL);
1150 XtAddCallback(c,XtNcallback,command_cb,(XtPointer)&cmd_fs);
1151
1152 c = XtVaCreateManagedWidget("grabppm", commandWidgetClass, opt_paned,
1153 PANED_FIX, NULL);
1154 XtAddCallback(c,XtNcallback,command_cb,(XtPointer)&cmd_ppm);
1155 c = XtVaCreateManagedWidget("grabjpeg", commandWidgetClass, opt_paned,
1156 PANED_FIX, NULL);
1157 XtAddCallback(c,XtNcallback,command_cb,(XtPointer)&cmd_jpeg);
1158 c = XtVaCreateManagedWidget("recavi", commandWidgetClass, opt_paned,
1159 PANED_FIX, NULL);
1160 XtAddCallback(c,XtNcallback,action_cb,(XtPointer)&ac_avi);
1161 c = XtVaCreateManagedWidget("chanwin", commandWidgetClass, opt_paned,
1162 PANED_FIX, NULL);
1163 XtAddCallback(c,XtNcallback,action_cb,(XtPointer)&ac_chan);
1164 c = XtVaCreateManagedWidget("confwin", commandWidgetClass, opt_paned,
1165 PANED_FIX, NULL);
1166 XtAddCallback(c,XtNcallback,action_cb,(XtPointer)&ac_conf);
1167 c = XtVaCreateManagedWidget("launchwin", commandWidgetClass, opt_paned,
1168 PANED_FIX, NULL);
1169 XtAddCallback(c,XtNcallback,action_cb,(XtPointer)&ac_launch);
1170 c = XtVaCreateManagedWidget("zap", commandWidgetClass, opt_paned,
1171 PANED_FIX, NULL);
1172 XtAddCallback(c,XtNcallback,action_cb,(XtPointer)&ac_zap);
1173 if (wm_stay_on_top) {
1174 c = XtVaCreateManagedWidget("top", commandWidgetClass, opt_paned,
1175 PANED_FIX, NULL);
1176 XtAddCallback(c,XtNcallback,action_cb,(XtPointer)&ac_top);
1177 }
1178 }
1179
1180 static void
1181 create_attr_widgets(void)
1182 {
1183 struct ng_attribute *attr;
1184 Widget c;
1185
1186 /* menus / multiple choice options */
1187 attr = ng_attr_byid(attrs,ATTR_ID_NORM);
1188 if (NULL != attr)
1189 add_attr_option(opt_paned,attr);
1190 attr = ng_attr_byid(attrs,ATTR_ID_INPUT);
1191 if (NULL != attr)
1192 add_attr_option(opt_paned,attr);
1193 attr = ng_attr_byid(attrs,ATTR_ID_AUDIO_MODE);
1194 if (NULL != attr)
1195 add_attr_option(opt_paned,attr);
1196
1197 if (f_drv & CAN_TUNE) {
1198 c_freq = XtVaCreateManagedWidget("freq", commandWidgetClass, opt_paned,
1199 PANED_FIX, NULL);
1200 XtAddCallback(c_freq,XtNcallback,menu_cb,(XtPointer)12);
1201 }
1202 c_cap = XtVaCreateManagedWidget("cap", commandWidgetClass, opt_paned,
1203 PANED_FIX, NULL);
1204 XtAddCallback(c_cap,XtNcallback,menu_cb,(XtPointer)14);
1205
1206 for (attr = attrs; attr->name != NULL; attr++) {
1207 if (attr->id < ATTR_ID_COUNT)
1208 continue;
1209 if (attr->type != ATTR_TYPE_CHOICE)
1210 continue;
1211 add_attr_option(opt_paned,attr);
1212 }
1213
1214 /* integer options */
1215 attr = ng_attr_byid(attrs,ATTR_ID_BRIGHT);
1216 if (NULL != attr)
1217 add_attr_option(opt_paned,attr);
1218 attr = ng_attr_byid(attrs,ATTR_ID_HUE);
1219 if (NULL != attr)
1220 add_attr_option(opt_paned,attr);
1221 attr = ng_attr_byid(attrs,ATTR_ID_CONTRAST);
1222 if (NULL != attr)
1223 add_attr_option(opt_paned,attr);
1224 attr = ng_attr_byid(attrs,ATTR_ID_COLOR);
1225 if (NULL != attr)
1226 add_attr_option(opt_paned,attr);
1227 attr = ng_attr_byid(attrs,ATTR_ID_VOLUME);
1228 if (NULL != attr)
1229 add_attr_option(opt_paned,attr);
1230
1231 for (attr = attrs; attr->name != NULL; attr++) {
1232 if (attr->id < ATTR_ID_COUNT)
1233 continue;
1234 if (attr->type != ATTR_TYPE_INTEGER)
1235 continue;
1236 add_attr_option(opt_paned,attr);
1237 }
1238
1239 /* boolean options */
1240 for (attr = attrs; attr->name != NULL; attr++) {
1241 if (attr->id < ATTR_ID_COUNT)
1242 continue;
1243 if (attr->type != ATTR_TYPE_BOOL)
1244 continue;
1245 add_attr_option(opt_paned,attr);
1246 }
1247
1248 /* quit */
1249 c = XtVaCreateManagedWidget("quit", commandWidgetClass, opt_paned,
1250 PANED_FIX, NULL);
1251 XtAddCallback(c,XtNcallback,ExitCB,NULL);
1252 }
1253
1254 /*--- avi recording ------------------------------------------------------*/
1255
1256 static void
1257 exec_record(Widget widget, XtPointer client_data, XtPointer calldata)
1258 {
1259 if (!(f_drv & CAN_CAPTURE)) {
1260 fprintf(stderr,"grabbing: not supported [try -noxv switch?]\n");
1261 return;
1262 }
1263
1264 if (rec_work_id) {
1265 do_va_cmd(2,"movie","stop");
1266 } else {
1267 do_va_cmd(2,"movie","start");
1268 }
1269 return;
1270 }
1271
1272 static void
1273 exec_player_cb(Widget widget, XtPointer client_data, XtPointer calldata)
1274 {
1275 char *filename;
1276
1277 XtVaGetValues(w_movie_fvideo,XtNstring,&filename,NULL);
1278 filename = tilde_expand(filename);
1279 exec_player(filename);
1280 }
1281
1282 static void
1283 do_movie_record(int argc, char **argv)
1284 {
1285 char *fvideo,*faudio;
1286 struct ng_video_fmt video;
1287 struct ng_audio_fmt audio;
1288 const struct ng_writer *wr;
1289 int i;
1290
1291 /* set parameters */
1292 if (argc > 1 && 0 == strcasecmp(argv[0],"driver")) {
1293 struct list_head *item;
1294 struct ng_writer *writer;
1295 i = 0;
1296 list_for_each(item,&ng_writers) {
1297 writer = list_entry(item, struct ng_writer, list);
1298 if (0 == strcasecmp(argv[1],writer->name)) {
1299 movie_driver = writer;
1300 i_movie_driver = i;
1301 }
1302 i++;
1303 }
1304
1305 set_menu_val(w_movie_driver,MOVIE_DRIVER,
1306 m_movie_driver,i_movie_driver);
1307 update_movie_menus();
1308 set_menu_val(w_movie_audio,MOVIE_AUDIO,
1309 m_movie_audio,movie_audio);
1310 set_menu_val(w_movie_video,MOVIE_VIDEO,
1311 m_movie_video,movie_video);
1312 return;
1313 }
1314 if (argc > 1 && 0 == strcasecmp(argv[0],"fvideo")) {
1315 XtVaSetValues(w_movie_fvideo,XtNstring,argv[1],NULL);
1316 return;
1317 }
1318 if (argc > 1 && 0 == strcasecmp(argv[0],"faudio")) {
1319 XtVaSetValues(w_movie_faudio,XtNstring,argv[1],NULL);
1320 return;
1321 }
1322 if (argc > 1 && 0 == strcasecmp(argv[0],"audio")) {
1323 for (i = 0; NULL != movie_driver->audio[i].name; i++) {
1324 if (0 == strcasecmp(argv[1],movie_driver->audio[i].name))
1325 movie_audio = m_movie_audio[i].nr;
1326 }
1327 if (0 == strcmp(argv[1],"none"))
1328 movie_audio = m_movie_audio[i].nr;
1329 set_menu_val(w_movie_audio,MOVIE_AUDIO,
1330 m_movie_audio,movie_audio);
1331 return;
1332 }
1333 if (argc > 1 && 0 == strcasecmp(argv[0],"rate")) {
1334 for (i = 0; m_movie_rate[i].str != NULL; i++)
1335 if (atoi(argv[1]) == m_movie_rate[i].nr)
1336 movie_rate = m_movie_rate[i].nr;
1337 set_menu_val(w_movie_rate,MOVIE_RATE,
1338 m_movie_rate,movie_rate);
1339 }
1340 if (argc > 1 && 0 == strcasecmp(argv[0],"video")) {
1341 for (i = 0; NULL != movie_driver->video[i].name; i++)
1342 if (0 == strcasecmp(argv[1],movie_driver->video[i].name))
1343 movie_video = m_movie_video[i].nr;
1344 set_menu_val(w_movie_video,MOVIE_VIDEO,
1345 m_movie_video,movie_video);
1346 return;
1347 }
1348 if (argc > 1 && 0 == strcasecmp(argv[0],"fps")) {
1349 for (i = 0; m_movie_fps[i].str != NULL; i++) {
1350 int fps = (int)(atof(argv[1]) * 1000 + .5);
1351 if (fps == m_movie_fps[i].nr)
1352 movie_fps = m_movie_fps[i].nr;
1353 }
1354 set_menu_val(w_movie_fps,MOVIE_FPS,
1355 m_movie_fps,movie_fps);
1356 }
1357
1358 /* start */
1359 if (argc > 0 && 0 == strcasecmp(argv[0],"start")) {
1360 if (0 != cur_movie)
1361 return; /* records already */
1362 cur_movie = 1;
1363 movie_blit = (cur_capture == CAPTURE_GRABDISPLAY);
1364 video_gd_suspend();
1365
1366 XtVaGetValues(w_movie_fvideo,XtNstring,&fvideo,NULL);
1367 XtVaGetValues(w_movie_faudio,XtNstring,&faudio,NULL);
1368 fvideo = tilde_expand(fvideo);
1369 faudio = tilde_expand(faudio);
1370
1371 memset(&video,0,sizeof(video));
1372 memset(&audio,0,sizeof(audio));
1373
1374 wr = movie_driver;
1375 video.fmtid = wr->video[movie_video].fmtid;
1376 video.width = cur_tv_width;
1377 video.height = cur_tv_height;
1378 if (NULL != wr->audio[movie_audio].name) {
1379 audio.fmtid = wr->audio[movie_audio].fmtid;
1380 audio.rate = movie_rate;
1381 } else {
1382 audio.fmtid = AUDIO_NONE;
1383 }
1384
1385 movie_state = movie_writer_init
1386 (fvideo, faudio, wr,
1387 &video, wr->video[movie_video].priv, movie_fps,
1388 &audio, wr->audio[movie_audio].priv, args.dspdev,
1389 args.bufcount,args.parallel);
1390 if (NULL == movie_state) {
1391 /* init failed */
1392 video_gd_restart();
1393 cur_movie = 0;
1394 /* hmm, not the most elegant way to flag an error ... */
1395 XtVaSetValues(w_movie_status,XtNlabel,"error [init]",NULL);
1396 return;
1397 }
1398 if (0 != movie_writer_start(movie_state)) {
1399 /* start failed */
1400 movie_writer_stop(movie_state);
1401 video_gd_restart();
1402 cur_movie = 0;
1403 /* hmm, not the most elegant way to flag an error ... */
1404 XtVaSetValues(w_movie_status,XtNlabel,"error [start]",NULL);
1405 return;
1406 }
1407 rec_work_id = XtAppAddWorkProc(app_context,rec_work,NULL);
1408 XtVaSetValues(w_movie_status,XtNlabel,"recording",NULL);
1409 return;
1410 }
1411
1412 /* stop */
1413 if (argc > 0 && 0 == strcasecmp(argv[0],"stop")) {
1414 if (0 == cur_movie)
1415 return; /* nothing to stop here */
1416
1417 movie_writer_stop(movie_state);
1418 XtRemoveWorkProc(rec_work_id);
1419 rec_work_id = 0;
1420 video_gd_restart();
1421 cur_movie = 0;
1422 return;
1423 }
1424 }
1425
1426 static void
1427 do_rec_status(char *message)
1428 {
1429 XtVaSetValues(w_movie_status,XtNlabel,message,NULL);
1430 }
1431
1432 #define FIX_LEFT_TOP \
1433 XtNleft,XawChainLeft, \
1434 XtNright,XawChainRight, \
1435 XtNtop,XawChainTop, \
1436 XtNbottom,XawChainTop
1437
1438 static void
1439 create_strwin(void)
1440 {
1441 Widget form,label,button,text;
1442
1443 str_shell = XtVaAppCreateShell("Streamer", "Xawtv",
1444 topLevelShellWidgetClass,
1445 dpy,
1446 XtNclientLeader,app_shell,
1447 XtNvisual,vinfo.visual,
1448 XtNcolormap,colormap,
1449 XtNdepth,vinfo.depth,
1450 NULL);
1451 XtOverrideTranslations(str_shell, XtParseTranslationTable
1452 ("<Message>WM_PROTOCOLS: Popup()"));
1453
1454 form = XtVaCreateManagedWidget("form", formWidgetClass, str_shell,
1455 NULL);
1456
1457 /* driver */
1458 button = XtVaCreateManagedWidget("driver", commandWidgetClass, form,
1459 FIX_LEFT_TOP,
1460 NULL);
1461 w_movie_driver = button;
1462
1463 /* movie filename */
1464 label = XtVaCreateManagedWidget("vlabel", labelWidgetClass, form,
1465 FIX_LEFT_TOP,
1466 XtNfromVert, button,
1467 NULL);
1468 text = XtVaCreateManagedWidget("vname", asciiTextWidgetClass, form,
1469 FIX_LEFT_TOP,
1470 XtNfromVert, label,
1471 NULL);
1472 w_movie_fvideo = text;
1473
1474 /* audio filename */
1475 label = XtVaCreateManagedWidget("alabel", labelWidgetClass, form,
1476 FIX_LEFT_TOP,
1477 XtNfromVert, text,
1478 NULL);
1479 w_movie_flabel = label;
1480 text= XtVaCreateManagedWidget("aname", asciiTextWidgetClass, form,
1481 FIX_LEFT_TOP,
1482 XtNfromVert, label,
1483 NULL);
1484 w_movie_faudio = text;
1485
1486 /* audio format */
1487 button = XtVaCreateManagedWidget("audio", commandWidgetClass, form,
1488 FIX_LEFT_TOP,
1489 XtNfromVert, text,
1490 NULL);
1491 w_movie_audio = button;
1492 button = XtVaCreateManagedWidget("rate", commandWidgetClass, form,
1493 FIX_LEFT_TOP,
1494 XtNfromVert, button,
1495 NULL);
1496 w_movie_rate = button;
1497
1498 /* video format */
1499 button = XtVaCreateManagedWidget("video", commandWidgetClass, form,
1500 FIX_LEFT_TOP,
1501 XtNfromVert, button,
1502 NULL);
1503 w_movie_video = button;
1504 button = XtVaCreateManagedWidget("fps", commandWidgetClass, form,
1505 FIX_LEFT_TOP,
1506 XtNfromVert, button,
1507 NULL);
1508 w_movie_fps = button;
1509 label = XtVaCreateManagedWidget("size", labelWidgetClass, form,
1510 FIX_LEFT_TOP,
1511 XtNfromVert, button,
1512 NULL);
1513 w_movie_size = label;
1514
1515 /* status line */
1516 label = XtVaCreateManagedWidget("status", labelWidgetClass, form,
1517 FIX_LEFT_TOP,
1518 XtNfromVert, label,
1519 XtNlabel, "",
1520 NULL);
1521 w_movie_status = label;
1522
1523 /* cmd buttons */
1524 button = XtVaCreateManagedWidget("streamer", commandWidgetClass, form,
1525 FIX_LEFT_TOP,
1526 XtNfromVert, label,
1527 NULL);
1528 XtAddCallback(button,XtNcallback,exec_record,NULL);
1529
1530 button = XtVaCreateManagedWidget("xanim", commandWidgetClass, form,
1531 FIX_LEFT_TOP,
1532 XtNfromVert, button,
1533 NULL);
1534 XtAddCallback(button,XtNcallback,exec_player_cb,NULL);
1535
1536 #if 0
1537 label = XtVaCreateManagedWidget("olabel", labelWidgetClass, form,
1538 FIX_LEFT_TOP,
1539 XtNfromVert,button,
1540 NULL);
1541 str_text = XtVaCreateManagedWidget("output", asciiTextWidgetClass, form,
1542 XtNleft,XawChainLeft,
1543 XtNright,XawChainRight,
1544 XtNtop,XawChainTop,
1545 XtNbottom,XawChainBottom,
1546 XtNfromVert,label,
1547 NULL);
1548 #endif
1549
1550 XtAddCallback(w_movie_driver,XtNcallback,menu_cb,(XtPointer)20);
1551 XtAddCallback(w_movie_audio,XtNcallback,menu_cb,(XtPointer)21);
1552 XtAddCallback(w_movie_rate,XtNcallback,menu_cb,(XtPointer)22);
1553 XtAddCallback(w_movie_video,XtNcallback,menu_cb,(XtPointer)23);
1554 XtAddCallback(w_movie_fps,XtNcallback,menu_cb,(XtPointer)24);
1555 }
1556
1557 /*--- launcher window -----------------------------------------------------*/
1558
1559 static void
1560 create_launchwin(void)
1561 {
1562 launch_shell = XtVaAppCreateShell("Launcher", "Xawtv",
1563 topLevelShellWidgetClass,
1564 dpy,
1565 XtNclientLeader,app_shell,
1566 XtNvisual,vinfo.visual,
1567 XtNcolormap,colormap,
1568 XtNdepth,vinfo.depth,
1569 NULL);
1570 XtOverrideTranslations(launch_shell, XtParseTranslationTable
1571 ("<Message>WM_PROTOCOLS: Popup()"));
1572 launch_paned = XtVaCreateManagedWidget("paned", panedWidgetClass,
1573 launch_shell, NULL);
1574 }
1575
1576 /*--- main ---------------------------------------------------------------*/
1577
1578 int
1579 main(int argc, char *argv[])
1580 {
1581 int i;
1582 unsigned long freq;
1583
1584 hello_world("xawtv");
1585 progname = strdup(argv[0]);
1586
1587 /* toplevel */
1588 XtSetLanguageProc(NULL,NULL,NULL);
1589 app_shell = XtVaAppInitialize(&app_context, "Xawtv",
1590 opt_desc, opt_count,
1591 &argc, argv,
1592 fallback_ressources,
1593 NULL);
1594 dpy = XtDisplay(app_shell);
1595 init_atoms(dpy);
1596
1597 /* command line args */
1598 ng_init();
1599 handle_cmdline_args();
1600
1601 /* device scan */
1602 if (args.hwscan) {
1603 fprintf(stderr,"looking for available devices\n");
1604 #ifdef HAVE_LIBXV
1605 xv_video_init(-1,1);
1606 #endif
1607 grabber_scan();
1608 }
1609
1610 /* look for a useful visual */
1611 visual_init("xawtv","Xawtv");
1612
1613 /* remote display? */
1614 do_overlay = !args.remote;
1615 if (do_overlay)
1616 x11_check_remote();
1617 v4lconf_init();
1618
1619 /* x11 stuff */
1620 XtAppAddActions(app_context,actionTable,
1621 sizeof(actionTable)/sizeof(XtActionsRec));
1622 x11_misc_init(dpy);
1623 if (debug)
1624 fprintf(stderr,"main: dga extention...\n");
1625 xfree_dga_init(dpy);
1626 if (debug)
1627 fprintf(stderr,"main: xinerama extention...\n");
1628 xfree_xinerama_init(dpy);
1629 #ifdef HAVE_LIBXV
1630 if (debug)
1631 fprintf(stderr,"main: xvideo extention [video]...\n");
1632 if (args.xv_video)
1633 xv_video_init(args.xv_port,0);
1634 if (debug)
1635 fprintf(stderr,"main: xvideo extention [image]...\n");
1636 if (args.xv_image)
1637 xv_image_init(dpy);
1638 #endif
1639
1640 /* set hooks (command.c) */
1641 update_title = new_title;
1642 display_message = new_message;
1643 #if TT
1644 vtx_message = display_vtx;
1645 #endif
1646 #ifdef HAVE_ZVBI
1647 vtx_subtitle = display_subtitle;
1648 #endif
1649 attr_notify = new_attr;
1650 volume_notify = new_volume;
1651 freqtab_notify = new_freqtab;
1652 setfreqtab_notify = new_freqtab;
1653 setstation_notify = new_channel;
1654 set_capture_hook = do_capture;
1655 fullscreen_hook = do_fullscreen;
1656 movie_hook = do_movie_record;
1657 rec_status = do_rec_status;
1658 exit_hook = do_exit;
1659 capture_get_hook = video_gd_suspend;
1660 capture_rel_hook = video_gd_restart;
1661 channel_switch_hook = pixit;
1662
1663 if (debug)
1664 fprintf(stderr,"main: init main window...\n");
1665 tv = video_init(app_shell, &vinfo, simpleWidgetClass,
1666 args.bpp, args.gl);
1667 XtAddEventHandler(XtParent(tv),StructureNotifyMask, True,
1668 resize_event, NULL);
1669 if (debug)
1670 fprintf(stderr,"main: install signal handlers...\n");
1671 xt_siginit();
1672 if (NULL == drv) {
1673 if (debug)
1674 fprintf(stderr,"main: open grabber device...\n");
1675 grabber_init();
1676 }
1677
1678 /* create windows */
1679 XSetIOErrorHandler(x11_ctrl_alt_backspace);
1680 if (debug)
1681 fprintf(stderr,"main: checking wm...\n");
1682 wm_detect(dpy);
1683 if (debug)
1684 fprintf(stderr,"main: creating windows ...\n");
1685 create_optwin();
1686 create_onscreen(labelWidgetClass);
1687 create_vtx();
1688 create_chanwin();
1689 create_confwin();
1690 create_strwin();
1691 create_launchwin();
1692
1693 /* read config file + related settings */
1694 if (debug)
1695 fprintf(stderr,"main: init frequency tables ...\n");
1696 freq_init();
1697 if (args.readconfig) {
1698 if (debug)
1699 fprintf(stderr,"main: read config file ...\n");
1700 read_config(args.conffile ? args.conffile : NULL, &argc, argv);
1701 }
1702 if (0 != strlen(mixerdev)) {
1703 struct ng_attribute *attr;
1704 if (debug)
1705 fprintf(stderr,"main: open mixer device...\n");
1706 if (NULL != (attr = ng_mix_init(mixerdev,mixerctl)))
1707 add_attrs(attr);
1708 }
1709 init_movie_menus();
1710 create_attr_widgets();
1711
1712 xt_vm_randr_input_init(dpy);
1713
1714 if (debug)
1715 fprintf(stderr,"main: mapping main window ...\n");
1716 XtRealizeWidget(app_shell);
1717 create_pointers(app_shell);
1718 create_bitmaps(app_shell);
1719 XDefineCursor(dpy, XtWindow(app_shell), left_ptr);
1720 XSetWMProtocols(XtDisplay(app_shell), XtWindow(app_shell),
1721 &WM_DELETE_WINDOW, 1);
1722
1723 XtVaSetValues(app_shell,
1724 XtNwidthInc, WIDTH_INC,
1725 XtNheightInc, HEIGHT_INC,
1726 XtNminWidth, WIDTH_INC,
1727 XtNminHeight, HEIGHT_INC,
1728 NULL);
1729 XtVaSetValues(chan_shell,
1730 XtNwidth,pix_width*pix_cols+30,
1731 NULL);
1732
1733 /* mouse pointer magic */
1734 XtAddEventHandler(tv, PointerMotionMask, True, mouse_event, NULL);
1735 mouse_event(tv,NULL,NULL,NULL);
1736
1737 /* init hardware */
1738 if (debug)
1739 fprintf(stderr,"main: initialize hardware ...\n");
1740 attr_init();
1741 audio_on();
1742 audio_init();
1743
1744 /* build channel list */
1745 if (args.readconfig) {
1746 if (debug)
1747 fprintf(stderr,"main: parse channels from config file ...\n");
1748 parse_config();
1749 }
1750 channel_menu();
1751
1752 xt_handle_pending(dpy);
1753 init_overlay();
1754
1755 set_property(0,NULL,NULL);
1756 if (optind+1 == argc) {
1757 do_va_cmd(2,"setstation",argv[optind]);
1758 } else {
1759 if ((f_drv & CAN_TUNE) && 0 != (freq = drv->getfreq(h_drv))) {
1760 for (i = 0; i < chancount; i++)
1761 if (chanlist[i].freq == freq*1000/16) {
1762 do_va_cmd(2,"setchannel",chanlist[i].name);
1763 break;
1764 }
1765 }
1766 if (-1 == cur_channel) {
1767 if (count > 0) {
1768 if (debug)
1769 fprintf(stderr,"main: tuning first station\n");
1770 do_va_cmd(2,"setstation","");
1771 } else {
1772 if (debug)
1773 fprintf(stderr,"main: setting defaults\n");
1774 set_defaults();
1775 }
1776 } else {
1777 if (debug)
1778 fprintf(stderr,"main: known station tuned, not changing\n");
1779 }
1780 }
1781 XtAddEventHandler(tv,ExposureMask, True, tv_expose_event, NULL);
1782
1783 if (args.fullscreen) {
1784 XSync(dpy,False);
1785 do_fullscreen();
1786 } else {
1787 XtAppAddWorkProc(app_context,MyResize,NULL);
1788 }
1789
1790 sprintf(modename,"%dx%d, ",
1791 XtScreen(app_shell)->width,XtScreen(app_shell)->height);
1792 strcat(modename,ng_vfmt_to_desc[x11_dpy_fmtid]);
1793 new_message(modename);
1794 if (!have_config)
1795 XtCallActionProc(tv,"Help",NULL,NULL,0);
1796
1797 xt_main_loop();
1798 return 0;
1799 }
1800
|
This page was automatically generated by the
LXR engine.
|