1 /*
2  * vidsav_dlg.c
3  *
4  * Code for the video save control dialog
5  *
6  * (C) 1997 Randall Hopper
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met: 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer. 2.
12  * Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 /*      ******************** Include Files                ************** */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <signal.h>
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <sys/uio.h>
41 #include <sys/stat.h>
42 #include <sys/time.h>
43 #include "voxware.h"
44 #include <X11/Xatom.h>
45 #include <X11/Intrinsic.h>
46 #include <X11/StringDefs.h>
47 #include <X11/Shell.h>
48 #include <X11/Xaw/AsciiText.h>
49 #include <X11/Xaw/Box.h>
50 #include <X11/Xaw/Command.h>
51 #include <X11/Xaw/Label.h>
52 #include <X11/Xaw/AsciiText.h>
53 #include <X11/Xaw/Form.h>
54 #include <X11/Xaw/MenuButton.h>
55 #include <X11/Xaw/SimpleMenu.h>
56 #include <X11/Xaw/SmeBSB.h>
57 #include <X11/Xaw/SmeLine.h>
58 #include <X11/Xaw/Toggle.h>
59 #include "tvdefines.h"
60 #include "glob.h"
61 #include "actions.h"
62 #include "xutil.h"
63 #include "tvutil.h"
64 #include "rawvideo.h"
65 #include "audsav_dlg.h"
66 #include "imgsav_dlg.h"
67 #include "vidsav_dlg.h"
68 #include "app_rsrc.h"
69 
70 /*      ******************** Local defines                ************** */
71 
72 #define AV_RAWNAME_FMT      "%s.AVraw"
73 #define SCRIPT_FNAME_FMT    "%s%s.sh"
74 
75 #define OPTIMIZE_NUM_FRAMES 200
76 
77 typedef struct {
78     char           fname_base[ MAXPATHLEN ];  /*  prefix of gen'ed files     */
79     TV_BOOL        audio_enabled;             /*  audio was captured         */
80     TV_BOOL        target_mpeg;               /*  T = MPEG target; F = imgs  */
81     TV_BOOL        streaming;                 /*  T = stream to mpeg_encode  */
82     TV_SOUND_FMT   snd_fmt;                   /*  desired sound format       */
83     TV_GEOM        geom;                      /*  captured image geometry    */
84     TV_PIXEL_GEOM  pix_geom;                  /*  captured image format      */
85     TV_ICAP_FMT    img_cap_fmt;               /*  captured image format      */
86     TV_STILL_FMT   img_sav_fmt;               /*  desired image save format  */
87     char           raw_fnames[ TV_RAW_MAX_FILES ][ MAXPATHLEN ];
88     TV_INT32       num_raw_fnames;            /*  where Raw AV data captured */
89     TV_UINT32      fps;                       /*  FPS we captured at         */
90     TV_UINT32      fps_max;                   /*  Max FPS for this signal    */
91     TV_BOOL        cleanup_tmp_files;         /*  T = cleanup; F = don't     */
92     char           script_fname[ MAXPATHLEN ]; /* Name of conversion script  */
93 } TV_CAP_PARM;
94 
95 typedef struct {
96     TV_ICAP_FMT          fmt;
97     char                *file_ext;
98     char                *wgt_name;
99     Widget               wgt;
100 } TV_ICAPFMT_ITEM_DEF;
101 
102 typedef struct {
103     TV_VIDEO_TARGET      fmt;
104     char                *wgt_name;
105     Widget               wgt;
106 } TV_VTRG_ITEM_DEF;
107 
108 typedef struct {
109     TV_INT32 frames;
110     TV_INT32 time_us;
111 } TV_VID_STATS;
112 
113 typedef struct {
114     char        **cmd;                     /*  Cmd running and its args     */
115     char         *tmpstr;
116     Widget        dialog_shell;
117     char          raw_fnames[ TV_RAW_MAX_FILES ][ MAXPATHLEN ];
118 } TV_CNVT_CMD_STATE;
119 
120 
121 /*  "mpeg_encode" param file template  */
122 #define ENCODE_SCRIPT_HEADER "#!/bin/sh\n\
123 # \n\
124 # %s\n\
125 #    Fxtv Video/System Stream Conversion Script\n\
126 # \n\
127 #    Automatically generated by BSD X TV v" VERS_STR "\n\
128 # \n\
129 \n\
130 "
131 
132 #define YUV_PIX_GEOM_MATCH(a,b) \
133      ( !memcmp( (a)->samp_size ,(b)->samp_size ,sizeof((a)->samp_size)  ) && \
134        !memcmp( (a)->samp_int_h,(b)->samp_int_h,sizeof((a)->samp_int_h) ) && \
135        !memcmp( (a)->samp_int_v,(b)->samp_int_v,sizeof((a)->samp_int_v) ) && \
136        (a)->frame_packing == (b)->frame_packing                           && \
137        !strcmp( (a)->comp_order, (b)->comp_order )                        && \
138        (a)->order_t_to_b  == (a)->order_t_to_b                            && \
139        (a)->order_l_to_r  == (a)->order_l_to_r                            && \
140        (a)->y_trans       == (a)->y_trans )
141 
142 #define IMG_CAP_FMT_IS_RGB(icap) ( (icap) == TV_ICAP_FMT_RGB16 )
143 #define IMG_ENC_FMT_IS_RGB(ienc) ( (ienc) != TV_STILL_FMT_YUV  )
144 
145 /*      ******************** Private variables            ************** */
146 
147 static TV_BOOL             Audio_enabled;
148 static TV_AUDIO_FILE_FMT   Sel_ffmt;
149 static TV_AUDIO_SAMPLE_FMT Sel_sfmt;
150 static TV_BOOL             Sel_stereo;
151 static TV_UINT32           Sel_rate;
152 static TV_VIDEO_TARGET     Sel_vtarg;
153 static TV_STILL_FMT        Sel_ifilefmt;
154 static TV_ICAP_FMT         Sel_icapfmt;
155 static TV_VID_STATS        Vid_stats;
156 
157 static Widget  Dialog_wgt         = NULL,
158                Main_wgt           = NULL,
159                Audio_cap_mgr      = NULL,
160                Audio_enc_mgr      = NULL,
161                File_label         = NULL,
162                Fname_text         = NULL,
163                Res_text           = NULL,
164                FPS_text           = NULL,
165                Icap_fmt_menu_btn  = NULL,
166                Ifile_fmt_menu_btn = NULL,
167                Vtrg_menu_btn      = NULL,
168                Cleanup_form       = NULL,
169                Cleanup_wgt        = NULL,
170                Record_btn         = NULL,
171                Stop_btn           = NULL,
172                Dismiss_btn        = NULL,
173                Wait_dialog        = NULL,
174                Ffmt_menu_btn      = NULL,
175                Sfmt_menu_btn      = NULL,
176                Chan_menu_btn      = NULL,
177                Rate_menu_btn      = NULL,
178                Audio_cap_wgt      = NULL;
179 static TV_BOOL Recording          = FALSE,
180                Optimizing         = FALSE,
181                First_image        = TRUE;
182 static TV_RAW_VIDEO_FILE *Out_rf  = NULL;
183 static TV_SOUND           Snd;
184 static int     Dsp_fd           = -1;
185 
186 /*  FIXME:  Copied from audsav_dlg.c  - This needs abstracted better  */
187 static TV_FFMT_ITEM_DEF    Ffmt_item_def[] = {
188     { TV_AUDIO_FILE_FMT_RAW         , "raw"  , NULL            },
189     { TV_AUDIO_FILE_FMT_SUNAU       , "au"   , "-t raw -U -b -c 1 -r 8012" },
190     { TV_AUDIO_FILE_FMT_WAV         , "wav"  , "-t wav"        },
191     { TV_AUDIO_FILE_FMT_VOC         , "voc"  , "-t voc"        },
192     { TV_AUDIO_FILE_FMT_AIFF        , "aiff" , "-t aiff"       },
193     { TV_AUDIO_FILE_FMT_MPEG2       , "mp2"  , "-t aiff  "     }, /* precond */
194     { TV_AUDIO_FILE_FMT_MPEG3       , "mp3"  , "-t aiff"       }, /* precond */
195 };
196 
197 static TV_SFMT_ITEM_DEF    Sfmt_item_def[] = {
198     { TV_AUDIO_SAMPLE_FMT_MULAW_U8  , "mulawU8"  , "-t raw -U -b"    },
199     { TV_AUDIO_SAMPLE_FMT_LIN_S8    , "linS8"    , "-t raw -s -b"    },
200     { TV_AUDIO_SAMPLE_FMT_LIN_U8    , "linU8"    , "-t raw -u -b"    },
201     { TV_AUDIO_SAMPLE_FMT_LIN_S16_LE, "linS16LE" , "-t raw -s -w"    },
202     { TV_AUDIO_SAMPLE_FMT_LIN_U16_LE, "linU16LE" , "-t raw -u -w"    },
203     { TV_AUDIO_SAMPLE_FMT_LIN_S16_BE, "linS16BE" , "-t raw -s -w -x" },
204     { TV_AUDIO_SAMPLE_FMT_LIN_U16_BE, "linU16BE" , "-t raw -u -w -x" }
205 };
206 
207 static TV_CHAN_ITEM_DEF    Chan_item_def[] = {
208     { FALSE                         , "mono"     },
209     { TRUE                          , "stereo"   }
210 };
211 
212 static TV_RATE_ITEM_DEF    Rate_item_def[] = {
213     { 8012                          , "r8012"    },
214     { 11025                         , "r11025"   },
215     { 22050                         , "r22050"   },
216     { 44100                         , "r44100"   },
217 };
218 
219 static TV_VTRG_ITEM_DEF    Vtrg_item_def[] = {
220 #ifdef DEBUG
221     { TV_VIDEO_TARGET_RAW         , "raw"       },
222 #endif
223     { TV_VIDEO_TARGET_IMAGES      , "images"    },
224     { TV_VIDEO_TARGET_MPEG_READY  , "mpegready" },
225     { TV_VIDEO_TARGET_MPEG        , "mpeg"      }
226 
227 };
228 
229 static TV_ICAPFMT_ITEM_DEF     Icap_fmt_item_def[] = {
230     { TV_ICAP_FMT_RGB16   ,  NULL   ,  "rgb16Cmd" },
231     { TV_ICAP_FMT_IYUV    ,  "iyuv" ,  "iyuvCmd"  },
232     { TV_ICAP_FMT_YUY2    ,  "yuy2" ,  "yuy2Cmd"  },
233     { TV_ICAP_FMT_YUY2L   ,  "yuy2l",  "yuy2lCmd" }
234 };
235 
236 static TV_IFILEFMT_ITEM_DEF    Ifile_fmt_item_def[] = {
237     { TV_STILL_FMT_TIFF   ,  "tiff", "tiffCmd" },
238     { TV_STILL_FMT_PPM    ,  "ppm" , "ppmCmd"  },
239     { TV_STILL_FMT_YUV    ,  "yuv" , "yuvCmd"  }
240 };
241 
242 static TV_INT32 Vtrg_item_def_size      = XtNumber( Vtrg_item_def ),
243                 Ifile_fmt_item_def_size = XtNumber( Ifile_fmt_item_def ),
244                 Icap_fmt_item_def_size  = XtNumber( Icap_fmt_item_def ),
245                 Ffmt_item_def_size      = XtNumber( Ffmt_item_def ),
246                 Sfmt_item_def_size      = XtNumber( Sfmt_item_def ),
247                 Chan_item_def_size      = XtNumber( Chan_item_def ),
248                 Rate_item_def_size      = XtNumber( Rate_item_def );
249 
250 /*      ******************** Forward declarations         ************** */
251 
252 static void TVVIDSAVDialogBuild( Widget *dialog_wgt );
253 
254 /*      ******************** Function Definitions         ************** */
255 
256 /*  SetMenuSelection: Set the active selection of the option menus  */
SetMenuSelection(Widget menu_btn,TV_UINT32 choice)257 static void SetMenuSelection( Widget menu_btn, TV_UINT32 choice )
258 {
259     TV_INT32  i;
260     String    label;
261 
262     if ( menu_btn == Icap_fmt_menu_btn ) {
263         for ( i = 0; i < Icap_fmt_item_def_size; i++ )
264             if ( Icap_fmt_item_def[i].fmt == choice ) {
265                 XtVaGetValues( Icap_fmt_item_def[i].wgt, XtNlabel, &label,
266                                                          NULL );
267                 XtVaSetValues( menu_btn, XtNlabel, label,
268                                NULL );
269                 break;
270             }
271         if ( i >= Icap_fmt_item_def_size ) {
272             fprintf( stderr,
273                      "TVVIDSAVDIALOGSetSel: Unsupported filefmt %lu\n",
274                      choice );
275             exit(1);
276         }
277         Sel_icapfmt = choice;
278     }
279     else if ( menu_btn == Ifile_fmt_menu_btn ) {
280         for ( i = 0; i < Ifile_fmt_item_def_size; i++ )
281             if ( Ifile_fmt_item_def[i].fmt == choice ) {
282                 XtVaGetValues( Ifile_fmt_item_def[i].wgt, XtNlabel, &label,
283                                                           NULL );
284                 XtVaSetValues( menu_btn, XtNlabel, label,
285                                NULL );
286                 break;
287             }
288         if ( i >= Ifile_fmt_item_def_size ) {
289             fprintf( stderr,
290                      "TVVIDSAVDIALOGSetSel: Unsupported filefmt %lu\n",
291                      choice );
292             exit(1);
293         }
294         Sel_ifilefmt = choice;
295     }
296     else if ( menu_btn == Ffmt_menu_btn ) {
297         for ( i = 0; i < Ffmt_item_def_size; i++ )
298             if ( Ffmt_item_def[i].fmt == choice ) {
299                 XtVaGetValues( Ffmt_item_def[i].wgt, XtNlabel, &label,
300                                                      NULL );
301                 XtVaSetValues( menu_btn, XtNlabel, label,
302                                NULL );
303                 break;
304             }
305         if ( i >= Ffmt_item_def_size ) {
306             fprintf( stderr,
307                      "TVVIDSAVDIALOGSetSel: Unsupported filefmt %lu\n",
308                      choice );
309             exit(1);
310         }
311         Sel_ffmt = choice;
312     }
313     else if ( menu_btn == Sfmt_menu_btn ) {
314         for ( i = 0; i < Sfmt_item_def_size; i++ )
315             if ( Sfmt_item_def[i].fmt == choice ) {
316                 XtVaGetValues( Sfmt_item_def[i].wgt, XtNlabel, &label,
317                                                      NULL );
318                 XtVaSetValues( menu_btn, XtNlabel, label,
319                                NULL );
320                 break;
321             }
322         if ( i >= Sfmt_item_def_size ) {
323             fprintf( stderr,
324                      "TVVIDSAVDIALOGSetSel: Unsupported sampfmt %lu\n",
325                      choice );
326             exit(1);
327         }
328         Sel_sfmt = choice;
329     }
330     else if ( menu_btn == Chan_menu_btn ) {
331         for ( i = 0; i < Chan_item_def_size; i++ )
332             if ( Chan_item_def[i].stereo == choice ) {
333                 XtVaGetValues( Chan_item_def[i].wgt, XtNlabel, &label,
334                                                      NULL );
335                 XtVaSetValues( menu_btn, XtNlabel, label,
336                                NULL );
337                 break;
338             }
339         if ( i >= Chan_item_def_size ) {
340             fprintf( stderr,
341                      "TVVIDSAVDIALOGSetSel: Unsupported #chan %lu\n",
342                      choice );
343             exit(1);
344         }
345         Sel_stereo = choice;
346     }
347     else if ( menu_btn == Rate_menu_btn ) {
348         for ( i = 0; i < Rate_item_def_size; i++ )
349             if ( Rate_item_def[i].rate == choice ) {
350                 XtVaGetValues( Rate_item_def[i].wgt, XtNlabel, &label,
351                                                      NULL );
352                 XtVaSetValues( menu_btn, XtNlabel, label,
353                                NULL );
354                 break;
355             }
356         if ( i >= Rate_item_def_size ) {
357             fprintf( stderr,
358                      "TVVIDSAVDIALOGSetSel: Unsupported rate %lu\n",
359                      choice );
360             exit(1);
361         }
362         Sel_rate = choice;
363     }
364     else if ( menu_btn == Vtrg_menu_btn ) {
365         for ( i = 0; i < XtNumber( Vtrg_item_def ); i++ )
366             if ( Vtrg_item_def[i].fmt == choice ) {
367                 XtVaGetValues( Vtrg_item_def[i].wgt, XtNlabel, &label,
368                                                      NULL );
369                 XtVaSetValues( menu_btn, XtNlabel, label,
370                                NULL );
371                 break;
372             }
373         if ( i >= XtNumber( Vtrg_item_def ) ) {
374             fprintf( stderr,
375                      "TVVIDSAVDIALOGSetSel: Unsupported filefmt %lu\n",
376                      choice );
377             exit(1);
378         }
379         Sel_vtarg = choice;
380     }
381     else {
382         fprintf( stderr, "TVVIDSAV:SetMenuSelection: Bad menu_btn\n" );
383         exit(1);
384     }
385 
386     /*  HANDLE CONSTRAINTS  */
387 
388     /*  When change IMGCAPFMT, ensure IMGENCFMT stays compatible  */
389     /*    and vice versa.                                         */
390     if ( menu_btn == Icap_fmt_menu_btn ) {
391         if ( IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) &&
392              !IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) )
393             SetMenuSelection( Ifile_fmt_menu_btn, TV_STILL_FMT_PPM );
394         else if ( !IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) &&
395                   IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) )
396             SetMenuSelection( Ifile_fmt_menu_btn, TV_STILL_FMT_YUV );
397     }
398     else if ( menu_btn == Ifile_fmt_menu_btn ) {
399         if ( IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) &&
400              !IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) )
401             SetMenuSelection( Icap_fmt_menu_btn, TV_ICAP_FMT_RGB16 );
402         else if ( !IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) &&
403                   IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) )
404             SetMenuSelection( Icap_fmt_menu_btn, TV_ICAP_FMT_IYUV );
405     }
406 
407     else if ( menu_btn == Vtrg_menu_btn ) {
408         /*  Enable/disable & set/reset related options  */
409         XtSetSensitive( Cleanup_form, choice == TV_VIDEO_TARGET_MPEG   );
410 
411         /*  When change target to MPEG, switch to IYUV/MPEG2 by default  */
412         if (( Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY ) ||
413             ( Sel_vtarg == TV_VIDEO_TARGET_MPEG )) {
414 #ifdef WHEN_ITS_READY_FOR_PRIME_TIME
415             SetMenuSelection( Icap_fmt_menu_btn, TV_ICAP_FMT_IYUV );
416             SetMenuSelection( Ifile_fmt_menu_btn, TV_STILL_FMT_YUV );
417             SetMenuSelection( Ffmt_menu_btn, TV_AUDIO_FILE_FMT_MPEG2 );
418 #else
419             SetMenuSelection( Icap_fmt_menu_btn, TV_ICAP_FMT_RGB16 );
420             SetMenuSelection( Ifile_fmt_menu_btn, TV_STILL_FMT_PPM );
421             SetMenuSelection( Ffmt_menu_btn, TV_AUDIO_FILE_FMT_MPEG2 );
422 #endif
423         }
424     }
425 }
426 
TextValUpdate(Widget text_wgt,char * str)427 static void TextValUpdate( Widget text_wgt, char *str )
428 {
429     XawTextBlock     tblk;
430     char            *old_str;
431     int              old_len;
432 
433     assert( text_wgt != NULL );
434 
435     memset( &tblk, '\0', sizeof( tblk ) );
436     tblk.firstPos = 0;
437     tblk.length   = strlen( str );
438     tblk.ptr      = str;
439     tblk.format   = XawFmt8Bit;
440 
441     XtVaGetValues( text_wgt, XtNstring, &old_str,
442                              NULL );
443     old_len = (old_str == NULL) ? 0 : strlen( old_str );
444     XawTextReplace( text_wgt, 0, old_len, &tblk );
445 }
446 
447 
448 /*  UpdateButtons - Enable/disable btns based on state  */
UpdateButtons()449 static void UpdateButtons()
450 {
451     TV_BOOL rec, stop, dismiss;
452 
453     if ( Recording )
454         stop = TRUE , rec = dismiss = FALSE;
455     else
456         stop = FALSE, rec = dismiss = TRUE;
457 
458     XtSetSensitive( Record_btn  , rec     );
459     XtSetSensitive( Stop_btn    , stop    );
460     XtSetSensitive( Dismiss_btn , dismiss );
461 }
462 
463 
464 /*  DialogSetEnabled - Utility rtn to sensitive/desensitize the video dialog */
DialogSetEnabled(TV_BOOL enabled)465 static void DialogSetEnabled( TV_BOOL enabled )
466 {
467     if ( Main_wgt == NULL )
468         return;
469 
470     XtSetSensitive( Main_wgt, enabled );
471     if ( enabled ) {
472         UpdateButtons();
473 
474         XtSetSensitive( Audio_cap_mgr, Audio_enabled );
475     }
476 }
477 
478 /*  PrepareForVideo - Take dialog values, save them in globals, open  */
479 /*    the video device and setup its play/record parameters.          */
PrepareForVideo(TV_BOOL optimize_only)480 static TV_BOOL PrepareForVideo( TV_BOOL optimize_only )
481 {
482     TV_CAPTURE *c   = &G_glob.capture;
483     TV_DISK    *d = &G_glob.disk;
484     TV_BOOL     error = FALSE;
485     String      filename,
486                 str,
487                 str2;
488     char        msg[100];
489     TV_GEOM     g = { 0,0,0,0 };
490     TV_INT32    fps;
491     Boolean     cleanup_temp;
492 
493     /*  ...Filename base  */
494     XtVaGetValues( Fname_text, XtNstring, &filename,
495                                NULL );
496     if ( filename == NULL )
497         filename = "";
498     if ( strlen( filename ) == 0 ) {
499         XUTILDialogPause( TVTOPLEVEL, "Error", "No filename specified.",
500                           TV_DIALOG_TYPE_OK );
501         error = TRUE;
502         goto RETURN;
503     }
504 
505     /*  ...Size  */
506     XtVaGetValues( Res_text, XtNstring, &str,
507                              NULL );
508     if ( str == NULL )
509         str = "";
510     if (( sscanf( str, "%ldx%ld", &g.w, &g.h ) != 2 ) ||
511         !TVCAPTUREValidRegionGeom( c, &g )) {
512         XUTILDialogPause( TVTOPLEVEL, "Error", "Invalid size.",
513                           TV_DIALOG_TYPE_OK );
514         error = TRUE;
515         goto RETURN;
516     }
517 
518     if ( !optimize_only ) {
519         /*  ...Speed  */
520         XtVaGetValues( FPS_text, XtNstring, &str,
521                                  NULL );
522         if ( str == NULL )
523             str = "";
524         if (( sscanf( str, "%ld", &fps ) != 1 ) ||
525             ( fps < 1 ) || ( fps > c->fps_max )) {
526             XUTILDialogPause( TVTOPLEVEL, "Error", "Invalid speed.",
527                               TV_DIALOG_TYPE_OK );
528             error = TRUE;
529             goto RETURN;
530         }
531 
532         /*  ...ImageCapFmt/ImageEncFmt dependency  */
533         if (( Sel_vtarg != TV_VIDEO_TARGET_RAW ) &&
534             ( IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) !=
535               IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) )) {
536             str  = IMG_CAP_FMT_IS_RGB( Sel_icapfmt  ) ? "RGB" : "YUV";
537             str2 = IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) ? "RGB" : "YUV";
538             sprintf( msg, "Image Capture Format is %s-based but\n"
539                           "Image Encode  Format is %s-based.",
540                      str, str2 );
541             XUTILDialogPause( TVTOPLEVEL, "Error", msg, TV_DIALOG_TYPE_OK );
542             error = TRUE;
543             goto RETURN;
544         }
545 
546         /*  ...Target/AudioEncFmt dependency  */
547         if ( (( Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY ) ||
548               ( Sel_vtarg == TV_VIDEO_TARGET_MPEG       )) &&
549              Audio_enabled &&
550              (( Sel_ffmt != TV_AUDIO_FILE_FMT_MPEG2 ) &&
551               ( Sel_ffmt != TV_AUDIO_FILE_FMT_MPEG3 )) ) {
552             XUTILDialogPause( TVTOPLEVEL, "Error",
553                 "Valid Audio Encoding Formats for the\n"
554                 "MPEG and MPEG-Ready Targets are:\n"
555                 "MPEG-2 and MPEG-3.\n",
556                 TV_DIALOG_TYPE_OK );
557             error = TRUE;
558             goto RETURN;
559         }
560 
561         /*  Cleanup Temp Files  */
562         XtVaGetValues( Cleanup_wgt, XtNstate,  &cleanup_temp,
563                                     NULL);
564     }
565 
566     /*  Save off settings  */
567     d->fn_video_base[0] = '\0';
568     strncat( d->fn_video_base, filename, sizeof( d->fn_video_base ) - 1 );
569     d->video.geom     = g;
570 
571     if ( !optimize_only ) {
572         d->video.target   = Sel_vtarg;
573         d->video.fps      = fps;
574         d->video.cleanup_temp = (cleanup_temp != 0);
575     }
576 
577  RETURN:
578     return !error;
579 }
580 
581 
582 /*  DoCmdFailDialog - Display a dialog citing the command that failed  */
583 /*    and its exit status; wait on user to dismiss.                    */
DoCmdFailDialog(char * cmd[],int status)584 static void DoCmdFailDialog(
585                  char *cmd[],
586                  int   status )
587 {
588     char     msg[ 2*MAXPATHLEN + 160 ];
589     TV_INT32 i;
590 
591     sprintf( msg, "Video conversion failed.\nCMD    = " );
592     for ( i = 0; cmd[i] != NULL; i++ )
593         sprintf( msg+strlen(msg), "%s ", cmd[i] );
594     sprintf( msg+strlen(msg), "\nSTATUS = 0x%.4x", status );
595     XUTILDialogPause( TVTOPLEVEL, "Error", msg, TV_DIALOG_TYPE_OK );
596 }
597 
598 /*  OkToWriteTo - Determine if its OK to write to the specified file.  */
599 /*    If the file exists, user is prompted to overwrite, and file is   */
600 /*    unlinked.  If device file, user is prompted for verification.    */
601 /*    If insuff perms, error dialog displayed.                         */
OkToWriteTo(char filename[MAXPATHLEN],TV_BOOL * dont_unlink)602 static TV_BOOL OkToWriteTo(
603                    char     filename[ MAXPATHLEN ],
604                    TV_BOOL *dont_unlink )
605 {
606     struct stat   sb;
607     TV_BOOL       exists = TRUE,
608                   ok = FALSE;
609     char          msg[MAXPATHLEN+160];
610 
611     *dont_unlink = FALSE;
612 
613     if ( stat( filename, &sb ) < 0 ) {
614         if ( errno != ENOENT ) {
615             fprintf( stderr, "Whoah!  stat() failed on '%s'.\n", filename );
616             XBell( TVDISPLAY, 100 );
617             ok = FALSE;
618             goto RETURN;
619         }
620         exists = FALSE;
621     }
622 
623     if ( exists ) {
624         if ( access( filename, R_OK | W_OK ) < 0 ) {
625             XUTILDialogPause( TVTOPLEVEL, "Error",
626                               "Can't read and write to this file.",
627                               TV_DIALOG_TYPE_OK );
628             goto RETURN;
629         }
630         if ( S_ISREG(sb.st_mode) ) {
631             sprintf( msg, "This file exists (%s).\nOverwrite?", filename );
632             if ( XUTILDialogPause( TVTOPLEVEL, "Confirm", msg,
633                                    TV_DIALOG_TYPE_YES_NO ) != TV_DIALOG_YES )
634                 goto RETURN;
635             unlink( filename );
636         }
637 #ifdef FIXME__NOT_SUPPORTED_ANYMORE
638         else if ( S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode) ) {
639             sprintf( msg, "This is a device file (%s).\nAre you sure "
640                           "you want to write to it?", filename );
641             if ( XUTILDialogPause( TVTOPLEVEL, "Confirm", msg,
642                                    TV_DIALOG_TYPE_YES_NO ) != TV_DIALOG_YES )
643                 goto RETURN;
644             *dont_unlink = TRUE;
645         }
646 #endif
647         else {
648             XUTILDialogPause( TVTOPLEVEL, "Error",
649                               "Can't write to this type of file.",
650                               TV_DIALOG_TYPE_OK );
651             goto RETURN;
652         }
653     }
654 
655     ok = TRUE;
656 
657  RETURN:
658     return ok;
659 }
660 
661 
662 /*  VideoPixelGeom  - Returns ptr to a pixel geom that we're going to  */
663 /*    use for capture.                                                 */
VideoPixelGeom(TV_ICAP_FMT img_cap_fmt)664 static TV_PIXEL_GEOM *VideoPixelGeom( TV_ICAP_FMT img_cap_fmt )
665 {
666     static TV_PIXEL_GEOM
667         IYUV  = { -1,TV_PIXELTYPE_YUV,0,{},0,0,
668                   {8,8,8},{1,2,2},{1,2,2},TV_FRAME_PLANAR,"YUV",1,1,0 },
669         YUY2  = { -1,TV_PIXELTYPE_YUV,0,{},0,0,
670                   {8,8,8},{1,2,2},{1,1,1},TV_FRAME_PACKED,"YUYV",1,1,0 },
671         YUY2L = { -1,TV_PIXELTYPE_YUV,0,{},0,0,
672                   {8,8,8},{1,2,2},{1,1,1},TV_FRAME_PLANAR,"YUV",1,1,0 };
673 
674     static TV_PIXEL_GEOM pg;
675 
676     TV_CAPTURE   *c   = &G_glob.capture;
677     TV_UINT32     num_pg,
678                   i;
679     TV_BOOL       found = FALSE;
680 
681     TVCAPTUREGetNumPixFmts( c, &num_pg );
682     for ( i = 0; (i < num_pg) && !found; i++ ) {
683         TVCAPTUREGetNthPixFmt( c, i, &pg );
684 
685         switch ( img_cap_fmt ) {
686             case TV_ICAP_FMT_RGB16 :
687                 /*  Pull out 565 16bpp byte swapped  */
688                 if (( pg.type == TV_PIXELTYPE_RGB ) && ( pg.Bpp == 2 ) &&
689                     ( pg.mask[0] == 0xf800 ) && ( pg.mask[1] == 0x07e0 ) &&
690                     ( pg.mask[2] == 0x001f ) && pg.swap_bytes )
691                     found = TRUE;
692                 break;
693 
694             case TV_ICAP_FMT_IYUV  :
695                 if ( YUV_PIX_GEOM_MATCH( &pg ,&IYUV  ) )
696                     found = TRUE;
697                 break;
698 
699             case TV_ICAP_FMT_YUY2  :
700                 if ( YUV_PIX_GEOM_MATCH( &pg ,&YUY2  ) )
701                     found = TRUE;
702                 break;
703 
704             case TV_ICAP_FMT_YUY2L :
705                 if ( YUV_PIX_GEOM_MATCH( &pg ,&YUY2L ) )
706                     found = TRUE;
707                 break;
708 
709             default :
710                 fprintf( stderr,
711                          "VideoPixelGeom: Unsupported pixel cap fmt: %d\n",
712                          img_cap_fmt );
713                 exit(1);
714         }
715     }
716 
717     if ( !found ) {
718         fprintf( stderr, "VideoPixelGeom: Can't find cap fmt %d\n",
719                  img_cap_fmt );
720         exit(1);
721     }
722 
723     return &pg;
724 }
725 
726 /*  GetRawFilenames - Fill in list of raw filenames to use for  */
727 /*    video capture                                             */
GetRawFilenames(char cap_file[MAXPATHLEN],TV_BOOL target_raw,char fnames[4][MAXPATHLEN],TV_INT32 * num_raw_files)728 static void GetRawFilenames( char      cap_file[ MAXPATHLEN ],
729                              TV_BOOL   target_raw,
730                              char      fnames[ 4 ][ MAXPATHLEN ],
731                              TV_INT32 *num_raw_files )
732 {
733     TV_INT32   i;
734     String    *app_res = App_res.video_cap_file;
735 
736     *num_raw_files = 0;
737     for ( i = 0; i < 4; i++ )
738         fnames[i][0] = '\0';
739 
740     /*  If user didn't set resources, fall-back on a capture file based  */
741     /*    on the name of the user-entered target-file.                   */
742     if ( !app_res[0] || !app_res[0][0] ) {
743 
744         /*  FIXME: for raw devices, no difference for target_raw  */
745         if ( !target_raw )
746             sprintf( fnames[0], AV_RAWNAME_FMT, cap_file );
747         else {
748             fnames[0][0] = '\0';
749             strncat( fnames[0], cap_file, MAXPATHLEN-1 );
750         }
751         *num_raw_files = 1;
752     }
753 
754     /*  User set at least one capture file resource; use those  */
755     else {
756         for ( i = 0; i < 4; i++ )
757             if ( app_res[i] && app_res[i][0] )
758                 strncat( fnames[i], app_res[i], MAXPATHLEN-1 );
759             else
760                 break;
761         *num_raw_files = i;
762     }
763 }
764 
765 
766 /*  Unlink the raw files used for video capture  */
UnlinkRawFiles(char fnames[4][MAXPATHLEN])767 static void UnlinkRawFiles( char      fnames[ 4 ][ MAXPATHLEN ] )
768 {
769     TV_INT32 i;
770 
771     for ( i = 0; i < 4; i++ )
772         if ( fnames[i][0] )
773             unlink( fnames[i] );
774 }
775 
776 
777 /**@BEGINFUNC**************************************************************
778 
779     Prototype  : static TV_BOOL WriteVidConvertShScript(
780                       TV_CAP_PARM   *p )
781 
782     Purpose    : Write an SH script to perform the encoding of a raw
783                  capture file into images and an audio file or an MPEG
784                  video and/or system stream as selected by the user.
785 
786     Programmer : 16-Jan-98  Randall Hopper
787 
788     Parameters : p - I: image conversion parameters
789 
790     Returns    : T = success; F = failure
791 
792     Globals    : None.
793 
794  **@ENDFUNC*****************************************************************/
795 
WriteVidConvertShScript(TV_CAP_PARM * p)796 static TV_BOOL WriteVidConvertShScript(
797                    TV_CAP_PARM   *p )
798 {
799     FILE        *fp  = NULL;
800     TV_BOOL      ret = FALSE;
801     char        *str;
802     struct stat  stat;
803     TV_INT32     i;
804     TV_BOOL      has_path;
805 
806     /*  Open output script file  */
807     has_path = strchr( p->fname_base, '/' ) != NULL;
808     sprintf( p->script_fname, SCRIPT_FNAME_FMT,
809              (has_path ? "" : "./"), p->fname_base );
810 
811     if ( (fp = fopen( p->script_fname, "wt" )) == NULL ) {
812         fprintf( stderr, "Failed to open for write: %s\n", p->script_fname );
813         goto RETURN;
814     }
815 
816     /*  Write the header  */
817     fprintf( fp, ENCODE_SCRIPT_HEADER, p->script_fname );
818 
819     /*fprintf( fp, "\n\n\n  exec 1>LOG 2>&1 \n\n\n\n" );*/
820 
821     /*  Write the conversion settings  */
822     fprintf( fp, "AV_RAW_FILES='" );
823     for ( i = 0; i < p->num_raw_fnames; i++ )
824             fprintf( fp, "%s%s", (( i==0 ) ? "" : "," ), p->raw_fnames[i] );
825     fprintf( fp, "'\n" );
826 
827     fprintf( fp, "AV_TARGET='%s'\n\n", (p->target_mpeg ? "MPEG" : "IMAGES") );
828     fprintf( fp, "TARGET_FN_BASE='%s'\n\n", p->fname_base );
829     fprintf( fp, "AUDIO_ENABLED='%s'\n", (p->audio_enabled ? "YES" : "NO") );
830 
831     switch ( p->snd_fmt.samp_fmt ) {
832         case TV_AUDIO_SAMPLE_FMT_MULAW_U8   : str = "MULAW_U8"  ;  break;
833         case TV_AUDIO_SAMPLE_FMT_LIN_S8     : str = "LIN_S8"    ;  break;
834         case TV_AUDIO_SAMPLE_FMT_LIN_U8     : str = "LIN_U8"    ;  break;
835         case TV_AUDIO_SAMPLE_FMT_LIN_S16_LE : str = "LIN_S16_LE";  break;
836         case TV_AUDIO_SAMPLE_FMT_LIN_U16_LE : str = "LIN_U16_LE";  break;
837         case TV_AUDIO_SAMPLE_FMT_LIN_S16_BE : str = "LIN_S16_BE";  break;
838         case TV_AUDIO_SAMPLE_FMT_LIN_U16_BE : str = "LIN_U16_BE";  break;
839         default                             : str = ""          ;  break;
840     }
841     fprintf( fp, "AUDIO_CAP_FMT_SAMPLE='%s'\n", str );
842     fprintf( fp, "AUDIO_CAP_FMT_CHAN='%d'\n", p->snd_fmt.stereo ? 2 : 1 );
843     fprintf( fp, "AUDIO_CAP_FMT_FREQ='%ld'\n", p->snd_fmt.samp_rate );
844 
845     switch ( p->snd_fmt.file_fmt ) {
846         case TV_AUDIO_FILE_FMT_RAW    : str = "RAW"  ;  break;
847         case TV_AUDIO_FILE_FMT_SUNAU  : str = "SUNAU";  break;
848         case TV_AUDIO_FILE_FMT_WAV    : str = "WAV"  ;  break;
849         case TV_AUDIO_FILE_FMT_VOC    : str = "VOC"  ;  break;
850         case TV_AUDIO_FILE_FMT_AIFF   : str = "AIFF" ;  break;
851         case TV_AUDIO_FILE_FMT_MPEG2  : str = "MPEG2";  break;
852         case TV_AUDIO_FILE_FMT_MPEG3  : str = "MPEG3";  break;
853         default                       : str = ""     ;  break;
854     }
855 
856     fprintf( fp, "AUDIO_TARGET_FMT='%s'\n\n", str );
857 
858     fprintf( fp, "VIDEO_RES_X='%ld'\n", p->geom.w );
859     fprintf( fp, "VIDEO_RES_Y='%ld'\n", p->geom.h );
860 
861     switch ( p->img_cap_fmt ) {
862         case TV_ICAP_FMT_RGB16 : str = "RGB16";  break;
863         case TV_ICAP_FMT_IYUV  : str = "IYUV" ;  break;
864         case TV_ICAP_FMT_YUY2  : str = "YUY2" ;  break;
865         case TV_ICAP_FMT_YUY2L : str = "YUY2L";  break;
866         default                : str = ""     ;  break;
867     }
868 
869     fprintf( fp, "VIDEO_CAP_FMT='%s'\n", str );
870     fprintf( fp, "VIDEO_TARGET_FPS='%ld'\n", p->fps );
871     fprintf( fp, "VIDEO_STREAM='%s'\n\n", ( p->streaming ? "YES" : "NO" ) );
872 
873     switch ( p->img_sav_fmt ) {
874         case TV_STILL_FMT_TIFF : str = "TIFF";  break;
875         case TV_STILL_FMT_PPM  : str = "PPM" ;  break;
876         case TV_STILL_FMT_YUV  : str = "YUV" ;  break;
877         default                : str = ""    ;  break;
878     }
879     fprintf( fp, "IMAGE_TARGET_FMT='%s'\n\n", str );
880 
881     fprintf( fp, "CLEANUP_TEMP_FILES='%s'\n",
882              (p->cleanup_tmp_files ? "YES" : "NO" ) );
883 
884     /*  Source the main script with the methods  */
885     fprintf( fp, "\n\n\n"
886                  "#\n"
887                  "#  Source the videoCnvtScript with the conversion methods\n"
888                  "#\n"
889                  ". %s\n",
890              App_res.video_cnvt_script );
891 
892     /*  Add execute permission  */
893     fstat ( fileno(fp), &stat );
894     fchmod( fileno(fp), stat.st_mode | S_IXUSR );
895 
896     ret = TRUE;
897 
898  RETURN:
899     if ( fp != NULL )
900         fclose( fp );
901     if ( !ret )
902         unlink( p->script_fname );
903     return ret;
904 }
905 
906 
907 /*  CnvtCmdCancelTestCB                                             */
908 /*    - Used by CnvtCmd invocations of XUTILRunCmdAllowCancel to    */
909 /*      identify when the the user aborted the operation.           */
CnvtCmdCancelTestCB(void * cb_data)910 static TV_BOOL CnvtCmdCancelTestCB( void *cb_data )
911 {
912     TV_CNVT_CMD_STATE   *state = (TV_CNVT_CMD_STATE *) cb_data;
913 
914     return !XtIsRealized( state->dialog_shell );
915 }
916 
917 
918 /*  CnvtCmdDoneCB                                                  */
919 /*    - Called when the conversion script completes or is aborted  */
CnvtCmdDoneCB(TV_BOOL aborted,int status,void * cb_data)920 static void CnvtCmdDoneCB( TV_BOOL aborted, int status, void *cb_data )
921 {
922     TV_CNVT_CMD_STATE *state = (TV_CNVT_CMD_STATE *) cb_data;
923 
924     /*  At this stage, always pull down the "wait" dialog and destroy it  */
925     if ( !aborted )
926         XtPopdown( state->dialog_shell );
927     XtDestroyWidget( state->dialog_shell );
928 
929     /*  If the command failed, tell the user about it  */
930     if ( !aborted && ( status != 0 ) )
931         DoCmdFailDialog( state->cmd, status );
932 
933     /*  Do post-cmd cleanup  */
934     free( state->cmd    );
935     free( state->tmpstr );
936 
937     /*  If we completed successfully or the user canceled the conversion,  */
938     /*    remove raw capture files (if passed).                            */
939     /*    NOTE: if we're capturing to devices, don't clean those up.       */
940     if ( aborted || ( status == 0 ) ) {
941         TV_INT32     i;
942         struct stat  stat_s;
943 
944         for ( i = 0; i < XtNumber( state->raw_fnames ); i++ )
945             if (( state->raw_fnames[i][0] != '\0' ) &&
946                 ( stat( state->raw_fnames[i], &stat_s ) == 0 ) &&
947                 ( stat_s.st_mode & S_IFREG ))
948                 unlink( state->raw_fnames[i] );
949     }
950 
951     /*  FIXME:  The fxtv_conv.sh script isn't nuking its child processes  */
952     /*    and temporary files when it's killed.  It just dies.            */
953 
954     DialogSetEnabled( True );
955     Recording = FALSE;
956     UpdateButtons();
957     free( state );
958     return;
959 }
960 
961 
962 /*  RunCnvtCmd - Exec command to convert raw capture to desired format(s)  */
RunCnvtCmd(char cmd[],char raw_fnames[TV_RAW_MAX_FILES][MAXPATHLEN])963 static void RunCnvtCmd( char                 cmd[],
964                         char          raw_fnames[ TV_RAW_MAX_FILES ][ MAXPATHLEN ] )
965 {
966     TVUTIL_PIPE_END     end[3] = {{ -1 }, { -1 }, { -1 }};
967     TV_CNVT_CMD_STATE  *state;
968     Widget              dialog_shell;
969 
970     if ( (state = calloc( 1, sizeof(*state) )) == NULL )
971         TVUTILOutOfMemory();
972 
973     TVUTILCmdStrToArgList( cmd, &state->cmd, &state->tmpstr );
974 
975     dialog_shell = XUTILDialogBuild( TVTOPLEVEL, "Please Wait",
976                                "Conversion in Progress...",
977                                TV_DIALOG_TYPE_CANCEL );
978     XUTILXtPopup( dialog_shell, XtGrabNone, TVTOPLEVEL );
979 
980     /*  Execute conversion cmd & wait on it to finish or user to cancel  */
981     state->dialog_shell = dialog_shell;
982     if ( raw_fnames )
983         memcpy( state->raw_fnames, raw_fnames, sizeof( state->raw_fnames ) );
984     else
985         memset( state->raw_fnames, '\0', sizeof( state->raw_fnames ) );
986 
987     XUTILRunCmdAllowCancel( TVAPPCTX, state->cmd, end,
988                             CnvtCmdCancelTestCB, state,
989                             CnvtCmdDoneCB      , state );
990 }
991 
992 
993 /**@BEGINFUNC**************************************************************
994 
995     Prototype  : static void RecordCmdCB(
996                       Widget w,
997                       XtPointer cl,
998                       XtPointer cb )
999 
1000     Purpose    : Callback used for Record and Optimize functions.
1001 
1002     Programmer : 17-Jan-98  Randall Hopper
1003 
1004     Parameters : w  - I: what widget was pressed that invoked us (don't care)
1005                  cl - I: "optimize" or "record"
1006                  cb - I: (don't care)
1007 
1008     Returns    : None.
1009 
1010     Globals    : None.
1011 
1012  **@ENDFUNC*****************************************************************/
1013 
RecordCmdCB(Widget w,XtPointer cl,XtPointer cb)1014 static void RecordCmdCB( Widget w, XtPointer cl, XtPointer cb )
1015 {
1016     TV_CAPTURE        *c   = &G_glob.capture;
1017     TV_DISK           *d   = &G_glob.disk;
1018     char              *error_msg,
1019                       *cfg_fail_msg,
1020                       *raw_fnames[ TV_RAW_MAX_FILES ];
1021     int                dsp_fd = -1;
1022     TV_BOOL            optimizing = STREQ( (char *)cl, "optimize" ),
1023                        dont_unlink,
1024                        video_disabled = FALSE,
1025                        cmd_running    = FALSE;
1026     TV_RAW_VIDEO_FILE *rf = NULL;
1027     Widget             dialog_shell = NULL;
1028     TV_INT32           i;
1029     TV_CAP_PARM       *p  = NULL;
1030 
1031     /*  If we haven't created the dialog yet, do so  */
1032     if ( Dialog_wgt == NULL ) {
1033         TVVIDSAVDialogBuild( &Dialog_wgt );
1034         TVVIDSAVDIALOGResync();
1035     }
1036 
1037     /*  If already recording, ignore user  */
1038     if ( Recording ) {
1039         XBell( TVDISPLAY, 100 );
1040         return;
1041     }
1042 
1043     /*  Save off old freeze state, and stop capture if necessary  */
1044     TVSCREENSetScreenUpdateEnabled( FALSE );
1045     video_disabled = TRUE;
1046 
1047     /*  Grab values off dialog  */
1048     if ( !PrepareForVideo( optimizing ) )
1049         goto RETURN;
1050 
1051     /*  Fill in capture parameters  */
1052     if ( (p = calloc( 1, sizeof(*p) )) == NULL )
1053         TVUTILOutOfMemory();
1054 
1055     p->fname_base[0] = '\0';
1056     strncat( p->fname_base, d->fn_video_base, sizeof(p->fname_base)-1 );
1057     p->audio_enabled = Audio_enabled;
1058     p->target_mpeg   = (Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY) ||
1059                        (Sel_vtarg == TV_VIDEO_TARGET_MPEG);
1060 
1061     /*  FIXME: Allow user to set whether we'll do streaming or not  */
1062     /*    (for YUV, uses YUV; for RGB, uses PPM)                    */
1063     p->streaming     = (( Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY ) ||
1064                         ( Sel_vtarg == TV_VIDEO_TARGET_MPEG       )) &&
1065                        (( Sel_icapfmt  == TV_ICAP_FMT_IYUV          ) ||
1066                         (( Sel_icapfmt  == TV_ICAP_FMT_RGB16         ) &&
1067                          ( Sel_ifilefmt == TV_STILL_FMT_PPM          )));
1068 
1069     p->snd_fmt.samp_fmt  = Sel_sfmt,          /*  Cap sample format  */
1070     p->snd_fmt.stereo    = Sel_stereo,        /*  Cap num chan       */
1071     p->snd_fmt.samp_rate = Sel_rate,          /*  Cap sample rate    */
1072     p->snd_fmt.file_fmt  = Sel_ffmt;          /*  Desired output format  */
1073 
1074     memcpy( &p->geom, &d->video.geom, sizeof(p->geom) );
1075     memcpy( &p->pix_geom, VideoPixelGeom(Sel_icapfmt), sizeof(p->pix_geom) );
1076     p->img_cap_fmt = Sel_icapfmt;
1077     p->img_sav_fmt = Sel_ifilefmt;
1078 
1079     GetRawFilenames( p->fname_base, (Sel_vtarg == TV_VIDEO_TARGET_RAW),
1080                      p->raw_fnames, &p->num_raw_fnames );
1081     p->fps = d->video.fps;
1082     TVCAPTUREGetFPSMax( c, &p->fps_max );
1083 
1084     p->cleanup_tmp_files = d->video.cleanup_temp;
1085     p->script_fname[0] = '\0';
1086 
1087     /*  See if the output file exists; if so, we need to verify user wants  */
1088     /*    to overwrite.                                                     */
1089 
1090     /*  FIXME: We really need to check all the possible output filenames    */
1091 
1092     /*  FIXME: Add RAW DEVICE SUPPORT or add check for target NOT raw device */
1093     /*    For RAW devices, we need to write EOF record (read() doesn't       */
1094     /*    return 0 on end of stream).  Also, quit unlinking raw device files */
1095     /*    And if raw write is to device file, must write frames someplace    */
1096     /*    else; also, can't use tempname when writing to this file.          */
1097 
1098     if ( !OkToWriteTo( p->fname_base, &dont_unlink ) )
1099         goto RETURN;
1100 
1101     /*  Now open the raw output file  */
1102     for ( i = 0; i < XtNumber( raw_fnames ); i++ )
1103         raw_fnames[i] = p->raw_fnames[i];
1104 
1105     if ( !TVRAWVIDEOOpen( raw_fnames, p->num_raw_fnames, FALSE, &rf ) ) {
1106         XUTILDialogPause( TVTOPLEVEL, "Error", "Couldn't open output file\n",
1107                           TV_DIALOG_TYPE_OK );
1108         goto RETURN;
1109     }
1110 
1111     if ( p->audio_enabled ) {
1112         Snd.sample_fmt  = p->snd_fmt.samp_fmt,
1113         Snd.stereo      = p->snd_fmt.stereo,
1114         Snd.sample_rate = p->snd_fmt.samp_rate,
1115         Snd.buf         = NULL,
1116         Snd.bytes       = 0;
1117 
1118         if ( !TVAUDIOOpenDsp( &Snd, TRUE, &dsp_fd, &error_msg ) ) {
1119             XUTILDialogPause( TVTOPLEVEL, "Error", error_msg,
1120                               TV_DIALOG_TYPE_OK );
1121             dsp_fd = -1;
1122             goto RETURN;
1123         }
1124         TVAUDIOSelectLineForRecord();
1125     }
1126 
1127     /*  Disable all but stop btn  */
1128     Recording           = TRUE;
1129     Optimizing          = optimizing;
1130     First_image         = TRUE;
1131     d->video.capture_on = TRUE;
1132 
1133     if ( !optimizing )
1134         UpdateButtons();
1135     else {
1136         DialogSetEnabled( False );
1137 
1138         /*  Build wait dialog  */
1139         dialog_shell = XUTILDialogBuild( TVTOPLEVEL, "Please Wait",
1140                                          "Optimizing Capture Speed...",
1141                                          TV_DIALOG_TYPE_CANCEL );
1142         XUTILXtPopup( dialog_shell, XtGrabNone, TVTOPLEVEL );
1143         Wait_dialog = dialog_shell;
1144     }
1145 
1146     /*  Flush X events (update GUI buttons, etc.)  */
1147     XSync( TVDISPLAY, False );
1148 
1149     /*  Queue up an initial request to capture a frame (to driver buffer)  */
1150     TVCAPTURESetFrameDoneCBEnabled( c, TRUE );
1151     TVCAPTURESetCaptureMode       ( c, TV_CAPTURE_CONTINUOUS      );
1152     TVCAPTURESetTransferMode      ( c, TV_TRANSFER_STD_IMAGE      );
1153     TVCAPTURESetRegionGeom        ( c, &p->geom                   );
1154     TVCAPTURESetPixelGeom         ( c, &p->pix_geom               );
1155     TVCAPTURESetFPS               ( c, p->fps                     );
1156 
1157     if ( !TVCAPTUREConfigure( c, &cfg_fail_msg ) ) {
1158         fprintf( stderr, "TVCAPTUREConfigure() failed: %s\n", cfg_fail_msg );
1159 
1160         UnlinkRawFiles( p->raw_fnames );
1161         goto RETURN;
1162     }
1163     TVCAPTUREStart( c );
1164     Out_rf = rf;
1165     if ( Audio_enabled )
1166         Dsp_fd = dsp_fd;
1167 
1168     /*******************************************************************/
1169     /*  Now, madly capture frames/audio and write to disk in raw form  */
1170     /*******************************************************************/
1171     TVSetWorkProcTimeout( 0 );
1172     while ( Recording && ( !optimizing || XtIsRealized( Wait_dialog ) ) ) {
1173         XEvent   ev;
1174 
1175         XtAppNextEvent( TVAPPCTX, &ev );
1176         XtDispatchEvent( &ev );
1177     }
1178     TVSetWorkProcTimeout( -1 );
1179 
1180     TVCAPTUREStop( c );
1181 
1182     Out_rf = NULL;
1183     TVRAWVIDEOClose( &rf );
1184     if ( Audio_enabled ) {
1185         close( dsp_fd );
1186         Dsp_fd = dsp_fd = -1;
1187     }
1188 
1189     /*  Turn TV back on so user can watch while they wait  */
1190     TVCAPTUREClearPendingFrames();
1191     TVSCREENSetScreenUpdateEnabled( TRUE );
1192     d->video.capture_on = FALSE;
1193     video_disabled      = FALSE;
1194 
1195     if ( optimizing ) {
1196         TV_INT32 fps;
1197         char     str[20];
1198 
1199         /*  If user canceled optimization, bail out  */
1200         if ( Optimizing )
1201             goto RETURN;
1202 
1203         /*  This is a cheesy first-cut  */
1204         fps = Vid_stats.frames*1000000L/Vid_stats.time_us;
1205         sprintf( str, "%ld", fps );
1206         TextValUpdate( FPS_text, str );
1207     }
1208 
1209     /*  Does user want more than just the vid/aud captured to a raw file?  */
1210     if ( Sel_vtarg == TV_VIDEO_TARGET_RAW )
1211         goto RETURN;
1212 
1213     /*  Are we optimizing?  If so, done  */
1214     if ( optimizing )
1215         goto RETURN;
1216 
1217     /*  FIXME:  The Record and Optimize callbacks now aren't very different  */
1218     /*          before this point.  Merge them.                              */
1219 
1220     /*  FIXME:  Consider parallelizing aud/vid encode in fxtv_cnvt.sh script */
1221 
1222     /*  Write conversion script  */
1223     /*    FIXME:  Snapshot these parms when we start capture  */
1224     if ( !WriteVidConvertShScript( p ) ) {
1225         XUTILDialogPause( TVTOPLEVEL, "Error",
1226                           "Failed writing format conversion script\n",
1227                           TV_DIALOG_TYPE_OK );
1228         goto RETURN;
1229     }
1230 
1231     /*  Does user want us to go ahead and produce images/video/audio  */
1232     if ( Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY )
1233         goto RETURN;
1234 
1235     /*  Run conversion subprocess  */
1236     DialogSetEnabled( False );
1237     RunCnvtCmd( p->script_fname,
1238                 ( p->cleanup_tmp_files ? p->raw_fnames : NULL ) );
1239     cmd_running = TRUE;
1240 
1241  RETURN:
1242     if ( dialog_shell )
1243         XtDestroyWidget( dialog_shell );
1244 
1245     if ( optimizing && p )
1246         UnlinkRawFiles( p->raw_fnames );
1247 
1248     if ( rf != NULL )
1249         TVRAWVIDEOClose( &rf );
1250 
1251     if ( !cmd_running ) {
1252         Recording = FALSE;
1253         free(p);
1254     }
1255 
1256     d->video.capture_on = FALSE;
1257 
1258     if ( dsp_fd >= 0 )
1259         close( dsp_fd );
1260 
1261     if ( video_disabled ) {
1262         TVCAPTUREClearPendingFrames();
1263         TVSCREENSetScreenUpdateEnabled( TRUE );
1264     }
1265 
1266     if ( !optimizing )
1267         UpdateButtons();
1268     else
1269         DialogSetEnabled( True );
1270 }
1271 
1272 
1273 /*  StopCmdCB - Stop recording, if we're recording now  */
StopCmdCB(Widget w,XtPointer cl,XtPointer cb)1274 static void StopCmdCB( Widget w, XtPointer cl, XtPointer cb )
1275 {
1276     if ( !Recording ) {
1277         XBell( TVDISPLAY, 100 );
1278         return;
1279     }
1280 
1281     Recording = FALSE;
1282 }
1283 
1284 
1285 /*  DismissCmdCB - Dismiss the dialog, if we're not recording  */
DismissCmdCB(Widget w,XtPointer cl,XtPointer cb)1286 static void DismissCmdCB( Widget w, XtPointer cl, XtPointer cb )
1287 {
1288     if ( Recording ) {
1289         XBell( TVDISPLAY, 100 );
1290         return;
1291     }
1292 
1293     XtPopdown( Dialog_wgt );
1294 }
1295 
1296 
1297 /*  ICapFmtMenuCB - Update menu button text  */
ICapFmtMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1298 static void ICapFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1299 {
1300     Widget                menu_btn = Icap_fmt_menu_btn;
1301     TV_ICAPFMT_ITEM_DEF  *def  = (TV_ICAPFMT_ITEM_DEF *) cl;
1302 
1303     SetMenuSelection( menu_btn, def->fmt );
1304 }
1305 
1306 /*  IFileFmtMenuCB - Update menu button text  */
IFileFmtMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1307 static void IFileFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1308 {
1309     Widget                menu_btn = Ifile_fmt_menu_btn;
1310     TV_IFILEFMT_ITEM_DEF *def  = (TV_IFILEFMT_ITEM_DEF *) cl;
1311 
1312     SetMenuSelection( menu_btn, def->fmt );
1313 }
1314 
1315 /*  VFmtMenuItemCB - Update menu button text with selected choice  */
VFmtMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1316 static void VFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1317 {
1318     Widget            menu_btn = Vtrg_menu_btn;
1319     TV_VTRG_ITEM_DEF *def      = (TV_VTRG_ITEM_DEF *) cl;
1320 
1321     SetMenuSelection( menu_btn, def->fmt );
1322 }
1323 
1324 /*  FFmtMenuItemCB - Update menu button text with selected choice  */
FFmtMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1325 static void FFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1326 {
1327     Widget            menu_btn = Ffmt_menu_btn;
1328     TV_FFMT_ITEM_DEF *def      = (TV_FFMT_ITEM_DEF *) cl;
1329 
1330     SetMenuSelection( menu_btn, def->fmt );
1331 
1332     /*  If Sun AU, default to 8-bit MULAW, Mono, & 8012 s/s  */
1333     if ( def->fmt == TV_AUDIO_FILE_FMT_SUNAU ) {
1334         SetMenuSelection( Sfmt_menu_btn, TV_AUDIO_SAMPLE_FMT_MULAW_U8 );
1335         SetMenuSelection( Chan_menu_btn, FALSE );
1336         SetMenuSelection( Rate_menu_btn, 8012 );
1337     }
1338 
1339     /*  If any of the rest, go for the max  */
1340     else {
1341         SetMenuSelection( Sfmt_menu_btn, TV_AUDIO_SAMPLE_FMT_LIN_S16_LE );
1342         SetMenuSelection( Chan_menu_btn, TRUE );
1343         SetMenuSelection( Rate_menu_btn, 44100 );
1344     }
1345 }
1346 
1347 
1348 /*  SFmtMenuItemCB - Update menu button text with selected choice  */
SFmtMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1349 static void SFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1350 {
1351     Widget            menu_btn = Sfmt_menu_btn;
1352     TV_SFMT_ITEM_DEF *def      = (TV_SFMT_ITEM_DEF *) cl;
1353 
1354     SetMenuSelection( menu_btn, def->fmt );
1355 
1356     /*  If 8-bit ULAW, default to Mono & 8012 s/s  */
1357     if ( def->fmt == TV_AUDIO_SAMPLE_FMT_MULAW_U8 ) {
1358         SetMenuSelection( Chan_menu_btn, FALSE );
1359         SetMenuSelection( Rate_menu_btn, 8012 );
1360     }
1361 }
1362 
1363 
1364 /*  ChanMenuItemCB - Update menu button text with selected choice  */
ChanMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1365 static void ChanMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1366 {
1367     Widget           menu_btn = Chan_menu_btn;
1368     TV_CHAN_ITEM_DEF *def     = (TV_CHAN_ITEM_DEF *) cl;
1369 
1370     SetMenuSelection( menu_btn, def->stereo );
1371 }
1372 
1373 
1374 /*  RateMenuItemCB - Update menu button text with selected choice  */
RateMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1375 static void RateMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1376 {
1377     Widget           menu_btn = Rate_menu_btn;
1378     TV_RATE_ITEM_DEF *def     = (TV_RATE_ITEM_DEF *) cl;
1379 
1380     SetMenuSelection( menu_btn, def->rate );
1381 }
1382 
1383 
1384 /*  AudioEnableToggleCB - Enable/Disable capturing of audio with video  */
AudioEnableToggleCB(Widget w,XtPointer cl,XtPointer cb)1385 static void AudioEnableToggleCB( Widget w, XtPointer cl, XtPointer cb )
1386 {
1387     XtVaGetValues( w, XtNstate, &Audio_enabled,
1388                       NULL );
1389     EVPRINTF(( "Audio Enable = %s\n", Audio_enabled ? "yes" : "no" ));
1390 
1391     DialogSetEnabled( TRUE );
1392 }
1393 
1394 
1395 /*  CreateSingleLineTextField - convenience rtn to create single line  */
1396 /*    text fields the way we like 'em.                                 */
CreateSingleLineTextField(char wgt_name[],Widget parent)1397 Widget CreateSingleLineTextField( char wgt_name[], Widget parent )
1398 {
1399     XtTranslations transl;
1400 
1401     Widget wgt;
1402 
1403     wgt = XtVaCreateManagedWidget( wgt_name, asciiTextWidgetClass, parent,
1404                                    XtNtype            , XawAsciiString,
1405                                    XtNuseStringInPlace, False,
1406                                    XtNscrollHorizontal, XawtextScrollNever,
1407                                    XtNscrollVertical  , XawtextScrollNever,
1408                                    XtNdisplayCaret    , False,
1409                                    XtNeditType        , XawtextEdit,
1410                                    XtNresize          , XawtextResizeNever,
1411                                    NULL );
1412 
1413     /*  Text widget translation overrides  */
1414     transl = XtParseTranslationTable( G_transl_ovr_ascii_text );
1415     XtOverrideTranslations( wgt, transl );
1416     transl = XtParseTranslationTable( G_transl_ovr_ascii_text_1line );
1417     XtOverrideTranslations( wgt, transl );
1418 
1419     return wgt;
1420 }
1421 
1422 
BuildImageCapWgtPanel(Widget parent)1423 static void BuildImageCapWgtPanel( Widget parent )
1424 {
1425     Widget    w, gbox, cbox, menu_shell;
1426     TV_INT32  i;
1427 
1428     gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, parent,
1429                                    XtNorientation, XtorientVertical,
1430                                    NULL );
1431 
1432     w = XtVaCreateManagedWidget( "imageCapLabel", labelWidgetClass, gbox,
1433                                  XtNjustify, XtJustifyLeft,
1434                                  XtNwidth, 263,
1435                                  NULL );
1436 
1437     /*  Resolution text  */
1438     cbox = XtVaCreateManagedWidget( "resBox", boxWidgetClass, gbox,
1439                                    XtNorientation, XtorientHorizontal,
1440                                    NULL );
1441 
1442     w = XtVaCreateManagedWidget( "resLabel", labelWidgetClass, cbox,
1443                                  XtNjustify, XtJustifyRight,
1444                                  XtNwidth, 90,
1445                                  NULL );
1446 
1447     Res_text = CreateSingleLineTextField( "resText", cbox );
1448 
1449     /*  Capture format menu  */
1450     cbox = XtVaCreateManagedWidget( "iCapFmtBox", boxWidgetClass, gbox,
1451                                    XtNorientation, XtorientHorizontal,
1452                                    NULL );
1453 
1454 
1455     w = XtVaCreateManagedWidget( "iCapFmtLabel", labelWidgetClass, cbox,
1456                                  XtNjustify, XtJustifyRight,
1457                                  XtNwidth, 90,
1458                                  NULL );
1459 
1460     w = XtVaCreateManagedWidget( "iCapFmtMenuBox", boxWidgetClass, cbox,
1461                                  XtNorientation, XtorientHorizontal,
1462                                  NULL );
1463 
1464     Icap_fmt_menu_btn = XtVaCreateManagedWidget( "iCapFmtMenu",
1465                                         menuButtonWidgetClass, w,
1466                                         XtNresize, XawtextResizeNever,
1467                                         NULL );
1468 
1469     menu_shell = XtVaCreatePopupShell( "menu",
1470                                      simpleMenuWidgetClass, Icap_fmt_menu_btn,
1471                                      NULL );
1472 
1473     /*  Create all format items for this menu  */
1474     for ( i = 0; i < XtNumber( Icap_fmt_item_def ); i++ ) {
1475         Widget item;
1476 
1477         item = XtVaCreateManagedWidget( Icap_fmt_item_def[i].wgt_name,
1478                                         smeBSBObjectClass,
1479                                         menu_shell,
1480                                         NULL );
1481         Icap_fmt_item_def[i].wgt = item;
1482 
1483         XtAddCallback( item, XtNcallback, ICapFmtMenuItemCB,
1484                        (XtPointer) &Icap_fmt_item_def[i] );
1485 
1486         /*  Add a separator between the YUV and RGB entries  */
1487         if ( i == TV_ICAP_LAST_RGB )
1488             XtVaCreateManagedWidget( "separator", smeLineObjectClass,
1489                                      menu_shell, NULL );
1490     }
1491 
1492     /*  FPS text & optimize button  */
1493     cbox = XtVaCreateManagedWidget( "fpsBox", boxWidgetClass, gbox,
1494                                    XtNorientation, XtorientHorizontal,
1495                                    NULL );
1496 
1497     w = XtVaCreateManagedWidget( "fpsLabel", labelWidgetClass, cbox,
1498                                  XtNjustify, XtJustifyRight,
1499                                  XtNwidth, 90,
1500                                  NULL );
1501 
1502     FPS_text = CreateSingleLineTextField( "fpsText", cbox );
1503 
1504     w = XtVaCreateManagedWidget( "optimizeCmd", commandWidgetClass, cbox,
1505                                  NULL );
1506     XtAddCallback( w, XtNcallback, RecordCmdCB, "optimize" );
1507 }
1508 
1509 
BuildImageEncWgtPanel(Widget parent)1510 static void BuildImageEncWgtPanel( Widget parent )
1511 {
1512     Widget    w, gbox, cbox, menu_shell;
1513     TV_INT32  i;
1514 
1515     gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, parent,
1516                                    XtNorientation, XtorientVertical,
1517                                    NULL );
1518 
1519     /*  Box label  */
1520     w = XtVaCreateManagedWidget( "imageEncLabel", labelWidgetClass, gbox,
1521                                  XtNjustify, XtJustifyLeft,
1522                                  XtNwidth, 263,
1523                                  NULL );
1524 
1525     /*  Format menu panel  */
1526     cbox = XtVaCreateManagedWidget( "iFileFmtBox", boxWidgetClass, gbox,
1527                                    XtNorientation, XtorientHorizontal,
1528                                    NULL );
1529 
1530 
1531     w = XtVaCreateManagedWidget( "iFileFmtLabel", labelWidgetClass, cbox,
1532                                  NULL );
1533 
1534     w = XtVaCreateManagedWidget( "iFileFmtMenuBox", boxWidgetClass, cbox,
1535                                  XtNorientation, XtorientHorizontal,
1536                                  NULL );
1537 
1538     Ifile_fmt_menu_btn = XtVaCreateManagedWidget( "iFileFmtMenu",
1539                                         menuButtonWidgetClass, w,
1540                                         XtNresize, XawtextResizeNever,
1541                                         NULL );
1542 
1543     menu_shell = XtVaCreatePopupShell( "menu",
1544                                      simpleMenuWidgetClass, Ifile_fmt_menu_btn,
1545                                      NULL );
1546 
1547     /*  Create all format items for this menu  */
1548     for ( i = 0; i < XtNumber( Ifile_fmt_item_def ); i++ ) {
1549         Widget item;
1550 
1551         item = XtVaCreateManagedWidget( Ifile_fmt_item_def[i].wgt_name,
1552                                         smeBSBObjectClass,
1553                                         menu_shell,
1554                                         NULL );
1555         Ifile_fmt_item_def[i].wgt = item;
1556 
1557         XtAddCallback( item, XtNcallback, IFileFmtMenuItemCB,
1558                        (XtPointer) &Ifile_fmt_item_def[i] );
1559     }
1560 }
1561 
1562 
BuildAudioCapWgtPanel(Widget parent)1563 static void BuildAudioCapWgtPanel( Widget parent )
1564 {
1565     Widget    w, gbox, cbox, fbox, lbox, form, menu_shell;
1566     TV_INT32  i;
1567 
1568     gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, parent,
1569                                    XtNorientation, XtorientVertical,
1570                                    NULL );
1571 
1572     w = XtVaCreateManagedWidget( "audioCapLabel", labelWidgetClass, gbox,
1573                                  XtNjustify, XtJustifyLeft,
1574                                  XtNwidth, 345,
1575                                  NULL );
1576 
1577     /*  Audio Capture On/Off Toggle  */
1578     form = XtVaCreateManagedWidget( "audCapForm", formWidgetClass, gbox,
1579                                    XtNorientation, XtorientHorizontal,
1580                                    NULL );
1581 
1582     w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, form,
1583                                  XtNjustify, XtJustifyLeft,
1584                                  XtNlabel, " ",
1585                                  XtNresize, False,
1586                                  XtNencoding, XawTextEncoding8bit,
1587                                  XtNwidth, 70,
1588                                  NULL );
1589 
1590     w = XtVaCreateManagedWidget( "audCapToggle", toggleWidgetClass, form,
1591                                  XtNlabel, " ",
1592                                  XtNfromHoriz, w,
1593                                  NULL );
1594     Audio_cap_wgt = w;
1595 
1596     XtAddCallback( w, XtNcallback, AudioEnableToggleCB, NULL );
1597 
1598     w = XtVaCreateManagedWidget( "audCapToggleLabel", labelWidgetClass, form,
1599                                  XtNresize, False,
1600                                  XtNfromHoriz, w,
1601                                  XtNencoding, XawTextEncoding8bit,
1602                                  XtNjustify, XtJustifyLeft,
1603                                  NULL );
1604 
1605     /*  Capture format box  */
1606     fbox = XtVaCreateManagedWidget( "captureBox", boxWidgetClass, gbox,
1607                                     XtNorientation, XtorientHorizontal,
1608                                     NULL );
1609     Audio_cap_mgr = fbox;
1610 
1611     lbox = XtVaCreateManagedWidget( "sampFmtLabelBox", boxWidgetClass, fbox,
1612                                     XtNorientation, XtorientVertical,
1613                                     NULL );
1614 
1615     w = XtVaCreateManagedWidget( "sampFmtLabel", labelWidgetClass, lbox,
1616                                  XtNresize, False,
1617                                  XtNencoding, XawTextEncoding8bit,
1618                                  NULL );
1619 
1620     cbox = XtVaCreateManagedWidget( "sampFmtWgtBox", boxWidgetClass, fbox,
1621                                     XtNorientation, XtorientVertical,
1622                                     NULL );
1623 
1624     /*  Sample Format Choice Box  */
1625     Sfmt_menu_btn = XtVaCreateManagedWidget( "sampFmtMenu",
1626                                         menuButtonWidgetClass, cbox,
1627                                         XtNresize, XawtextResizeNever,
1628                                         NULL );
1629 
1630     menu_shell = XtVaCreatePopupShell( "menu",
1631                                        simpleMenuWidgetClass, Sfmt_menu_btn,
1632                                        NULL );
1633 
1634     for ( i = 0; i < Sfmt_item_def_size; i++ ) {
1635         Widget item;
1636 
1637         item = XtVaCreateManagedWidget( Sfmt_item_def[i].wgt_name,
1638                                         smeBSBObjectClass,
1639                                         menu_shell,
1640                                         NULL );
1641         Sfmt_item_def[i].wgt = item;
1642 
1643         XtAddCallback( item, XtNcallback, SFmtMenuItemCB, &Sfmt_item_def[i] );
1644     }
1645 
1646     /*  # Channels Choice Box  */
1647     w = XtVaCreateManagedWidget( "chanMenuBox", boxWidgetClass, cbox,
1648                                  XtNorientation, XtorientHorizontal,
1649                                  NULL );
1650 
1651     Chan_menu_btn = XtVaCreateManagedWidget( "chanMenu",
1652                                         menuButtonWidgetClass, w,
1653                                         XtNresize, XawtextResizeNever,
1654                                         NULL );
1655 
1656     menu_shell = XtVaCreatePopupShell( "menu",
1657                                        simpleMenuWidgetClass, Chan_menu_btn,
1658                                        NULL );
1659 
1660     for ( i = 0; i < Chan_item_def_size; i++ ) {
1661         Widget item;
1662 
1663         item = XtVaCreateManagedWidget( Chan_item_def[i].wgt_name,
1664                                         smeBSBObjectClass,
1665                                         menu_shell,
1666                                         NULL );
1667         Chan_item_def[i].wgt = item;
1668 
1669         XtAddCallback( item, XtNcallback, ChanMenuItemCB, &Chan_item_def[i] );
1670     }
1671 
1672     /*  Sample Rate Choice Box  */
1673     w = XtVaCreateManagedWidget( "rateMenuBox", boxWidgetClass, cbox,
1674                                  XtNorientation, XtorientHorizontal,
1675                                  NULL );
1676 
1677     Rate_menu_btn = XtVaCreateManagedWidget( "rateMenu",
1678                                         menuButtonWidgetClass, w,
1679                                         XtNresize, XawtextResizeNever,
1680                                         NULL );
1681 
1682     menu_shell = XtVaCreatePopupShell( "menu",
1683                                        simpleMenuWidgetClass, Rate_menu_btn,
1684                                        NULL );
1685 
1686     for ( i = 0; i < Rate_item_def_size; i++ ) {
1687         Widget item;
1688 
1689         item = XtVaCreateManagedWidget( Rate_item_def[i].wgt_name,
1690                                         smeBSBObjectClass,
1691                                         menu_shell,
1692                                         NULL );
1693         Rate_item_def[i].wgt = item;
1694 
1695         XtAddCallback( item, XtNcallback, RateMenuItemCB, &Rate_item_def[i] );
1696     }
1697 }
1698 
1699 
BuildAudioEncWgtPanel(Widget parent)1700 static void BuildAudioEncWgtPanel( Widget parent )
1701 {
1702     Widget    w, gbox, cbox, menu_shell;
1703     TV_INT32  i;
1704 
1705     gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, parent,
1706                                    XtNorientation, XtorientVertical,
1707                                    NULL );
1708     Audio_enc_mgr = gbox;
1709 
1710     w = XtVaCreateManagedWidget( "audioEncLabel", labelWidgetClass, gbox,
1711                                  XtNjustify, XtJustifyLeft,
1712                                  XtNwidth, 345,
1713                                  NULL );
1714 
1715     cbox = XtVaCreateManagedWidget( "fileFmtBox", boxWidgetClass, gbox,
1716                                     XtNorientation, XtorientHorizontal,
1717                                     NULL );
1718 
1719     w = XtVaCreateManagedWidget( "fileFmtLabel", labelWidgetClass, cbox,
1720                                  XtNjustify, XtJustifyRight,
1721                                  NULL );
1722 
1723     /*  File Format Choice Box  */
1724     Ffmt_menu_btn = XtVaCreateManagedWidget( "fileFmtMenu",
1725                                         menuButtonWidgetClass, cbox,
1726                                         XtNresize, XawtextResizeNever,
1727                                         NULL );
1728 
1729     menu_shell = XtVaCreatePopupShell( "menu",
1730                                        simpleMenuWidgetClass, Ffmt_menu_btn,
1731                                        NULL );
1732 
1733     for ( i = 0; i < Ffmt_item_def_size; i++ ) {
1734         Widget item;
1735 
1736         item = XtVaCreateManagedWidget( Ffmt_item_def[i].file_ext,
1737                                         smeBSBObjectClass,
1738                                         menu_shell,
1739                                         NULL );
1740         Ffmt_item_def[i].wgt = item;
1741 
1742         XtAddCallback( item, XtNcallback, FFmtMenuItemCB, &Ffmt_item_def[i] );
1743     }
1744 }
1745 
1746 
TVVIDSAVDialogBuild(Widget * dialog_wgt)1747 static void TVVIDSAVDialogBuild( Widget *dialog_wgt )
1748 {
1749     Widget    w, main1, main2, sbox, cbox, gbox, form, menu_shell;
1750     TV_INT32  i;
1751 
1752     /*  Create the dialog widgets  */
1753     *dialog_wgt = XtVaCreatePopupShell( "videoSaveDialog",
1754                       transientShellWidgetClass, TVTOPLEVEL,
1755                       NULL );
1756 
1757     main1 = XtVaCreateManagedWidget( "mainBox", boxWidgetClass, *dialog_wgt,
1758                                      XtNorientation, XtorientVertical,
1759                                      NULL );
1760     Main_wgt = main1;
1761 
1762     main2 = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, main1,
1763                                    XtNorientation, XtorientVertical,
1764                                    NULL );
1765 
1766     /*  Filename base text  */
1767     cbox = XtVaCreateManagedWidget( "fileBaseBox", boxWidgetClass, main2,
1768                                    XtNorientation, XtorientHorizontal,
1769                                    NULL );
1770 
1771     w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, cbox,
1772                                    XtNlabel, " ",
1773                                    XtNresize, False,
1774                                    XtNwidth, 170,
1775                                    NULL );
1776 
1777     File_label = XtVaCreateManagedWidget( "fileBaseLabel", labelWidgetClass,
1778                                    cbox,
1779                                    XtNresize, False,
1780                                    XtNwidth, 100,
1781                                    XtNjustify, XtJustifyRight,
1782                                    NULL );
1783 
1784     Fname_text = CreateSingleLineTextField( "fileBaseText", cbox );
1785 
1786     /*  File format selection box  */
1787     cbox = XtVaCreateManagedWidget( "fileFmtBox", boxWidgetClass, main2,
1788                                     XtNorientation, XtorientHorizontal,
1789                                     XtNfromVert, cbox,
1790                                     NULL );
1791 
1792     w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, cbox,
1793                                    XtNlabel, " ",
1794                                    XtNresize, False,
1795                                    XtNwidth, 170,
1796                                    NULL );
1797 
1798     w = XtVaCreateManagedWidget( "targetLabel", labelWidgetClass, cbox,
1799                                  XtNjustify, XtJustifyRight,
1800                                  XtNwidth, 100,
1801                                  NULL );
1802 
1803     Vtrg_menu_btn = XtVaCreateManagedWidget( "targetMenu",
1804                                         menuButtonWidgetClass, cbox,
1805                                         XtNresize, XawtextResizeNever,
1806                                         NULL );
1807 
1808     menu_shell = XtVaCreatePopupShell( "menu",
1809                                        simpleMenuWidgetClass, Vtrg_menu_btn,
1810                                        NULL );
1811 
1812     for ( i = 0; i < Vtrg_item_def_size; i++ ) {
1813         Widget item;
1814 
1815         item = XtVaCreateManagedWidget( Vtrg_item_def[i].wgt_name,
1816                                         smeBSBObjectClass,
1817                                         menu_shell,
1818                                         NULL );
1819         Vtrg_item_def[i].wgt = item;
1820 
1821         XtAddCallback( item, XtNcallback, VFmtMenuItemCB, &Vtrg_item_def[i] );
1822     }
1823 
1824     /*  Parameter panels (left and right)  */
1825     sbox = XtVaCreateManagedWidget( "sideBox", boxWidgetClass, main2,
1826                                    XtNorientation, XtorientVertical,
1827                                    XtNfromVert, cbox,
1828                                    NULL );
1829 
1830     gbox = XtVaCreateManagedWidget( "group1Box", boxWidgetClass, sbox,
1831                                    XtNorientation, XtorientHorizontal,
1832                                    NULL );
1833 
1834     BuildImageCapWgtPanel( gbox );
1835     BuildAudioCapWgtPanel( gbox );
1836 
1837     gbox = XtVaCreateManagedWidget( "group2Box", boxWidgetClass, sbox,
1838                                    XtNorientation, XtorientHorizontal,
1839                                    NULL );
1840 
1841     BuildImageEncWgtPanel( gbox );
1842     BuildAudioEncWgtPanel( gbox );
1843 
1844 
1845     /*  Cleanup Temp Files toggle  */
1846     form = XtVaCreateManagedWidget( "cleanupForm", formWidgetClass, main2,
1847                                    XtNorientation, XtorientHorizontal,
1848                                    NULL );
1849     Cleanup_form = form;
1850 
1851     w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, form,
1852                                  XtNjustify, XtJustifyLeft,
1853                                  XtNlabel, " ",
1854                                  XtNresize, False,
1855                                  XtNencoding, XawTextEncoding8bit,
1856                                  XtNwidth, 250,
1857                                  NULL );
1858 
1859     w = XtVaCreateManagedWidget( "cleanupToggle", toggleWidgetClass, form,
1860                                  XtNlabel, " ",
1861                                  XtNfromHoriz, w,
1862                                  NULL );
1863     Cleanup_wgt = w;
1864 
1865     w = XtVaCreateManagedWidget( "cleanupLabel", labelWidgetClass, form,
1866                                  XtNresize, False,
1867                                  XtNfromHoriz, w,
1868                                  XtNencoding, XawTextEncoding8bit,
1869                                  XtNjustify, XtJustifyLeft,
1870                                  NULL );
1871     /*  Action button form  */
1872     form = XtVaCreateManagedWidget( "actionForm", formWidgetClass, main1,
1873                                  XtNfromVert, gbox,
1874                                  NULL );
1875 
1876     w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, form,
1877                                  XtNjustify, XtJustifyLeft,
1878                                  XtNlabel, " ",
1879                                  XtNresize, False,
1880                                  XtNencoding, XawTextEncoding8bit,
1881                                  XtNwidth, 200,
1882                                  NULL );
1883 
1884     w = XtVaCreateManagedWidget( "recordCmd", commandWidgetClass, form,
1885                                  XtNfromHoriz, w,
1886                                  NULL );
1887     XtAddCallback( w, XtNcallback, RecordCmdCB, "record" );
1888     Record_btn = w;
1889 
1890     w = XtVaCreateManagedWidget( "stopCmd", commandWidgetClass, form,
1891                                  XtNfromHoriz, w,
1892                                  NULL );
1893     XtAddCallback( w, XtNcallback, StopCmdCB, NULL );
1894     Stop_btn = w;
1895 
1896     w = XtVaCreateManagedWidget( "dismissCmd", commandWidgetClass, form,
1897                                  XtNfromHoriz, w,
1898                                  NULL );
1899     XtAddCallback( w, XtNcallback, DismissCmdCB, NULL );
1900     Dismiss_btn = w;
1901 }
1902 
1903 
TVVIDSAVDIALOGPopUp()1904 void TVVIDSAVDIALOGPopUp()
1905 {
1906     /*  Do dialog  */
1907     if ( Dialog_wgt == NULL )
1908         TVVIDSAVDialogBuild( &Dialog_wgt );
1909 
1910     TVVIDSAVDIALOGResync();
1911 
1912     XUTILXtPopup( Dialog_wgt, XtGrabNone, TVTOPLEVEL );
1913 }
1914 
TVVIDSAVDIALOGResync()1915 void TVVIDSAVDIALOGResync()
1916 {
1917     TV_DISK    *d = &G_glob.disk;
1918     char        str[80];
1919 
1920     /*  FIXME:  Also install EnterNotify handler for this dialog to  */
1921     /*    resync values on entry of it's shell.                      */
1922 
1923     if ( Dialog_wgt == NULL )
1924         return;
1925 
1926     /*  Set text fields to current settings  */
1927     TextValUpdate( Fname_text, d->fn_video_base );
1928 
1929     sprintf( str, "%ldx%ld", d->video.geom.w, d->video.geom.h );
1930     TextValUpdate( Res_text, str );
1931 
1932     sprintf( str, "%ld", d->video.fps );
1933     TextValUpdate( FPS_text, str );
1934 
1935     /*  Set selections based on active format  */
1936     SetMenuSelection( Ffmt_menu_btn, d->audio.file_fmt    );
1937     SetMenuSelection( Sfmt_menu_btn, d->audio.sample_fmt  );
1938     SetMenuSelection( Chan_menu_btn, d->audio.stereo      );
1939     SetMenuSelection( Rate_menu_btn, d->audio.sample_rate );
1940 
1941     SetMenuSelection( Icap_fmt_menu_btn , d->video.icap_fmt );
1942     SetMenuSelection( Ifile_fmt_menu_btn, d->freeze_fmt );
1943 
1944     SetMenuSelection( Vtrg_menu_btn, d->video.target );
1945 
1946     XtVaSetValues( Cleanup_wgt  , XtNstate,  d->video.cleanup_temp, NULL);
1947     Audio_enabled = d->video.cap_audio && App_res.do_audio;
1948     XtVaSetValues( Audio_cap_wgt, XtNstate,  Audio_enabled        , NULL );
1949 
1950     XtSetSensitive( Audio_cap_mgr, d->video.cap_audio );
1951     XtSetSensitive( Audio_enc_mgr, d->video.cap_audio );
1952 
1953     /*  Set button sensitivites  */
1954     UpdateButtons();
1955 }
1956 
1957 
1958 /**@BEGINFUNC**************************************************************
1959 
1960     Prototype  : void TVVIDSAVDIALOGNewFrameHdlr(
1961                       TV_IMAGE *img )
1962 
1963     Purpose    : Called to handle a new frame when we're capturing video
1964                  frames to disk.
1965 
1966     Programmer : 31-May-97  Randall Hopper
1967 
1968     Parameters : img      - I: captured image
1969 
1970     Returns    : None.
1971 
1972     Globals    : None.
1973 
1974  **@ENDFUNC*****************************************************************/
1975 
TVVIDSAVDIALOGNewFrameHdlr(TV_IMAGE * img)1976 void TVVIDSAVDIALOGNewFrameHdlr( TV_IMAGE *img )
1977 {
1978     static long last_time,   /*  FIXME  */
1979                 last_save_time,
1980                 in_a_row = 0;
1981     TV_RAW_IMAGE_HEADER head;
1982 
1983     /*  This shouldn't happen  */
1984     if ( !Recording )
1985         return;
1986 
1987     /*  FIXME:  Remove this  */
1988 #ifdef COPY_FRAME
1989     TV_IMAGE    tmp_img;
1990     static TV_UINT8 *buf = NULL;
1991     static TV_INT32  buf_size = 0;
1992     TV_INT32    bytes;
1993 
1994 
1995     /*  Alloc mem for tmp copy of image -- FIXME hack  */
1996     /*    We do this to avoid the driver stomping on the image   */
1997     /*    before we've written it.                               */
1998     /*    Fixme: modify the driver for multiple staging buffers  */
1999     /*    we shouldn't have to do this copy                      */
2000     memcpy( &tmp_img, img, sizeof( tmp_img ) );
2001     bytes = TVRAWVIDEOCalcImageSize( &tmp_img );
2002     if (( buf == NULL ) || ( bytes != buf_size )) {
2003         free( buf );
2004         if ( (buf = malloc( bytes )) == NULL )
2005             TVUTILOutOfMemory();
2006         buf_size = bytes;
2007     }
2008     memcpy( buf, img->buf, buf_size );
2009     tmp_img.buf = buf;
2010 #   define img (&tmp_img)
2011 #endif
2012 
2013     /*  If this is the first image, write a header block  */
2014     if ( First_image &&
2015          !TVRAWVIDEOHeaderWrite( Out_rf, img, &Snd ) ) {
2016         fprintf( stderr,
2017                  "TVVIDSAVDIALOGNewFrameHdlr: header disk write failed\n" );
2018         Recording = FALSE;
2019         return;
2020     }
2021 
2022     /*  Take note of elapsed time since the last frame so we know how  */
2023     /*    to temporally space resulting images in the video stream     */
2024     {
2025         struct timeval tv;
2026         long           new_time;
2027         long           diff;
2028 
2029         gettimeofday( &tv, NULL );
2030         new_time = (tv.tv_sec & 0xFFFF) * 1000000L + tv.tv_usec;
2031         if ( First_image ) {
2032             VDPRINTF(( "Frame Delays (in microseconds)\n" ));
2033             First_image = FALSE;
2034             head.delay = 0;
2035             in_a_row = 1;
2036             Vid_stats.frames  = 0;
2037             Vid_stats.time_us = 0;
2038             last_time = last_save_time = new_time;
2039         }
2040         else {
2041             diff        = new_time - last_time;
2042             head.delay = new_time - last_save_time;
2043             Vid_stats.time_us += diff;
2044             Vid_stats.frames++;
2045             last_time = new_time;
2046             if ( /*++*/in_a_row < 2 )
2047                 last_save_time = new_time;
2048             else
2049                 in_a_row = 0;
2050 
2051             VDPRINTF(("%4ld: Delay = %7ld us (Avg = %7ld ms, FPS = %2ld)%s\n",
2052                        Vid_stats.frames, diff,
2053                        Vid_stats.time_us/Vid_stats.frames/1000,
2054                        Vid_stats.frames*1000000L/Vid_stats.time_us,
2055                        (last_time == last_save_time) ? "" : "...Skipped" ));
2056         }
2057     }
2058 
2059     /*  FIXME:  Maybe check that img w,h,Bpp stay same between images  */
2060 
2061     if ( last_time == last_save_time ) {
2062         char            buf[65536];
2063 
2064         /*  Read pending audio  */
2065         if ( Audio_enabled ) {
2066 #ifdef OLD
2067             audio_buf_info  info;
2068 
2069             ioctl( Dsp_fd, SNDCTL_DSP_GETISPACE, &info );
2070             VDPRINTF(( "Bufs = %d / %d, FragSize = %d, TotalBytes = %d\n",
2071                        info.fragments, info.fragstotal, info.fragsize,
2072                        info.bytes ));
2073 #endif
2074 
2075             Snd.bytes = read( Dsp_fd, buf, 65536 );
2076             if ( Snd.bytes == -1 )
2077                 Snd.bytes = 0;
2078             Snd.buf = buf;
2079             /*  FIXME:  Deal with case where more than 64k may be ready      */
2080             /*    Also deal with case where more sound data may become       */
2081             /*    available while waiting for a frame than can fit in sound  */
2082             /*    buffers.                                                   */
2083         }
2084 
2085         if ( !TVRAWVIDEOImageWrite( Out_rf, &head, img, &Snd ) ) {
2086             fprintf( stderr,
2087                      "TVVIDSAVDIALOGNewFrameHdlr: image disk write failed\n" );
2088             Recording = FALSE;
2089             return;
2090         }
2091 
2092         if ( Audio_enabled )
2093             Snd.buf = NULL, Snd.bytes = 0;
2094     }
2095 
2096     /*  If we're optimizing, we're done after X num of images  */
2097     if ( Optimizing && ( Vid_stats.frames >= OPTIMIZE_NUM_FRAMES ) ) {
2098         Optimizing = False;
2099         XtPopdown        ( Wait_dialog );
2100         XtUnrealizeWidget( Wait_dialog );     /*  Wake up optimize Xt loop  */
2101     }
2102 }
2103 
2104 
2105 /**@BEGINFUNC**************************************************************
2106 
2107     Prototype  : void TVVIDSAVDIALOGRecordStart(
2108                      TV_INT32 w, TV_INT32 h )
2109 
2110     Purpose    : Start recording video using the default video capture
2111                  parameters.
2112 
2113     Programmer : 31-May-98  Randall Hopper
2114 
2115     Parameters : w,h - Suggested resolution (e.g. 320x240)
2116                        (if 0, use default resolution)
2117 
2118     Returns    : None.
2119 
2120     Globals    : None.
2121 
2122  **@ENDFUNC*****************************************************************/
2123 
TVVIDSAVDIALOGRecordStart(TV_INT32 w,TV_INT32 h)2124 void TVVIDSAVDIALOGRecordStart( TV_INT32 w, TV_INT32 h )
2125 {
2126     char      *dir, *file;
2127     TV_BOOL    alloc = TRUE;
2128     TV_DISK   *d     = &G_glob.disk;
2129     char       res_str[20];
2130 
2131     /*  FIXME:  Really, we need to separate this save functionality out  */
2132     /*    from the user interface.                                       */
2133     /*    Thus the bogusness of setting the values on the dialog if it   */
2134     /*    exists.                                                        */
2135 
2136     /*  If no filename, generate one at random.  */
2137     if ( d->fn_video_base[0] == '\0' ){
2138         if ( (dir = getcwd( NULL, 0 )) == NULL ) {
2139             dir   = "/tmp";
2140             alloc = FALSE;
2141         }
2142 
2143         unsetenv( "TMPDIR" );
2144         file = tempnam( dir, "Fxtv-video." );
2145         if ( alloc )
2146             free( dir );
2147         dir = NULL;
2148 
2149         d->fn_video_base[0] = '\0';
2150         strncat( d->fn_video_base, file, sizeof(d->fn_video_base)-1 );
2151         free(file);
2152 
2153         if ( Fname_text )
2154             TextValUpdate( Fname_text, d->fn_video_base );
2155     }
2156 
2157     if (( w > 0 ) && ( h > 0 )) {
2158         d->video.geom.w = w;
2159         d->video.geom.h = h;
2160         if ( Res_text ) {
2161             sprintf( res_str, "%ldx%ld", w,h );
2162             TextValUpdate( Res_text, res_str );
2163         }
2164     }
2165 
2166     RecordCmdCB( NULL, "record", NULL );
2167 }
2168 
2169 
2170 /**@BEGINFUNC**************************************************************
2171 
2172     Prototype  : void TVVIDSAVDIALOGRecordStop( void )
2173 
2174     Purpose    : Stop recording video.
2175 
2176     Programmer : 31-May-98  Randall Hopper
2177 
2178     Parameters : None.
2179 
2180     Returns    : None.
2181 
2182     Globals    : None.
2183 
2184  **@ENDFUNC*****************************************************************/
2185 
TVVIDSAVDIALOGRecordStop(void)2186 void TVVIDSAVDIALOGRecordStop( void )
2187 {
2188     StopCmdCB( NULL, NULL, NULL );
2189 
2190     /*  This is a hack.  We want the record event loop to "wake up" and      */
2191     /*    loop.  It doesn't when just any event comes in (timer, file        */
2192     /*    descriptor activity, because apparently some events are processed  */
2193     /*    internally by XtAppNextEvent.  This is bad.  Of course, our        */
2194     /*    having internal X event loop is bad too but anyway.                */
2195     /*  So we send ourselves a dummy client message event.  This magically   */
2196     /*    XtAppNextEvent to return, so we can test for loop termination      */
2197     /*    conditions and get on with life.                                   */
2198     {
2199         static Atom          xa_FXTV_WAKEUP_NEXTEVENT = None;
2200         XClientMessageEvent  ev;
2201 
2202         if ( xa_FXTV_WAKEUP_NEXTEVENT == None )
2203             xa_FXTV_WAKEUP_NEXTEVENT = XInternAtom(TVDISPLAY,
2204                                                "FXTV_WAKEUP_NEXTEVENT", False);
2205 
2206         ev.type = ClientMessage;
2207         ev.display = TVDISPLAY;
2208         ev.message_type = xa_FXTV_WAKEUP_NEXTEVENT;
2209         ev.format = 8;
2210         sprintf( ev.data.b, "Hey, wake up!" );
2211         ev.window = XtWindow(TVTOPLEVEL);
2212 
2213         XSendEvent( TVDISPLAY, XtWindow(TVTOPLEVEL),
2214                     True, NoEventMask, (XEvent *) &ev );
2215         XFlush (TVDISPLAY);
2216     }
2217 }
2218