1/*
2 * Broadcast.c --
3 *
4 *      This module implements a "broadcast" widget that is object
5 *      based. It is part of the QuickTimeTcl package.
6 *      It provides an interface to the (ill docoumented) QuickTime broadcaster
7 *      API's. This includes the Presentation and Sourcer classes of APIs.
8 *
9 * Copyright (c) 2003 Mats Bengtsson
10 *
11 * $Id: Broadcast.c,v 1.8 2008/02/15 15:23:05 matben Exp $
12 */
13
14#include "Broadcast.h"
15
16/*
17 * We need a (single) linked list for our broadcasters.
18 */
19
20static MovieBroadcastList  *gMovieBroadcastListPtr = NULL;
21
22static GWorldPtr    gOffscreenGWorldPtr = NULL;
23
24#if TARGET_API_MAC_CARBON
25EventLoopTimerRef   gCarbonPresentationTimerRef = NULL;
26const EventTime 	kCarbonPresentationTimerSecs = kEventDurationSecond / 10;
27//long				kMinimumIdleDurationInMillis = 20;
28#endif
29
30static char *broadcastCommands[] = {
31    "audioconfigure",
32    "cget",
33    "configure",
34    "export",
35    "getsettings",
36    "getstatistics",
37    "pause",
38    "picture",
39    "preview",
40    "settingsdialog",
41    "skipto",
42    "sourcer",
43    "start",
44    "stop",
45    "stream",
46    "videoconfigure",
47    (char *) NULL
48};
49
50enum {
51    kBcastCmdAudioConfigure     = 0L,
52    kBcastCmdCget,
53    kBcastCmdConfigure,
54    kBcastCmdExport,
55    kBcastCmdGetSettings,
56    kBcastCmdGetStatistics,
57    kBcastCmdPause,
58    kBcastCmdPicture,
59    kBcastCmdPreview,
60    kBcastCmdSettingsDialog,
61    kBcastCmdSkipTo,
62    kBcastCmdSourcer,
63    kBcastCmdStart,
64    kBcastCmdStop,
65    kBcastCmdStream,
66    kBcastCmdVideoConfigure
67};
68
69static char *sourcerCommands[] = {
70    "add", "info", "remove",
71    (char *) NULL
72};
73
74enum {
75    kSourcerCmdAdd          = 0L,
76    kSourcerCmdInfo,
77    kSourcerCmdRemove
78};
79
80static char *videoConfigOptions[] = {
81    "-brightness", "-contrast", "-hue",
82    "-saturation", "-sharpness",
83    (char *) NULL
84};
85
86enum {
87    kVideoConfigOptionBrightness    = 0L,
88    kVideoConfigOptionContrast,
89    kVideoConfigOptionHue,
90    kVideoConfigOptionSaturation,
91    kVideoConfigOptionSharpness
92};
93
94static char *audioConfigOptions[] = {
95    "-autogain", "-gain",
96    (char *) NULL
97};
98
99enum {
100    kAudioConfigOptionAutoGainOnOff    = 0L,
101    kAudioConfigOptionGain
102};
103
104/*
105 * Strings for the partial parsing of SDP data.
106 */
107
108#define kSDPAudioDefString      "m=audio"
109#define kSDPVideoDefString      "m=video"
110
111/*
112 * Information used for parsing configuration options.
113 * Mask bits for options changed.
114 */
115
116enum {
117    BCAST_CONF_NEWGWORLD              	= (1L << 0)
118};
119
120/*
121 * The following table defines the legal values for the -size option.
122 */
123
124static CONST char *widgetSizeST[] = {
125	"quarter", "half", "full", (char *) NULL
126};
127#define	BCAST_WIDGET_SIZE_QUARTER 		0
128#define	BCAST_WIDGET_SIZE_HALF			1
129#define	BCAST_WIDGET_SIZE_FULL			2
130
131/*
132 * Information used for objv parsing.
133 */
134
135static Tk_OptionSpec optionSpecs[] = {
136	{TK_OPTION_STRING, "-command", "command", "Command",
137		NULL, -1, Tk_Offset(MovieBroadcast, command), TK_OPTION_NULL_OK,
138		(ClientData) NULL, 0},
139	{TK_OPTION_PIXELS, "-height", "height", "Height",
140		"0", -1, Tk_Offset(MovieBroadcast, height), 0,
141		(ClientData) NULL, BCAST_CONF_NEWGWORLD},
142	{TK_OPTION_STRING_TABLE, "-size", "size", "Size",
143		"half", -1, Tk_Offset(MovieBroadcast, indSize), 0,
144		(ClientData) widgetSizeST, BCAST_CONF_NEWGWORLD},
145	{TK_OPTION_PIXELS, "-width", "width", "Width",
146		"0", -1, Tk_Offset(MovieBroadcast, width), 0,
147		(ClientData) NULL, BCAST_CONF_NEWGWORLD},
148    {TK_OPTION_END}
149};
150
151/*
152 * Forward declarations for procedures defined later in this file:
153 */
154
155static void		BroadcastDeletedProc( ClientData clientData );
156static int		BroadcastConfigure( Tcl_Interp *interp,
157        			    MovieBroadcast *bcastPtr, int objc,
158                        Tcl_Obj *CONST objv[] );
159static void     BroadcastWorldChanged( ClientData clientData );
160static int      BroadcastComputeGeometry( MovieBroadcast *bcastPtr,
161                        int *width, int *height );
162static void		DisplayBroadcast( ClientData clientData );
163static void     DrawBluescreen( MovieBroadcast *bcastPtr );
164static void		DestroyBroadcast( ClientData clientData );
165static void		BroadcastEventProc( ClientData clientData,
166        			    XEvent *eventPtr );
167static int		BroadcastWidgetObjCmd( ClientData clientData,
168        			    Tcl_Interp *, int objc, Tcl_Obj * CONST objv[] );
169static ComponentResult PresentationNotification( ComponentResult inErr,
170                        OSType inNotificationType,
171	                    void *inNotificationParams, void *inRefCon );
172static ComponentResult HandleErrorNotification( MovieBroadcast *bcastPtr,
173                	    const char *inNotificationTypeString,
174                		ComponentResult inErr,
175                		QTSErrorParams *inErrorParams );
176static int      GetSDPFromFile( MovieBroadcast *bcastPtr,
177                        Tcl_Obj *fileNamePtr );
178static int      GetSDPFromURL( MovieBroadcast *bcastPtr,
179                        Tcl_Obj *urlPtr );
180static int      GetSDPFromData( MovieBroadcast *bcastPtr,
181                        Tcl_Obj *sdpListPtr );
182static void     AddToBroadcastList( MovieBroadcast *bcastPtr );
183static void     RemoveFromBroadcastList( MovieBroadcast *bcastPtr );
184static void     IdleAllPresentations( void );
185static void     IdleSourcersForPresentation( MovieBroadcast *bcastPtr );
186static int      PresentationExportToFile( MovieBroadcast *bcastPtr,
187	                    int objc, Tcl_Obj *CONST objv[] );
188static int      SourcerObjCmd( Tcl_Interp *interp,
189                        MovieBroadcast *movPtr,
190	                    int objc, Tcl_Obj *CONST objv[] );
191static int      StreamObjCmd( Tcl_Interp *interp,
192                        MovieBroadcast *bcastPtr,
193	                    int objc, Tcl_Obj *CONST objv[] );
194static void     GetAllStreamMediaTypes( QTSPresentation  presentation,
195                        int *haveAudio, int *haveVideo, int *haveText );
196static int      SourcerGetInfo( Tcl_Interp *interp,
197                        MovieBroadcast *bcastPtr, QTSStream inStream,
198	                    int objc, Tcl_Obj *CONST objv[] );
199static OSErr    GetSourcerType( QTSSourcer sourcer, OSType *typePtr );
200static OSErr    GetSeqGrabberFullInputRect( QTSPresentation	presentation,
201                        int *fullWidth, int *fullHeight );
202static int      PictureObjCmd( Tcl_Interp *interp,
203                        MovieBroadcast *bcastPtr,
204	                    int objc, Tcl_Obj *CONST objv[] );
205static int      SetupTrackSourcerForStream( MovieBroadcast *bcastPtr,
206                        QTSStream inStream );
207static void     PresentationFree( MovieBroadcast *bcastPtr );
208static void     SourcerFree( BcastSourcerInfo *sourcerPtr );
209static pascal ComponentResult MovieSourcingCallbackProc(
210                        ComponentResult inErr, OSType inSelector,
211		                void  *ioParams, void *inRefCon );
212void            QTSDisposeMediaParams( QTSMediaParams *inMediaParams );
213static pascal Boolean   MyModalFilterDialogProc( DialogPtr dialogPtr,
214                        EventRecord * event, SInt16 *itemHit, long *refCon );
215
216#if TARGET_API_MAC_CARBON
217static OSStatus	InstallBroadcastIdlingEventLoopTimer( void );
218static pascal void	PresentationCarbonTimer( EventLoopTimerRef theTimer,
219                        void *userData );
220#endif
221
222/*
223 *--------------------------------------------------------------
224 *
225 * BroadcastCmd --
226 *
227 *	    This procedure is invoked to process the "qtbroadcast" Tcl
228 *	    command.  It creates a new "qtbroadcast" widget.
229 *
230 * Results:
231 *	    A standard Tcl result.
232 *
233 * Side effects:
234 *	    A new widget is created and configured.
235 *
236 *--------------------------------------------------------------
237 */
238
239int
240BroadcastObjCmd(
241        ClientData  clientData,	    /* NULL. */
242        Tcl_Interp  *interp,		/* Current interpreter. */
243        int         objc,		    /* Number of arguments. */
244        Tcl_Obj     *CONST objv[] )	/* Argument objects. */
245{
246    MovieBroadcast              *bcastPtr;
247    long                        eventFlags = 0L;
248    Tk_Window                   tkwin;
249    Tk_OptionTable              optionTable;
250	static int  	            isInitialized = false;
251    OSErr                       err = noErr;
252
253    if (objc < 3) {
254    	Tcl_WrongNumArgs( interp, 1, objv, "pathName sdpFile ?options?" );
255    	return TCL_ERROR;
256    }
257
258    /*
259     * We use one and the same offscreen GWorld for all movies.
260     */
261
262    if (gOffscreenGWorldPtr == NULL) {
263        Rect    aRect = { 0, 0, 10, 10 };   /* Just a dummy rect. */
264
265	    err = MySafeNewGWorld( &gOffscreenGWorldPtr, 32, &aRect, NULL, NULL, 0 );
266	    if (err != noErr) {
267	        CheckAndSetErrorResult( interp, err );
268			return TCL_ERROR;
269	    }
270    }
271
272    if (!isInitialized) {
273        //err = InitializeQTS();
274        if (err != noErr) {
275
276            return TCL_ERROR;
277        }
278	    isInitialized = true;
279
280        QTTclDebugPrintf( interp, 2,
281                "FocusIn=%2d FocusOut=%2d Expose=%2d DestroyNotify=%2d UnmapNotify=%2d MapNotify=%2d ConfigureNotify=%2d",
282                FocusIn, FocusOut, Expose, DestroyNotify, UnmapNotify, MapNotify, ConfigureNotify );
283    }
284
285    tkwin = Tk_CreateWindowFromPath( interp, Tk_MainWindow( interp ),
286    	    Tcl_GetStringFromObj( objv[1], NULL ), (char *) NULL );
287    if (tkwin == NULL) {
288    	return TCL_ERROR;
289    }
290    Tk_SetClass( tkwin, "QTBroadcast" );
291
292    /*
293     * Create the option table for this widget class.  If it has
294     * already been created, the refcount will get bumped and just
295     * the pointer will be returned.  The refcount getting bumped
296     * does not concern us, because Tk will ensure the table is
297     * deleted when the interpreter is destroyed.
298     */
299
300    optionTable = Tk_CreateOptionTable( interp, optionSpecs );
301
302    /*
303     * Allocate and initialize the widget record.  The memset allows
304     * us to set just the non-NULL/0 items.
305     */
306
307    bcastPtr = (MovieBroadcast *) ckalloc( sizeof(MovieBroadcast) );
308    memset( (void *) bcastPtr, 0, sizeof(MovieBroadcast) );
309
310    bcastPtr->tkwin = tkwin;
311    bcastPtr->display = Tk_Display( tkwin );
312    bcastPtr->interp = interp;
313    bcastPtr->widgetCmd	= Tcl_CreateObjCommand( interp,
314	    Tk_PathName( bcastPtr->tkwin ), BroadcastWidgetObjCmd,
315	    (ClientData) bcastPtr, BroadcastDeletedProc );
316    bcastPtr->optionTable = optionTable;
317
318    if (Tk_InitOptions( interp, (char *) bcastPtr, optionTable, tkwin)
319    	    != TCL_OK) {
320    	Tk_DestroyWindow( bcastPtr->tkwin );
321    	ckfree( (char *) bcastPtr );
322    	return TCL_ERROR;
323    }
324    bcastPtr->srcWidth = 320;
325    bcastPtr->srcHeight = 240;
326    bcastPtr->command = NULL;
327    bcastPtr->grafPtr = NULL;
328    bcastPtr->presentation = kQTSInvalidPresentation;
329
330    if (GetSDPFromFile( bcastPtr, objv[2] ) != TCL_OK) {
331    	goto error;
332    }
333    bcastPtr->state = kPresentationStateIdle;
334    bcastPtr->targetState = kPresentationStateIdle;
335
336    GetAllStreamMediaTypes( bcastPtr->presentation,
337            &bcastPtr->haveAudioStream, &bcastPtr->haveVideoStream,
338            &bcastPtr->haveTextStream );
339
340    eventFlags = ExposureMask | StructureNotifyMask;
341    Tk_CreateEventHandler( bcastPtr->tkwin, eventFlags,
342    	    BroadcastEventProc, (ClientData) bcastPtr );
343
344    if (BroadcastConfigure( interp, bcastPtr, objc - 3, objv + 3) != TCL_OK) {
345    	goto error;
346    }
347
348	bcastPtr->flags |= NEWGWORLD;
349	BroadcastWorldChanged( (ClientData) bcastPtr );
350	AddToBroadcastList( bcastPtr );
351
352#if TARGET_API_MAC_CARBON
353    /*
354        *  If this is the first presentation we open on Carbon, be sure to set a timer
355        *  to serv it and all that may follow.
356        */
357
358    if (gMovieBroadcastListPtr == NULL) {
359        InstallBroadcastIdlingEventLoopTimer();
360    }
361#endif
362
363    Tcl_SetObjResult( interp,
364    	    Tcl_NewStringObj( Tk_PathName( bcastPtr->tkwin ), -1) );
365    return TCL_OK;
366
367error:
368
369    Tk_DestroyWindow( bcastPtr->tkwin );
370    return TCL_ERROR;
371}
372
373/*
374 *--------------------------------------------------------------
375 *
376 * BroadcastWidgetObjCmd --
377 *
378 *	    This procedure is invoked to process the Tcl command
379 *	    that corresponds to a widget managed by this module.
380 *	    See the user documentation for details on what it does.
381 *
382 * Results:
383 *	    A standard Tcl result.
384 *
385 * Side effects:
386 *	    See the user documentation.
387 *
388 *--------------------------------------------------------------
389 */
390
391static int
392BroadcastWidgetObjCmd(
393        ClientData  clientData,		/* Information about square widget. */
394        Tcl_Interp  *interp,		/* Current interpreter. */
395        int         objc,		    /* Number of arguments. */
396        Tcl_Obj     *CONST objv[] ) /* Argument objects. */
397{
398    //MovieBroadcast  *bcastPtr = (MovieBroadcast *) clientData;
399    MovieBroadcast  *bcastPtr;
400    Tcl_Obj         *resultObjPtr;
401    int             index;
402    OSErr           err = noErr;
403    int             result = TCL_OK;
404
405    bcastPtr = (MovieBroadcast *) clientData;
406
407	QTTclDebugPrintf( bcastPtr->interp, 2,
408	        "BroadcastWidgetObjCmd: targetState=%2d, state=%2d",
409	        bcastPtr->targetState, bcastPtr->state );
410
411    if (objc < 2) {
412    	Tcl_WrongNumArgs( interp, 1, objv, "option ?arg arg...?" );
413    	return TCL_ERROR;
414    }
415   	if (bcastPtr->presentation == kQTSInvalidPresentation)  {
416        Tcl_SetObjResult( interp,
417                Tcl_NewStringObj( "Invalid presentation", -1) );
418        goto error;
419   	}
420    if (Tcl_GetIndexFromObj( interp, objv[1], broadcastCommands, "command",
421    	    0, &index ) != TCL_OK) {
422    	return TCL_ERROR;
423    }
424
425    Tcl_Preserve( (ClientData) bcastPtr );
426
427    switch (index) {
428
429        case kBcastCmdAudioConfigure: {
430
431
432            break;
433        }
434
435    	case kBcastCmdCget: {
436    	    if (objc != 3) {
437        		Tcl_WrongNumArgs( interp, 2, objv, "option" );
438        		goto error;
439    	    }
440    	    resultObjPtr = Tk_GetOptionValue( interp, (char *) bcastPtr,
441    		        bcastPtr->optionTable, objv[2], bcastPtr->tkwin );
442    	    if (resultObjPtr == NULL) {
443        		result = TCL_ERROR;
444    	    } else {
445        		Tcl_SetObjResult( interp, resultObjPtr );
446    	    }
447    	    break;
448    	}
449
450    	case kBcastCmdConfigure: {
451    	    resultObjPtr = NULL;
452			if (objc <= 3) {
453				resultObjPtr = Tk_GetOptionInfo( interp, (char *) bcastPtr,
454					bcastPtr->optionTable,
455					(objc == 2) ? (Tcl_Obj *) NULL : objv[2],
456					bcastPtr->tkwin );
457				if (resultObjPtr == NULL) {
458					result = TCL_ERROR;
459				} else {
460					Tcl_SetObjResult( interp, resultObjPtr );
461				}
462			} else {
463
464    			/*
465    			 * Change one or more attributes. Reschedule a new display via
466    			 * 'BroadcastWorldChanged'. Be sure to only set the objv values by the flag.
467    			 * The NEWGWORLD bit in flags is set if we need a redisplay.
468    			 */
469
470    			result = BroadcastConfigure( interp, bcastPtr, objc - 2, objv + 2 );
471
472                /*
473                 * Only if we made a configuration that needs a redisplay.
474                 */
475
476    			if ((result == TCL_OK) && (bcastPtr->flags & NEWGWORLD)) {
477    				BroadcastWorldChanged( (ClientData) bcastPtr );
478    			}
479			}
480			break;
481        }
482
483        case kBcastCmdExport: {
484            if (PresentationExportToFile( bcastPtr, objc - 2, objv + 2 )
485                    != TCL_OK) {
486                goto error;
487            }
488            break;
489        }
490
491        case kBcastCmdGetSettings: {
492          	char 			nul = 0;
493          	Ptr 			p = NULL;
494            Handle          outTextHand = NULL;
495        	Tcl_DString     ds;
496
497            //  kQTSSettingsTextSummary
498            //  kQTSSettingsTextDetails
499            err = QTSPresGetSettingsAsText( bcastPtr->presentation,
500                    kQTSAllStreams, 0, kQTSSettingsTextDetails,
501                    &outTextHand, NULL, NULL );
502            if (err != noErr) {
503
504                goto error;
505            }
506            HLock( outTextHand );
507
508	        /* null-terminate the string in the handle */
509	   		PtrAndHand( &nul, outTextHand, 1 );
510     		p = *outTextHand;
511     		while (*p) {
512           		if (*p == kReturnChar) {
513           			*p = kNewlineChar;
514           		}
515           		p++;
516     		};
517            Tcl_ExternalToUtfDString( gQTTclTranslationEncoding, *outTextHand,
518                    -1, &ds );
519            Tcl_SetObjResult( interp, Tcl_NewStringObj(
520               		Tcl_DStringValue(&ds), -1 ) );
521            HUnlock( outTextHand );
522            Tcl_DStringFree( &ds );
523            break;
524        }
525
526        case kBcastCmdGetStatistics: {
527
528
529
530            break;
531        }
532
533        case kBcastCmdPause: {
534        	err = QTSPresStop( bcastPtr->presentation, kQTSAllStreams, 0L );
535        	if (err != noErr) {
536            	err = QTSPresPreview( bcastPtr->presentation, kQTSAllStreams,
537                        NULL, kQTSNormalForwardRate, 0 );
538            }
539        	if (err != noErr) {
540
541                result = TCL_ERROR;
542            }
543        	bcastPtr->targetState = kPresentationStateIdle;
544            break;
545        }
546        case kBcastCmdPicture: {
547            if (PictureObjCmd( interp, bcastPtr, objc - 2, objv + 2 )
548                    != TCL_OK) {
549
550                goto error;
551            }
552            break;
553        }
554
555        case kBcastCmdPreview: {
556            Fixed       rate;
557            int         doStart = true;
558
559            if (objc == 2) {
560                rate = kQTSNormalForwardRate;
561            } else if (objc == 3) {
562				if (Tcl_GetBooleanFromObj( interp, objv[2], &doStart )
563				        != TCL_OK) {
564					Tcl_AddErrorInfo( interp,
565							"\n	(processing preview command)" );
566					result = TCL_ERROR;
567					goto error;
568				}
569				if (doStart) {
570                    rate = kQTSNormalForwardRate;
571				} else {
572                    rate = kQTSStoppedRate;
573                }
574            } else {
575            	Tcl_WrongNumArgs( interp, 2, objv, "?boolean?" );
576            	return TCL_ERROR;
577            }
578            if (doStart) {
579            	if (bcastPtr->state >= kPresentationStateStartingPreview) {
580
581            		/* we're already playing or trying to start */
582                    goto error;
583            	}
584            	bcastPtr->targetState = kPresentationStatePreviewing;
585            	bcastPtr->state = kPresentationStateStartingPreview;
586        	} else {
587            	bcastPtr->targetState = kPresentationStateIdle;
588        	}
589            err = QTSPresPreview( bcastPtr->presentation, kQTSAllStreams, NULL,
590                    rate, 0 );
591
592            break;
593        }
594
595        case kBcastCmdSettingsDialog: {
596            SInt32      flags = 0L;
597
598            err = QTSPresSettingsDialog( bcastPtr->presentation,
599                    kQTSAllStreams, flags,
600                    NewQTSModalFilterUPP(MyModalFilterDialogProc), bcastPtr );
601
602            break;
603        }
604
605        case kBcastCmdSkipTo: {
606            TimeValue64     timeValue64;
607
608            if (objc < 3) {
609            	Tcl_WrongNumArgs( interp, 2, objv, "?time?" );
610            	return TCL_ERROR;
611            }
612            // get TimeValue64 from string?????????
613
614            err = QTSPresSkipTo64( bcastPtr->presentation, &timeValue64 );
615            break;
616        }
617
618        case kBcastCmdSourcer: {
619            if (objc < 3) {
620            	Tcl_WrongNumArgs( interp, 2, objv, "command ?arg arg...?" );
621            	return TCL_ERROR;
622            }
623            if (SourcerObjCmd( interp, bcastPtr, objc - 2, objv + 2 )
624                    != TCL_OK) {
625
626                goto error;
627            }
628            break;
629        }
630
631        case kBcastCmdStart: {
632        	if (bcastPtr->state >= kPresentationStateStarting)  {
633
634        		/* we're already playing or trying to start */
635                goto error;
636        	}
637
638        	/* Right now, we have to preroll before we start. */
639        	bcastPtr->targetState = kPresentationStatePlaying;
640        	bcastPtr->state = kPresentationStateStartingPreroll;
641        	err = QTSPresPreroll( bcastPtr->presentation, kQTSAllStreams,
642                    0, kQTSNormalForwardRate, 0L );
643            if (err != noErr) {
644
645                goto error;
646            }
647            break;
648        }
649
650        case kBcastCmdStop: {
651        	bcastPtr->targetState = kPresentationStateIdle;
652        	if (bcastPtr->state != kPresentationStateIdle)  {
653        		bcastPtr->state = kPresentationStateIdle;
654        		err = QTSPresStop( bcastPtr->presentation, kQTSAllStreams, 0L );
655                if (err != noErr) {
656
657                    goto error;
658                }
659        	}
660
661
662            break;
663        }
664
665        case kBcastCmdStream: {
666            if (StreamObjCmd( interp, bcastPtr, objc - 2, objv + 2 )
667                    != TCL_OK) {
668
669                goto error;
670            }
671
672            break;
673        }
674
675        case kBcastCmdVideoConfigure: {
676
677
678            break;
679        }
680    }
681    Tcl_Release( (ClientData) bcastPtr );
682    return result;
683
684error:
685    Tcl_Release( (ClientData) bcastPtr );
686    return TCL_ERROR;
687}
688
689/*
690 *----------------------------------------------------------------------
691 *
692 * BroadcastConfigure --
693 *
694 *	    This procedure is called to process an objv/objc list in
695 *	    conjunction with the Tk option database to configure (or
696 *	    reconfigure) a square widget.
697 *
698 * Results:
699 *	    The return value is a standard Tcl result.  If TCL_ERROR is
700 *	    returned, then the interp's result contains an error message.
701 *
702 * Side effects:
703 *	    Configuration information, such as colors, border width,
704 *	    etc. get set for bcastPtr;  old resources get freed,
705 *	    if there were any.
706 *
707 *----------------------------------------------------------------------
708 */
709
710static int
711BroadcastConfigure(
712        Tcl_Interp  *interp,	    /* Used for error reporting. */
713        MovieBroadcast *bcastPtr, 	/* Information about widget. */
714        int         objc,
715        Tcl_Obj     *CONST objv[] )
716{
717    int 				ierror;
718	int 				mask = 0L;
719    int                 width, height;
720	Tk_SavedOptions 	savedOptions;
721	Tcl_Obj 			*resultObjPtr = NULL;
722    Tcl_Obj 			*errorResult = NULL;
723	OSErr				err = noErr;
724
725	QTTclDebugPrintf( bcastPtr->interp, 2, "BroadcastConfigure" );
726
727    /*
728     * The following loop is potentially executed twice.  During the
729     * first pass configuration options get set to their new values.
730     * If there is an error in this pass, we execute a second pass
731     * to restore all the options to their previous values.
732     *
733     * A 'continue' within this loop signals an error condition;
734     * 'break' when everything went OK.
735     */
736
737    for (ierror = 0; ierror <= 1; ierror++) {
738		if (!ierror) {
739		    /*
740		     * First pass: set options to new values.
741		     */
742
743			if (Tk_SetOptions( interp, (char *) bcastPtr, bcastPtr->optionTable,
744                    objc, objv, bcastPtr->tkwin, &savedOptions, &mask)
745                        != TCL_OK ) {
746				continue;
747		    }
748		} else {
749		    /*
750		     * Second pass: restore options to old values.
751		     */
752
753		    errorResult = Tcl_GetObjResult( interp );
754		    Tcl_IncrRefCount( errorResult );
755		    Tk_RestoreSavedOptions( &savedOptions );
756		}
757
758		if (mask & BCAST_CONF_NEWGWORLD) {
759		    if (BroadcastComputeGeometry( bcastPtr, &width, &height ) != TCL_OK) {
760				continue;
761	        }
762            err = QTSPresSetDimensions( bcastPtr->presentation,
763                    kQTSAllStreams, Long2Fix( width ), Long2Fix( height ) );
764		    bcastPtr->flags |= NEWGWORLD;
765		}
766
767
768
769
770		/*
771		 * If we came so far break out of the ierror loop.
772		 */
773
774		break;
775    }
776    if (ierror) {
777		Tcl_SetObjResult( interp, errorResult );
778		Tcl_DecrRefCount( errorResult );
779		return TCL_ERROR;
780    } else {
781		Tk_FreeSavedOptions( &savedOptions );
782		return TCL_OK;
783    }
784}
785
786/*
787 *----------------------------------------------------------------------
788 *
789 * BroadcastWorldChanged --
790 *
791 *		Something changed, arrange for the widget to be redisplayed.
792 *      Compute geometry.
793 *
794 * Results:
795 *		None.
796 *
797 * Side effects:
798 *		Broadcast Widget displayed: if already on display it is scheduled for
799 *		a renewed display, else, the size is requested by the tk geometry manager,
800 *		and displayed upon a MapNotify event.
801 *
802 *----------------------------------------------------------------------
803 */
804
805static void
806BroadcastWorldChanged( ClientData clientData )
807{
808    MovieBroadcast  *bcastPtr = (MovieBroadcast *) clientData;
809	int 			width, height;
810
811	QTTclDebugPrintf( bcastPtr->interp, 2, "BroadcastConfigure" );
812
813	/*
814	 * If not already scheduled for (re)display, it should be if it's mapped,
815	 * else upon a MapNotify event.
816	 */
817
818	if (Tk_IsMapped(bcastPtr->tkwin) && !(bcastPtr->flags & REDRAW_PENDING)) {
819		Tcl_DoWhenIdle( DisplayBroadcast, (ClientData) bcastPtr );
820		bcastPtr->flags |= REDRAW_PENDING;
821	}
822
823	/*
824	 * Get the desired width and height to request.
825	 */
826
827    BroadcastComputeGeometry( bcastPtr, &width, &height );
828
829	if (bcastPtr->tkwin != NULL) {
830
831		/*
832		 * After getting our geometry above, let tk also know.
833		 */
834
835		Tk_GeometryRequest( bcastPtr->tkwin, width, height );
836		Tk_SetInternalBorder( bcastPtr->tkwin, 0 );
837		bcastPtr->flags |= NEWGWORLD;
838	}
839}
840
841/*
842 *----------------------------------------------------------------------
843 *
844 * BroadcastComputeGeometry --
845 *
846 *		Finds the widget size to request at tk.
847 *
848 * Results:
849 *		The return value is a standard Tcl result. If TCL_ERROR is
850 *		returned, then the interp's result contains an error message.
851 *
852 * Side effects:
853 *		Returns width and height in function arguments. Note that these
854 *		are the actual width and height to request from tk, and not the
855 *		options.
856 *
857 *----------------------------------------------------------------------
858 */
859
860static int
861BroadcastComputeGeometry(
862        MovieBroadcast *bcastPtr,
863        int *width, int *height )
864{
865    Tcl_Interp 	    *interp = bcastPtr->interp;
866    int             divisor;
867    int             srcWidth = 0, srcHeight = 0;
868	double		    goldenRatio;
869	OSErr           err = noErr;
870
871	QTTclDebugPrintf( bcastPtr->interp, 2, "BroadcastComputeGeometry" );
872
873	*width = 0;
874	*height = 0;
875
876    err = GetSeqGrabberFullInputRect( bcastPtr->presentation,
877            &srcWidth, &srcHeight );
878    if (err == noErr) {
879    	bcastPtr->srcWidth = srcWidth;
880    	bcastPtr->srcHeight = srcHeight;
881    	QTTclDebugPrintf( bcastPtr->interp, 2,
882    	        "\tsrcWidth=%d, srcHeight=%d", srcWidth, srcHeight );
883    }
884
885	/*
886	 * There are two possibilities here: either we have got one of
887	 * '-width' or '-height' > 0; use these in this case. Or use the
888	 * '-size' option (quarter, half, or full).
889	 */
890
891	if ((bcastPtr->width > 0) || (bcastPtr->height > 0)) {
892		goldenRatio = (double) bcastPtr->srcWidth/(double) bcastPtr->srcHeight;
893		if ((bcastPtr->width > 0) && (bcastPtr->height == 0)) {
894			*width = bcastPtr->width;
895			*height = (int) ((double) bcastPtr->width/goldenRatio);
896		} else if ((bcastPtr->width == 0) && (bcastPtr->height > 0)) {
897			*height = bcastPtr->height;
898			*width = (int) (goldenRatio * bcastPtr->height);
899		} else {
900
901			/* This code should never be executed; my QuickCam becomes weired! */
902			*width = bcastPtr->width;
903			*height = bcastPtr->height;
904		}
905
906		/*
907		 * Check max source size.
908		 */
909
910		if ((bcastPtr->width > bcastPtr->srcWidth) ||
911				(bcastPtr->height > bcastPtr->srcHeight)) {
912			bcastPtr->width = bcastPtr->srcWidth;
913			*width =  bcastPtr->srcWidth;
914			bcastPtr->height = bcastPtr->srcHeight;
915			*height =  bcastPtr->srcHeight;
916		}
917	} else {
918		switch (bcastPtr->indSize) {
919			case BCAST_WIDGET_SIZE_QUARTER:
920				divisor = 4;
921				break;
922			case BCAST_WIDGET_SIZE_HALF:
923				divisor = 2;
924				break;
925			case BCAST_WIDGET_SIZE_FULL:
926				divisor = 1;
927				break;
928		}
929		*width = bcastPtr->srcWidth/divisor;
930		*height = bcastPtr->srcHeight/divisor;
931	}
932	QTTclDebugPrintf( bcastPtr->interp, 2, "\twidth=%d, height=%d",
933	        *width, *height );
934
935    return TCL_OK;
936}
937
938/*
939 *--------------------------------------------------------------
940 *
941 * GetSDPFromFile --
942 *
943 *      Fills in the necessary elements necessary for getting
944 *      settings from an SDP file.
945 *
946 * Results:
947 *	    Standard TCL result.
948 *
949 * Side effects:
950 *      None.
951 *
952 *--------------------------------------------------------------
953 */
954
955static int
956GetSDPFromFile(
957        MovieBroadcast *bcastPtr,
958        Tcl_Obj *fileNamePtr )
959{
960    Tcl_Interp      *interp = bcastPtr->interp;
961   	Tcl_Channel     readChannel = NULL;
962   	Tcl_Obj         *readObj = Tcl_NewObj();
963    unsigned char   *contentPtr;
964   	int             nread;
965   	int             len;
966	QTSPresentation presentation = kQTSInvalidPresentation;
967	QTSPresParams	presParams;
968	QTSMediaParams	mediaParams;
969    FSSpec          fsSpec;
970    OSErr           err = noErr;
971   	int             result = TCL_OK;
972
973    err = QTTclNativePathNameToFSSpec( interp,
974            Tcl_GetString( fileNamePtr ), &fsSpec );
975    if (err != noErr) {
976		Tcl_SetObjResult( bcastPtr->interp, Tcl_NewStringObj(
977				"File not found", -1 ) );
978        return TCL_ERROR;
979    }
980
981	/*
982	 * Make a partial parsing...
983	 */
984
985    readChannel = Tcl_FSOpenFileChannel( interp, Tcl_GetString(fileNamePtr),
986            "r", 0666 );
987    if (readChannel == NULL) {
988        result = TCL_ERROR;
989        goto bail;
990    }
991    result = Tcl_SetChannelOption( interp, readChannel, "-translation", "binary" );
992    if (result != TCL_OK) {
993        goto bail;
994    }
995    nread = Tcl_ReadChars( readChannel, readObj, 99999, 0 );
996    if (nread == -1) {
997        Tcl_SetObjResult( interp,
998     		    Tcl_NewStringObj( Tcl_ErrnoMsg( Tcl_GetErrno() ), -1 ) );
999        result = TCL_ERROR;
1000        goto bail;
1001    }
1002    contentPtr = Tcl_GetByteArrayFromObj( readObj, &len );
1003
1004
1005	memset( &presParams, 0, sizeof(presParams) );
1006	memset( &mediaParams, 0, sizeof(mediaParams) );
1007    QTSInitializeMediaParams( &mediaParams );
1008
1009	mediaParams.v.width = Long2Fix(320);
1010	mediaParams.v.height = Long2Fix(240);
1011	//mediaParams.v.gWorld = gOffscreenGWorldPtr;
1012    mediaParams.v.gWorld = TkMacOSXGetDrawablePort(Tk_WindowId(bcastPtr->tkwin));
1013	mediaParams.v.gdHandle = GetMainDevice();
1014
1015	presParams.version = kQTSPresParamsVersion1;
1016	presParams.flags = kQTSSendMediaFlag |      /* Send, not receive,
1017	                                             * kQTSReceiveMediaFlag */
1018	        kQTSAutoModeFlag |                  /* Use seq grabber */
1019	        kQTSDontShowStatusFlag;
1020	presParams.timeScale = kDefaultPresTimeScale;
1021	presParams.mediaParams = &mediaParams;
1022	presParams.notificationProc = (QTSNotificationUPP)
1023            NewQTSNotificationUPP( PresentationNotification );
1024	presParams.notificationRefCon = bcastPtr;
1025
1026    /*
1027     * Create a new presentation.
1028     */
1029
1030	err = QTSNewPresentationFromFile( &fsSpec, &presParams, &bcastPtr->presentation );
1031	if (err != noErr) {
1032        Tcl_SetObjResult( interp,
1033        	    Tcl_NewStringObj( "Failed creating new presentation", -1) );
1034        result = TCL_ERROR;
1035        goto bail;
1036    }
1037    //bcastPtr->presentation = presentation;
1038
1039bail:
1040
1041    Tcl_Close( interp, readChannel );
1042    Tcl_DecrRefCount( readObj );
1043  	QTSDisposeMediaParams( &mediaParams );
1044    return result;
1045}
1046
1047/*
1048 *--------------------------------------------------------------
1049 *
1050 * GetSDPFromData --
1051 *
1052 *      Fills in the necessary elements necessary for getting
1053 *      settings from SDP Tcl list. Each item corresponding to a
1054 *      line in an SDP file.
1055 *
1056 * Results:
1057 *	    Standard TCL result.
1058 *
1059 * Side effects:
1060 *      None.
1061 *
1062 *--------------------------------------------------------------
1063 */
1064
1065static int
1066GetSDPFromData(
1067        MovieBroadcast *bcastPtr,
1068        Tcl_Obj *sdpListPtr )
1069{
1070    Tcl_Interp      *interp = bcastPtr->interp;
1071	char			*charPtr;
1072    int             nlines;
1073    int             i;
1074	Tcl_DString     ds;
1075	Tcl_DString     dsSDP;
1076    SInt64          dataLength;
1077    Tcl_Obj         *objPtr = NULL;
1078	QTSMediaParams	mediaParams;
1079	QTSPresParams	presParams;
1080    OSErr           err = noErr;
1081    int             result = TCL_OK;
1082
1083    if (Tcl_ListObjLength( interp, sdpListPtr, &nlines ) != TCL_OK) {
1084        result = TCL_ERROR;
1085        goto bail;
1086    }
1087    Tcl_DStringInit( &dsSDP );
1088
1089    for (i = 0; i < nlines; i++) {
1090        if (Tcl_ListObjIndex( interp, sdpListPtr, i, &objPtr ) != TCL_OK) {
1091            result = TCL_ERROR;
1092            goto bail;
1093        }
1094 	    charPtr = Tcl_UtfToExternalDString( gQTTclTranslationEncoding,
1095 	            Tcl_GetString( objPtr ), -1 , &ds );
1096 	    Tcl_DStringAppendElement( &dsSDP, charPtr );
1097        Tcl_DStringFree( &ds );
1098        if (i < nlines - 1) {
1099            Tcl_DStringAppend( &dsSDP, "\r", 1 );
1100        }
1101    }
1102	QTTclDebugPrintf( bcastPtr->interp, 2,
1103	        "GetSDPFromData: SDP=%s", Tcl_DStringValue( &dsSDP ) );
1104
1105	memset( &presParams, 0, sizeof(presParams) );
1106	memset( &mediaParams, 0, sizeof(mediaParams) );
1107    QTSInitializeMediaParams( &mediaParams );
1108
1109	mediaParams.v.width = Long2Fix(320);
1110	mediaParams.v.height = Long2Fix(240);
1111	mediaParams.v.gWorld = gOffscreenGWorldPtr;
1112	mediaParams.v.gdHandle = GetMainDevice();
1113
1114	presParams.version = kQTSPresParamsVersion1;
1115    presParams.flags = kQTSSendMediaFlag |  kQTSAutoModeFlag |
1116            kQTSDontShowStatusFlag;
1117    presParams.timeScale = kDefaultPresTimeScale;
1118	presParams.mediaParams = &mediaParams;
1119    presParams.notificationProc = (QTSNotificationUPP)
1120            NewQTSNotificationUPP( PresentationNotification );
1121	presParams.notificationRefCon = bcastPtr;
1122
1123    dataLength = Tcl_DStringLength( &dsSDP );
1124	err = QTSNewPresentationFromData( kQTSSDPDataType,
1125	        Tcl_DStringValue( &dsSDP ), &dataLength,
1126	        &presParams, &bcastPtr->presentation );
1127	if (err != noErr) {
1128        Tcl_SetObjResult( interp,
1129        	    Tcl_NewStringObj( "Failed creating new presentation", -1) );
1130        goto bail;
1131    }
1132
1133bail:
1134
1135    Tcl_DStringFree( &dsSDP );
1136    return result;
1137}
1138
1139static int
1140GetSDPFromURL(
1141        MovieBroadcast *bcastPtr,
1142        Tcl_Obj *urlPtr )
1143{
1144	char			*charPtr;
1145    int             len;
1146	QTSMediaParams	mediaParams;
1147	QTSPresParams	presParams;
1148    OSErr           err = noErr;
1149	Handle			urlDataRef = NULL;
1150	Tcl_DString     ds;
1151    int             result = TCL_OK;
1152
1153	memset( &presParams, 0, sizeof(presParams) );
1154	memset( &mediaParams, 0, sizeof(mediaParams) );
1155    QTSInitializeMediaParams( &mediaParams );
1156
1157	mediaParams.v.width = Long2Fix(320);
1158	mediaParams.v.height = Long2Fix(240);
1159	mediaParams.v.gWorld = gOffscreenGWorldPtr;
1160	mediaParams.v.gdHandle = GetMainDevice();
1161
1162	presParams.version = kQTSPresParamsVersion1;
1163    presParams.flags = kQTSSendMediaFlag |  kQTSAutoModeFlag |
1164            kQTSDontShowStatusFlag;
1165    presParams.timeScale = kDefaultPresTimeScale;
1166	presParams.mediaParams = &mediaParams;
1167    presParams.notificationProc = (QTSNotificationUPP)
1168            NewQTSNotificationUPP( PresentationNotification );
1169	presParams.notificationRefCon = bcastPtr;
1170
1171    charPtr = Tcl_UtfToExternalDString( gQTTclTranslationEncoding,
1172            Tcl_GetString( urlPtr ), -1, &ds);
1173    len = Tcl_DStringLength( &ds );
1174	urlDataRef = MySafeNewHandle( len + 1, 1 );
1175	BlockMoveData( charPtr, *urlDataRef, len );
1176    err = QTSNewPresentationFromDataRef( urlDataRef,
1177            URLDataHandlerSubType, &presParams, &bcastPtr->presentation );
1178    Tcl_DStringFree( &ds );
1179	if (err != noErr) {
1180        Tcl_SetObjResult( bcastPtr->interp,
1181        	    Tcl_NewStringObj( "Failed creating new presentation", -1) );
1182        goto bail;
1183    }
1184
1185bail:
1186
1187	if (urlDataRef != NULL) {
1188		DisposeHandle( urlDataRef );
1189	}
1190    return result;
1191}
1192
1193/*
1194 *--------------------------------------------------------------
1195 *
1196 * BroadcastEventProc --
1197 *
1198 *	    This procedure is invoked by the Tk dispatcher for various
1199 *	    events on squares.
1200 *
1201 * Results:
1202 *	    None.
1203 *
1204 * Side effects:
1205 *	    When the window gets deleted, internal structures get
1206 *	    cleaned up.  When it gets exposed, it is redisplayed.
1207 *
1208 *--------------------------------------------------------------
1209 */
1210
1211static void
1212BroadcastEventProc(
1213    ClientData clientData,	/* Information about window. */
1214    XEvent *eventPtr )		/* Information about event. */
1215{
1216    MovieBroadcast *bcastPtr = (MovieBroadcast *) clientData;
1217    OSErr           err = noErr;
1218
1219	QTTclDebugPrintf( bcastPtr->interp, 2,
1220            "BroadcastEventProc: type=%d, flags=%1d%1d%1d%1d",
1221			eventPtr->type, (bcastPtr->flags >> 3) & 0x1, (bcastPtr->flags >> 2) & 0x1,
1222            (bcastPtr->flags >> 1) & 0x1, (bcastPtr->flags >> 0) & 0x1 );
1223
1224	if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
1225
1226        goto redraw;
1227    } else if (eventPtr->type == ConfigureNotify) {
1228
1229        goto redraw;
1230	} else if (eventPtr->type == UnmapNotify) {
1231        err = QTSPresSetGWorld( bcastPtr->presentation, kQTSAllStreams,
1232                gOffscreenGWorldPtr, NULL );
1233        bcastPtr->grafPtr = NULL;
1234	} else if (eventPtr->type == MapNotify) {
1235		bcastPtr->flags |= NEWGWORLD;
1236		goto redraw;
1237    } else if (eventPtr->type == DestroyNotify) {
1238
1239		/*
1240		 * We are being destroyed.
1241		 */
1242
1243		if (bcastPtr->tkwin != NULL) {
1244			bcastPtr->tkwin = NULL;
1245			Tcl_DeleteCommandFromToken( bcastPtr->interp, bcastPtr->widgetCmd );
1246		}
1247		if (bcastPtr->flags & REDRAW_PENDING) {
1248			Tcl_CancelIdleCall( DisplayBroadcast, (ClientData) bcastPtr );
1249		}
1250		DestroyBroadcast( bcastPtr );
1251    }
1252	return;
1253
1254redraw:
1255
1256	/*
1257	 * Now we know that the event type was such that the widget needs to be redrawn.
1258	 * Schedule the redrawing procedure.
1259	 */
1260
1261	if ((bcastPtr->tkwin != NULL) && Tk_IsMapped(bcastPtr->tkwin) &&
1262			!(bcastPtr->flags & REDRAW_PENDING)) {
1263		Tcl_DoWhenIdle( DisplayBroadcast, (ClientData) bcastPtr );
1264		bcastPtr->flags |= REDRAW_PENDING;
1265	}
1266}
1267
1268/*
1269 *--------------------------------------------------------------
1270 *
1271 * DisplayBroadcast --
1272 *
1273 *	This procedure redraws the contents of a square window.
1274 *	It is invoked as a do-when-idle handler, so it only runs
1275 *	when there's nothing else for the application to do.
1276 *
1277 * Results:
1278 *	None.
1279 *
1280 * Side effects:
1281 *	Information appears on the screen.
1282 *
1283 *--------------------------------------------------------------
1284 */
1285
1286static void
1287DisplayBroadcast(
1288        ClientData clientData )	/* Information about window. */
1289{
1290    MovieBroadcast      *bcastPtr = (MovieBroadcast *) clientData;
1291    Tk_Window           tkwin = bcastPtr->tkwin;
1292	CGrafPtr	 		saveWorld = NULL;
1293	GDHandle 			saveDevice = NULL;
1294	GWorldPtr			theGWorldPtr = NULL;
1295	Rect 				tkRect;
1296	MatrixRecord        matrix;
1297	OSErr               err = noErr;
1298
1299	QTTclDebugPrintf( bcastPtr->interp, 2,
1300	        "DisplayBroadcast: targetState=%2d, state=%2d",
1301	        bcastPtr->targetState, bcastPtr->state );
1302
1303	bcastPtr->flags &= ~REDRAW_PENDING;
1304    if (!Tk_IsMapped( tkwin)) {
1305	    return;
1306    }
1307    if (bcastPtr->presentation == kQTSInvalidPresentation) {
1308        return;
1309    }
1310	GetGWorld( &saveWorld, &saveDevice );
1311
1312	theGWorldPtr = QTTclMacGetDrawablePort( Tk_WindowId(tkwin) );
1313	MacSetPort( (GrafPtr) theGWorldPtr );
1314	//GetClip( saveRegion );
1315	QTTclMacWinBounds( (TkWindow *) tkwin, &tkRect );
1316
1317    /*
1318     * Setting QTSPresSetGWorld stops presentation if rolling.
1319     */
1320
1321    if (bcastPtr->grafPtr == NULL) {
1322        err = QTSPresSetGWorld( bcastPtr->presentation, kQTSAllStreams,
1323                theGWorldPtr, NULL );
1324        bcastPtr->grafPtr = theGWorldPtr;
1325    }
1326    SetIdentityMatrix( &matrix );
1327    TranslateMatrix( &matrix, Long2Fix( tkRect.left ), Long2Fix( tkRect.top ) );
1328    err = QTSPresSetMatrix( bcastPtr->presentation, kQTSAllStreams, &matrix );
1329
1330    err = QTSPresSetDimensions( bcastPtr->presentation, kQTSAllStreams,
1331            Long2Fix( tkRect.right - tkRect.left ),
1332            Long2Fix( tkRect.bottom - tkRect.top ) );
1333
1334    if (bcastPtr->targetState <= kPresentationStateIdle) {
1335        DrawBluescreen( bcastPtr );
1336    }
1337
1338	SetGWorld( saveWorld, saveDevice );
1339}
1340
1341/*
1342 *--------------------------------------------------------------
1343 *
1344 * DrawBluescreen --
1345 *
1346 *      Draws a "pause" screen when broadcaster idle.
1347 *
1348 * Results:
1349 *	    Standard TCL result.
1350 *
1351 * Side effects:
1352 *	    None.
1353 *
1354 *--------------------------------------------------------------
1355 */
1356
1357static void
1358DrawBluescreen( MovieBroadcast *bcastPtr )
1359{
1360    short               x, y;
1361    short               dx;
1362    short               rad;
1363    short               faceNum;
1364    short               strWidth;
1365    short               width;
1366    int                 i;
1367    RGBColor            blueColor = {0x0000, 0x0000, 0xFFFF};
1368    RGBColor            blackColor = {0x0000, 0x0000, 0x0000};
1369    RGBColor            whiteColor = {0xFFFF, 0xFFFF, 0xFFFF};
1370    RGBColor            color;
1371	Rect 				tkRect;
1372	Rect                r;
1373	GWorldPtr			theGWorldPtr = NULL;
1374	CGrafPtr	 		saveWorld = NULL;
1375	GDHandle 			saveDevice = NULL;
1376
1377	QTTclDebugPrintf( bcastPtr->interp, 2, "DrawBluescreen" );
1378
1379	QTTclMacWinBounds( (TkWindow *) bcastPtr->tkwin, &tkRect );
1380	theGWorldPtr = QTTclMacGetDrawablePort( Tk_WindowId(bcastPtr->tkwin) );
1381	GetGWorld( &saveWorld, &saveDevice );
1382	SetGWorld( theGWorldPtr, NULL );
1383#if TARGET_API_MAC_CARBON
1384    TkMacOSXSetUpClippingRgn( (Drawable) Tk_WindowId( bcastPtr->tkwin ) );
1385#else
1386    TkMacSetUpClippingRgn( (Drawable) Tk_WindowId( bcastPtr->tkwin ) );
1387#endif
1388
1389	RGBForeColor( &blueColor );
1390	PenMode( patCopy );
1391	PaintRect( &tkRect );
1392
1393	width = tkRect.right - tkRect.left;
1394	dx = width/15;
1395	x = (tkRect.right + tkRect.left)/2. - 5.5 * dx;
1396	r.top = tkRect.top + (1./3.) * (tkRect.bottom - tkRect.top);
1397	r.bottom = tkRect.top + (2./3.) * (tkRect.bottom - tkRect.top);
1398    for (i = 0; i <= 10; i++) {
1399        color.red = (10 - i)/10.0 * 0xFFFF;
1400        color.green = (10 - i)/10.0 * 0xFFFF;
1401        color.blue = (10 - i)/10.0 * 0xFFFF;
1402        RGBForeColor( &color );
1403        r.left = x + i * dx;
1404        r.right = r.left + dx;
1405    	PaintRect( &r );
1406    }
1407    rad = 0.4 * (tkRect.bottom - tkRect.top);
1408	x = tkRect.left + 0.5 * (tkRect.right - tkRect.left);
1409	y = tkRect.top + 0.5 * (tkRect.bottom - tkRect.top);
1410    r.left = x - rad;
1411    r.right = x + rad;
1412    r.top = y - rad;
1413    r.bottom = y + rad;
1414    RGBForeColor( &blackColor );
1415    FrameOval( &r );
1416
1417    RGBForeColor( &whiteColor );
1418#if TARGET_API_MAC_CARBON
1419    faceNum = FMGetFontFamilyFromName( "\pHelvetica" );
1420#else
1421    GetFNum( "\pHelvetica", &faceNum );
1422#endif
1423	TextFont( faceNum );
1424    TextSize( 24 );
1425	strWidth = StringWidth( "\pQuickTime TV" );
1426    if (strWidth > 0.8 * width) {
1427    	TextSize( 18 );
1428    	strWidth = StringWidth( "\pQuickTime TV" );
1429    }
1430    MoveTo( width/2 - strWidth/2, 32 );
1431    DrawString( "\pQuickTime TV" );
1432
1433	SetGWorld( saveWorld, saveDevice );
1434}
1435
1436/*
1437 *----------------------------------------------------------------------
1438 *
1439 * BroadcastDeletedProc --
1440 *
1441 *	    This procedure is invoked when a widget command is deleted.  If
1442 *	    the widget isn't already in the process of being destroyed,
1443 *	    this command destroys it.
1444 *
1445 * Results:
1446 *	    None.
1447 *
1448 * Side effects:
1449 *	    The widget is destroyed.
1450 *
1451 *----------------------------------------------------------------------
1452 */
1453
1454static void
1455BroadcastDeletedProc(
1456    ClientData clientData )	/* Pointer to widget record for widget. */
1457{
1458    MovieBroadcast  *bcastPtr = (MovieBroadcast *) clientData;
1459    Tk_Window       tkwin = bcastPtr->tkwin;
1460
1461    /*
1462     * This procedure could be invoked either because the window was
1463     * destroyed and the command was then deleted (in which case tkwin
1464     * is NULL) or because the command was deleted, and then this procedure
1465     * destroys the widget.
1466     */
1467
1468    if (tkwin != NULL) {
1469        bcastPtr->tkwin = NULL;
1470	    Tk_DestroyWindow( tkwin );
1471    }
1472}
1473
1474/*
1475 *----------------------------------------------------------------------
1476 *
1477 * DestroyBroadcast --
1478 *
1479 *		Deletes a Broadcast Widget. Most things cleaned up with Tk_FreeOptions
1480 *		but some things are freed up by me.
1481 *
1482 * Results:
1483 *		None.
1484 *
1485 * Side effects:
1486 *		Hopefully frees memory.
1487 *
1488 *----------------------------------------------------------------------
1489 */
1490
1491static void
1492DestroyBroadcast( ClientData clientData )
1493{
1494    MovieBroadcast  *bcastPtr = (MovieBroadcast *) clientData;
1495
1496
1497	QTTclDebugPrintf( bcastPtr->interp, 2, "DestroyBroadcast" );
1498
1499    RemoveFromBroadcastList( bcastPtr );
1500    PresentationFree( bcastPtr );
1501
1502
1503
1504	Tk_FreeConfigOptions( (char *) bcastPtr, bcastPtr->optionTable,
1505	        bcastPtr->tkwin );
1506	Tcl_EventuallyFree( (ClientData) bcastPtr, TCL_DYNAMIC );
1507}
1508
1509static void
1510PresentationFree( MovieBroadcast *bcastPtr )
1511{
1512    BcastSourcerInfo    *sourcerPtr = NULL;
1513	OSErr               err = noErr;
1514
1515    sourcerPtr = bcastPtr->sourcerInfoListPtr;
1516    while (sourcerPtr != NULL) {
1517        SourcerFree( sourcerPtr );
1518        sourcerPtr = sourcerPtr->next;
1519    }
1520
1521    err = QTSDisposePresentation( bcastPtr->presentation, 0 );
1522    bcastPtr->presentation = kQTSInvalidPresentation;
1523}
1524
1525static void
1526SourcerFree( BcastSourcerInfo *sourcerPtr )
1527{
1528	OSErr               err = noErr;
1529
1530
1531    QTSDisposeStream( sourcerPtr->stream, 0 );
1532}
1533
1534/*
1535 *----------------------------------------------------------------------
1536 *
1537 * PresentationNotification --
1538 *
1539 *      Notification callback function for the presentation.
1540 *
1541 * Results:
1542 *	    Apple error code.
1543 *
1544 * Side effects:
1545 *      Depending in notification type.
1546 *
1547 *----------------------------------------------------------------------
1548 */
1549
1550static ComponentResult
1551PresentationNotification(
1552        ComponentResult inErr,
1553        OSType          inNotificationType,
1554	    void            *inNotificationParams,
1555        void            *inRefCon )
1556{
1557    MovieBroadcast      *bcastPtr = (MovieBroadcast *) inRefCon;
1558	ComponentResult		err = noErr;
1559    char				tmp1[64];
1560	char 			    cmd[255];
1561    int                 callTclCommand = false;
1562	int                 result = TCL_OK;
1563
1564	if (bcastPtr->tkwin == NULL) {
1565	    return noErr;
1566	}
1567
1568    memset( tmp1, 0, 64 );
1569
1570    switch (inNotificationType) {
1571        case kQTSPreviewAckNotification: strcpy( tmp1, "kQTSPreviewAckNotification" ); break;
1572        case kQTSPrerollAckNotification: strcpy( tmp1, "kQTSPrerollAckNotification" ); break;
1573        case kQTSStartAckNotification: strcpy( tmp1, "kQTSStartAckNotification" ); break;
1574        case kQTSStopAckNotification: strcpy( tmp1, "kQTSStopAckNotification" ); break;
1575        case kQTSStatusNotification: strcpy( tmp1, "kQTSStatusNotification" ); break;
1576        case kQTSErrorNotification: strcpy( tmp1, "kQTSErrorNotification" ); break;
1577        default: memcpy( tmp1, &inNotificationType, 4 );
1578    }
1579
1580	QTTclDebugPrintf( bcastPtr->interp, 2,
1581	        "PresentationNotification targetState=%2d, state=%2d, type=%s",
1582	        bcastPtr->targetState, bcastPtr->state, tmp1 );
1583
1584    if (bcastPtr->command != NULL) {
1585        memset( cmd, 0, 255 );
1586		strcpy( cmd, bcastPtr->command );
1587		strcat( cmd, " " );
1588		strcat( cmd, Tcl_GetCommandName(bcastPtr->interp, bcastPtr->widgetCmd) );
1589    }
1590
1591	switch (inNotificationType)  {
1592
1593		case kQTSPreviewAckNotification: {
1594        	if (bcastPtr->state != kPresentationStateStartingPreview) {
1595        		// we're in the wrong state to be getting this ack
1596        		// ignore it...
1597
1598        	} else {
1599        		if (inErr != noErr) {
1600        			bcastPtr->targetState = kPresentationStateIdle;
1601        			bcastPtr->state = kPresentationStateIdle;
1602                    HandleErrorNotification( bcastPtr, "Preview", inErr, NULL );
1603        		} else {
1604        			bcastPtr->state = kPresentationStatePreviewing;
1605            		strcat( cmd, " preview" );
1606            		callTclCommand = true;
1607        		}
1608        	}
1609			break;
1610		}
1611
1612		case kQTSPrerollAckNotification: {
1613        	if (bcastPtr->state != kPresentationStateStartingPreroll) {
1614        		// we're in the wrong state to be getting this ack
1615        		// ignore it...
1616
1617        	} else {
1618        		if (inErr != noErr) {
1619        			bcastPtr->targetState = kPresentationStateIdle;
1620        			bcastPtr->state = kPresentationStateIdle;
1621                    HandleErrorNotification( bcastPtr, "Preroll", inErr, NULL );
1622        		} else {
1623        			bcastPtr->state = kPresentationStateReadyToPlay;
1624        			if (bcastPtr->targetState > kPresentationStateReadyToPlay)  {
1625	                    bcastPtr->state = kPresentationStateStarting;
1626	                    err = QTSPresStart( bcastPtr->presentation,
1627	                            kQTSAllStreams, 0L );
1628                		strcat( cmd, " preroll" );
1629                		callTclCommand = true;
1630	                }
1631	            }
1632			}
1633			break;
1634		}
1635
1636		case kQTSStartAckNotification: {
1637        	if (bcastPtr->state != kPresentationStateStarting) {
1638        		// we're in the wrong state to be getting this ack
1639        		// ignore it...
1640
1641        	} else {
1642        		if (inErr != noErr) {
1643        			bcastPtr->targetState = kPresentationStateIdle;
1644        			bcastPtr->state = kPresentationStateIdle;
1645                    HandleErrorNotification( bcastPtr, "Start", inErr, NULL );
1646        		} else {
1647        			bcastPtr->state = kPresentationStatePlaying;
1648                		strcat( cmd, " start" );
1649                		callTclCommand = true;
1650        		}
1651        	}
1652			break;
1653		}
1654
1655		case kQTSStopAckNotification: {
1656        	if (bcastPtr->state != kPresentationStateStarting) {
1657        		// we're in the wrong state to be getting this ack
1658        		// ignore it...
1659
1660        	} else {
1661        		if (inErr != noErr) {
1662        			bcastPtr->targetState = kPresentationStateIdle;
1663        			bcastPtr->state = kPresentationStateIdle;
1664                    HandleErrorNotification( bcastPtr, "Preroll", inErr, NULL );
1665                } else {
1666            	    bcastPtr->state = kPresentationStateIdle;
1667               		strcat( cmd, " stop" );
1668               		callTclCommand = true;
1669            	}
1670            }
1671			break;
1672		}
1673
1674		case kQTSStatusNotification: {
1675            if (inErr != noErr) {
1676                bcastPtr->targetState = kPresentationStateIdle;
1677                bcastPtr->state = kPresentationStateIdle;
1678                HandleErrorNotification( bcastPtr, "Status", inErr, NULL );
1679            }
1680			break;
1681		}
1682
1683		case kQTSErrorNotification: {
1684        	if (inErr != noErr) {
1685                HandleErrorNotification( bcastPtr, "Error", inErr, NULL );
1686        	}
1687			break;
1688		}
1689
1690		default: {
1691			// it's ok to get a notification you don't know about
1692			// just silently ignore it - this is not an error
1693			break;
1694		}
1695
1696		// all you can do in a kQTSNewPresentationNotification
1697		// is store the presentation - do not make any presentation calls
1698		// when you that notification
1699
1700	}
1701    if (Tk_IsMapped(bcastPtr->tkwin) &&
1702            (bcastPtr->state == kPresentationStateIdle)) {
1703	    DrawBluescreen( bcastPtr );
1704	}
1705	if ((bcastPtr->command != NULL) && callTclCommand) {
1706    	result = Tcl_Eval( bcastPtr->interp, cmd );
1707	}
1708	return err;
1709}
1710
1711static ComponentResult
1712HandleErrorNotification( MovieBroadcast *bcastPtr,
1713	    const char *inNotificationTypeString,
1714		ComponentResult inErr,
1715		QTSErrorParams *inErrorParams)
1716{
1717	char 			cmd[255];
1718	int             result = TCL_OK;
1719
1720	/*
1721	 * Tell the user about the error in some nice way.
1722	 */
1723
1724    if (bcastPtr->command != NULL) {
1725        memset( cmd, 0, 255 );
1726		strcpy( cmd, bcastPtr->command );
1727		strcat( cmd, " " );
1728		strcat( cmd, Tcl_GetCommandName(bcastPtr->interp, bcastPtr->widgetCmd) );
1729		strcat( cmd, " error " );
1730		strcat( cmd, inNotificationTypeString );
1731    }
1732	result = Tcl_Eval( bcastPtr->interp, cmd );
1733
1734	return noErr;
1735}
1736
1737/*
1738 *----------------------------------------------------------------------
1739 *
1740 * AddToBroadcastList, RemoveFromBroadcastList --
1741 *
1742 *      Adds/removes broadcast from our linked list.
1743 *
1744 * Results:
1745 *	    None.
1746 *
1747 * Side effects:
1748 *	    None.
1749 *
1750 *----------------------------------------------------------------------
1751 */
1752
1753static void
1754AddToBroadcastList( MovieBroadcast *bcastPtr )
1755{
1756    MovieBroadcastList      *newEntryPtr = NULL;
1757
1758    // check!!!!
1759    newEntryPtr = (MovieBroadcastList *) ckalloc( sizeof(MovieBroadcastList) );
1760    newEntryPtr->bcastPtr = bcastPtr;
1761	newEntryPtr->next = gMovieBroadcastListPtr;
1762	gMovieBroadcastListPtr = newEntryPtr;
1763}
1764
1765static void
1766RemoveFromBroadcastList( MovieBroadcast *bcastPtr )
1767{
1768	MovieBroadcastList		*currentPtr = NULL;
1769	MovieBroadcastList		*prevPtr = NULL;
1770
1771    /*
1772     * Find the entry in the linked list for a start.
1773     */
1774
1775    // check!!!!
1776	currentPtr = gMovieBroadcastListPtr;
1777	while (currentPtr != NULL) {
1778		if (currentPtr->bcastPtr == bcastPtr) {
1779			if (prevPtr != NULL)  {
1780				prevPtr->next = currentPtr->next;
1781			}  else  {
1782				gMovieBroadcastListPtr = currentPtr->next;
1783			}
1784    	    ckfree( (char *) currentPtr );
1785			break;
1786		}
1787		prevPtr = currentPtr;
1788		currentPtr = currentPtr->next;
1789	}
1790}
1791
1792void
1793BroadcastMacEvent( void )
1794{
1795    IdleAllPresentations();
1796}
1797
1798/*
1799 *----------------------------------------------------------------------
1800 *
1801 * IdleAllPresentations, IdleSourcersForPresentation --
1802 *
1803 *      Idles presentations in our linked list.
1804 *      Also idles any sourcers for each presentation.
1805 *
1806 * Results:
1807 *	    None.
1808 *
1809 * Side effects:
1810 *	    Just gives cpu cycles.
1811 *
1812 *----------------------------------------------------------------------
1813 */
1814
1815static void
1816IdleAllPresentations( void )
1817{
1818	int         	        clipInvalid = false;
1819    RgnHandle               rgnHandle = NULL;
1820	MovieBroadcastList		*currentPtr;
1821    MovieBroadcast          *bcastPtr;
1822    OSErr                   err = noErr;
1823
1824	currentPtr = gMovieBroadcastListPtr;
1825	while (currentPtr != NULL) {
1826        bcastPtr = currentPtr->bcastPtr;
1827    	if ((bcastPtr->presentation != kQTSInvalidPresentation) &&
1828                (bcastPtr->presentation != NULL)) {
1829
1830            clipInvalid = false;
1831            if (MyIsClipRegionInvalid( bcastPtr->tkwin )) {
1832                clipInvalid = true;
1833                QTTclDebugPrintf( bcastPtr->interp, 3, "\tMyIsClipRegionInvalid" );
1834            }
1835            if (clipInvalid) {
1836                rgnHandle = QTTclMacVisableClipRgn( (TkWindow *) bcastPtr->tkwin );
1837                if (rgnHandle == NULL) {
1838                    clipInvalid = false;
1839                }
1840            }
1841            if (clipInvalid) {
1842                err = QTSPresSetClip( bcastPtr->presentation, kQTSAllStreams,
1843                        rgnHandle );
1844            }
1845    		QTSPresIdle( bcastPtr->presentation, NULL );
1846    		IdleSourcersForPresentation( bcastPtr );
1847    	}
1848		currentPtr = currentPtr->next;
1849    };
1850}
1851
1852static void
1853IdleSourcersForPresentation( MovieBroadcast *bcastPtr )
1854{
1855    BcastSourcerInfo        *sourcerPtr = NULL;
1856
1857    sourcerPtr = bcastPtr->sourcerInfoListPtr;
1858    while (sourcerPtr != NULL) {
1859        QTSSourcerIdle( sourcerPtr->sourcer, 0, 0, 0 );
1860        sourcerPtr = sourcerPtr->next;
1861    }
1862}
1863
1864/*
1865 *----------------------------------------------------------------------
1866 *
1867 * InstallBroadcastIdlingEventLoopTimer, TaskNeededSoonerCallback,
1868 * 		CarbonTimerNextTime --
1869 *
1870 *		Sets up Carbon timers to serve presentations.
1871 *
1872 * Results:
1873 *		.
1874 *
1875 * Side effects:
1876 *		Timers installed etc.
1877 *
1878 *----------------------------------------------------------------------
1879 */
1880
1881#if TARGET_API_MAC_CARBON
1882static OSStatus
1883InstallBroadcastIdlingEventLoopTimer( void )
1884{
1885	OSStatus 		err;
1886
1887    int kMinimumIdleDurationInMillis = 100;
1888
1889	err = InstallEventLoopTimer( GetMainEventLoop(),
1890			0, 											/* firedelay */
1891			kEventDurationMillisecond * kMinimumIdleDurationInMillis, /* interval */
1892			NewEventLoopTimerUPP( PresentationCarbonTimer ),
1893			NULL,
1894			&gCarbonPresentationTimerRef );
1895	if (err != noErr) {
1896		/*
1897		 * Install a callback that the Idle Manager will use when
1898		 * QuickTime needs to wake me up immediately.
1899		 */
1900
1901//		err = QTInstallNextTaskNeededSoonerCallback(
1902//				NewQTNextTaskNeededSoonerCallbackUPP( TaskNeededSoonerCallback ),
1903//				1000, 			/* Millisecond timescale */
1904//				0, 				/* No flags */
1905//				(void *) gCarbonMovieTimerRef );
1906
1907	}
1908	return err;
1909}
1910#endif
1911
1912/*
1913 *----------------------------------------------------------------------
1914 *
1915 * MoviePlayerCarbonTimer --
1916 *
1917 *		Timer callback to serve movies under Carbon.
1918 *
1919 * Results:
1920 *		None.
1921 *
1922 * Side effects:
1923 *		Calls 'MoviePlayerMacEvent'.
1924 *
1925 *----------------------------------------------------------------------
1926 */
1927
1928#if TARGET_API_MAC_CARBON
1929static pascal void
1930PresentationCarbonTimer( EventLoopTimerRef theTimer,
1931		void *userData )
1932{
1933    IdleAllPresentations();
1934}
1935#endif
1936
1937static int
1938PresentationExportToFile( MovieBroadcast *bcastPtr,
1939	    int objc, Tcl_Obj *CONST objv[] )
1940{
1941	OSErr				err = noErr;
1942	QTSExportParams		exportParams;
1943
1944	if (bcastPtr->presentation != kQTSInvalidPresentation)  {
1945		memset( &exportParams, 0, sizeof(exportParams) );
1946		exportParams.version = kQTSExportParamsVersion1;
1947		exportParams.exportType = ksigMoviePlayer;
1948		exportParams.flagsIn = kQTSExportFlag_ShowDialog;
1949		exportParams.filterProc = NewQTSModalFilterUPP( MyModalFilterDialogProc );
1950        exportParams.filterProcRefCon = bcastPtr;
1951
1952		err = QTSPresExport( bcastPtr->presentation, kQTSAllStreams, &exportParams );
1953		DisposeQTSNotificationUPP( exportParams.filterProc );
1954		if (err != noErr)  {
1955
1956		}
1957	}  else  {
1958
1959		err = qtsBadStateErr;
1960	}
1961	return err;
1962}
1963
1964static int
1965SourcerObjCmd( Tcl_Interp *interp,
1966        MovieBroadcast *bcastPtr,
1967	    int objc, Tcl_Obj *CONST objv[] )
1968{
1969    int             index;
1970	OSErr           err = noErr;
1971    int             result = TCL_OK;
1972
1973    if (objc < 1) {
1974    	Tcl_WrongNumArgs( interp, 1, objv, "option ?arg arg...?" );
1975    	return TCL_ERROR;
1976    }
1977    if (Tcl_GetIndexFromObj( interp, objv[0], sourcerCommands, "command",
1978    	    0, &index) != TCL_OK) {
1979    	return TCL_ERROR;
1980    }
1981
1982    Tcl_Preserve( (ClientData) bcastPtr );
1983
1984    switch (index) {
1985
1986        case kSourcerCmdAdd: {
1987
1988            break;
1989        }
1990        case kSourcerCmdInfo: {
1991            SourcerGetInfo( interp, bcastPtr, kQTSAllStreams, objc - 1, objv + 1 );
1992            break;
1993        }
1994        case kSourcerCmdRemove: {
1995
1996            break;
1997        }
1998	}
1999    Tcl_Release( (ClientData) bcastPtr );
2000    return result;
2001
2002error:
2003    Tcl_Release( (ClientData) bcastPtr );
2004    return TCL_ERROR;
2005}
2006
2007/*
2008 *----------------------------------------------------------------------
2009 *
2010 * GetAllStreamMediaTypes --
2011 *
2012 *		Investigate the presentation for stream types.
2013 *
2014 * Results:
2015 *		None.
2016 *
2017 * Side effects:
2018 *		None.
2019 *
2020 *----------------------------------------------------------------------
2021 */
2022
2023static void
2024GetAllStreamMediaTypes( QTSPresentation  presentation, int *haveAudio,
2025        int *haveVideo, int *haveText )
2026{
2027    int             i;
2028    int             numStreams;
2029    QTSStream       stream;
2030    OSType          mediaType;
2031    OSErr           err = noErr;
2032
2033    *haveAudio = 0;
2034    *haveVideo = 0;
2035    *haveText = 0;
2036	numStreams = QTSPresGetNumStreams( presentation );
2037
2038    for (i = 1; i <= numStreams; ++i) {
2039		stream = QTSPresGetIndStream( presentation, i );
2040        if (err == noErr) {
2041    	    err = QTSPresGetInfo( presentation, stream,
2042    	            kQTSMediaTypeInfo, &mediaType );
2043            if (err == noErr) {
2044                switch (mediaType) {
2045                    case SoundMediaType: {
2046                        *haveAudio = 1;
2047                        break;
2048                    }
2049                    case VideoMediaType: {
2050                        *haveVideo = 1;
2051                        break;
2052                    }
2053                    case TextMediaType: {
2054                        *haveText = 1;
2055                        break;
2056                    }
2057                    case MPEGMediaType: {
2058                        /* empty */
2059                    }
2060                }
2061            }
2062        }
2063    }
2064}
2065
2066/*
2067 *----------------------------------------------------------------------
2068 *
2069 * SourcerGetInfo --
2070 *
2071 *		Returns a hierarchical list of various info for a particular stream,
2072 *      or for all if kQTSAllStreams.
2073 *
2074 * Results:
2075 *		Standard Tcl result.
2076 *
2077 * Side effects:
2078 *		None.
2079 *
2080 *----------------------------------------------------------------------
2081 */
2082
2083static int
2084SourcerGetInfo( Tcl_Interp *interp,
2085        MovieBroadcast *bcastPtr,
2086        QTSStream       inStream,   /* Investigate only this stream */
2087	    int             objc,
2088	    Tcl_Obj         *CONST objv[] )
2089{
2090    int             i;
2091    int             numSourcers;
2092    long            lType;
2093    char            type8Char[8];
2094    QTSSourcer      sourcer = 0;
2095	Tcl_Obj			*listObjPtr;
2096	Tcl_Obj			*subListObjPtr;
2097	Tcl_Obj			*rectObjPtr;
2098	Rect            rect;
2099	QTSSourcerTimingParams  timingParams;
2100	UInt16          uint16;
2101	UInt32          uint32;
2102	Handle          handle = NULL;
2103	OSType          osType;
2104	Fixed           fixed;
2105	OSErr           err = noErr;
2106    int             result = TCL_OK;
2107
2108    listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
2109    numSourcers = QTSPresGetNumSourcers( bcastPtr->presentation,
2110            inStream );
2111    if (numSourcers == 0) {
2112        Tcl_SetObjResult( interp, Tcl_NewStringObj("", -1) );
2113    }
2114
2115    for (i = 1; i <= numSourcers; ++i) {
2116        subListObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
2117
2118        QTSPresGetIndSourcer( bcastPtr->presentation, inStream,
2119                i, &sourcer );
2120
2121        err = GetSourcerType( sourcer, &osType );
2122        if (err == noErr) {
2123			Tcl_ListObjAppendElement( interp, subListObjPtr,
2124			        Tcl_NewStringObj("-sourcertype", -1) );
2125    		lType = EndianU32_BtoN( osType );
2126    		memset( type8Char, '\0', 8 );
2127    		memcpy( type8Char, &lType, 4 );
2128    		Tcl_ListObjAppendElement( interp, subListObjPtr,
2129    				Tcl_NewStringObj(type8Char, -1) );
2130        }
2131
2132        err = QTSSourcerGetInfo( sourcer, kQTSMediaTypeInfo, &osType );
2133        if (err == noErr) {
2134			Tcl_ListObjAppendElement( interp, subListObjPtr,
2135			        Tcl_NewStringObj("-mediatype", -1) );
2136    		lType = EndianU32_BtoN( osType );
2137    		memset( type8Char, '\0', 8 );
2138    		memcpy( type8Char, &lType, 4 );
2139    		Tcl_ListObjAppendElement( interp, subListObjPtr,
2140    				Tcl_NewStringObj(type8Char, -1) );
2141        }
2142
2143        err = QTSSourcerGetInfo( sourcer, kQTSInfo_TargetFrameRate, &fixed );
2144        if (err == noErr) {
2145			Tcl_ListObjAppendElement( interp, subListObjPtr,
2146			        Tcl_NewStringObj("-targetframerate", -1) );
2147    		Tcl_ListObjAppendElement( interp, subListObjPtr,
2148    				Tcl_NewDoubleObj( Fix2X(fixed) ) );
2149        }
2150
2151        err = QTSSourcerGetInfo( sourcer, kQTSInfo_TargetDataRate, &uint32 );
2152        if (err == noErr) {
2153			Tcl_ListObjAppendElement( interp, subListObjPtr,
2154			        Tcl_NewStringObj("-targetdatarate", -1) );
2155    		Tcl_ListObjAppendElement( interp, subListObjPtr,
2156    				Tcl_NewLongObj( uint32 ) );
2157        }
2158
2159        err = QTSSourcerGetInfo( sourcer, kQTSInfo_InputDeviceName, &handle );
2160        if (err == noErr) {
2161			Tcl_ListObjAppendElement( interp, subListObjPtr,
2162			        Tcl_NewStringObj("-inputdevicename", -1) );
2163    		Tcl_ListObjAppendElement( interp, subListObjPtr,
2164    				Tcl_NewStringObj( *handle, -1) );
2165    		DisposeHandle( handle );
2166        }
2167
2168        err = QTSSourcerGetInfo( sourcer, kQTSInfo_InputSourceName, &handle );
2169        if (err == noErr) {
2170			Tcl_ListObjAppendElement( interp, subListObjPtr,
2171			        Tcl_NewStringObj("-inputsourcename", -1) );
2172    		Tcl_ListObjAppendElement( interp, subListObjPtr,
2173    				Tcl_NewStringObj( *handle, -1) );
2174    		DisposeHandle( handle );
2175        }
2176
2177        err = QTSSourcerGetInfo( sourcer, kQTSInfo_FullInputRect, &rect );
2178        if (err == noErr) {
2179			Tcl_ListObjAppendElement( interp, subListObjPtr,
2180			        Tcl_NewStringObj("-fullinputrect", -1) );
2181            rectObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
2182			Tcl_ListObjAppendElement( interp, rectObjPtr,
2183			        Tcl_NewIntObj(rect.left) );
2184			Tcl_ListObjAppendElement( interp, rectObjPtr,
2185			        Tcl_NewIntObj(rect.top) );
2186			Tcl_ListObjAppendElement( interp, rectObjPtr,
2187			        Tcl_NewIntObj(rect.right) );
2188			Tcl_ListObjAppendElement( interp, rectObjPtr,
2189			        Tcl_NewIntObj(rect.bottom) );
2190            Tcl_ListObjAppendElement( interp, subListObjPtr, rectObjPtr );
2191        }
2192
2193        /*
2194         * Video device settings.
2195         */
2196
2197        err = QTSSourcerGetInfo( sourcer, kQTSInfo_VideoHue, &uint16 );
2198        if (err == noErr) {
2199			Tcl_ListObjAppendElement( interp, subListObjPtr,
2200			        Tcl_NewStringObj("-videohue", -1) );
2201    		Tcl_ListObjAppendElement( interp, subListObjPtr,
2202    				Tcl_NewIntObj( uint16 ) );
2203        }
2204
2205
2206        /*
2207         * For tracks sourcers only.
2208         */
2209
2210        err = QTSSourcerGetInfo( sourcer, kQTSInfo_SourcerTiming, &timingParams );
2211        if (err == noErr) {
2212			Tcl_ListObjAppendElement( interp, subListObjPtr,
2213			        Tcl_NewStringObj("-timescale", -1) );
2214			Tcl_ListObjAppendElement( interp, subListObjPtr,
2215			        Tcl_NewLongObj(timingParams.timeScale) );
2216			/* TimeValue64 !!!! wrong!!!!!! */
2217			Tcl_ListObjAppendElement( interp, subListObjPtr,
2218			        Tcl_NewStringObj("-presentationstarttime", -1) );
2219			Tcl_ListObjAppendElement( interp, subListObjPtr,
2220			        Tcl_NewLongObj(timingParams.presentationStartTime) );
2221			Tcl_ListObjAppendElement( interp, subListObjPtr,
2222			        Tcl_NewStringObj("-presentationendtime", -1) );
2223			Tcl_ListObjAppendElement( interp, subListObjPtr,
2224			        Tcl_NewLongObj(timingParams.presentationEndTime) );
2225			Tcl_ListObjAppendElement( interp, subListObjPtr,
2226			        Tcl_NewStringObj("-presentationcurrenttime", -1) );
2227			Tcl_ListObjAppendElement( interp, subListObjPtr,
2228			        Tcl_NewLongObj(timingParams.presentationCurrentTime) );
2229			Tcl_ListObjAppendElement( interp, subListObjPtr,
2230			        Tcl_NewStringObj("-localstarttime", -1) );
2231			Tcl_ListObjAppendElement( interp, subListObjPtr,
2232			        Tcl_NewLongObj(timingParams.localStartTime) );
2233			Tcl_ListObjAppendElement( interp, subListObjPtr,
2234			        Tcl_NewStringObj("-localendtime", -1) );
2235			Tcl_ListObjAppendElement( interp, subListObjPtr,
2236			        Tcl_NewLongObj(timingParams.localEndTime) );
2237			Tcl_ListObjAppendElement( interp, subListObjPtr,
2238			        Tcl_NewStringObj("-localcurrenttime", -1) );
2239			Tcl_ListObjAppendElement( interp, subListObjPtr,
2240			        Tcl_NewLongObj(timingParams.localCurrentTime) );
2241        }
2242
2243        Tcl_ListObjAppendElement( interp, listObjPtr, subListObjPtr );
2244    }
2245    Tcl_SetObjResult( interp, listObjPtr );
2246
2247    return result;
2248}
2249
2250static OSErr
2251GetSourcerType( QTSSourcer sourcer, OSType *typePtr )
2252{
2253    ComponentDescription    cd;
2254    OSErr                   err = noErr;
2255
2256    err = GetComponentInfo( (Component) sourcer, &cd, NULL, NULL, NULL );
2257    if (err == noErr) {
2258        *typePtr = cd.componentSubType;
2259    }
2260    return err;
2261}
2262
2263static OSErr
2264GetSeqGrabberFullInputRect( QTSPresentation	presentation,
2265        int *fullWidth, int *fullHeight )
2266{
2267    int             i;
2268    int             numSourcers;
2269    QTSSourcer      sourcer = 0;
2270	Rect            rect;
2271	OSErr           err = paramErr;
2272
2273    numSourcers = QTSPresGetNumSourcers( presentation,
2274            kQTSAllStreams );
2275    for (i = 1; i <= numSourcers; ++i) {
2276        QTSPresGetIndSourcer( presentation, kQTSAllStreams,
2277                i, &sourcer );
2278        err = QTSSourcerGetInfo( sourcer, kQTSInfo_FullInputRect, &rect );
2279        if (err == noErr) {
2280    		MacOffsetRect( &rect, (short) -rect.left, (short) -rect.top );
2281            *fullWidth = rect.right;
2282            *fullHeight = rect.bottom;
2283            break;
2284        }
2285    }
2286    return err;
2287}
2288
2289static int
2290StreamObjCmd( Tcl_Interp *interp,
2291        MovieBroadcast *bcastPtr,
2292	    int objc, Tcl_Obj *CONST objv[] )
2293{
2294    int             numStreams;
2295    int             numSourcers;
2296    int             i;
2297    char	        type8Char[8];
2298    long            lType;
2299    QTSStream       stream;
2300	OSType		    trackType;
2301	OSErr           err = noErr;
2302	Tcl_Obj			*listObjPtr;
2303	Tcl_Obj			*subListObjPtr;
2304    int             result = TCL_OK;
2305
2306
2307	listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
2308	numStreams = QTSPresGetNumStreams( bcastPtr->presentation );
2309
2310	for (i = 1; i <= numStreams; ++i) {
2311		stream = QTSPresGetIndStream( bcastPtr->presentation, i );
2312	    err = QTSPresGetInfo( bcastPtr->presentation, stream,
2313	            kQTSMediaTypeInfo, &trackType );
2314
2315		subListObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
2316		Tcl_ListObjAppendElement( interp, subListObjPtr,
2317		        Tcl_NewStringObj("-mediatype", -1) );
2318		lType = EndianU32_BtoN( trackType );
2319		memset( type8Char, '\0', 8 );
2320		memcpy( type8Char, &lType, 4 );
2321		Tcl_ListObjAppendElement( interp, subListObjPtr,
2322				Tcl_NewStringObj(type8Char, -1) );
2323
2324
2325		if (stream != kQTSInvalidStream) {
2326			numSourcers = QTSPresGetNumSourcers( bcastPtr->presentation, stream );
2327        }
2328		Tcl_ListObjAppendElement( interp, listObjPtr, subListObjPtr );
2329    }
2330
2331	Tcl_SetObjResult( interp, listObjPtr );
2332    return result;
2333}
2334
2335static int
2336PictureObjCmd( Tcl_Interp *interp,
2337        MovieBroadcast *bcastPtr,
2338	    int objc, Tcl_Obj *CONST objv[] )
2339{
2340    int             dstWidth = 0;
2341    int             dstHeight = 0;
2342    PicHandle       picHand = NULL;
2343    OSErr           err = noErr;
2344    int             result = TCL_OK;
2345
2346    err = QTSPresGetPicture( bcastPtr->presentation, kQTSAllStreams,
2347            &picHand );
2348    if ((err == noErr) && (picHand != NULL)) {
2349		result = ConvertPictureToTkPhoto( interp, picHand,
2350				dstWidth, dstHeight, Tcl_GetString(objv[0]) );
2351    } else {
2352        Tcl_SetObjResult( interp,
2353        	    Tcl_NewStringObj( "Failed taking picture", -1) );
2354        result = TCL_ERROR;
2355    }
2356    if (picHand != NULL) {
2357		KillPicture( picHand );
2358    }
2359    return result;
2360}
2361
2362static int
2363SetupTrackSourcerForStream(
2364        MovieBroadcast  *bcastPtr,
2365        QTSStream       inStream )
2366{
2367    QTSSourcer          sourcer = 0;
2368    ComponentDescription    cd;
2369    Component           component;
2370    OSType              trackType = SoundMediaType;
2371	//QTSSourcerCallbackProcParams	cParams;
2372    OSErr               tmpErr = noErr;
2373    OSErr               err = noErr;
2374    BcastSourcerInfo	*sourcerInfoPtr = NULL;
2375    int                 result = TCL_OK;
2376
2377	/*
2378	 * Ask the presentation if it already has a media type.
2379	 */
2380
2381	tmpErr = QTSPresGetInfo( bcastPtr->presentation, inStream,
2382	        kQTSMediaTypeInfo, &trackType );
2383	if (tmpErr != noErr)  {
2384		trackType = SoundMediaType;
2385	}
2386
2387	memset( &cd, 0, sizeof(cd) );
2388	cd.componentType = kQTSSourcerType;
2389	cd.componentSubType = kQTSMovieTrackSourcerType;
2390	cd.componentManufacturer = trackType;
2391	component = FindNextComponent( 0, &cd );
2392	if (component == 0)  {
2393
2394        goto exit;
2395	}
2396
2397	if ((err = OpenAComponent( component, &sourcer ) ) != noErr) {
2398
2399	    goto exit;
2400	}
2401	if ((err = QTSSourcerInitialize( sourcer, NULL ) ) != noErr) {
2402
2403	    goto exit;
2404	}
2405/*
2406	if (inHandler->loop) {
2407		memset(&loopParams, 0, sizeof(loopParams));
2408		loopParams.loopFlags = kQTSLoopFlag_Loop;
2409		loopParams.flagsMask = kQTSLoopFlag_Loop;
2410		QTSSourcerSetInfo( sourcer, kQTSInfo_Loop, &loopParams );
2411	}
2412*/
2413/*
2414	memset( &cParams, 0, sizeof(cParams) );
2415	cParams.version = kQTSSourcerCallbackProcParamsVersion1;
2416	if (gMovieSourcingCallbackUPP == NULL)  {
2417		gMovieSourcingCallbackUPP =
2418		        NewQTSNotificationUPP( MovieSourcingCallbackProc );
2419	}
2420	cParams.proc = gMovieSourcingCallbackUPP;
2421	cParams.refCon = inHandler;
2422
2423	if ((err = QTSSourcerSetInfo( sourcer, kQTSInfo_SourcerCallbackProc,
2424	        &cParams) ) != noErr) {
2425
2426	    goto error;
2427    }
2428*/
2429    /*
2430     * Create and fill in a SourcerInfo object.
2431     */
2432
2433    sourcerInfoPtr = (BcastSourcerInfo *) ckalloc(sizeof(BcastSourcerInfo) );
2434    memset( (void *) sourcerInfoPtr, 0, (sizeof(BcastSourcerInfo)) );
2435	sourcerInfoPtr->presentation = bcastPtr->presentation;
2436	sourcerInfoPtr->stream = inStream;
2437	sourcerInfoPtr->component = component;
2438	sourcerInfoPtr->sourcer = sourcer;
2439	sourcerInfoPtr->trackType = trackType;
2440	sourcerInfoPtr->track = NULL;
2441	sourcerInfoPtr->done = false;
2442
2443	/*
2444	 * The presentation now owns the sourcer and will dispose of it unless you
2445	 * set the right flags.
2446	 */
2447
2448	if ((err = QTSPresAddSourcer( bcastPtr->presentation, inStream,
2449	        sourcer, 0L) ) != noErr) {
2450
2451	    goto exit;
2452	}
2453
2454exit:
2455	if (err != noErr)  {
2456		if (sourcer != 0)  {
2457			CloseComponent( sourcer );
2458		}
2459	}
2460
2461    return result;
2462}
2463
2464
2465static pascal ComponentResult
2466MovieSourcingCallbackProc(
2467        ComponentResult inErr,
2468		OSType          inSelector,
2469		void            *ioParams,
2470		void            *inRefCon )
2471{
2472    MovieBroadcast      *bcastPtr = (MovieBroadcast *) inRefCon;
2473	ComponentResult		err = noErr;
2474
2475	if (inErr != noErr)  {
2476
2477	}
2478
2479	switch (inSelector)  {
2480		case 9999:
2481
2482			break;
2483
2484		default:
2485			/* Ignore any selectors you don't know about. */
2486			break;
2487
2488	}
2489	return err;
2490}
2491
2492void
2493QTSDisposeMediaParams( QTSMediaParams *inMediaParams )
2494{
2495	if (inMediaParams != NULL) {
2496	    if (inMediaParams->v.clip != NULL) {
2497	        DisposeRgn( inMediaParams->v.clip );
2498	    }
2499		DisposePtr( (Ptr) inMediaParams->a.frequencyBands );
2500	}
2501}
2502
2503#if TARGET_OS_MAC && !TARGET_API_MAC_CARBON
2504
2505static pascal Boolean
2506MyModalFilterDialogProc( DialogPtr dialogPtr,
2507        EventRecord *event,
2508        SInt16 *itemHit,
2509        long *refCon )
2510{
2511    Boolean         handled = false;
2512    WindowRef       eventWindow = NULL;
2513    WindowRef       dialogWindow = NULL;
2514
2515#if TARGET_API_MAC_CARBON
2516    dialogWindow = GetDialogWindow( dialogPtr );
2517#else
2518    dialogWindow = dialogPtr;
2519#endif
2520
2521    switch (event->what) {
2522
2523        case updateEvt:
2524            eventWindow = (WindowRef) event->message;
2525            if ((eventWindow != NULL) && (eventWindow != dialogWindow)) {
2526
2527        		/*
2528        		 * Handle update events to background windows here.
2529        		 * First, translate mac event to a number of tcl events.
2530        		 * If any tcl events generated, execute them until empty, and don't wait.
2531        		 */
2532
2533        		if (TkMacConvertEvent( event )) {
2534        			while ( Tcl_DoOneEvent( TCL_IDLE_EVENTS | TCL_DONT_WAIT | TCL_WINDOW_EVENTS ) )
2535        				/* empty */
2536        				;
2537        		}
2538            }
2539            break;
2540    }
2541    return handled;
2542}
2543
2544#endif // TARGET_OS_MAC && !TARGET_API_MAC_CARBON
2545
2546#if TARGET_API_MAC_CARBON
2547static Boolean
2548MyModalFilterDialogProc( DialogPtr dialogPtr,
2549        EventRecord *event,
2550        SInt16 *itemHit,
2551        long *refCon )
2552{
2553    Boolean         handled = false;
2554
2555    return handled;
2556}
2557#endif // TARGET_API_MAC_CARBON
2558
2559/*---------------------------------------------------------------------------*/
2560
2561