1 /*
2  * annot.c
3  *
4  * Routines to manage the display of annotation on top of the video window.
5  *
6  * (C) 1997 Randall Hopper
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met: 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer. 2.
12  * Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 /*      ******************** Include Files                ************** */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <assert.h>
36 #include <sys/time.h>
37 #include <X11/Xlib.h>
38 #include "tvdefines.h"
39 #include "tvtypes.h"
40 #include "app_rsrc.h"
41 #include "annot.h"
42 #include "glob.h"
43 #include "tvutil.h"
44 #include "tvscreen.h"
45 #include "tvaudio.h"
46 #include "tvcapture.h"
47 
48 /*      ******************** Local defines                ************** */
49 
50 #define TIMEBASE_SEC 876000000L
51 
52 #define FALLBACK_FONT "10x20"
53 
54 #define QUANTIZE_FONT_SIZE(size) MAX( ((TV_INT32)(size)+4)/5*5, 10 )
55               /* Let's not go nuts creating different font sizes.  They  */
56               /*   use memory and take CPU to compute.                   */
57 
58 #define AUTOMODE_TIMER_MS 8
59 
60 #define VOLUME_FMT "Volume: %ld%%"
61 #define MUTE_STR   "MUTE"
62 
63 /*#define FNPRINTF(x) printf x*/
64 #define FNPRINTF(x)
65 
66 /*      ******************** Forward declarations         ************** */
67 /*      ******************** Private variables            ************** */
68 /*      ******************** Function Definitions         ************** */
69 
70 /**@BEGINFUNC**************************************************************
71 
72     Prototype  : static void TVANNOTLoadFont(
73                       Display     *display,
74                       char         font[],
75                       TV_INT32     pixel_size,
76                       XFontSet    *fontset )
77 
78     Purpose    : Loads the specified font pattern in the specified pixel_size,
79                  if possible.
80 
81                  If multiple fonts match the pattern, the first match is
82                  loaded.
83 
84                  If the first match is not an XLFD font with '0' or '*' in
85                  the pixel_size component, pixel_size is ignored and the
86                  font is loaded as-is.
87 
88                  If the font is invalid, a fallback font is loaded.
89 
90     Programmer : 04-Oct-97  Randall Hopper
91 
92     Parameters : display      - I: X display
93                  font         - I: font pattern.  Examples:
94                                   -*-utopia-regular-r-*-*-0-0-*-*-*-0-iso8859-1
95                                   9x15
96                  pixel_size   - I: desired pixel size to load font in
97                  fontset      - O: fontset handle to the loaded font
98 
99     Returns    : None.
100 
101     Globals    : None.
102 
103  **@ENDFUNC*****************************************************************/
104 
TVANNOTLoadFont(Display * display,char font[],TV_INT32 pixel_size,XFontSet * fontset)105 static void TVANNOTLoadFont(
106           Display     *display,
107           char         font[],
108           TV_INT32     pixel_size,
109           XFontSet    *fontset )
110 {
111     char        **fonts;
112     int           count;
113     char          new_font[ 256 ] = "";
114     XFontStruct  *fontstr = NULL;
115     char        **missing_charset_list;
116     int           missing_count;
117 
118     *fontset = NULL;
119 
120     FNPRINTF(( "Validating font spec '%s'\n", font ));
121 
122     fonts = XListFonts( display, font, 1, &count );
123 
124     if ( count == 0 )
125         fprintf( stderr,
126             "No matching font(s) registered with X Server:\n  %s\n", font );
127     else if ( XUTILIsWellFormedXLFDFont( fonts[0] ) ) {
128         FNPRINTF(( "Found at least one registered font, and the first "
129                   "one is an XLFD font:\n  '%s'\n", fonts[0] ));
130         FNPRINTF(( "Trying to load a '%d' pixel size version\n",
131                   pixel_size ));
132 
133         fontstr = XUTILLoadPixelSizeFont( TVDISPLAY, fonts[0], pixel_size,
134                                            new_font, sizeof(new_font) );
135 
136         if ( fontstr == NULL ) {
137             new_font[0] = '\0';
138             fprintf( stderr, "Load failed:\n  %s\n", fonts[0] );
139         }
140         else {
141             XFreeFontInfo( NULL, fontstr, 0 );
142             FNPRINTF(( "Load successful: '%s'\n", new_font ));
143         }
144     }
145     else {
146         FNPRINTF(( "Found at least one registered font; the first "
147                   "one isn't an XLFD font:\n  '%s'\n", fonts[0] ));
148 
149         strncat( new_font, fonts[0], sizeof(new_font)-1 );
150         fontstr = XLoadQueryFont( TVDISPLAY, fonts[0] );
151 
152         if ( fontstr == NULL )
153             new_font[0] = '\0';
154         else
155             XFreeFontInfo( NULL, fontstr, 0 );
156 
157         if ( fontstr )
158             FNPRINTF(( "Load successful.\n" ));
159         else
160             fprintf( stderr, "Load failed:\n  %s\n", fonts[0] );
161     }
162 
163     if ( count )
164         XFreeFontNames( fonts );
165 
166     if ( new_font[0] == '\0' ) {
167         strcpy( new_font, FALLBACK_FONT );
168         fprintf( stderr, "Falling back on '%s' font.\n", new_font );
169 
170         fontstr = XLoadQueryFont( TVDISPLAY, new_font );
171         if ( fontstr == NULL ) {
172             fprintf( stderr,
173                      "Failed to load the fall-back '%s' font.\n", new_font );
174             exit(1);
175         }
176         XFreeFontInfo( NULL, fontstr, 0 );
177     }
178 
179     *fontset = XCreateFontSet( TVDISPLAY, new_font, &missing_charset_list,
180                               &missing_count, NULL );
181     if ( ! *fontset ) {
182         fprintf( stderr,
183           "Whoah!  Failed to load font '%s' which X 'said' it knew about.\n"
184           "This shouldn't happen (but does in XFree86 3.9.16).\n"
185           "Basically, choose another font or take the default font.\n",
186           new_font );
187         exit(1);
188     }
189 }
190 
191 /*---------------------------------------------------------------------------*/
192 
TVANNOTPropInit(TV_ANNOT_PROP * p)193 static void TVANNOTPropInit(
194                 TV_ANNOT_PROP *p )
195 {
196     p->gc                = NULL;
197     p->font_pattern      = NULL;
198     p->fontset           = NULL;
199     p->delay             = 2000;
200     p->start_time.tv_sec = -1;
201 }
202 
TVANNOTInit(TV_ANNOT * a,Display * display,int screen,XtAppContext app_context)203 void TVANNOTInit(
204          TV_ANNOT     *a,
205          Display      *display,
206          int           screen,
207          XtAppContext  app_context )
208 {
209     memset( a, '\0', sizeof(*a) );
210 
211     a->display     = display;
212     a->screen      = screen;
213     a->app_context = app_context;
214     a->drawable    = None;
215     a->geom.w      =
216     a->geom.h      = -1;
217     a->auto_update = False;
218 
219     TVANNOTPropInit( &a->station    );
220     TVANNOTPropInit( &a->tuner_mode );
221     TVANNOTPropInit( &a->input_dev  );
222     TVANNOTPropInit( &a->volume     );
223     TVANNOTPropInit( &a->mute       );
224 
225     /*  Station  */
226     a->station.delay        = App_res.station_annot_delay;
227     a->station.font_pattern = App_res.station_annot_font;
228     a->station.fg_pixel     = App_res.station_annot_color;
229 
230     /*  Tuner mode  */
231     a->tuner_mode.delay        = App_res.tuner_mode_annot_delay;
232     a->tuner_mode.font_pattern = App_res.tuner_mode_annot_font;
233     a->tuner_mode.fg_pixel     = App_res.tuner_mode_annot_color;
234 
235     /*  Input Device  */
236     a->input_dev.delay        = App_res.input_dev_annot_delay;
237     a->input_dev.font_pattern = App_res.input_dev_annot_font;
238     a->input_dev.fg_pixel     = App_res.input_dev_annot_color;
239 
240     /*  Volume  */
241     a->volume.delay        = App_res.volume_annot_delay;
242     a->volume.font_pattern = App_res.volume_annot_font;
243     a->volume.fg_pixel     = App_res.volume_annot_color;
244 
245     /*  Mute  */
246     a->mute.delay        = App_res.mute_annot_delay;
247     a->mute.font_pattern = App_res.mute_annot_font;
248     a->mute.fg_pixel     = App_res.mute_annot_color;
249 }
250 
251 /*---------------------------------------------------------------------------*/
252 
TVANNOTPropSetDrawable(TV_ANNOT_PROP * p,Display * display,Drawable drawable)253 static void TVANNOTPropSetDrawable(
254                TV_ANNOT_PROP *p,
255                Display       *display,
256                Drawable       drawable )
257 {
258     if ( p->gc != NULL )
259         XFreeGC( display, p->gc );
260     p->gc = XCreateGC( display, drawable, 0, NULL );
261 }
262 
263 
TVANNOTSetDrawable(TV_ANNOT * a,Drawable drawable)264 void TVANNOTSetDrawable(
265          TV_ANNOT *a,
266          Drawable  drawable )
267 {
268     if ( a->drawable == drawable )
269         return;
270     a->drawable = drawable;
271 
272     a->geom.w =
273     a->geom.h = -1;
274 
275     /*  When the drawable is set or changes, we need to recreate the GCs */
276     TVANNOTPropSetDrawable( &a->station   , a->display, drawable );
277     TVANNOTPropSetDrawable( &a->tuner_mode, a->display, drawable );
278     TVANNOTPropSetDrawable( &a->input_dev , a->display, drawable );
279     TVANNOTPropSetDrawable( &a->volume    , a->display, drawable );
280     TVANNOTPropSetDrawable( &a->mute      , a->display, drawable );
281 }
282 
283 /*---------------------------------------------------------------------------*/
284 
TVANNOTGetModeGeom(Dimension * width,Dimension * height)285 static void TVANNOTGetModeGeom(
286                 Dimension     *width,
287                 Dimension     *height )
288 {
289     TV_XSCREEN      *s = &G_glob.x;
290 
291     if ( s->vmode_ext_supported )
292         TVSCREENGetCurVidModeGeometry( s, width, height );
293     else
294         *width  = DisplayWidth ( TVDISPLAY, TVSCREEN ),
295         *height = DisplayHeight( TVDISPLAY, TVSCREEN );
296 }
297 
298 
TVANNOTStationPropSetDrawableSize(TV_ANNOT * a,TV_ANNOT_PROP * p,Display * display,Dimension width,Dimension height)299 static void TVANNOTStationPropSetDrawableSize(
300                 TV_ANNOT      *a,
301                 TV_ANNOT_PROP *p,
302                 Display       *display,
303                 Dimension      width,
304                 Dimension      height )
305 {
306     TV_INT32         pixel_size;
307     Dimension        vm_width,
308                      vm_height;
309     XFontSetExtents *extents;
310 
311     /*  Determine new pixel size based on Drawable size and Video Mode  */
312     TVANNOTGetModeGeom( &vm_width, &vm_height );
313     pixel_size = QUANTIZE_FONT_SIZE( (TV_INT32)height * vm_height / 768 / 15 );
314 
315     /*  If different than the one we've got, reload at the new size  */
316     if (( p->fontset == NULL ) || ( p->pixel_size != pixel_size )) {
317 
318         FNPRINTF(( "\nSTATION ANNOTATION FONT:\n" ));
319 
320         if ( p->fontset != NULL )
321             XFreeFontSet( display, p->fontset );
322 
323         TVANNOTLoadFont( display, p->font_pattern, pixel_size, &p->fontset );
324         assert( p->fontset != NULL );
325 
326         p->pixel_size = pixel_size;
327     }
328 
329     /*  Determine new location  */
330     extents = XExtentsOfFontSet( p->fontset );
331     p->loc.x = 15;
332     p->loc.y = height - 15;
333 }
334 
335 
TVANNOTTunerModePropSetDrawableSize(TV_ANNOT * a,TV_ANNOT_PROP * p,Display * display,Dimension width,Dimension height)336 static void TVANNOTTunerModePropSetDrawableSize(
337                 TV_ANNOT      *a,
338                 TV_ANNOT_PROP *p,
339                 Display       *display,
340                 Dimension      width,
341                 Dimension      height )
342 {
343     TV_INT32         pixel_size;
344     Dimension        vm_width,
345                      vm_height;
346     XFontSetExtents *extents;
347 
348     /*  Determine new pixel size based on Drawable size and Video Mode  */
349     TVANNOTGetModeGeom( &vm_width, &vm_height );
350     pixel_size = QUANTIZE_FONT_SIZE( (TV_INT32)height * vm_height / 768 / 20 );
351 
352     /*  If different than the one we've got, reload at the new size  */
353     if (( p->fontset == NULL ) || ( p->pixel_size != pixel_size )) {
354 
355         FNPRINTF(( "\nTUNER MODE ANNOTATION FONT:\n" ));
356 
357         if ( p->fontset != NULL )
358             XFreeFontSet( display, p->fontset );
359 
360         TVANNOTLoadFont( display, p->font_pattern, pixel_size, &p->fontset );
361         assert( p->fontset != NULL );
362 
363         p->pixel_size = pixel_size;
364     }
365 
366     /*  Determine new location  */
367     extents = XExtentsOfFontSet( p->fontset );
368     p->loc.x = 15;
369     p->loc.y = 5 + extents->max_logical_extent.height;
370 }
371 
372 
TVANNOTInputDevPropSetDrawableSize(TV_ANNOT * a,TV_ANNOT_PROP * p,Display * display,Dimension width,Dimension height)373 static void TVANNOTInputDevPropSetDrawableSize(
374                 TV_ANNOT      *a,
375                 TV_ANNOT_PROP *p,
376                 Display       *display,
377                 Dimension      width,
378                 Dimension      height )
379 {
380     TV_INT32         pixel_size;
381     Dimension        vm_width,
382                      vm_height;
383     XFontSetExtents *extents_tm,
384                     *extents;
385 
386     /*  Determine new pixel size based on Drawable size and Video Mode  */
387     TVANNOTGetModeGeom( &vm_width, &vm_height );
388     pixel_size = QUANTIZE_FONT_SIZE( (TV_INT32)height * vm_height / 768 / 20 );
389 
390     /*  If different than the one we've got, reload at the new size  */
391     if (( p->fontset == NULL ) || ( p->pixel_size != pixel_size )) {
392 
393         FNPRINTF(( "\nINPUT DEVICE ANNOTATION FONT:\n" ));
394 
395         if ( p->fontset != NULL )
396             XFreeFontSet( display, p->fontset );
397 
398         TVANNOTLoadFont( display, p->font_pattern, pixel_size, &p->fontset );
399         assert( p->fontset != NULL );
400 
401         p->pixel_size = pixel_size;
402     }
403 
404     /*  Determine new location - put it below Tuner Mode annotation  */
405     extents_tm = XExtentsOfFontSet( a->tuner_mode.fontset );
406     extents    = XExtentsOfFontSet( p->fontset );
407     p->loc.x = 15;
408     p->loc.y = a->tuner_mode.loc.y +
409                extents_tm->max_logical_extent.height / 2 +
410                extents->max_logical_extent.height    / 2;
411 }
412 
413 
TVANNOTVolumePropSetDrawableSize(TV_ANNOT * a,TV_ANNOT_PROP * p,Display * display,Dimension width,Dimension height)414 static void TVANNOTVolumePropSetDrawableSize(
415                 TV_ANNOT      *a,
416                 TV_ANNOT_PROP *p,
417                 Display       *display,
418                 Dimension      width,
419                 Dimension      height )
420 {
421     TV_INT32         pixel_size;
422     Dimension        vm_width,
423                      vm_height;
424     XFontSetExtents *extents;
425     char             str[80];
426     int              str_width;
427 
428     /*  Determine new pixel size based on Drawable size and Video Mode  */
429     TVANNOTGetModeGeom( &vm_width, &vm_height );
430     pixel_size = QUANTIZE_FONT_SIZE( (TV_INT32)height * vm_height / 768 / 20 );
431 
432     /*  If different than the one we've got, reload at the new size  */
433     if (( p->fontset == NULL ) || ( p->pixel_size != pixel_size )) {
434 
435         FNPRINTF(( "\nVOLUME ANNOTATION FONT:\n" ));
436 
437         if ( p->fontset != NULL )
438             XFreeFontSet( display, p->fontset );
439 
440         TVANNOTLoadFont( display, p->font_pattern, pixel_size, &p->fontset );
441         assert( p->fontset != NULL );
442 
443         p->pixel_size = pixel_size;
444     }
445 
446     /*  Determine new location  */
447     sprintf( str, VOLUME_FMT, 100L );
448     str_width = XmbTextEscapement( p->fontset, str, strlen(str) );
449     extents    = XExtentsOfFontSet( p->fontset );
450 
451     p->loc.x = width - 15 - str_width;
452     p->loc.y = 5 + extents->max_logical_extent.height;
453 }
454 
455 
TVANNOTMutePropSetDrawableSize(TV_ANNOT * a,TV_ANNOT_PROP * p,Display * display,Dimension width,Dimension height)456 static void TVANNOTMutePropSetDrawableSize(
457                 TV_ANNOT      *a,
458                 TV_ANNOT_PROP *p,
459                 Display       *display,
460                 Dimension      width,
461                 Dimension      height )
462 {
463     TV_INT32         pixel_size;
464     Dimension        vm_width,
465                      vm_height;
466     XFontSetExtents *extents_v,
467                     *extents;
468     int              str_width;
469 
470     /*  Determine new pixel size based on Drawable size and Video Mode  */
471     TVANNOTGetModeGeom( &vm_width, &vm_height );
472     pixel_size = QUANTIZE_FONT_SIZE( (TV_INT32)height * vm_height / 768 / 25 );
473 
474     /*  If different than the one we've got, reload at the new size  */
475     if (( p->fontset == NULL ) || ( p->pixel_size != pixel_size )) {
476 
477         FNPRINTF(( "\nMUTE ANNOTATION FONT:\n" ));
478 
479         if ( p->fontset != NULL )
480             XFreeFontSet( display, p->fontset );
481 
482         TVANNOTLoadFont( display, p->font_pattern, pixel_size, &p->fontset );
483         assert( p->fontset != NULL );
484 
485         p->pixel_size = pixel_size;
486     }
487 
488     /*  Determine new location - Put below Volume Annotation  */
489     str_width = XmbTextEscapement( p->fontset, MUTE_STR, strlen(MUTE_STR) );
490     extents_v = XExtentsOfFontSet( a->volume.fontset );
491     extents   = XExtentsOfFontSet( p->fontset );
492 
493     p->loc.x = width - 15 - str_width;
494     p->loc.y = a->volume.loc.y +
495                extents_v->max_logical_extent.height / 2 +
496                extents->max_logical_extent.height   / 2;
497 }
498 
499 
TVANNOTSetDrawableSize(TV_ANNOT * a,Dimension width,Dimension height)500 void TVANNOTSetDrawableSize(
501          TV_ANNOT      *a,
502          Dimension      width,
503          Dimension      height )
504 {
505     Display *d = a->display;
506 
507     a->geom.w = width;
508     a->geom.h = height;
509 
510     TVANNOTStationPropSetDrawableSize  ( a, &a->station   , d, width, height );
511     TVANNOTTunerModePropSetDrawableSize( a, &a->tuner_mode, d, width, height );
512     TVANNOTInputDevPropSetDrawableSize ( a, &a->input_dev , d, width, height );
513     TVANNOTVolumePropSetDrawableSize   ( a, &a->volume    , d, width, height );
514     TVANNOTMutePropSetDrawableSize     ( a, &a->mute      , d, width, height );
515 }
516 
517 
518 /*---------------------------------------------------------------------------*/
519 
TVANNOTUpdateIsRequired(TV_ANNOT_PROP * p,struct timeval * cur_time)520 static TV_BOOL TVANNOTUpdateIsRequired(
521                    TV_ANNOT_PROP  *p,
522                    struct timeval *cur_time )
523 {
524     TV_BOOL  required;
525     TV_INT32 elapsed = -1;
526 
527     if (( p->delay > 0 ) && ( p->start_time.tv_sec >= 0 ))
528         elapsed = ( cur_time->tv_usec - p->start_time.tv_usec ) / 1000 +
529                   ( cur_time->tv_sec - p->start_time.tv_sec ) * 1000;
530 
531     required = ( p->delay < 0 ) ||
532                (( elapsed >= 0 ) && ( elapsed <= p->delay ));
533 
534     if ( !required )
535         p->start_time.tv_sec = -1;
536 
537     return required;
538 }
539 
TVANNOTDrawShadowedText(TV_ANNOT * a,TV_ANNOT_PROP * p,char str[])540 static void TVANNOTDrawShadowedText(
541                    TV_ANNOT       *a,
542                    TV_ANNOT_PROP  *p,
543                    char            str[] )
544 {
545     XGCValues        gcv;
546     XtGCMask         gcmask;
547 
548     gcmask         = GCForeground;
549     gcv.foreground = BlackPixel( a->display, a->screen );
550     XChangeGC( a->display, p->gc, gcmask, &gcv );
551 
552     XmbDrawString( a->display, a->drawable, p->fontset, p->gc,
553                    p->loc.x+1, p->loc.y+1, str, strlen(str) );
554 
555     gcv.foreground = p->fg_pixel;
556     XChangeGC( a->display, p->gc, gcmask, &gcv );
557 
558     XmbDrawString( a->display, a->drawable, p->fontset, p->gc,
559                    p->loc.x, p->loc.y, str, strlen(str) );
560 }
561 
562 
TVANNOTStationPropUpdate(TV_ANNOT * a,struct timeval * cur_time,TV_ANNOT_PROP * p)563 static TV_BOOL TVANNOTStationPropUpdate(
564                    TV_ANNOT       *a,
565                    struct timeval *cur_time,
566                    TV_ANNOT_PROP  *p )
567 {
568     TV_CAPTURE      *c = &G_glob.capture;
569     TV_DRIVER_STATE  state;
570     TV_STATION      *station;
571     char             chan_str[ 80 ];
572 
573     /*  First, see if we even need to display  */
574     if ( !TVANNOTUpdateIsRequired( p, cur_time ) )
575         return FALSE;
576 
577     /*  Paranoid sanity check  */
578     assert((p->gc != NULL ) && ( p->fontset != NULL ));
579 
580     /*  Query driver state (freq or channel) */
581     /*    FIXME:  Add CAPTURE interface to just query indiv params  */
582     if ( !TVCAPTUREQueryDriverState( c, &state ) ) {
583         fprintf( stderr, "TVCAPTUREQueryDriverState() failed\n" );
584         exit(1);
585     }
586 
587     /*  Figure out which station we're on (if any)  */
588     TVSTATIONLookup( &state, &station );
589 
590     /*  Format station text  */
591     if ( station != NULL ) {
592         if ( station->set_via_channel ) {
593             sprintf( chan_str, "%d", station->channel );
594             if ( strcmp( station->id, chan_str ) != 0 ) {
595                 if ( App_res.station_annot_idonly )
596                     sprintf( chan_str, "%s", station->id );
597                 else
598                     sprintf( chan_str, "%s (%d)", station->id,
599                                                   station->channel );
600             }
601         }
602         else
603             if ( App_res.station_annot_idonly )
604                 sprintf( chan_str, "%s", station->id );
605             else
606                 sprintf( chan_str, "%s (%.2f MHz)", station->id,
607                                                     station->freq );
608     }
609     else if ( state.tuner_chan_active )
610         sprintf( chan_str, "%ld", state.tuner_chan );
611     else
612         sprintf( chan_str, "%.2f MHz", state.tuner_freq );
613 
614     /*  And display it  */
615     TVANNOTDrawShadowedText( a, p, chan_str );
616 
617     return TRUE;
618 }
619 
620 
TVANNOTTunerModePropUpdate(TV_ANNOT * a,struct timeval * cur_time,TV_ANNOT_PROP * p)621 static TV_BOOL TVANNOTTunerModePropUpdate(
622                    TV_ANNOT       *a,
623                    struct timeval *cur_time,
624                    TV_ANNOT_PROP  *p )
625 {
626     TV_PREFS        *prefs = &G_glob.prefs;
627     char            *str;
628 
629     /*  First, see if we even need to display  */
630     if ( !TVANNOTUpdateIsRequired( p, cur_time ) )
631         return FALSE;
632 
633     /*  Paranoid sanity check  */
634     assert((p->gc != NULL ) && ( p->fontset != NULL ));
635 
636     /*  Format text  */
637     switch ( prefs->tuner_mode ) {
638         case TV_TUNER_MODE_ANTENNA : str = "ANTENNA";  break;
639         case TV_TUNER_MODE_CABLE   : str = "CABLE";    break;
640         default                    :
641             fprintf( stderr, "TVANNOTTunerModePropUpdate: Unknown mode\n" );
642             exit(1);
643     }
644 
645     /*  And display it  */
646     TVANNOTDrawShadowedText( a, p, str );
647 
648     return TRUE;
649 }
650 
651 
TVANNOTInputDevPropUpdate(TV_ANNOT * a,struct timeval * cur_time,TV_ANNOT_PROP * p)652 static TV_BOOL TVANNOTInputDevPropUpdate(
653                    TV_ANNOT       *a,
654                    struct timeval *cur_time,
655                    TV_ANNOT_PROP  *p )
656 {
657     TV_CAPTURE      *c = &G_glob.capture;
658     TV_DRIVER_STATE  state;
659     char            *str;
660 
661     /*  First, see if we even need to display  */
662     if ( !TVANNOTUpdateIsRequired( p, cur_time ) )
663         return FALSE;
664 
665     /*  Paranoid sanity check  */
666     assert((p->gc != NULL ) && ( p->fontset != NULL ));
667 
668     /*  Query driver state (input device) */
669     /*    FIXME:  Add CAPTURE interface to just query indiv params  */
670     if ( !TVCAPTUREQueryDriverState( c, &state ) ) {
671         fprintf( stderr, "TVCAPTUREQueryDriverState() failed\n" );
672         exit(1);
673     }
674 
675     /*  Format text  */
676     switch ( state.input_dev ) {
677         case TV_DEVICE_TUNER   : str = "TUNER IN"  ; break;
678         case TV_DEVICE_VIDEO   : str = "VIDEO IN"  ; break;
679         case TV_DEVICE_SVIDEO  : str = "SVIDEO IN" ; break;
680         case TV_DEVICE_CSVIDEO : str = "CSVIDEO IN"; break;
681         default :
682              fprintf( stderr, "TVANNOTInputDevPropUpdate: Bad device\n" );
683              exit(1);
684     }
685 
686     /*  And display it  */
687     TVANNOTDrawShadowedText( a, p, str );
688 
689     return TRUE;
690 }
691 
692 
TVANNOTVolumePropUpdate(TV_ANNOT * a,struct timeval * cur_time,TV_ANNOT_PROP * p)693 static TV_BOOL TVANNOTVolumePropUpdate(
694                    TV_ANNOT       *a,
695                    struct timeval *cur_time,
696                    TV_ANNOT_PROP  *p )
697 {
698     TV_INT32         vol;
699     char             str[80];
700 
701     /*  First, see if we even need to display  */
702     if ( !TVANNOTUpdateIsRequired( p, cur_time ) )
703         return FALSE;
704 
705     /*  Paranoid sanity check  */
706     assert((p->gc != NULL ) && ( p->fontset != NULL ));
707 
708     /*  Query current line volume  */
709     TVAUDIOGetLineVolume( &vol );
710 
711     /*  Format text  */
712     sprintf( str, VOLUME_FMT, vol );
713 
714     /*  And display it  */
715     TVANNOTDrawShadowedText( a, p, str );
716 
717     return TRUE;
718 }
719 
720 
TVANNOTMutePropUpdate(TV_ANNOT * a,struct timeval * cur_time,TV_ANNOT_PROP * p)721 static TV_BOOL TVANNOTMutePropUpdate(
722                    TV_ANNOT       *a,
723                    struct timeval *cur_time,
724                    TV_ANNOT_PROP  *p )
725 {
726     TV_BOOL          mute_on;
727     char            *str;
728 
729     /*  Query current mute state  */
730     TVAUDIOGetMuteState( &mute_on );
731 
732     /*  See if we even need to display  */
733     if ( !mute_on || !TVANNOTUpdateIsRequired( p, cur_time ) )
734         return FALSE;
735 
736     /*  Paranoid sanity check  */
737     assert((p->gc != NULL ) && ( p->fontset != NULL ));
738 
739     /*  Format text  */
740     str = mute_on ? MUTE_STR : "";
741 
742     /*  And display it  */
743     TVANNOTDrawShadowedText( a, p, str );
744 
745     return TRUE;
746 }
747 
748 
749 /**@BEGINFUNC**************************************************************
750 
751     Prototype  : TV_BOOL TVANNOTUpdate(
752                       TV_ANNOT *a )
753 
754     Purpose    : Give annotations a chance to update.  Returns TRUE if
755                  any annotations were active and thus needed to be
756                  drawn.
757 
758     Programmer : 05-Oct-97  Randall Hopper
759 
760     Parameters : a - I: annotation def
761 
762     Returns    : T = something was drawn; F = nothing drawn
763 
764     Globals    : None.
765 
766  **@ENDFUNC*****************************************************************/
767 
TVANNOTUpdate(TV_ANNOT * a)768 TV_BOOL TVANNOTUpdate(
769          TV_ANNOT *a )
770 {
771     struct timeval tv;
772     TV_BOOL        changed = FALSE,
773                    ch[5];
774 
775     /*  If drawable and its size haven't been set yet, no action  */
776     if (( a->drawable == None ) || ( a->geom.w < 0 ))
777         return FALSE;
778 
779     /*  Compute current time (in msec)  */
780     gettimeofday( &tv, NULL );
781 
782     /*  Update all annotation properties  */
783     ch[0] = TVANNOTStationPropUpdate  ( a, &tv, &a->station    );
784     ch[1] = TVANNOTTunerModePropUpdate( a, &tv, &a->tuner_mode );
785     ch[2] = TVANNOTInputDevPropUpdate ( a, &tv, &a->input_dev  );
786     ch[3] = TVANNOTVolumePropUpdate   ( a, &tv, &a->volume     );
787     ch[4] = TVANNOTMutePropUpdate     ( a, &tv, &a->mute       );
788 
789     changed = ch[0] || ch[1] || ch[2] || ch[3] || ch[4];
790 
791     return changed;
792 }
793 
794 /*---------------------------------------------------------------------------*/
795 
TVANNOTUpdateTimeoutCB(XtPointer cl_data,XtIntervalId * timer)796 static void TVANNOTUpdateTimeoutCB(
797          XtPointer          cl_data,
798          XtIntervalId      *timer )
799 {
800     TV_BOOL   changed;
801     TV_ANNOT *a = (TV_ANNOT *) cl_data;
802 
803     a->timer_set = FALSE;
804 
805     /*  If somebody killed auto-update mode, bail  */
806     if ( !a->auto_update )
807         return;
808 
809     changed = TVANNOTUpdate( a );
810 
811     /*  If anything changed, reregister our timer  */
812     if ( changed ) {
813         a->timer = XtAppAddTimeOut( a->app_context, AUTOMODE_TIMER_MS,
814                                     TVANNOTUpdateTimeoutCB, a );
815         a->timer_set = TRUE;
816     }
817 }
818 
819 
820 /**@BEGINFUNC**************************************************************
821 
822     Prototype  : void TVANNOTSetAutoUpdateMode(
823                       TV_ANNOT *a,
824                       TV_BOOL   enabled )
825 
826     Purpose    : The annotation module can be set in one of two different
827                  modes:
828 
829                  1) AutoUpdate OFF means the client has to call TVANNOTUpdate
830                  manually to add the annotation to the registered drawable
831                  If no annotation is pending, the call is a no-op.
832 
833                  2) AutoUpdate ON means that this module sets a timer and
834                  periodically calls TVANNOTUpdate itself.  The timer is
835                  only registered when annotation is pending.  After all
836                  annotation expires, the timer is canceled.
837 
838     Programmer : 05-Oct-97  Randall Hopper
839 
840     Parameters : a       - I: annotation def
841                  enabled - I: new state for auto-update mode
842 
843     Returns    : None.
844 
845     Globals    : None.
846 
847  **@ENDFUNC*****************************************************************/
848 
TVANNOTSetAutoUpdateMode(TV_ANNOT * a,TV_BOOL enabled)849 void TVANNOTSetAutoUpdateMode(
850          TV_ANNOT *a,
851          TV_BOOL   enabled )
852 {
853     enabled = (enabled != 0);
854 
855     if ( a->auto_update == enabled )
856         return;
857 
858     a->auto_update = enabled;
859 
860     /*  If leaving auto-update, kill timer  */
861     if ( !a->auto_update ) {
862         if ( a->timer_set ) {
863             XtRemoveTimeOut( a->timer );
864             a->timer_set = FALSE;
865         }
866     }
867 
868     /*  Else entering auto-update; start timer  */
869     else {
870         a->timer = XtAppAddTimeOut( a->app_context, AUTOMODE_TIMER_MS,
871                                     TVANNOTUpdateTimeoutCB, a );
872         a->timer_set = TRUE;
873     }
874 }
875 
876 
877 /*---------------------------------------------------------------------------*/
878 
TVANNOTSignalPropChange(TV_ANNOT * a,TV_ANNOT_TYPE type)879 void TVANNOTSignalPropChange(
880          TV_ANNOT      *a,
881          TV_ANNOT_TYPE  type )
882 {
883     struct timeval  tv;
884     TV_ANNOT_PROP  *p;
885 
886     gettimeofday( &tv, NULL );
887 
888     switch ( type ) {
889         case TV_ANNOT_TYPE_STATION    :  p = &a->station   ;  break;
890         case TV_ANNOT_TYPE_TUNER_MODE :  p = &a->tuner_mode;  break;
891         case TV_ANNOT_TYPE_INPUT_DEV  :  p = &a->input_dev ;  break;
892         case TV_ANNOT_TYPE_VOLUME     :  p = &a->volume    ;  break;
893         case TV_ANNOT_TYPE_MUTE       :  p = &a->mute      ;  break;
894         default :
895             fprintf( stderr, "TVANNOTSignalPropChange: Bad type\n" );
896             exit(1);
897     }
898 
899     memcpy( &p->start_time, &tv, sizeof(tv) );
900 
901     /*  If we're in auto-update mode, start the auto-update timer  */
902     if ( a->auto_update && !a->timer_set ) {
903         a->timer = XtAppAddTimeOut( a->app_context, AUTOMODE_TIMER_MS,
904                                     TVANNOTUpdateTimeoutCB, a );
905         a->timer_set = TRUE;
906     }
907 }
908 
909