1/*
2 * SeqGrabber.c --
3 *
4 *		This file implements a sequence grabber for audio and video using QuickTime
5 *		on the Macintosh. It is part of the QuickTimeTcl package which provides
6 *		Tcl/Tk bindings for QuickTime.
7 *
8 * Copyright (c) 2000-2005  Mats Bengtsson
9 *
10 * $Id: SeqGrabber.c,v 1.17 2008/02/15 15:23:06 matben Exp $
11 */
12
13#include "SeqGrabber.h"
14
15/*
16 * I store the seqence grabber widget struct in a global variable here because I couldn't
17 * find a way to get it into 'SeqGrabberMacEvent'; perhaps through EventRecord?
18 */
19
20static SeqGrabber *gSGrabPtr = NULL;
21
22/*
23 * Time interval for Carbon event timer (in secs) from SonOfMungGrab.c.
24 * Q: How often should I call SGIdle in my application?
25 * A: Generally, you should call SGIdle frequently enough for the type of content
26 * you are trying to capture. There's no single answer which will work optimally
27 * for every format. For example, if you are capturing DV at 29.97 fps, at a minimum
28 * you should be calling SGIdle at least as frequently as the frame rate.
29 * If you are capturing audio simultaneously, you should take this into account and
30 * increase the frequency as well.
31 */
32
33#if TARGET_API_MAC_CARBON
34EventLoopTimerRef   gCarbonGrabberTimerRef = NULL;
35const EventTime kCarbonGrabberTimerInterval = kEventDurationSecond / 60;
36#endif
37
38/*
39 * Record to handle async image command.
40 */
41
42typedef struct AsyncImageHandlerRecord {
43    SeqGrabber			*sgrabPtr;
44    Tcl_TimerToken		timerToken;     /* Tcl's token for the timer handler. */
45    Tcl_Obj 			*commandObjPtr;	/* Command to invoke. */
46} AsyncImageHandlerRecord;
47
48/*
49 * For dispatching grabber commands.
50 */
51
52static char *allGrabberCmds[] = {
53    "audiosettings",
54	"cget", "configure", "image",
55	"ispaused", "isrunning", "isstopped",
56	"pause", "picture",
57	"record", "start", "stop",
58	"videosettings",
59    (char *) NULL
60};
61
62enum {
63    kGrabberCmdAudioSettings        = 0L,
64    kGrabberCmdCget,
65    kGrabberCmdConfigure,
66    kGrabberCmdImage,
67    kGrabberCmdIsPaused,
68    kGrabberCmdIsRunning,
69    kGrabberCmdIsStopped,
70    kGrabberCmdPause,
71    kGrabberCmdPicture,
72    kGrabberCmdRecord,
73    kGrabberCmdStart,
74    kGrabberCmdStop,
75    kGrabberCmdVideoSettings
76};
77
78/*
79 * We need a global variable to set if either or both of MoviePlayer or SeqGrabber is running.
80 * This is used by our common Mac event procedure to figure out if we should call
81 * MoviePlayerMacEvent and/or SeqGrabberMacEvent.
82 * It is defined in MoviePlayer.c.
83 */
84
85extern long    gIsRunningFlags;
86
87/*
88 * Information used for parsing configuration options.
89 * Mask bits for options changed.
90 */
91
92enum {
93    SEQGRAB_CONF_NEWGWORLD              	= (1L << 0),
94    SEQGRAB_CONF_FILE                     	= (1L << 1),
95    SEQGRAB_CONF_QUALITY                	= (1L << 2),
96    SEQGRAB_CONF_OVERLAYIMAGE            	= (1L << 3),
97    SEQGRAB_CONF_ZOOM                 		= (1L << 4),
98    SEQGRAB_CONF_PLAYDURINGRECORD        	= (1L << 5),
99    SEQGRAB_CONF_VIDEOCOMPRESSOR        	= (1L << 6),
100    SEQGRAB_CONF_VOLUME                     = (1L << 7),
101    SEQGRAB_CONF_FRAMERATE                  = (1L << 8)
102};
103
104/*
105 * The following table defines the legal values for the -quality option.
106 */
107
108static CONST char *playbackQualityST[] = {
109 	"fast", "normal", "high", (char *) NULL
110};
111#define	SG_QUALITY_MODE_FAST 			0
112#define	SG_QUALITY_MODE_NORMAL			1
113#define	SG_QUALITY_MODE_HIGH			2
114
115/*
116 * The following table defines the legal values for the -size option.
117 */
118
119static CONST char *widgetSizeST[] = {
120	"quarter", "half", "full", (char *) NULL
121};
122#define	SG_WIDGET_SIZE_QUARTER 		0
123#define	SG_WIDGET_SIZE_HALF			1
124#define	SG_WIDGET_SIZE_FULL			2
125
126static Tk_OptionSpec SeqGrabberConfigSpecs[] = {
127	{TK_OPTION_BOOLEAN, "-audio", "audio", "Audio",
128		"1", -1, Tk_Offset(SeqGrabber, audio), 0,
129		(ClientData) NULL, 0},
130	{TK_OPTION_BOOLEAN, "-audioonly", "audioOnly", "AudioOnly",
131		"0", -1, Tk_Offset(SeqGrabber, audioOnly), 0,
132		(ClientData) NULL, 0},
133	{TK_OPTION_STRING, "-file", "file", "File",
134		NULL, -1, Tk_Offset(SeqGrabber, filename), TK_OPTION_NULL_OK,
135		(ClientData) NULL, SEQGRAB_CONF_FILE},
136	{TK_OPTION_DOUBLE, "-framerate", "frameRate", "FrameRate",
137		"0.0", -1, Tk_Offset(SeqGrabber, frameRate), 0,
138		(ClientData) NULL, SEQGRAB_CONF_FRAMERATE},
139	{TK_OPTION_PIXELS, "-height", "height", "Height",
140		"0", -1, Tk_Offset(SeqGrabber, height), 0,
141		(ClientData) NULL, SEQGRAB_CONF_NEWGWORLD},
142#if TARGET_OS_MAC
143	{TK_OPTION_STRING, "-overlayimage", "overlayImage", "OverlayImage",
144		NULL, -1, Tk_Offset(SeqGrabber, overlayimage), TK_OPTION_NULL_OK,
145		(ClientData) NULL, SEQGRAB_CONF_OVERLAYIMAGE},
146#endif
147	{TK_OPTION_STRING_TABLE, "-quality", "quality", "Quality",
148		"normal", -1, Tk_Offset(SeqGrabber, indQuality), 0,
149		(ClientData) playbackQualityST, SEQGRAB_CONF_QUALITY},
150	{TK_OPTION_BOOLEAN, "-playduringrecord", "playDuringRecord", "PlayDuringRecord",
151		"1", -1, Tk_Offset(SeqGrabber, playDuringRecord), 0,
152		(ClientData) NULL, SEQGRAB_CONF_PLAYDURINGRECORD},
153	{TK_OPTION_BOOLEAN, "-showfps", "showFPS", "ShowFPS",
154		"0", -1, Tk_Offset(SeqGrabber, showFPS), 0,
155		(ClientData) NULL, 0},
156	{TK_OPTION_STRING_TABLE, "-size", "size", "Size",
157		"half", -1, Tk_Offset(SeqGrabber, indSize), 0,
158		(ClientData) widgetSizeST, SEQGRAB_CONF_NEWGWORLD},
159	{TK_OPTION_STRING, "-videocompressor", "videoCompressor", "VideoCompressor",
160		NULL, -1, Tk_Offset(SeqGrabber, videoCompressor), TK_OPTION_NULL_OK,
161		(ClientData) NULL, SEQGRAB_CONF_VIDEOCOMPRESSOR},
162	{TK_OPTION_DOUBLE, "-volume", "volume", "Volume",
163		"1.0", -1, Tk_Offset(SeqGrabber, volume), 0,
164		(ClientData) NULL, SEQGRAB_CONF_VOLUME},
165	{TK_OPTION_PIXELS, "-width", "width", "Width",
166		"0", -1, Tk_Offset(SeqGrabber, width), 0,
167		(ClientData) NULL, SEQGRAB_CONF_NEWGWORLD},
168	{TK_OPTION_DOUBLE, "-zoom", "zoom", "Zoom",
169		"1.0", -1, Tk_Offset(SeqGrabber, zoom), 0,
170		(ClientData) NULL, SEQGRAB_CONF_ZOOM},
171    {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
172		(char *) NULL, 0, 0, 0, 0}
173};
174
175/*
176 * Prototypes for procedures referenced only in this file:
177 */
178
179static int 		SeqGrabberWidgetCmd( ClientData clientData, Tcl_Interp *interp,
180						int objc, Tcl_Obj *CONST objv[] );
181static int 		ConfigureSeqGrabber( Tcl_Interp *interp, SeqGrabber *sgrabPtr,
182						int objc, Tcl_Obj *CONST objv[] );
183static int 	    SeqGrabberComputeGeometry( SeqGrabber *sgrabPtr, int *width,
184						int *height, int setZoom );
185static void 	SeqGrabberEventProc( ClientData clientData, XEvent *eventPtr );
186static void 	SeqGrabberDeletedProc( ClientData clientData );
187static void 	DestroySeqGrabber( SeqGrabber *sgrabPtr );
188static void		SeqGrabberWorldChanged( ClientData instanceData );
189static SeqGrabber *CreateSeqGrabber( Tk_Window tkwin );
190static void 	DisplaySeqGrabber( ClientData clientData );
191static void		SeqGrabberExitProc( ClientData clientData );
192static int		SetInternalVideoOptions( SeqGrabber *sgrabPtr );
193static int		SetInternalAudioOptions( SeqGrabber *sgrabPtr );
194static void		AsyncImageHandlerProc( ClientData clientData );
195
196
197/* ...and the non tk specific ones. */
198static SeqGrabComponent 		MakeMySequenceGrabber( void );
199static pascal ComponentResult 	MakeVideoChannel( SeqGrabComponent seqGrab,
200                                        SGChannel *sgchanVideo, const Rect *rect,
201                                        int playDuringRecord, Boolean willRecord );
202static pascal ComponentResult 	MakeAudioChannel( SeqGrabComponent seqGrab,
203                                        SGChannel *sgchanSound, int playDuringRecord,
204                                        Boolean willRecord );
205static void						FreeVideoChannel(SeqGrabber *sgrabPtr);
206static void						FreeAudioChannel(SeqGrabber *sgrabPtr);
207static pascal ComponentResult 	SetupMyVideoBottlenecks( SGChannel sgchanVideo,
208										WindowPtr macWndPtr, SeqGrabber *sgrabPtr );
209static pascal ComponentResult  	MyGrabFrameComplete( SGChannel sgChan,
210										short nBufferNum, Boolean *pbDone, long refConst );
211static pascal ComponentResult   TakePicture( SeqGrabber *sgrabPtr,
212                                		PicHandle *thePic );
213pascal Boolean          		MovableDialogModalFilter( DialogPtr theDialog,
214                                	    const EventRecord *event, short *itemHit, long refCon );
215
216#if TARGET_API_MAC_CARBON
217pascal void                     SeqGrabberCarbonTimer( EventLoopTimerRef theTimer, void *userData );
218#endif
219
220#ifdef _WIN32
221LRESULT CALLBACK 		QTGrabberWinProc( HWND hWnd, UINT message,
222								WPARAM wParam, LPARAM lParam );
223#endif
224
225
226Tk_ClassProcs SeqGrabberProcs = {
227        sizeof(Tk_ClassProcs),
228		SeqGrabberWorldChanged,		/* geometryProc */
229		NULL,						/* createProc */
230		NULL						/* modalproc */
231};
232
233/*
234 *----------------------------------------------------------------------
235 *
236 * SeqGrabberObjCmd --
237 *
238 *		Create a Sequence Grabber widget in QuickTime.
239 *
240 * Results:
241 *		Standard TCL result
242 *
243 * Side effects:
244 *		Creates a command and allocates memory.
245 *
246 *----------------------------------------------------------------------
247 */
248
249int
250SeqGrabberObjCmd(
251		ClientData clientData, 		/* NULL */
252		Tcl_Interp *interp,
253		int objc,
254		Tcl_Obj *CONST objv[] )
255{
256	Tk_Window 	        tkwin;
257	SeqGrabber 	        *sgrabPtr;
258	Tk_OptionTable 		optionTable;
259	ComponentResult		err;
260    GWorldPtr	        sgGWorldPtr = NULL;
261#ifdef _WIN32
262    HWND        		tempHwnd = NULL;
263#endif
264
265	if (objc < 2) {
266		Tcl_WrongNumArgs( interp, 1, objv, "command ?arg arg...?" );
267		return TCL_ERROR;
268	}
269	if (gSGrabPtr != NULL) {
270		Tcl_SetObjResult( interp,
271				Tcl_NewStringObj("Can only have one simultaneous grabber", -1) );
272		return TCL_ERROR;
273	}
274
275	/* Create new window */
276
277	tkwin = Tk_CreateWindowFromPath( interp, Tk_MainWindow(interp),
278			Tcl_GetStringFromObj(objv[1], NULL), (char *) NULL);
279	if (tkwin == NULL) {
280		return TCL_ERROR;
281	}
282
283    /*
284     * Create the option table for this widget class.  If it has already
285     * been created, the cached pointer will be returned.
286     */
287
288	optionTable = Tk_CreateOptionTable( interp, SeqGrabberConfigSpecs );
289
290	/* Set resource class */
291
292	Tk_SetClass( tkwin, "SeqGrabber" );
293
294	/* allocate memory */
295	sgrabPtr = CreateSeqGrabber( tkwin );
296
297	Tk_SetClassProcs( tkwin, &SeqGrabberProcs, (ClientData) sgrabPtr );
298
299	sgrabPtr->tkwin = tkwin;
300	sgrabPtr->display = Tk_Display(tkwin);
301	sgrabPtr->interp = interp;
302	sgrabPtr->widgetCmd = Tcl_CreateObjCommand( interp,
303			Tk_PathName(sgrabPtr->tkwin), SeqGrabberWidgetCmd,
304			(ClientData) sgrabPtr, SeqGrabberDeletedProc );
305	sgrabPtr->optionTable = optionTable;
306
307	/*
308	 * Sequence Grabber specific attributes.
309	 */
310
311	sgrabPtr->seqGrab = NULL;
312	sgrabPtr->sgChanVideo = NULL;
313	sgrabPtr->sgChanAudio = NULL;
314	sgrabPtr->filename = NULL;
315	sgrabPtr->willRecord = true;	/* Seems not to cause any harm even of not recording. */
316	sgrabPtr->sgWidth = 0;
317	sgrabPtr->sgHeight = 0;
318	sgrabPtr->srcWidth = 0;
319	sgrabPtr->srcHeight = 0;
320	sgrabPtr->videoWidth = 0;
321	sgrabPtr->videoHeight = 0;
322	sgrabPtr->width = 0;
323	sgrabPtr->height = 0;
324	sgrabPtr->zoom = 1.0;
325	sgrabPtr->audio = 1;
326	sgrabPtr->audioOnly = 0;
327	sgrabPtr->playDuringRecord = 1;
328	sgrabPtr->frameCount = 0;
329	sgrabPtr->showFPS = 0;
330	sgrabPtr->latestTick = TickCount();
331	sgrabPtr->padx = 0;
332	sgrabPtr->pady = 0;
333	sgrabPtr->background = NULL;
334	sgrabPtr->videoBottle = 1;
335#ifdef _WIN32
336	sgrabPtr->videoBottle = 0;
337#endif
338    sgrabPtr->imageAsyncProcObj = NULL;
339    sgrabPtr->imageNameObj = NULL;
340    sgrabPtr->asyncImageHandlerPtr = NULL;
341	sgrabPtr->overlayimage = NULL;
342	sgrabPtr->overlayPictHand = NULL;
343	sgrabPtr->updatePictHand = NULL;
344	sgrabPtr->flags = 0;
345
346	Tcl_CreateExitHandler( SeqGrabberExitProc, (ClientData) NULL );
347
348	/* We want all the events */
349	Tk_CreateEventHandler( sgrabPtr->tkwin,
350		ExposureMask|StructureNotifyMask|FocusChangeMask|VisibilityChangeMask|KeyReleaseMask,
351		SeqGrabberEventProc, (ClientData) sgrabPtr );
352
353    /*
354     * Windows need to have a window already to set the new winproc, and in either case,
355     * we need to set the GWorld of the grabber before creating the video channel!
356     */
357
358    if (Tk_WindowId(sgrabPtr->tkwin) == None) {
359        Tk_MakeWindowExist(sgrabPtr->tkwin);
360    }
361
362	/*
363	 * Windows specific code to create a Mac style graphics port, and associate
364	 * it with a Windows HWND. Get the winproc given by tk, save it to be called
365	 * later, and set our own winproc.
366	 */
367
368#ifdef _WIN32
369    tempHwnd = TkWinGetHWND( Tk_WindowId(sgrabPtr->tkwin) );
370    CreatePortAssociation( tempHwnd, NULL, 0 );
371    sgrabPtr->winEventProc = GetWindowLong( tempHwnd, GWL_WNDPROC );
372    SetWindowLong( tempHwnd, GWL_WNDPROC, (LONG) QTGrabberWinProc );
373#endif
374
375	if (Tk_InitOptions( interp, (char *) sgrabPtr, optionTable, tkwin ) != TCL_OK) {
376		Tk_DestroyWindow( sgrabPtr->tkwin );
377		return TCL_ERROR;
378	}
379
380	/*
381	 * Init the sequence grabber component.
382	 */
383
384	sgrabPtr->seqGrab = MakeMySequenceGrabber();
385	if (sgrabPtr->seqGrab == NULL) {
386		Tk_DestroyWindow( sgrabPtr->tkwin );
387		Tcl_SetObjResult( interp,
388				Tcl_NewStringObj("Failed making sequence grabber", -1) );
389		return TCL_ERROR;
390	}
391
392    /*
393     * Windows (and Mac?) needs to have the SGSetGWorld before creating the video channel.
394     */
395
396    sgGWorldPtr = QTTclMacGetDrawablePort( Tk_WindowId(sgrabPtr->tkwin) );
397    if (sgGWorldPtr) {
398	    SGSetGWorld( sgrabPtr->seqGrab, sgGWorldPtr, NULL );
399    }
400
401	/*
402	 * Configure the widget; parse the command line arguments and look for defaults
403	 * in the resource database.
404	 * IMPORTANT: need to have the channels prepared before configuring them!
405	 * Channels created in 'ConfigureSeqGrabber'.
406	 */
407
408	if (ConfigureSeqGrabber( interp, sgrabPtr, objc - 2, objv + 2 ) != TCL_OK) {
409		Tk_DestroyWindow(sgrabPtr->tkwin);
410		return TCL_ERROR;
411	}
412
413	/*
414	 * Check if channels succesfully created. Perhaps accept audio channel only?
415	 */
416
417	if ((sgrabPtr->audioOnly == false) && (sgrabPtr->sgChanVideo == NULL)) {
418		Tk_DestroyWindow( sgrabPtr->tkwin );
419		Tcl_SetObjResult( interp,
420				Tcl_NewStringObj("Failed making video channel. Component not found", -1) );
421		return TCL_ERROR;
422	}
423
424	/* Do we want a video bottleneck for... */
425
426	if (sgrabPtr->videoBottle && (sgrabPtr->sgChanVideo != NULL)) {
427#if TARGET_API_MAC_CARBON
428		err = SetupMyVideoBottlenecks( sgrabPtr->sgChanVideo,
429				GetWindowFromPort( QTTclMacGetDrawablePort( Tk_WindowId(sgrabPtr->tkwin) ) ),
430				sgrabPtr );
431#else
432		err = SetupMyVideoBottlenecks( sgrabPtr->sgChanVideo,
433				(GrafPtr) QTTclMacGetDrawablePort( Tk_WindowId(sgrabPtr->tkwin) ),
434				sgrabPtr );
435#endif
436		if (err != noErr) {
437			Tk_DestroyWindow( sgrabPtr->tkwin );
438			Tcl_SetObjResult( interp,
439					Tcl_NewStringObj("Failed making video bottleneck", -1) );
440			return TCL_ERROR;
441		}
442	}
443
444	/* Store in a global variable, used in mac/win event procedure. */
445	gSGrabPtr = sgrabPtr;
446
447    if (sgrabPtr->audioOnly) {
448        gIsRunningFlags |= SEQ_GRABBER_RUNS;
449    }
450
451#if TARGET_API_MAC_CARBON
452        if (gCarbonGrabberTimerRef == NULL) {
453            InstallEventLoopTimer( GetMainEventLoop(),
454                    0,									/* firedelay */
455                    kCarbonGrabberTimerInterval,  		/* interval */
456                    NewEventLoopTimerUPP( SeqGrabberCarbonTimer ),
457                    NULL, &gCarbonGrabberTimerRef );
458        }
459#endif
460
461	/*
462	 * Set the NEWGWORLD in 'flags' to require an update and to find the geometry.
463	 */
464
465	sgrabPtr->flags |= NEWGWORLD;
466	SeqGrabberWorldChanged( (ClientData) sgrabPtr );
467	Tcl_SetObjResult( interp, Tcl_NewStringObj(Tk_PathName(sgrabPtr->tkwin), -1) );
468	return TCL_OK;
469}
470
471/*
472 *----------------------------------------------------------------------
473 *
474 * SeqGrabberWidgetCmd --
475 *
476 *		Command to run for  each widget
477 *
478 * Results:
479 *		Normal TCL results
480 *
481 * Side effects:
482 *		Memory allocated and/or freed, Mac Movie structures modified
483 *
484 *----------------------------------------------------------------------
485 */
486
487static int
488SeqGrabberWidgetCmd( ClientData clientData, Tcl_Interp *interp,
489		int objc, Tcl_Obj *CONST objv[] )
490{
491	SeqGrabber 		*sgrabPtr = (SeqGrabber *) clientData;
492    int             cmdIndex;
493	int             usedUpdatePict;
494	int				saveIsRunningFlag;
495	int				boolean;
496	PicHandle		thePic = NULL;
497	ComponentResult res;
498	Rect			aRect;
499	Byte			isPaused;
500	Tcl_Obj 		*resultObjPtr;
501	int 			result = TCL_OK;
502
503	if (objc < 2) {
504		Tcl_WrongNumArgs( interp, 1, objv, "command ?arg arg...?" );
505		return TCL_ERROR;
506	}
507
508	Tcl_Preserve( (ClientData) sgrabPtr );
509	if (Tcl_GetIndexFromObj( interp, objv[1], allGrabberCmds, "command",
510			TCL_EXACT, &cmdIndex ) != TCL_OK ) {
511	    result = TCL_ERROR;
512	    goto error;
513	}
514
515    /*
516     * Dispatch the movie command to the right branch.
517     */
518
519    switch(cmdIndex) {
520
521        case kGrabberCmdCget: {
522			if (objc != 3) {
523				Tcl_WrongNumArgs(interp, 2, objv, "option");
524			    result = TCL_ERROR;
525				goto error;
526			}
527			resultObjPtr = Tk_GetOptionValue( interp, (char *) sgrabPtr,
528				sgrabPtr->optionTable, objv[2], sgrabPtr->tkwin );
529			if (resultObjPtr == NULL) {
530				result = TCL_ERROR;
531			} else {
532				Tcl_SetObjResult( interp, resultObjPtr );
533			}
534			break;
535        }
536
537        case kGrabberCmdConfigure: {
538			resultObjPtr = NULL;
539			if (objc <= 3) {
540				resultObjPtr = Tk_GetOptionInfo( interp, (char *) sgrabPtr,
541					sgrabPtr->optionTable,
542					(objc == 2) ? (Tcl_Obj *) NULL : objv[2],
543					sgrabPtr->tkwin );
544				if (resultObjPtr == NULL) {
545					result = TCL_ERROR;
546				} else {
547					Tcl_SetObjResult( interp, resultObjPtr );
548				}
549			} else {
550
551    			/*
552    			 * Change one or more attributes. Reschedule a new display via
553    			 * 'SeqGrabberWorldChanged'. Be sure to only set the argv values by the flag.
554    			 * The NEWGWORLD bit in flags is set if we need a redisplay.
555    			 */
556
557    			result = ConfigureSeqGrabber( interp, sgrabPtr, objc - 2, objv + 2 );
558
559                /*
560                 * Only if we made a configuration that needs a redisplay.
561                 */
562
563    			if ((result == TCL_OK) && (sgrabPtr->flags & NEWGWORLD)) {
564    				SeqGrabberWorldChanged( (ClientData) sgrabPtr );
565    			}
566			}
567			break;
568        }
569
570        case kGrabberCmdImage: {
571            Tcl_CmdInfo		info;
572            Tk_PhotoHandle 	photo;
573            Tcl_Obj			*resultObjPtr;
574
575    		if (objc != 4) {
576                Tcl_WrongNumArgs( interp, 2, objv, "procName imageName" );
577    			result = TCL_ERROR;
578    			goto error;
579    		}
580            if (!Tk_IsMapped(sgrabPtr->tkwin)) {
581                Tcl_SetObjResult( interp, Tcl_NewStringObj("seqgrabber must be displayed", -1) );
582                result = TCL_ERROR;
583                goto error;
584            }
585            if (Tcl_GetCommandInfo( interp, Tcl_GetString(objv[2]), &info ) == 0) {
586                resultObjPtr = Tcl_NewStringObj("proc name not found: \"", -1);
587                Tcl_AppendObjToObj( resultObjPtr, objv[2] );
588                Tcl_SetObjResult( interp, resultObjPtr );
589                result = TCL_ERROR;
590                goto error;
591            }
592
593            photo = Tk_FindPhoto( interp, Tcl_GetString(objv[3]) );
594            if (photo == NULL) {
595                resultObjPtr = Tcl_NewStringObj("Image not found \"", -1);
596                Tcl_AppendObjToObj( resultObjPtr, objv[3] );
597                Tcl_SetObjResult( interp, resultObjPtr );
598                result = TCL_ERROR;
599                goto error;
600            }
601            if (sgrabPtr->imageAsyncProcObj != NULL) {
602                Tcl_DecrRefCount( sgrabPtr->imageAsyncProcObj );
603            }
604            sgrabPtr->imageAsyncProcObj = (void *) objv[2];
605            Tcl_IncrRefCount( objv[2] );
606
607            sgrabPtr->imageNameObj = objv[3];
608            Tcl_IncrRefCount( objv[3] );
609
610            break;
611        }
612
613        case kGrabberCmdIsPaused: {
614    		if (objc > 2) {
615				Tcl_WrongNumArgs( interp, 2, objv, NULL );
616    			result = TCL_ERROR;
617    			goto error;
618    		}
619    		if (sgrabPtr->seqGrab) {
620                SGGetPause( sgrabPtr->seqGrab, &isPaused );
621                if (isPaused == seqGrabPause) {
622					Tcl_SetObjResult( interp, Tcl_NewBooleanObj(1) );
623                } else {
624					Tcl_SetObjResult( interp, Tcl_NewBooleanObj(0) );
625                }
626    		} else {
627				Tcl_SetObjResult( interp,
628						Tcl_NewStringObj("No sequence grabber", -1) );
629    			result = TCL_ERROR;
630    		}
631		    break;
632        }
633
634        case kGrabberCmdIsRunning: {
635    		if (objc > 2) {
636				Tcl_WrongNumArgs( interp, 2, objv, NULL );
637    			result = TCL_ERROR;
638    			goto error;
639    		}
640    		if (sgrabPtr->seqGrab) {
641    		    if (sgrabPtr->flags & ISRUNNING) {
642					Tcl_SetObjResult( interp, Tcl_NewBooleanObj(1) );
643                } else {
644					Tcl_SetObjResult( interp, Tcl_NewBooleanObj(0) );
645                }
646    		} else {
647				Tcl_SetObjResult( interp,
648						Tcl_NewStringObj("No sequence grabber", -1) );
649    			result = TCL_ERROR;
650    		}
651		    break;
652        }
653
654        case kGrabberCmdIsStopped: {
655    		if (objc > 2) {
656				Tcl_WrongNumArgs( interp, 2, objv, NULL );
657    			result = TCL_ERROR;
658    			goto error;
659    		}
660    		if (sgrabPtr->seqGrab) {
661
662                /* If not paused and not running. */
663
664                SGGetPause( sgrabPtr->seqGrab, &isPaused );
665                if (!(isPaused == seqGrabPause) && !(sgrabPtr->flags & ISRUNNING)) {
666					Tcl_SetObjResult( interp, Tcl_NewBooleanObj(1) );
667                } else {
668					Tcl_SetObjResult( interp, Tcl_NewBooleanObj(0) );
669                }
670    		} else {
671				Tcl_SetObjResult( interp,
672						Tcl_NewStringObj("No sequence grabber", -1) );
673    			result = TCL_ERROR;
674    		}
675		    break;
676        }
677
678        case kGrabberCmdPause: {
679    		if (objc != 3) {
680				Tcl_WrongNumArgs( interp, 2, objv, "boolean" );
681    			result = TCL_ERROR;
682    			goto error;
683    		}
684    		if (sgrabPtr->seqGrab) {
685                SGGetPause( sgrabPtr->seqGrab, &isPaused );
686				if (Tcl_GetBooleanFromObj( interp, objv[2], &boolean ) != TCL_OK) {
687					Tcl_AddErrorInfo( interp,
688							"\n	(processing pause command)" );
689					result = TCL_ERROR;
690					goto error;
691				}
692    			if (boolean) {
693
694        		    /* Take pict for updating and then pause if we are not already paused. */
695
696    				if (isPaused != seqGrabPause) {
697    				    if (sgrabPtr->sgChanVideo) {
698            			    res = TakePicture( sgrabPtr, &thePic );
699            			    sgrabPtr->updatePictHand = thePic;
700        				}
701        				SGPause( sgrabPtr->seqGrab, seqGrabPause );
702        				sgrabPtr->flags &= ~ISRUNNING;
703                    	sgrabPtr->frameCount = 0;
704    				}
705    			} else {
706    			    SGPause( sgrabPtr->seqGrab, seqGrabUnpause );
707    				sgrabPtr->flags |= ISRUNNING;
708
709    				/*
710    				 * Annulate update pict since invalid now.
711    				 */
712
713    				if (sgrabPtr->updatePictHand) {
714    				    KillPicture( sgrabPtr->updatePictHand );
715    				}
716    				sgrabPtr->updatePictHand = NULL;
717
718    			}
719    		} else {
720				Tcl_SetObjResult( interp,
721						Tcl_NewStringObj("No sequence grabber", -1) );
722    			result = TCL_ERROR;
723    		}
724		    break;
725        }
726
727        case kGrabberCmdPicture: {
728
729    		/*
730    		 * Make a tk image from the sequence grabber display.
731    		 * If we are paused there should already be a pict for us to use.
732    		 */
733
734    		if (sgrabPtr->seqGrab && sgrabPtr->sgChanVideo) {
735    			if (objc != 3) {
736					Tcl_WrongNumArgs( interp, 2, objv, "imageName" );
737    				result = TCL_ERROR;
738    				goto error;
739    			}
740                SGGetPause( sgrabPtr->seqGrab, &isPaused );
741    			if ((isPaused == seqGrabPause) && sgrabPtr->updatePictHand) {
742    			    thePic = sgrabPtr->updatePictHand;
743
744    			    // fails on windows
745    			    aRect = (**thePic).picFrame;
746    			    usedUpdatePict = true;
747    			} else {
748    		        res = TakePicture( sgrabPtr, &thePic );
749    			    usedUpdatePict = false;
750    		    }
751
752    			if (thePic == NULL) {
753					Tcl_SetObjResult( interp,
754							Tcl_NewStringObj("Error getting pict from sequence grabber", -1) );
755    				result = TCL_ERROR;
756    				goto error;
757    			}
758    			result = ConvertPictureToTkPhoto( interp, thePic, 0, 0,
759    					Tcl_GetStringFromObj(objv[2], NULL) );
760
761    			/* Do not kill the pict if we used the update pict (updatePictHand)! */
762    			if (!usedUpdatePict) {
763    			    KillPicture(thePic);
764    			}
765    		} else {
766				Tcl_SetObjResult( interp,
767						Tcl_NewStringObj("No sequence grabber", -1) );
768    			result = TCL_ERROR;
769    		}
770		    break;
771        }
772
773        case kGrabberCmdRecord: {
774
775    		/*
776    		 * Start recording to file specified with -file option.
777    		 */
778
779    	    if (Tcl_IsSafe( interp )) {
780				Tcl_SetObjResult( interp,
781						Tcl_NewStringObj("\"record\" not allowed in a safe interpreter", -1) );
782    			result = TCL_ERROR;
783    		    goto error;
784    	    }
785    		if (sgrabPtr->seqGrab) {
786    			if (objc != 2) {
787					Tcl_WrongNumArgs( interp, 2, objv, NULL );
788    				result = TCL_ERROR;
789    				goto error;
790    			}
791    			if (sgrabPtr->filename == NULL){
792					Tcl_SetObjResult( interp,
793							Tcl_NewStringObj("Need a file specified with -file option", -1) );
794    				result = TCL_ERROR;
795    				goto error;
796    			}
797    			if (noErr != SGStartRecord( sgrabPtr->seqGrab )) {
798					Tcl_SetObjResult( interp,
799							Tcl_NewStringObj("Failed starting recording", -1) );
800    				result = TCL_ERROR;
801    				goto error;
802    			}
803    			sgrabPtr->frameCount = 0;
804    		} else {
805				Tcl_SetObjResult( interp,
806						Tcl_NewStringObj("No sequence grabber", -1) );
807    			result = TCL_ERROR;
808    		}
809		    break;
810        }
811
812        case kGrabberCmdStart: {
813    		if (objc > 2) {
814				Tcl_WrongNumArgs( interp, 2, objv, NULL );
815    			result = TCL_ERROR;
816    			goto error;
817    		}
818    		if (sgrabPtr->seqGrab) {
819    			SGStartPreview( sgrabPtr->seqGrab );
820    			sgrabPtr->flags |= ISRUNNING;
821    			sgrabPtr->frameCount = 0;
822
823    			/*
824    			 * Annulate update pict since invalid now.
825    			 */
826
827    			if (sgrabPtr->updatePictHand) {
828    			    KillPicture(sgrabPtr->updatePictHand);
829    			}
830    			sgrabPtr->updatePictHand = NULL;
831    		} else {
832				Tcl_SetObjResult( interp,
833						Tcl_NewStringObj("No sequence grabber", -1) );
834    			result = TCL_ERROR;
835    		}
836		    break;
837        }
838
839        case kGrabberCmdStop: {
840    		if (objc > 2) {
841				Tcl_WrongNumArgs( interp, 2, objv, NULL );
842    			result = TCL_ERROR;
843    			goto error;
844    		}
845    		if (sgrabPtr->seqGrab) {
846
847    			/*
848    			 * Take a Pict to have to show when getting an update event. Same when pausing.
849    			 */
850
851                if (sgrabPtr->sgChanVideo) {
852        			res = TakePicture( sgrabPtr, &thePic );
853        			sgrabPtr->updatePictHand = thePic;
854    			}
855                SGPause( sgrabPtr->seqGrab, seqGrabPause );
856    			SGStop( sgrabPtr->seqGrab );
857    			sgrabPtr->flags &= ~ISRUNNING;
858                sgrabPtr->frameCount = 0;
859    		} else {
860				Tcl_SetObjResult( interp,
861						Tcl_NewStringObj("No sequence grabber", -1) );
862    			result = TCL_ERROR;
863    		}
864		    break;
865        }
866
867        case kGrabberCmdAudioSettings:
868        case kGrabberCmdVideoSettings: {
869    		if (objc > 2) {
870				Tcl_WrongNumArgs( interp, 2, objv, NULL );
871    			result = TCL_ERROR;
872    			goto error;
873    		}
874    		if (sgrabPtr->seqGrab) {
875
876    			/*
877    			 * Standard dialog boxes for video or audio grabber settings.
878    			 * Pause grabbing during settings. Made automatically.
879    			 */
880
881    			if ((sgrabPtr->updatePictHand == NULL) && (sgrabPtr->sgChanVideo)) {
882        			res = TakePicture( sgrabPtr, &thePic );
883        			sgrabPtr->updatePictHand = thePic;
884        		}
885    			res = SGGetPause( sgrabPtr->seqGrab, &isPaused );
886    			saveIsRunningFlag = sgrabPtr->flags & ISRUNNING;
887        		sgrabPtr->flags &= ~ISRUNNING;
888
889    			if (cmdIndex == kGrabberCmdVideoSettings) {
890    				if (sgrabPtr->sgChanVideo != NULL) {
891    			        /* pausing seems not necessary */
892    			        //res = SGPause( sgrabPtr->seqGrab, seqGrabPause );
893#if TARGET_API_MAC_CARBON
894    					res = SGSettingsDialog( sgrabPtr->seqGrab, sgrabPtr->sgChanVideo,
895    					        0, NULL, 0, NewSGModalFilterUPP( MovableDialogModalFilter ),
896    					        (long) sgrabPtr );
897#else
898    					res = SGSettingsDialog( sgrabPtr->seqGrab, sgrabPtr->sgChanVideo,
899    					        0, NULL, 0, NewSGModalFilterProc( MovableDialogModalFilter ),
900    					        (long) sgrabPtr );
901#endif
902
903    					/* Could also use this one to hide compressor settings. */
904    					/*
905    					res = SGSettingsDialog( sgrabPtr->seqGrab, sgrabPtr->sgChanVideo,
906    					        0, NULL, seqGrabSettingsPreviewOnly, NULL, 0 );
907    					*/
908    			        if (res == noErr) {
909							Tcl_SetObjResult( interp, Tcl_NewBooleanObj(1) );
910    			        } else if (res == userCanceledErr) {
911							Tcl_SetObjResult( interp, Tcl_NewBooleanObj(0) );
912    			        } else {
913    				        CheckAndSetErrorResult( interp, res );
914    						result = TCL_ERROR;
915    			        }
916    				} else {
917						Tcl_SetObjResult( interp,
918								Tcl_NewStringObj("No video channel", -1) );
919    					result = TCL_ERROR;
920    				}
921                    if (TCL_OK != SetInternalVideoOptions( sgrabPtr )) {
922                        /* ??? */
923                    }
924    			} else if (cmdIndex == kGrabberCmdAudioSettings) {
925    				if (sgrabPtr->sgChanAudio != NULL) {
926#if TARGET_API_MAC_CARBON
927    					res = SGSettingsDialog( sgrabPtr->seqGrab, sgrabPtr->sgChanAudio,
928    					        0, NULL, seqGrabSettingsPreviewOnly,
929    							NewSGModalFilterUPP( MovableDialogModalFilter ),
930    							(long) sgrabPtr );
931#else
932    					res = SGSettingsDialog( sgrabPtr->seqGrab, sgrabPtr->sgChanAudio,
933    					        0, NULL, seqGrabSettingsPreviewOnly,
934    							NewSGModalFilterProc( MovableDialogModalFilter ),
935    							(long) sgrabPtr );
936#endif
937    			        if (res == noErr) {
938							Tcl_SetObjResult( interp, Tcl_NewBooleanObj(1) );
939    			        } else if (res == userCanceledErr) {
940							Tcl_SetObjResult( interp, Tcl_NewBooleanObj(0) );
941    			        } else {
942    				        CheckAndSetErrorResult( interp, res );
943    						result = TCL_ERROR;
944    			        }
945    				} else {
946						Tcl_SetObjResult( interp,
947								Tcl_NewStringObj("No audio channel", -1) );
948    					result = TCL_ERROR;
949    				}
950                    if (TCL_OK != SetInternalAudioOptions( sgrabPtr )) {
951                        /* ??? */
952                    }
953    			}
954    	        res = SGPause( sgrabPtr->seqGrab, isPaused );
955
956    	        /* set running flag to what it was */
957    	        if (saveIsRunningFlag & ISRUNNING) {
958    				sgrabPtr->flags |= ISRUNNING;
959    	        } else {
960    	    		sgrabPtr->flags &= ~ISRUNNING;
961    	        }
962    	        sgrabPtr->frameCount = 0;
963    		} else {
964				Tcl_SetObjResult( interp,
965						Tcl_NewStringObj("No sequence grabber", -1) );
966    			result = TCL_ERROR;
967    		}
968        }
969	}
970
971error:
972
973	Tcl_Release( (ClientData) sgrabPtr );
974	return result;
975}
976
977/*
978 *----------------------------------------------------------------------
979 *
980 * ConfigureSeqGrabber --
981 *
982 *		This procedure is called to process an objv/objc list, plus
983 *		the Tk option database, in order to configure (or
984 *		reconfigure) a sequence grabber widget.
985 *
986 * Results:
987 *		The return value is a standard Tcl result.  If TCL_ERROR is
988 *		returned, then the interp's result contains an error message.
989 *
990 * Side effects:
991 *		Configuration information, such as text string, colors, font,
992 *		etc. get set for sgrabPtr;  old resources get freed, if there
993 *		were any.
994 *      Only if the configuration means that a (re)display is necessary,
995 *      the NEWGWORLD flag is set. This triggers a call to 'SeqGrabberWorldChanged'.
996 *
997 *----------------------------------------------------------------------
998 */
999
1000static int
1001ConfigureSeqGrabber( Tcl_Interp *interp, SeqGrabber *sgrabPtr,
1002		int objc, Tcl_Obj *CONST objv[] )
1003{
1004    int             	width, height;
1005	int 				mask = 0L;
1006    int 				ierror;
1007	long				qflag = 0L;
1008    Boolean         	wantVideo, wantAudio;
1009	Tcl_Obj 			*resultObjPtr = NULL;
1010    Tcl_Obj 			*errorResult = NULL;
1011	Tk_PhotoHandle 		tkPhoto = NULL;
1012	Tk_SavedOptions 	savedOptions;
1013	Rect            	winRect;
1014	FSSpec				fss;
1015	PicHandle 			thePic = NULL;
1016	ComponentResult		err;
1017	OSErr				osErr;
1018
1019    /*
1020     * The following loop is potentially executed twice.  During the
1021     * first pass configuration options get set to their new values.
1022     * If there is an error in this pass, we execute a second pass
1023     * to restore all the options to their previous values.
1024     *
1025     * A 'continue' within this loop signals an error condition;
1026     * 'break' when everything went OK.
1027     */
1028
1029    for (ierror = 0; ierror <= 1; ierror++) {
1030		if (!ierror) {
1031		    /*
1032		     * First pass: set options to new values.
1033		     */
1034
1035			if (Tk_SetOptions( interp, (char *) sgrabPtr, sgrabPtr->optionTable, objc,
1036					objv, sgrabPtr->tkwin, &savedOptions, &mask) != TCL_OK ) {
1037				continue;
1038		    }
1039		} else {
1040		    /*
1041		     * Second pass: restore options to old values.
1042		     */
1043
1044		    errorResult = Tcl_GetObjResult( interp );
1045		    Tcl_IncrRefCount( errorResult );
1046		    Tk_RestoreSavedOptions( &savedOptions );
1047		}
1048
1049		/*
1050		 * Init the video and audio channel components.
1051		 * Size of widget. Need to find the geometry first??? (SeqGrabberComputeGeometry).
1052		 * Just take a standard size for now.
1053		 */
1054
1055		if (sgrabPtr->audioOnly == false) {
1056		    wantVideo = true;
1057	    } else {
1058		    wantVideo = false;
1059	    }
1060        if (sgrabPtr->audio) {
1061		    wantAudio = true;
1062        } else {
1063		    wantAudio = false;
1064        }
1065		if (wantVideo && sgrabPtr->sgChanVideo == NULL) {
1066	    	MacSetRect( &winRect, 0, 0, 160, 120 );
1067	     	err = MakeVideoChannel( sgrabPtr->seqGrab,  /* This is the actual
1068	     											 * sequence grabber component. */
1069	    			&(sgrabPtr->sgChanVideo),       /* The video channel component. */
1070	    			&winRect,                       /* A temporary rectangle
1071	    											 * for the video bounds. */
1072	    			sgrabPtr->playDuringRecord,
1073	    			sgrabPtr->willRecord );         /* Prepare for recording? */
1074            if (err != noErr) {
1075    			CheckAndSetErrorResult( interp, err );
1076                continue;
1077            }
1078            if (TCL_OK != SetInternalVideoOptions( sgrabPtr )) {
1079                continue;
1080            }
1081        }
1082        if (wantAudio && sgrabPtr->sgChanAudio == NULL) {
1083	     	err = MakeAudioChannel( sgrabPtr->seqGrab,  /* This is the actual
1084	     											 * sequence grabber component. */
1085	    			&(sgrabPtr->sgChanAudio),       /* The audio channel component. */
1086	    			sgrabPtr->playDuringRecord,
1087	    			sgrabPtr->willRecord );         /* Prepare for recording? */
1088            if (err != noErr) {
1089    			CheckAndSetErrorResult( interp, err );
1090                continue;
1091            }
1092            if (TCL_OK != SetInternalAudioOptions( sgrabPtr )) {
1093                continue;
1094            }
1095        } else if (!wantAudio && sgrabPtr->sgChanAudio != NULL) {
1096            FreeAudioChannel( sgrabPtr );
1097        }
1098
1099		/*
1100		 * Check possible inconsistencies of the options. Return error if found any.
1101		 */
1102
1103		if (sgrabPtr->zoom < 1.0) {
1104			sgrabPtr->zoom = 1.0;
1105		}
1106		if ((sgrabPtr->width > 0) && (sgrabPtr->height > 0)) {
1107			Tcl_SetObjResult( interp,
1108					Tcl_NewStringObj("Cannot set both width and height. Set one to 0", -1) );
1109			continue;
1110		}
1111
1112		/*
1113		 * Geometry and zoom. If any of width, height, size, or zoom changed.
1114		 * We only need to set the flag bit NEWGWORLD for the new geometry to be
1115		 * found and requested in .
1116		 */
1117
1118		if ((mask & SEQGRAB_CONF_NEWGWORLD) || (mask & SEQGRAB_CONF_ZOOM)) {
1119		    if (SeqGrabberComputeGeometry( sgrabPtr, &width, &height, 1 ) != TCL_OK) {
1120				continue;
1121	        }
1122		    sgrabPtr->flags |= NEWGWORLD;
1123		}
1124
1125		if ((mask & SEQGRAB_CONF_PLAYDURINGRECORD) &&
1126				(sgrabPtr->sgChanVideo != NULL)) {
1127	        long    lUsage = 0;
1128
1129	        SGGetChannelUsage( sgrabPtr->sgChanVideo, &lUsage );
1130	        if (sgrabPtr->playDuringRecord) {
1131	            lUsage |= seqGrabPlayDuringRecord;
1132	        } else {
1133	            lUsage &= ~seqGrabPlayDuringRecord;
1134	        }
1135	        err = SGSetChannelUsage( sgrabPtr->sgChanVideo, lUsage );
1136			if (err != noErr) {
1137    			CheckAndSetErrorResult( interp, err );
1138				continue;
1139			}
1140	    }
1141
1142		/*
1143		 * Set quality.
1144		 */
1145
1146		if ((mask & SEQGRAB_CONF_QUALITY) && (sgrabPtr->sgChanVideo != NULL)) {
1147			switch (sgrabPtr->indQuality) {
1148				case SG_QUALITY_MODE_FAST:
1149					qflag = channelPlayFast;
1150					break;
1151				case SG_QUALITY_MODE_NORMAL:
1152					qflag = channelPlayNormal;
1153					break;
1154				case SG_QUALITY_MODE_HIGH:
1155					qflag = channelPlayHighQuality;
1156					break;
1157			}
1158			qflag |= channelPlayAllData;
1159			err = SGSetChannelPlayFlags( sgrabPtr->sgChanVideo, qflag );
1160			if (err != noErr) {
1161    			CheckAndSetErrorResult( interp, err );
1162				continue;
1163			}
1164		}
1165
1166		/*
1167		 * Specify an output file when recording.
1168		 */
1169
1170		if (mask & SEQGRAB_CONF_FILE) {
1171			osErr = QTTclNativePathNameToFSSpec( interp, sgrabPtr->filename, &fss );
1172			if ((osErr != fnfErr) && (osErr != noErr)) {
1173				resultObjPtr = Tcl_NewStringObj("Failed making FSSpec from filename \"", -1);
1174				Tcl_AppendStringsToObj( resultObjPtr, sgrabPtr->filename, "\"",
1175						(char *) NULL);
1176				Tcl_SetObjResult( interp, resultObjPtr );
1177				continue;
1178			}
1179			if (noErr != SGSetDataOutput( sgrabPtr->seqGrab, &fss, seqGrabToDisk )) {
1180				resultObjPtr = Tcl_NewStringObj("Failed setting data output file \"", -1);
1181				Tcl_AppendStringsToObj( resultObjPtr, sgrabPtr->filename, "\"",
1182						(char *) NULL);
1183				Tcl_SetObjResult( interp, resultObjPtr );
1184				continue;
1185			}
1186		}
1187
1188		if (mask & SEQGRAB_CONF_VIDEOCOMPRESSOR) {
1189            if (sgrabPtr->sgChanVideo == NULL) {
1190				resultObjPtr = Tcl_NewStringObj(
1191                        "Can't configure video compressor without video channel", -1);
1192				Tcl_SetObjResult( interp, resultObjPtr );
1193				continue;
1194            } else {
1195                OSType				compressorType;
1196                unsigned long		lType;
1197
1198                memcpy( &lType, sgrabPtr->videoCompressor, 4 );
1199                compressorType = EndianU32_NtoB( lType );
1200                err = SGSetVideoCompressorType( sgrabPtr->sgChanVideo, compressorType );
1201                if (err != noErr) {
1202                    CheckAndSetErrorResult( interp, err );
1203                    continue;
1204                }
1205            }
1206        }
1207
1208		if (mask & SEQGRAB_CONF_FRAMERATE) {
1209            if (sgrabPtr->sgChanVideo == NULL) {
1210				resultObjPtr = Tcl_NewStringObj(
1211                        "Can't configure video compressor without video channel", -1);
1212				Tcl_SetObjResult( interp, resultObjPtr );
1213				continue;
1214            } else {
1215                Fixed		frameRate;
1216
1217                frameRate = X2Fix( sgrabPtr->frameRate );
1218                err = SGSetFrameRate( sgrabPtr->sgChanVideo, frameRate );
1219                if (err != noErr) {
1220                    CheckAndSetErrorResult( interp, err );
1221                    continue;
1222                }
1223            }
1224        }
1225
1226		if (mask & SEQGRAB_CONF_VOLUME) {
1227            short        		volume;
1228
1229            volume = (short) ( 256.0 * sgrabPtr->volume );
1230            err = SGSetChannelVolume( sgrabPtr->sgChanAudio, volume );
1231            if (err != noErr) {
1232                CheckAndSetErrorResult( interp, err );
1233                continue;
1234            }
1235        }
1236
1237		/*
1238		 * If we want to overlay a tk image in one of the corners, translate it to
1239		 * a mac picture.
1240		 */
1241
1242		/* Beware! must not be done during previewing, only before started previewing. */
1243
1244		if (Tk_IsMapped(sgrabPtr->tkwin) && sgrabPtr->overlayimage &&
1245				(sgrabPtr->overlayPictHand == NULL)) {
1246			Tcl_SetObjResult( interp,
1247					Tcl_NewStringObj("Overlayimage must be made before window is mapped", -1) );
1248			continue;
1249		}
1250
1251		if (sgrabPtr->overlayimage && (sgrabPtr->overlayPictHand == NULL)) {
1252			tkPhoto = Tk_FindPhoto( interp, sgrabPtr->overlayimage );
1253			if (!tkPhoto) {
1254				Tcl_SetObjResult( interp,
1255						Tcl_NewStringObj("Image not found", -1) );
1256				continue;
1257			}
1258			if (ConvertTkPhotoToPicture( interp, tkPhoto, &thePic ) != TCL_OK) {
1259				Tcl_SetObjResult( interp,
1260						Tcl_NewStringObj("Error converting image to Picture", -1) );
1261				continue;
1262			}
1263			sgrabPtr->overlayPictHand = thePic;
1264		}
1265
1266		/*
1267		 * If we came so far break out of the ierror loop.
1268		 */
1269
1270		break;
1271    }
1272    if (ierror) {
1273		Tcl_SetObjResult( interp, errorResult );
1274		Tcl_DecrRefCount( errorResult );
1275		return TCL_ERROR;
1276    } else {
1277		Tk_FreeSavedOptions( &savedOptions );
1278		return TCL_OK;
1279    }
1280}
1281
1282static int
1283SetInternalVideoOptions( SeqGrabber *sgrabPtr )
1284{
1285    OSType				compressorType;
1286    Fixed        		frameRate;
1287	ComponentResult		err;
1288
1289    if (sgrabPtr->sgChanVideo == NULL) {
1290        return TCL_OK;
1291    }
1292
1293    err = SGGetVideoCompressorType( sgrabPtr->sgChanVideo, &compressorType );
1294    if (err == noErr) {
1295        unsigned long			lType;
1296
1297        lType = EndianU32_BtoN( compressorType );
1298        sgrabPtr->videoCompressor = (char *) ckalloc(5);
1299        memset( (void *) sgrabPtr->videoCompressor, 0, 5 );
1300        memcpy( sgrabPtr->videoCompressor, &lType, 4 );
1301    } else {
1302        return TCL_ERROR;
1303    }
1304
1305    err = SGGetFrameRate( sgrabPtr->sgChanVideo, &frameRate );
1306    if (err == noErr) {
1307        sgrabPtr->frameRate = Fix2X( frameRate );
1308    } else {
1309        return TCL_ERROR;
1310    }
1311    return TCL_OK;
1312}
1313
1314static int
1315SetInternalAudioOptions( SeqGrabber *sgrabPtr )
1316{
1317    short        		volume;
1318	ComponentResult		err;
1319
1320    if (sgrabPtr->sgChanAudio == NULL) {
1321        return TCL_OK;
1322    }
1323
1324    /*
1325     *  The volume setting of the channel represented as a 16-bit, fixed-point number. The high-
1326     *  order 8 bits contain the integer part of the value; the low-order 8 bits
1327     *  contain the fractional part. Volume values range from -1.0 to 1.0.
1328     *  Negative values play no sound but preserve the absolute value of the volume setting.
1329     */
1330
1331    err = SGGetChannelVolume( sgrabPtr->sgChanAudio, &volume );
1332    if (err == noErr) {
1333        sgrabPtr->volume = volume/256.0;
1334    } else {
1335        return TCL_ERROR;
1336    }
1337    return TCL_OK;
1338}
1339
1340/*
1341 *----------------------------------------------------------------------
1342 *
1343 * SeqGrabberComputeGeometry --
1344 *
1345 *		Finds the widget size to request at tk. It manages the zooming
1346 *		also.
1347 *
1348 * Results:
1349 *		The return value is a standard Tcl result. If TCL_ERROR is
1350 *		returned, then the interp's result contains an error message.
1351 *
1352 * Side effects:
1353 *		Returns width and height in function arguments. Note that these
1354 *		are the actual width and height to request from tk, and not the
1355 *		options. Set the video rectangle in source coordinates (zoom)
1356 *		if setZoom is true.
1357 *
1358 *----------------------------------------------------------------------
1359 */
1360
1361static int
1362SeqGrabberComputeGeometry( SeqGrabber *sgrabPtr, int *width, int *height, int setZoom )
1363{
1364    Tcl_Interp 	*interp;
1365	Rect		srcVideoRect, newVideoRect, origVideoRect;
1366	Rect		winRect;
1367	int			divisor = 1;
1368	short	    newVideoWidth, newVideoHeight;
1369	double		goldenRatio;
1370	ComponentResult	err = noErr;
1371
1372    interp = sgrabPtr->interp;
1373	*width = 0;
1374	*height = 0;
1375
1376	/*
1377	 * First, make sure we have got a video source at all.
1378	 */
1379
1380    if (sgrabPtr->sgChanVideo == NULL) {
1381		*width = 1;
1382		*height = 1;
1383	} else if (sgrabPtr->seqGrab && sgrabPtr->sgChanVideo) {
1384		winRect.left = 0;
1385		winRect.top = 0;
1386
1387		/*
1388		 * Get and store the maximal video source size. Here?
1389		 */
1390
1391		SGGetSrcVideoBounds( sgrabPtr->sgChanVideo, &srcVideoRect );
1392		sgrabPtr->srcWidth = srcVideoRect.right - srcVideoRect.left;
1393		sgrabPtr->srcHeight = srcVideoRect.bottom - srcVideoRect.top;
1394
1395		/*
1396		 * There are two possibilities here: either we have got one of
1397		 * '-width' or '-height' > 0; use these in this case. Or use the
1398		 * '-size' option (quarter, half, or full).
1399		 */
1400
1401		if (sgrabPtr->width > 0 || sgrabPtr->height > 0) {
1402			goldenRatio = (double) sgrabPtr->srcWidth/(double) sgrabPtr->srcHeight;
1403			if (sgrabPtr->width > 0 && sgrabPtr->height == 0) {
1404				*width = sgrabPtr->width;
1405				*height = (int) ((double) sgrabPtr->width/goldenRatio);
1406			} else if (sgrabPtr->width == 0 && sgrabPtr->height > 0) {
1407				*height = sgrabPtr->height;
1408				*width = (int) (goldenRatio * sgrabPtr->height);
1409			} else {
1410
1411				/* This code should never be executed; my QuickCam becomes weired! */
1412				*width = sgrabPtr->width;
1413				*height = sgrabPtr->height;
1414			}
1415
1416			/*
1417			 * Check max source size.
1418			 */
1419
1420			if ((sgrabPtr->width > sgrabPtr->srcWidth) ||
1421					(sgrabPtr->height > sgrabPtr->srcHeight)) {
1422				sgrabPtr->width = sgrabPtr->srcWidth;
1423				*width =  sgrabPtr->srcWidth;
1424				sgrabPtr->height = sgrabPtr->srcHeight;
1425				*height =  sgrabPtr->srcHeight;
1426			}
1427		} else {
1428			switch (sgrabPtr->indSize) {
1429				case SG_WIDGET_SIZE_QUARTER:
1430					divisor = 4;
1431					break;
1432				case SG_WIDGET_SIZE_HALF:
1433					divisor = 2;
1434					break;
1435				case SG_WIDGET_SIZE_FULL:
1436					divisor = 1;
1437					break;
1438			}
1439			*width = sgrabPtr->srcWidth/divisor;
1440			*height = sgrabPtr->srcHeight/divisor;
1441		}
1442
1443		if (setZoom) {
1444
1445			/*
1446			 * If we have got a zoomed in video rectangle, do that.
1447			 * In video source coordinates!
1448			 */
1449
1450			newVideoWidth = (short) ((double) sgrabPtr->srcWidth/sgrabPtr->zoom);
1451			newVideoHeight = (short) ((double) sgrabPtr->srcHeight/sgrabPtr->zoom);
1452			MacSetRect( &newVideoRect, (short) ((sgrabPtr->srcWidth - newVideoWidth)/2),
1453					 (short) ((sgrabPtr->srcHeight - newVideoHeight)/2),
1454					 (short) ((sgrabPtr->srcWidth + newVideoWidth)/2),
1455					 (short) ((sgrabPtr->srcHeight + newVideoHeight)/2) );
1456			err = SGGetVideoRect( sgrabPtr->sgChanVideo, &origVideoRect );
1457			if (!MacEqualRect( &origVideoRect, &newVideoRect )) {
1458				err = SGSetVideoRect( sgrabPtr->sgChanVideo, &newVideoRect );
1459			}
1460			if (err != noErr) {
1461				CheckAndSetErrorResult( interp, err );
1462			}
1463		}
1464	}
1465
1466	/* Add the padding. Presently unused. */
1467	*width += 2 * sgrabPtr->padx;
1468	*height += 2 * sgrabPtr->pady;
1469
1470	return TCL_OK;
1471}
1472
1473/*
1474 *----------------------------------------------------------------------
1475 *
1476 * SeqGrabberEventProc --
1477 *
1478 *		Deal with Sequence Grabber Events; these events are TCL events
1479 *
1480 * Results:
1481 *		None.
1482 *
1483 * Side effects:
1484 *		Depends on event. Schedules a redisplay.
1485 *
1486 *----------------------------------------------------------------------
1487 */
1488
1489static void
1490SeqGrabberEventProc( ClientData clientData, XEvent *eventPtr )
1491{
1492	SeqGrabber 	*sgrabPtr = (SeqGrabber *) clientData;
1493
1494	/*
1495	 * Depending on the event type, do different things. Set the relevant flag
1496	 * bits for use in 'DisplaySeqGrabber'. When first mapped it should be running.
1497	 */
1498
1499	if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
1500		sgrabPtr->flags |= UPDATEGRABBER;
1501		goto redraw;
1502	} else if (eventPtr->type == ConfigureNotify) {
1503		goto redraw;
1504	} else if (eventPtr->type == MapNotify) {
1505		sgrabPtr->flags |= NEWGWORLD;
1506        sgrabPtr->flags |= ISRUNNING;
1507		goto redraw;
1508	} else if (eventPtr->type == UnmapNotify) {
1509		if (sgrabPtr->seqGrab) {
1510
1511			/*
1512			 * Do some cleaning up:
1513			 * Set flag in global 'gIsRunningFlags' to stop serving the grabber.
1514			 */
1515
1516			SGStop( sgrabPtr->seqGrab );
1517			/*
1518			if (sgrabPtr->updatePictHand) {
1519			    KillPicture( sgrabPtr->updatePictHand );
1520			}
1521			*/
1522            sgrabPtr->flags &= ~ISRUNNING;
1523			gIsRunningFlags &= ~SEQ_GRABBER_RUNS;
1524        }
1525	} else if (eventPtr->type == DestroyNotify) {
1526
1527		/*
1528		 * We are being destroyed.
1529		 */
1530
1531		if (sgrabPtr->tkwin != NULL) {
1532			sgrabPtr->tkwin = NULL;
1533			Tcl_DeleteCommandFromToken( sgrabPtr->interp, sgrabPtr->widgetCmd );
1534		}
1535		if (sgrabPtr->flags & REDRAW_PENDING) {
1536			Tcl_CancelIdleCall( DisplaySeqGrabber, (ClientData) sgrabPtr );
1537		}
1538		DestroySeqGrabber( sgrabPtr );
1539	}
1540	return;
1541
1542	redraw:
1543
1544	/*
1545	 * Now we know that the event type was such that the widget needs to be redrawn.
1546	 * Schedule the redrawing procedure.
1547	 */
1548
1549	if ((sgrabPtr->tkwin != NULL) && Tk_IsMapped(sgrabPtr->tkwin) &&
1550			!(sgrabPtr->flags & REDRAW_PENDING)) {
1551		Tcl_DoWhenIdle( DisplaySeqGrabber, (ClientData) sgrabPtr );
1552		sgrabPtr->flags |= REDRAW_PENDING;
1553	}
1554}
1555
1556/*
1557 *----------------------------------------------------------------------
1558 *
1559 * SeqGrabberDeleted Proc --
1560 *
1561 *		Deletes a Sequence Grabber
1562 *
1563 * Results:
1564 *		None.
1565 *
1566 * Side effects:
1567 *		Uh, deletes that sequence grabber. Hopefully frees memory.
1568 *
1569 *----------------------------------------------------------------------
1570 */
1571
1572static void
1573SeqGrabberDeletedProc( ClientData clientData )
1574{
1575
1576	SeqGrabber *sgrabPtr = (SeqGrabber *) clientData;
1577	Tk_Window tkwin = sgrabPtr->tkwin;
1578
1579	QTTclDebugPrintf( sgrabPtr->interp, 2, "SeqGrabberDeletedProc" );
1580
1581	if (tkwin != NULL) {
1582		sgrabPtr->tkwin = NULL;
1583		Tk_DestroyWindow( tkwin );
1584	}
1585}
1586
1587/*
1588 *----------------------------------------------------------------------
1589 *
1590 * DestroySeqGrabber --
1591 *
1592 *		Deletes a SeqGrabber Widget. Most things cleaned up with Tk_FreeOptions
1593 *		but some things are freed up by me.
1594 *
1595 * Results:
1596 *		None.
1597 *
1598 * Side effects:
1599 *		Hopefully frees memory.
1600 *
1601 *----------------------------------------------------------------------
1602 */
1603
1604static void
1605DestroySeqGrabber( SeqGrabber *sgrabPtr )
1606{
1607
1608#ifdef _WIN32
1609	HWND    tempHwnd;
1610	if (sgrabPtr->tkwin) {
1611		tempHwnd = TkWinGetHWND( Tk_WindowId(sgrabPtr->tkwin) );
1612		/* should we use 'GetHWNDPort()' instead ? */
1613		//DestroyPortAssociation( (CGrafPtr) GetNativeWindowPort(tempHwnd) );
1614		DestroyPortAssociation( (CGrafPtr) GetHWNDPort(tempHwnd) );
1615	}
1616#endif
1617
1618	QTTclDebugPrintf( sgrabPtr->interp, 2, "DestroySeqGrabber" );
1619
1620	gIsRunningFlags &= ~SEQ_GRABBER_RUNS;
1621
1622    FreeVideoChannel( sgrabPtr );
1623    FreeAudioChannel( sgrabPtr );
1624	/*
1625	if (sgrabPtr->videoBottleTempPort) {
1626		CloseCPort( &sgrabPtr->videoBottleTempPort ); causes crash if no port created yet
1627	}*/
1628	if (sgrabPtr->seqGrab != NULL) {
1629		SGStop( sgrabPtr->seqGrab );
1630		CloseComponent( sgrabPtr->seqGrab );
1631		gSGrabPtr = NULL;
1632		sgrabPtr->seqGrab = NULL;
1633	}
1634	if (sgrabPtr->overlayPictHand != NULL) {
1635		KillPicture( sgrabPtr->overlayPictHand );
1636	}
1637	if (sgrabPtr->updatePictHand != NULL) {
1638	    KillPicture( sgrabPtr->updatePictHand );
1639	}
1640	if (sgrabPtr->asyncImageHandlerPtr != NULL) {
1641        AsyncImageHandlerRecord	*asyncPtr;
1642
1643        asyncPtr = (AsyncImageHandlerRecord *) sgrabPtr->asyncImageHandlerPtr;
1644        Tcl_DeleteTimerHandler( asyncPtr->timerToken );
1645        Tcl_DecrRefCount( asyncPtr->commandObjPtr );
1646        ckfree( (char *) asyncPtr );
1647	}
1648
1649#if TARGET_API_MAC_CARBON
1650	/*
1651	 *  On Carbon, be sure to remove the timer.
1652	 */
1653
1654    if (gCarbonGrabberTimerRef != NULL) {
1655        RemoveEventLoopTimer( gCarbonGrabberTimerRef );
1656        gCarbonGrabberTimerRef = NULL;
1657    }
1658#endif
1659
1660	Tk_FreeConfigOptions( (char *) sgrabPtr, sgrabPtr->optionTable, sgrabPtr->tkwin );
1661	Tcl_EventuallyFree( (ClientData) sgrabPtr, TCL_DYNAMIC );
1662}
1663
1664/*
1665 *----------------------------------------------------------------------
1666 *
1667 * SeqGrabberWorldChanged --
1668 *
1669 *		Something changed, arrange for the movie to be redisplayed.
1670 *      Compute geometry.
1671 *
1672 * Results:
1673 *		None.
1674 *
1675 * Side effects:
1676 *		SeqGrabber Widget displayed: if already on display it is scheduled for
1677 *		a renewed display, else, the size is requested by the tk geometry manager,
1678 *		and displayed upon a MapNotify event.
1679 *
1680 *----------------------------------------------------------------------
1681 */
1682
1683static void
1684SeqGrabberWorldChanged( ClientData clientData )
1685{
1686	SeqGrabber 		*sgrabPtr = (SeqGrabber *) clientData;
1687	int				width, height;
1688
1689	/*
1690	 * If not already scheduled for (re)display, it should be if it's mapped,
1691	 * else upon a MapNotify event.
1692	 */
1693
1694	if (Tk_IsMapped(sgrabPtr->tkwin) && !(sgrabPtr->flags & REDRAW_PENDING)) {
1695		Tcl_DoWhenIdle( DisplaySeqGrabber, (ClientData) sgrabPtr );
1696		sgrabPtr->flags |= REDRAW_PENDING;
1697	}
1698
1699	/*
1700	 * Get the desired width and height to request. We always finds zoom which is uneconomical.
1701	 */
1702
1703	SeqGrabberComputeGeometry( sgrabPtr, &width, &height, 1 );
1704
1705	if (sgrabPtr->seqGrab && sgrabPtr->sgChanVideo) {
1706
1707		/*
1708		 * After getting our geometry above, let tk also know.
1709		 */
1710
1711		Tk_GeometryRequest( sgrabPtr->tkwin, width, height );
1712		Tk_SetInternalBorder( sgrabPtr->tkwin, 0 );
1713		sgrabPtr->flags |= NEWGWORLD;
1714	}
1715	sgrabPtr->frameCount = 0;
1716}
1717
1718/*
1719 *----------------------------------------------------------------------
1720 *
1721 * CreateSeqGrabber --
1722 *
1723 *		Create a Sequence Grabber Object
1724 *
1725 * Results:
1726 *		Return pointer to sequence grabber object
1727 *
1728 * Side effects:
1729 *		Memory allocated
1730 *
1731 *----------------------------------------------------------------------
1732 */
1733
1734static SeqGrabber *
1735CreateSeqGrabber( Tk_Window tkwin )
1736{
1737    SeqGrabber *sgPtr = (SeqGrabber *) ckalloc(sizeof(SeqGrabber));
1738    memset((void *) sgPtr, 0, (sizeof(SeqGrabber)));
1739	return sgPtr;
1740}
1741
1742/*
1743 *----------------------------------------------------------------------
1744 *
1745 * DisplaySeqGrabber --
1746 *
1747 *		Display a sequence grabber window.
1748 *
1749 * Results:
1750 *		None.
1751 *
1752 * Side effects:
1753 *		With luck, the grabber is displayed
1754 *
1755 *----------------------------------------------------------------------
1756 */
1757
1758static void
1759DisplaySeqGrabber( ClientData clientData )
1760{
1761	SeqGrabber *sgrabPtr = (SeqGrabber *) clientData;
1762	CGrafPtr	 		saveWorld = NULL;
1763	GDHandle 			saveDevice = NULL;
1764	GWorldPtr			sgGWorldPtr = NULL;
1765	ComponentResult		res;
1766	Rect 				tkRect;
1767	static RgnHandle	region = NULL;
1768	static RgnHandle	saveRegion = NULL;
1769
1770	if (sgrabPtr->sgChanVideo == NULL) {
1771	    return;
1772	}
1773
1774	/* The first time we allocate a region to help us with update events. */
1775	if (region == NULL) {
1776		region = NewRgn();
1777	}
1778	if (saveRegion == NULL) {
1779		saveRegion = NewRgn();
1780	}
1781
1782	/* Save current graphics world, reset at end */
1783	GetGWorld( &saveWorld, &saveDevice );
1784
1785	/* Are we ready to display. Clear the redraw pending state. */
1786	sgrabPtr->flags &= ~REDRAW_PENDING;
1787
1788	/*
1789	 * Make sure that the sequence grabber widget still exists and is mapped to the
1790	 * display.
1791	 */
1792
1793	if ((sgrabPtr->tkwin == NULL) || !Tk_IsMapped(sgrabPtr->tkwin)) {
1794		return;
1795	}
1796
1797	if (sgrabPtr->seqGrab) {
1798
1799		/*
1800		 * Get our graph port, set it, and get the local coordinates available to us.
1801		 */
1802
1803		sgGWorldPtr = QTTclMacGetDrawablePort( Tk_WindowId(sgrabPtr->tkwin) );
1804		MacSetPort( (GrafPtr) sgGWorldPtr );
1805		GetClip( saveRegion );
1806		QTTclMacWinBounds( (TkWindow *) sgrabPtr->tkwin, &tkRect );
1807
1808		/*
1809		 * This gives the total size of the widget including the padding of the widget
1810		 * itself (which is not used for the moment). Therefore, subtract padding.
1811		 */
1812
1813		tkRect.left += sgrabPtr->padx;
1814		tkRect.right -= sgrabPtr->padx;
1815		tkRect.top += sgrabPtr->pady;
1816		tkRect.bottom -= sgrabPtr->pady;
1817
1818		if (sgrabPtr->flags & NEWGWORLD) {
1819
1820			/*
1821			 * This associates the sequence grabber with the actual graphics world.
1822			 * It should have been done already, before the video channel creation!
1823			 */
1824
1825			SGSetGWorld( sgrabPtr->seqGrab, sgGWorldPtr, NULL );
1826
1827			/*
1828			 * We start the previewing here only if not started before.
1829			 * Set the global flag 'gIsRunningFlags' that is used to process Mac events.
1830			 */
1831
1832            if (!(gIsRunningFlags & SEQ_GRABBER_RUNS)) {
1833    			SGStartPreview( sgrabPtr->seqGrab );
1834			    //SGPause( sgrabPtr->seqGrab, seqGrabUnpause );
1835    	        gIsRunningFlags |= SEQ_GRABBER_RUNS;
1836	        }
1837			sgrabPtr->flags &= ~NEWGWORLD;
1838		}
1839
1840		/*
1841		 * Treat the actual update event.
1842		 */
1843
1844		if (sgrabPtr->flags & UPDATEGRABBER) {
1845			SGUpdate( sgrabPtr->seqGrab, NULL );
1846			if (!(sgrabPtr->flags & ISRUNNING)) {
1847
1848				/*
1849				 * Here we copy the Pict that should have been taken when stopped or paused.
1850				 * Should fix old clip region!!!
1851				 */
1852
1853                if (sgrabPtr->updatePictHand) {
1854#if TARGET_OS_MAC
1855                    region = QTTclMacVisableClipRgn( ((TkWindow *) (sgrabPtr->tkwin)) );
1856			        SetClip( region );
1857#endif
1858                    DrawPicture( sgrabPtr->updatePictHand, &tkRect );
1859                }
1860 			}
1861			sgrabPtr->flags &= ~UPDATEGRABBER;
1862		}
1863
1864		/*
1865		 * Here we should set the clipping region equal to the visible region of the
1866		 * tk widget. My Grayscale QuickCam seems not to support clipping :-(
1867		 */
1868
1869#if TARGET_OS_MAC
1870        // A lot of guessing here...
1871        region = QTTclMacVisableClipRgn( ((TkWindow *) (sgrabPtr->tkwin)) );
1872		SGSetChannelClip( sgrabPtr->seqGrab, region );
1873#endif
1874
1875		/*
1876		 * If we decreased our size, it is possible that other parts need to be given
1877		 * an update event. Seems to work! Crash reason sometimes?
1878		 */
1879		 /*
1880			QTTclMacWinBounds( (TkWindow *) sgrabPtr->tkwin, &dirtyRect );
1881			InvalRect( &dirtyRect );
1882			*/
1883		/*
1884		 * Set the the video channel bounds. This is the actual size on screen without padding.
1885		 * Local coordinates with respect to the complete window.
1886		 */
1887
1888		res = SGSetChannelBounds( sgrabPtr->sgChanVideo, &tkRect );
1889		sgrabPtr->sgWidth = tkRect.right - tkRect.left;
1890		sgrabPtr->sgHeight = tkRect.bottom - tkRect.top;
1891
1892		SetClip( saveRegion );
1893	}
1894
1895	/* Reset the graphics world to the previous one. */
1896	SetGWorld( saveWorld, saveDevice );
1897}
1898
1899/*
1900 *----------------------------------------------------------------------
1901 *
1902 * SeqGrabberMacEvent --
1903 *
1904 *		Processes events
1905 *
1906 * Results:
1907 *		Always 0 since we only respond to null events.
1908 *
1909 * Side effects:
1910 *		Gives time to the sequence grabber through a 'SGIdle' call.
1911 *
1912 *----------------------------------------------------------------------
1913 */
1914
1915int
1916SeqGrabberMacEvent( EventRecord *eventPtr )
1917{
1918	GWorldPtr           saveWorld = NULL;
1919	GWorldPtr           sgGWorldPtr = NULL;
1920	GDHandle            saveDevice = NULL;
1921	ComponentResult		err;
1922	static RgnHandle	region = NULL;
1923	static RgnHandle	saveRegion = NULL;
1924
1925	/* The first time we allocate a region to help us with update events. */
1926	if (region == NULL) {
1927		region = NewRgn();
1928	}
1929	if (saveRegion == NULL) {
1930		saveRegion = NewRgn();
1931	}
1932	if (gSGrabPtr == NULL) {
1933		return 0;
1934	}
1935
1936	/*
1937	 * We catch only 'nullEvent'�s here.
1938	 */
1939
1940	if (eventPtr->what == nullEvent) {
1941		if (Tk_IsMapped(gSGrabPtr->tkwin) && gSGrabPtr->seqGrab) {
1942
1943    		sgGWorldPtr = QTTclMacGetDrawablePort( Tk_WindowId(gSGrabPtr->tkwin) );
1944			GetGWorld( &saveWorld, &saveDevice );
1945			SetGWorld( sgGWorldPtr, NULL );
1946			GetClip( saveRegion );
1947		    //MacSetPort( (GrafPort *) sgGWorldPtr );	//	shoudn't need this
1948
1949			/* We must find out if the present clip region for the movie is invalid. */
1950
1951#if TARGET_OS_MAC
1952			if (MyIsClipRegionInvalid( gSGrabPtr->tkwin )) {
1953    			region = QTTclMacVisableClipRgn( (TkWindow *) gSGrabPtr->tkwin );
1954        		SGSetChannelClip( gSGrabPtr->seqGrab, region );
1955				SetClip( region );
1956			}
1957#endif
1958			/*
1959			 * Here the sequence grabber is served.
1960			 */
1961
1962			err = SGIdle( gSGrabPtr->seqGrab );
1963			if (err != noErr) {
1964				SGStop( gSGrabPtr->seqGrab );
1965			}
1966			SetClip( saveRegion );
1967			SetGWorld( saveWorld, saveDevice );
1968		} else if (gSGrabPtr->audioOnly && gSGrabPtr->sgChanAudio) {
1969
1970			err = SGIdle( gSGrabPtr->seqGrab );
1971			if (err != noErr) {
1972				SGStop( gSGrabPtr->seqGrab );
1973			}
1974		}
1975	}
1976	return 0;
1977}
1978
1979
1980#if TARGET_API_MAC_CARBON
1981pascal void
1982SeqGrabberCarbonTimer( EventLoopTimerRef theTimer, void *userData )
1983{
1984    EventRecord     eventRec;
1985
1986    /* The grabber doesn't care what events it gets. */
1987    eventRec.what = nullEvent;
1988    SeqGrabberMacEvent( &eventRec );
1989}
1990#endif
1991
1992
1993/*
1994 *----------------------------------------------------------------------
1995 *
1996 * QTGrabberWinProc --
1997 *
1998 *		This is the window callback procedure for Windows only.
1999 *
2000 * Results:
2001 *		Same as the original WinProc.
2002 *
2003 * Side effects:
2004 *		First the Mac event procedure called, then the original WinProc.
2005 *
2006 *----------------------------------------------------------------------
2007 */
2008
2009#ifdef _WIN32
2010LRESULT CALLBACK
2011QTGrabberWinProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
2012{
2013
2014	if(GetNativeWindowPort(hWnd)) {
2015		MSG	        msg;
2016		EventRecord	macEvent;
2017		LONG        thePoint = GetMessagePos();
2018
2019		msg.hwnd = hWnd;
2020		msg.message = message;
2021		msg.wParam = wParam;
2022		msg.lParam = lParam;
2023		msg.time = GetMessageTime();
2024		msg.pt.x = LOWORD(thePoint);
2025		msg.pt.y = HIWORD(thePoint);
2026
2027		/* Convert the message to a QTML event   Keep it for the moment. */
2028		WinEventToMacEvent( &msg, &macEvent );
2029		if (gSGrabPtr && gSGrabPtr->seqGrab) {
2030			if (noErr != SGIdle( gSGrabPtr->seqGrab )) {
2031				SGStop( gSGrabPtr->seqGrab );
2032			}
2033		}
2034	}
2035
2036	/*
2037	 * Call the original winproc given by tk.
2038	 */
2039
2040	if (gSGrabPtr && gSGrabPtr->winEventProc) {
2041		return CallWindowProc( (WNDPROC) (gSGrabPtr->winEventProc), hWnd, message,
2042				wParam, lParam );
2043	} else {
2044		return 0;
2045	}
2046}
2047#endif      // _WIN32
2048
2049/*
2050 *----------------------------------------------------------------------
2051 *
2052 * SeqGrabberExitProc --
2053 *
2054 *		Last bit of Mac cleanup
2055 *
2056 * Results:
2057 *		None.
2058 *
2059 * Side effects:
2060 *
2061 *
2062 *----------------------------------------------------------------------
2063 */
2064
2065static void
2066SeqGrabberExitProc( ClientData clientData )
2067{
2068	SeqGrabber *sgrabPtr = (SeqGrabber *) clientData;
2069
2070	/* Only if not already destroyed the sequence grabber. */
2071	if (sgrabPtr != NULL) {
2072		DestroySeqGrabber( sgrabPtr );
2073	}
2074}
2075
2076/*
2077 *----------------------------------------------------------------------
2078 *
2079 * TakePicture --
2080 *
2081 *		Takes a Pict from the video.
2082 *
2083 * Results:
2084 *
2085 * Side effects:
2086 *      Returns the Pict. A Picture record allocated.
2087 *      Free it with KillPicture when finished.
2088 *
2089 *----------------------------------------------------------------------
2090 */
2091
2092static pascal ComponentResult
2093TakePicture( SeqGrabber *sgrabPtr, PicHandle *thePic )
2094{
2095    ComponentResult res;
2096	Byte			isPaused;
2097	Rect            aRect;
2098
2099	/*
2100	 * Critical to choose the flag 'grabPictOffScreen' to make it work.
2101	 * If we failed, try taking the picture without 'grabPictCurrentImage'.
2102	 */
2103
2104	res = SGGetChannelBounds( sgrabPtr->sgChanVideo, &aRect );
2105	res = SGGetPause( sgrabPtr->seqGrab, &isPaused );
2106
2107	res = SGGrabPict( sgrabPtr->seqGrab, thePic, &aRect, 0,
2108			grabPictIgnoreClip | grabPictCurrentImage | grabPictOffScreen );
2109	if (!thePic || (res != noErr)) {
2110		res = SGGrabPict( sgrabPtr->seqGrab, thePic, &aRect, 0,
2111				grabPictIgnoreClip | grabPictOffScreen );
2112	}
2113
2114    return res;
2115}
2116
2117
2118/*--- Non Tcl specific code (one exception) ----------------------------------*/
2119
2120/*
2121 *----------------------------------------------------------------------
2122 *
2123 * MakeMySequenceGrabber --
2124 *
2125 *		Creates the actual sequence grabber component.
2126 *
2127 * Results:
2128 *		The actual sequence grabber component.
2129 *
2130 * Side effects:
2131 *		None.
2132 *
2133 *----------------------------------------------------------------------
2134 */
2135
2136static SeqGrabComponent
2137MakeMySequenceGrabber( void )
2138{
2139	SeqGrabComponent		seqGrab = NULL;
2140	ComponentResult		    err = noErr;
2141
2142	/* open up the default sequence grabber */
2143	seqGrab = OpenDefaultComponent( SeqGrabComponentType, 0 );
2144	if (seqGrab != NULL) {
2145
2146		/* initialize the default sequence grabber component */
2147		err = SGInitialize(seqGrab);
2148	}
2149
2150	/* clean up on failure */
2151	if (err && (seqGrab != NULL)) {
2152		CloseComponent(seqGrab);
2153		seqGrab = NULL;
2154	}
2155	return seqGrab;
2156}
2157
2158/*
2159 *----------------------------------------------------------------------
2160 *
2161 * MakeVideoChannel, MakeAudioChannel --
2162 *
2163 *		Creates the actual sequence grabber *channel* components; Audio
2164 *		and video as respectively.
2165 *
2166 * Results:
2167 *		None.
2168 *
2169 * Side effects:
2170 *		None.
2171 *
2172 *----------------------------------------------------------------------
2173 */
2174
2175static pascal ComponentResult
2176MakeVideoChannel(
2177        SeqGrabComponent seqGrab,
2178		SGChannel *sgchanVideo,
2179		const Rect *rect,
2180		int playDuringRecord,
2181		Boolean willRecord )
2182{
2183	long				lUsage = 0;
2184	ComponentResult		err = noErr;
2185
2186	/* Figure out the usage. Always previewing. */
2187	lUsage = seqGrabPreview;
2188
2189	/* Sometimes recording. */
2190	if (willRecord) {
2191		lUsage |= seqGrabRecord;
2192    }
2193    if (playDuringRecord) {
2194		lUsage |= seqGrabPlayDuringRecord;
2195    }
2196    err = SGNewChannel( seqGrab, VideoMediaType, sgchanVideo );
2197    if (err == noErr) {
2198
2199        /* Set boundaries for new video channel. We don't know at this stage! */
2200        err = SGSetChannelBounds( *sgchanVideo, rect );
2201
2202        /* Set usage for new video channel. */
2203        if (err == noErr) {
2204            err = SGSetChannelUsage( *sgchanVideo, lUsage );
2205
2206            /*
2207             * We want an offscreen buffer. This eliminates cursor flicker, gives better
2208             * and flicker free display, works better together with tk.
2209             */
2210
2211            SGSetUseScreenBuffer( *sgchanVideo, false );
2212
2213            /*
2214             * Set compession parameters if prepared to record. There are more alternatives.
2215             * Could also use 'SGSetVideoCompressor(...)'. Use panels instead?
2216             */
2217
2218            if (0 && willRecord) {
2219                SGSetVideoCompressorType( *sgchanVideo,
2220                        FOUR_CHAR_CODE('cvid') );
2221            }
2222        } else {
2223
2224            /* Clean up on failure. */
2225            SGDisposeChannel( seqGrab, *sgchanVideo );
2226            *sgchanVideo = NULL;
2227        }
2228    }
2229    return err;
2230}
2231
2232static pascal ComponentResult
2233MakeAudioChannel(
2234        SeqGrabComponent seqGrab,
2235		SGChannel *sgchanSound,
2236		int playDuringRecord,
2237		Boolean willRecord )
2238{
2239	ComponentResult		err = noErr;
2240	long				lUsage = 0;
2241
2242	/* Figure out the usage. Always previewing. */
2243	lUsage = seqGrabPreview;
2244
2245	/* Sometimes recording. */
2246	if (willRecord) {
2247		lUsage |= seqGrabRecord;
2248    }
2249    if (playDuringRecord) {
2250		lUsage |= seqGrabPlayDuringRecord;
2251    }
2252    err = SGNewChannel( seqGrab, SoundMediaType, sgchanSound );
2253    if (err == noErr) {
2254
2255        /* Set usage of new sound channel. */
2256        err = SGSetChannelUsage( *sgchanSound, lUsage );
2257        if (err != noErr) {
2258
2259            /* Clean up on failure. */
2260            SGDisposeChannel( seqGrab, *sgchanSound );
2261            *sgchanSound = NULL;
2262        }
2263    }
2264    return err;
2265}
2266
2267static void
2268FreeVideoChannel(SeqGrabber *sgrabPtr)
2269{
2270	if (sgrabPtr->sgChanVideo != NULL) {
2271		SGDisposeChannel( sgrabPtr->seqGrab, sgrabPtr->sgChanVideo );
2272		sgrabPtr->sgChanVideo = NULL;
2273	}
2274}
2275
2276static void
2277FreeAudioChannel(SeqGrabber *sgrabPtr)
2278{
2279	if (sgrabPtr->sgChanAudio != NULL) {
2280		SGDisposeChannel( sgrabPtr->seqGrab, sgrabPtr->sgChanAudio );
2281		sgrabPtr->sgChanAudio = NULL;
2282	}
2283}
2284
2285/*
2286 *----------------------------------------------------------------------
2287 *
2288 * MyGrabFrameComplete --
2289 *
2290 *      This routine is the frame completion routine for video in the sequence
2291 *      grabber. It gets called just before the frame is displayed, and may
2292 *      be used to draw things in it, like a pict (via overlayPictHand).
2293 *
2294 * Results:
2295 *		ComponentResult.
2296 *
2297 * Side effects:
2298 *		Draws in graphics port.
2299 *
2300 *----------------------------------------------------------------------
2301 */
2302
2303static pascal ComponentResult
2304MyGrabFrameComplete( SGChannel sgChan, short nBufferNum, Boolean *pbDone, long refConst )
2305{
2306	ComponentResult		err;
2307
2308	/* First, call the default grab-complete function. */
2309
2310 	err = SGGrabFrameComplete( sgChan, nBufferNum, pbDone );
2311
2312	if (*pbDone) {
2313
2314		/* Frame is done. */
2315
2316		SeqGrabber 			*sgrabPtr = (SeqGrabber *) refConst;
2317		CGrafPtr			tmpPortPtr = sgrabPtr->videoBottlePortPtr;
2318		CGrafPtr			oldPortPtr;
2319		GDHandle			hghOldDevice;
2320		PixMapHandle		hpmBuffer, hpmOld;
2321		Rect				rectBuffer, destRect;
2322		char				str[16];
2323		static unsigned short	counter = 0;
2324		double              fps;
2325		double              averageFps;
2326
2327		/* Set to our temporary port. */
2328		GetGWorld( &oldPortPtr, &hghOldDevice );
2329		SetGWorld( tmpPortPtr, NULL );
2330
2331		/* Find out about this buffer. */
2332		err = SGGetBufferInfo( sgChan, nBufferNum, &hpmBuffer,
2333                &rectBuffer, NULL, NULL );
2334
2335		if (err == noErr) {
2336
2337			/*
2338			 * The temporary port passed in in 'refConst' via sgrabPtr is used to draw in with
2339			 * its pixmap replaced by the buffers pixmap. Must be sure that the temporary
2340			 * port is still valid.
2341			 */
2342
2343#if TARGET_API_MAC_CARBON
2344			hpmOld = GetPortPixMap( tmpPortPtr );
2345#else
2346			hpmOld = tmpPortPtr->portPixMap;
2347#endif
2348			SetPortPix( hpmBuffer );
2349#if TARGET_API_MAC_CARBON
2350            //LockPortBits( hpmBuffer );
2351#endif
2352			if (sgrabPtr->frameCount == 0) {
2353			    sgrabPtr->startTick = TickCount();
2354			}
2355		    sgrabPtr->frameCount++;
2356
2357            if ((sgrabPtr->imageAsyncProcObj != NULL) &&
2358                    (sgrabPtr->asyncImageHandlerPtr == NULL)) {
2359                Tcl_Obj		*listObjPtr = NULL;
2360
2361                if (MakeTkPhotoFromPixMap( sgrabPtr->interp, hpmBuffer,
2362                        Tcl_GetString(sgrabPtr->imageNameObj) ) == TCL_OK) {
2363                    AsyncImageHandlerRecord	*asyncImageHandlerPtr;
2364
2365                    listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
2366                    Tcl_ListObjAppendElement( sgrabPtr->interp, listObjPtr, sgrabPtr->imageAsyncProcObj );
2367                    Tcl_ListObjAppendElement( sgrabPtr->interp, listObjPtr,
2368                           Tcl_NewStringObj( Tk_PathName(sgrabPtr->tkwin), -1) );
2369                    Tcl_ListObjAppendElement( sgrabPtr->interp, listObjPtr, sgrabPtr->imageNameObj );
2370
2371                    /*
2372                     * Do the callback as soon as possible but from a safe place,
2373                     * like the Tcl event loop instead of here.
2374                     */
2375                    asyncImageHandlerPtr = (AsyncImageHandlerRecord *)
2376                            ckalloc( sizeof(AsyncImageHandlerRecord) );
2377                    asyncImageHandlerPtr->sgrabPtr = sgrabPtr;
2378                    asyncImageHandlerPtr->commandObjPtr = listObjPtr;
2379                    asyncImageHandlerPtr->timerToken = Tcl_CreateTimerHandler( 0,
2380                            AsyncImageHandlerProc, (ClientData) asyncImageHandlerPtr );
2381                    Tcl_IncrRefCount( asyncImageHandlerPtr->commandObjPtr );
2382                    sgrabPtr->asyncImageHandlerPtr = (void *) asyncImageHandlerPtr;
2383                }
2384                sgrabPtr->imageAsyncProcObj = NULL;
2385            }
2386
2387			/*
2388			 * Draw some text into the buffer.
2389			 */
2390
2391            if (sgrabPtr->showFPS) {
2392    			TextMode( srcXor );
2393    			MoveTo( (short) (rectBuffer.right - 50), (short) (rectBuffer.bottom - 22) );
2394
2395#if TARGET_API_MAC_CARBON
2396    			sprintf( StrBody(str), "%d", counter++ );
2397                str[0] = strlen(str) + 1;
2398    			DrawString( (ConstStr255Param) str );
2399#else
2400    			sprintf( str, "%d", counter++ );
2401    			DrawString( c2pstr( str ) );
2402#endif
2403    			MoveTo( (short) (rectBuffer.right - 120), (short) (rectBuffer.bottom - 8) );
2404    	       	fps = 60.0/(TickCount() - sgrabPtr->latestTick);
2405    	       	averageFps = 60.0 * sgrabPtr->frameCount/(TickCount() - sgrabPtr->startTick);
2406
2407#if TARGET_API_MAC_CARBON
2408    	       	sprintf( StrBody(str), "<fps>:%4.1f fps:%4.1f", averageFps, fps );
2409        		str[0] = strlen(str) + 1;
2410        		DrawString( (ConstStr255Param) str );
2411#else
2412    	       	sprintf( str, "<fps>:%4.1f fps:%4.1f", averageFps, fps );
2413        		DrawString( c2pstr( str ) );
2414#endif
2415    	       	sgrabPtr->latestTick = TickCount();
2416    			TextMode( srcOr );
2417			}
2418
2419			/* Have we got a pict to overlay? Set the 'destRect' to the NE corner. */
2420			if (sgrabPtr->overlayPictHand) {
2421				destRect = (**(sgrabPtr->overlayPictHand)).picFrame;
2422				if (!EmptyRect( &destRect )) {
2423					MacOffsetRect( &destRect, (short) (rectBuffer.right - destRect.right),
2424							(short) (rectBuffer.top - destRect.top) );
2425					DrawPicture( sgrabPtr->overlayPictHand, &destRect );
2426				}
2427			}
2428
2429#if TARGET_API_MAC_CARBON
2430            //UnlockPortBits( hpmBuffer );
2431#endif
2432			/* Restore temporary port's pixmap. */
2433			SetPortPix( hpmOld );
2434		}
2435		SetGWorld( oldPortPtr, hghOldDevice );
2436	}
2437	return err;
2438}
2439
2440static void
2441AsyncImageHandlerProc(
2442    ClientData clientData )
2443{
2444    AsyncImageHandlerRecord *asyncImageHandlerPtr = (AsyncImageHandlerRecord *) clientData;
2445    int				code;
2446    SeqGrabber 		*sgrabPtr = asyncImageHandlerPtr->sgrabPtr;
2447
2448    /* Since we may be destroyed in the callback keep storage alive. */
2449    Tcl_Preserve( (ClientData) sgrabPtr );
2450
2451    Tcl_IncrRefCount( asyncImageHandlerPtr->commandObjPtr );
2452    code = Tcl_EvalObjEx( sgrabPtr->interp, asyncImageHandlerPtr->commandObjPtr,
2453            TCL_EVAL_GLOBAL );
2454    Tcl_DecrRefCount( asyncImageHandlerPtr->commandObjPtr );
2455
2456    sgrabPtr->asyncImageHandlerPtr = NULL;
2457    Tcl_Release( (ClientData) sgrabPtr );
2458    ckfree( (char *) asyncImageHandlerPtr );
2459}
2460
2461/*
2462 *----------------------------------------------------------------------
2463 *
2464 * SetupMyVideoBottlenecks --
2465 *
2466 *      This is used to register the MyGrabFrameComplete procedure before
2467 *      starting playback.
2468 *
2469 * Results:
2470 *		OSErr.
2471 *
2472 * Side effects:
2473 *		Registers the MyGrabFrameComplete procedure.
2474 *
2475 *----------------------------------------------------------------------
2476 */
2477
2478static pascal ComponentResult
2479SetupMyVideoBottlenecks( SGChannel sgchanVideo,
2480		WindowPtr macWndPtr,
2481		SeqGrabber *sgrabPtr )
2482{
2483	ComponentResult		err = noErr;
2484	CGrafPtr            tempPortPtr;
2485	Rect                bounds = {0, 0, 10, 10};
2486
2487	/*
2488	 * We set the reference constant to our sequence grabber struct (SeqGrabber) to make things
2489	 * available to our bottleneck callback procedure.
2490	 */
2491
2492	err = SGSetChannelRefCon( sgchanVideo, (long) sgrabPtr );
2493	if (err == noErr) {
2494		VideoBottles	vb;
2495
2496		/* get the current bottlenecks */
2497		vb.procCount = 9;
2498		err = SGGetVideoBottlenecks(sgchanVideo, &vb);
2499		if (err == noErr) {
2500
2501			/*
2502			 * Add our GrabFrameComplete function.
2503			 * Searched in <QuickTimeComponents.h> for UPP stuff.
2504			 */
2505#if TARGET_API_MAC_CARBON
2506			vb.grabCompleteProc = NewSGGrabCompleteBottleUPP( MyGrabFrameComplete );
2507#else
2508			vb.grabCompleteProc = NewSGGrabCompleteBottleProc( MyGrabFrameComplete );
2509#endif
2510			err = SGSetVideoBottlenecks( sgchanVideo, &vb );
2511
2512			/*
2513			 * Create a temporary port for drawing...
2514			 * ...with a wide open visible and clip region.
2515			 * We just borrow the graphics port from the GWorld.
2516			 */
2517
2518            err = MySafeNewGWorld( &tempPortPtr, 32, &bounds, NULL, NULL, 0 );
2519            if (err != noErr) {
2520                panic( "Out of memory: NewGWorld failed in SetupMyVideoBottlenecks" );
2521            }
2522            sgrabPtr->videoBottlePortPtr = tempPortPtr;
2523
2524#if TARGET_API_MAC_CARBON
2525            {
2526                RgnHandle   rgn;
2527
2528                rgn = NewRgn();
2529    			tempPortPtr = CreateNewPort();
2530                MacSetRectRgn( rgn, -32000, -32000, 32000, 32000 );
2531                SetPortVisibleRegion( tempPortPtr, rgn );
2532                SetPortClipRegion( tempPortPtr, rgn );
2533    			PortChanged( (GrafPtr) tempPortPtr );
2534                DisposeRgn( rgn );
2535            }
2536#else
2537			//OpenCPort( tempPortPtr );
2538			MacSetRectRgn( tempPortPtr->visRgn, -32000, -32000, 32000, 32000 );
2539			/* so that you can use it in any video buffer */
2540			MacCopyRgn( tempPortPtr->visRgn, tempPortPtr->clipRgn );
2541			/* tell QuickDraw about the changes */
2542			PortChanged( (GrafPtr) tempPortPtr );
2543#endif
2544		}
2545	}
2546
2547	return err;
2548}
2549
2550/*
2551 *----------------------------------------------------------------------
2552 *
2553 * MovableDialogModalFilter --
2554 *
2555 *      Used to handle update events when the dialog is moved.
2556 *
2557 * Results:
2558 *		Boolean describing if the event was handled or not.
2559 *
2560 * Side effects:
2561 *		Handles update events.
2562 *
2563 *----------------------------------------------------------------------
2564 */
2565
2566pascal Boolean
2567MovableDialogModalFilter( DialogPtr dialogPtr,
2568		const EventRecord *eventPtr,
2569		short *itemHit,
2570		long refCon )
2571{
2572
2573#if TARGET_OS_MAC	// Windows works different
2574    SeqGrabber 	*sgrabPtr = (SeqGrabber *) refCon;
2575    Boolean     eventDone = false;
2576    GrafPtr		savePort;
2577
2578#if TARGET_API_MAC_CARBON
2579    if ((eventPtr->what == updateEvt) &&
2580    		((WindowPtr) eventPtr->message != GetDialogWindow( dialogPtr ))) {
2581#else
2582    if ((eventPtr->what == updateEvt) &&
2583    		((WindowPtr) eventPtr->message != dialogPtr)) {
2584        Rect		saveBounds;
2585
2586    	SGGetChannelBounds( sgrabPtr->sgChanVideo, &saveBounds );
2587
2588		/*
2589		 * Handle update events to background windows here.
2590		 * First, translate mac event to a number of tcl events.
2591		 * If any tcl events generated, execute them until empty, and don't wait.
2592		 */
2593
2594		if (TkMacConvertEvent( (EventRecord *) eventPtr )) {
2595			while ( Tcl_DoOneEvent( TCL_IDLE_EVENTS | TCL_DONT_WAIT | TCL_WINDOW_EVENTS ) )
2596				/* empty */
2597				;
2598		}
2599		SGSetChannelBounds( sgrabPtr->sgChanVideo, &saveBounds );
2600#endif
2601
2602    } else {
2603    	GetPort( &savePort );
2604#if TARGET_API_MAC_CARBON
2605    	MacSetPort( GetDialogPort( dialogPtr ) );
2606#else
2607    	MacSetPort( dialogPtr );
2608#endif
2609    	eventDone = StdFilterProc( dialogPtr, (EventRecord *) eventPtr, itemHit );
2610
2611    	MacSetPort( savePort );
2612    }
2613    return eventDone;
2614#endif	// TARGET_OS_MAC
2615
2616#ifdef _WIN32
2617    Boolean     eventDone = false;
2618    SeqGrabber 	*sgrabPtr = (SeqGrabber *) refCon;
2619    GrafPtr		savePort;
2620
2621    if ((eventPtr->what == updateEvt) &&
2622    		((WindowPtr) eventPtr->message != dialogPtr)) {
2623
2624    // this is Windows equivalent of TkMacConvertEvent above
2625    //GenerateXEvent(hwnd, message, wParam, lParam);
2626
2627    } else {
2628    	GetPort( &savePort );
2629    	MacSetPort( dialogPtr );
2630
2631    	eventDone = StdFilterProc( dialogPtr, (EventRecord *) eventPtr, itemHit );
2632
2633    	MacSetPort( savePort );
2634    }
2635
2636    return eventDone;
2637#endif  // WIN32
2638}
2639
2640/*------------------------------------------------------------------------------------*/