1/*
2 * MovieCallBack.c --
3 *
4 *		Process the "callback" subcommand.
5 *
6 * Copyright (c) 2003  Mats Bengtsson
7 *
8 * $Id: MovieCallBack.c,v 1.3 2003/12/02 10:17:03 matben Exp $
9 */
10
11#include "MoviePlayer.h"
12
13
14/*
15 * For dispatching 'callback' sub commands.
16 */
17
18static char *allCallBackCmds[] = {
19	"cancel", "info",
20    (char *) NULL
21};
22
23enum {
24    kCallBackCmdCancel              	= 0L,
25	kCallBackCmdInfo
26};
27
28/*
29 * Use hash table to map between token names and the corresponding
30 * callback struct entry.
31 */
32
33typedef struct CallBackEntry {
34	MoviePlayer 	*movPtr;
35	TimeValue		movTime;
36	long			uid;
37	Tcl_Obj			*cmdObjPtr;
38	QTCallBack		callback;
39	Tcl_HashEntry	*hashPtr;		/* Self referencing; useful when freeing */
40} CallBackEntry;
41
42static char		gTokenCallbackStr[] = "moviecallback";
43static long		gTokenUID = 0;
44
45pascal void		CallBackProc( QTCallBack cb, long refCon );
46
47/*
48 *----------------------------------------------------------------------
49 *
50 * ProcessCallBackCmd --
51 *
52 *		Process the "callback" command
53 *
54 * Results:
55 *  	Normal TCL results
56 *
57 * Side effects:
58 *		Sets up or cancels callbacks, or just returns info.
59 *
60 *----------------------------------------------------------------------
61 */
62
63int
64ProcessCallBackCmd(
65        MoviePlayer *movPtr,
66		int objc, 				/* Starts with the thing after 'callback' */
67		Tcl_Obj *CONST objv[] )
68{
69	Tcl_Interp 		*interp = movPtr->interp;
70	Movie 			theMovie = movPtr->aMovie;
71	int				cmdIndex;
72	int				isNew;
73	long			movTime;
74	Tcl_HashTable 	*callBackHashTablePtr = movPtr->callBackHashTablePtr;
75	Tcl_HashSearch 	search;
76	Tcl_HashEntry   *hashPtr = NULL;
77	Tcl_Obj			*objPtr;
78	Tcl_Obj			*listObjPtr;
79	CallBackEntry	*entryPtr;
80	OSErr			err = noErr;
81	int				result = TCL_OK;
82
83  	if (callBackHashTablePtr == NULL) {
84	    movPtr->callBackHashTablePtr = (Tcl_HashTable *)
85	    		ckalloc( sizeof(Tcl_HashTable) );
86	    callBackHashTablePtr = movPtr->callBackHashTablePtr;
87		Tcl_InitHashTable( callBackHashTablePtr, TCL_STRING_KEYS );
88  	}
89	if (objc < 1) {
90		Tcl_WrongNumArgs( interp, 0, objv,
91				"pathName callback time|cmd ?args?" );
92		return TCL_ERROR;
93	}
94
95	/*
96	 * Either a command (cancel or info) or an integer number.
97	 */
98
99	if (Tcl_GetIndexFromObj( interp, objv[0], allCallBackCmds, "callback command",
100			TCL_EXACT, &cmdIndex ) != TCL_OK ) {
101		if (Tcl_GetLongFromObj( interp, objv[0], &movTime ) == TCL_OK) {
102			cmdIndex = -1;
103		} else {
104    		Tcl_SetObjResult( interp, Tcl_NewStringObj(
105    				"Usage: \"pathName callback time|cmd ?args?\"", -1 ) );
106		    return TCL_ERROR;
107		}
108	}
109
110    /*
111     * Dispatch the callback command to the right branch.
112     */
113
114    switch (cmdIndex) {
115
116        case -1: {
117        	char	tmpstr[32];
118
119        	if (objc != 2) {
120				Tcl_WrongNumArgs( interp, 0, objv,
121						"pathName callback time procName" );
122				return TCL_ERROR;
123			}
124			if (Tcl_GetLongFromObj( interp, objv[0], &movTime ) != TCL_OK) {
125				Tcl_AddErrorInfo( interp, "\n	(processing time value)" );
126	    		return TCL_ERROR;
127			}
128        	entryPtr = (CallBackEntry *) ckalloc( sizeof(CallBackEntry) );
129        	entryPtr->callback = NewCallBack( GetMovieTimeBase( theMovie ),
130        			callBackAtTime );
131        	entryPtr->uid = gTokenUID;
132        	entryPtr->movPtr = movPtr;
133        	entryPtr->movTime = movTime;
134        	err = CallMeWhen( entryPtr->callback,
135        			NewQTCallBackUPP( CallBackProc), (long) entryPtr,
136        			triggerTimeEither, movTime, GetMovieTimeScale( theMovie ) );
137
138         	entryPtr->cmdObjPtr = objv[1];
139            Tcl_IncrRefCount( objv[1] );
140        	sprintf( tmpstr, "%s%ld", gTokenCallbackStr, gTokenUID );
141			gTokenUID++;
142			hashPtr = Tcl_CreateHashEntry( callBackHashTablePtr,
143					tmpstr, &isNew );
144			Tcl_SetHashValue( hashPtr, entryPtr );
145			entryPtr->hashPtr = hashPtr;
146			Tcl_SetObjResult( interp, Tcl_NewStringObj( tmpstr, -1 ) );
147    	    break;
148       	}
149
150        case kCallBackCmdCancel: {
151       		hashPtr = Tcl_FindHashEntry( callBackHashTablePtr,
152					Tcl_GetString( objv[1] ) );
153		    if (hashPtr == NULL) {
154				Tcl_SetObjResult( interp, Tcl_NewStringObj(
155						"Unrecognized callback token", -1 ) );
156				return TCL_ERROR;
157		    }
158		    entryPtr = (CallBackEntry *) Tcl_GetHashValue( hashPtr );
159		    CancelCallBack( entryPtr->callback );
160		    DisposeCallBack( entryPtr->callback );
161		    Tcl_DeleteHashEntry( hashPtr );
162		    ckfree( (char *) entryPtr );
163    	    break;
164       	}
165
166        case kCallBackCmdInfo: {
167        	if (callBackHashTablePtr == NULL) {
168				return TCL_OK;
169			}
170			if (objc == 1) {
171
172				/*
173				 * Return a list of all tokens.
174				 */
175
176		    	listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
177				hashPtr = Tcl_FirstHashEntry( callBackHashTablePtr, &search );
178				while (hashPtr != NULL) {
179					entryPtr = (CallBackEntry *) Tcl_GetHashValue( hashPtr );
180				    objPtr = Tcl_NewStringObj( gTokenCallbackStr, -1 );
181				    Tcl_AppendObjToObj( objPtr, Tcl_NewIntObj( entryPtr->uid ) );
182			    	Tcl_ListObjAppendElement( interp, listObjPtr, objPtr );
183					hashPtr = Tcl_NextHashEntry( &search );
184				}
185				Tcl_SetObjResult( interp, listObjPtr );
186
187			} else if (objc == 2) {
188
189				/*
190				 * Return detailed info on this particular callback token.
191				 */
192
193	       		hashPtr = Tcl_FindHashEntry( callBackHashTablePtr,
194						Tcl_GetString( objv[1] ) );
195			    if (hashPtr == NULL) {
196					Tcl_SetObjResult( interp, Tcl_NewStringObj(
197							"Unrecognized callback token", -1 ) );
198					return TCL_ERROR;
199			    }
200			    entryPtr = (CallBackEntry *) Tcl_GetHashValue( hashPtr );
201		    	listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL );
202		    	Tcl_ListObjAppendElement( interp, listObjPtr, entryPtr->cmdObjPtr );
203		    	Tcl_ListObjAppendElement( interp, listObjPtr,
204		    			Tcl_NewLongObj( entryPtr->movTime ) );
205				Tcl_SetObjResult( interp, listObjPtr );
206
207			} else {
208				Tcl_WrongNumArgs( interp, 0, objv,
209						"pathName callback info ?callbacktoken?" );
210				return TCL_ERROR;
211			}
212    	    break;
213       	}
214	}
215
216	return result;
217}
218
219/*
220 *----------------------------------------------------------------------
221 *
222 * CallBackProc --
223 *
224 *		Called from QuickTime when time event to invoke the registered
225 *		Tcl procedure.
226 *
227 * Results:
228 *  	None.
229 *
230 * Side effects:
231 *		Invokes Tcl procedure.
232 *
233 *----------------------------------------------------------------------
234 */
235
236pascal void
237CallBackProc( QTCallBack cb, long refCon )
238{
239	CallBackEntry 	*entryPtr = (CallBackEntry *) refCon;
240	Tcl_Interp 		*interp = entryPtr->movPtr->interp;
241	int				i;
242	int				result = TCL_OK;
243    Tcl_Obj			*objv[4];
244
245    objv[0] = entryPtr->cmdObjPtr;
246    objv[1] = Tcl_NewStringObj( gTokenCallbackStr, -1 );
247    Tcl_AppendObjToObj( objv[1], Tcl_NewIntObj( entryPtr->uid ) );
248    objv[2] = Tcl_NewStringObj( Tk_PathName(entryPtr->movPtr->tkwin), -1 );
249    objv[3] = Tcl_NewLongObj( entryPtr->movTime );
250    for (i = 0; i < 4; i++) {
251        Tcl_IncrRefCount( objv[i] );
252    }
253    Tcl_Preserve( (ClientData) entryPtr );
254    result = Tcl_EvalObjv( interp, 4, objv, 0 );
255    Tcl_Release( (ClientData) entryPtr );
256    for (i = 0; i < 4; i++) {
257        Tcl_DecrRefCount( objv[i] );
258    }
259	Tcl_DecrRefCount( entryPtr->cmdObjPtr );
260
261	/*
262	 * One shot callback; remove from our hash table since used.
263	 */
264
265    if (entryPtr->hashPtr != NULL) {
266		Tcl_DeleteHashEntry( entryPtr->hashPtr );
267    }
268	ckfree( (char *) entryPtr );
269}
270
271/*
272 *----------------------------------------------------------------------
273 *
274 * CallBackFree --
275 *
276 *		Called when destroying movie to free up hash table for this specific
277 *		instance. Assume the QT callbacks are destroyed with the movie itself.
278 *
279 * Results:
280 *  	None.
281 *
282 * Side effects:
283 *		Frees memory.
284 *
285 *----------------------------------------------------------------------
286 */
287
288void
289CallBackFree( MoviePlayer *movPtr )
290{
291	Tcl_HashTable 		*callBackHashTablePtr = movPtr->callBackHashTablePtr;
292	Tcl_HashEntry 		*hPtr;
293	Tcl_HashSearch 		search;
294	CallBackEntry 		*entryPtr;
295
296	if (callBackHashTablePtr != NULL) {
297		hPtr = Tcl_FirstHashEntry( callBackHashTablePtr, &search );
298		while (hPtr != NULL) {
299			entryPtr = (CallBackEntry *) Tcl_GetHashValue( hPtr );
300			Tcl_DecrRefCount( entryPtr->cmdObjPtr );
301			Tcl_DeleteHashEntry( hPtr );
302        	ckfree( (char *) entryPtr );
303			hPtr = Tcl_NextHashEntry( &search );
304		}
305	}
306}
307
308/*---------------------------------------------------------------------------*/
309