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/*------------------------------------------------------------------------------------*/