/* * tvscreen.c * * API for determining basic capabilities of the video card on an * X server screen. * * (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 /* xf86dga.h needs this */ #ifdef HAVE_XFREE86 # include /* XF86 pre-4.0 needs for dga */ # include # include /* xf86dgastr.h needs this */ # include # include #endif #include #include #include #include #include #include "tvdefines.h" #include "tvtypes.h" #include "tvscreen.h" #include "tvmenu.h" #include "annot.h" #include "glob.h" #include "eventnames.h" #include "appear_dlg.h" #include "videolib.h" /* ******************** Local defines ************** */ #define VMODE_EXT_MIN_MAJOR 0 #define VMODE_EXT_MIN_MINOR 5 #define VERS_SAME_OR_NEWER( major, minor, major_min, minor_min ) \ (( (major) > (major_min) ) || \ (( (major) == (major_min) ) && ( (minor) >= (minor_min) )) ) #define RES_MULT_OK(d,c) \ (( (d)->geom.w == (d)->geom.w / (c)->width_res * (c)->width_res ) &&\ ( (d)->geom.h == (d)->geom.h / (c)->height_res * (c)->height_res )) #define DIRECTVID_RESTART_DELAY_MS 200 #define CURSOR_TURNOFF_DELAY_MS 1000 #define REFRESH_SLACK_PIXELS 30 static XtIntervalId S_restart_timer; static TV_BOOL S_restart_timer_set = False; #define CURSOR_SIZE 16 /* ******************** Forward declarations ************** */ /* ******************** Private variables ************** */ /* ******************** Function Definitions ************** */ /**@BEGINFUNC************************************************************** Prototype : static INT32 TVSCREENGetCurVidMode( TV_XSCREEN *s ) Purpose : Returns the index of the current video mode in the s->vm_list video mode list. Programmer : 13-Apr-97 Randall Hopper Parameters : s - I: screen def struct Returns : On success, index into s->vm_list On failure, -1 (XF86 video mode extension not supported, or not found in list) Globals : None. **@ENDFUNC*****************************************************************/ static TV_INT32 TVSCREENGetCurVidMode( TV_XSCREEN *s ) { if ( !s->vmode_ext_supported ) return -1; #ifdef HAVE_XFREE86 { int vm, dot_clock; XF86VidModeModeLine vm_info; XF86VidModeGetModeLine( s->display, s->screen, &dot_clock, &vm_info ); for ( vm = 0; vm < s->vm_list_len; vm++ ) if (( vm_info.hdisplay == (s->vm_list)[vm]->hdisplay ) && ( vm_info.hsyncstart == (s->vm_list)[vm]->hsyncstart ) && ( vm_info.hsyncend == (s->vm_list)[vm]->hsyncend ) && ( vm_info.htotal == (s->vm_list)[vm]->htotal ) && ( vm_info.vdisplay == (s->vm_list)[vm]->vdisplay ) && ( vm_info.vsyncstart == (s->vm_list)[vm]->vsyncstart ) && ( vm_info.vsyncend == (s->vm_list)[vm]->vsyncend ) && ( vm_info.vtotal == (s->vm_list)[vm]->vtotal )) break; if ( vm >= s->vm_list_len ) { fprintf( stderr, "Failed to find current video mode in server mode list\n" ); vm = -1; } return vm; } #endif } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENGetVidModeGeometry( TV_XSCREEN *s, int vmode, Dimension *width, Dimension *height ) Purpose : Obtain the dimensions of the specified video mode. Note that this may be different from the size of the desktop (i.e. the X Screen). Programmer : 16-Jan-99 Randall Hopper Parameters : s - I: screen def struct vmode - I: mode index width - O: width of video mode height - O: height of video mode Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENGetVidModeGeometry( TV_XSCREEN *s, int vmode, Dimension *width, Dimension *height ) { if ( !s->vmode_ext_supported ) { fprintf( stderr, "TVSCREENGetVidModeGeometry called without VidMode " "ext support\n" ); exit(1); } #ifdef HAVE_XFREE86 *width = (s->vm_list)[vmode]->hdisplay; *height = (s->vm_list)[vmode]->vdisplay; #endif } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENGetCurVidModeGeometry( TV_XSCREEN *s, Dimension *width, Dimension *height ) Purpose : Obtain the dimensions of the current video mode. Note that this may be different from the size of the desktop (i.e. the X Screen). Programmer : 04-Oct-97 Randall Hopper Parameters : s - I: screen def struct width - O: width of current video mode height - O: height of current video mode Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENGetCurVidModeGeometry( TV_XSCREEN *s, Dimension *width, Dimension *height ) { int vmode; if ( !s->vmode_ext_supported ) { fprintf( stderr, "TVSCREENGetCurVidModeGeometry called without VidMode " "ext support\n" ); exit(1); } #ifdef HAVE_XFREE86 vmode = TVSCREENGetCurVidMode( s ); TVSCREENGetVidModeGeometry( s, vmode, width, height ); #endif } /**@BEGINFUNC************************************************************** Prototype : static INT32 TVSCREENClosestVidMode( TV_XSCREEN *s, Dimension width, Dimension height ) Purpose : Returns the index of the closest video mode in s->vm_list to the specified resolution. NOTE: This call assumes the XFree Video Mode extension is supported. If not, it returns -1. Programmer : 13-Apr-97 Randall Hopper Parameters : s - I: screen def struct width - I: target width height - I: target height Returns : On success, index into s->vm_list On failure, -1 (XF86 video mode extension not supported, or no applicable video mode) Globals : None. **@ENDFUNC*****************************************************************/ static TV_INT32 TVSCREENClosestVidMode( TV_XSCREEN *s, Dimension width, Dimension height ) { if ( !s->vmode_ext_supported ) return -1; #ifdef HAVE_XFREE86 { INT32 i, closest = -1, delta = -1; XF86VidModeModeInfo *vm_info; for ( i = 0; i < s->vm_list_len; i++ ) { vm_info = (s->vm_list)[i]; if (( vm_info->hdisplay >= width ) && ( vm_info->vdisplay >= height )) { if (( closest < 0 ) || ( vm_info->hdisplay - width < delta )) { closest = i; delta = vm_info->hdisplay - width; } } } return closest; } #endif } /**@BEGINFUNC************************************************************** Prototype : static Boolean TVSCREENGetVisualDirectCap( TV_XSCREEN *s, XVisualInfo *v, XVisualInfo *def_v ) Purpose : Determine whether or not direct video is supported for the specified visual. Programmer : 02-Mar-97 Randall Hopper Parameters : s - I: screen def with VideoLL fields filled in v - I: visual to analyze def_v - I: default visual of screen Returns : T = direct video supported; F = not supported Globals : None. **@ENDFUNC*****************************************************************/ static Boolean TVSCREENGetVisualDirectCap( TV_XSCREEN *s, XVisualInfo *v, XVisualInfo *def_v ) { TV_CAPTURE *c = &G_glob.capture; TV_PIXEL_GEOM pix_geom; TV_INT32 idx; TV_BOOL swap_b, swap_s; /* Direct transfer is an option if: */ /* 1) linearly mapped frame buffer, */ /* 2) visual matches screen's default visual, & */ /* 3) capture card supports direct transfer for this visual. */ /* FIXME: Consider beefing up #3 for general 16bpp and */ /* pseudocolor at some point. */ if (( s->base_addr != 0 ) && ( s->bank_size >= s->ram_size ) && ( v->visualid == def_v->visualid ) && ( v->class == TrueColor )) { memset( &pix_geom, '\0', sizeof( pix_geom ) ); pix_geom.type = TV_PIXELTYPE_RGB; XUTILGetVisualBpp( TVDISPLAY, v, NULL, &pix_geom.Bpp ); pix_geom.mask[0] = v->red_mask; pix_geom.mask[1] = v->green_mask; pix_geom.mask[2] = v->blue_mask; XUTILGetVisualSwaps( TVDISPLAY, v, &swap_b, &swap_s ); pix_geom.swap_bytes = swap_b; pix_geom.swap_shorts = swap_s; TVCAPTUREGetPixFmtByPixGeom( c, &pix_geom, &idx ); if ( idx < 0 ) return False; else return True; } else return False; } /**@BEGINFUNC************************************************************** Prototype : static void TVSCREENAtExit() Purpose : Deactivate zoom on exit (back to right vid mode). Programmer : 04-Nov-97 Randall Hopper Parameters : None. Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVSCREENAtExit() { if ( !G_in_x_error ) TVSCREENSetZoomState( FALSE, FALSE ); } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENInit( TV_XSCREEN *s, Display *display, int screen ) Purpose : Initialize the screen structure with the capabilities of the video card, server, and display driver. Programmer : 02-Mar-97 Randall Hopper Parameters : s - I/O: Screen definition structure display - I: X display screen - I: X screen number Returns : None. Globals : G_tvscreen **@ENDFUNC*****************************************************************/ void TVSCREENInit( TV_XSCREEN *s, Display *display, int screen ) { int shm_majv, shm_minv, dga_majv, dga_minv; Bool shm_pixmaps; XVisualInfo vinfo_pref; TV_INT32 i, rank, best_i, best_rank; TV_TRANSFER_MODE modes; int vmode_ev_base, vmode_err_base, dga_flags_sup; Bool ret; TV_BOOL dga_avail, vmode_avail; char **ext_list; int ext_count, ext; #if defined(HAVE_XFREE86) && defined(TESTING) XF86VidModeMonitor monitor; #endif s->display = display; s->screen = screen; /* Until we get an enhanced DGA 2.0 to tell us, assume the frame */ /* buffer visual is the one with the biggest and badest pixel fmt. */ XUTILDetermineFrameBufferVisual( s->display, s->screen, &s->fb_visual ); /* Dump some info about the X server itself */ SUPRINTF(( "\nXSERVER: '%s' v%d, Protocol Verson %d.%d\n", ServerVendor(display), VendorRelease(display), ProtocolVersion(display), ProtocolRevision(display) )); SUPRINTF(( " Screen Res = %dx%d, DefDepth = %d; " "NumScreens = %d\n", DisplayWidth ( display, screen ), DisplayHeight( display, screen ), DefaultDepth ( display, screen ), ScreenCount ( display ) )); SUPRINTF(( " Bitmap Unit/BitOrder/Pad = %d/%s/%d, " "Image ByteOrder = %s\n", BitmapUnit ( display ), BitmapBitOrder( display ) == LSBFirst ? "LSBFirst" : "MSBFirst", BitmapPad ( display ), ImageByteOrder( display ) == LSBFirst ? "LSBFirst" : "MSBFirst" )); SUPRINTF(( "\n" )); /* Get list of supported server extensions */ ext_list = XListExtensions( TVDISPLAY, &ext_count ); /* Ok, now see what card capabilities we've got to play with here */ modes = TV_TRANSFER_STD_IMAGE; /* Shared memory extension (TRANSFER_SHMEM_{IMAGE,PIXMAP}) */ if ( !XUTILXServerIsLocal( TVDISPLAY ) ) SUPRINTF(( "Shm Extension not available...X Server isn't local.\n" )); else if ( XShmQueryVersion( s->display, &shm_majv, &shm_minv, &shm_pixmaps ) == True ) { modes |= TV_TRANSFER_SHMEM_IMAGE; if ( shm_pixmaps ) modes |= TV_TRANSFER_SHMEM_PIXMAP; } /* Linear frame buf? (TRANSFER_DIRECT) */ dga_avail = FALSE; #ifdef HAVE_XFREE86 if ( App_res.disable_direct_v ) SUPRINTF(( "Will not init DGA since -disableDirectV was given.\n" )); else if ( !XUTILXServerIsLocal( TVDISPLAY ) ) SUPRINTF(( "XF86DGA not available...X Server isn't local.\n" )); else { for ( ext = 0; ext < ext_count; ext++ ) if ( strcmp( ext_list[ext], XF86DGANAME ) ) break; if ( ext < ext_count ) dga_avail = TRUE; else SUPRINTF(( "XF86DGA extension not found\n" )); } if ( dga_avail ) { ret = XF86DGAQueryVersion( s->display, &dga_majv, &dga_minv ); if ( ret == True ) ret = XF86DGAQueryDirectVideo( s->display, s->screen, &dga_flags_sup ); if ( ret == False ) { SUPRINTF(( "XF86DGA{QueryVersion,QueryDirectVideo}() failed\n" )); dga_avail = FALSE; } else if ( !( dga_flags_sup & XF86DGADirectPresent ) ) { SUPRINTF(( "XF86DGAQueryDirectVideo() reports DGA not avail\n" )); dga_avail = FALSE; } else { SUPRINTF(( "XF86DGAQueryVersion() succeeded - vers = %d.%.2d\n", dga_majv, dga_minv )); XF86DGAGetVideoLL( s->display, s->screen, (int *) &s->base_addr, (int *) &s->pitch, (int *) &s->bank_size, (int *) &s->ram_size ); s->ram_size *= 1024; SUPRINTF(( " BaseAddr = 0x%lx, Pitch = %ld, " "BankSize/RamSize = %ld/%ld\n", s->base_addr, s->pitch, s->bank_size, s->ram_size )); } } #endif if ( !dga_avail ) { s->base_addr = 0; s->pitch = 0; s->bank_size = 0; s->ram_size = 0; } s->dga_ext_supported = dga_avail; /* Get a list of all visuals on our screen */ vinfo_pref.screen = s->screen; s->visual = XGetVisualInfo( s->display, VisualScreenMask, &vinfo_pref, (int *) &s->num_visuals ); if ( s->num_visuals == 0 ) { fprintf( stderr, "XGetVisualInfo() says no visuals available!\n" ); exit(1); } if ( (s->visual_modes = calloc( s->num_visuals, sizeof(TV_TRANSFER_MODE) )) == NULL ) TVUTILOutOfMemory(); /* Default to a visual that supports direct video, if possible (the */ /* deeper the better). If not available, take any TrueColor */ /* visual. If none, then we'll just hafta make due with */ /* Pseudocolor/GrayScale. */ /* NOTE: StaticColor, StaticGray, and DirectColor not supported now. */ /* Also update visual "valid modes" mask while we're at it. */ best_i = -1; best_rank = -1; SUPRINTF(( "\nRating Available Visuals:\n" " Rating Class bpp Bpp R,G,B Masks " "Swap DirectVid\n" " ------ ----------- --- --- ---------------------------- " "---- ---------\n" )); for ( i = 0; i < s->num_visuals; i++ ) { XVisualInfo *v = &s->visual[i]; TV_INT32 Bpp_pixmap, Bpp_fbuffer; TV_BOOL direct_v; XUTILGetVisualBpp( TVDISPLAY, v, &Bpp_pixmap, &Bpp_fbuffer ); s->visual_modes[i] = modes; if ( TVSCREENGetVisualDirectCap( s, v, s->fb_visual ) ) s->visual_modes[i] |= TV_TRANSFER_DIRECT; direct_v = ( s->visual_modes[i] & TV_TRANSFER_DIRECT ) != 0; /* Rank this visual */ switch ( v->class ) { case TrueColor : switch ( v->depth ) { case 32 : case 24 : if ( direct_v ) rank = 7; else rank = 4; break; case 15 : rank = 5; break; case 16 : if ( direct_v ) rank = 5; else rank = 4; break; default : /* FIXME: Ignore 8-bit true-color for now */ rank = ( v->depth <= 8 ) ? 0 : 3; break; } break; case PseudoColor : rank = 2; break; case GrayScale : rank = 1; break; case StaticGray : case StaticColor : case DirectColor : default : rank = 0; break; } /* Is this the best so far? */ if ( rank > best_rank ) best_i = i, best_rank = rank; /* FIXME: Handle byte swapping */ SUPRINTF(( " %3ld %-11s %2d %1ld,%1ld %.8lx, %.8lx, %.8lx " "-- %-3s\n", rank, visual_classes[ v->class ], v->depth, Bpp_pixmap, Bpp_fbuffer, v->red_mask, v->green_mask, v->blue_mask, ( direct_v ? "Yes" : "No" ) )); } if ( best_rank <= 0 ) { fprintf( stderr, "No supported visual found\n" ); exit(1); } s->active_visual = best_i; SUPRINTF(( "Chosen Visual is %d-bpp %s\n\n", s->visual[ s->active_visual ].depth, visual_classes[ s->visual[ s->active_visual ].class ] )); /* Query for VidMode Extension */ s->vmode_ext_supported = False; vmode_avail = FALSE; #ifdef HAVE_XFREE86 if ( !XUTILXServerIsLocal( TVDISPLAY ) ) SUPRINTF(( "XF86VidMode probably isn't enabled, if even available..." "X Server isn't local.\n" )); else { for ( ext = 0; ext < ext_count; ext++ ) if ( strcmp( ext_list[ext], XF86VIDMODENAME ) ) break; if ( ext < ext_count ) vmode_avail = TRUE; else SUPRINTF(( "XF86VidMode extension not found\n" )); } if ( vmode_avail ) { if ( !XF86VidModeQueryVersion( s->display, &s->vmode_majv, &s->vmode_minv ) ) SUPRINTF(( "XF86VidModeQueryVersion() failed\n" )); else if ( !VERS_SAME_OR_NEWER( s->vmode_majv, s->vmode_minv, VMODE_EXT_MIN_MAJOR, VMODE_EXT_MIN_MINOR ) ) SUPRINTF(( "XF86VidMode extension to old. " "Its version %d.%.2d; we need >= %d.%.2d\n", s->vmode_majv, s->vmode_minv, VMODE_EXT_MIN_MAJOR, VMODE_EXT_MIN_MINOR )); else if ( !XF86VidModeQueryExtension( s->display, &vmode_ev_base, &vmode_err_base ) ) SUPRINTF(( "XF86VidModeQueryExtension() failed\n" )); else { s->vmode_ext_supported = True; SUPRINTF(( "XF86VidModeQueryVersion() succeeded - version = %d.%.2d\n", s->vmode_majv, s->vmode_minv )); # ifdef TESTING /* Dump monitor info */ if ( XF86VidModeGetMonitor( s->display, s->screen, &monitor ) ) { SUPRINTF(( "\nMonitor: %s %s\n", monitor.vendor, monitor.model )); } # endif /* Query all available video modes */ XF86VidModeGetAllModeLines( s->display, s->screen, &s->vm_list_len, &s->vm_list ); s->vm_startup = TVSCREENGetCurVidMode( s ); if ( s->vm_startup < 0 ) s->vmode_ext_supported = False; } } #endif /* Hook in handler to deactivate zoom on exit */ atexit( TVSCREENAtExit ); /* FIXME: Other things it'd be good if we could print out when */ /* "startup" debugs are enabled (SUPRINTF): */ /* - Graphics card */ /* - X Server (SVGA,S3V,etc.) */ /* - "startx -- -probeonly" output */ /* - "appres Fxtv" output */ } /**@BEGINFUNC************************************************************** Prototype : static TV_TRANSFER_MODE TVSCREENDetermineTransferMode( TV_DISPLAY *d, TV_CAPTURE_MODE cap_mode ) Purpose : Determines the appropriate transfer mode to use for starting a capture using the specified capture mode. This rtn takes into account the detected capabilities of the display system and capture driver, as well as the current state of the display system (e.g. whether the video window is unoccluded or not). Programmer : 08-Oct-97 Randall Hopper Parameters : d - I: display def struct cap_mode - I: desired capture mode (SINGLE or CONTINUOUS) Returns : transfer_mode to use for capture Globals : None. **@ENDFUNC*****************************************************************/ static TV_TRANSFER_MODE TVSCREENDetermineTransferMode( TV_DISPLAY *d, TV_CAPTURE_MODE cap_mode ) { static TV_BOOL S_done_no_dv_msg = FALSE; TV_XSCREEN *x = &G_glob.x; TV_TRANSFER_MODE transfer_mode = 0; TV_BOOL dirvid_sup, shimg_sup; dirvid_sup = x->visual_modes[ x->active_visual ] & TV_TRANSFER_DIRECT; shimg_sup = x->visual_modes[ x->active_visual ] & TV_TRANSFER_SHMEM_IMAGE; /* For SINGLE capture mode... */ if ( cap_mode == TV_CAPTURE_SINGLE ) { if ( shimg_sup ) transfer_mode = TV_TRANSFER_SHMEM_IMAGE; else transfer_mode = TV_TRANSFER_STD_IMAGE; } /* For CONTINUOUS capture mode... */ else { TV_BOOL set = FALSE; if ( !App_res.disable_direct_v && ( d->win_visibility == VisibilityUnobscured ) ) { if ( dirvid_sup ) { transfer_mode = TV_TRANSFER_DIRECT; set = TRUE; } #ifdef HAVE_XFREE86 else if ( !S_done_no_dv_msg ) { fprintf( stderr, "Direct Video not supported by visual...using XImages\n"); S_done_no_dv_msg = TRUE; } #endif } if ( !set ) if ( shimg_sup ) transfer_mode = TV_TRANSFER_SHMEM_IMAGE; else transfer_mode = TV_TRANSFER_STD_IMAGE; } assert( transfer_mode ); return transfer_mode; } /**@BEGINFUNC************************************************************** Prototype : static void TVSCREENGetWinRootGeometry( Window win, TV_GEOM in, TV_GEOM *out ) Purpose : Translates window coordinates to the root window. Programmer : 04-Mar-97 Randall Hopper Parameters : win - I: window in - I: coords rel to win out - O: coords rel to root window Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVSCREENWinGeometryToRoot( Window win, TV_GEOM in, TV_GEOM *out ) { Window child_win; int root_x, root_y; if ( !XTranslateCoordinates( TVDISPLAY, win, RootWindow( TVDISPLAY, TVSCREEN ), in.x, in.y, &root_x, &root_y, &child_win ) ) { fprintf( stderr, "XTranslateCoordinates() failed\n" ); exit(1); } out->x = root_x; out->y = root_y; out->w = in.w; out->h = in.h; } /**@BEGINFUNC************************************************************** Prototype : static void TVSCREENGetWinGeometry( Window win, TV_GEOM *g, TV_BOOL reget ) Purpose : Return the geometry of the specified window. Programmer : 04-Mar-97 Randall Hopper Parameters : win - I: window g - O: window coords Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVSCREENGetWinGeometry( Window win, TV_GEOM *g ) { XWindowAttributes xwa; XGetWindowAttributes( TVDISPLAY, win, &xwa ); g->x = 0; g->y = 0; g->w = xwa.width; g->h = xwa.height; } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENUpdateWinGeometry() Purpose : Called whenever the location of our video window (relative to the root window) could have changed. We must keep this up-to-date when running in direct video since this information is used to compute the driver capture parameters for frame transfer into video memory. Programmer : 06-Oct-97 Randall Hopper Parameters : None. Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENUpdateWinGeometry() { TV_DISPLAY *d = &G_glob.display; /* First, update the video win geom relative to the root window */ TVSCREENGetWinGeometry( d->win, &d->geom ); TVSCREENWinGeometryToRoot( d->win, d->geom, &d->geom ); /* And also update the refresh region relative to the root window. */ /* This region includes all the widgets on the same top-level shell */ /* as the video window to clean up any stray video droppings while */ /* moving the video window in direct video mode. */ if ( !XtIsRealized( d->shell_wgt ) ) memcpy( &d->refresh_geom, &d->geom, sizeof( d->refresh_geom ) ); else { Window win = XtWindow( d->shell_wgt ); TVSCREENGetWinGeometry( win, &d->refresh_geom ); TVSCREENWinGeometryToRoot( win, d->refresh_geom, &d->refresh_geom ); /* Add a little slack */ d->refresh_geom.x -= REFRESH_SLACK_PIXELS; d->refresh_geom.y -= REFRESH_SLACK_PIXELS; d->refresh_geom.w += 2*REFRESH_SLACK_PIXELS; d->refresh_geom.h += 2*REFRESH_SLACK_PIXELS; } } /**@BEGINFUNC************************************************************** Prototype : static void TVSCREENGetCapturePixGeom( TV_XSCREEN *s, TV_BOOL direct_vid, TV_PIXEL_GEOM *pix_geom ) Purpose : Populate the passed pix_geom structure with info from the active visual. Programmer : 30-Mar-97 Randall Hopper Parameters : d - I: display def struct pix_geom - O: pixel geometry for the visual Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVSCREENGetCapturePixGeom( TV_XSCREEN *s, TV_BOOL direct_vid, TV_PIXEL_GEOM *pix_geom ) { TV_CAPTURE *c = &G_glob.capture; TV_BOOL swap_b, swap_s; TV_INT32 idx, Bpp_pixmap, Bpp_fbuffer; XVisualInfo *v = &s->visual[ s->active_visual ]; pix_geom->type = TV_PIXELTYPE_RGB; /* Our first pick is the pixel geometry of the visual */ if ( v->class == TrueColor ) { XUTILGetVisualBpp( TVDISPLAY, v, &Bpp_pixmap, &Bpp_fbuffer ); pix_geom->Bpp = (direct_vid ? Bpp_fbuffer : Bpp_pixmap ); pix_geom->mask[0] = v->red_mask; pix_geom->mask[1] = v->green_mask; pix_geom->mask[2] = v->blue_mask; if ( direct_vid ) XUTILGetVisualSwaps( TVDISPLAY, v, &swap_b, &swap_s ); else { swap_b = ( ImageByteOrder( TVDISPLAY ) == LSBFirst ); swap_s = ( ImageByteOrder( TVDISPLAY ) == LSBFirst ) && ( pix_geom->Bpp >= 4 ); } pix_geom->swap_bytes = swap_b; pix_geom->swap_shorts = swap_s; } else { /* For e.g. 8bpp PseudoColor */ pix_geom->Bpp = 2; pix_geom->mask[0] = 0x7C00; pix_geom->mask[1] = 0x03E0; pix_geom->mask[2] = 0x001F; pix_geom->swap_bytes = 1; pix_geom->swap_shorts = 0; } TVCAPTUREGetPixFmtByPixGeom( c, pix_geom, &idx ); if ( idx >= 0 ) TVCAPTUREGetNthPixFmt( c, idx, pix_geom ); /* If its not supported by the capture hardware, just find something */ /* reasonable and we'll convert it on the CPU. */ else { TV_PIXEL_GEOM pg, best_pg; TV_UINT32 i, best_i = -1, num_pg; TV_BOOL take_it; TVCAPTUREGetNumPixFmts( c, &num_pg ); for ( i = 0; i < num_pg; i++ ) { TVCAPTUREGetNthPixFmt( c, i, &pg ); take_it = False; /* Never take a non-RGB format (e.g. YUV) */ if ( pg.type != TV_PIXELTYPE_RGB ) take_it = False; /* Grab it if we don't have a pick yet */ else if ( best_i < 0 ) take_it = True; /* Prefer 2Bpp byte swapped over the rest (since its the */ /* thing NewFrameHdlr currently byte swaps well). */ else if (( best_pg.Bpp != 2 ) || !best_pg.swap_bytes ) if (( pg.Bpp == 2 ) && pg.swap_bytes ) take_it = True; else if ( pg.Bpp == 2 ) take_it = True; if ( take_it ) { best_i = i; best_pg = pg; } } *pix_geom = best_pg; } } /**@BEGINFUNC************************************************************** Prototype : static void TVSCREENUpdateRootRegion( TV_GEOM g, XtAppContext app_context ) Purpose : Forces a redraw of a particular region of the root window. Programmer : 04-Mar-97 Randall Hopper Parameters : g - I: the region to update (rel to root win) app_context - I: the application context Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVSCREENUpdateRootRegion( TV_GEOM g, XtAppContext app_context ) { static Window refresh_win = None; #ifdef WE_DONT_NEED_TO_WAIT_FOR_MAP XEvent ev; #endif XSetWindowAttributes xswa; /* There's doubtless a better way to do this; XSendEventing an expose */ /* to the root window didn't work. */ /* FIXME: Don't create new win each time; cache and just reconfigure */ /* for each use. */ xswa.override_redirect = True; if ( refresh_win == None ) { refresh_win = XCreateWindow( TVDISPLAY, DefaultRootWindow(TVDISPLAY), g.x, g.y, g.w, g.h, 0, CopyFromParent, InputOutput, CopyFromParent, CWOverrideRedirect, &xswa ); XSelectInput( TVDISPLAY, refresh_win, StructureNotifyMask ); } else XMoveResizeWindow( TVDISPLAY, refresh_win, g.x, g.y, g.w, g.h ); XMapWindow ( TVDISPLAY, refresh_win ); XRaiseWindow( TVDISPLAY, refresh_win ); #ifdef WE_DONT_NEED_TO_WAIT_FOR_MAP /* Wait for map. */ while(1) { XtAppNextEvent( app_context, &ev ); /* XNextEvent( TVDISPLAY, &ev ); */ if ( ev.type == MapNotify && ev.xmap.event == refresh_win ) break; XtDispatchEvent( &ev ); } #endif XSync( TVDISPLAY, False ); XUnmapWindow( TVDISPLAY, refresh_win ); XSync( TVDISPLAY, False ); } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENRedrawVideoWin() Purpose : If capture is stopped and the saved XImage looks reasonable, blast it back up in the video window. Programmer : 08-Mar-97 Randall Hopper Parameters : None. Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENRedrawVideoWin() { TV_DISPLAY *d = &G_glob.display; TV_CAPTURE *c = &G_glob.capture; TV_XSCREEN *x = &G_glob.x; if ( d->win == None ) return; /* Draw the latest Image in the video window */ if ( d->enabled && d->ximage_use_for_expose && ( d->ximage.ximg != NULL ) && ( d->ximage.geom.w == d->geom.w ) && ( d->ximage.geom.h == d->geom.h ) && ( d->ximage.visual == &x->visual[ x->active_visual ] ) ) { if ( d->ximage.is_shm ) XShmPutImage( TVDISPLAY, d->win, d->gc, d->ximage.ximg, 0, 0, 0, 0, d->ximage.geom.w, d->ximage.geom.h, False ); else XPutImage ( TVDISPLAY, d->win, d->gc, d->ximage.ximg, 0, 0, 0, 0, d->ximage.geom.w, d->ximage.geom.h ); } else XClearWindow( TVDISPLAY, d->win ); /* Update any annotation that's active (if continuous capture is on). */ /* Note that this only affects ximages mode. */ if ( c->cap_mode == TV_CAPTURE_CONTINUOUS ) TVANNOTUpdate( &d->annot ); XFlush( TVDISPLAY ); /*XSync( TVDISPLAY, 0 );*/ } /* TVSCREENGetTransparentCursor - Build a totally transparent cursor */ static Cursor TVSCREENGetTransparentCursor() { static Cursor S_cursor = None; TV_DISPLAY *d = &G_glob.display; if (( d->video_wgt == NULL ) || ( d->win == None )) { fprintf( stderr, "TVSCREENGetBlankCursor: Bad state\n" ); exit(1); } if ( S_cursor == None ) { Pixmap pix, pix_mask; XColor white, black; GC gc; XGCValues gcv; TV_INT32 size = CURSOR_SIZE; black.pixel = BlackPixel( TVDISPLAY, TVSCREEN ); white.pixel = WhitePixel( TVDISPLAY, TVSCREEN ); black.red = black.green = black.blue = 0x0000; white.red = white.green = white.blue = 0xFFFF; pix = XCreatePixmap( TVDISPLAY, d->win, size, size, 1 ); pix_mask = XCreatePixmap( TVDISPLAY, d->win, size, size, 1 ); gcv.function = GXclear; gcv.foreground = 1; gcv.background = 0; gc = XCreateGC( TVDISPLAY, pix, GCFunction | GCForeground | GCBackground, &gcv ); XFillRectangle( TVDISPLAY, pix , gc, 0, 0, size, size ); XFillRectangle( TVDISPLAY, pix_mask, gc, 0, 0, size, size ); XFreeGC( TVDISPLAY, gc ); S_cursor = XCreatePixmapCursor( TVDISPLAY, pix, pix_mask, &white, &black, size/2, size/2 ); } return S_cursor; } /* TVSCREENSetVideoWinCursorEnabled - Turn the cursor on the video */ /* window on or off. */ static void TVSCREENSetVideoWinCursorEnabled( TV_BOOL enable ) { TV_DISPLAY *d = &G_glob.display; if (( d->video_wgt == NULL ) || ( d->win == None )) return; if ( enable ) XUndefineCursor( TVDISPLAY, d->win ); else XDefineCursor( TVDISPLAY, d->win, TVSCREENGetTransparentCursor() ); } /* TVSCREENCursorTurnoffTimeoutCB - When this timeout expires, turn off */ /* the cursor over the video window. */ static void TVSCREENCursorTurnoffTimeoutCB( XtPointer cl_data, XtIntervalId *timer ) { TV_DISPLAY *d = &G_glob.display; d->cursor_timer_set = FALSE; if ( !d->cursor_dozeoff_enabled ) return; TVSCREENSetVideoWinCursorEnabled( FALSE ); } /* TVSCREENWakeupCursor - If the cursor is off, turns it back on, and */ /* restarts the "doze-off" timer. */ static void TVSCREENWakeupCursor() { TV_DISPLAY *d = &G_glob.display; if ( !d->cursor_dozeoff_enabled ) return; if ( !d->cursor_timer_set ) TVSCREENSetVideoWinCursorEnabled( TRUE ); else XtRemoveTimeOut( d->cursor_timer ); d->cursor_timer = XtAppAddTimeOut( TVAPPCTX, CURSOR_TURNOFF_DELAY_MS, TVSCREENCursorTurnoffTimeoutCB, NULL ); d->cursor_timer_set = TRUE; } /* TVSCREENCapConfigure - convience wrapper for configuring the capture */ /* subsystem for on-screen use. */ static TV_BOOL TVSCREENCapConfigure( TV_CAPTURE_MODE cap_mode, char **fail_reason ) { TV_CAPTURE *c = &G_glob.capture; TV_DISPLAY *d = &G_glob.display; TV_BOOL ret, do_frame_cb; TV_INT32 fps = App_res.display_fps; TV_TRANSFER_MODE transfer_mode; transfer_mode = TVSCREENDetermineTransferMode( d, cap_mode ); do_frame_cb = (transfer_mode != TV_TRANSFER_DIRECT); TVCAPTURESetFrameDoneCBEnabled( c, do_frame_cb ); /* FIXME: FPS doesnt work consistently in the driver yet. With the */ /* below hack, we'll capture full-speed to the driver buffer, though */ /* we're only sampling one every 1/fps sec. */ #ifdef DRIVER_FPS_BUG TVCAPTURESetFPS ( c, c->fps_max ); #else TVCAPTURESetFPS ( c, fps ); #endif TVSetWorkProcTimeout( do_frame_cb ? (1000/fps) : -1 ); TVCAPTURESetCaptureMode ( c, d->cap_mode ); TVCAPTURESetTransferMode( c, transfer_mode ); TVCAPTURESetRegionGeom ( c, &d->geom ); TVCAPTURESetPixelGeom ( c, &d->pix_geom ); ret = TVCAPTUREConfigure( c, fail_reason ); return ret; } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENResetStartVideoTimer( void ) Purpose : Clear any pending start request timer. No-op if none set. Programmer : 06-Oct-97 Randall Hopper Parameters : None. Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENResetStartVideoTimer( void ) { if ( S_restart_timer_set ) { XtRemoveTimeOut( S_restart_timer ); S_restart_timer_set = False; } } /**@BEGINFUNC************************************************************** Prototype : static void TVSCREENRestartVideoTimeoutCB( XtPointer cl_data, XtIntervalId *timer ) Purpose : Xt timer callback to restart video after a specific number of milliseconds have passed. See comment below in TVSCREENQueueStartRequest header for illumination as to "why" this is even here. Programmer : 05-Mar-97 Randall Hopper Parameters : cl_data - I: not used timer - I: not used Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVSCREENRestartVideoTimeoutCB( XtPointer cl_data, XtIntervalId *timer ) { TV_DISPLAY *d = &G_glob.display; TV_CAPTURE *c = &G_glob.capture; TV_XSCREEN *x = &G_glob.x; char *cfg_fail_msg, *xfer_str; S_restart_timer_set = False; TVSCREENUpdateWinGeometry(); TVSCREENGetCapturePixGeom ( x, (c->xfer_mode == TV_TRANSFER_DIRECT), &d->pix_geom ); /* Reconfigure. Note this may fail if new window geo */ /* won't jive with hardware or driver capabilities. */ if ( !TVSCREENCapConfigure( d->cap_mode, &cfg_fail_msg ) ) { fprintf( stderr, "TVSCREENCapConfigure() failed: %s\n", cfg_fail_msg ); XClearWindow( TVDISPLAY, d->win ); XBell( TVDISPLAY, 25 ); return; } /* When starting direct video, invalidate use of ximage for exposes. */ /* Produces annoying flash when restacking window (redrawing region) */ if ( c->xfer_mode == TV_TRANSFER_DIRECT ) d->ximage_use_for_expose = FALSE; /* Ready to go. Set annotation update mode. */ TVANNOTSetAutoUpdateMode( &d->annot, (( d->cap_mode == TV_CAPTURE_CONTINUOUS ) && ( c->xfer_mode == TV_TRANSFER_DIRECT )) ); /* Set cursor turn-off timer */ if ( d->cap_mode == TV_CAPTURE_CONTINUOUS ) { d->cursor_dozeoff_enabled = TRUE; TVSCREENWakeupCursor(); } /* Tell user what the transfer mode is going to be */ xfer_str = ""; switch ( c->xfer_mode ) { case TV_TRANSFER_STD_IMAGE : xfer_str = "Images" ; break; case TV_TRANSFER_SHMEM_IMAGE : xfer_str = "Shm Images" ; break; case TV_TRANSFER_DIRECT : xfer_str = "Direct Video" ; break; case TV_TRANSFER_SHMEM_PIXMAP: xfer_str = "Shm Pixmaps" ; break; } DRVPRINTF(( "TRANSFER MODE: %s\n", xfer_str )); /* And fire it up */ TVCAPTUREStart( c ); } /**@BEGINFUNC************************************************************** Prototype : static void TVSCREENQueueStartRequest( void ) Purpose : This restart delay is a big hack for continuous direct video mode. Basically we need to allow all the visibility events to flush from the last CAPTUREStop before we can initiate another CAPTUREStart or we'll just be stopping again when we get a Partial- or Total-Obscured notif (which will itself cause more visibility notifications and we'll just end up in a start/stop/start/stop... loop that accomplishes nothing). FIXME: If there's a better way, change to doing it. Programmer : 05-Mar-97 Randall Hopper Parameters : None. Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVSCREENQueueStartRequest( void ) { TVSCREENResetStartVideoTimer(); S_restart_timer = XtAppAddTimeOut( TVAPPCTX, DIRECTVID_RESTART_DELAY_MS, TVSCREENRestartVideoTimeoutCB, NULL ); S_restart_timer_set = True; } /**@BEGINFUNC************************************************************** Prototype : TV_BOOL TVSCREENVideoStarted( void ) Purpose : Returns TRUE if video capture has been initiated via TVSCREENStartVideo() and is pending or active. Programmer : 06-Oct-97 Randall Hopper Parameters : None. Returns : TRUE - Capture is pending or active Globals : None. **@ENDFUNC*****************************************************************/ TV_BOOL TVSCREENVideoStarted( void ) { TV_CAPTURE *c = &G_glob.capture; return ( S_restart_timer_set || c->contin_on ); } /**@BEGINFUNC************************************************************** Prototype : TV_BOOL TVSCREENVideoCapContin( void ) Purpose : After a StartVideo request has been made, returns TRUE if the configured capture mode for the transfer was CONTINUOUS. Programmer : 06-Oct-97 Randall Hopper Parameters : None. Returns : T if cap mode configured was CONTINUOUS Globals : None. **@ENDFUNC*****************************************************************/ TV_BOOL TVSCREENVideoReqCaptureContin( void ) { TV_CAPTURE *c = &G_glob.capture; /* Calling this is an error if a capture isn't pending or active */ if ( !TVSCREENVideoStarted() ) { fprintf( stderr, "TVSCREENVideoReqCaptureContin called with no " "capture pending or active\n" ); exit(1); } return (c->cap_mode == TV_CAPTURE_CONTINUOUS); } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENStartVideo( void ) void TVSCREENStopVideo( TV_BOOL suppress_redraw ) Purpose : Convenience routines used to start and stop capture when the video is being displayed on-screen. Abstracts some of the nastiness that has to happen for direct video. Note: suppress_refresh can be used to suppress the forced expose/redraw of the video area when in direct video mode. This is useful when stopping video briefly to capture a frame to XImage or to briefly stop to reconfigure driver parameters. Otherwise, the exposure event causes a blit of the last-saved XImage up into the window which is distracting. Programmer : 16-Mar-97 Randall Hopper Parameters : suppress_redraw - I: in direct video, suppress forced redraw of video window Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENStartVideo( void ) { TV_CAPTURE *c = &G_glob.capture; /* This is an error if a capture is pending or active. */ if ( S_restart_timer_set ) { fprintf( stderr, "TVSCREENStartVideo called with capture " "already pending...ignored\n" ); return; } if ( c->contin_on ) { fprintf( stderr, "TVSCREENStartVideo called with continous capture " "already running...ignored\n" ); return; } /* All clear. Queue a start request. */ TVSCREENQueueStartRequest(); } void TVSCREENStopVideo( TV_BOOL suppress_redraw ) { TV_DISPLAY *d = &G_glob.display; TV_CAPTURE *c = &G_glob.capture; TV_GEOM new_refresh_geom; Window shell_win; /* This is an error if no capture is pending or active. */ if ( S_restart_timer_set ) TVSCREENResetStartVideoTimer(); else if ( c->contin_on ) { /* Make sure cursor is back on when over video win */ if ( d->cursor_timer_set ) { XtRemoveTimeOut( d->cursor_timer ); d->cursor_timer_set = FALSE; } d->cursor_dozeoff_enabled = FALSE; TVSCREENSetVideoWinCursorEnabled( TRUE ); TVANNOTSetAutoUpdateMode( &d->annot, FALSE ); TVCAPTUREStop( c ); if ( !suppress_redraw && (c->xfer_mode == TV_TRANSFER_DIRECT )) { /* Refresh where we were... */ TVSCREENUpdateRootRegion( d->refresh_geom, TVAPPCTX ); /* and where we are now (since the widgets on our video shell */ /* can be stomped if, during a move, the user moved them */ /* the video stream; the server then typically just BITBLTs */ /* this trash around during the move, so we force a redraw) */ /* when it sees fit to finally tell us about the Configure. */ shell_win = XtWindow( d->shell_wgt ); TVSCREENGetWinGeometry( shell_win, &new_refresh_geom ); TVSCREENWinGeometryToRoot( shell_win, new_refresh_geom, &new_refresh_geom ); new_refresh_geom.x -= REFRESH_SLACK_PIXELS; new_refresh_geom.y -= REFRESH_SLACK_PIXELS; new_refresh_geom.w += 2*REFRESH_SLACK_PIXELS; new_refresh_geom.h += 2*REFRESH_SLACK_PIXELS; TVSCREENUpdateRootRegion( new_refresh_geom, TVAPPCTX ); } } else { fprintf( stderr, "TVSCREENStopVideo called with no capture " "pending or active...ignored\n" ); } } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENVideoWinEventHdlr( Widget wgt, XtPointer cl_data, XEvent *ev, Boolean *continue_dispatch ) Purpose : Event handler for our video window's widget. Programmer : 04-Mar-97 Randall Hopper Parameters : wgt - I: video window's widget cl_data - I: ev - I: xevent received for widget window continue_dispatch - I: Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENVideoWinEventHdlr( Widget wgt, XtPointer cl_data, XEvent *ev, Boolean *continue_dispatch ) { static TV_INT32 S_call_level = 0; TV_DISPLAY *d = &G_glob.display; TV_CAPTURE *c = &G_glob.capture; TV_XSCREEN *x = &G_glob.x; TV_AUDIO *a = &G_glob.audio; char *cfg_fail_msg; TV_BOOL dirvid_allowed; if (( ev->type < 0 ) && ( ev->type >= XtNumber( event_names ) )) { fprintf( stderr, "VideoWin EVENT: Unknown %d!\n", ev->type ); exit(1); } S_call_level++; EVPRINTF(( "%2ld: VideoWin EVENT: %s\n", S_call_level, event_names[ ev->type ] )); /* Always keep recorded visibility state current */ if ( ev->type == VisibilityNotify ) d->win_visibility = ev->xvisibility.state; /* We don't do anything until the window's mapped, and even then, */ /* not until the first Expose */ if ( d->win == None ) { if ( ev->type != Expose ) goto RETURN; d->win = XtWindow( wgt ); /* When window is mapped, kick off enabled auto-behaviors */ if ( !d->enabled ) goto RETURN; TVSCREENUpdateWinGeometry(); TVSCREENSetVideoWinGeom( d->geom ); /* Tweak if necessary */ /* Kick off a single- or continuous-capture */ if ( RES_MULT_OK(d,c) ) { if ( !TVSCREENCapConfigure( d->cap_mode, &cfg_fail_msg ) ) { fprintf( stderr, "TVSCREENCapConfigure() failed: %s\n", cfg_fail_msg ); goto RETURN; } /* Ready to roll; unmute audio now */ TVCAPTURESetAudioMute( c, a->mute_on ); TVSCREENStartVideo(); } else d->waiting_for_resize = TRUE; goto RETURN; } dirvid_allowed = !App_res.disable_direct_v && ( x->visual_modes[ x->active_visual ] & TV_TRANSFER_DIRECT ); switch ( ev->type ) { case VisibilityNotify: EVPRINTF(( "\t\t%s\n",visibility_names[ ev->xvisibility.state ])); /* No need to process when we're frozen or otherwise disabled */ if ( d->freeze_on || !d->enabled ) break; /* If now fully obscured, stop capture & force a redraw */ if ( ev->xvisibility.state == VisibilityFullyObscured ) { if ( TVSCREENVideoStarted() ) TVSCREENStopVideo( False ); } /* Else we are now partially or totally visible. */ /* If we aren't capturing, start. */ /* If we already are (e.g. partial<->total), we only need */ /* to stop and restart if direct video is an option. */ /* We use direct video when unobscured for max frame rate */ /* and min system load, and switch to ximages when partially */ /* obscured to let the X Server do clipping for us. */ else { TV_BOOL contin_on = TVSCREENVideoStarted() && TVSCREENVideoReqCaptureContin(); if (( !contin_on || dirvid_allowed ) && RES_MULT_OK(d,c) ) { if ( TVSCREENVideoStarted() ) TVSCREENStopVideo( False ); if ( !TVSCREENCapConfigure( d->cap_mode, &cfg_fail_msg ) ){ fprintf( stderr, "TVSCREENCapConfigure() failed: %s\n", cfg_fail_msg ); break; } TVSCREENStartVideo(); } } break; case ConfigureNotify: /* First, stop any running capture && force a redraw */ if ( d->enabled && TVSCREENVideoStarted() && TVSCREENVideoReqCaptureContin() ) { TVSCREENStopVideo( False ); TVSCREENStartVideo(); } /* Always keep window geom up-to-date */ TVSCREENUpdateWinGeometry(); /* Tweak geometry if necessary */ TVSCREENSetVideoWinGeom( d->geom ); break; case Expose : /* Ignore all but the last expose */ if ( ev->xexpose.count != 0 ) break; /* If capture is stopped and saved ximage looks reasonable, */ /* blast it back up there. */ TVSCREENRedrawVideoWin(); break; case MotionNotify : /* Wake up the cursor, if its sleeping */ TVSCREENWakeupCursor(); break; } RETURN: S_call_level--; return; } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENShellWinEventHdlr( Widget wgt, XtPointer cl_data, XEvent *ev, Boolean *continue_dispatch ) Purpose : Event handler for our shell window's widget. Programmer : 04-Mar-97 Randall Hopper Parameters : wgt - I: shell window's widget cl_data - I: ev - I: xevent received for widget window continue_dispatch - I: Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENShellWinEventHdlr( Widget wgt, XtPointer cl_data, XEvent *ev, Boolean *continue_dispatch ) { TV_DISPLAY *d = &G_glob.display; TV_CAPTURE *c = &G_glob.capture; TV_AUDIO *a = &G_glob.audio; char *cfg_fail_msg; if (( ev->type < 0 ) && ( ev->type >= XtNumber( event_names ) )) EVPRINTF(( "--- ShellWin EVENT: Unknown %d!\n", ev->type )); else EVPRINTF(( "--- ShellWin EVENT: %s\n", event_names[ ev->type ] )); /* We don't do anything until our video window's mapped */ if ( d->win == None ) goto RETURN; switch ( ev->type ) { case UnmapNotify: /* When we unmap (e.g. shell iconified), stop capture */ if ( d->enabled && TVSCREENVideoStarted() ) TVSCREENStopVideo( False ); break; case ConfigureNotify: /* FIXME: Really all we need to do here is catch the */ /* cases where the shell (and all children including our */ /* video window) have moved because a Configure "won't" be */ /* sent to our video window for this. But for now, this */ /* will stop/config/start the video twice on a shell */ /* resize; once for this config and once for the video */ /* win config. */ /* First, stop any running capture && force a redraw */ if ( d->enabled && TVSCREENVideoStarted() && TVSCREENVideoReqCaptureContin() ) { TVSCREENStopVideo( False ); TVSCREENStartVideo(); } /* Always keep window geom up-to-date */ TVSCREENUpdateWinGeometry(); /* Tweak geometry if necessary */ TVSCREENSetVideoWinGeom( d->geom ); /* If we've just mapped and were waiting for resize, start */ if ( d->waiting_for_resize && RES_MULT_OK(d,c) ) { d->waiting_for_resize = FALSE; if ( !TVSCREENCapConfigure( d->cap_mode, &cfg_fail_msg ) ) { fprintf( stderr, "TVSCREENCapConfigure() failed: %s\n", cfg_fail_msg ); goto RETURN; } /* Ready to roll; unmute audio now */ TVCAPTURESetAudioMute( c, a->mute_on ); if ( TVSCREENVideoStarted() ) TVSCREENStopVideo( False ); TVSCREENStartVideo(); } break; case EnterNotify: /* Resync menu options and tools with driver defaults */ TVMENUResync(); TVTOOLSResync(); TVAPPEARDIALOGResync(); break; } RETURN: return; } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENSetVideoWinGeom( TV_GEOM videowin_geom ) Purpose : Resize the application such that the video window is the specified size Note that the width will be adjusted to be a multiple of 2 so we don't have to fool with scan line padding when doing ximage conversion/display. Programmer : 08-Mar-97 Randall Hopper Parameters : videowin_geom - I: desired geometry for video subwindow Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENSetVideoWinGeom( TV_GEOM videowin_geom ) { TV_DISPLAY *d = &G_glob.display; TV_CAPTURE *c = &G_glob.capture; TV_GEOM g = videowin_geom; int shell_x, shell_y; Window child_win; TV_INT32 w, h; /* And if aspect lock is on, tweak back to 4:3 ratio */ if ( d->aspect_lock ) { h = ( g.h + g.w * 3 / 4 ) / 2; w = h * 4 / 3; /* Don't sweat the round-off errors */ if (( abs(g.h - h) > 1 ) || ( abs(g.w - w) > 1 )) g.w = w, g.h = h; } /* Update based on capture res limits */ g.w = g.w / c->width_res * c->width_res; g.h = g.h / c->height_res * c->height_res; /* If we're already there, no need to do anything */ if ( memcmp( &d->geom, &g, sizeof( g )) == 0 ) return; assert( XtIsRealized( d->shell_wgt ) ); assert( XtIsRealized( d->video_wgt ) ); /* Propagete new size to annotations */ TVANNOTSetDrawable( &d->annot, XtWindow( d->video_wgt ) ); TVANNOTSetDrawableSize( &d->annot, g.w, g.h ); /* Recompute new shell coordinates that'll put our video window */ /* where we want it. */ if ( !XTranslateCoordinates( TVDISPLAY, XtWindow( d->video_wgt ), XtWindow( d->shell_wgt ), 0, 0, &shell_x, &shell_y, &child_win ) ) { fprintf( stderr, "XTranslateCoordinates() failed\n" ); exit(1); } g.x -= shell_x; g.y -= shell_y; #ifdef BUSTED /* I'm doin' somethin wrong */ /* Doesn't work and freeezes the prog for a few seconds */ XtVaSetValues( d->shell_wgt, XtNx, g.x, XtNy, g.y, NULL ); #endif EVPRINTF(( "Resetting video widget geometry: %ldx%ld\n", g.w, g.h )); #ifdef OLD XawPanedSetRefigureMode( XtParent( d->video_wgt ), False ); XtVaSetValues( d->video_wgt, XtNwidth , g.w, XtNheight, g.h, NULL ); XawPanedSetRefigureMode( XtParent( d->video_wgt ), True ); #endif g.w += shell_x; g.h += shell_y; XtVaSetValues( d->shell_wgt, XtNx , g.x, XtNy , g.y, XtNwidth , g.w, XtNheight , g.h, XtNmaxWidth , shell_x + c->width_max, XtNmaxHeight, shell_y + c->height_max, NULL ); } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENGetVideoWinGeom( TV_GEOM *videowin_geom ) Purpose : Return the most recently registered position of the video window. It is an error to call this before the video window is mapped (because the geometry doesn't exist). Programmer : 06-Oct-97 Randall Hopper Parameters : videowin_geom - O: most recently queried geometry Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENGetVideoWinGeom( TV_GEOM *videowin_geom ) { TV_DISPLAY *d = &G_glob.display; if ( d->win == None ) { fprintf( stderr, "TVSCREENGetVideoWinGeom called before video " "window mapped\n" ); exit(1); } memcpy( videowin_geom, &d->geom, sizeof( *videowin_geom ) ); } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENUpdateShellRsrcs( Widget shell_wgt, Widget video_wgt ) Purpose : Update the max width/height of our shell based on max width/height of video window imposed by capture size limitations. Programmer : 08-Mar-97 Randall Hopper Parameters : shell_wgt - I: shell widget video_wgt - I: video widget Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENUpdateShellRsrcs( Widget shell_wgt, Widget video_wgt ) { TV_CAPTURE *c = &G_glob.capture; int shell_x, shell_y; Window child_win; if ( !XTranslateCoordinates( TVDISPLAY, XtWindow( video_wgt ), XtWindow( shell_wgt ), 0, 0, &shell_x, &shell_y, &child_win ) ) { fprintf( stderr, "XTranslateCoordinates() failed\n" ); exit(1); } XtVaSetValues( shell_wgt, XtNmaxWidth , shell_x + c->width_max, XtNmaxHeight, shell_y + c->height_max, NULL ); } /**@BEGINFUNC************************************************************** Prototype : static void STVSCREENSwitchToMode( TV_XSCREEN *s, int cur_vm, int new_vm ) Purpose : Wrapper function used to hide the mess we have to go through because XF86VidModeSwitchToMode doesn't work in some versions of the VM extension. For these versions, we have to simulate it with multiple XF86VidModeSwitchMode calls. Programmer : 24-May-97 Randall Hopper Parameters : s - I: X screen def struct cur_vm - I: index of video mode we're currently in new_vm - I: index of video mode to switch to Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void STVSCREENSwitchToMode( TV_XSCREEN *s, int cur_vm, int new_vm ) { if ( !s->vmode_ext_supported ) { fprintf( stderr, "STVSCREENSwitchToMode called without VidMode ext support\n" ); exit(1); } #ifdef HAVE_XFREE86 /* NOTE: XF86VidModeSwitchToMode dumps core on VMode <= 0.7 */ /* (XFree <= 3.2A) w/ S3 & S3V servers (ModeInfo private data */ /* is garbage). David Dawes @ XFree said he fixed this 5/24/97 */ /* and it'd be in 3.3 w/ VMode >= 0.8. Not confirmed yet. */ if ( VERS_SAME_OR_NEWER( s->vmode_majv, s->vmode_minv, 0, 8 ) ) XF86VidModeSwitchToMode( TVDISPLAY, TVSCREEN, (s->vm_list)[ new_vm ] ); else { /* NOTE - 6/15/98 - As of XFree86 3.3.1 (& .2 probably), there's */ /* no way to know whether the vidmode extension is truly enabled. */ /* It's advertised even when -disableVidMode was specified or */ /* if the client is remote and -allowNonLocalXvidtune was not */ /* specified. As a result, VidMode calls may spontaneously fail */ /* with an XError. */ /* For this reason, we just disable VidMode when running remotely */ /* as its probably not enabled, but this can still bite us if */ /* the user is running locally and has specified -disableVidMode. */ /* This is particulary nasty since we have a Mouse grab active */ /* active at that point and XFree86 has another bug where it */ /* doesn't release the mouse grab when the client dies. */ /* FIXME: XFree86 will hopefull fix these two bugs: release DGA */ /* mouse grab when client dies, and provide method to query */ /* whether vidmode extension is really available. */ int i, inc = (new_vm > cur_vm) ? 1 : -1; for ( i = cur_vm; i != new_vm; i += inc ) { if ( !XF86VidModeSwitchMode( TVDISPLAY, TVSCREEN, (inc > 0) ? 1 : 0 ) ) { fprintf( stderr, "XF86VidModeSwitchMode() failed\n" ); exit(1); } } } #endif } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENSetZoomState( TV_BOOL zoom_on, TV_BOOL full_screen ) Purpose : Toggles Zoom (max capture res) mode on and off. If full_screen is on when zooming, the video mode and viewport origin will be adjusted to make the video window fill as much of the monitor display area as possible. Programmer : 05-Mar-97 Randall Hopper Parameters : zoom_on - I: T = zoom; F = unzoom full_screen - I: if zoom_on = TRUE, chg vid mode and adjust viewport so video win fills monitor Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENSetZoomState( TV_BOOL zoom_on, TV_BOOL full_screen ) { TV_DISPLAY *d = &G_glob.display; TV_XSCREEN *s = &G_glob.x; TV_CAPTURE *c = &G_glob.capture; TV_GEOM g, vp; int cur_vm = -1, new_vm = -1; TV_BOOL need_video_restart, vport_api_works = FALSE; Dimension vm_width, vm_height, dpy_width = DisplayWidth(TVDISPLAY,TVSCREEN), dpy_height= DisplayHeight(TVDISPLAY,TVSCREEN); if ( zoom_on == d->zoom_on ) return; if ( s->vmode_ext_supported ) { cur_vm = TVSCREENGetCurVidMode( s ); #ifdef HAVE_XFREE86 vport_api_works = VERS_SAME_OR_NEWER( s->vmode_majv, s->vmode_minv, 0, 8 ); #endif } /* First, stop any running capture && force a redraw. */ /* We don't want to be DMAing to the video card when its being */ /* reconfigured for a new mode or it can hang the system. */ need_video_restart = FALSE; if ( d->enabled && TVSCREENVideoStarted() && TVSCREENVideoReqCaptureContin() && s->vmode_ext_supported && (( zoom_on && full_screen ) || ( !zoom_on && (cur_vm != d->unzoomed.mode) )) ) { TVSCREENStopVideo( False ); XSync( TVDISPLAY, False ); need_video_restart = TRUE; } /* When we go into zoom mode, try to keep the same upper-left, but */ /* adjust it if we need to in order to stay on the display. */ /* When zooming to full-screen, change the video mode and viewport */ /* so that the video window fills as much of the screen as */ /* possible. */ /* FIXME: full-screen viewport behavior not as described due to */ /* XFree 3.2A bugs in video mode extension. For now, on */ /* full-screen zoom, put video win in upper-left of desktop and */ /* just change modes. */ /* When we go back to unzoomed, go back to exactly where we were */ /* and what size we were. */ /* XFREE BUG: Note that if you attempt to set the desktop viewport */ /* origin (even if to 0,0) when the desktop (display) size == */ /* the video mode resolution, X throws you off into unused video */ /* memory somewhere. So avoid this bug and don't call it then */ /* (we really don't need to anyway). */ if ( zoom_on ) { d->unzoomed.geom = d->geom; /* Save old */ if ( !s->vmode_ext_supported || !full_screen ) { d->unzoomed.mode = d->unzoomed.viewp_x = d->unzoomed.viewp_y = -1; } #ifdef HAVE_XFREE86 else { d->unzoomed.mode = s->vm_startup; if ( vport_api_works ) XF86VidModeGetViewPort( TVDISPLAY, TVSCREEN, &d->unzoomed.viewp_x, &d->unzoomed.viewp_y ); } #endif g = d->geom; g.w = c->width_max; g.h = c->height_max; if ( !vport_api_works && full_screen ) g.x = g.y = 0; else { if ( g.x + g.w - 1 >= dpy_width ) g.x -= (g.x+g.w) - dpy_width; if ( g.y + g.h - 1 >= dpy_height ) g.y -= (g.y+g.h) - dpy_height; if ( g.x < 0 ) g.x = 0, g.w = dpy_width; if ( g.y < 0 ) g.y = 0, g.h = dpy_height; } } else { g = d->unzoomed.geom; } /* Do it */ TVSCREENSetVideoWinGeom( g ); /* If full-screen was requested, determine whether it makes sense */ /* based on whether we're going to change modes. */ if ( zoom_on && full_screen ) { new_vm = TVSCREENClosestVidMode( s, g.w, g.h ); if (( new_vm < 0 ) || ( cur_vm == new_vm )) { full_screen = FALSE; d->unzoomed.mode = -1; } } /* Deal with full-screen mode changes on zoom/unzoom. */ /* Also change viewport origins to coincide with zoomed video win. */ #ifdef HAVE_XFREE86 if ( s->vmode_ext_supported ) { assert( cur_vm >= 0 ); if ( zoom_on ) { if ( full_screen ) { XRaiseWindow( TVDISPLAY, XtWindow(d->shell_wgt) ); /* Lock out the mouse so user can't move off the TV. */ if ( s->dga_ext_supported ) XF86DGADirectVideo( TVDISPLAY, TVSCREEN, XF86DGADirectMouse ); if ( new_vm >= 0 ) { vp.x = g.x, vp.y = g.y; STVSCREENSwitchToMode( s, cur_vm, new_vm ); TVSCREENGetVidModeGeometry( s, new_vm, &vm_width, &vm_height); if ( vp.x + vm_width > dpy_width ) vp.x = dpy_width - vm_width; if ( vp.y + vm_height > dpy_height ) vp.y = dpy_height - vm_height; if ( vport_api_works && ( vp.x >= 0 ) && ( vp.y >= 0 ) ) XF86VidModeSetViewPort( TVDISPLAY, TVSCREEN, vp.x, vp.y); XSync( TVDISPLAY, False ); } /* Warp the pointer to the middle of the window. */ if ( s->dga_ext_supported ) XWarpPointer( TVDISPLAY, None, RootWindow( TVDISPLAY, TVSCREEN ), 0,0,0,0, g.x+g.w/2, g.y+g.h/2 ); } } else { if ( d->unzoomed.mode >= 0 ) { if ( cur_vm != d->unzoomed.mode ) { new_vm = d->unzoomed.mode; STVSCREENSwitchToMode( s, cur_vm, new_vm ); } if ( vport_api_works & ( d->unzoomed.viewp_x >= 0 ) ) XF86VidModeSetViewPort( TVDISPLAY, TVSCREEN, d->unzoomed.viewp_x, d->unzoomed.viewp_y ); if ( s->dga_ext_supported ) XF86DGADirectVideo( TVDISPLAY, TVSCREEN, 0 ); XSync( TVDISPLAY, False ); } } } #endif d->zoom_on = !d->zoom_on; /* Give annotation a chance to resync annot size for new mode */ TVANNOTSetDrawable( &d->annot, XtWindow( d->video_wgt ) ); TVANNOTSetDrawableSize( &d->annot, g.w, g.h ); /* Finally, restart video if it was running before */ if ( need_video_restart ) TVSCREENStartVideo(); } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENSetFreezeState( TV_BOOL freeze_on ) Purpose : Toggle on and off frozen display. Programmer : 08-Mar-97 Randall Hopper Parameters : freeze_on - I: T = freeze; F = continuous update Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENSetFreezeState( TV_BOOL freeze_on ) { TV_DISPLAY *d = &G_glob.display; char *cfg_fail_msg; if ( !d->enabled ) return; if ( freeze_on && TVSCREENVideoStarted() ) { /* Stop continuous and capture a single intact frame */ /* to memory to display persistently in the window. */ TVSCREENStopVideo( True ); if ( !TVSCREENCapConfigure( TV_CAPTURE_SINGLE, &cfg_fail_msg ) ) { fprintf( stderr, "TVSCREENCapConfigure() failed: %s\n", cfg_fail_msg ); return; } TVSCREENStartVideo(); d->cap_mode = TV_CAPTURE_SINGLE; d->freeze_on = TRUE; } else if ( !freeze_on && !( TVSCREENVideoStarted() && TVSCREENVideoReqCaptureContin() ) ){ /* Start continuous */ if ( !TVSCREENCapConfigure( TV_CAPTURE_CONTINUOUS, &cfg_fail_msg ) ) { fprintf( stderr, "TVSCREENCapConfigure() failed: %s\n", cfg_fail_msg ); return; } TVSCREENStartVideo(); d->cap_mode = TV_CAPTURE_CONTINUOUS; d->freeze_on = FALSE; } } void TVSCREENToggleFreezeState( void ) { TV_DISPLAY *d = &G_glob.display; TVSCREENSetFreezeState( !d->freeze_on ); } void TVSCREENSetScreenUpdateEnabled( TV_BOOL enabled ) { TV_DISPLAY *d = &G_glob.display; char *cfg_fail_msg; if ( enabled == d->enabled ) { fprintf( stderr, "TVSCREENSetScreenUpdateEnabled: new/was=%ld\n", enabled ); return; } if ( d->enabled ) { d->enabled = False; if ( TVSCREENVideoStarted() ) TVSCREENStopVideo( True ); } else { d->enabled = True; TVCAPTUREClearPendingFrames(); if ( d->freeze_on ) { /* Capture a single intact frame to memory to display */ /* persistently in the window. */ if ( !TVSCREENCapConfigure( TV_CAPTURE_SINGLE, &cfg_fail_msg ) ) { fprintf( stderr, "TVSCREENCapConfigure() failed: %s\n", cfg_fail_msg ); return; } TVSCREENStartVideo(); d->cap_mode = TV_CAPTURE_SINGLE; } else { /* Start continuous */ if ( !TVSCREENCapConfigure( TV_CAPTURE_CONTINUOUS, &cfg_fail_msg)){ fprintf( stderr, "TVSCREENCapConfigure() failed: %s\n", cfg_fail_msg ); return; } TVSCREENStartVideo(); d->cap_mode = TV_CAPTURE_CONTINUOUS; } } } /**@BEGINFUNC************************************************************** Prototype : static void TVSCREENPrepXImage( TV_DISPLAY *d, TV_GEOM *g, XVisualInfo *v ) Purpose : Verifies that the XImage currently allocated to move captured images into satisfies the requirements for the resolution and depth we're going to be stuffing into it. If not, the XImage is reallocated. Also, if pixel conversion or quantization is going to be necessary to display the image, verifies that the appropriate conversion array is populated. Programmer : 07-Mar-97 Randall Hopper Parameters : d - I: display struct g - I: desired image geometry v - I: visual to use Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVSCREENPrepXImage( TV_DISPLAY *d, TV_GEOM *g, XVisualInfo *v ) { TV_CAPTURE *c = &G_glob.capture; TV_XIMAGE *image = &d->ximage; /**************************************/ /* Create a new image if we need to */ /**************************************/ if ( ! (( image->ximg != NULL ) && ( image->visual->visualid == v->visualid ) && ( image->geom.w == g->w ) && ( image->geom.h == g->h ))) { d->ximage_use_for_expose = FALSE; /* Free the old */ if ( image->ximg ) if ( image->is_shm ) { if ( !XShmDetach ( TVDISPLAY, &image->shm_info ) ) { fprintf( stderr, "XShmDetach() failed\n" ); exit(1); } XDestroyImage( image->ximg ); image->ximg = NULL; if ( shmdt ( image->shm_info.shmaddr ) ) { fprintf( stderr, "shmdt() failed: %s\n", strerror(errno)); exit(1); } if ( shmctl( image->shm_info.shmid, IPC_RMID, 0 ) ) { fprintf( stderr, "shmctl() failed: %s\n",strerror(errno)); exit(1); } image->is_shm = False; } else { free( image->ximg->data ); image->ximg->data = NULL; XDestroyImage( image->ximg ); image->ximg = NULL; } /* Create desired new */ if ( c->xfer_mode == TV_TRANSFER_SHMEM_IMAGE ) { /* FIXME: Clean up shared mem segments on exit and on */ /* next run. */ image->ximg = XShmCreateImage( TVDISPLAY, v->visual, v->depth, ZPixmap, NULL, &image->shm_info, g->w, g->h ); if ( image->ximg == NULL ) { fprintf( stderr, "XShmCreateImage() failed\n" ); exit(1); } image->shm_info.shmid = shmget( IPC_PRIVATE, image->ximg->bytes_per_line * image->ximg->height, IPC_CREAT | 0777 ); if ( image->shm_info.shmid < 0 ) { fprintf( stderr, "shmget() failed: %s\n", strerror(errno)); exit(1); } image->shm_info.shmaddr = image->ximg->data = shmat( image->shm_info.shmid, 0, 0 ); if ( image->ximg->data == NULL ) { fprintf( stderr, "shmat() failed: %s\n", strerror(errno)); exit(1); } image->shm_info.readOnly = True; if ( !XShmAttach( TVDISPLAY, &image->shm_info ) ) { fprintf( stderr, "XShmAttach() failed\n" ); exit(1); } image->is_shm = True; /*printf( "ImageByteOrder = %s\n", image->ximg->byte_order == LSBFirst ? "LSB" : "MSB" );*/ } else if ( c->xfer_mode == TV_TRANSFER_STD_IMAGE ) { image->ximg = XCreateImage( TVDISPLAY, v->visual, v->depth, ZPixmap, 0, NULL, g->w, g->h, BitmapPad(TVDISPLAY), 0 ); if ( image->ximg == NULL ) { fprintf( stderr, "XCreateImage() failed\n" ); exit(1); } image->ximg->data = malloc( image->ximg->bytes_per_line * image->ximg->height ); if ( image->ximg->data == NULL ) TVUTILOutOfMemory(); image->is_shm = False; } else { fprintf( stderr, "TVSCREENPrepXImage() - Unsupported " "xfer_mode %d\n", c->xfer_mode ); exit(1); } image->geom = *g; image->visual = v; } } /**@BEGINFUNC************************************************************** Prototype : static void TVSCREENAllocColorCube( TV_DISPLAY *d, VL_COLORMAP **cmap ) Purpose : Creates the largest colorcube that the default 8-bit colormap will allow. This is used to map direct color images for pseudocolor display. Programmer : 08-Mar-97 Randall Hopper Parameters : d - I: display struct cmap - O: allocated colormap struct, with allocated colors Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVSCREENAllocColorCube( TV_DISPLAY *d, VL_COLORMAP **cmap ) { static Color_cubes[][3] = { {7,7,5},{6,6,6},{6,6,5},{6,6,4},{5,5,5},{5,5,4},{5,5,3}, {4,4,4},{3,3,3},{2,2,2} }; #define MAX_CUBE_DIM 7 #define MIN_BRIGHT 0.0 #define MAX_BRIGHT 1.0 XColor col[ 256 ]; TV_INT32 i, r,g,b, num_col, cube_idx, r_dim, g_dim, b_dim, min_br = 255 * MIN_BRIGHT, max_br = 255 * MAX_BRIGHT; Colormap colormap = XDefaultColormap( TVDISPLAY, TVSCREEN ); TV_UINT16 color_tbl_rg[ MAX_CUBE_DIM ], color_tbl_b [ MAX_CUBE_DIM ]; /* For now, keep it simple and allocate the largest cube we can */ for ( cube_idx = 0; cube_idx < XtNumber( Color_cubes ); cube_idx++ ) { memset( col, '\0', sizeof( col ) ); r_dim = Color_cubes[ cube_idx ][0]; g_dim = Color_cubes[ cube_idx ][1]; b_dim = Color_cubes[ cube_idx ][2]; num_col = r_dim * g_dim * b_dim; assert( num_col <= XtNumber( col ) ); /* Fill up the color cube */ for ( i = 0; i < r_dim; i++ ) { int val = i * (max_br-min_br) / (r_dim-1) + min_br; color_tbl_rg[i] = val | ( val << 8 ); } for ( i = 0; i < b_dim; i++ ) { int val = i * (max_br-min_br) / (b_dim-1) + min_br; color_tbl_b[i] = val | ( val << 8 ); } i = 0; for ( r = 0; r < r_dim; r++ ) for ( g = 0; g < g_dim; g++ ) for ( b = 0; b < b_dim; b++ ) col[ i ].red = color_tbl_rg[ r ], col[ i ].green = color_tbl_rg[ g ], col[ i ].blue = color_tbl_b [ b ], col[ i++].flags = DoRed | DoGreen | DoBlue; /* Allocate cube in the colormap */ for ( i = 0; i < num_col; i++ ) if ( !XAllocColor( TVDISPLAY, colormap, &col[i] ) ) break; /* If couldn't, fall back on next smaller cube size */ if ( i >= num_col ) break; SUPRINTF(( "Failed to alloc %ldx%ldx%ld color cube\n", r_dim, g_dim, b_dim )); for ( i--; i >= 0; i-- ) XFreeColors( TVDISPLAY, colormap, &col[i].pixel, 1, 0 ); } if ( r_dim < 2 ) { fprintf( stderr, "Can't even get a %ldx%ldx%ld colormap..." "bailing out\n", r_dim, g_dim, b_dim ); exit(1); } SUPRINTF(( "%ldx%ldx%ld Color Cube Allocated\n",r_dim,g_dim,b_dim )); /* Done. Now allocate and fill in the VideoLib colormap definition */ *cmap = VIDEOLIBNewColormap( num_col ); if ( !*cmap ) TVUTILOutOfMemory(); for ( i = 0; i < num_col; i++ ) { (*cmap)->color[i].pixel = col[ i ].pixel; (*cmap)->color[i].r = col[ i ].red >> 8; (*cmap)->color[i].g = col[ i ].green >> 8; (*cmap)->color[i].b = col[ i ].blue >> 8; } (*cmap)->type = COLORMAP_PREDEF_CUBE; (*cmap)->dim[0] = r_dim; (*cmap)->dim[1] = g_dim; (*cmap)->dim[2] = b_dim; (*cmap)->corners[0][0] = (*cmap)->corners[0][1] = (*cmap)->corners[0][2] = 0; (*cmap)->corners[1][0] = (*cmap)->corners[1][1] = (*cmap)->corners[1][2] = 255; } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENNewFrameHdlr( TV_IMAGE *img ) Purpose : Called to handle display of a new frame when we're not in direct-video mode. This is where any conversion or quantization that needs to be performed for display occurs. Programmer : 07-Mar-97 Randall Hopper Parameters : img - I: captured image Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENNewFrameHdlr( TV_IMAGE *img ) { TV_XSCREEN *x = &G_glob.x; TV_DISPLAY *d = &G_glob.display; TV_XIMAGE *ximage = &d->ximage; XVisualInfo *v = &x->visual[ x->active_visual ]; TV_INT32 dst_Bpp; VL_IMAGE src, dst; #ifdef TESTING TVCAPTUREStop( &G_glob.capture ); sleep(1); signal( SIGUSR1, SIG_IGN ); #endif /* FIXME */ /* First, if user just froze the video, save this freeze-frame off */ /* in case they want to work with it later (e.g. save it to disk). */ if ( d->cap_mode == TV_CAPTURE_SINGLE ) { TV_UINT32 bytes = img->geom.w * img->geom.h * img->pix_geom.Bpp; free( d->image.buf ); if ( (d->image.buf = malloc( bytes )) == NULL ) TVUTILOutOfMemory(); memcpy( d->image.buf, img->buf, bytes ); memcpy( &d->image.geom, &img->geom, sizeof( d->image.geom ) ); memcpy( &d->image.pix_geom, &img->pix_geom, sizeof( d->image.pix_geom ) ); } TVCAPTUREClearPendingFrames(); /* FIXME: For testing, hack-convert source image to videolib format. */ src.buf = img->buf; src.geom.x = img->geom.x; src.geom.y = img->geom.y; src.geom.w = img->geom.w; src.geom.h = img->geom.h; src.geom.bytes_per_line = img->geom.w * img->pix_geom.Bpp; src.pix_geom.type = VL_PIXELTYPE_RGB; src.pix_geom.rgb.direct_color = (img->pix_geom.Bpp != 8); src.pix_geom.rgb.Bpp = img->pix_geom.Bpp; src.pix_geom.rgb.mask[0] = img->pix_geom.mask[0]; src.pix_geom.rgb.mask[1] = img->pix_geom.mask[1]; src.pix_geom.rgb.mask[2] = img->pix_geom.mask[2]; src.pix_geom.rgb.colormap = NULL; src.pix_geom.rgb.swap_bytes = img->pix_geom.swap_bytes; src.pix_geom.rgb.swap_shorts = img->pix_geom.swap_shorts; /* Create the GC if needed */ XUTILGetVisualBpp( TVDISPLAY, v, &dst_Bpp, NULL ); if ( d->gc == NULL ) d->gc = XCreateGC( TVDISPLAY, d->win, 0, NULL ); /* Make sure the XImage buf is ready for this frame res/depth */ TVSCREENPrepXImage( d, &img->geom, v ); /* Now fill in destination image format */ dst.buf = ximage->ximg->data; dst.geom.x = src.geom.x; dst.geom.y = src.geom.y; dst.geom.w = ximage->geom.w; dst.geom.h = ximage->geom.h; dst.geom.bytes_per_line = ximage->ximg->bytes_per_line; dst.pix_geom.type = VL_PIXELTYPE_RGB; dst.pix_geom.rgb.direct_color = (v->class == TrueColor); dst.pix_geom.rgb.Bpp = dst_Bpp; dst.pix_geom.rgb.mask[0] = v->red_mask; dst.pix_geom.rgb.mask[1] = v->green_mask; dst.pix_geom.rgb.mask[2] = v->blue_mask; dst.pix_geom.rgb.colormap = NULL; dst.pix_geom.rgb.swap_bytes = ( ximage->ximg->byte_order == LSBFirst ); dst.pix_geom.rgb.swap_shorts = ( ximage->ximg->byte_order == LSBFirst ) && ( dst_Bpp >= 4 ); /* If this is a -to-PseudoColor conversion, we need to allocate a */ /* color cube. */ if (( v->class == PseudoColor ) && ( v->depth == 8 )) { assert( src.pix_geom.rgb.Bpp == 2 ); if ( !d->colormap ) TVSCREENAllocColorCube( d, &d->colormap ); dst.pix_geom.rgb.colormap = d->colormap; } /* Do conversion */ VIDEOLIBConvertImage( &src, &dst ); /* Ok, ximage is ready for use */ d->ximage_use_for_expose = TRUE; /* Blast the image onto the display */ TVSCREENRedrawVideoWin(); } /**@BEGINFUNC************************************************************** Prototype : void TVSCREENSetAspectLock( TV_BOOL aspect_lock ) Purpose : Turn aspect lock on/off. This setting allows Fxtv to adjust the size of the video window after a resize to insure that it stays roughly 4:3 aspect ratio like NTSC. FIXME: May need tweaks for PAL. Programmer : 07-Sep-97 Randall Hopper Parameters : aspect_lock - I: new aspect lock setting Returns : Globals : None. **@ENDFUNC*****************************************************************/ void TVSCREENSetAspectLock( TV_BOOL aspect_lock ) { TV_DISPLAY *d = &G_glob.display; d->aspect_lock = (aspect_lock != FALSE); /* If window is mapped and aspect lock is on, adjust video window geom */ if (( d->win != None ) && d->aspect_lock ) { if ( abs( d->geom.h * 4 / 3 - d->geom.w ) > 1 ) TVSCREENSetVideoWinGeom( d->geom ); } } void TVSCREENGetAspectLock( TV_BOOL *aspect_lock ) { TV_DISPLAY *d = &G_glob.display; *aspect_lock = d->aspect_lock; }