]> git.sesse.net Git - vlc/blob - plugins/macosx/vout_macosx.c
96239ec339ad9973367247ae5bc341b2a5b241fe
[vlc] / plugins / macosx / vout_macosx.c
1 /*****************************************************************************
2  * vout_macosx.c: MacOS X video output plugin
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  *
6  * Authors: Colin Delacroix <colin@zoy.org>
7  *          Eugenio Jarosiewicz <ej0@cise.ufl.edu>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 #define MODULE_NAME macosx
25 #include "modules_inner.h"
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include "defs.h"
31
32 #include <errno.h>                                                 /* ENOMEM */
33 #include <stdlib.h>                                                /* free() */
34 #include <string.h>                                            /* strerror() */
35
36 #include "config.h"
37 #include "common.h"
38 #include "threads.h"
39 #include "mtime.h"
40 #include "tests.h"
41
42 #include "video.h"
43 #include "video_output.h"
44
45 #include "intf_msg.h"
46
47 #include "main.h"
48
49 #include "modules.h"
50
51 #ifndef __CARBONPREFIX__
52     #define __CARBONPREFIX__
53
54     // Needed for carbonization
55     #define TARGET_API_MAC_CARBON 1
56
57     // For the pascal to C or C to pascal string conversions in carbon
58     #define OLDP2C 1
59 #endif
60
61 #include <Carbon/Carbon.h>
62
63 // Initial Window Constants
64 enum
65 {
66     kWindowOffset = 100
67 };
68
69 // where is the off screen
70 enum
71 {
72     kNoWhere = 0,
73     kInVRAM,
74     kInAGP,
75     kInSystem
76 };
77
78 /*****************************************************************************
79  * vout_sys_t: MacOS X video output method descriptor
80  *****************************************************************************
81  * This structure is part of the video output thread descriptor.
82  * It describes the MacOS X specific properties of an output thread.
83  *****************************************************************************/
84 typedef struct vout_sys_s
85 {
86     /* MacOS X video memory */
87     byte_t *                    p_video;                      /* base adress */
88     size_t                      i_page_size;                    /* page size */
89     
90     Rect        wrect;
91     WindowRef   p_window;
92     short gwLocOffscreen;
93     GWorldPtr p_gw[ 2 ];
94     Boolean gNewNewGWorld;      /* can we allocate in VRAm or AGP memory ? */
95     
96     // Boolean gDone;
97     // SInt32 gSleepTime;
98     
99     GDHandle  theGDList;
100     Ptr                         theBase;
101     int                         theRow;
102     int                         theDepth;
103 } vout_sys_t;
104
105 /*****************************************************************************
106  * Local prototypes
107  *****************************************************************************/
108 static int  vout_Probe     ( probedata_t *p_data );
109 static int  vout_Create    ( struct vout_thread_s * );
110 static int  vout_Init      ( struct vout_thread_s * );
111 static void vout_End       ( struct vout_thread_s * );
112 static void vout_Destroy   ( struct vout_thread_s * );
113 static int  vout_Manage    ( struct vout_thread_s * );
114 static void vout_Display   ( struct vout_thread_s * );
115
116 /* OS specific */
117
118 static int CreateDisplay        ( struct vout_thread_s * );
119 static int MakeWindow           ( struct vout_thread_s * );
120 static int AllocBuffer          ( struct vout_thread_s * , short index );
121
122 void BlitToWindow               ( struct vout_thread_s * , short index );
123 GDHandle GetWindowDevice        ( struct vout_thread_s * );
124 void FillOffscreen              ( struct vout_thread_s * , short index);
125
126 void FindBestMemoryLocation( struct vout_thread_s * );
127
128 /*****************************************************************************
129  * Functions exported as capabilities. They are declared as static so that
130  * we don't pollute the namespace too much.
131  *****************************************************************************/
132 void _M( vout_getfunctions )( function_list_t * p_function_list )
133 {
134     p_function_list->pf_probe = vout_Probe;
135     p_function_list->functions.vout.pf_create     = vout_Create;
136     p_function_list->functions.vout.pf_init       = vout_Init;
137     p_function_list->functions.vout.pf_end        = vout_End;
138     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
139     p_function_list->functions.vout.pf_manage     = vout_Manage;
140     p_function_list->functions.vout.pf_display    = vout_Display;
141     p_function_list->functions.vout.pf_setpalette = NULL;
142 }
143
144 /*****************************************************************************
145  * intf_Probe: return a score
146  *****************************************************************************/
147 static int vout_Probe( probedata_t *p_data )
148 {
149     if( TestMethod( VOUT_METHOD_VAR, "macosx" ) )
150     {
151         return( 999 );
152     }
153
154     return( 100 );
155 }
156
157 /*****************************************************************************
158  * vout_Create: allocates MacOS X video thread output method
159  *****************************************************************************
160  * This function allocates and initializes a MacOS X vout method.
161  *****************************************************************************/
162 static int vout_Create( vout_thread_t *p_vout )
163 {
164     //intf_ErrMsg( "vout_Create()" );
165
166     /* Allocate structure */
167     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
168     if( p_vout->p_sys == NULL )
169     {
170         intf_ErrMsg( "error: %s", strerror( ENOMEM ) );
171         return( 1 );
172     }
173
174     p_vout->p_sys->gwLocOffscreen = kNoWhere;
175     p_vout->p_sys->p_window       = NULL;
176     p_vout->p_sys->p_gw[ 0 ]      = NULL;
177     p_vout->p_sys->p_gw[ 1 ]      = NULL;
178     p_vout->p_sys->i_page_size    = p_vout->i_width * p_vout->i_height
179                                   * p_vout->i_bytes_per_pixel;
180     
181     if ( CreateDisplay( p_vout ) )
182     {
183         intf_ErrMsg( "vout error: can't open display" );
184         free( p_vout->p_sys );
185         return( 1 );
186     }
187
188 #if 0
189     intf_ErrMsg( "vout p_vout->i_width %d" , p_vout->i_width);
190     intf_ErrMsg( "vout p_vout->i_height %d" , p_vout->i_height);
191     intf_ErrMsg( "vout p_vout->i_bytes_per_pixel %d" , p_vout->i_bytes_per_pixel);
192     intf_ErrMsg( "vout p_vout->i_screen_depth %d" , p_vout->i_screen_depth);
193     intf_ErrMsg( "vout p_vout->p_sys->i_page_size %d" , p_vout->p_sys->i_page_size);
194 #endif
195
196 #if 0
197     /* Map two framebuffers a the very beginning of the fb */
198     p_vout->p_sys->p_video = malloc( 2 * p_vout->p_sys->i_page_size );
199     if( p_vout->p_sys->p_video == NULL )
200     {
201         intf_ErrMsg( "vout error: can't map video memory (%s)",
202                      strerror(errno) );
203         free( p_vout->p_sys );
204         return( 1 );
205     }
206     /* Set and initialize buffers */
207     vout_SetBuffers( p_vout, p_vout->p_sys->p_video,
208                     p_vout->p_sys->p_video + p_vout->p_sys->i_page_size );
209 #endif
210
211     return( 0 );
212 }
213
214 /*****************************************************************************
215  * Find the best memory (AGP, VRAM, system) location
216  *****************************************************************************/
217 void FindBestMemoryLocation( vout_thread_t *p_vout )
218 {
219     long versionSystem;
220
221     Gestalt( gestaltSystemVersion, &versionSystem );
222     if ( 0x00000900 <= ( versionSystem & 0x00000FF00  ) )
223     {
224         intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = true" );
225         p_vout->p_sys->gNewNewGWorld = true;
226     }
227     else
228     {
229         // now it is tricky
230         // we will try to allocate in VRAM and find out where the allocation really ended up.
231         GWorldPtr pgwTest = NULL;
232         Rect rectTest = {0, 0, 10, 10};
233         short wPixDepth = 
234             (**(GetPortPixMap( GetWindowPort( p_vout->p_sys->p_window ) ))).pixelSize;
235         GDHandle hgdWindow = GetWindowDevice( p_vout );
236
237         intf_ErrMsg( "FindBestMemoryLocation : gNewNewGWorld = false !" );
238 #if 0
239 //EJ added
240         p_vout->i_screen_depth = wPixDepth;
241         p_vout->i_bytes_per_pixel = wPixDepth;
242         p_vout->i_bytes_per_line   = p_vout->i_width * p_vout->i_bytes_per_pixel;
243         p_vout->p_sys->i_page_size = p_vout->i_width * p_vout->i_height * p_vout->i_bytes_per_pixel;
244 //p_vout->i_bytes_per_line = (**(**hgdWindow).gdPMap).rowBytes & 0x3FFF ;
245 #endif
246         if(    ( noErr == NewGWorld( &pgwTest, wPixDepth, &rectTest, NULL, hgdWindow,
247                                      noNewDevice | useDistantHdwrMem ) ) 
248             && ( pgwTest ) )
249         {
250             p_vout->p_sys->gNewNewGWorld = true;        
251         }
252         
253         if( pgwTest )
254         {
255             DisposeGWorld( pgwTest );
256         }
257     }
258 }
259
260 /*****************************************************************************
261  * CreateDisplay: setup display params...
262  *****************************************************************************/
263 static int CreateDisplay( vout_thread_t *p_vout )
264 {
265     PixMapHandle hPixmap0, hPixmap1;
266     void * hPixmapBaseAddr0, * hPixmapBaseAddr1;
267
268     //intf_ErrMsg( "CreateDisplay()" );
269
270     if( MakeWindow( p_vout ) )
271     {
272         intf_ErrMsg( "vout error: can't open window display" );
273         return( 1 );
274     }
275
276     // FindBestMemoryLocation( p_vout );
277
278     //try to allocate @ best location, will eventaully trickle down to worst
279     p_vout->p_sys->gwLocOffscreen = kInVRAM;
280     if( AllocBuffer( p_vout, 0 ) || AllocBuffer( p_vout, 1 ) )
281     {
282         intf_ErrMsg( "vout error: can't alloc offscreen buffers" );
283         return( 1 );
284     }
285
286     hPixmap0 = GetGWorldPixMap( p_vout->p_sys->p_gw[0] );
287 //FIXME BIGTIME - in SDL they just lock this down until the end...KLUDGE
288 //but alas sounds good to me to try it.
289 //well fuck a duck it works.
290     LockPixels(hPixmap0);
291     hPixmap1 = GetGWorldPixMap( p_vout->p_sys->p_gw[1] );
292     LockPixels(hPixmap1);
293     
294     //FIXME hopefully this is the same for all Gworlds & window since they are the same size
295     p_vout->i_bytes_per_line = (**hPixmap0).rowBytes & 0x3FFF;
296
297     if ( (hPixmap0 == NULL) || (hPixmap1 == NULL) )
298     {
299         intf_ErrMsg( "vout error: pixmap problem");
300         UnlockPixels(hPixmap0);
301         UnlockPixels(hPixmap1);
302         return( 1 );
303     }
304
305     hPixmapBaseAddr0 = GetPixBaseAddr( hPixmap0 );
306     hPixmapBaseAddr1 = GetPixBaseAddr( hPixmap1 );
307     if ( (hPixmapBaseAddr0 == NULL) || (hPixmapBaseAddr1 == NULL) )
308     {
309         intf_ErrMsg( "vout error: pixmap base addr problem");
310         return( 1 );
311     }
312
313 //FIXME TODO - if I ever dispose of the Gworlds and recreate them, i'll have a new address
314 //and I'll need to tell vout about them...  dunno what problems vout might have if we just updateGworld  
315     vout_SetBuffers( p_vout, hPixmapBaseAddr0, hPixmapBaseAddr1 );
316
317     return 0;
318 }
319
320 /*****************************************************************************
321  * MakeWindow: open and set-up a Mac OS main window
322  *****************************************************************************/
323 static int MakeWindow( vout_thread_t *p_vout )
324 {
325     int left = 0;
326     int top = 0;
327     int bottom = p_vout->i_height;
328     int right = p_vout->i_width;
329
330     WindowAttributes windowAttr = kWindowStandardDocumentAttributes | 
331                                     kWindowStandardHandlerAttribute |
332                                     kWindowInWindowMenuAttribute;
333     
334     SetRect( &p_vout->p_sys->wrect, left, top, right, bottom );
335     OffsetRect( &p_vout->p_sys->wrect, kWindowOffset, kWindowOffset );
336
337     CreateNewWindow( kDocumentWindowClass, windowAttr, &p_vout->p_sys->wrect, &p_vout->p_sys->p_window );
338     if ( p_vout->p_sys->p_window == nil )
339     {
340         return( 1 );
341     }
342
343     InstallStandardEventHandler(GetWindowEventTarget(p_vout->p_sys->p_window));
344     SetPort( GetWindowPort( p_vout->p_sys->p_window ) );
345     SetWindowTitleWithCFString( p_vout->p_sys->p_window, CFSTR("VLC") );
346 //    ShowWindow( p_vout->p_sys->p_window );
347     TransitionWindow( p_vout->p_sys->p_window, kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL);
348     BringToFront(  p_vout->p_sys->p_window );
349
350 {
351     short wPixDepth = (**(GetPortPixMap( GetWindowPort( p_vout->p_sys->p_window ) ))).pixelSize;
352     p_vout->i_screen_depth = wPixDepth;
353     p_vout->i_bytes_per_pixel = p_vout->i_screen_depth / 8;
354     p_vout->i_bytes_per_line   = p_vout->i_width * p_vout->i_bytes_per_pixel;
355     p_vout->p_sys->i_page_size = p_vout->i_width * p_vout->i_height * p_vout->i_bytes_per_pixel;
356
357 //EJ added
358 #if 1
359 p_vout->i_bytes_per_line = (**(**GetWindowDevice( p_vout )).gdPMap).rowBytes & 0x3FFF ;
360
361     switch ( p_vout->i_screen_depth )
362     {
363         case 32:
364         case 24:
365             p_vout->i_red_mask =   0xff0000;
366             p_vout->i_green_mask = 0xff00;
367             p_vout->i_blue_mask =  0xff;
368             break;
369         case 16:
370         case 15:
371             p_vout->i_red_mask =   0x00007c00;
372             p_vout->i_green_mask = 0x000003e0;
373             p_vout->i_blue_mask =  0x0000001f;
374             break;
375         default:
376             break;
377     }
378 #endif
379
380 //EJ - not sure about these...
381 #if 0
382     p_vout->i_red_lshift = 0x10;
383     p_vout->i_red_rshift = 0x0;
384     p_vout->i_green_lshift = 0x8;
385     p_vout->i_green_rshift = 0x0;
386     p_vout->i_blue_lshift = 0x0;
387     p_vout->i_blue_rshift = 0x0;
388
389     p_vout->i_white_pixel = 0xffffff;
390     p_vout->i_black_pixel = 0x0;
391     p_vout->i_gray_pixel = 0x808080;
392     p_vout->i_blue_pixel = 0x32;
393 #endif
394 }
395
396     return( 0 );
397 }
398
399 /*****************************************************************************
400  * AllocBuffer: forces offscreen allocation (if different than current) in memory type specified
401  *****************************************************************************/
402 static int AllocBuffer ( vout_thread_t *p_vout, short index )
403 {
404     Rect bounds;
405     GDHandle hgdWindow = GetWindowDevice( p_vout );
406
407     switch ( p_vout->p_sys->gwLocOffscreen )
408     {
409         case kInVRAM:
410             if ( noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth, 
411                 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL, 
412                 hgdWindow, noNewDevice | useDistantHdwrMem ) )          
413             {
414                 intf_ErrMsg( "Allocate off screen image in VRAM" );
415                 break;
416             }
417             intf_ErrMsg( "Unable to allocate off screen image in VRAM, trying next best AGP" );
418             p_vout->p_sys->gwLocOffscreen = kInAGP;
419         case kInAGP:
420             if (noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth, 
421                 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL, 
422                 hgdWindow, noNewDevice | useLocalHdwrMem ) )
423             {
424                 intf_ErrMsg( "Allocate off screen image in AGP" );
425                 break;
426             }
427             intf_ErrMsg( "Unable to allocate off screen image in AGP, trying next best System" );
428             p_vout->p_sys->gwLocOffscreen = kInSystem;
429         case kInSystem:
430         default:
431             if ( noErr == NewGWorld( &p_vout->p_sys->p_gw[index], p_vout->i_screen_depth, 
432                 GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &bounds ), NULL, 
433                 hgdWindow, noNewDevice | keepLocal) )
434             {
435                 intf_ErrMsg( "Allocate off screen image in System" );
436                 break;
437             }
438             intf_ErrMsg( "Unable to allocate off screen image in System, no options left - failing" );
439             p_vout->p_sys->gwLocOffscreen = kNoWhere;
440             return( 1 ); // nothing was allocated
441     } 
442     return( 0 );
443 }
444
445 /*****************************************************************************
446  * vout_Init: initialize video thread output method
447  *****************************************************************************/
448 static int vout_Init( vout_thread_t *p_vout )
449 {
450     //intf_ErrMsg( "vout_Init()" );
451     return( 0 );
452 }
453
454 /*****************************************************************************
455  * vout_End: terminate video thread output method
456  *****************************************************************************/
457 static void vout_End( vout_thread_t *p_vout )
458 {
459     //intf_ErrMsg( "vout_End()" );
460     ;
461 }
462
463 /*****************************************************************************
464  * vout_Destroy: destroy video thread output method
465  *****************************************************************************/
466 static void vout_Destroy( vout_thread_t *p_vout )
467 {
468     //intf_ErrMsg( "vout_Destroy()" );
469
470 //FIXME KLUDGE to lock pixels
471 #if 1
472 {
473     PixMapHandle hPixmap0, hPixmap1;
474     hPixmap0 = GetGWorldPixMap( p_vout->p_sys->p_gw[0] );
475     hPixmap1 = GetGWorldPixMap( p_vout->p_sys->p_gw[1] );
476     UnlockPixels(hPixmap0);
477     UnlockPixels(hPixmap1);
478 }
479 #endif
480
481 #if 1
482     if ( p_vout->p_sys->p_gw[0] )
483     {
484         DisposeGWorld( p_vout->p_sys->p_gw[0] );
485     }
486     if ( p_vout->p_sys->p_gw[1] )
487     {
488         DisposeGWorld( p_vout->p_sys->p_gw[1] );
489     }
490     if ( p_vout->p_sys->p_window )
491     {
492         DisposeWindow( p_vout->p_sys->p_window );
493     }
494 #endif
495     free( p_vout->p_sys->p_video );
496     free( p_vout->p_sys );
497 }
498
499 /*****************************************************************************
500  * vout_Manage: handle events
501  *****************************************************************************
502  * This function should be called regularly by video output thread. It manages
503  * console events. It returns a non null value on error.
504  *****************************************************************************/
505 static int vout_Manage( vout_thread_t *p_vout )
506 {
507 //    intf_ErrMsg( "vout_Manage()" );
508     return( 0 );
509 }
510
511 /*****************************************************************************
512  * vout_Display: displays previously rendered output
513  *****************************************************************************
514  * This function send the currently rendered image to image, waits until
515  * it is displayed and switch the two rendering buffers, preparing next frame.
516  *****************************************************************************/
517 static void vout_Display( vout_thread_t *p_vout )
518 {
519 //    intf_ErrMsg( "vout_Display()" );
520
521     BlitToWindow ( p_vout, p_vout->i_buffer_index );
522 }
523
524
525 /*****************************************************************************
526  * flushQD: flushes buffered window area
527   *****************************************************************************/
528 void flushQD( vout_thread_t *p_vout )
529 {
530     CGrafPtr thePort;
531
532     //intf_ErrMsg( "flushQD()" );
533     
534     thePort = GetWindowPort( p_vout->p_sys->p_window );
535     
536     /* flush the entire port */
537     if (QDIsPortBuffered(thePort))
538         QDFlushPortBuffer(thePort, NULL);
539
540 #if 0
541     /* flush part of the port */
542     if (QDIsPortBuffered(thePort)) {
543         RgnHandle theRgn;
544         theRgn = NewRgn();
545             /* local port coordinates */
546         SetRectRgn(theRgn, 10, 10, 100, 30); 
547         QDFlushPortBuffer(thePort, theRgn);
548         DisposeRgn(theRgn);
549     }
550 #endif
551
552 }
553
554 /*****************************************************************************
555  * BlitToWindow: checks offscreen and blits it to the front
556   *****************************************************************************/
557
558 void BlitToWindow( vout_thread_t *p_vout, short index )
559 {
560     Rect rectDest, rectSource;
561     GrafPtr pCGrafSave, windowPort = GetWindowPort( p_vout->p_sys->p_window );
562
563     //intf_ErrMsg( "BlitToWindow() for %d", index );
564
565     GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
566     GetPortBounds( windowPort, &rectDest );
567     
568     GetPort ( &pCGrafSave );
569     SetPortWindowPort( p_vout->p_sys->p_window );
570 //FIXME have global lock - kinda bad but oh well 
571 //    if ( LockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) ) )
572 //    {
573         CopyBits( GetPortBitMapForCopyBits( p_vout->p_sys->p_gw[index] ), 
574                     GetPortBitMapForCopyBits( GetWindowPort( p_vout->p_sys->p_window ) ), 
575                     &rectSource, &rectDest, srcCopy, NULL);
576 //        UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
577 //EJ
578         //flushQD( p_vout );
579 /*
580     }
581     else
582     {
583         intf_ErrMsg( "error: Could not LockPixels" );
584     }
585 */
586     SetPort ( pCGrafSave );
587 }
588
589
590 /*****************************************************************************
591  * GetWindowDevice: returns GDHandle that window resides on (most of it anyway)
592  *****************************************************************************/
593 GDHandle GetWindowDevice( vout_thread_t *p_vout )
594 {
595     GrafPtr pgpSave;
596     Rect rectWind, rectSect;
597     long greatestArea, sectArea;
598     GDHandle hgdNthDevice, hgdZoomOnThisDevice = NULL;
599     
600     //intf_ErrMsg( "GetWindowDevice()" );
601
602     GetPort( &pgpSave );
603     SetPortWindowPort( p_vout->p_sys->p_window );
604     GetPortBounds( GetWindowPort( p_vout->p_sys->p_window ), &rectWind );
605     LocalToGlobal( ( Point* ) &rectWind.top );
606     LocalToGlobal( ( Point* ) &rectWind.bottom );
607     hgdNthDevice = GetDeviceList();
608     greatestArea = 0;
609     // check window against all gdRects in gDevice list and remember 
610     //  which gdRect contains largest area of window}
611     while ( hgdNthDevice )
612     {
613         if ( TestDeviceAttribute( hgdNthDevice, screenDevice ) )
614         {
615             if ( TestDeviceAttribute( hgdNthDevice, screenActive ) )
616             {
617                 // The SectRect routine calculates the intersection 
618                 //  of the window rectangle and this gDevice 
619                 //  rectangle and returns TRUE if the rectangles intersect, 
620                 //  FALSE if they don't.
621                 SectRect( &rectWind, &( **hgdNthDevice ).gdRect, &rectSect );
622                 // determine which screen holds greatest window area
623                 //  first, calculate area of rectangle on current device
624                 sectArea = ( long )( rectSect.right - rectSect.left ) * ( rectSect.bottom - rectSect.top );
625                 if ( sectArea > greatestArea )
626                 {
627                     greatestArea = sectArea;    // set greatest area so far
628                     hgdZoomOnThisDevice = hgdNthDevice; // set zoom device
629                 }
630                 hgdNthDevice = GetNextDevice( hgdNthDevice );
631             }
632         }
633     }   // of WHILE
634     SetPort( pgpSave );
635     return hgdZoomOnThisDevice;
636 }
637
638 /*****************************************************************************
639  * FillOffScreen: fills offscreen buffer with random bright color
640  *****************************************************************************/
641
642 void FillOffscreen( vout_thread_t *p_vout, short index )
643 {
644     static RGBColor rgbColorOld;
645     GDHandle hGDSave;
646     CGrafPtr pCGrafSave;
647     Rect rectSource;
648     RGBColor rgbColor;
649     
650     //intf_ErrMsg( "FillOffscreen" );
651
652     GetPortBounds( p_vout->p_sys->p_gw[index], &rectSource );
653     
654     do 
655         rgbColor.red = ( Random () + 32767) / 2 + 32767;
656     while ( abs ( rgbColor.red - rgbColorOld.red ) <  3000 );   
657     do 
658         rgbColor.green = (Random () + 32767) / 2 + 32767;
659     while ( abs ( rgbColor.green - rgbColorOld.green ) <  3000);
660     do 
661         rgbColor.blue = (Random () + 32767) / 2 + 32767;
662     while ( abs ( rgbColor.blue - rgbColorOld.blue ) <  3000);
663     
664     rgbColorOld = rgbColor;
665
666     GetGWorld( &pCGrafSave, &hGDSave );
667     SetGWorld( p_vout->p_sys->p_gw[index], NULL );
668 //FIXME have global lock - kinda bad but oh well 
669 //    if ( LockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) ) )
670 //    {
671         // draw some background
672         EraseRect( &rectSource );
673         RGBForeColor( &rgbColor );
674         PaintRect( &rectSource );
675 //        UnlockPixels( GetGWorldPixMap( p_vout->p_sys->p_gw[index] ) );
676 //    }
677     SetGWorld( pCGrafSave, hGDSave );
678 }