/* * tv.c * * Main module for FXTV. * * (C) 1997 Randall Hopper * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* ******************** Include Files ************** */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFINE_APP_RESOURCES #include "app_rsrc.h" #include "tvdefines.h" #include "tvutil.h" #include "tvmenu.h" #include "tvscreen.h" #include "actions.h" #include "batch_mode.h" #include "remote.h" #include "glob.h" #include "remotetrans.h" #include "vidsav_dlg.h" #include "xutil.h" /* ******************** Local defines ************** */ #define WORKPROC_DEF_DELAY_MS 200 /* ******************** Private variables ************** */ #ifndef VERS_STR # define VERS_STR "" #endif static int S_signal = 0; static XtIntervalId S_workproc_timer; static TV_BOOL S_workproc_timer_set = False; static TV_INT32 S_workproc_timeout = WORKPROC_DEF_DELAY_MS; static Atom S_wm_delete_window; /* ******************** Forward declarations ************** */ void _XEditResCheckMessages(); void _XDefaultError( Display *display, XErrorEvent *err_event ); /* ******************** Function Definitions ************** */ /* TVIntrHdlr - Called when we receive interrupts. Queue the last one */ /* and process it next time we get back to the work proc. */ static void TVIntrHdlr( int sig ) { /* Don't exit here. X (and our code) are not reentrant. */ /* atexit cleanup code may foul up if we exit() here. */ if (( sig == SIGTERM ) || ( sig == SIGINT ) || ( sig == SIGTSTP )) S_signal = sig; } /* TVXErrorHdlr - Called when we receive an X error. Like the default */ /* X error handler, we'll exit. */ static void TVXErrorHdlr( Display *dpy, XErrorEvent *err_event ) { /* Set flag so we know not to call X calls while we're bailing out. */ G_in_x_error = TRUE; /* Default behavior */ _XDefaultError( dpy, err_event ); /* We'll never get here... */ exit(1); } /* TVWMDeleteWindow - Called when we receive a window manager event. */ /* We register for WM_DELETE_WINDOW for WM Close events. When this */ /* occurs, stop capture and bail out. */ static void TVWMDeleteWindow( Widget w, XEvent *event, String *params, Cardinal num_params ) { if ( event->type == ClientMessage && ( event->xclient.data.l[0] != S_wm_delete_window ) ) { XBell ( XtDisplay(w), 0 ); return; } if ( TVSCREENVideoStarted() ) TVSCREENStopVideo( True ); exit(0); } /* TVWorkProcTimeoutCB - Routine called occasionally to determine if we */ /* need to process a signal that's occurred, or let the capture driver */ /* driver get a few cycles to tell us about a capture frame. */ /* */ /* NOTE: This was originally a work procedure (XtAppAddWorkProc), but */ /* Xt burns up the CPU calling it as fast as it can. So we now use an */ /* Xt timer to throttle it more efficiently. */ static void TVWorkProcTimeoutCB( XtPointer cl_data, XtIntervalId *timer ) { TV_BOOL restart_video; /* Exit if we were asked to */ if (( S_signal == SIGTERM ) || ( S_signal == SIGINT )) exit(0); if ( S_signal == SIGTSTP ) { S_signal = 0; restart_video = TVSCREENVideoStarted(); if ( restart_video ) TVSCREENStopVideo( False ); kill( getpid(), SIGSTOP ); if ( restart_video ) TVSCREENStartVideo(); goto RETURN; } /* Deal with any frame captures we care about */ TVCAPTUREWorkProc(); #ifdef RHH { TV_DISPLAY *d = &G_glob.display; TVANNOTUpdate( &d->annot ); } #endif RETURN: /* Register to get called again */ S_workproc_timer = XtAppAddTimeOut( TVAPPCTX, S_workproc_timeout, TVWorkProcTimeoutCB, NULL ); S_workproc_timer_set = TRUE; return; } /* TVSetWorkProcTimeout - set the workproc timer timeout; if -1, default */ void TVSetWorkProcTimeout( TV_INT32 delay_ms ) { S_workproc_timeout = (delay_ms < 0) ? WORKPROC_DEF_DELAY_MS : delay_ms; /* Restart timer */ if ( S_workproc_timer_set ) XtRemoveTimeOut( S_workproc_timer ); S_workproc_timer = XtAppAddTimeOut( TVAPPCTX, S_workproc_timeout, TVWorkProcTimeoutCB, NULL ); S_workproc_timer_set = TRUE; } static void TVNewFrameHdlr( TV_IMAGE *img ) { if ( G_glob.disk.video.capture_on ) TVVIDSAVDIALOGNewFrameHdlr( img ); else TVSCREENNewFrameHdlr( img ); } /**@BEGINFUNC************************************************************** Prototype : static void TVRemoteCallback( const char key[] ) Purpose : Funct called when we get a key event from a remote control. This is just a glue routine. Programmer : 17-May-98 Randall Hopper Parameters : key - I: key string Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVRemoteCallback( const char key[] ) { TV_BOOL handled; /* First, pass these through the user's Remote translation table */ /* (so they can map remote events to action procedures if desired. */ if ( !TVREMOTETRANSHandleKey( key ) ) { /* No translation for this key, so just pass to our */ /* generic "key" handler and take the default behavior. */ TVACTIONKeyEventHdlr( key, &handled ); } } /**@BEGINFUNC************************************************************** Prototype : static void TVStationListStrToList( String str, TV_STATION **station, TV_INT32 *num_stations ) Purpose : Converts a string list of station definitions to list form. Note that a "station definition" maps a station identifier to a channel number or a frequency. Acceptable formats: 1) defines ( ID , Chan ) 2) - defines , + 1, ..., 3) () defines ( ID , Chan ) 4) id(f) defines ( ID , Freq ) Example: str = "3 ABC(4) 5-7 CBS(9) CNN(f91.25) defines (and is the same as): str = "3(3) ABC(4) 5(5) 6(6) 7(7) CBS(9) CNN(f91.25) Programmer : 01-Oct-97 Randall Hopper Parameters : str - I: string list of station defs station - O: station def struct array (malloced here) num_stations - O: num elements allocated to station[] array Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVStationListStrToList( String str, TV_STATION **station, TV_INT32 *num_stations ) { TV_INT32 i; *station = NULL; *num_stations = 0; /* If no channel list, define (ch#,ch#) mappings for all channels */ if (( str == NULL ) || ( str[0] == '\0' )) { *station = calloc( TV_MAX_CHANNELS, sizeof( (*station)[0] ) ); if ( !*station ) TVUTILOutOfMemory(); for ( i = 0; i < TV_MAX_CHANNELS; i++ ) { sprintf( (*station)[i].id, "%ld", i+TV_CHAN_MIN ); (*station)[i].set_via_channel = TRUE; (*station)[i].channel = i+TV_CHAN_MIN; } *num_stations = TV_MAX_CHANNELS; } /* Otherwise, parse the given channel list definition string */ else { char *s = strdup( str ), *p, *tok, *id_end; TV_STATION new; TV_BOOL chan_range; TV_INT32 alloc_size = 0, num_alloc, chan1, chan2, tmp_int; if ( !s ) TVUTILOutOfMemory(); i = 0; for ( p = s; (tok = strsep( &p, " \t" )) != NULL; ) { if ( *tok == '\0' ) continue; /* Verify any user ID isn't too long or short */ if (( (id_end = strchr( tok, '(' )) != NULL ) && (( id_end == tok ) || ( id_end - tok > sizeof(new.id) - 1 ))) { fprintf( stderr, "Invalid station ID: %s ... skipped\n", tok ); continue; } /* Ok, parse it */ chan_range = FALSE; new.id[0] = '\0'; if (( sscanf( tok, "%[^()](f%f)", new.id, &new.freq ) == 2 ) || ( sscanf( tok, "%[^()](F%f)", new.id, &new.freq ) == 2 )) new.set_via_channel = FALSE; else if ( sscanf( tok, "%[^()](%ld)", new.id, &tmp_int ) == 2 ) { new.set_via_channel = TRUE; new.channel = tmp_int; } else if ( sscanf( tok, "%ld-%ld", &chan1, &chan2 ) == 2 ) chan_range = TRUE; else if ( sscanf( tok, "%ld", &tmp_int ) == 1 ) { new.set_via_channel = TRUE; new.channel = tmp_int; sprintf( new.id, "%d", new.channel ); } else { fprintf( stderr, "Invalid station def: '%s' ...skipped\n", tok ); continue; } /* Further validation */ if (( !chan_range && new.set_via_channel && ( new.channel < 0 ) ) || ( chan_range && ((chan1 < 0) || (chan2 < 0) || (chan1 > chan2)) )) { fprintf( stderr, "Bad chan number in station def: '%s' ...skipped\n", tok ); continue; } /* Ok, now alloc and store new station def(s) */ num_alloc = chan_range ? (chan2-chan1+1) : 1; if ( i + num_alloc > alloc_size ) { alloc_size += num_alloc + 10; *station = realloc( *station, alloc_size * sizeof((*station)[0]) ); if ( !(*station) ) TVUTILOutOfMemory(); } if ( !chan_range ) memcpy( &(*station)[i++], &new, sizeof(new) ); else for ( ; chan1 <= chan2; chan1++ ) { sprintf( new.id, "%ld", chan1 ); new.set_via_channel = TRUE; new.channel = chan1; new.freq = 0.0; memcpy( &(*station)[i++], &new, sizeof(new) ); } } free(s); /* If nothing valid found, add "3(3)" as a default */ if ( i == 0 ) { if ( (*station = malloc( sizeof((*station)[0]) )) == NULL ) TVUTILOutOfMemory(); sprintf( (*station)[0].id, "%d", 3 ); (*station)[0].set_via_channel = TRUE; (*station)[0].channel = 3; (*station)[0].freq = 0.0; i++; } *num_stations = i; } } static void TVSetInitialCaptureDefaults( TV_CAPTURE *c, TV_PREFS *p ) { static const struct { TV_INPUT_FORMAT mode; const char *str; } formats[] = { { TV_INPUT_NTSCM , "ntsc" }, { TV_INPUT_NTSCM , "ntscm" }, { TV_INPUT_NTSCJ , "ntscj" }, { TV_INPUT_PALBDGHI, "palbdghi" }, { TV_INPUT_PALBDGHI, "pal" }, { TV_INPUT_PALM , "palm" }, { TV_INPUT_PALN , "paln" }, { TV_INPUT_SECAM , "secam" }, { TV_INPUT_PALNCOMB, "palncomb" }, { TV_INPUT_PALNCOMB, "rsvd" } }; TV_DISK *dsk = &G_glob.disk; TV_DRIVER_STATE s; TV_INPUT_FORMAT input_format; TV_INPUT_DEVICE video_input_dev; TV_AUDIO_INPUT_DEVICE audio_input_dev; TV_INT32 i,j; char str[160]; /* Query capture driver's current values */ if ( !TVCAPTUREQueryDriverState( c, &s ) ) { fprintf( stderr, "TVCAPTUREQueryDriverState() failed\n" ); exit(1); } /* Add app default settings, if desired */ if ( !App_res.driver_defaults ) { /* Input format */ input_format = TV_INPUT_NTSCM; for ( i = 0; i < XtNumber(formats); i++ ) if ( strcasecmp( App_res.input_format, formats[i].str ) == 0 ) input_format = formats[i].mode; TVCAPTURESetInputFormat( c, input_format ); dsk->video.geom.w = c->width_max / 2; dsk->video.geom.h = c->height_max / 2; dsk->video.fps = c->fps_max; /* Frames per sec (for on-screen display) */ if ( App_res.display_fps < 0 ) App_res.display_fps = c->fps_max; App_res.display_fps = MAX( 1, MIN( c->fps_max, App_res.display_fps ) ); /* Cable/antenna tuner frequency modes */ p->ant_freq_set = 1; p->cable_freq_set = 1; for ( i = 0; i < 2; i++ ) { TV_FREQ_SET *set = (i == 0) ? &p->ant_freq_set : &p->cable_freq_set; char *p; str[0] = '\0'; if ( i == 0 ) strncat( str, App_res.ant_freq_set , sizeof(str)-1 ); else strncat( str, App_res.cable_freq_set, sizeof(str)-1 ); for ( p = str; *p != '\0'; p++ ) *p = tolower( *p ); for ( j = 1; ; j++ ) { if ( (p = TVCAPTUREGetTunerFreqSetName(c,j)) == NULL ) { fprintf( stderr, "Unknown or unsupported channelset: \"%s\"\n", str ); exit(1); } if ( strstr( str, p ) != NULL ) { *set = j; break; } } } /* Tuner mode: antenna/cable */ p->tuner_mode = TV_TUNER_MODE_ANTENNA; if (( strstr( App_res.tuner_mode, "cable" ) != NULL ) || ( strstr( App_res.tuner_mode, "CABLE" ) != NULL )) p->tuner_mode = TV_TUNER_MODE_CABLE; if ( p->tuner_mode == TV_TUNER_MODE_ANTENNA ) TVCAPTURESetTunerFreqSet( c, p->ant_freq_set ); else TVCAPTURESetTunerFreqSet( c, p->cable_freq_set ); /* Appearance params */ TVCAPTURESetBrightness ( c, App_res.brightness ); TVCAPTURESetContrast ( c, App_res.contrast ); TVCAPTURESetHue ( c, App_res.hue ); TVCAPTURESetSatU ( c, App_res.sat_u ); TVCAPTURESetSatV ( c, App_res.sat_v ); /* Set default video signal input if desired */ video_input_dev = -1; if (( strstr( App_res.def_input, "tuner" ) != NULL ) || ( strstr( App_res.def_input, "TUNER" ) != NULL )) video_input_dev = TV_DEVICE_TUNER; else if (( strstr( App_res.def_input, "csvideo" ) != NULL ) || ( strstr( App_res.def_input, "CSVIDEO" ) != NULL )) video_input_dev = TV_DEVICE_CSVIDEO; else if (( strstr( App_res.def_input, "svideo" ) != NULL ) || ( strstr( App_res.def_input, "SVIDEO" ) != NULL )) video_input_dev = TV_DEVICE_SVIDEO; else if (( strstr( App_res.def_input, "video" ) != NULL ) || ( strstr( App_res.def_input, "VIDEO" ) != NULL )) video_input_dev = TV_DEVICE_VIDEO; else if (( strstr( App_res.def_input, "dev3" ) != NULL ) || ( strstr( App_res.def_input, "DEV3" ) != NULL )) video_input_dev = TV_DEVICE_DEV3; if ( video_input_dev != -1 ) TVCAPTURESetInputDevice ( c, video_input_dev ); /* Set default audio signal input if desired */ audio_input_dev = -1; if (( strstr( App_res.def_audio_input, "tuner" ) != NULL ) || ( strstr( App_res.def_audio_input, "TUNER" ) != NULL )) audio_input_dev = TV_AUDIO_INPUT_TUNER; else if (( strstr( App_res.def_audio_input, "external" ) != NULL ) || ( strstr( App_res.def_audio_input, "EXTERNAL" ) != NULL )) audio_input_dev = TV_AUDIO_INPUT_EXTERN; else if (( strstr( App_res.def_audio_input, "internal" ) != NULL ) || ( strstr( App_res.def_audio_input, "INTERNAL" ) != NULL )) audio_input_dev = TV_AUDIO_INPUT_INTERN; else if (( strstr( App_res.def_audio_input, "auto" ) != NULL ) || ( strstr( App_res.def_audio_input, "AUTO" ) != NULL )) audio_input_dev = TV_AUDIO_INPUT_AUTO; if ( audio_input_dev != -1 ) TVCAPTURESetAudioInputDevice( c, audio_input_dev ); /* Colorbars */ TVCAPTURESetColorbars( c, App_res.colorbars ); /* Afc */ p->afc_mode = (App_res.afc_mode != FALSE); TVCAPTURESetAfc( c, p->afc_mode ); } /* Antenna/cable channel lists */ TVStationListStrToList( App_res.cable_station_list, &p->cable_station, &p->cable_num_stations ); TVStationListStrToList( App_res.ant_station_list, &p->ant_station, &p->ant_num_stations ); /* Refetch latest driver values */ if ( !TVCAPTUREQueryDriverState( c, &s ) ) { fprintf( stderr, "TVCAPTUREQueryDriverState() failed\n" ); exit(1); } /* Last minute tweaks */ if (( s.tuner_chan < TV_CHAN_MIN ) || ( s.tuner_chan > TV_CHAN_MAX )) TVCAPTURESetTunerChannel( c, 3 ); /* Default channel */ if ( App_res.def_chan[0] && !STREQ( App_res.def_chan, "0" ) ) { XEvent xev; /* Set up a dummy xevent */ memset( &xev, '\0', sizeof(xev) ); xev.xany.type = ClientMessage; xev.xclient.display = TVDISPLAY; xev.xclient.window = XtWindow( G_glob.display.video_wgt ); /* Count args */ XtCallActionProc( G_glob.display.video_wgt, "TVSetStation", &xev, &App_res.def_chan, 1 ); } /* Update menu/toolbar with currently selected options */ TVMENUSetSelectedInputDevice( s.input_dev ); TVMENUSetSelectedInputFormat( s.input_fmt ); /* Aspect lock */ TVSCREENSetAspectLock( App_res.aspect_lock ); TVMENUSetSelectedAspectLock( App_res.aspect_lock ); /* Allow user to force no direct video */ if ( App_res.disable_direct_v ) fprintf( stderr, "Direct video disabled on request\n" ); } /**@BEGINFUNC************************************************************** Prototype : static void TVCreateTopLevelShell( XtAppContext *app_context, Widget *top_level, int argc, char *argv[] ) Purpose : Determine which visual we want the app to run in, and invoke the appropriate X magic to make that happen. Programmer : 16-Oct-99 Randall Hopper Parameters : app_context - O: Xt application context top_level - O: Xt top level shell widget argc - I: main() argc argv - I: main() argv Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVCreateTopLevelShell( XtAppContext *app_context, Widget *top_level, int argc, char *argv[] ) { Display *display; int screen; XVisualInfo *fb_visual; char **xargv; /* saved argument vector */ int xargc; /* saved argument count */ /* Save command line args */ xargc = argc; xargv = (char **) XtMalloc (argc * sizeof (char *)); if ( !xargv ) TVUTILOutOfMemory(); memcpy( xargv, argv, argc * sizeof( argv[0] ) ); /* Create a dummy top level to pull resources and get displays, */ /* screens, etc. */ *top_level = XtVaAppInitialize( app_context, "Fxtv", Cmd_line_options, XtNumber(Cmd_line_options), &argc, argv, fallback_resources, NULL ); display = XtDisplay( *top_level ); screen = XScreenNumberOfScreen( XtScreen( *top_level ) ); /* Ask the screen module which visual matches up with the frame */ /* buffer visual. That's the one we want (direct video!) */ XUTILDetermineFrameBufferVisual( display, screen, &fb_visual ); /* Now, create a new application shell in that visual */ if ( fb_visual->visual != DefaultVisualOfScreen( XtScreen( *top_level ) ) ) { static Pixmap visual_pixmap = None; Window root_win = RootWindowOfScreen( XtScreen(*top_level) ); XVisualInfo vinfo; XVisualInfo *vinfo_list; int vinfo_len; Colormap colormap; int depth; fprintf( stderr, "\nWARNING: Non-default X visuals not supported yet.\n\n"); fflush( stderr ); vinfo.visualid = XVisualIDFromVisual( fb_visual->visual ); vinfo_list = XGetVisualInfo( display, VisualIDMask, &vinfo, &vinfo_len ); assert( vinfo_list && vinfo_len > 0 ); depth = vinfo_list[0].depth; XFree( vinfo_list ); colormap = XCreateColormap( display, root_win, fb_visual->visual, AllocNone ); visual_pixmap = XCreatePixmap( display, root_win, 1, 1, depth ); /* Stuff the visual into the Xt database so we don't have to */ /* set this stupid thing on every shell widget we create. */ /* (all because the X folk made visual a Shell-only resource). */ { XrmDatabase db = XtDatabase( display ); XrmValue v; v.size = sizeof( fb_visual->visual ); v.addr = (XtPointer) &fb_visual->visual; XrmPutResource( &db, "*visual", XtCVisual, &v ); /* FIXME: Probably don't need these */ #ifdef OLDSTUFF v.size = sizeof( colormap ); v.addr = (XtPointer) &colormap; XrmPutResource( &db, "*Colormap" , XtCColormap, &v ); v.size = sizeof( visual_pixmap ); v.addr = (XtPointer) &visual_pixmap; XrmPutResource( &db, "*borderPixmap" , XtCPixmap, &v ); v.size = sizeof( depth ); v.addr = (XtPointer) &depth; XrmPutResource( &db, "*depth" , XtRInt /*FIXME:XtR? */, &v ); #endif } XtDestroyWidget( *top_level ); *top_level = XtVaAppCreateShell( (char *)NULL, "Fxtv", applicationShellWidgetClass, display, XtNargv , xargv, XtNargc , xargc, XtNvisual , fb_visual->visual, XtNcolormap , colormap, XtNdepth , depth, XtNborderPixmap, visual_pixmap, NULL ); } else { XtDestroyWidget( *top_level ); *top_level = XtVaAppCreateShell( (char *)NULL, "Fxtv", applicationShellWidgetClass, display, XtNargv , xargv, XtNargc , xargc, NULL ); } } static void TVCreateWidgets( Widget top_level ) { Widget top_paned, box, paned, simple; TV_INT32 audio_vol; TV_CAPTURE *c = &G_glob.capture; TV_DISPLAY *d = &G_glob.display; top_paned = XtVaCreateManagedWidget( "topPaned", panedWidgetClass, top_level, XtNorientation, XtorientVertical, NULL ); box = TVMENUCreate( top_paned ); XtVaSetValues( box, XtNshowGrip, False, XtNallowResize, True, NULL ); box = TVTOOLSCreate( top_paned ); XtVaSetValues( box, XtNshowGrip, False, XtNallowResize, True, XtNresizeToPreferred, False, NULL ); TVAUDIOGetLineVolume( &audio_vol ); TVAUDIOSetLineVolume( audio_vol, TRUE ); paned = XtVaCreateManagedWidget( "videoPaned", panedWidgetClass, top_paned, XtNshowGrip, False, XtNorientation, XtorientHorizontal, XtNallowResize, True, XtNresizeToPreferred, True, NULL ); simple = XtVaCreateManagedWidget( "videoWin", simpleWidgetClass, paned, XtNwidth , c->width_max / 2, #ifndef FIXME_KLUDGE_FOR_DRIVER_RISC_BUG XtNheight, c->height_max / 2 + 2, #else XtNheight, c->height_max / 2, #endif XtNallowResize, True, XtNresizeToPreferred, True, NULL ); d->video_wgt = simple; /* Add a video win event handler to support station ID and channel num */ /* key-in on top of the video window. */ XtAddEventHandler( simple, TVACTION_VIDEOWIN_MASK, False, TVACTIONVideoWinEventHdlr, NULL ); /* NOTE: We wait for the videoWin widget's Window to be realized */ /* before doing anything interesting. */ XtAddEventHandler( simple, TVSCREEN_VIDEOWIN_MASK, False, TVSCREENVideoWinEventHdlr, NULL ); /* We also need a configure handler for the shell window so we */ /* know when the whole mass of our application windows are moved. */ XtAddEventHandler( top_level, TVSCREEN_SHELLWIN_MASK, False, TVSCREENShellWinEventHdlr, NULL ); } int main( int argc, char *argv[] ) { static XtActionsRec S_tv_actions[] = { { "WMDeleteWindow", (XtActionProc) TVWMDeleteWindow } }; XtAppContext app_context; Widget top_level; TV_DISPLAY *d = &G_glob.display; TV_PREFS *p = &G_glob.prefs; TV_CAPTURE *c = &G_glob.capture; /* Before we go and do anything Xish, see if this is a batch mode run */ if ( DoBatchMode(argc,argv) ) exit(0); XtSetLanguageProc(NULL, (XtLanguageProc)NULL, NULL); TVCreateTopLevelShell( &app_context, &top_level, argc, argv ); XSetErrorHandler( (int (*)(Display *dpy, XErrorEvent *err_event)) TVXErrorHdlr ); XtGetApplicationResources( top_level, (XtPointer) &App_res, Resources, XtNumber(Resources), NULL, 0 ); /* If user wants -help, give it to 'em and exit */ if ( App_res.help ) { printf( "%s\n", OPTION_HELP_STR ); exit(0); } if ( strstr( App_res.debug, "startup" ) ) G_debug |= DEBUG_STARTUP; if ( strstr( App_res.debug, "driver" ) ) G_debug |= DEBUG_DRIVER; if ( strstr( App_res.debug, "events" ) ) G_debug |= DEBUG_EVENTS; if ( strstr( App_res.debug, "subproc" ) ) G_debug |= DEBUG_SUBPROC; if ( strstr( App_res.debug, "video" ) ) G_debug |= DEBUG_VIDEO; if ( strstr( App_res.debug, "frame" ) ) G_debug |= DEBUG_FRAME; if ( strstr( App_res.debug, "remote" ) ) G_debug |= DEBUG_REMOTE; SUPRINTF(( "Fxtv v" VERS_STR "\n\n" )); XtAddEventHandler( top_level, (EventMask) 0, True, _XEditResCheckMessages, NULL ); /* Attach remote, if configured */ if ( App_res.remote_type[0] && !STREQ( App_res.remote_type, "None" ) && !STREQ( App_res.remote_type, "none" ) ) { TVREMOTETRANSParse( XtDisplay( top_level ) ); TVREMOTEOpen( app_context, App_res.remote_type, TVRemoteCallback ); } XtVaSetValues( top_level, XtNallowShellResize, True, NULL ); TVGLOBInit( XtDisplay( top_level ), XScreenNumberOfScreen( XtScreen( top_level ) ), top_level ); TVActionAddAppActions(); TVCreateWidgets( top_level ); /* Set initial defaults */ TVSetInitialCaptureDefaults( c, p ); XtRealizeWidget( top_level ); /* Handle WM_DELETE_WINDOW (Close) events */ XtAppAddActions ( app_context, S_tv_actions, XtNumber(S_tv_actions) ); XtOverrideTranslations( TVTOPLEVEL, XtParseTranslationTable( "WM_PROTOCOLS: WMDeleteWindow()" ) ); S_wm_delete_window = XInternAtom( TVDISPLAY, "WM_DELETE_WINDOW", False ); XSetWMProtocols( TVDISPLAY, XtWindow( top_level ), &S_wm_delete_window, 1); TVSCREENUpdateShellRsrcs( top_level, d->video_wgt ); TVMENUResync(); TVTOOLSResync(); /* Add signal handlers and callbacks for exit handling */ signal( SIGINT , TVIntrHdlr ); signal( SIGTERM, TVIntrHdlr ); signal( SIGTSTP, TVIntrHdlr ); /* Work proc to handle signals & driver-buf frame captures */ S_workproc_timer = XtAppAddTimeOut( app_context, S_workproc_timeout, TVWorkProcTimeoutCB, NULL ); S_workproc_timer_set = TRUE; /* For handling any driver mem frame captures we might get */ /* (NOT called for direct video captures) */ c->frame_done_cb = TVNewFrameHdlr; while (1) { XEvent ev; TV_BOOL handled; XtAppNextEvent( app_context, &ev ); handled = False; /* Any custom event handling that can't happen in an */ /* XtAddEventHandler callback goes here. */ if ( !handled ) XtDispatchEvent( &ev ); } XtAppMainLoop(app_context); }