Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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.