1 /*
2 * Openmotif user interface
3 *
4 * (c) 2000-2002 Gerd Knorr <kraxel@bytesex.org>
5 *
6 */
7
8 #include "config.h"
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <signal.h>
18 #include <pthread.h>
19 #include <sys/ioctl.h>
20
21 #include <X11/Xlib.h>
22 #include <X11/Intrinsic.h>
23 #include <Xm/Xm.h>
24 #include <Xm/XmStrDefs.h>
25 #include <Xm/Primitive.h>
26 #include <Xm/Form.h>
27 #include <Xm/Label.h>
28 #include <Xm/RowColumn.h>
29 #include <Xm/PushB.h>
30 #include <Xm/ToggleB.h>
31 #include <Xm/CascadeB.h>
32 #include <Xm/Separator.h>
33 #include <Xm/Scale.h>
34 #include <Xm/Protocols.h>
35 #include <Xm/Display.h>
36 #include <Xm/Text.h>
37 #include <Xm/FileSB.h>
38 #include <Xm/ComboBox.h>
39 #include <Xm/ScrolledW.h>
40 #include <Xm/MessageB.h>
41 #include <Xm/Frame.h>
42 #include <Xm/SelectioB.h>
43 #include <Xm/TransferP.h>
44 #include <Xm/DragIcon.h>
45 #include <X11/extensions/XShm.h>
46
47 #include "grab-ng.h"
48 #include "channel.h"
49 #include "commands.h"
50 #include "frequencies.h"
51 #include "capture.h"
52 #include "wmhooks.h"
53 #include "atoms.h"
54 #include "x11.h"
55 #include "xt.h"
56 #include "xv.h"
57 #include "man.h"
58 #include "RegEdit.h"
59 #include "icons.h"
60 #include "sound.h"
61 #include "complete.h"
62 #include "writefile.h"
63 #include "list.h"
64 #include "vbi-data.h"
65 #include "vbi-x11.h"
66 #include "blit.h"
67
68 /*----------------------------------------------------------------------*/
69
70 int jpeg_quality, mjpeg_quality, debug;
71
72 /*----------------------------------------------------------------------*/
73
74 static void PopupAction(Widget, XEvent*, String*, Cardinal*);
75 static void DebugAction(Widget, XEvent*, String*, Cardinal*);
76 static void IpcAction(Widget, XEvent*, String*, Cardinal*);
77 static void ontop_ac(Widget, XEvent*, String*, Cardinal*);
78 static void chan_makebutton(struct CHANNEL *channel);
79 static void channel_menu(void);
80
81 #ifdef HAVE_ZVBI
82 static void chscan_cb(Widget widget, XtPointer clientdata,
83 XtPointer call_data);
84 #endif
85 static void pref_manage_cb(Widget widget, XtPointer clientdata,
86 XtPointer call_data);
87 static void add_cmd_callback(Widget widget, String callback,char *command,
88 const char *arg1, const char *arg2);
89
90 static XtActionsRec actionTable[] = {
91 { "CloseMain", CloseMainAction },
92 { "Command", CommandAction },
93 { "Popup", PopupAction },
94 { "Debug", DebugAction },
95 { "Remote", RemoteAction },
96 { "Zap", ZapAction },
97 { "Scan", ScanAction },
98 { "man", man_action },
99 { "Ratio", RatioAction },
100 { "Launch", LaunchAction },
101 #ifdef HAVE_ZVBI
102 { "Vtx", VtxAction },
103 #endif
104 { "Complete", CompleteAction },
105 { "Ipc", IpcAction },
106 { "Filter", FilterAction },
107 { "StayOnTop", ontop_ac },
108 { "Event", EventAction },
109 };
110
111 static String fallback_ressources[] = {
112 #include "MoTV.h"
113 NULL
114 };
115
116 XtIntervalId audio_timer;
117
118 static Widget st_menu1,st_menu2;
119 static Widget control_shell,str_shell,levels_shell,levels_toggle;
120 static Widget launch_menu,opt_menu,cap_menu,freq_menu;
121 static Widget chan_viewport,chan_box;
122 static Widget st_freq,st_chan,st_name,st_key;
123 static Widget scale_shell,filter_shell;
124 static Widget w_full;
125 static Widget b_ontop;
126 #ifdef HAVE_ZVBI
127 static struct vbi_window *vtx;
128 #endif
129
130 /* properties */
131 static Widget prop_dlg,prop_name,prop_key,prop_channel,prop_button;
132 static Widget prop_group;
133
134 /* preferences */
135 static Widget pref_dlg,pref_fs_toggle,pref_fs_menu,pref_fs_option;
136 static Widget pref_osd,pref_ntsc,pref_partial,pref_quality;
137 static Widget pref_mix_toggle,pref_mix1_menu,pref_mix1_option;
138 static Widget pref_mix2_menu,pref_mix2_option;
139
140 /* streamer */
141 static Widget driver_menu, driver_option;
142 static Widget audio_menu, audio_option;
143 static Widget video_menu, video_option;
144 static Widget m_rate,m_fps,m_fvideo,m_status;
145 static Widget m_faudio,m_faudioL,m_faudioB;
146
147 static struct ng_writer *movie_driver;
148 static unsigned int i_movie_driver;
149 static unsigned int movie_audio;
150 static unsigned int movie_video;
151 static XtWorkProcId rec_work_id;
152
153 static struct MY_TOPLEVELS {
154 char *name;
155 Widget *shell;
156 int mapped;
157 } my_toplevels [] = {
158 { "control", &control_shell },
159 { "streamer", &str_shell },
160 { "scale", &scale_shell },
161 { "filter", &filter_shell },
162 { "levels", &levels_shell },
163 };
164 #define TOPLEVELS (sizeof(my_toplevels)/sizeof(struct MY_TOPLEVELS))
165
166 struct motif_attribute {
167 struct motif_attribute *next;
168 struct ng_attribute *attr;
169 Widget widget;
170 };
171 static struct motif_attribute *motif_attrs;
172
173 struct filter_attribute {
174 struct filter_attribute *next;
175 struct ng_filter *filter;
176 struct ng_attribute *attr;
177 int value;
178 Widget widget;
179 };
180 static struct filter_attribute *filter_attrs;
181
182 /*----------------------------------------------------------------------*/
183 /* debug/test code */
184
185 #if 0
186 static void
187 print_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
188 {
189 char *msg = (char*) clientdata;
190 fprintf(stderr,"debug: %s\n",msg);
191 }
192
193 static void
194 toggle_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
195 {
196 XmToggleButtonCallbackStruct *tb = call_data;
197
198 if (tb->reason != XmCR_VALUE_CHANGED)
199 return;
200 fprintf(stderr,"toggle: [%s] set=%s\n",
201 clientdata ? (char*)clientdata : "???",
202 tb->set ? "on" : "off");
203 }
204 #endif
205
206 void
207 DebugAction(Widget widget, XEvent *event,
208 String *params, Cardinal *num_params)
209 {
210 fprintf(stderr,"debug: foo\n");
211 }
212
213 /*----------------------------------------------------------------------*/
214
215 void toolkit_set_label(Widget widget, char *str)
216 {
217 XmString xmstr;
218
219 xmstr = XmStringGenerate(str, NULL, XmMULTIBYTE_TEXT, NULL);
220 XtVaSetValues(widget,XmNlabelString,xmstr,NULL);
221 XmStringFree(xmstr);
222 }
223
224 static void delete_children(Widget widget)
225 {
226 WidgetList children,list;
227 Cardinal nchildren;
228 unsigned int i;
229
230 XtVaGetValues(widget,XtNchildren,&children,
231 XtNnumChildren,&nchildren,NULL);
232 if (0 == nchildren)
233 return;
234 list = malloc(sizeof(Widget*)*nchildren);
235 memcpy(list,children,sizeof(Widget*)*nchildren);
236 for (i = 0; i < nchildren; i++)
237 XtDestroyWidget(list[i]);
238 free(list);
239 }
240
241 static void
242 watch_audio(XtPointer data, XtIntervalId *id)
243 {
244 if (-1 != cur_sender)
245 change_audio(channels[cur_sender]->audio);
246 audio_timer = 0;
247 }
248
249 static void
250 new_channel(void)
251 {
252 char line[1024];
253
254 set_property(cur_freq,
255 (cur_channel == -1) ? NULL : chanlist[cur_channel].name,
256 (cur_sender == -1) ? NULL : channels[cur_sender]->name);
257
258 sprintf(line,"%d.%02d MHz",cur_freq / 16, (cur_freq % 16) * 100 / 16);
259 toolkit_set_label(st_freq,line);
260 toolkit_set_label(st_chan, (cur_channel != -1) ?
261 chanlist[cur_channel].name : "");
262 toolkit_set_label(st_name, (cur_sender != -1) ?
263 channels[cur_sender]->name : "");
264 toolkit_set_label(st_key,(cur_sender != -1 &&
265 NULL != channels[cur_sender]->key) ?
266 channels[cur_sender]->key : "");
267
268 if (zap_timer) {
269 XtRemoveTimeOut(zap_timer);
270 zap_timer = 0;
271 }
272 if (scan_timer) {
273 XtRemoveTimeOut(scan_timer);
274 scan_timer = 0;
275 }
276 if (audio_timer) {
277 XtRemoveTimeOut(audio_timer);
278 audio_timer = 0;
279 }
280 audio_timer = XtAppAddTimeOut(app_context, 5000, watch_audio, NULL);
281 }
282
283 static void
284 do_ontop(Boolean state)
285 {
286 unsigned int i;
287
288 if (!wm_stay_on_top)
289 return;
290
291 stay_on_top = state;
292 wm_stay_on_top(dpy,XtWindow(app_shell),stay_on_top);
293 wm_stay_on_top(dpy,XtWindow(on_shell),stay_on_top);
294 for (i = 0; i < TOPLEVELS; i++)
295 wm_stay_on_top(dpy,XtWindow(*(my_toplevels[i].shell)),
296 (stay_on_top == -1) ? 0 : stay_on_top);
297 XmToggleButtonSetState(b_ontop,state,False);
298 }
299
300 static void
301 ontop_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
302 {
303 XmToggleButtonCallbackStruct *tb = call_data;
304
305 if (tb->reason != XmCR_VALUE_CHANGED)
306 return;
307 do_ontop(tb->set);
308 }
309
310 static void
311 ontop_ac(Widget widget, XEvent *event, String *params, Cardinal *num_params)
312 {
313 do_ontop(stay_on_top ? False : True);
314 }
315
316 /*----------------------------------------------------------------------*/
317
318 static void
319 resize_event(Widget widget, XtPointer client_data, XEvent *event, Boolean *d)
320 {
321 static int width = 0, height = 0, first = 1;
322
323 switch(event->type) {
324 case ConfigureNotify:
325 if (first) {
326 video_gd_init(tv,args.gl);
327 first = 0;
328 }
329 if (width != event->xconfigure.width ||
330 height != event->xconfigure.height) {
331 width = event->xconfigure.width;
332 height = event->xconfigure.height;
333 video_gd_configure(width, height);
334 XClearWindow(XtDisplay(tv),XtWindow(tv));
335 }
336 break;
337 }
338 }
339
340 static void
341 PopupAction(Widget widget, XEvent *event,
342 String *params, Cardinal *num_params)
343 {
344 unsigned int i;
345
346 /* which window we are talking about ? */
347 if (*num_params > 0) {
348 for (i = 0; i < TOPLEVELS; i++) {
349 if (0 == strcasecmp(my_toplevels[i].name,params[0]))
350 break;
351 }
352 if (i == TOPLEVELS) {
353 fprintf(stderr,"PopupAction: oops: shell not found (name=%s)\n",
354 params[0]);
355 return;
356 }
357 } else {
358 for (i = 0; i < TOPLEVELS; i++) {
359 if (*(my_toplevels[i].shell) == widget)
360 break;
361 }
362 if (i == TOPLEVELS) {
363 fprintf(stderr,"PopupAction: oops: shell not found (%p:%s)\n",
364 widget,XtName(widget));
365 return;
366 }
367 }
368
369 /* popup/down window */
370 if (!my_toplevels[i].mapped) {
371 XtPopup(*(my_toplevels[i].shell), XtGrabNone);
372 if (wm_stay_on_top && stay_on_top > 0)
373 wm_stay_on_top(dpy,XtWindow(*(my_toplevels[i].shell)),1);
374 my_toplevels[i].mapped = 1;
375 } else {
376 XtPopdown(*(my_toplevels[i].shell));
377 my_toplevels[i].mapped = 0;
378 }
379 }
380
381 static void
382 popup_eh(Widget widget, XtPointer clientdata, XEvent *event, Boolean *cont)
383 {
384 Widget popup = clientdata;
385
386 XmMenuPosition(popup,(XButtonPressedEvent*)event);
387 XtManageChild(popup);
388 }
389
390 static void
391 popupdown_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
392 {
393 int i = 0;
394 PopupAction(clientdata, NULL, NULL, &i);
395 }
396
397 static void
398 destroy_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
399 {
400 XtDestroyWidget(clientdata);
401 }
402
403 static void
404 free_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
405 {
406 free(clientdata);
407 }
408
409 static void
410 about_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
411 {
412 Widget msgbox;
413
414 msgbox = XmCreateInformationDialog(app_shell,"about_box",NULL,0);
415 XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_HELP_BUTTON));
416 XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_CANCEL_BUTTON));
417 XtAddCallback(msgbox,XmNokCallback,destroy_cb,msgbox);
418 XtManageChild(msgbox);
419 }
420
421 static void
422 action_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
423 {
424 char *calls, *action, *argv[8];
425 int argc;
426
427 calls = strdup(clientdata);
428 action = strtok(calls,"(),");
429 for (argc = 0; NULL != (argv[argc] = strtok(NULL,"(),")); argc++)
430 /* nothing */;
431 XtCallActionProc(widget,action,NULL,argv,argc);
432 free(calls);
433 }
434
435 /*--- videotext ----------------------------------------------------------*/
436
437 #if TT
438 static void
439 rend_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
440 {
441 XmDisplayCallbackStruct *arg = call_data;
442 XmRendition rend;
443 Colormap cmap;
444 XColor color,dummy;
445 char fg[16],bg[16];
446 Arg args[4];
447 int n = 0;
448
449 if (arg->reason != XmCR_NO_RENDITION)
450 return;
451 if (2 != sscanf(arg->tag,"%[a-z]_on_%[a-z]",fg,bg))
452 return;
453
454 XtVaGetValues(app_shell, XmNcolormap, &cmap, NULL);
455 if (0 != strcmp(fg,"def"))
456 if (XAllocNamedColor(dpy, cmap, fg, &color, &dummy))
457 XtSetArg(args[n],XmNrenditionForeground,color.pixel), n++;
458 if (0 != strcmp(bg,"def"))
459 if (XAllocNamedColor(dpy, cmap, bg, &color, &dummy))
460 XtSetArg(args[n],XmNrenditionBackground,color.pixel), n++;
461 if (debug)
462 fprintf(stderr,"rend_cb: %s: %d %s/%s\n",arg->tag,n,fg,bg);
463 rend = XmRenditionCreate(app_shell, arg->tag, args, n);
464 arg->render_table = XmRenderTableAddRenditions
465 (arg->render_table,&rend,1,XmMERGE_NEW);
466 }
467 #endif
468
469 static void create_vtx(void)
470 {
471 Widget shell,label;
472
473 shell = XtVaCreateWidget("vtx",transientShellWidgetClass,
474 app_shell,
475 XtNoverrideRedirect,True,
476 XtNvisual,vinfo.visual,
477 XtNcolormap,colormap,
478 XtNdepth,vinfo.depth,
479 NULL);
480 label = XtVaCreateManagedWidget("label", xmLabelWidgetClass, shell,
481 NULL);
482 #if TT
483 XtAddCallback(XmGetXmDisplay(dpy),XmNnoRenditionCallback,rend_cb,NULL);
484 #endif
485 #ifdef HAVE_ZVBI
486 vtx = vbi_render_init(shell,label,NULL);
487 #endif
488 }
489
490 #if TT
491 static void
492 display_vtx(struct TEXTELEM *tt)
493 {
494 static int first = 1;
495 Dimension x,y,w,h,sw,sh;
496 char tag[32];
497 int i,lastline;
498 XmString str,elem;
499
500 if (NULL == tt) {
501 XtPopdown(vtx_shell);
502 return;
503 }
504
505 /* build xmstring */
506 str = NULL;
507 lastline = tt[0].line;
508 for (i = 0; tt[i].len > 0; i++) {
509 tt[i].str[tt[i].len] = 0;
510 sprintf(tag,"%s_on_%s",
511 tt[i].fg ? tt[i].fg : "def",
512 tt[i].bg ? tt[i].bg : "def");
513 if (NULL != str && tt[i].line != lastline) {
514 lastline = tt[i].line;
515 str = XmStringConcatAndFree(str,XmStringSeparatorCreate());
516 }
517 elem = XmStringGenerate(tt[i].str, NULL, XmMULTIBYTE_TEXT, tag);
518 if (NULL != str)
519 str = XmStringConcatAndFree(str,elem);
520 else
521 str = elem;
522 }
523 XtVaSetValues(vtx_label,XmNlabelString,str,NULL);
524 XmStringFree(str);
525
526 /* show popup */
527 XtVaGetValues(app_shell,XtNx,&x,XtNy,&y,XtNwidth,&w,XtNheight,&h,NULL);
528 XtVaGetValues(vtx_shell,XtNwidth,&sw,XtNheight,&sh,NULL);
529 XtVaSetValues(vtx_shell,XtNx,x+(w-sw)/2,XtNy,y+h-10-sh,NULL);
530 XtPopup(vtx_shell, XtGrabNone);
531 if (wm_stay_on_top && stay_on_top > 0)
532 wm_stay_on_top(dpy,XtWindow(vtx_shell),1);
533
534 if (first) {
535 first = 0;
536 XDefineCursor(dpy, XtWindow(vtx_shell), left_ptr);
537 XDefineCursor(dpy, XtWindow(vtx_label), left_ptr);
538 }
539 }
540 #endif
541
542 #ifdef HAVE_ZVBI
543 static void
544 display_subtitle(struct vbi_page *pg, struct vbi_rect *rect)
545 {
546 static int first = 1;
547 static Pixmap pix;
548 Dimension x,y,w,h,sw,sh;
549
550 if (NULL == pg) {
551 XtPopdown(vtx->shell);
552 return;
553 }
554
555 if (pix)
556 XFreePixmap(dpy,pix);
557 pix = vbi_export_pixmap(vtx,pg,rect);
558 XtVaSetValues(vtx->tt,XmNlabelPixmap,pix,XmNlabelType,XmPIXMAP,NULL);
559
560 XtVaGetValues(app_shell,XtNx,&x,XtNy,&y,XtNwidth,&w,XtNheight,&h,NULL);
561 XtVaGetValues(vtx->shell,XtNwidth,&sw,XtNheight,&sh,NULL);
562 XtVaSetValues(vtx->shell,XtNx,x+(w-sw)/2,XtNy,y+h-10-sh,NULL);
563 XtPopup(vtx->shell, XtGrabNone);
564 if (wm_stay_on_top && stay_on_top > 0)
565 wm_stay_on_top(dpy,XtWindow(vtx->shell),1);
566
567 if (first) {
568 first = 0;
569 XDefineCursor(dpy, XtWindow(vtx->shell), left_ptr);
570 XDefineCursor(dpy, XtWindow(vtx->tt), left_ptr);
571 }
572 }
573 #endif
574
575 /*----------------------------------------------------------------------*/
576
577 static void
578 new_attr(struct ng_attribute *attr, int val)
579 {
580 struct motif_attribute *a;
581 WidgetList children;
582 Cardinal nchildren;
583 unsigned int i;
584
585 for (a = motif_attrs; NULL != a; a = a->next) {
586 if (a->attr->id == attr->id)
587 break;
588 }
589 if (NULL == a)
590 return;
591
592 switch (attr->type) {
593 case ATTR_TYPE_CHOICE:
594 XtVaGetValues(a->widget,XtNchildren,&children,
595 XtNnumChildren,&nchildren,NULL);
596 for (i = 0; i < nchildren; i++) {
597 XmToggleButtonSetState(children[i],a->attr->choices[i].nr == val,
598 False);
599 }
600 break;
601 case ATTR_TYPE_BOOL:
602 XmToggleButtonSetState(a->widget,val,False);
603 break;
604 case ATTR_TYPE_INTEGER:
605 XmScaleSetValue(a->widget,val);
606 break;
607 }
608 return;
609 }
610
611 static void
612 new_volume(void)
613 {
614 struct ng_attribute *attr;
615
616 attr = ng_attr_byid(attrs,ATTR_ID_VOLUME);
617 if (NULL != attr)
618 new_attr(attr,cur_attrs[ATTR_ID_VOLUME]);
619 attr = ng_attr_byid(attrs,ATTR_ID_MUTE);
620 if (NULL != attr)
621 new_attr(attr,cur_attrs[ATTR_ID_MUTE]);
622 }
623
624 static void
625 new_freqtab(void)
626 {
627 WidgetList children;
628 Cardinal nchildren;
629 XmStringTable tab;
630 int i;
631
632 /* update menu */
633 XtVaGetValues(freq_menu,XtNchildren,&children,
634 XtNnumChildren,&nchildren,NULL);
635 for (i = 0; chanlist_names[i].str != NULL; i++)
636 XmToggleButtonSetState(children[i],chanlist_names[i].nr == chantab,
637 False);
638
639 /* update property window */
640 tab = malloc(chancount*sizeof(*tab));
641 for (i = 0; i < chancount; i++)
642 tab[i] = XmStringGenerate(chanlist[i].name,
643 NULL, XmMULTIBYTE_TEXT, NULL);
644 XtVaSetValues(prop_channel,
645 XmNitemCount,chancount,
646 XmNitems,tab,
647 XmNselectedItem,tab[0],
648 NULL);
649 for (i = 0; i < chancount; i++)
650 XmStringFree(tab[i]);
651 free(tab);
652 }
653
654 /*----------------------------------------------------------------------*/
655
656 static void
657 chan_key_eh(Widget widget, XtPointer client_data, XEvent *event, Boolean *cont)
658 {
659 XKeyEvent *ke = (XKeyEvent*)event;
660 KeySym sym;
661 char *key;
662 char line[64];
663
664 sym = XKeycodeToKeysym(dpy,ke->keycode,0);
665 if (NoSymbol == sym) {
666 fprintf(stderr,"can't translate keycode %d\n",ke->keycode);
667 return;
668 }
669 key = XKeysymToString(sym);
670
671 line[0] = '\0';
672 if (ke->state & ShiftMask) strcpy(line,"Shift+");
673 if (ke->state & ControlMask) strcpy(line,"Ctrl+");
674 strcat(line,key);
675 XmTextSetString(prop_key,line);
676 }
677
678 static void
679 chan_add_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
680 {
681 XmString str;
682 int i;
683
684 /* init stuff */
685 prop_button = NULL;
686 XmTextSetString(prop_name,"");
687 XmTextSetString(prop_key,"");
688 XmTextSetString(prop_group,"main");
689 i = (-1 == cur_channel) ? 0 : cur_channel;
690 str = XmStringGenerate(chanlist[i].name, NULL, XmMULTIBYTE_TEXT, NULL);
691 XtVaSetValues(prop_channel,XmNselectedItem,str,NULL);
692 XmStringFree(str);
693
694 XtManageChild(prop_dlg);
695 }
696
697 static void
698 chan_edit_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
699 {
700 XmString str;
701 int i;
702
703 /* find channel */
704 prop_button = clientdata;
705 for (i = 0; i < count; i++)
706 if (prop_button == channels[i]->button)
707 break;
708 if (i == count)
709 return;
710
711 /* init stuff */
712 XmTextSetString(prop_name,channels[i]->name);
713 XmTextSetString(prop_key,channels[i]->key);
714 XmTextSetString(prop_group,channels[i]->group);
715 i = (-1 == channels[i]->channel) ? 0 : channels[i]->channel;
716 str = XmStringGenerate(chanlist[i].name, NULL, XmMULTIBYTE_TEXT, NULL);
717 XtVaSetValues(prop_channel,XmNselectedItem,str,NULL);
718 XmStringFree(str);
719
720 XtManageChild(prop_dlg);
721 }
722
723 static void
724 chan_apply_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
725 {
726 char *name, *key, *cname, *group;
727 struct CHANNEL *c;
728 XmString str;
729 int i,channel;
730 Widget msgbox;
731
732 /* find channel */
733 i = count;
734 if (NULL != prop_button)
735 for (i = 0; i < count; i++)
736 if (prop_button == channels[i]->button)
737 break;
738
739 name = XmTextGetString(prop_name);
740 key = XmTextGetString(prop_key);
741 group = XmTextGetString(prop_group);
742 if (0 == strlen(group))
743 group = "main";
744
745 XtVaGetValues(prop_channel,XmNselectedItem,&str,NULL);
746 cname = XmStringUnparse(str,NULL,XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT,
747 NULL,0,0);
748 channel = lookup_channel(cname);
749
750 if (0 == strlen(name)) {
751 msgbox = XmCreateErrorDialog(prop_dlg,"no_name",NULL,0);
752 XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_HELP_BUTTON));
753 XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_CANCEL_BUTTON));
754 XtAddCallback(msgbox,XmNokCallback,destroy_cb,msgbox);
755 XtManageChild(msgbox);
756 return;
757 }
758
759 if (i == count) {
760 /* add */
761 c = add_channel(name);
762 if (strlen(key) > 0)
763 c->key = strdup(key);
764 c->cname = strdup(cname);
765 c->group = strdup(group);
766 c->channel = channel;
767 } else {
768 /* update */
769 c = channels[i];
770 free(c->name);
771 c->name = strdup(name);
772 if (c->key) {
773 free(c->key);
774 c->key = NULL;
775 }
776 if (0 != strlen(key))
777 c->key = strdup(key);
778 c->cname = strdup(cname);
779 c->group = strdup(group);
780 c->channel = channel;
781 XtRemoveAllCallbacks(c->button, XmNactivateCallback);
782 add_cmd_callback(c->button, XmNactivateCallback,
783 "setstation", c->name, NULL);
784 toolkit_set_label(c->button,c->name);
785 }
786 #if 0
787 c->input = cur_attrs[ATTR_ID_INPUT];
788 c->fine = cur_fine;
789 #endif
790 channel_menu();
791 }
792
793 static void
794 chan_save_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
795 {
796 save_config();
797 }
798
799 static void
800 chan_tune_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
801 {
802 XmComboBoxCallbackStruct *cb = call_data;
803 char *line;
804
805 line = XmStringUnparse(cb->item_or_text,NULL,
806 XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT,
807 NULL,0,0);
808 do_va_cmd(3,"setchannel",line);
809 }
810
811 static void
812 create_prop(void)
813 {
814 Widget label,rowcol;
815
816 prop_dlg = XmCreatePromptDialog(control_shell,"prop",NULL,0);
817 XmdRegisterEditres(XtParent(prop_dlg));
818 XtUnmanageChild(XmSelectionBoxGetChild(prop_dlg,XmDIALOG_SELECTION_LABEL));
819 XtUnmanageChild(XmSelectionBoxGetChild(prop_dlg,XmDIALOG_HELP_BUTTON));
820 XtUnmanageChild(XmSelectionBoxGetChild(prop_dlg,XmDIALOG_TEXT));
821
822 rowcol = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass, prop_dlg,
823 NULL);
824 label = XtVaCreateManagedWidget("nameL", xmLabelWidgetClass, rowcol,
825 NULL);
826 prop_name = XtVaCreateManagedWidget("name", xmTextWidgetClass, rowcol,
827 NULL);
828
829 label = XtVaCreateManagedWidget("keyL", xmLabelWidgetClass, rowcol,
830 NULL);
831 prop_key = XtVaCreateManagedWidget("key", xmTextWidgetClass, rowcol,
832 NULL);
833 XtAddEventHandler(prop_key, KeyPressMask, False, chan_key_eh, NULL);
834
835 label = XtVaCreateManagedWidget("groupL", xmLabelWidgetClass, rowcol,
836 NULL);
837 prop_group = XtVaCreateManagedWidget("group", xmTextWidgetClass, rowcol,
838 NULL);
839
840 label = XtVaCreateManagedWidget("channelL", xmLabelWidgetClass, rowcol,
841 NULL);
842 prop_channel = XtVaCreateManagedWidget("channel",xmComboBoxWidgetClass,
843 rowcol,NULL);
844 XtAddCallback(prop_channel,XmNselectionCallback, chan_tune_cb, NULL);
845
846 XtAddCallback(prop_dlg,XmNokCallback, chan_apply_cb, NULL);
847 }
848
849 /*----------------------------------------------------------------------*/
850
851 static void
852 filter_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
853 {
854 struct filter_attribute *f = clientdata;
855 Widget w;
856
857 switch (f->attr->type) {
858 case ATTR_TYPE_INTEGER:
859 XmScaleGetValue(f->widget,&f->value);
860 break;
861 case ATTR_TYPE_BOOL:
862 f->value = XmToggleButtonGetState(f->widget);
863 break;
864 case ATTR_TYPE_CHOICE:
865 w = NULL;
866 XtVaGetValues(f->widget,XmNmenuHistory,&w,NULL);
867 f->value = ng_attr_getint(f->attr,XtName(w));
868 break;
869 }
870 f->attr->write(f->attr,f->value);
871 }
872
873 static void
874 filter_add_ctrls(Widget rc, struct ng_filter *filter,
875 struct ng_attribute *attr)
876 {
877 struct filter_attribute *f;
878 Widget opt,push;
879 XmString str;
880 Arg args[2];
881 int i;
882
883 f = malloc(sizeof(*f));
884 memset(f,0,sizeof(*f));
885 f->filter = filter;
886 f->attr = attr;
887 f->next = filter_attrs;
888 f->value = attr->read(attr);
889 filter_attrs = f;
890
891 str = XmStringGenerate((char*)attr->name, NULL, XmMULTIBYTE_TEXT, NULL);
892 switch (attr->type) {
893 case ATTR_TYPE_INTEGER:
894 f->widget = XtVaCreateManagedWidget("scale",
895 xmScaleWidgetClass,rc,
896 XmNtitleString,str,
897 XmNminimum,attr->min,
898 XmNmaximum,attr->max,
899 XmNdecimalPoints,attr->points,
900 NULL);
901 XmScaleSetValue(f->widget,f->value);
902 XtAddCallback(f->widget,XmNvalueChangedCallback,filter_cb,f);
903 break;
904 case ATTR_TYPE_BOOL:
905 f->widget = XtVaCreateManagedWidget("bool",
906 xmToggleButtonWidgetClass,rc,
907 XmNlabelString,str,
908 NULL);
909 XmToggleButtonSetState(f->widget,f->value,False);
910 XtAddCallback(f->widget,XmNvalueChangedCallback,filter_cb,f);
911 break;
912 case ATTR_TYPE_CHOICE:
913 f->widget = XmCreatePulldownMenu(rc,"choiceM",NULL,0);
914 XtSetArg(args[0],XmNsubMenuId,f->widget);
915 XtSetArg(args[1],XmNlabelString,str);
916 opt = XmCreateOptionMenu(rc,"choiceO",args,2);
917 XtManageChild(opt);
918 for (i = 0; attr->choices[i].str != NULL; i++) {
919 push = XtVaCreateManagedWidget(attr->choices[i].str,
920 xmPushButtonWidgetClass,f->widget,
921 NULL);
922 XtAddCallback(push,XmNactivateCallback,filter_cb,f);
923 if (f->value == attr->choices[i].nr)
924 XtVaSetValues(f->widget,XmNmenuHistory,push,NULL);
925 }
926 break;
927 }
928 XmStringFree(str);
929 }
930
931 static void
932 create_filter_prop(void)
933 {
934 Widget rc1,frame,rc2;
935 XmString str;
936 struct list_head *item;
937 struct ng_filter *filter;
938 int j;
939
940 filter_shell = XtVaAppCreateShell("filter","MoTV",
941 topLevelShellWidgetClass,
942 dpy,
943 XtNclientLeader,app_shell,
944 XtNvisual,vinfo.visual,
945 XtNcolormap,colormap,
946 XtNdepth,vinfo.depth,
947 XmNdeleteResponse,XmDO_NOTHING,
948 NULL);
949 XmdRegisterEditres(filter_shell);
950 XmAddWMProtocolCallback(filter_shell,WM_DELETE_WINDOW,
951 popupdown_cb,filter_shell);
952
953 rc1 = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass, filter_shell,
954 NULL);
955
956 list_for_each(item,&ng_filters) {
957 filter = list_entry(item, struct ng_filter, list);
958 if (NULL == filter->attrs)
959 continue;
960 str = XmStringGenerate(filter->name, NULL,
961 XmMULTIBYTE_TEXT, NULL);
962 frame = XtVaCreateManagedWidget("frame",xmFrameWidgetClass,rc1,NULL);
963 XtVaCreateManagedWidget("label",xmLabelWidgetClass,frame,
964 XmNlabelString,str,
965 NULL);
966 rc2 = XtVaCreateManagedWidget("rc",xmRowColumnWidgetClass,frame,NULL);
967 XmStringFree(str);
968 for (j = 0; NULL != filter->attrs[j].name; j++)
969 filter_add_ctrls(rc2,filter,&filter->attrs[j]);
970 }
971 }
972
973 /*----------------------------------------------------------------------*/
974
975 static void
976 scroll_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
977 {
978 struct motif_attribute *a = clientdata;
979 int ret;
980 char val[10];
981
982 XmScaleGetValue(a->widget,&ret);
983 sprintf(val,"%d", ret);
984 do_va_cmd(3,"setattr",a->attr->name,val);
985 }
986
987
988 void
989 add_cmd_callback(Widget widget, String callback,
990 char *command, const char *arg1, const char *arg2)
991 {
992 struct DO_CMD *cmd;
993
994 cmd = malloc(sizeof(*cmd));
995 cmd->argc = 1;
996 cmd->argv[0] = command;
997 if (arg1) {
998 cmd->argc = 2;
999 cmd->argv[1] = (char*)arg1;
1000 }
1001 if (arg2) {
1002 cmd->argc = 3;
1003 cmd->argv[2] = (char*)arg2;
1004 }
1005 XtAddCallback(widget,callback,command_cb,cmd);
1006 XtAddCallback(widget,XmNdestroyCallback,free_cb,cmd);
1007 }
1008
1009 static Widget
1010 add_cmd_menuitem(const char *n, int nr, Widget parent, const char *l,
1011 char *k, char *a, int toggle,
1012 char *c, const char *arg1, const char *arg2)
1013 {
1014 char name[16];
1015 XmString label,accel;
1016 Widget w;
1017 WidgetClass class;
1018 String callback;
1019
1020 sprintf(name,"%.10s%d",n,nr);
1021 label = XmStringGenerate((char*)l, NULL, XmMULTIBYTE_TEXT, NULL);
1022 if (toggle) {
1023 class = xmToggleButtonWidgetClass;
1024 callback = XmNvalueChangedCallback;
1025 } else {
1026 class = xmPushButtonWidgetClass;
1027 callback = XmNactivateCallback;
1028 }
1029 if (k && a) {
1030 accel = XmStringGenerate(k, NULL, XmMULTIBYTE_TEXT, NULL);
1031 w = XtVaCreateManagedWidget(name,class,parent,
1032 XmNlabelString,label,
1033 XmNacceleratorText,accel,
1034 XmNaccelerator,a,
1035 NULL);
1036 } else {
1037 w = XtVaCreateManagedWidget(name,class,parent,
1038 XmNlabelString,label,
1039 NULL);
1040 }
1041 if (toggle)
1042 XtVaSetValues(w,XmNindicatorType,toggle,NULL);
1043 if (c)
1044 add_cmd_callback(w,callback,c,arg1,arg2);
1045 XmStringFree(label);
1046 return w;
1047 }
1048
1049 static void
1050 add_attr_option(Widget menu, struct ng_attribute *attr)
1051 {
1052 int i;
1053 struct motif_attribute *a;
1054
1055 a = malloc(sizeof(*a));
1056 memset(a,0,sizeof(*a));
1057 a->attr = attr;
1058
1059 switch (attr->type) {
1060 case ATTR_TYPE_CHOICE:
1061 a->widget = XmCreatePulldownMenu(menu,(char*)attr->name,NULL,0);
1062 XtVaCreateManagedWidget(attr->name,xmCascadeButtonWidgetClass,menu,
1063 XmNsubMenuId,a->widget,NULL);
1064 for (i = 0; attr->choices[i].str != NULL; i++)
1065 add_cmd_menuitem(attr->name, i, a->widget,
1066 attr->choices[i].str, NULL, NULL, XmONE_OF_MANY,
1067 "setattr",attr->name,attr->choices[i].str);
1068 break;
1069 case ATTR_TYPE_BOOL:
1070 a->widget = XtVaCreateManagedWidget(attr->name,
1071 xmToggleButtonWidgetClass,menu,
1072 NULL);
1073 add_cmd_callback(a->widget,XmNvalueChangedCallback,
1074 "setattr", attr->name, "toggle");
1075 break;
1076 }
1077 a->next = motif_attrs;
1078 motif_attrs = a;
1079 }
1080
1081 /* use more columns until the menu fits onto the screen */
1082 static void
1083 menu_cols_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
1084 {
1085 Dimension height,num;
1086 int i = 8;
1087
1088 for (;i;i--) {
1089 XtVaGetValues(widget,
1090 XtNheight,&height,
1091 XmNnumColumns,&num,
1092 NULL);
1093 if (height < XtScreen(widget)->height - 100)
1094 break;
1095 XtVaSetValues(widget,XmNnumColumns,num+1,NULL);
1096 }
1097 }
1098
1099 void
1100 channel_menu(void)
1101 {
1102 struct {
1103 char *name;
1104 Widget menu1;
1105 Widget menu2;
1106 } *sub = NULL;
1107 int subs = 0;
1108
1109 Widget menu1,menu2;
1110 char ctrl[16],key[32],accel[64];
1111 int i,j;
1112
1113 if (0 == st_menu2) {
1114 st_menu2 = XmCreatePopupMenu(tv,"stationsM",NULL,0);
1115 XtAddEventHandler(tv,ButtonPressMask,False,popup_eh,st_menu2);
1116 XtAddCallback(st_menu2,XmNmapCallback,menu_cols_cb,NULL);
1117 }
1118
1119 /* delete entries */
1120 delete_children(st_menu1);
1121 delete_children(st_menu2);
1122
1123 /* rebuild everything */
1124 for (i = 0; i < count; i++) {
1125 if (channels[i]->key) {
1126 if (2 == sscanf(channels[i]->key,
1127 "%15[A-Za-z0-9_]+%31[A-Za-z0-9_]",
1128 ctrl,key)) {
1129 sprintf(accel,"%s<Key>%s",ctrl,key);
1130 } else {
1131 sprintf(accel,"<Key>%s",channels[i]->key);
1132 }
1133 } else {
1134 accel[0] = 0;
1135 }
1136
1137 menu1 = st_menu1;
1138 menu2 = st_menu2;
1139 if (0 != strcmp(channels[i]->group,"main")) {
1140 for (j = 0; j < subs; j++)
1141 if (0 == strcmp(channels[i]->group,sub[j].name))
1142 break;
1143 if (j == subs) {
1144 subs++;
1145 sub = realloc(sub, subs * sizeof(*sub));
1146 sub[j].name = channels[i]->group;
1147 sub[j].menu1 = XmCreatePulldownMenu(st_menu1,
1148 channels[i]->group,
1149 NULL,0);
1150 sub[j].menu2 = XmCreatePulldownMenu(st_menu2,
1151 channels[i]->group,
1152 NULL,0);
1153 XtVaCreateManagedWidget(channels[i]->group,
1154 xmCascadeButtonWidgetClass, st_menu1,
1155 XmNsubMenuId, sub[j].menu1,
1156 NULL);
1157 XtVaCreateManagedWidget(channels[i]->group,
1158 xmCascadeButtonWidgetClass, st_menu2,
1159 XmNsubMenuId, sub[j].menu2,
1160 NULL);
1161 }
1162 menu1 = sub[j].menu1;
1163 menu2 = sub[j].menu2;
1164 }
1165
1166 add_cmd_menuitem("station", i, menu1,
1167 channels[i]->name, channels[i]->key, accel, FALSE,
1168 "setstation",channels[i]->name,NULL);
1169 add_cmd_menuitem("station", i, menu2,
1170 channels[i]->name, channels[i]->key, accel, FALSE,
1171 "setstation",channels[i]->name,NULL);
1172 if (NULL == channels[i]->button)
1173 chan_makebutton(channels[i]);
1174 }
1175 free(sub);
1176 calc_frequencies();
1177 }
1178
1179 static void
1180 chan_resize_eh(Widget widget, XtPointer client_data, XEvent *event, Boolean *d)
1181 {
1182 Widget clip;
1183 Dimension width;
1184
1185 XtVaGetValues(chan_viewport,XmNclipWindow,&clip,NULL);
1186 XtVaGetValues(clip,XtNwidth,&width,NULL);
1187 XtVaSetValues(chan_box,XtNwidth,width,NULL);
1188 }
1189
1190 static void
1191 chan_del_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
1192 {
1193 Widget button = clientdata;
1194 int i;
1195
1196 for (i = 0; i < count; i++)
1197 if (button == channels[i]->button)
1198 break;
1199 if (i == count)
1200 return;
1201 XtDestroyWidget(channels[i]->button);
1202 del_channel(i);
1203 if (cur_sender == i)
1204 cur_sender = -1;
1205 if (cur_sender > i)
1206 cur_sender--;
1207 channel_menu();
1208 }
1209
1210 static void
1211 chan_makebutton(struct CHANNEL *channel)
1212 {
1213 Widget menu,push;
1214
1215 if (NULL != channel->button)
1216 return;
1217
1218 channel->button =
1219 XtVaCreateManagedWidget(channel->name,
1220 xmPushButtonWidgetClass, chan_box,
1221 NULL);
1222 add_cmd_callback(channel->button, XmNactivateCallback,
1223 "setstation", channel->name, NULL);
1224 menu = XmCreatePopupMenu(channel->button,"menu",NULL,0);
1225 XtAddEventHandler(channel->button,ButtonPressMask,False,popup_eh,menu);
1226 push = XtVaCreateManagedWidget("del",xmPushButtonWidgetClass,menu,NULL);
1227 XtAddCallback(push,XmNactivateCallback,chan_del_cb,channel->button);
1228 push = XtVaCreateManagedWidget("edit",xmPushButtonWidgetClass,menu,NULL);
1229 XtAddCallback(push,XmNactivateCallback,chan_edit_cb,channel->button);
1230 }
1231
1232 static void
1233 create_control(void)
1234 {
1235 Widget form,menubar,menu,smenu,push,clip,tool,rc,fr;
1236 char action[256];
1237 int i;
1238
1239 control_shell = XtVaAppCreateShell("control","MoTV",
1240 topLevelShellWidgetClass,
1241 dpy,
1242 XtNclientLeader,app_shell,
1243 XtNvisual,vinfo.visual,
1244 XtNcolormap,colormap,
1245 XtNdepth,vinfo.depth,
1246 XmNdeleteResponse,XmDO_NOTHING,
1247 NULL);
1248 XmdRegisterEditres(control_shell);
1249 XmAddWMProtocolCallback(control_shell,WM_DELETE_WINDOW,
1250 popupdown_cb,control_shell);
1251 form = XtVaCreateManagedWidget("form", xmFormWidgetClass, control_shell,
1252 NULL);
1253
1254 /* menbar */
1255 menubar = XmCreateMenuBar(form,"menubar",NULL,0);
1256 XtManageChild(menubar);
1257
1258 tool = XtVaCreateManagedWidget("tool",xmRowColumnWidgetClass,form,NULL);
1259
1260 /* status line */
1261 rc = XtVaCreateManagedWidget("status", xmRowColumnWidgetClass, form,
1262 NULL);
1263 fr = XtVaCreateManagedWidget("f", xmFrameWidgetClass, rc, NULL);
1264 st_freq = XtVaCreateManagedWidget("freq", xmLabelWidgetClass, fr, NULL);
1265 fr = XtVaCreateManagedWidget("f", xmFrameWidgetClass, rc, NULL);
1266 st_chan = XtVaCreateManagedWidget("chan", xmLabelWidgetClass, fr, NULL);
1267 fr = XtVaCreateManagedWidget("f", xmFrameWidgetClass, rc, NULL);
1268 st_name = XtVaCreateManagedWidget("name", xmLabelWidgetClass, fr, NULL);
1269 fr = XtVaCreateManagedWidget("f", xmFrameWidgetClass, rc, NULL);
1270 st_key = XtVaCreateManagedWidget("key", xmLabelWidgetClass, fr, NULL);
1271 #if 0
1272 fr = XtVaCreateManagedWidget("f", xmFrameWidgetClass, rc, NULL);
1273 st_other = XtVaCreateManagedWidget("other", xmLabelWidgetClass, fr, NULL);
1274 #endif
1275
1276 /* channel buttons */
1277 chan_viewport = XmCreateScrolledWindow(form,"view",NULL,0);
1278 XtManageChild(chan_viewport);
1279 chan_box = XtVaCreateManagedWidget("box", xmRowColumnWidgetClass,
1280 chan_viewport, NULL);
1281 XtVaGetValues(chan_viewport,XmNclipWindow,&clip,NULL);
1282 XtAddEventHandler(clip,StructureNotifyMask, True,
1283 chan_resize_eh, NULL);
1284
1285 /* menu - file */
1286 menu = XmCreatePulldownMenu(menubar,"fileM",NULL,0);
1287 XtVaCreateManagedWidget("file",xmCascadeButtonWidgetClass,menubar,
1288 XmNsubMenuId,menu,NULL);
1289 push = XtVaCreateManagedWidget("rec",xmPushButtonWidgetClass,menu,NULL);
1290 XtAddCallback(push,XmNactivateCallback,popupdown_cb,str_shell);
1291 XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
1292 push = XtVaCreateManagedWidget("quit",xmPushButtonWidgetClass,menu,NULL);
1293 XtAddCallback(push,XmNactivateCallback,ExitCB,NULL);
1294
1295 #if 1
1296 /* menu - edit */
1297 if (f_drv & CAN_CAPTURE) {
1298 menu = XmCreatePulldownMenu(menubar,"editM",NULL,0);
1299 XtVaCreateManagedWidget("edit",xmCascadeButtonWidgetClass,menubar,
1300 XmNsubMenuId,menu,NULL);
1301 push = XtVaCreateManagedWidget("copy",xmPushButtonWidgetClass,menu,
1302 NULL);
1303 XtAddCallback(push,XmNactivateCallback,action_cb,"Ipc(clipboard)");
1304 }
1305 #endif
1306
1307 /* menu - tv stations */
1308 st_menu1 = XmCreatePulldownMenu(menubar,"stationsM",NULL,0);
1309 XtVaCreateManagedWidget("stations",xmCascadeButtonWidgetClass,menubar,
1310 XmNsubMenuId,st_menu1,NULL);
1311 XtAddCallback(st_menu1,XmNmapCallback,menu_cols_cb,NULL);
1312
1313 /* menu - tools (name?) */
1314 menu = XmCreatePulldownMenu(menubar,"toolsM",NULL,0);
1315 XtVaCreateManagedWidget("tools",xmCascadeButtonWidgetClass,menubar,
1316 XmNsubMenuId,menu,NULL);
1317 w_full = XtVaCreateManagedWidget("full",xmToggleButtonWidgetClass,menu,
1318 NULL);
1319 XtAddCallback(w_full,XmNvalueChangedCallback,action_cb,
1320 "Command(fullscreen)");
1321 push = XtVaCreateManagedWidget("ontop",xmToggleButtonWidgetClass,menu,
1322 NULL);
1323 b_ontop = push;
1324 XtAddCallback(push,XmNvalueChangedCallback,ontop_cb,NULL);
1325 push = XtVaCreateManagedWidget("levels",xmPushButtonWidgetClass,menu,
1326 NULL);
1327 XtAddCallback(push,XmNactivateCallback,action_cb,"Popup(levels)");
1328 XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
1329 push = XtVaCreateManagedWidget("st_up",xmPushButtonWidgetClass,menu,NULL);
1330 XtAddCallback(push,XmNactivateCallback,action_cb,
1331 "Command(setstation,next)");
1332 push = XtVaCreateManagedWidget("st_dn",xmPushButtonWidgetClass,menu,NULL);
1333 XtAddCallback(push,XmNactivateCallback,action_cb,
1334 "Command(setstation,prev)");
1335
1336 /* menu - tools / tuner */
1337 smenu = XmCreatePulldownMenu(menu,"tuneM",NULL,0);
1338 XtVaCreateManagedWidget("tune",xmCascadeButtonWidgetClass,menu,
1339 XmNsubMenuId,smenu,NULL);
1340 push = XtVaCreateManagedWidget("ch_up",xmPushButtonWidgetClass,smenu,NULL);
1341 XtAddCallback(push,XmNactivateCallback,action_cb,
1342 "Command(setchannel,next)");
1343 push = XtVaCreateManagedWidget("ch_dn",xmPushButtonWidgetClass,smenu,NULL);
1344 XtAddCallback(push,XmNactivateCallback,action_cb,
1345 "Command(setchannel,prev)");
1346 push = XtVaCreateManagedWidget("fi_up",xmPushButtonWidgetClass,smenu,NULL);
1347 XtAddCallback(push,XmNactivateCallback,action_cb,
1348 "Command(setchannel,fine_up)");
1349 push = XtVaCreateManagedWidget("fi_dn",xmPushButtonWidgetClass,smenu,NULL);
1350 XtAddCallback(push,XmNactivateCallback,action_cb,
1351 "Command(setchannel,fine_down)");
1352
1353 /* menu - tools / capture */
1354 smenu = XmCreatePulldownMenu(menu,"grabM",NULL,0);
1355 XtVaCreateManagedWidget("grab",xmCascadeButtonWidgetClass,menu,
1356 XmNsubMenuId,smenu,NULL);
1357 push = XtVaCreateManagedWidget("ppm_f",xmPushButtonWidgetClass,smenu,NULL);
1358 XtAddCallback(push,XmNactivateCallback,action_cb,
1359 "Command(snap,ppm,full)");
1360 push = XtVaCreateManagedWidget("ppm_w",xmPushButtonWidgetClass,smenu,NULL);
1361 XtAddCallback(push,XmNactivateCallback,action_cb,
1362 "Command(snap,ppm,win)");
1363 push = XtVaCreateManagedWidget("jpg_f",xmPushButtonWidgetClass,smenu,NULL);
1364 XtAddCallback(push,XmNactivateCallback,action_cb,
1365 "Command(snap,jpeg,full)");
1366 push = XtVaCreateManagedWidget("jpg_w",xmPushButtonWidgetClass,smenu,NULL);
1367 XtAddCallback(push,XmNactivateCallback,action_cb,
1368 "Command(snap,jpeg,win)");
1369
1370 /* menu - tools / aspect ratio */
1371 smenu = XmCreatePulldownMenu(menu,"ratioM",NULL,0);
1372 XtVaCreateManagedWidget("ratio",xmCascadeButtonWidgetClass,menu,
1373 XmNsubMenuId,smenu,NULL);
1374 push = XtVaCreateManagedWidget("r_no",xmPushButtonWidgetClass,smenu,NULL);
1375 XtAddCallback(push,XmNactivateCallback,action_cb,"Ratio(0,0)");
1376 push = XtVaCreateManagedWidget("r_43",xmPushButtonWidgetClass,smenu,NULL);
1377 XtAddCallback(push,XmNactivateCallback,action_cb,"Ratio(4,3)");
1378
1379 /* menu - tools / launch */
1380 launch_menu = XmCreatePulldownMenu(menu,"launchM",NULL,0);
1381 XtVaCreateManagedWidget("launch",xmCascadeButtonWidgetClass,menu,
1382 XmNsubMenuId,launch_menu,NULL);
1383
1384 #ifdef HAVE_ZVBI
1385 /* menu - tools / subtitles */
1386 smenu = XmCreatePulldownMenu(menu,"subM",NULL,0);
1387 XtVaCreateManagedWidget("sub",xmCascadeButtonWidgetClass,menu,
1388 XmNsubMenuId,smenu,NULL);
1389 push = XtVaCreateManagedWidget("s_off",xmPushButtonWidgetClass,smenu,NULL);
1390 XtAddCallback(push,XmNactivateCallback,action_cb,"Vtx(stop)");
1391 push = XtVaCreateManagedWidget("s_150",xmPushButtonWidgetClass,smenu,NULL);
1392 XtAddCallback(push,XmNactivateCallback,action_cb,"Vtx(start,150)");
1393 push = XtVaCreateManagedWidget("s_333",xmPushButtonWidgetClass,smenu,NULL);
1394 XtAddCallback(push,XmNactivateCallback,action_cb,"Vtx(start,333)");
1395 push = XtVaCreateManagedWidget("s_777",xmPushButtonWidgetClass,smenu,NULL);
1396 XtAddCallback(push,XmNactivateCallback,action_cb,"Vtx(start,777)");
1397 push = XtVaCreateManagedWidget("s_801",xmPushButtonWidgetClass,smenu,NULL);
1398 XtAddCallback(push,XmNactivateCallback,action_cb,"Vtx(start,801)");
1399 push = XtVaCreateManagedWidget("s_888",xmPushButtonWidgetClass,smenu,NULL);
1400 XtAddCallback(push,XmNactivateCallback,action_cb,"Vtx(start,888)");
1401 #endif
1402
1403 /* menu - internal options */
1404 opt_menu = menu = XmCreatePulldownMenu(menubar,"optionsM",NULL,0);
1405 XtVaCreateManagedWidget("options",xmCascadeButtonWidgetClass,menubar,
1406 XmNsubMenuId,menu,NULL);
1407 push = XtVaCreateManagedWidget("add",xmPushButtonWidgetClass,menu,NULL);
1408 XtAddCallback(push,XmNactivateCallback,chan_add_cb,NULL);
1409 #ifdef HAVE_ZVBI
1410 push = XtVaCreateManagedWidget("scan",xmPushButtonWidgetClass,menu,NULL);
1411 XtAddCallback(push,XmNactivateCallback,chscan_cb,NULL);
1412 #endif
1413 #if 1
1414 push = XtVaCreateManagedWidget("pref",xmPushButtonWidgetClass,menu,NULL);
1415 XtAddCallback(push,XmNactivateCallback,pref_manage_cb,NULL);
1416 #endif
1417 push = XtVaCreateManagedWidget("save",xmPushButtonWidgetClass,menu,NULL);
1418 XtAddCallback(push,XmNactivateCallback,chan_save_cb,NULL);
1419 XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
1420
1421 cap_menu = XmCreatePulldownMenu(menu,"captureM",NULL,0);
1422 XtVaCreateManagedWidget("capture",xmCascadeButtonWidgetClass,menu,
1423 XmNsubMenuId,cap_menu,NULL);
1424 push = XtVaCreateManagedWidget("overlay",xmToggleButtonWidgetClass,
1425 cap_menu,XmNindicatorType,XmONE_OF_MANY,
1426 NULL);
1427 add_cmd_callback(push,XmNvalueChangedCallback,"capture","overlay",NULL);
1428 push = XtVaCreateManagedWidget("grabdisplay",xmToggleButtonWidgetClass,
1429 cap_menu,XmNindicatorType,XmONE_OF_MANY,
1430 NULL);
1431 add_cmd_callback(push,XmNvalueChangedCallback,"capture","grab",NULL);
1432 push = XtVaCreateManagedWidget("none",xmToggleButtonWidgetClass,
1433 cap_menu,XmNindicatorType,XmONE_OF_MANY,
1434 NULL);
1435 add_cmd_callback(push,XmNvalueChangedCallback,"capture","off",NULL);
1436
1437 freq_menu = XmCreatePulldownMenu(menu,"freqM",NULL,0);
1438 XtVaCreateManagedWidget("freq",xmCascadeButtonWidgetClass,menu,
1439 XmNsubMenuId,freq_menu,NULL);
1440 for (i = 0; chanlist_names[i].str != NULL; i++) {
1441 push = XtVaCreateManagedWidget(chanlist_names[i].str,
1442 xmToggleButtonWidgetClass,freq_menu,
1443 XmNindicatorType,XmONE_OF_MANY,
1444 NULL);
1445 add_cmd_callback(push,XmNvalueChangedCallback,
1446 "setfreqtab", chanlist_names[i].str, NULL);
1447 }
1448 XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
1449
1450 /* menu - filter */
1451 if ((f_drv & CAN_CAPTURE) && !list_empty(&ng_filters)) {
1452 struct list_head *item;
1453 struct ng_filter *filter;
1454
1455 menu = XmCreatePulldownMenu(menubar,"filterM",NULL,0);
1456 XtVaCreateManagedWidget("filter",xmCascadeButtonWidgetClass,menubar,
1457 XmNsubMenuId,menu,NULL);
1458 push = XtVaCreateManagedWidget("fnone",
1459 xmPushButtonWidgetClass,menu,
1460 NULL);
1461 XtAddCallback(push,XmNactivateCallback,action_cb,"Filter()");
1462 list_for_each(item,&ng_filters) {
1463 filter = list_entry(item, struct ng_filter, list);
1464 push = XtVaCreateManagedWidget(filter->name,
1465 xmPushButtonWidgetClass,menu,
1466 NULL);
1467 sprintf(action,"Filter(%s)",filter->name);
1468 XtAddCallback(push,XmNactivateCallback,action_cb,strdup(action));
1469 }
1470 XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
1471 push = XtVaCreateManagedWidget("fopts",xmPushButtonWidgetClass,menu,
1472 NULL);
1473 XtAddCallback(push,XmNactivateCallback,action_cb,"Popup(filter)");
1474 }
1475
1476 /* menu - help */
1477 menu = XmCreatePulldownMenu(menubar,"helpM",NULL,0);
1478 push = XtVaCreateManagedWidget("help",xmCascadeButtonWidgetClass,menubar,
1479 XmNsubMenuId,menu,NULL);
1480 XtVaSetValues(menubar,XmNmenuHelpWidget,push,NULL);
1481 push = XtVaCreateManagedWidget("man",xmPushButtonWidgetClass,menu,NULL);
1482 XtAddCallback(push,XmNactivateCallback,man_cb,"motv");
1483 XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL);
1484 push = XtVaCreateManagedWidget("about",xmPushButtonWidgetClass,menu,NULL);
1485 XtAddCallback(push,XmNactivateCallback,about_cb,NULL);
1486
1487 /* toolbar */
1488 push = XtVaCreateManagedWidget("prev",xmPushButtonWidgetClass,tool,NULL);
1489 XtAddCallback(push,XmNactivateCallback,action_cb,
1490 "Command(setstation,prev)");
1491 push = XtVaCreateManagedWidget("next",xmPushButtonWidgetClass,tool,NULL);
1492 XtAddCallback(push,XmNactivateCallback,action_cb,
1493 "Command(setstation,next)");
1494
1495 XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,tool,NULL);
1496 push = XtVaCreateManagedWidget("snap",xmPushButtonWidgetClass,tool,NULL);
1497 XtAddCallback(push,XmNactivateCallback,action_cb,
1498 "Command(snap,jpeg,full)");
1499 push = XtVaCreateManagedWidget("movie",xmPushButtonWidgetClass,tool,NULL);
1500 XtAddCallback(push,XmNactivateCallback,popupdown_cb,str_shell);
1501 push = XtVaCreateManagedWidget("mute",xmPushButtonWidgetClass,tool,NULL);
1502 XtAddCallback(push,XmNactivateCallback,action_cb,
1503 "Command(volume,mute,toggle)");
1504
1505 XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,tool,NULL);
1506 push = XtVaCreateManagedWidget("exit",xmPushButtonWidgetClass,tool,NULL);
1507 XtAddCallback(push,XmNactivateCallback,ExitCB,NULL);
1508 }
1509
1510 static void create_attr_widgets(void)
1511 {
1512 struct ng_attribute *attr;
1513 Widget push;
1514 XmString label,accel;
1515 char str[100],key[32],ctrl[16];
1516 Arg argv[8];
1517 Cardinal argc;
1518 int i;
1519
1520 /* menu - driver options
1521 input + norm */
1522 attr = ng_attr_byid(attrs,ATTR_ID_NORM);
1523 if (NULL != attr)
1524 add_attr_option(opt_menu, attr);
1525 attr = ng_attr_byid(attrs,ATTR_ID_INPUT);
1526 if (NULL != attr)
1527 add_attr_option(opt_menu,attr);
1528 attr = ng_attr_byid(attrs,ATTR_ID_AUDIO_MODE);
1529 if (NULL != attr)
1530 add_attr_option(opt_menu,attr);
1531 for (attr = attrs; attr->name != NULL; attr++) {
1532 if (attr->id < ATTR_ID_COUNT)
1533 continue;
1534 if (attr->type != ATTR_TYPE_CHOICE)
1535 continue;
1536 add_attr_option(opt_menu,attr);
1537 }
1538
1539 /* bools */
1540 attr = ng_attr_byid(attrs,ATTR_ID_MUTE);
1541 if (NULL != attr)
1542 add_attr_option(opt_menu,attr);
1543 for (attr = attrs; attr->name != NULL; attr++) {
1544 if (attr->id < ATTR_ID_COUNT)
1545 continue;
1546 if (attr->type != ATTR_TYPE_BOOL)
1547 continue;
1548 add_attr_option(opt_menu,attr);
1549 }
1550
1551 /* integer (scales) */
1552 push = XtVaCreateManagedWidget("scale",xmPushButtonWidgetClass,
1553 opt_menu,NULL);
1554 XtAddCallback(push,XmNactivateCallback,action_cb,"Popup(scale)");
1555
1556 /* launch menu entries */
1557 for (i = 0; i < nlaunch; i++) {
1558 argc = 0;
1559 label = NULL;
1560 accel = NULL;
1561
1562 label = XmStringGenerate(launch[i].name,NULL,XmMULTIBYTE_TEXT,NULL);
1563 XtSetArg(argv[argc],XmNlabelString,label); argc++;
1564 if (NULL != launch[i].key) {
1565 accel = XmStringGenerate(launch[i].key,NULL,XmMULTIBYTE_TEXT,NULL);
1566 XtSetArg(argv[argc],XmNacceleratorText,accel); argc++;
1567 if (2 == sscanf(launch[i].key,"%15[A-Za-z0-9_]+%31[A-Za-z0-9_]",
1568 ctrl,key))
1569 sprintf(str,"%s<Key>%s",ctrl,key);
1570 else
1571 sprintf(str,"<Key>%s",launch[i].key);
1572 XtSetArg(argv[argc],XmNaccelerator,str); argc++;
1573 }
1574 push = XtCreateManagedWidget(launch[i].name,
1575 xmPushButtonWidgetClass,
1576 launch_menu,argv,argc);
1577 if (label)
1578 XmStringFree(label);
1579 if (accel)
1580 XmStringFree(accel);
1581
1582 /* translations */
1583 strcat(str,": Launch(");
1584 strcat(str,launch[i].name);
1585 strcat(str,")");
1586 XtOverrideTranslations(tv,XtParseTranslationTable(str));
1587
1588 /* button callback */
1589 sprintf(str,"Launch(%s)",launch[i].name);
1590 XtAddCallback(push,XmNactivateCallback,action_cb,
1591 strdup(str));
1592 }
1593 }
1594
1595 static void
1596 create_scale(void)
1597 {
1598 Widget form,attach;
1599 struct ng_attribute *attr;
1600 struct motif_attribute *a;
1601 int vol = 0;
1602 XmString str;
1603
1604 scale_shell = XtVaAppCreateShell("scale","MoTV",
1605 topLevelShellWidgetClass,
1606 dpy,
1607 XtNclientLeader,app_shell,
1608 XtNvisual,vinfo.visual,
1609 XtNcolormap,colormap,
1610 XtNdepth,vinfo.depth,
1611 XmNdeleteResponse,XmDO_NOTHING,
1612 NULL);
1613 XmdRegisterEditres(scale_shell);
1614 XmAddWMProtocolCallback(scale_shell,WM_DELETE_WINDOW,
1615 popupdown_cb,scale_shell);
1616 form = XtVaCreateManagedWidget("form", xmFormWidgetClass, scale_shell,
1617 NULL);
1618 /* scales */
1619 attach = NULL;
1620 for (attr = attrs; attr->name != NULL; attr++) {
1621 if (attr->type != ATTR_TYPE_INTEGER)
1622 continue;
1623 if (attr->id == ATTR_ID_VOLUME) {
1624 if (vol)
1625 continue;
1626 vol++;
1627 }
1628 a = malloc(sizeof(*a));
1629 memset(a,0,sizeof(*a));
1630 a->attr = attr;
1631 a->next = motif_attrs;
1632 motif_attrs = a;
1633 if (a->attr->id < ATTR_ID_COUNT) {
1634 a->widget = XtVaCreateManagedWidget(attr->name,
1635 xmScaleWidgetClass, form,
1636 XmNtopWidget,attach,
1637 XmNminimum,attr->min,
1638 XmNmaximum,attr->max,
1639 XmNdecimalPoints,attr->points,
1640 NULL);
1641 } else {
1642 str = XmStringGenerate((char*)attr->name, NULL,
1643 XmMULTIBYTE_TEXT, NULL);
1644 a->widget = XtVaCreateManagedWidget(attr->name,
1645 xmScaleWidgetClass, form,
1646 XmNtopWidget,attach,
1647 XmNtitleString,str,
1648 XmNminimum,attr->min,
1649 XmNmaximum,attr->max,
1650 XmNdecimalPoints,attr->points,
1651 NULL);
1652 XmStringFree(str);
1653 }
1654 XtAddCallback(a->widget,XmNvalueChangedCallback,scroll_cb,a);
1655 XtAddCallback(a->widget,XmNdragCallback,scroll_cb,a);
1656 attach = a->widget;
1657 }
1658 }
1659
1660 /*----------------------------------------------------------------------*/
1661
1662 #if 0
1663 void create_chanwin(void)
1664 {
1665 Widget form,clip,menu,push;
1666
1667 }
1668 #endif
1669
1670 /* gets called before switching away from a channel */
1671 static void
1672 pixit(void)
1673 {
1674 Pixmap pix;
1675 struct ng_video_fmt fmt;
1676 struct ng_video_buf *buf;
1677
1678 if (cur_sender == -1)
1679 return;
1680
1681 /* save picture settings */
1682 channels[cur_sender]->color = cur_attrs[ATTR_ID_COLOR];
1683 channels[cur_sender]->bright = cur_attrs[ATTR_ID_BRIGHT];
1684 channels[cur_sender]->hue = cur_attrs[ATTR_ID_HUE];
1685 channels[cur_sender]->contrast = cur_attrs[ATTR_ID_CONTRAST];
1686 channels[cur_sender]->input = cur_attrs[ATTR_ID_INPUT];
1687 channels[cur_sender]->norm = cur_attrs[ATTR_ID_NORM];
1688
1689 if (0 == pix_width || 0 == pix_height)
1690 return;
1691
1692 /* capture mini picture */
1693 if (!(f_drv & CAN_CAPTURE))
1694 return;
1695
1696 video_gd_suspend();
1697 memset(&fmt,0,sizeof(fmt));
1698 fmt.fmtid = x11_dpy_fmtid;
1699 fmt.width = pix_width;
1700 fmt.height = pix_height;
1701 if (NULL == (buf = ng_grabber_get_image(&fmt)))
1702 goto done1;
1703 buf = ng_filter_single(cur_filter,buf);
1704 if (0 == (pix = x11_create_pixmap(dpy,&vinfo,buf)))
1705 goto done2;
1706 x11_label_pixmap(dpy,colormap,pix,buf->fmt.height,
1707 channels[cur_sender]->name);
1708 XtVaSetValues(channels[cur_sender]->button,
1709 XmNlabelPixmap,pix,
1710 XmNlabelType,XmPIXMAP,
1711 NULL);
1712 if (channels[cur_sender]->pixmap)
1713 XFreePixmap(dpy,channels[cur_sender]->pixmap);
1714 channels[cur_sender]->pixmap = pix;
1715 done2:
1716 ng_release_video_buf(buf);
1717 done1:
1718 video_gd_restart();
1719 }
1720
1721 /*----------------------------------------------------------------------*/
1722
1723 static void
1724 do_capture(int from, int to, int tmp_switch)
1725 {
1726 static int niced = 0;
1727 WidgetList children;
1728
1729 /* off */
1730 switch (from) {
1731 case CAPTURE_GRABDISPLAY:
1732 video_gd_stop();
1733 XClearArea(XtDisplay(tv), XtWindow(tv), 0,0,0,0, True);
1734 break;
1735 case CAPTURE_OVERLAY:
1736 video_overlay(0);
1737 break;
1738 }
1739
1740 /* on */
1741 switch (to) {
1742 case CAPTURE_GRABDISPLAY:
1743 if (!niced)
1744 nice(niced = 10);
1745 video_gd_start();
1746 break;
1747 case CAPTURE_OVERLAY:
1748 video_overlay(1);
1749 break;
1750 }
1751
1752 /* update menu */
1753 XtVaGetValues(cap_menu,XtNchildren,&children,NULL);
1754 XmToggleButtonSetState(children[0],to == CAPTURE_OVERLAY, False);
1755 XmToggleButtonSetState(children[1],to == CAPTURE_GRABDISPLAY,False);
1756 XmToggleButtonSetState(children[2],to == CAPTURE_OFF, False);
1757 }
1758
1759 static void
1760 do_motif_fullscreen(void)
1761 {
1762 XmToggleButtonSetState(w_full,!fs,False);
1763 do_fullscreen();
1764 }
1765
1766 /*----------------------------------------------------------------------*/
1767
1768 struct FILE_DATA {
1769 Widget filebox;
1770 Widget text;
1771 Widget push;
1772 };
1773
1774 static void
1775 file_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
1776 {
1777 XmFileSelectionBoxCallbackStruct *cb = call_data;
1778 struct FILE_DATA *h = clientdata;
1779 char *line;
1780
1781 if (cb->reason == XmCR_OK) {
1782 line = XmStringUnparse(cb->value,NULL,
1783 XmMULTIBYTE_TEXT,XmMULTIBYTE_TEXT,
1784 NULL,0,0);
1785 XmTextSetString(h->text,line);
1786 }
1787 XtUnmanageChild(h->filebox);
1788 }
1789
1790 static void
1791 file_browse_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
1792 {
1793 struct FILE_DATA *h = clientdata;
1794 Widget help;
1795 /* XmString str; */
1796
1797 if (NULL == h->filebox) {
1798 h->filebox = XmCreateFileSelectionDialog(h->push,"filebox",NULL,0);
1799 help = XmFileSelectionBoxGetChild(h->filebox,XmDIALOG_HELP_BUTTON);
1800 XtUnmanageChild(help);
1801 XtAddCallback(h->filebox,XmNokCallback,file_done_cb,h);
1802 XtAddCallback(h->filebox,XmNcancelCallback,file_done_cb,h);
1803 }
1804 #if 0
1805 str = XmStringGenerate(XmTextGetString(h->text),
1806 NULL, XmMULTIBYTE_TEXT, NULL);
1807 XtVaSetValues(h->filebox,XmNdirMask,str,NULL);
1808 XmStringFree(str);
1809 #endif
1810 XtManageChild(h->filebox);
1811 }
1812
1813 static void
1814 exec_player_cb(Widget widget, XtPointer client_data, XtPointer calldata)
1815 {
1816 char *filename;
1817
1818 filename = XmTextGetString(m_fvideo);
1819 exec_player(filename);
1820 }
1821
1822 static void
1823 create_strwin(void)
1824 {
1825 Widget form,push,rowcol,frame,fbox;
1826 struct FILE_DATA *h;
1827 Arg args[2];
1828
1829 str_shell = XtVaAppCreateShell("streamer", "MoTV",
1830 topLevelShellWidgetClass,
1831 dpy,
1832 XtNclientLeader,app_shell,
1833 XtNvisual,vinfo.visual,
1834 XtNcolormap,colormap,
1835 XtNdepth,vinfo.depth,
1836 XmNdeleteResponse,XmDO_NOTHING,
1837 NULL);
1838 XmdRegisterEditres(str_shell);
1839 XmAddWMProtocolCallback(str_shell,WM_DELETE_WINDOW,
1840 popupdown_cb,str_shell);
1841 form = XtVaCreateManagedWidget("form", xmFormWidgetClass, str_shell,
1842 NULL);
1843
1844 /* driver */
1845 frame = XtVaCreateManagedWidget("driverF", xmFrameWidgetClass, form, NULL);
1846 XtVaCreateManagedWidget("driverL",xmLabelWidgetClass,frame,NULL);
1847 driver_menu = XmCreatePulldownMenu(form,"driverM",NULL,0);
1848 XtSetArg(args[0],XmNsubMenuId,driver_menu);
1849 driver_option = XmCreateOptionMenu(frame,"driver",args,1);
1850 XtManageChild(driver_option);
1851
1852 /* video format + frame rate */
1853 frame = XtVaCreateManagedWidget("videoF", xmFrameWidgetClass, form, NULL);
1854 XtVaCreateManagedWidget("videoL",xmLabelWidgetClass,frame,NULL);
1855 rowcol = XtVaCreateManagedWidget("videoB",xmRowColumnWidgetClass,
1856 frame,NULL);
1857 video_menu = XmCreatePulldownMenu(rowcol,"videoM",NULL,0);
1858 XtSetArg(args[0],XmNsubMenuId,video_menu);
1859 video_option = XmCreateOptionMenu(rowcol,"video",args,1);
1860 XtManageChild(video_option);
1861 XtVaCreateManagedWidget("fpsL",xmLabelWidgetClass,rowcol,NULL);
1862 m_fps = XtVaCreateManagedWidget("fps",xmComboBoxWidgetClass,rowcol,NULL);
1863
1864 /* audio format + sample rate */
1865 frame = XtVaCreateManagedWidget("audioF", xmFrameWidgetClass, form, NULL);
1866 XtVaCreateManagedWidget("audioL",xmLabelWidgetClass,frame,NULL);
1867 rowcol = XtVaCreateManagedWidget("audioB",xmRowColumnWidgetClass,
1868 frame,NULL);
1869 audio_menu = XmCreatePulldownMenu(rowcol,"audioM",NULL,0);
1870 XtSetArg(args[0],XmNsubMenuId,audio_menu);
1871 audio_option = XmCreateOptionMenu(rowcol,"audio",args,1);
1872 XtManageChild(audio_option);
1873 XtVaCreateManagedWidget("rateL",xmLabelWidgetClass,rowcol,NULL);
1874 m_rate = XtVaCreateManagedWidget("rate",xmComboBoxWidgetClass,rowcol,NULL);
1875
1876 /* filenames */
1877 frame = XtVaCreateManagedWidget("fileF", xmFrameWidgetClass, form, NULL);
1878 XtVaCreateManagedWidget("fileL",xmLabelWidgetClass,frame,NULL);
1879 fbox = XtVaCreateManagedWidget("fbox",xmRowColumnWidgetClass,
1880 frame,NULL);
1881
1882 rowcol = XtVaCreateManagedWidget("fvideoB",xmRowColumnWidgetClass,
1883 fbox,NULL);
1884 XtVaCreateManagedWidget("fvideoL",xmLabelWidgetClass,rowcol,NULL);
1885 h = malloc(sizeof(*h));
1886 memset(h,0,sizeof(*h));
1887 h->text = XtVaCreateManagedWidget("fvideo",xmTextWidgetClass,
1888 rowcol,NULL);
1889 m_fvideo = h->text;
1890 h->push = XtVaCreateManagedWidget("files",xmPushButtonWidgetClass,rowcol,
1891 NULL);
1892 XtAddCallback(h->push,XmNactivateCallback,file_browse_cb,h);
1893
1894 rowcol = XtVaCreateManagedWidget("faudioB",xmRowColumnWidgetClass,
1895 fbox,NULL);
1896 m_faudioL = XtVaCreateManagedWidget("faudioL",xmLabelWidgetClass,rowcol,
1897 NULL);
1898 h = malloc(sizeof(*h));
1899 memset(h,0,sizeof(*h));
1900 h->text = XtVaCreateManagedWidget("faudio",xmTextWidgetClass,rowcol,
1901 NULL);
1902 m_faudio = h->text;
1903 h->push = XtVaCreateManagedWidget("files",xmPushButtonWidgetClass,rowcol,
1904 NULL);
1905 m_faudioB = h->push;
1906 XtAddCallback(h->push,XmNactivateCallback,file_browse_cb,h);
1907
1908 /* seperator, buttons */
1909 m_status = XtVaCreateManagedWidget("status",xmLabelWidgetClass,form,NULL);
1910 rowcol = XtVaCreateManagedWidget("buttons",xmRowColumnWidgetClass,form,
1911 NULL);
1912 push = XtVaCreateManagedWidget("rec", xmPushButtonWidgetClass, rowcol,
1913 NULL);
1914 add_cmd_callback(push,XmNactivateCallback, "movie","start",NULL);
1915 push = XtVaCreateManagedWidget("stop", xmPushButtonWidgetClass, rowcol,
1916 NULL);
1917 add_cmd_callback(push,XmNactivateCallback, "movie","stop",NULL);
1918 push = XtVaCreateManagedWidget("play", xmPushButtonWidgetClass, rowcol,
1919 NULL);
1920 XtAddCallback(push,XmNactivateCallback,exec_player_cb,NULL);
1921 push = XtVaCreateManagedWidget("cancel", xmPushButtonWidgetClass, rowcol,
1922 NULL);
1923 XtAddCallback(push,XmNactivateCallback, popupdown_cb, str_shell);
1924 }
1925
1926 static void
1927 update_movie_menus(void)
1928 {
1929 struct list_head *item;
1930 struct ng_writer *writer;
1931 static int first = 1;
1932 Widget push;
1933 XmString str;
1934 Boolean sensitive;
1935 int i;
1936
1937 /* drivers */
1938 if (first) {
1939 first = 0;
1940 i = 0;
1941 list_for_each(item,&ng_writers) {
1942 writer = list_entry(item, struct ng_writer, list);
1943 str = XmStringGenerate((char*)writer->desc,
1944 NULL, XmMULTIBYTE_TEXT, NULL);
1945 push = XtVaCreateManagedWidget(writer->name,
1946 xmPushButtonWidgetClass,driver_menu,
1947 XmNlabelString,str,
1948 NULL);
1949 XmStringFree(str);
1950 add_cmd_callback(push,XmNactivateCallback,
1951 "movie","driver",writer->name);
1952 if (NULL == movie_driver ||
1953 (NULL != mov_driver && 0 == strcasecmp(mov_driver,writer->name))) {
1954 movie_driver = writer;
1955 i_movie_driver = i;
1956 XtVaSetValues(driver_option,XmNmenuHistory,push,NULL);
1957 }
1958 i++;
1959 }
1960 }
1961
1962 /* audio formats */
1963 delete_children(audio_menu);
1964 for (i = 0; NULL != movie_driver->audio[i].name; i++) {
1965 str = XmStringGenerate
1966 ((char*)(movie_driver->audio[i].desc ?
1967 movie_driver->audio[i].desc :
1968 ng_afmt_to_desc[movie_driver->audio[i].fmtid]),
1969 NULL, XmMULTIBYTE_TEXT, NULL);
1970 push = XtVaCreateManagedWidget(movie_driver->audio[i].name,
1971 xmPushButtonWidgetClass,audio_menu,
1972 XmNlabelString,str,
1973 NULL);
1974 XmStringFree(str);
1975 add_cmd_callback(push,XmNactivateCallback,
1976 "movie","audio",movie_driver->audio[i].name);
1977 if (NULL != mov_audio)
1978 if (0 == strcasecmp(mov_audio,movie_driver->audio[i].name)) {
1979 XtVaSetValues(audio_option,XmNmenuHistory,push,NULL);
1980 movie_audio = i;
1981 }
1982 }
1983 str = XmStringGenerate("no sound", NULL, XmMULTIBYTE_TEXT, NULL);
1984 push = XtVaCreateManagedWidget("none",xmPushButtonWidgetClass,audio_menu,
1985 XmNlabelString,str,NULL);
1986 XmStringFree(str);
1987 add_cmd_callback(push,XmNactivateCallback, "movie","audio","none");
1988
1989 /* video formats */
1990 delete_children(video_menu);
1991 for (i = 0; NULL != movie_driver->video[i].name; i++) {
1992 str = XmStringGenerate
1993 ((char*)(movie_driver->video[i].desc ?
1994 movie_driver->video[i].desc :
1995 ng_vfmt_to_desc[movie_driver->video[i].fmtid]),
1996 NULL, XmMULTIBYTE_TEXT, NULL);
1997 push = XtVaCreateManagedWidget(movie_driver->video[i].name,
1998 xmPushButtonWidgetClass,video_menu,
1999 XmNlabelString,str,
2000 NULL);
2001 XmStringFree(str);
2002 add_cmd_callback(push,XmNactivateCallback,
2003 "movie","video",movie_driver->video[i].name);
2004 if (NULL != mov_video)
2005 if (0 == strcasecmp(mov_video,movie_driver->video[i].name)) {
2006 XtVaSetValues(video_option,XmNmenuHistory,push,NULL);
2007 movie_video = i;
2008 }
2009 }
2010
2011 /* need audio filename? */
2012 sensitive = movie_driver->combined ? False : True;
2013 XtVaSetValues(m_faudio, XtNsensitive,sensitive, NULL);
2014 XtVaSetValues(m_faudioL, XtNsensitive,sensitive, NULL);
2015 XtVaSetValues(m_faudioB, XtNsensitive,sensitive, NULL);
2016 }
2017
2018 static void
2019 init_movie_menus(void)
2020 {
2021 update_movie_menus();
2022
2023 if (mov_rate)
2024 do_va_cmd(3,"movie","rate",mov_rate);
2025 if (mov_fps)
2026 do_va_cmd(3,"movie","fps",mov_fps);
2027 }
2028
2029 static void
2030 do_movie_record(int argc, char **argv)
2031 {
2032 char *fvideo,*faudio;
2033 struct ng_video_fmt video;
2034 struct ng_audio_fmt audio;
2035 const struct ng_writer *wr;
2036 WidgetList children;
2037 Cardinal nchildren;
2038 Widget text;
2039 int i,rate,fps;
2040
2041 /* set parameters */
2042 if (argc > 1 && 0 == strcasecmp(argv[0],"driver")) {
2043 struct list_head *item;
2044 struct ng_writer *writer;
2045
2046 if (debug)
2047 fprintf(stderr,"set driver: %s\n",argv[1]);
2048 XtVaGetValues(driver_menu,XtNchildren,&children,
2049 XtNnumChildren,&nchildren,NULL);
2050 i = 0;
2051 list_for_each(item,&ng_writers) {
2052 writer = list_entry(item, struct ng_writer, list);
2053 if (0 == strcasecmp(argv[1],writer->name)) {
2054 movie_driver = writer;
2055 i_movie_driver = i;
2056 }
2057 i++;
2058 }
2059 update_movie_menus();
2060 }
2061 if (argc > 1 && 0 == strcasecmp(argv[0],"audio")) {
2062 if (debug)
2063 fprintf(stderr,"set audio: %s\n",argv[1]);
2064 XtVaGetValues(audio_menu,XtNchildren,&children,
2065 XtNnumChildren,&nchildren,NULL);
2066 for (i = 0; NULL != movie_driver->audio[i].name; i++) {
2067 if (0 == strcasecmp(argv[1],movie_driver->audio[i].name)) {
2068 XtVaSetValues(audio_option,XmNmenuHistory,children[i],NULL);
2069 movie_audio = i;
2070 }
2071 }
2072 if (0 == strcmp(argv[1],"none")) {
2073 XtVaSetValues(audio_option,XmNmenuHistory,children[i],NULL);
2074 movie_audio = i;
2075 }
2076 }
2077 if (argc > 1 && 0 == strcasecmp(argv[0],"video")) {
2078 if (debug)
2079 fprintf(stderr,"set video: %s\n",argv[1]);
2080 XtVaGetValues(video_menu,XtNchildren,&children,
2081 XtNnumChildren,&nchildren,NULL);
2082 for (i = 0; NULL != movie_driver->video[i].name; i++) {
2083 if (0 == strcasecmp(argv[1],movie_driver->video[i].name)) {
2084 XtVaSetValues(video_option,XmNmenuHistory,children[i],NULL);
2085 movie_video = i;
2086 }
2087 }
2088 }
2089 if (argc > 1 && 0 == strcasecmp(argv[0],"rate")) {
2090 XtVaGetValues(m_rate,XmNtextField,&text,NULL);
2091 XmTextSetString(text,argv[1]);
2092 }
2093 if (argc > 1 && 0 == strcasecmp(argv[0],"fps")) {
2094 XtVaGetValues(m_fps,XmNtextField,&text,NULL);
2095 XmTextSetString(text,argv[1]);
2096 }
2097 if (argc > 1 && 0 == strcasecmp(argv[0],"fvideo")) {
2098 XmTextSetString(m_fvideo,argv[1]);
2099 }
2100 if (argc > 1 && 0 == strcasecmp(argv[0],"faudio")) {
2101 XmTextSetString(m_faudio,argv[1]);
2102 }
2103
2104 /* start */
2105 if (argc > 0 && 0 == strcasecmp(argv[0],"start")) {
2106 if (0 != cur_movie)
2107 return; /* records already */
2108 cur_movie = 1;
2109 movie_blit = (cur_capture == CAPTURE_GRABDISPLAY);
2110 video_gd_suspend();
2111 XmToggleButtonSetState(levels_toggle,0,True);
2112
2113 fvideo = XmTextGetString(m_fvideo);
2114 faudio = XmTextGetString(m_faudio);
2115 fvideo = tilde_expand(fvideo);
2116 faudio = tilde_expand(faudio);
2117
2118 XtVaGetValues(m_rate,XmNtextField,&text,NULL);
2119 rate = atoi(XmTextGetString(text));
2120 XtVaGetValues(m_fps,XmNtextField,&text,NULL);
2121 fps = (int)(atof(XmTextGetString(text))*1000);
2122
2123 memset(&video,0,sizeof(video));
2124 memset(&audio,0,sizeof(audio));
2125
2126 wr = movie_driver;
2127 video.fmtid = wr->video[movie_video].fmtid;
2128 video.width = cur_tv_width;
2129 video.height = cur_tv_height;
2130 audio.fmtid = wr->audio[movie_audio].fmtid;
2131 audio.rate = rate;
2132
2133 movie_state = movie_writer_init
2134 (fvideo, faudio, wr,
2135 &video, wr->video[movie_video].priv, fps,
2136 &audio, wr->audio[movie_audio].priv, args.dspdev,
2137 args.bufcount,args.parallel);
2138 if (NULL == movie_state) {
2139 /* init failed */
2140 video_gd_restart();
2141 cur_movie = 0;
2142 /* hmm, not the most elegant way to flag an error ... */
2143 toolkit_set_label(m_status, "error [init]");
2144 return;
2145 }
2146 if (0 != movie_writer_start(movie_state)) {
2147 /* start failed */
2148 movie_writer_stop(movie_state);
2149 video_gd_restart();
2150 cur_movie = 0;
2151 /* hmm, not the most elegant way to flag an error ... */
2152 toolkit_set_label(m_status, "error [start]");
2153 return;
2154 }
2155 rec_work_id = XtAppAddWorkProc(app_context,rec_work,NULL);
2156 toolkit_set_label(m_status, "recording");
2157 return;
2158 }
2159
2160 /* stop */
2161 if (argc > 0 && 0 == strcasecmp(argv[0],"stop")) {
2162 if (0 == cur_movie)
2163 return; /* nothing to stop here */
2164
2165 movie_writer_stop(movie_state);
2166 XtRemoveWorkProc(rec_work_id);
2167 rec_work_id = 0;
2168 video_gd_restart();
2169 cur_movie = 0;
2170 return;
2171 }
2172 }
2173
2174 static void
2175 do_rec_status(char *message)
2176 {
2177 toolkit_set_label(m_status, message);
2178 }
2179
2180 /*----------------------------------------------------------------------*/
2181
2182 #ifdef HAVE_ZVBI
2183 #define CHSCAN 250
2184 static int chscan;
2185 static int chvbi;
2186 static XtIntervalId chtimer;
2187 static Widget chdlg,chscale;
2188
2189 static void
2190 chscan_timeout(XtPointer client_data, XtIntervalId *id)
2191 {
2192 struct CHANNEL *c;
2193 char title[32];
2194 XmString xmstr;
2195
2196 if (!x11_vbi_tuned()) {
2197 if (debug)
2198 fprintf(stderr,"scan [%s]: no station\n",chanlist[chscan].name);
2199 goto next_station;
2200 }
2201
2202 if (0 != x11_vbi_station[0]) {
2203 if (debug)
2204 fprintf(stderr,"scan [%s]: %s\n",chanlist[chscan].name,
2205 x11_vbi_station);
2206 c = add_channel(x11_vbi_station);
2207 c->cname = strdup(chanlist[chscan].name);
2208 c->channel = chscan;
2209 cur_sender = count-1;
2210 channel_menu();
2211 goto next_station;
2212 }
2213
2214 if (chvbi++ > 3) {
2215 if (debug)
2216 fprintf(stderr,"scan [%s]: no vbi name\n",chanlist[chscan].name);
2217 sprintf(title,"%s [no name]",chanlist[chscan].name);
2218 c = add_channel(title);
2219 c->cname = strdup(chanlist[chscan].name);
2220 c->channel = chscan;
2221 cur_sender = count-1;
2222 channel_menu();
2223 goto next_station;
2224 }
2225
2226 if (debug)
2227 fprintf(stderr,"scan [%s] vbi ...\n",chanlist[chscan].name);
2228 chtimer = XtAppAddTimeOut(app_context, CHSCAN, chscan_timeout, NULL);
2229 return;
2230
2231 next_station:
2232 chscan++;
2233 if (chscan >= chancount) {
2234 /* all done */
2235 x11_vbi_stop();
2236 chtimer = 0;
2237 if (count)
2238 do_va_cmd(2,"setchannel",channels[0]->name);
2239 XtDestroyWidget(chdlg);
2240 chdlg = NULL;
2241 return;
2242 }
2243 chvbi = 0;
2244 if (channel_switch_hook)
2245 channel_switch_hook();
2246 xmstr = XmStringGenerate(chanlist[chscan].name, NULL,
2247 XmMULTIBYTE_TEXT, NULL);
2248 XtVaSetValues(chscale,XmNtitleString,xmstr,NULL);
2249 XmStringFree(xmstr);
2250 XmScaleSetValue(chscale,chscan);
2251 do_va_cmd(2,"setchannel",chanlist[chscan]);
2252 x11_vbi_station[0] = 0;
2253 chtimer = XtAppAddTimeOut(app_context, CHSCAN, chscan_timeout,NULL);
2254 return;
2255 }
2256
2257 static void
2258 chscan_start_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
2259 {
2260 XmString xmstr;
2261
2262 /* check */
2263 if (!(f_drv & CAN_TUNE))
2264 return;
2265 if (channel_switch_hook)
2266 channel_switch_hook();
2267
2268 /* clear */
2269 while (count) {
2270 XtDestroyWidget(channels[count-1]->button);
2271 del_channel(count-1);
2272 }
2273 cur_sender = -1;
2274 channel_menu();
2275
2276 x11_vbi_start(args.vbidev);
2277 chscan = 0;
2278 chvbi = 0;
2279 xmstr = XmStringGenerate(chanlist[chscan].name, NULL,
2280 XmMULTIBYTE_TEXT, NULL);
2281 XtVaSetValues(chscale,XmNtitleString,xmstr,XmNmaximum,chancount,NULL);
2282 XmStringFree(xmstr);
2283 XmScaleSetValue(chscale,chscan);
2284 do_va_cmd(2,"setchannel",chanlist[chscan]);
2285 x11_vbi_station[0] = 0;
2286 chtimer = XtAppAddTimeOut(app_context, CHSCAN, chscan_timeout,NULL);
2287 }
2288
2289 static void
2290 chscan_cancel_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
2291 {
2292 x11_vbi_stop();
2293 if (chtimer)
2294 XtRemoveTimeOut(chtimer);
2295 chtimer = 0;
2296 XtDestroyWidget(chdlg);
2297 chdlg = NULL;
2298 }
2299
2300 static void
2301 chscan_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
2302 {
2303 Widget rc;
2304
2305 chdlg = XmCreatePromptDialog(control_shell,"chscan",NULL,0);
2306 XmdRegisterEditres(XtParent(chdlg));
2307 XtUnmanageChild(XmSelectionBoxGetChild(chdlg,XmDIALOG_SELECTION_LABEL));
2308 XtUnmanageChild(XmSelectionBoxGetChild(chdlg,XmDIALOG_HELP_BUTTON));
2309 XtUnmanageChild(XmSelectionBoxGetChild(chdlg,XmDIALOG_TEXT));
2310
2311 rc = XtVaCreateManagedWidget("rc",xmRowColumnWidgetClass,chdlg,NULL);
2312 XtVaCreateManagedWidget("hints",xmLabelWidgetClass,rc,NULL);
2313 chscale = XtVaCreateManagedWidget("channel",xmScaleWidgetClass,rc,NULL);
2314 XtRemoveAllCallbacks(XmSelectionBoxGetChild(chdlg,XmDIALOG_OK_BUTTON),
2315 XmNactivateCallback);
2316 XtAddCallback(XmSelectionBoxGetChild(chdlg,XmDIALOG_OK_BUTTON),
2317 XmNactivateCallback,chscan_start_cb,NULL);
2318 XtAddCallback(chdlg,XmNcancelCallback,chscan_cancel_cb,NULL);
2319 XtManageChild(chdlg);
2320 }
2321 #endif
2322
2323 /*----------------------------------------------------------------------*/
2324
2325 static void
2326 pref_menu(Widget option, Widget menu, int enable)
2327 {
2328 delete_children(menu);
2329 XtVaSetValues(XmOptionButtonGadget(option),XtNsensitive,enable,NULL);
2330 XtVaSetValues(XmOptionLabelGadget(option),XtNsensitive,enable,NULL);
2331 if (!enable)
2332 XtVaCreateManagedWidget("none",xmPushButtonWidgetClass,menu,NULL);
2333 }
2334
2335 #if defined(HAVE_LIBXXF86VM) || defined(HAVE_LIBXRANDR)
2336
2337 static void
2338 pref_fs(void)
2339 {
2340 Widget push;
2341 char s[32];
2342 int i,on;
2343
2344 on = XmToggleButtonGetState(pref_fs_toggle);
2345 if (on) {
2346 #if defined(HAVE_LIBXXF86VM)
2347 if (0 == have_randr && 0 == args.vidmode) {
2348 args.vidmode = 1;
2349 xfree_vm_init(dpy);
2350 }
2351 #endif
2352 if (0 == have_randr && 0 == have_vm) {
2353 on = 0;
2354 XtVaSetValues(pref_fs_toggle,XtNsensitive,0,NULL);
2355 }
2356 }
2357
2358 XmToggleButtonSetState(pref_fs_toggle,on,False);
2359 if (on) {
2360 pref_menu(pref_fs_option,pref_fs_menu,1);
2361 #if defined(HAVE_LIBXRANDR)
2362 if (have_randr) {
2363 for (i = 0; i < nrandr; i++) {
2364 sprintf(s,"%d x %d",randr[i].width,randr[i].height);
2365 push = XtVaCreateManagedWidget(s,xmPushButtonWidgetClass,
2366 pref_fs_menu,NULL);
2367 if (randr[i].width == fs_width &&
2368 randr[i].height == fs_height) {
2369 XtVaSetValues(pref_fs_menu,XmNmenuHistory,push,NULL);
2370 }
2371 }
2372 }
2373 #endif
2374 #if defined(HAVE_LIBXXF86VM)
2375 if (!have_randr) {
2376 for (i = 0; i < vm_count; i++) {
2377 sprintf(s,"%d x %d",
2378 vm_modelines[i]->hdisplay,
2379 vm_modelines[i]->vdisplay);
2380 push = XtVaCreateManagedWidget(s,xmPushButtonWidgetClass,
2381 pref_fs_menu,NULL);
2382 if (vm_modelines[i]->hdisplay == fs_width &&
2383 vm_modelines[i]->vdisplay == fs_height) {
2384 XtVaSetValues(pref_fs_menu,XmNmenuHistory,push,NULL);
2385 }
2386 }
2387 }
2388 #endif
2389 } else {
2390 pref_menu(pref_fs_option,pref_fs_menu,0);
2391 }
2392 }
2393
2394 static void
2395 pref_fst_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
2396 {
2397 pref_fs();
2398 }
2399 #endif
2400
2401 static void
2402 pref_mix2(void)
2403 {
2404 struct ng_mix_driver *mix;
2405 Widget push,w = NULL;
2406 char *name;
2407 int i,on;
2408 struct ng_devinfo *info = NULL;
2409
2410 on = XmToggleButtonGetState(pref_mix_toggle);
2411 XtVaGetValues(pref_mix1_menu,XmNmenuHistory,&w,NULL);
2412 if (w) {
2413 name = XtName(w);
2414 if (!list_empty(&ng_mix_drivers) && 0 != strcmp(name,"none")) {
2415 mix = list_entry(ng_mix_drivers.next,struct ng_mix_driver,list);
2416 info = mix->channels(name);
2417 }
2418 }
2419
2420 if (NULL != info && on) {
2421 pref_menu(pref_mix2_option,pref_mix2_menu,1);
2422 for (i = 0; 0 != strlen(info[i].name); i++) {
2423 push = XtVaCreateManagedWidget(info[i].device,
2424 xmPushButtonWidgetClass,
2425 pref_mix2_menu,NULL);
2426 toolkit_set_label(push,info[i].name);
2427 if (strcasecmp(info[i].device,mixerctl) == 0)
2428 XtVaSetValues(pref_mix2_menu,XmNmenuHistory,push,NULL);
2429 }
2430 } else {
2431 pref_menu(pref_mix2_option,pref_mix2_menu,0);
2432 }
2433 }
2434
2435 static void
2436 pref_mix2_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
2437 {
2438 pref_mix2();
2439 }
2440
2441 static void
2442 pref_mix1(void)
2443 {
2444 struct ng_mix_driver *mix;
2445 Widget push;
2446 int on,i;
2447 struct ng_devinfo *info = NULL;
2448
2449 on = XmToggleButtonGetState(pref_mix_toggle);
2450 if (!list_empty(&ng_mix_drivers)) {
2451 mix = list_entry(ng_mix_drivers.next,struct ng_mix_driver,list);
2452 info = mix->probe();
2453 }
2454 if (NULL != info && on) {
2455 pref_menu(pref_mix1_option,pref_mix1_menu,1);
2456 for (i = 0; 0 != strlen(info[i].name); i++) {
2457 push = XtVaCreateManagedWidget(info[i].device,
2458 xmPushButtonWidgetClass,
2459 pref_mix1_menu,
2460 NULL);
2461 XtAddCallback(push,XmNactivateCallback,pref_mix2_cb,NULL);
2462 toolkit_set_label(push,info[i].name);
2463 if (0 == i || 0 == strcmp(info[i].device,mixerdev))
2464 XtVaSetValues(pref_mix1_menu,XmNmenuHistory,push,NULL);
2465 }
2466 } else {
2467 pref_menu(pref_mix1_option,pref_mix1_menu,0);
2468 }
2469 }
2470
2471 static void
2472 pref_mix1_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
2473 {
2474 pref_mix1();
2475 pref_mix2();
2476 }
2477
2478 static void
2479 pref_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
2480 {
2481 XmSelectionBoxCallbackStruct *cb = call_data;
2482 Widget w;
2483 char *name = NULL;
2484 int on,width,height;
2485
2486 if (cb->reason == XmCR_OK || cb->reason == XmCR_APPLY) {
2487 #ifdef HAVE_LIBXXF86VM
2488 on = XmToggleButtonGetState(pref_fs_toggle);
2489 if (on) {
2490 XtVaGetValues(pref_fs_menu,XmNmenuHistory,&w,NULL);
2491 name = XtName(w);
2492 sscanf(name,"%d x %d",&width,&height);
2493 fs_width = width;
2494 fs_height = height;
2495 } else {
2496 fs_width = 0;
2497 fs_height = 0;
2498 }
2499 #endif
2500 on = XmToggleButtonGetState(pref_mix_toggle);
2501 if (on) {
2502 w = NULL;
2503 XtVaGetValues(pref_mix1_menu,XmNmenuHistory,&w,NULL);
2504 if (w)
2505 strcpy(mixerdev,XtName(w));
2506 w = NULL;
2507 XtVaGetValues(pref_mix2_menu,XmNmenuHistory,&w,NULL);
2508 if (w)
2509 strcpy(mixerctl,XtName(w));
2510 } else {
2511 mixerdev[0] = '\0';
2512 mixerctl[0] = '\0';
2513 }
2514 use_osd = XmToggleButtonGetState(pref_osd);
2515 keypad_ntsc = XmToggleButtonGetState(pref_ntsc);
2516 keypad_partial = XmToggleButtonGetState(pref_partial);
2517 ng_jpeg_quality = atoi(XmTextGetString(pref_quality));
2518 }
2519 if (cb->reason == XmCR_OK) {
2520 save_config();
2521 }
2522 XtUnmanageChild(pref_dlg);
2523 }
2524
2525 static void
2526 pref_manage_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
2527 {
2528 char tmp[16];
2529
2530 #ifdef HAVE_LIBXXF86VM
2531 XmToggleButtonSetState(pref_fs_toggle,fs_width && fs_height,False);
2532 pref_fs();
2533 #endif
2534 XmToggleButtonSetState(pref_mix_toggle,strlen(mixerdev) > 0,False);
2535 pref_mix1();
2536 pref_mix2();
2537 XmToggleButtonSetState(pref_osd,use_osd,False);
2538 XmToggleButtonSetState(pref_ntsc,keypad_ntsc,False);
2539 XmToggleButtonSetState(pref_partial,keypad_partial,False);
2540 sprintf(tmp,"%d",ng_jpeg_quality);
2541 XmTextSetString(pref_quality,tmp);
2542 XtManageChild(pref_dlg);
2543 }
2544
2545 static void
2546 create_pref(void)
2547 {
2548 Widget rc1,frame,rc2,rc3;
2549 Arg args[2];
2550
2551 pref_dlg = XmCreatePromptDialog(control_shell,"pref",NULL,0);
2552 XmdRegisterEditres(XtParent(pref_dlg));
2553 XtUnmanageChild(XmSelectionBoxGetChild(pref_dlg,XmDIALOG_SELECTION_LABEL));
2554 XtUnmanageChild(XmSelectionBoxGetChild(pref_dlg,XmDIALOG_HELP_BUTTON));
2555 XtManageChild(XmSelectionBoxGetChild(pref_dlg,XmDIALOG_APPLY_BUTTON));
2556 XtUnmanageChild(XmSelectionBoxGetChild(pref_dlg,XmDIALOG_TEXT));
2557
2558 rc1 = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass, pref_dlg,
2559 NULL);
2560
2561 #ifdef HAVE_LIBXXF86VM
2562 /* first frame */
2563 frame = XtVaCreateManagedWidget("fsF",xmFrameWidgetClass,rc1,NULL);
2564 XtVaCreateManagedWidget("fsL",xmLabelWidgetClass,frame,NULL);
2565 rc2 = XtVaCreateManagedWidget("rc",xmRowColumnWidgetClass,frame,NULL);
2566
2567 /* fullscreen */
2568 pref_fs_toggle = XtVaCreateManagedWidget("fsT",xmToggleButtonWidgetClass,
2569 rc2,NULL);
2570 XtAddCallback(pref_fs_toggle,XmNvalueChangedCallback,pref_fst_cb,NULL);
2571 pref_fs_menu = XmCreatePulldownMenu(rc2,"fsM",NULL,0);
2572 XtSetArg(args[0],XmNsubMenuId,pref_fs_menu);
2573 pref_fs_option = XmCreateOptionMenu(rc2,"fsO",args,1);
2574 XtManageChild(pref_fs_option);
2575 #endif
2576
2577 /* second frame */
2578 frame = XtVaCreateManagedWidget("mixF",xmFrameWidgetClass,rc1,NULL);
2579 XtVaCreateManagedWidget("mixL",xmLabelWidgetClass,frame,NULL);
2580 rc2 = XtVaCreateManagedWidget("rc",xmRowColumnWidgetClass,frame,NULL);
2581 pref_mix_toggle = XtVaCreateManagedWidget("mixT",xmToggleButtonWidgetClass,
2582 rc2,NULL);
2583 XtAddCallback(pref_mix_toggle,XmNvalueChangedCallback,pref_mix1_cb,NULL);
2584
2585 pref_mix1_menu = XmCreatePulldownMenu(rc2,"mix1M",NULL,0);
2586 XtSetArg(args[0],XmNsubMenuId,pref_mix1_menu);
2587 pref_mix1_option = XmCreateOptionMenu(rc2,"mix1O",args,1);
2588 XtManageChild(pref_mix1_option);
2589
2590 pref_mix2_menu = XmCreatePulldownMenu(rc2,"mix2M",NULL,0);
2591 XtSetArg(args[0],XmNsubMenuId,pref_mix2_menu);
2592 pref_mix2_option = XmCreateOptionMenu(rc2,"mix2O",args,1);
2593 XtManageChild(pref_mix2_option);
2594
2595 /* third frame */
2596 frame = XtVaCreateManagedWidget("optF",xmFrameWidgetClass,rc1,NULL);
2597 XtVaCreateManagedWidget("optL",xmLabelWidgetClass,frame,NULL);
2598 rc2 = XtVaCreateManagedWidget("rc",xmRowColumnWidgetClass,frame,NULL);
2599
2600 /* options */
2601 pref_osd = XtVaCreateManagedWidget("osd",xmToggleButtonWidgetClass,
2602 rc2,NULL);
2603 pref_ntsc = XtVaCreateManagedWidget("keypad-ntsc",
2604 xmToggleButtonWidgetClass,
2605 rc2,NULL);
2606 pref_partial = XtVaCreateManagedWidget("keypad-partial",
2607 xmToggleButtonWidgetClass,
2608 rc2,NULL);
2609 rc3 = XtVaCreateManagedWidget("jpeg", xmRowColumnWidgetClass,
2610 rc2,NULL);
2611 XtVaCreateManagedWidget("label",xmLabelWidgetClass,rc3,NULL);
2612 pref_quality = XtVaCreateManagedWidget("quality",
2613 xmTextWidgetClass,
2614 rc3,NULL);
2615
2616 /* buttons */
2617 XtAddCallback(pref_dlg,XmNokCallback,pref_done_cb,NULL);
2618 XtAddCallback(pref_dlg,XmNapplyCallback,pref_done_cb,NULL);
2619 XtAddCallback(pref_dlg,XmNcancelCallback,pref_done_cb,NULL);
2620 }
2621
2622 /*---------------------------------------------------------------------- */
2623 /* selection & dnd support */
2624
2625 static struct ng_video_buf*
2626 convert_buffer(struct ng_video_buf *in, int out_fmt)
2627 {
2628 struct ng_video_conv *conv;
2629 struct ng_convert_handle *ch;
2630 struct ng_video_fmt ofmt;
2631 int i;
2632
2633 /* find converter */
2634 for (i = 0;;) {
2635 conv = ng_conv_find_to(out_fmt,&i);
2636 if (NULL == conv)
2637 break;
2638 if (conv->fmtid_in == in->fmt.fmtid)
2639 goto found;
2640 }
2641 return NULL;
2642
2643 found:
2644 memset(&ofmt,0,sizeof(ofmt));
2645 ofmt.fmtid = out_fmt;
2646 ofmt.width = in->fmt.width;
2647 ofmt.height = in->fmt.height;
2648 ch = ng_convert_alloc(conv,&in->fmt,&ofmt);
2649 return ng_convert_single(ch,in);
2650 }
2651
2652 static struct ng_video_buf*
2653 scale_rgb_buffer(struct ng_video_buf *in, int scale)
2654 {
2655 struct ng_video_fmt fmt;
2656 struct ng_video_buf *buf;
2657 char *src,*dst;
2658 unsigned int x,y;
2659
2660 fmt = in->fmt;
2661 fmt.width = in->fmt.width / scale;
2662 fmt.height = in->fmt.height / scale;
2663 while (fmt.width & 0x03)
2664 fmt.width++;
2665 fmt.bytesperline = fmt.width * 3;
2666 buf = ng_malloc_video_buf(&fmt, fmt.width * fmt.height * 3);
2667
2668 /* scale down */
2669 dst = buf->data;
2670 for (y = 0; y < fmt.height; y++) {
2671 src = in->data + y * scale * in->fmt.bytesperline;
2672 for (x = 0; x < fmt.width; x++) {
2673 dst[0] = src[0];
2674 dst[1] = src[1];
2675 dst[2] = src[2];
2676 dst += 3;
2677 src += 3*scale;
2678 }
2679 }
2680 return buf;
2681 }
2682
2683 struct ipc_data {
2684 struct list_head list;
2685 Atom atom;
2686 struct ng_video_buf *buf;
2687 char *filename;
2688 Pixmap pix;
2689 Pixmap icon_pixmap;
2690 Widget icon_widget;
2691 };
2692 struct list_head ipc_selections;
2693
2694 static void
2695 ipc_iconify(Widget widget, struct ipc_data *ipc)
2696 {
2697 struct ng_video_buf *small;
2698 int scale,depth;
2699 Arg args[4];
2700 Cardinal n=0;
2701
2702 /* calc size */
2703 for (scale = 1;; scale++) {
2704 if (ipc->buf->fmt.width / scale < 128 &&
2705 ipc->buf->fmt.height / scale < 128)
2706 break;
2707 }
2708
2709 /* scale down & create pixmap */
2710 small = scale_rgb_buffer(ipc->buf,scale);
2711 small = convert_buffer(small, x11_dpy_fmtid);
2712 ipc->icon_pixmap = x11_create_pixmap(dpy,&vinfo,small);
2713
2714 /* build DnD icon */
2715 n = 0;
2716 depth = DefaultDepthOfScreen(XtScreen(widget));
2717 XtSetArg(args[n], XmNpixmap, ipc->icon_pixmap); n++;
2718 XtSetArg(args[n], XmNwidth, small->fmt.width); n++;
2719 XtSetArg(args[n], XmNheight, small->fmt.height); n++;
2720 XtSetArg(args[n], XmNdepth, depth); n++;
2721 ipc->icon_widget = XmCreateDragIcon(widget,"dragicon",args,n);
2722
2723 ng_release_video_buf(small);
2724 }
2725
2726 static struct ipc_data*
2727 ipc_find(Atom selection)
2728 {
2729 struct list_head *item;
2730 struct ipc_data *ipc;
2731
2732 list_for_each(item,&ipc_selections) {
2733 ipc = list_entry(item, struct ipc_data, list);
2734 if (ipc->atom == selection)
2735 return ipc;
2736 }
2737 return NULL;
2738 }
2739
2740 static struct ipc_data*
2741 ipc_init(Atom selection)
2742 {
2743 struct ipc_data *ipc;
2744 struct ng_video_fmt fmt;
2745 struct ng_video_buf *buf;
2746
2747 /* capture a frame and save a copy */
2748 video_gd_suspend();
2749 memset(&fmt,0,sizeof(fmt));
2750 fmt.fmtid = VIDEO_RGB24;
2751 fmt.width = cur_tv_width;
2752 fmt.height = cur_tv_height;
2753 buf = ng_grabber_get_image(&fmt);
2754 buf = ng_filter_single(cur_filter,buf);
2755 ipc = malloc(sizeof(*ipc));
2756 memset(ipc,0,sizeof(*ipc));
2757 ipc->buf = ng_malloc_video_buf(&buf->fmt,buf->size);
2758 ipc->atom = selection;
2759 ipc->buf->info = buf->info;
2760 memcpy(ipc->buf->data,buf->data,buf->size);
2761 ng_release_video_buf(buf);
2762 video_gd_restart();
2763
2764 list_add_tail(&ipc->list,&ipc_selections);
2765 return ipc;
2766 }
2767
2768 static void
2769 ipc_tmpfile(struct ipc_data *ipc)
2770 {
2771 static char *base = "motv";
2772 struct ng_video_buf *buf;
2773 char *tmpdir;
2774 int fd;
2775
2776 if (NULL != ipc->filename)
2777 return;
2778
2779 tmpdir = getenv("TMPDIR");
2780 if (NULL == tmpdir)
2781 tmpdir="/tmp";
2782 ipc->filename = malloc(strlen(tmpdir)+strlen(base)+16);
2783 sprintf(ipc->filename,"%s/%s-XXXXXX",tmpdir,base);
2784 fd = mkstemp(ipc->filename);
2785
2786 ipc->buf->refcount++;
2787 buf = convert_buffer(ipc->buf, VIDEO_JPEG);
2788 write(fd,buf->data,buf->size);
2789 ng_release_video_buf(buf);
2790 }
2791
2792 static void
2793 ipc_pixmap(struct ipc_data *ipc)
2794 {
2795 struct ng_video_buf *buf;
2796
2797 if (0 != ipc->pix)
2798 return;
2799
2800 ipc->buf->refcount++;
2801 buf = convert_buffer(ipc->buf, x11_dpy_fmtid);
2802 ipc->pix = x11_create_pixmap(dpy,&vinfo,buf);
2803 ng_release_video_buf(buf);
2804 return;
2805 }
2806
2807 static void
2808 ipc_fini(Atom selection)
2809 {
2810 struct ipc_data *ipc;
2811
2812 ipc = ipc_find(selection);
2813 if (NULL == ipc)
2814 return;
2815
2816 /* free stuff */
2817 if (ipc->buf)
2818 ng_release_video_buf(ipc->buf);
2819 if (ipc->filename) {
2820 unlink(ipc->filename);
2821 free(ipc->filename);
2822 }
2823 if (ipc->icon_widget)
2824 XtDestroyWidget(ipc->icon_widget);
2825 if (ipc->icon_pixmap)
2826 XFreePixmap(dpy,ipc->icon_pixmap);
2827 if (ipc->pix)
2828 XFreePixmap(dpy,ipc->pix);
2829
2830 list_del(&ipc->list);
2831 free(ipc);
2832 }
2833
2834 static Atom ipc_unique_atom(Widget widget)
2835 {
2836 char id_name[32];
2837 Atom id;
2838 int i;
2839
2840 for (i = 0;; i++) {
2841 sprintf(id_name,"_MOTV_IMAGE_%lX_%d",XtWindow(widget),i);
2842 id = XInternAtom(XtDisplay(widget),id_name,False);
2843 if (NULL == ipc_find(id))
2844 break;
2845 }
2846 return id;
2847 }
2848
2849 static void
2850 ipc_convert(Widget widget, XtPointer ignore, XtPointer call_data)
2851 {
2852 XmConvertCallbackStruct *ccs = call_data;
2853 struct ipc_data *ipc;
2854 Atom *targs;
2855 Pixmap *pix;
2856 unsigned long *ldata;
2857 unsigned char *cdata;
2858 char *filename;
2859 int n;
2860
2861 if (debug) {
2862 char *y = !ccs->type ? NULL : XGetAtomName(dpy,ccs->type);
2863 char *t = !ccs->target ? NULL : XGetAtomName(dpy,ccs->target);
2864 char *s = !ccs->selection ? NULL : XGetAtomName(dpy,ccs->selection);
2865 fprintf(stderr,"conv: target=%s type=%s selection=%s\n",t,y,s);
2866 if (y) XFree(y);
2867 if (t) XFree(t);
2868 if (s) XFree(s);
2869 }
2870
2871 /* tell which formats we can handle */
2872 if ((ccs->target == XA_TARGETS) ||
2873 (ccs->target == _MOTIF_CLIPBOARD_TARGETS) ||
2874 (ccs->target == _MOTIF_DEFERRED_CLIPBOARD_TARGETS) ||
2875 (ccs->target == _MOTIF_EXPORT_TARGETS)) {
2876 n = 0;
2877 targs = (Atom*)XtMalloc(sizeof(Atom)*12);
2878 if (ccs->target != _MOTIF_CLIPBOARD_TARGETS) {
2879 targs[n++] = XA_TARGETS;
2880 targs[n++] = MIME_IMAGE_PPM;
2881 targs[n++] = XA_PIXMAP;
2882 targs[n++] = XA_FOREGROUND;
2883 targs[n++] = XA_BACKGROUND;
2884 targs[n++] = XA_COLORMAP;
2885 targs[n++] = MIME_IMAGE_JPEG;
2886 targs[n++] = XA_FILE_NAME;
2887 targs[n++] = XA_FILE;
2888 targs[n++] = MIME_TEXT_URI_LIST;
2889 targs[n++] = _NETSCAPE_URL;
2890 }
2891 if (ccs->target == _MOTIF_EXPORT_TARGETS) {
2892 /* save away drag'n'drop data */
2893 ipc_init(ccs->selection);
2894 }
2895 ccs->value = targs;
2896 ccs->length = n;
2897 ccs->type = XA_ATOM;
2898 ccs->format = 32;
2899 ccs->status = XmCONVERT_DONE;
2900 return;
2901
2902 } else if (ccs->target == _MOTIF_SNAPSHOT) {
2903 /* save away clipboard data */
2904 n = 0;
2905 targs = (Atom*)XtMalloc(sizeof(Atom));
2906 targs[n++] = ipc_unique_atom(widget);
2907 ipc_init(targs[0]);
2908 ccs->value = targs;
2909 ccs->length = n;
2910 ccs->type = XA_ATOM;
2911 ccs->format = 32;
2912 ccs->status = XmCONVERT_DONE;
2913 return;
2914 }
2915
2916 /* find data */
2917 ipc = ipc_find(ccs->selection);
2918 if (NULL == ipc) {
2919 /* shouldn't happen */
2920 fprintf(stderr,"oops: selection data not found\n");
2921 ccs->status = XmCONVERT_REFUSE;
2922 return;
2923 }
2924
2925 if (ccs->target == _MOTIF_LOSE_SELECTION ||
2926 ccs->target == XA_DONE) {
2927 /* cleanup */
2928 ipc_fini(ccs->selection);
2929 ccs->value = NULL;
2930 ccs->length = 0;
2931 ccs->type = XA_INTEGER;
2932 ccs->format = 32;
2933 ccs->status = XmCONVERT_DONE;
2934 return;
2935 }
2936
2937 /* convert data */
2938 if (ccs->target == XA_BACKGROUND ||
2939 ccs->target == XA_FOREGROUND ||
2940 ccs->target == XA_COLORMAP) {
2941 n = 0;
2942 ldata = (Atom*)XtMalloc(sizeof(Atom)*8);
2943 if (ccs->target == XA_BACKGROUND) {
2944 ldata[n++] = WhitePixelOfScreen(XtScreen(widget));
2945 ccs->type = XA_PIXEL;
2946 }
2947 if (ccs->target == XA_FOREGROUND) {
2948 ldata[n++] = BlackPixelOfScreen(XtScreen(widget));
2949 ccs->type = XA_PIXEL;
2950 }
2951 if (ccs->target == XA_COLORMAP) {
2952 ldata[n++] = DefaultColormapOfScreen(XtScreen(widget));
2953 ccs->type = XA_COLORMAP;
2954 }
2955 ccs->value = ldata;
2956 ccs->length = n;
2957 ccs->format = 32;
2958 ccs->status = XmCONVERT_DONE;
2959
2960 } else if (ccs->target == XA_PIXMAP) {
2961 /* xfer pixmap */
2962 ipc_pixmap(ipc);
2963 pix = (Pixmap*)XtMalloc(sizeof(Pixmap));
2964 pix[0] = ipc->pix;
2965 if (debug)
2966 fprintf(stderr,"conv: pixmap id is 0x%lx\n",pix[0]);
2967 ccs->value = pix;
2968 ccs->length = 1;
2969 ccs->type = XA_DRAWABLE;
2970 ccs->format = 32;
2971 ccs->status = XmCONVERT_DONE;
2972
2973 } else if (ccs->target == MIME_IMAGE_PPM) {
2974 cdata = XtMalloc(ipc->buf->size + 32);
2975 n = sprintf(cdata,"P6\n%d %d\n255\n",
2976 ipc->buf->fmt.width,ipc->buf->fmt.height);
2977 memcpy(cdata+n,ipc->buf->data,ipc->buf->size);
2978 ccs->value = cdata;
2979 ccs->length = n+ipc->buf->size;
2980 ccs->type = MIME_IMAGE_PPM;
2981 ccs->format = 8;
2982 ccs->status = XmCONVERT_DONE;
2983
2984 } else if (ccs->target == MIME_IMAGE_JPEG) {
2985 struct ng_video_buf *buf;
2986 ipc->buf->refcount++;
2987 buf = convert_buffer(ipc->buf, VIDEO_JPEG);
2988 cdata = XtMalloc(buf->size);
2989 memcpy(cdata,buf->data,buf->size);
2990 ng_release_video_buf(buf);
2991 ccs->value = cdata;
2992 ccs->length = buf->size;
2993 ccs->type = MIME_IMAGE_JPEG;
2994 ccs->format = 8;
2995 ccs->status = XmCONVERT_DONE;
2996
2997 } else if (ccs->target == XA_FILE_NAME ||
2998 ccs->target == XA_FILE ||
2999 ccs->target == XA_STRING ||
3000 ccs->target == MIME_TEXT_URI_LIST ||
3001 ccs->target == _NETSCAPE_URL) {
3002 /* xfer filename (image via tmp file) */
3003 ipc_tmpfile(ipc);
3004 if (ccs->target == MIME_TEXT_URI_LIST ||
3005 ccs->target == _NETSCAPE_URL) {
3006 /* filename => url */
3007 filename = XtMalloc(strlen(ipc->filename)+8);
3008 sprintf(filename,"file:%s\r\n",ipc->filename);
3009 ccs->type = ccs->target;
3010 if (debug)
3011 fprintf(stderr,"conv: tmp url is %s\n",filename);
3012 } else {
3013 filename = XtMalloc(strlen(ipc->filename));
3014 strcpy(filename,ipc->filename);
3015 ccs->type = XA_STRING;
3016 if (debug)
3017 fprintf(stderr,"conv: tmp file is %s\n",filename);
3018 }
3019 ccs->value = filename;
3020 ccs->length = strlen(filename);
3021 ccs->format = 8;
3022 ccs->status = XmCONVERT_DONE;
3023
3024 } else {
3025 /* shouldn't happen */
3026 fprintf(stderr,"oops: unknown target\n");
3027 ccs->status = XmCONVERT_REFUSE;
3028 }
3029 }
3030
3031 static void
3032 ipc_finish(Widget widget, XtPointer ignore, XtPointer call_data)
3033 {
3034 if (debug)
3035 fprintf(stderr,"conv: transfer finished\n");
3036 ipc_fini(_MOTIF_DROP);
3037 }
3038
3039 void IpcAction(Widget widget, XEvent *event, String *argv, Cardinal *argc)
3040 {
3041 struct ipc_data *ipc;
3042 Widget drag;
3043 Arg args[4];
3044 Cardinal n=0;
3045
3046 if (0 == *argc)
3047 return;
3048 if (!(f_drv & CAN_CAPTURE)) {
3049 if (debug)
3050 fprintf(stderr,"ipc: can't capture - cancel\n");
3051 return;
3052 }
3053
3054 if (debug)
3055 fprintf(stderr,"ipc: %s\n",argv[0]);
3056 if (0 == strcmp(argv[0],"drag")) {
3057 ipc_fini(_MOTIF_DROP);
3058 ipc = ipc_init(_MOTIF_DROP);
3059 ipc_iconify(widget,ipc);
3060 n = 0;
3061 XtSetArg(args[n], XmNdragOperations, XmDROP_COPY); n++;
3062 XtSetArg(args[n], XmNsourcePixmapIcon, ipc->icon_widget); n++;
3063 drag = XmeDragSource(tv, NULL, event, args, n);
3064 XtAddCallback(drag, XmNdragDropFinishCallback, ipc_finish, NULL);
3065 }
3066 if (0 == strcmp(argv[0],"primary")) {
3067 #if 0
3068 ipc_fini(XA_PRIMARY);
3069 ipc_init(XA_PRIMARY);
3070 XmePrimarySource(tv,XtLastTimestampProcessed(dpy));
3071 #else
3072 fprintf(stderr,"FIXME [primary called]\n");
3073 #endif
3074 }
3075 if (0 == strcmp(argv[0],"clipboard")) {
3076 XmeClipboardSource(tv,XmCOPY,XtLastTimestampProcessed(dpy));
3077 }
3078 }
3079
3080 /*----------------------------------------------------------------------*/
3081
3082 Widget levels_left, levels_right;
3083 XtInputId levels_id;
3084 const struct ng_dsp_driver *levels_dsp;
3085 void *levels_hdsp;
3086
3087 static void
3088 levels_input(XtPointer clientdata, int *src, XtInputId *id)
3089 {
3090 struct ng_audio_buf *buf;
3091 int left, right;
3092
3093 buf = levels_dsp->read(levels_hdsp,0);
3094 oss_levels(buf,&left,&right);
3095 XmScaleSetValue(levels_left,left);
3096 XmScaleSetValue(levels_right,right);
3097 if (debug > 1)
3098 fprintf(stderr,"levels: left = %3d, right = %3d\r",left,right);
3099 }
3100
3101 static void
3102 levels_toggle_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
3103 {
3104 XmToggleButtonCallbackStruct *tb = call_data;
3105 struct ng_audio_fmt a;
3106
3107 if (tb->reason != XmCR_VALUE_CHANGED)
3108 return;
3109
3110 if (tb->set && NULL == levels_dsp) {
3111 /* enable */
3112 a.fmtid = AUDIO_U8_STEREO;
3113 a.rate = 44100;
3114 levels_dsp = ng_dsp_open(args.dspdev,&a,1,&levels_hdsp);
3115 if (levels_dsp) {
3116 levels_dsp->startrec(levels_hdsp);
3117 levels_id = XtAppAddInput(app_context,levels_dsp->fd(levels_hdsp),
3118 (XtPointer)XtInputReadMask,
3119 levels_input,NULL);
3120 if (debug)
3121 fprintf(stderr,"levels: started sound monitor\n");
3122 }
3123 }
3124 if (!tb->set && NULL != levels_hdsp) {
3125 /* disable */
3126 XtRemoveInput(levels_id);
3127 levels_dsp->close(levels_hdsp);
3128 levels_dsp = NULL;
3129 levels_hdsp = NULL;
3130 XmScaleSetValue(levels_left,0);
3131 XmScaleSetValue(levels_right,0);
3132 if (debug)
3133 fprintf(stderr,"levels: stopped sound monitor\n");
3134 }
3135 }
3136
3137 static void
3138 create_levels(void)
3139 {
3140 Widget rc;
3141
3142 levels_shell = XtVaAppCreateShell("levels", "MoTV",
3143 topLevelShellWidgetClass,
3144 dpy,
3145 XtNclientLeader,app_shell,
3146 XtNvisual,vinfo.visual,
3147 XtNcolormap,colormap,
3148 XtNdepth,vinfo.depth,
3149 XmNdeleteResponse,XmDO_NOTHING,
3150 NULL);
3151 XmdRegisterEditres(levels_shell);
3152 XmAddWMProtocolCallback(levels_shell,WM_DELETE_WINDOW,
3153 popupdown_cb,levels_shell);
3154 rc = XtVaCreateManagedWidget("rc", xmRowColumnWidgetClass, levels_shell,
3155 NULL);
3156
3157 levels_toggle = XtVaCreateManagedWidget("enable",
3158 xmToggleButtonWidgetClass,rc,
3159 NULL);
3160 XtAddCallback(levels_toggle,XmNvalueChangedCallback,
3161 levels_toggle_cb,NULL);
3162
3163 levels_left = XtVaCreateManagedWidget("left",xmScaleWidgetClass,rc,
3164 NULL);
3165 levels_right = XtVaCreateManagedWidget("right",xmScaleWidgetClass,rc,
3166 NULL);
3167 }
3168
3169 /*----------------------------------------------------------------------*/
3170
3171 struct stderr_handler {
3172 Widget box;
3173 XmString str;
3174 int pipe;
3175 XtInputId id;
3176 };
3177
3178 static void
3179 stderr_input(XtPointer clientdata, int *src, XtInputId *id)
3180 {
3181 struct stderr_handler *h = clientdata;
3182 XmString item;
3183 Widget label;
3184 char buf[1024];
3185 int rc;
3186
3187 rc = read(h->pipe,buf,sizeof(buf)-1);
3188 if (rc <= 0) {
3189 /* Oops */
3190 XtRemoveInput(h->id);
3191 close(h->pipe);
3192 XtDestroyWidget(h->box);
3193 free(h);
3194 }
3195 buf[rc] = 0;
3196 item = XmStringGenerate(buf, NULL, XmMULTIBYTE_TEXT,NULL);
3197 h->str = XmStringConcatAndFree(h->str,item);
3198 label = XmMessageBoxGetChild(h->box,XmDIALOG_MESSAGE_LABEL);
3199 XtVaSetValues(label,XmNlabelString,h->str,NULL);
3200 XtManageChild(h->box);
3201 };
3202
3203 static void
3204 stderr_ok_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
3205 {
3206 struct stderr_handler *h = clientdata;
3207
3208 XmStringFree(h->str);
3209 h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL);
3210 XtUnmanageChild(h->box);
3211 }
3212
3213 static void
3214 stderr_close_cb(Widget widget, XtPointer clientdata, XtPointer call_data)
3215 {
3216 struct stderr_handler *h = clientdata;
3217
3218 XmStringFree(h->str);
3219 h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL);
3220 }
3221
3222 static void
3223 stderr_init(void)
3224 {
3225 struct stderr_handler *h;
3226 int p[2];
3227
3228 if (debug)
3229 return;
3230 h = malloc(sizeof(*h));
3231 memset(h,0,sizeof(*h));
3232 h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL);
3233 h->box = XmCreateErrorDialog(app_shell,"errbox",NULL,0);
3234 XtUnmanageChild(XmMessageBoxGetChild(h->box,XmDIALOG_HELP_BUTTON));
3235 XtUnmanageChild(XmMessageBoxGetChild(h->box,XmDIALOG_CANCEL_BUTTON));
3236 XtAddCallback(h->box,XmNokCallback,stderr_ok_cb,h);
3237 XtAddCallback(XtParent(h->box),XmNpopdownCallback,stderr_close_cb,h);
3238 pipe(p);
3239 dup2(p[1],2);
3240 close(p[1]);
3241 h->pipe = p[0];
3242 h->id = XtAppAddInput(app_context,h->pipe,(XtPointer)XtInputReadMask,
3243 stderr_input,h);
3244 }
3245
3246 /*----------------------------------------------------------------------*/
3247
3248 int
3249 main(int argc, char *argv[])
3250 {
3251 int i;
3252 unsigned long freq;
3253
3254 hello_world("motv");
3255 XtSetLanguageProc(NULL,NULL,NULL);
3256 app_shell = XtVaAppInitialize(&app_context, "MoTV",
3257 opt_desc, opt_count,
3258 &argc, argv,
3259 fallback_ressources,
3260 NULL);
3261 XmdRegisterEditres(app_shell);
3262 dpy = XtDisplay(app_shell);
3263 x11_icons_init(dpy,0);
3264 init_atoms(dpy);
3265
3266 /* handle command line args */
3267 ng_init();
3268 handle_cmdline_args();
3269
3270 /* device scan */
3271 if (args.hwscan) {
3272 fprintf(stderr,"looking for available devices\n");
3273 #ifdef HAVE_LIBXV
3274 xv_video_init(-1,1);
3275 #endif
3276 grabber_scan();
3277 }
3278
3279 /* look for a useful visual */
3280 visual_init("motv","MoTV");
3281
3282 /* remote display? */
3283 do_overlay = !args.remote;
3284 if (do_overlay)
3285 x11_check_remote();
3286 v4lconf_init();
3287
3288 /* x11 stuff */
3289 XtAppAddActions(app_context,actionTable,
3290 sizeof(actionTable)/sizeof(XtActionsRec));
3291 x11_misc_init(dpy);
3292 XmAddWMProtocolCallback(app_shell,WM_DELETE_WINDOW,ExitCB,NULL);
3293 if (debug)
3294 fprintf(stderr,"main: dga extention...\n");
3295 xfree_dga_init(dpy);
3296 if (debug)
3297 fprintf(stderr,"main: xinerama extention...\n");
3298 xfree_xinerama_init(dpy);
3299 #ifdef HAVE_LIBXV
3300 if (debug)
3301 fprintf(stderr,"main: xvideo extention [video]...\n");
3302 if (args.xv_video)
3303 xv_video_init(args.xv_port,0);
3304 if (debug)
3305 fprintf(stderr,"main: xvideo extention [image]...\n");
3306 if (args.xv_image)
3307 xv_image_init(dpy);
3308 #endif
3309
3310 /* set hooks (command.c) */
3311 update_title = new_title;
3312 display_message = new_message;
3313 #if TT
3314 vtx_message = display_vtx;
3315 #endif
3316 #ifdef HAVE_ZVBI
3317 vtx_subtitle = display_subtitle;
3318 #endif
3319 set_capture_hook = do_capture;
3320 fullscreen_hook = do_motif_fullscreen;
3321 attr_notify = new_attr;
3322 volume_notify = new_volume;
3323 freqtab_notify = new_freqtab;
3324 setstation_notify = new_channel;
3325 movie_hook = do_movie_record;
3326 rec_status = do_rec_status;
3327 exit_hook = do_exit;
3328 capture_get_hook = video_gd_suspend;
3329 capture_rel_hook = video_gd_restart;
3330 channel_switch_hook = pixit;
3331
3332 if (debug)
3333 fprintf(stderr,"main: init main window...\n");
3334 tv = video_init(app_shell,&vinfo,xmPrimitiveWidgetClass,
3335 args.bpp,args.gl);
3336 XtAddEventHandler(XtParent(tv),StructureNotifyMask, True,
3337 resize_event, NULL);
3338 if (debug)
3339 fprintf(stderr,"main: install signal handlers...\n");
3340 xt_siginit();
3341 if (NULL == drv) {
3342 if (debug)
3343 fprintf(stderr,"main: open grabber device...\n");
3344 grabber_init();
3345 }
3346
3347 /* create windows */
3348 XSetIOErrorHandler(x11_ctrl_alt_backspace);
3349 if (debug)
3350 fprintf(stderr,"main: checking wm...\n");
3351 wm_detect(dpy);
3352 if (debug)
3353 fprintf(stderr,"main: creating windows ...\n");
3354 create_onscreen(xmLabelWidgetClass);
3355 create_vtx();
3356 create_strwin();
3357 stderr_init();
3358
3359 /* read config file + related settings */
3360 if (debug)
3361 fprintf(stderr,"main: init frequency tables ...\n");
3362 freq_init();
3363 if (args.readconfig) {
3364 if (debug)
3365 fprintf(stderr,"main: read config file ...\n");
3366 read_config(args.conffile ? args.conffile : NULL, &argc, argv);
3367 }
3368 if (0 != strlen(mixerdev)) {
3369 struct ng_attribute *attr;
3370 if (debug)
3371 fprintf(stderr,"main: open mixer device...\n");
3372 if (NULL != (attr = ng_mix_init(mixerdev,mixerctl)))
3373 add_attrs(attr);
3374 }
3375
3376 create_control();
3377 create_prop();
3378 create_pref();
3379 create_levels();
3380 create_filter_prop();
3381
3382 init_movie_menus();
3383 create_scale();
3384 create_attr_widgets();
3385 INIT_LIST_HEAD(&ipc_selections);
3386
3387 xt_vm_randr_input_init(dpy);
3388
3389 if (debug)
3390 fprintf(stderr,"main: mapping main window ...\n");
3391 XtRealizeWidget(app_shell);
3392 create_pointers(app_shell);
3393 create_bitmaps(app_shell);
3394 XDefineCursor(dpy, XtWindow(app_shell), left_ptr);
3395 if (f_drv & CAN_CAPTURE)
3396 XtAddCallback(tv,XmNconvertCallback,ipc_convert,NULL);
3397
3398 XtVaSetValues(app_shell,
3399 XtNwidthInc, WIDTH_INC,
3400 XtNheightInc, HEIGHT_INC,
3401 XtNminWidth, WIDTH_INC,
3402 XtNminHeight, HEIGHT_INC,
3403 NULL);
3404
3405 /* mouse pointer magic */
3406 XtAddEventHandler(tv, PointerMotionMask, True, mouse_event, NULL);
3407 mouse_event(tv,NULL,NULL,NULL);
3408
3409 /* init hw */
3410 if (debug)
3411 fprintf(stderr,"main: initialize hardware ...\n");
3412 attr_init();
3413 audio_on();
3414 audio_init();
3415
3416 /* build channel list */
3417 if (args.readconfig) {
3418 if (debug)
3419 fprintf(stderr,"main: parse channels from config file ...\n");
3420 parse_config();
3421 }
3422 channel_menu();
3423
3424 xt_handle_pending(dpy);
3425 init_overlay();
3426
3427 set_property(0,NULL,NULL);
3428 if (optind+1 == argc) {
3429 do_va_cmd(2,"setstation",argv[optind]);
3430 } else {
3431 if ((f_drv & CAN_TUNE) && 0 != (freq = drv->getfreq(h_drv))) {
3432 for (i = 0; i < chancount; i++)
3433 if (chanlist[i].freq == freq*1000/16) {
3434 do_va_cmd(2,"setchannel",chanlist[i].name);
3435 break;
3436 }
3437 }
3438 if (-1 == cur_channel) {
3439 if (count > 0) {
3440 if (debug)
3441 fprintf(stderr,"main: tuning first station\n");
3442 do_va_cmd(2,"setstation","");
3443 } else {
3444 if (debug)
3445 fprintf(stderr,"main: setting defaults\n");
3446 set_defaults();
3447 }
3448 } else {
3449 if (debug)
3450 fprintf(stderr,"main: known station tuned, not changing\n");
3451 }
3452 }
3453 XtAddEventHandler(tv,ExposureMask, True, tv_expose_event, NULL);
3454
3455 if (args.fullscreen) {
3456 do_motif_fullscreen();
3457 } else {
3458 XtAppAddWorkProc(app_context,MyResize,NULL);
3459 }
3460
3461 xt_main_loop();
3462 return 0;
3463 }
3464
|
This page was automatically generated by the
LXR engine.
|