1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2003  Sam Lantinga
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with this library; if not, write to the Free
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24#include "SDL_QuartzVideo.h"
25#include "SDL_QuartzWindow.h"
26#include "SDL_QuartzWM.h"
27
28/*
29    Add methods to get at private members of NSScreen.
30    Since there is a bug in Apple's screen switching code
31    that does not update this variable when switching
32    to fullscreen, we'll set it manually (but only for the
33    main screen).
34*/
35@interface NSScreen (NSScreenAccess)
36- (void) setFrame:(NSRect)frame;
37@end
38
39@implementation NSScreen (NSScreenAccess)
40- (void) setFrame:(NSRect)frame;
41{
42    _frame = frame;
43}
44@end
45
46
47/* Bootstrap functions */
48static int              QZ_Available ();
49static SDL_VideoDevice* QZ_CreateDevice (int device_index);
50static void             QZ_DeleteDevice (SDL_VideoDevice *device);
51
52/* Initialization, Query, Setup, and Redrawing functions */
53static int          QZ_VideoInit        (_THIS, SDL_PixelFormat *video_format);
54
55static SDL_Rect**   QZ_ListModes        (_THIS, SDL_PixelFormat *format,
56                                         Uint32 flags);
57static void         QZ_UnsetVideoMode   (_THIS, BOOL to_desktop);
58
59static SDL_Surface* QZ_SetVideoMode     (_THIS, SDL_Surface *current,
60                                         int width, int height, int bpp,
61                                         Uint32 flags);
62static int          QZ_ToggleFullScreen (_THIS, int on);
63static int          QZ_SetColors        (_THIS, int first_color,
64                                         int num_colors, SDL_Color *colors);
65
66static int          QZ_LockDoubleBuffer   (_THIS, SDL_Surface *surface);
67static void         QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface);
68static int          QZ_ThreadFlip         (_THIS);
69static int          QZ_FlipDoubleBuffer   (_THIS, SDL_Surface *surface);
70static void         QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects);
71
72static void         QZ_DirectUpdate     (_THIS, int num_rects, SDL_Rect *rects);
73static int          QZ_LockWindow       (_THIS, SDL_Surface *surface);
74static void         QZ_UnlockWindow     (_THIS, SDL_Surface *surface);
75static void         QZ_UpdateRects      (_THIS, int num_rects, SDL_Rect *rects);
76static void         QZ_VideoQuit        (_THIS);
77
78/* Hardware surface functions (for fullscreen mode only) */
79#if 0 /* Not used (apparently, it's really slow) */
80static int  QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color);
81#endif
82static int  QZ_LockHWSurface(_THIS, SDL_Surface *surface);
83static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface);
84static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface);
85static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface);
86/* static int  QZ_FlipHWSurface (_THIS, SDL_Surface *surface); */
87
88/* Bootstrap binding, enables entry point into the driver */
89VideoBootStrap QZ_bootstrap = {
90    "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice
91};
92
93
94/* Bootstrap functions */
95static int QZ_Available () {
96    return 1;
97}
98
99static SDL_VideoDevice* QZ_CreateDevice (int device_index) {
100
101#pragma unused (device_index)
102
103    SDL_VideoDevice *device;
104    SDL_PrivateVideoData *hidden;
105
106    device = (SDL_VideoDevice*) SDL_malloc (sizeof (*device) );
107    hidden = (SDL_PrivateVideoData*) SDL_malloc (sizeof (*hidden) );
108
109    if (device == NULL || hidden == NULL)
110        SDL_OutOfMemory ();
111
112    SDL_memset (device, 0, sizeof (*device) );
113    SDL_memset (hidden, 0, sizeof (*hidden) );
114
115    device->hidden = hidden;
116
117    device->VideoInit        = QZ_VideoInit;
118    device->ListModes        = QZ_ListModes;
119    device->SetVideoMode     = QZ_SetVideoMode;
120    device->ToggleFullScreen = QZ_ToggleFullScreen;
121    device->UpdateMouse      = QZ_UpdateMouse;
122    device->SetColors        = QZ_SetColors;
123    /* device->UpdateRects      = QZ_UpdateRects; this is determined by SetVideoMode() */
124    device->VideoQuit        = QZ_VideoQuit;
125
126    device->LockHWSurface   = QZ_LockHWSurface;
127    device->UnlockHWSurface = QZ_UnlockHWSurface;
128    device->AllocHWSurface   = QZ_AllocHWSurface;
129    device->FreeHWSurface   = QZ_FreeHWSurface;
130    /* device->FlipHWSurface   = QZ_FlipHWSurface */;
131
132    device->SetGamma     = QZ_SetGamma;
133    device->GetGamma     = QZ_GetGamma;
134    device->SetGammaRamp = QZ_SetGammaRamp;
135    device->GetGammaRamp = QZ_GetGammaRamp;
136
137    device->GL_GetProcAddress = QZ_GL_GetProcAddress;
138    device->GL_GetAttribute   = QZ_GL_GetAttribute;
139    device->GL_MakeCurrent    = QZ_GL_MakeCurrent;
140    device->GL_SwapBuffers    = QZ_GL_SwapBuffers;
141    device->GL_LoadLibrary    = QZ_GL_LoadLibrary;
142
143    device->FreeWMCursor   = QZ_FreeWMCursor;
144    device->CreateWMCursor = QZ_CreateWMCursor;
145    device->ShowWMCursor   = QZ_ShowWMCursor;
146    device->WarpWMCursor   = QZ_WarpWMCursor;
147    device->MoveWMCursor   = QZ_MoveWMCursor;
148    device->CheckMouseMode = QZ_CheckMouseMode;
149    device->InitOSKeymap   = QZ_InitOSKeymap;
150    device->PumpEvents     = QZ_PumpEvents;
151
152    device->SetCaption      = QZ_SetCaption;
153    device->SetIcon         = QZ_SetIcon;
154    device->IconifyWindow   = QZ_IconifyWindow;
155    device->SetWindowPos    = QZ_SetWindowPos;
156    device->GetWindowPos    = QZ_GetWindowPos;
157    device->IsWindowVisible = QZ_IsWindowVisible;
158    device->GetMonitorDPI   = QZ_GetMonitorDPI;
159    device->GetMonitorRect  = QZ_GetMonitorRect;
160    device->GetWMInfo     = QZ_GetWMInfo;
161    device->GrabInput     = QZ_GrabInput;
162
163    device->CreateYUVOverlay =  QZ_CreateYUVOverlay;
164
165    device->free             = QZ_DeleteDevice;
166
167    return device;
168}
169
170static void QZ_DeleteDevice (SDL_VideoDevice *device) {
171
172    SDL_free (device->hidden);
173    SDL_free (device);
174}
175
176static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format) {
177
178    NSRect r = NSMakeRect(0.0, 0.0, 0.0, 0.0);
179    const char *env = NULL;
180
181    /* Initialize the video settings; this data persists between mode switches */
182    display_id = kCGDirectMainDisplay;
183    save_mode  = CGDisplayCurrentMode    (display_id);
184    mode_list  = CGDisplayAvailableModes (display_id);
185    palette    = CGPaletteCreateDefaultColorPalette ();
186
187    env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER");
188    allow_screensaver = ( env && SDL_atoi(env) ) ? YES : NO;
189
190    /* Gather some information that is useful to know about the display */
191    CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayBitsPerPixel),
192                      kCFNumberSInt32Type, &device_bpp);
193
194    CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayWidth),
195                      kCFNumberSInt32Type, &device_width);
196
197    CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayHeight),
198                      kCFNumberSInt32Type, &device_height);
199
200    /* Determine the current screen size */
201    this->info.current_w = device_width;
202    this->info.current_h = device_height;
203
204    /* Determine the default screen depth */
205    video_format->BitsPerPixel = device_bpp;
206
207    /* Set misc globals */
208    current_grab_mode = SDL_GRAB_OFF;
209    cursor_should_be_visible    = YES;
210    cursor_visible              = YES;
211    current_mods = 0;
212    field_edit =  [[NSTextView alloc] initWithFrame:r];
213
214    if ( Gestalt(gestaltSystemVersion, &system_version) != noErr )
215        system_version = 0;
216
217    /* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */
218    QZ_RegisterForSleepNotifications (this);
219
220    /* Fill in some window manager capabilities */
221    this->info.wm_available = 1;
222
223    return 0;
224}
225
226static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags) {
227
228    CFIndex num_modes;
229    CFIndex i;
230
231    int list_size = 0;
232
233    /* Any windowed mode is acceptable */
234    if ( (flags & SDL_FULLSCREEN) == 0 )
235        return (SDL_Rect**)-1;
236
237    /* Free memory from previous call, if any */
238    if ( client_mode_list != NULL ) {
239
240        int i;
241
242        for (i = 0; client_mode_list[i] != NULL; i++)
243            SDL_free (client_mode_list[i]);
244
245        SDL_free (client_mode_list);
246        client_mode_list = NULL;
247    }
248
249    num_modes = CFArrayGetCount (mode_list);
250
251    /* Build list of modes with the requested bpp */
252    for (i = 0; i < num_modes; i++) {
253
254        CFDictionaryRef onemode;
255        CFNumberRef     number;
256        int bpp;
257
258        onemode = CFArrayGetValueAtIndex (mode_list, i);
259        number = CFDictionaryGetValue (onemode, kCGDisplayBitsPerPixel);
260        CFNumberGetValue (number, kCFNumberSInt32Type, &bpp);
261
262        if (bpp == format->BitsPerPixel) {
263
264            int intvalue;
265            int hasMode;
266            int width, height;
267
268            number = CFDictionaryGetValue (onemode, kCGDisplayWidth);
269            CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
270            width = (Uint16) intvalue;
271
272            number = CFDictionaryGetValue (onemode, kCGDisplayHeight);
273            CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
274            height = (Uint16) intvalue;
275
276            /* Check if mode is already in the list */
277            {
278                int i;
279                hasMode = SDL_FALSE;
280                for (i = 0; i < list_size; i++) {
281                    if (client_mode_list[i]->w == width &&
282                        client_mode_list[i]->h == height) {
283                        hasMode = SDL_TRUE;
284                        break;
285                    }
286                }
287            }
288
289            /* Grow the list and add mode to the list */
290            if ( ! hasMode ) {
291
292                SDL_Rect *rect;
293
294                list_size++;
295
296                if (client_mode_list == NULL)
297                    client_mode_list = (SDL_Rect**)
298                        SDL_malloc (sizeof(*client_mode_list) * (list_size+1) );
299                else
300                    client_mode_list = (SDL_Rect**)
301                        SDL_realloc (client_mode_list, sizeof(*client_mode_list) * (list_size+1));
302
303                rect = (SDL_Rect*) SDL_malloc (sizeof(**client_mode_list));
304
305                if (client_mode_list == NULL || rect == NULL) {
306                    SDL_OutOfMemory ();
307                    return NULL;
308                }
309
310                rect->x = rect->y = 0;
311                rect->w = width;
312                rect->h = height;
313
314                client_mode_list[list_size-1] = rect;
315                client_mode_list[list_size]   = NULL;
316            }
317        }
318    }
319
320    /* Sort list largest to smallest (by area) */
321    {
322        int i, j;
323        for (i = 0; i < list_size; i++) {
324            for (j = 0; j < list_size-1; j++) {
325
326                int area1, area2;
327                area1 = client_mode_list[j]->w * client_mode_list[j]->h;
328                area2 = client_mode_list[j+1]->w * client_mode_list[j+1]->h;
329
330                if (area1 < area2) {
331                    SDL_Rect *tmp = client_mode_list[j];
332                    client_mode_list[j] = client_mode_list[j+1];
333                    client_mode_list[j+1] = tmp;
334                }
335            }
336        }
337    }
338    return client_mode_list;
339}
340
341static SDL_bool QZ_WindowPosition(_THIS, int *x, int *y)
342{
343    const char *window = getenv("SDL_VIDEO_WINDOW_POS");
344    if ( window ) {
345        if ( sscanf(window, "%d,%d", x, y) == 2 ) {
346            return SDL_TRUE;
347        }
348    }
349    return SDL_FALSE;
350}
351
352static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop) {
353
354    /* Reset values that may change between switches */
355    this->info.blit_fill  = 0;
356    this->FillHWRect      = NULL;
357    this->UpdateRects     = NULL;
358    this->LockHWSurface   = NULL;
359    this->UnlockHWSurface = NULL;
360
361    /* Release fullscreen resources */
362    if ( mode_flags & SDL_FULLSCREEN ) {
363
364        NSRect screen_rect;
365
366        /*  Release double buffer stuff */
367        if ( mode_flags & SDL_DOUBLEBUF) {
368            quit_thread = YES;
369            SDL_SemPost (sem1);
370            SDL_WaitThread (thread, NULL);
371            SDL_DestroySemaphore (sem1);
372            SDL_DestroySemaphore (sem2);
373            SDL_free (sw_buffers[0]);
374        }
375
376        /* If we still have a valid window, close it. */
377        if ( qz_window ) {
378            [ qz_window close ];
379            [ qz_window release ];
380            qz_window = nil;
381            window_view = nil;
382        }
383        /*
384            Release the OpenGL context
385            Do this first to avoid trash on the display before fade
386        */
387        if ( mode_flags & SDL_OPENGL ) {
388
389            QZ_TearDownOpenGL (this);
390            CGLSetFullScreen (NULL);
391        }
392        if (to_desktop) {
393            ShowMenuBar ();
394            /* Restore original screen resolution/bpp */
395            CGDisplaySwitchToMode (display_id, save_mode);
396            CGReleaseAllDisplays ();
397            /*
398                Reset the main screen's rectangle
399                See comment in QZ_SetVideoFullscreen for why we do this
400            */
401            screen_rect = NSMakeRect(0,0,device_width,device_height);
402            [ [ NSScreen mainScreen ] setFrame:screen_rect ];
403        }
404    }
405    /* Release window mode resources */
406    else {
407
408        [ qz_window close ];
409        [ qz_window release ];
410        qz_window = nil;
411        window_view = nil;
412
413        /* Release the OpenGL context */
414        if ( mode_flags & SDL_OPENGL )
415            QZ_TearDownOpenGL (this);
416    }
417
418    /* Signal successful teardown */
419    video_set = SDL_FALSE;
420}
421
422static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int width,
423                                           int height, int bpp, Uint32 flags) {
424    boolean_t exact_match = 0;
425    NSRect screen_rect;
426    CGError error;
427    NSRect contentRect;
428    BOOL isCustom = NO;
429    CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
430
431    /* Fade to black to hide resolution-switching flicker (and garbage
432       that is displayed by a destroyed OpenGL context, if applicable) */
433    if ( CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess ) {
434        CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
435    }
436
437    /* Destroy any previous mode */
438    if (video_set == SDL_TRUE)
439        QZ_UnsetVideoMode (this, FALSE);
440
441    /* See if requested mode exists */
442    mode = CGDisplayBestModeForParameters (display_id, bpp, width,
443                                           height, &exact_match);
444
445    /* Require an exact match to the requested mode */
446    if ( ! exact_match ) {
447        SDL_SetError ("Failed to find display resolution: %dx%dx%d", width, height, bpp);
448        goto ERR_NO_MATCH;
449    }
450
451    /* Put up the blanking window (a window above all other windows) */
452    if (getenv ("SDL_SINGLEDISPLAY"))
453        error = CGDisplayCapture (display_id);
454    else
455        error = CGCaptureAllDisplays ();
456
457    if ( CGDisplayNoErr != error ) {
458        SDL_SetError ("Failed capturing display");
459        goto ERR_NO_CAPTURE;
460    }
461
462    /* Do the physical switch */
463    if ( CGDisplayNoErr != CGDisplaySwitchToMode (display_id, mode) ) {
464        SDL_SetError ("Failed switching display resolution");
465        goto ERR_NO_SWITCH;
466    }
467
468    current->pixels = (Uint32*) CGDisplayBaseAddress (display_id);
469    current->pitch  = CGDisplayBytesPerRow (display_id);
470
471    current->flags = 0;
472    current->w = width;
473    current->h = height;
474    current->flags |= SDL_FULLSCREEN;
475    current->flags |= SDL_HWSURFACE;
476    current->flags |= SDL_PREALLOC;
477
478    this->UpdateRects     = QZ_DirectUpdate;
479    this->LockHWSurface   = QZ_LockHWSurface;
480    this->UnlockHWSurface = QZ_UnlockHWSurface;
481
482    /* Setup double-buffer emulation */
483    if ( flags & SDL_DOUBLEBUF ) {
484
485        /*
486            Setup a software backing store for reasonable results when
487            double buffering is requested (since a single-buffered hardware
488            surface looks hideous).
489
490            The actual screen blit occurs in a separate thread to allow
491            other blitting while waiting on the VBL (and hence results in higher framerates).
492        */
493        this->LockHWSurface = NULL;
494        this->UnlockHWSurface = NULL;
495        this->UpdateRects = NULL;
496
497        current->flags |= (SDL_HWSURFACE|SDL_DOUBLEBUF);
498        this->UpdateRects = QZ_DoubleBufferUpdate;
499        this->LockHWSurface = QZ_LockDoubleBuffer;
500        this->UnlockHWSurface = QZ_UnlockDoubleBuffer;
501        this->FlipHWSurface = QZ_FlipDoubleBuffer;
502
503        current->pixels = SDL_malloc (current->pitch * current->h * 2);
504        if (current->pixels == NULL) {
505            SDL_OutOfMemory ();
506            goto ERR_DOUBLEBUF;
507        }
508
509        sw_buffers[0] = current->pixels;
510        sw_buffers[1] = (Uint8*)current->pixels + current->pitch * current->h;
511
512        quit_thread = NO;
513        sem1 = SDL_CreateSemaphore (0);
514        sem2 = SDL_CreateSemaphore (1);
515        thread = SDL_CreateThread ((int (*)(void *))QZ_ThreadFlip, this);
516    }
517
518    if ( CGDisplayCanSetPalette (display_id) )
519        current->flags |= SDL_HWPALETTE;
520
521    /* The code below checks for any valid custom windows and views.  If none are
522       available, then we create new ones.  Window/View code was added in FULLSCREEN
523       so that special events like the changing of the cursor image would be handled
524       ( only the front-most and active application can change the cursor appearance
525       and with no valid window/view in FULLSCREEN, SDL wouldn't update its cursor. )
526    */
527	/* Check for user-specified window and view */
528    {
529        char *windowPtrString = getenv ("SDL_NSWindowPointer");
530        char *viewPtrString = getenv ("SDL_NSQuickDrawViewPointer");
531
532        contentRect = NSMakeRect (0, 0, width, height);
533
534        if (windowPtrString && viewPtrString) {
535            /* Release any previous window */
536            if ( qz_window ) {
537                [ qz_window release ];
538                qz_window = nil;
539            }
540
541            qz_window = (NSWindow*)atoi(windowPtrString);
542            window_view = (NSQuickDrawView*)atoi(viewPtrString);
543            isCustom = YES;
544            /*
545                Retain reference to window because we
546                might release it in QZ_UnsetVideoMode
547            */
548            [ qz_window retain ];
549        }
550    }
551    /* Check if we should recreate the window */
552    if (qz_window == nil) {
553        /* Manually create a window, avoids having a nib file resource */
554        qz_window = [ [ SDL_QuartzWindow alloc ]
555            initWithContentRect:contentRect
556                styleMask:nil
557                    backing:NSBackingStoreBuffered
558                        defer:NO ];
559
560        if (qz_window != nil) {
561            [ qz_window setAcceptsMouseMovedEvents:YES ];
562            [ qz_window setViewsNeedDisplay:NO ];
563        }
564    }
565    /* We already have a window, just change its size */
566    else {
567        if (!isCustom) {
568            [ qz_window setContentSize:contentRect.size ];
569            current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags;
570            [ window_view setFrameSize:contentRect.size ];
571        }
572    }
573
574    /* Setup OpenGL for a fullscreen context */
575    if (flags & SDL_OPENGL) {
576
577        CGLError err;
578        CGLContextObj ctx;
579
580        if ( ! QZ_SetupOpenGL (this, bpp, flags) ) {
581            goto ERR_NO_GL;
582        }
583
584        /* Initialize the NSView and add it to our window.  The presence of a valid window and
585           view allow the cursor to be changed whilst in fullscreen.*/
586        window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
587        [ [ qz_window contentView ] addSubview:window_view ];
588        [ window_view release ];
589
590        ctx = [ gl_context cglContext ];
591        err = CGLSetFullScreen (ctx);
592
593        if (err) {
594            SDL_SetError ("Error setting OpenGL fullscreen: %s", CGLErrorString(err));
595            goto ERR_NO_GL;
596        }
597
598        [ gl_context makeCurrentContext];
599
600        glClear (GL_COLOR_BUFFER_BIT);
601
602        [ gl_context flushBuffer ];
603
604        current->flags |= SDL_OPENGL;
605    }
606
607    /* If we don't hide menu bar, it will get events and interrupt the program */
608    HideMenuBar ();
609
610    /* Fade in again (asynchronously) */
611    if ( fade_token != kCGDisplayFadeReservationInvalidToken ) {
612        CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
613        CGReleaseDisplayFadeReservation(fade_token);
614    }
615
616    /*
617        There is a bug in Cocoa where NSScreen doesn't synchronize
618        with CGDirectDisplay, so the main screen's frame is wrong.
619        As a result, coordinate translation produces incorrect results.
620        We can hack around this bug by setting the screen rect
621        ourselves. This hack should be removed if/when the bug is fixed.
622    */
623    screen_rect = NSMakeRect(0,0,width,height);
624    [ [ NSScreen mainScreen ] setFrame:screen_rect ];
625
626    /* Save the flags to ensure correct tear-down */
627    mode_flags = current->flags;
628
629    /* Set app state, hide cursor if necessary, ... */
630    QZ_DoActivate(this);
631
632    return current;
633
634    /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
635ERR_NO_GL:
636ERR_DOUBLEBUF:  CGDisplaySwitchToMode (display_id, save_mode);
637ERR_NO_SWITCH:  CGReleaseAllDisplays ();
638ERR_NO_CAPTURE:
639ERR_NO_MATCH:   if ( fade_token != kCGDisplayFadeReservationInvalidToken ) {
640                    CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
641                    CGReleaseDisplayFadeReservation (fade_token);
642                }
643                return NULL;
644}
645
646static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width,
647                                         int height, int *bpp, Uint32 flags) {
648    unsigned int style;
649    NSRect contentRect;
650    BOOL isCustom = NO;
651    int center_window = 1;
652    int origin_x, origin_y;
653    CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
654
655    current->flags = 0;
656    current->w = width;
657    current->h = height;
658
659    contentRect = NSMakeRect (0, 0, width, height);
660
661    /*
662        Check if we should completely destroy the previous mode
663        - If it is fullscreen
664        - If it has different noframe or resizable attribute
665        - If it is OpenGL (since gl attributes could be different)
666        - If new mode is OpenGL, but previous mode wasn't
667    */
668    if (video_set == SDL_TRUE) {
669        if (mode_flags & SDL_FULLSCREEN) {
670            /* Fade to black to hide resolution-switching flicker (and garbage
671               that is displayed by a destroyed OpenGL context, if applicable) */
672            if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) {
673                CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
674            }
675            QZ_UnsetVideoMode (this, TRUE);
676        }
677        else if ( ((mode_flags ^ flags) & (SDL_NOFRAME|SDL_RESIZABLE)) ||
678                  (mode_flags & SDL_OPENGL) ||
679                  (flags & SDL_OPENGL) ) {
680            QZ_UnsetVideoMode (this, TRUE);
681        }
682    }
683
684    /* Check for user-specified window and view */
685    {
686        char *windowPtrString = getenv ("SDL_NSWindowPointer");
687        char *viewPtrString = getenv ("SDL_NSQuickDrawViewPointer");
688
689        if (windowPtrString && viewPtrString) {
690
691            /* Release any previous window */
692            if ( qz_window ) {
693                [ qz_window release ];
694                qz_window = nil;
695            }
696
697            qz_window = (NSWindow*)atoi(windowPtrString);
698            window_view = (NSQuickDrawView*)atoi(viewPtrString);
699            isCustom = YES;
700
701            /*
702                Retain reference to window because we
703                might release it in QZ_UnsetVideoMode
704            */
705            [ qz_window retain ];
706
707            style = [ qz_window styleMask ];
708            /* Check resizability */
709            if ( style & NSResizableWindowMask )
710                current->flags |= SDL_RESIZABLE;
711
712            /* Check frame */
713            if ( style & NSBorderlessWindowMask )
714                current->flags |= SDL_NOFRAME;
715        }
716    }
717
718    /* Check if we should recreate the window */
719    if (qz_window == nil) {
720
721        /* Set the window style based on input flags */
722        if ( flags & SDL_NOFRAME ) {
723            style = NSBorderlessWindowMask;
724            current->flags |= SDL_NOFRAME;
725        } else {
726            style = NSTitledWindowMask;
727            style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
728            if ( flags & SDL_RESIZABLE ) {
729                style |= NSResizableWindowMask;
730                current->flags |= SDL_RESIZABLE;
731            }
732        }
733        /* Manually create a window, avoids having a nib file resource */
734        qz_window = [ [ SDL_QuartzWindow alloc ]
735            initWithContentRect:contentRect
736                styleMask:style
737                    backing:NSBackingStoreBuffered
738                        defer:NO ];
739
740        if (qz_window == nil) {
741            SDL_SetError ("Could not create the Cocoa window");
742            if (fade_token != kCGDisplayFadeReservationInvalidToken) {
743                CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
744                CGReleaseDisplayFadeReservation (fade_token);
745            }
746            return NULL;
747        }
748
749        /*[ qz_window setReleasedWhenClosed:YES ];*/
750        QZ_SetCaption(this, this->wm_title, this->wm_icon);
751        [ qz_window setAcceptsMouseMovedEvents:YES ];
752        [ qz_window setViewsNeedDisplay:NO ];
753
754        if ( QZ_WindowPosition(this, &origin_x, &origin_y) ) {
755            [ qz_window setFrameTopLeftPoint:NSMakePoint(origin_x,this->info.current_h - origin_y) ];
756        } else {
757            [ qz_window center ];
758        }
759        [ qz_window setDelegate:
760            [ [ [ SDL_QuartzWindowDelegate alloc ] init ] autorelease ] ];
761        [ qz_window setContentView: [ [ [ SDL_QuartzView alloc ] init ] autorelease ] ];
762    }
763    /* We already have a window, just change its size */
764    else {
765
766        if (!isCustom) {
767            [ qz_window setContentSize:contentRect.size ];
768            current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags;
769            [ window_view setFrameSize:contentRect.size ];
770        }
771    }
772
773    /* For OpenGL, we bind the context to a subview */
774    if ( flags & SDL_OPENGL ) {
775
776        if ( ! QZ_SetupOpenGL (this, *bpp, flags) ) {
777            if (fade_token != kCGDisplayFadeReservationInvalidToken) {
778                CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
779                CGReleaseDisplayFadeReservation (fade_token);
780            }
781            return NULL;
782        }
783
784        window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
785        [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
786        [ [ qz_window contentView ] addSubview:window_view ];
787        [ gl_context setView: window_view ];
788        [ window_view release ];
789        [ gl_context makeCurrentContext];
790        [ qz_window makeKeyAndOrderFront:nil ];
791        current->flags |= SDL_OPENGL;
792    }
793    /* For 2D, we set the subview to an NSQuickDrawView */
794    else {
795        short     qdbpp = 0;
796        CGrafPtr  qdport;
797
798        /* Only recreate the view if it doesn't already exist */
799        if (window_view == nil) {
800
801            window_view = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ];
802            [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
803            [ [ qz_window contentView ] addSubview:window_view ];
804            [ window_view release ];
805            [ qz_window makeKeyAndOrderFront:nil ];
806        }
807
808        qdport = [ window_view qdPort ];
809
810        LockPortBits ( qdport );
811        current->pixels = GetPixBaseAddr ( GetPortPixMap ( qdport ) );
812        current->pitch  = GetPixRowBytes ( GetPortPixMap ( qdport ) );
813        qdbpp           = GetPixDepth ( GetPortPixMap ( qdport ) );
814        UnlockPortBits ( qdport );
815
816        /* QuickDraw may give a 16-bit shadow surface on 8-bit displays! */
817        *bpp = qdbpp;
818
819        current->flags |= SDL_SWSURFACE;
820        current->flags |= SDL_PREALLOC;
821        current->flags |= SDL_ASYNCBLIT;
822
823        /*
824            current->pixels now points to the window's pixels
825            We want it to point to the *view's* pixels
826        */
827        {
828            NSRect  winFrame  = [ qz_window frame ];
829            NSRect  viewFrame = [ window_view frame ];
830
831            int vOffset = winFrame.size.height - viewFrame.size.height - viewFrame.origin.y;
832            int hOffset = viewFrame.origin.x;
833
834            current->pixels = (Uint8 *)current->pixels + (vOffset * current->pitch) + hOffset * (qdbpp/8);
835        }
836        this->UpdateRects     = QZ_UpdateRects;
837        this->LockHWSurface   = QZ_LockWindow;
838        this->UnlockHWSurface = QZ_UnlockWindow;
839    }
840
841    /* Save flags to ensure correct teardown */
842    mode_flags = current->flags;
843
844    /* Fade in again (asynchronously) if we came from a fullscreen mode and faded to black */
845    if (fade_token != kCGDisplayFadeReservationInvalidToken) {
846        CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
847        CGReleaseDisplayFadeReservation (fade_token);
848    }
849
850    return current;
851}
852
853static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, int width,
854                                     int height, int bpp, Uint32 flags) {
855
856    current->flags = 0;
857    current->pixels = NULL;
858
859    /* Setup full screen video */
860    if ( flags & SDL_FULLSCREEN ) {
861        current = QZ_SetVideoFullScreen (this, current, width, height, bpp, flags );
862        if (current == NULL)
863            return NULL;
864    }
865    /* Setup windowed video */
866    else {
867        /* Force bpp to the device's bpp */
868        bpp = device_bpp;
869        current = QZ_SetVideoWindowed (this, current, width, height, &bpp, flags);
870        if (current == NULL)
871            return NULL;
872    }
873
874    /* Setup the new pixel format */
875    {
876        int amask = 0,
877        rmask = 0,
878        gmask = 0,
879        bmask = 0;
880
881        switch (bpp) {
882            case 16:   /* (1)-5-5-5 RGB */
883                amask = 0;
884                rmask = 0x7C00;
885                gmask = 0x03E0;
886                bmask = 0x001F;
887                break;
888            case 24:
889                SDL_SetError ("24bpp is not available");
890                return NULL;
891            case 32:   /* (8)-8-8-8 ARGB */
892                amask = 0x00000000;
893                rmask = 0x00FF0000;
894                gmask = 0x0000FF00;
895                bmask = 0x000000FF;
896                break;
897        }
898
899        if ( ! SDL_ReallocFormat (current, bpp,
900                                  rmask, gmask, bmask, amask ) ) {
901            SDL_SetError ("Couldn't reallocate pixel format");
902            return NULL;
903           }
904    }
905
906    /* Signal successful completion (used internally) */
907    video_set = SDL_TRUE;
908
909    return current;
910}
911
912static int QZ_ToggleFullScreen (_THIS, int on) {
913    return 0;
914}
915
916static int QZ_SetColors (_THIS, int first_color, int num_colors,
917                         SDL_Color *colors) {
918
919    CGTableCount  index;
920    CGDeviceColor color;
921
922    for (index = first_color; index < first_color+num_colors; index++) {
923
924        /* Clamp colors between 0.0 and 1.0 */
925        color.red   = colors->r / 255.0;
926        color.blue  = colors->b / 255.0;
927        color.green = colors->g / 255.0;
928
929        colors++;
930
931        CGPaletteSetColorAtIndex (palette, color, index);
932    }
933
934    if ( CGDisplayNoErr != CGDisplaySetPalette (display_id, palette) )
935        return 0;
936
937    return 1;
938}
939
940static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface) {
941
942    return 1;
943}
944
945static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface) {
946
947}
948
949 /* The VBL delay is based on code by Ian R Ollmann's RezLib <iano@cco.caltech.edu> */
950 static AbsoluteTime QZ_SecondsToAbsolute ( double seconds ) {
951
952    union
953    {
954        UInt64 i;
955        Nanoseconds ns;
956    } temp;
957
958    temp.i = seconds * 1000000000.0;
959
960    return NanosecondsToAbsolute ( temp.ns );
961}
962
963static int QZ_ThreadFlip (_THIS) {
964
965    Uint8 *src, *dst;
966    int skip, len, h;
967
968    /*
969        Give this thread the highest scheduling priority possible,
970        in the hopes that it will immediately run after the VBL delay
971    */
972    {
973        pthread_t current_thread;
974        int policy;
975        struct sched_param param;
976
977        current_thread = pthread_self ();
978        pthread_getschedparam (current_thread, &policy, &param);
979        policy = SCHED_RR;
980        param.sched_priority = sched_get_priority_max (policy);
981        pthread_setschedparam (current_thread, policy, &param);
982    }
983
984    while (1) {
985
986        SDL_SemWait (sem1);
987        if (quit_thread)
988            return 0;
989
990        /*
991         * We have to add SDL_VideoSurface->offset here, since we might be a
992         *  smaller surface in the center of the framebuffer (you asked for
993         *  a fullscreen resolution smaller than the hardware could supply
994         *  so SDL is centering it in a bigger resolution)...
995         */
996        dst = (Uint8 *)CGDisplayBaseAddress (display_id) + SDL_VideoSurface->offset;
997        src = current_buffer + SDL_VideoSurface->offset;
998        len = SDL_VideoSurface->w * SDL_VideoSurface->format->BytesPerPixel;
999        h = SDL_VideoSurface->h;
1000        skip = SDL_VideoSurface->pitch;
1001
1002        /* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */
1003        {
1004
1005            /* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */
1006            double refreshRate;
1007            double linesPerSecond;
1008            double target;
1009            double position;
1010            double adjustment;
1011            AbsoluteTime nextTime;
1012            CFNumberRef refreshRateCFNumber;
1013
1014            refreshRateCFNumber = CFDictionaryGetValue (mode, kCGDisplayRefreshRate);
1015            if ( NULL == refreshRateCFNumber ) {
1016                SDL_SetError ("Mode has no refresh rate");
1017                goto ERROR;
1018            }
1019
1020            if ( 0 == CFNumberGetValue (refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) ) {
1021                SDL_SetError ("Error getting refresh rate");
1022                goto ERROR;
1023            }
1024
1025            if ( 0 == refreshRate ) {
1026
1027               SDL_SetError ("Display has no refresh rate, using 60hz");
1028
1029                /* ok, for LCD's we'll emulate a 60hz refresh, which may or may not look right */
1030                refreshRate = 60.0;
1031            }
1032
1033            linesPerSecond = refreshRate * h;
1034            target = h;
1035
1036            /* Figure out the first delay so we start off about right */
1037            position = CGDisplayBeamPosition (display_id);
1038            if (position > target)
1039                position = 0;
1040
1041            adjustment = (target - position) / linesPerSecond;
1042
1043            nextTime = AddAbsoluteToAbsolute (UpTime (), QZ_SecondsToAbsolute (adjustment));
1044
1045            MPDelayUntil (&nextTime);
1046        }
1047
1048
1049        /* On error, skip VBL delay */
1050        ERROR:
1051
1052        while ( h-- ) {
1053
1054            SDL_memcpy (dst, src, len);
1055            src += skip;
1056            dst += skip;
1057        }
1058
1059        /* signal flip completion */
1060        SDL_SemPost (sem2);
1061    }
1062
1063    return 0;
1064}
1065
1066static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface) {
1067
1068    /* wait for previous flip to complete */
1069    SDL_SemWait (sem2);
1070
1071    current_buffer = surface->pixels;
1072
1073    if (surface->pixels == sw_buffers[0])
1074        surface->pixels = sw_buffers[1];
1075    else
1076        surface->pixels = sw_buffers[0];
1077
1078    /* signal worker thread to do the flip */
1079    SDL_SemPost (sem1);
1080
1081    return 0;
1082}
1083
1084
1085static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects) {
1086
1087    /* perform a flip if someone calls updaterects on a doublebuferred surface */
1088    this->FlipHWSurface (this, SDL_VideoSurface);
1089}
1090
1091static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects) {
1092#pragma unused(this,num_rects,rects)
1093}
1094
1095/*
1096    The obscured code is based on work by Matt Slot fprefect@ambrosiasw.com,
1097    who supplied sample code for Carbon.
1098*/
1099
1100/*#define TEST_OBSCURED 1*/
1101
1102#if TEST_OBSCURED
1103#include "CGS.h"
1104#endif
1105
1106static int QZ_IsWindowObscured (NSWindow *window) {
1107
1108
1109#if TEST_OBSCURED
1110
1111    /*
1112        In order to determine if a direct copy to the screen is possible,
1113        we must figure out if there are any windows covering ours (including shadows).
1114        This can be done by querying the window server about the on screen
1115        windows for their screen rectangle and window level.
1116        The procedure used below is puts accuracy before speed; however, it aims to call
1117        the window server the fewest number of times possible to keep things reasonable.
1118        In my testing on a 300mhz G3, this routine typically takes < 2 ms. -DW
1119
1120    Notes:
1121        -Calls into the Window Server involve IPC which is slow.
1122        -Getting a rectangle seems slower than getting the window level
1123        -The window list we get back is in sorted order, top to bottom
1124        -On average, I suspect, most windows above ours are dock icon windows (hence optimization)
1125        -Some windows above ours are always there, and cannot move or obscure us (menu bar)
1126
1127    Bugs:
1128        -no way (yet) to deactivate direct drawing when a window is dragged,
1129        or suddenly obscured, so drawing continues and can produce garbage
1130        We need some kind of locking mechanism on window movement to prevent this
1131
1132        -deactivated normal windows use activated normal
1133        window shadows (slight inaccuraccy)
1134    */
1135
1136    /* Cache the connection to the window server */
1137    static CGSConnectionID    cgsConnection = (CGSConnectionID) -1;
1138
1139    /* Cache the dock icon windows */
1140    static CGSWindowID          dockIcons[kMaxWindows];
1141    static int                  numCachedDockIcons = 0;
1142
1143    CGSWindowID                windows[kMaxWindows];
1144    CGSWindowCount             i, count;
1145    CGSWindowLevel             winLevel;
1146    CGSRect                    winRect;
1147
1148    CGSRect contentRect;
1149    int     windowNumber;
1150    int     firstDockIcon;
1151    int     dockIconCacheMiss;
1152    int     windowContentOffset;
1153
1154    int     obscured = SDL_TRUE;
1155
1156    if ( [ window isVisible ] ) {
1157
1158        /*
1159            walk the window list looking for windows over top of
1160            (or casting a shadow on) ours
1161        */
1162
1163        /*
1164           Get a connection to the window server
1165           Should probably be moved out into SetVideoMode() or InitVideo()
1166        */
1167        if (cgsConnection == (CGSConnectionID) -1) {
1168            cgsConnection = (CGSConnectionID) 0;
1169            cgsConnection = _CGSDefaultConnection ();
1170        }
1171
1172        if (cgsConnection) {
1173
1174            if ( ! [ window styleMask ] & NSBorderlessWindowMask )
1175                windowContentOffset = 22;
1176            else
1177                windowContentOffset = 0;
1178
1179            windowNumber = [ window windowNumber ];
1180
1181            /* The window list is sorted according to order on the screen */
1182            count = 0;
1183            CGSGetOnScreenWindowList (cgsConnection, 0, kMaxWindows, windows, &count);
1184            CGSGetScreenRectForWindow (cgsConnection, windowNumber, &contentRect);
1185
1186            /* adjust rect for window title bar (if present) */
1187            contentRect.origin.y    += windowContentOffset;
1188            contentRect.size.height -= windowContentOffset;
1189
1190            firstDockIcon = -1;
1191            dockIconCacheMiss = SDL_FALSE;
1192
1193            /*
1194                The first window is always an empty window with level kCGSWindowLevelTop
1195                so start at index 1
1196            */
1197            for (i = 1; i < count; i++) {
1198
1199                /* If we reach our window in the list, it cannot be obscured */
1200                if (windows[i] == windowNumber) {
1201
1202                    obscured = SDL_FALSE;
1203                    break;
1204                }
1205                else {
1206
1207                    float shadowSide;
1208                    float shadowTop;
1209                    float shadowBottom;
1210
1211                    CGSGetWindowLevel (cgsConnection, windows[i], &winLevel);
1212
1213                    if (winLevel == kCGSWindowLevelDockIcon) {
1214
1215                        int j;
1216
1217                        if (firstDockIcon < 0) {
1218
1219                            firstDockIcon = i;
1220
1221                            if (numCachedDockIcons > 0) {
1222
1223                                for (j = 0; j < numCachedDockIcons; j++) {
1224
1225                                    if (windows[i] == dockIcons[j])
1226                                        i++;
1227                                    else
1228                                        break;
1229                                }
1230
1231                                if (j != 0) {
1232
1233                                    i--;
1234
1235                                    if (j < numCachedDockIcons) {
1236
1237                                        dockIconCacheMiss = SDL_TRUE;
1238                                    }
1239                                }
1240
1241                            }
1242                        }
1243
1244                        continue;
1245                    }
1246                    else if (winLevel == kCGSWindowLevelMenuIgnore
1247                             /* winLevel == kCGSWindowLevelTop */) {
1248
1249                        continue; /* cannot obscure window */
1250                    }
1251                    else if (winLevel == kCGSWindowLevelDockMenu ||
1252                             winLevel == kCGSWindowLevelMenu) {
1253
1254                        shadowSide = 18;
1255                        shadowTop = 4;
1256                        shadowBottom = 22;
1257                    }
1258                    else if (winLevel == kCGSWindowLevelUtility) {
1259
1260                        shadowSide = 8;
1261                        shadowTop = 4;
1262                        shadowBottom = 12;
1263                    }
1264                    else if (winLevel == kCGSWindowLevelNormal) {
1265
1266                        /*
1267                            These numbers are for foreground windows,
1268                            they are too big (but will work) for background windows
1269                        */
1270                        shadowSide = 20;
1271                        shadowTop = 10;
1272                        shadowBottom = 24;
1273                    }
1274                    else if (winLevel == kCGSWindowLevelDock) {
1275
1276                        /* Create dock icon cache */
1277                        if (numCachedDockIcons != (i-firstDockIcon) ||
1278                            dockIconCacheMiss) {
1279
1280                            numCachedDockIcons = i - firstDockIcon;
1281                            SDL_memcpy (dockIcons, &(windows[firstDockIcon]),
1282                                    numCachedDockIcons * sizeof(*windows));
1283                        }
1284
1285                        /* no shadow */
1286                        shadowSide = 0;
1287                        shadowTop = 0;
1288                        shadowBottom = 0;
1289                    }
1290                    else {
1291
1292                        /*
1293                            kCGSWindowLevelDockLabel,
1294                            kCGSWindowLevelDock,
1295                            kOther???
1296                        */
1297
1298                        /* no shadow */
1299                        shadowSide = 0;
1300                        shadowTop = 0;
1301                        shadowBottom = 0;
1302                    }
1303
1304                    CGSGetScreenRectForWindow (cgsConnection, windows[i], &winRect);
1305
1306                    winRect.origin.x -= shadowSide;
1307                    winRect.origin.y -= shadowTop;
1308                    winRect.size.width += shadowSide;
1309                    winRect.size.height += shadowBottom;
1310
1311                    if (NSIntersectsRect (contentRect, winRect)) {
1312
1313                        obscured = SDL_TRUE;
1314                        break;
1315                    }
1316
1317                } /* window was not our window */
1318
1319            } /* iterate over windows */
1320
1321        } /* get cgsConnection */
1322
1323    } /* window is visible */
1324
1325    return obscured;
1326#else
1327    return SDL_TRUE;
1328#endif
1329}
1330
1331
1332/* Locking functions for the software window buffer */
1333static int QZ_LockWindow (_THIS, SDL_Surface *surface) {
1334#if 1
1335    return LockPortBits( [ window_view qdPort ] );
1336#else
1337    CGrafPtr   qdport = [ window_view qdPort ];
1338    int        result = LockPortBits ( qdport );
1339
1340    if ( !result ) {
1341        Uint8*  pixels    = GetPixBaseAddr ( GetPortPixMap ( qdport ) );
1342        NSRect  viewFrame = [ window_view frame ];
1343        NSRect  winFrame  = [ qz_window frame ];
1344        int     vOffset   = winFrame.size.height - viewFrame.size.height - viewFrame.origin.y;
1345        int     hOffset   = viewFrame.origin.x;
1346
1347        pixels = pixels + (vOffset * surface->pitch) + hOffset *4;
1348
1349        if ( surface->pixels != pixels ) {
1350            fprintf(stderr,"XXX: surface->pixels is %p, should be %p\n", surface->pixels, pixels);
1351        }
1352    } else {
1353        fprintf(stderr, "XXX: could not lock port %p for surface %p\n", qdport, surface);
1354    }
1355    return result;
1356#endif
1357}
1358
1359static void QZ_UnlockWindow (_THIS, SDL_Surface *surface) {
1360
1361    UnlockPortBits ( [ window_view qdPort ] );
1362}
1363
1364/* Resize icon, BMP format */
1365static const unsigned char QZ_ResizeIcon[] = {
1366    0x42,0x4d,0x31,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
1367    0x00,0x00,0x0d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,
1368    0x00,0x00,0xfb,0x01,0x00,0x00,0x13,0x0b,0x00,0x00,0x13,0x0b,0x00,0x00,0x00,0x00,
1369    0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1370    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1371    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
1372    0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,
1373    0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,
1374    0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xda,0xda,0xda,0x87,
1375    0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,
1376    0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
1377    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd5,0xd5,0xd5,0x87,0x87,0x87,0xe8,0xe8,0xe8,
1378    0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,
1379    0xda,0xda,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1380    0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,
1381    0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
1382    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,
1383    0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,
1384    0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1385    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,
1386    0xe8,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
1387    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1388    0xff,0xff,0xff,0xd9,0xd9,0xd9,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xdc,
1389    0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1390    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,
1391    0xdb,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
1392    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1393    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,0xdb,0x87,0x87,0x87,0xe8,
1394    0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1395    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1396    0xff,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
1397    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1398    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdc,
1399    0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1400    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1401    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b
1402};
1403
1404static void QZ_DrawResizeIcon (_THIS, RgnHandle dirtyRegion) {
1405
1406    /* Check if we should draw the resize icon */
1407    if (SDL_VideoSurface->flags & SDL_RESIZABLE) {
1408
1409        Rect    icon;
1410        SetRect (&icon, SDL_VideoSurface->w - 13, SDL_VideoSurface->h - 13,
1411                    SDL_VideoSurface->w, SDL_VideoSurface->h);
1412
1413        if (RectInRgn (&icon, dirtyRegion)) {
1414
1415            SDL_Rect icon_rect;
1416
1417            /* Create the icon image */
1418            if (resize_icon == NULL) {
1419
1420                SDL_RWops *rw;
1421                SDL_Surface *tmp;
1422
1423                rw = SDL_RWFromConstMem (QZ_ResizeIcon, sizeof(QZ_ResizeIcon));
1424                tmp = SDL_LoadBMP_RW (rw, SDL_TRUE);
1425
1426                resize_icon = SDL_ConvertSurface (tmp, SDL_VideoSurface->format, SDL_SRCCOLORKEY);
1427                SDL_SetColorKey (resize_icon, SDL_SRCCOLORKEY, 0xFFFFFF);
1428
1429                SDL_FreeSurface (tmp);
1430            }
1431
1432            icon_rect.x = SDL_VideoSurface->w - 13;
1433            icon_rect.y = SDL_VideoSurface->h - 13;
1434            icon_rect.w = 13;
1435            icon_rect.h = 13;
1436
1437            SDL_BlitSurface (resize_icon, NULL, SDL_VideoSurface, &icon_rect);
1438        }
1439    }
1440}
1441
1442static void QZ_UpdateRects (_THIS, int numRects, SDL_Rect *rects) {
1443
1444    if (SDL_VideoSurface->flags & SDL_OPENGLBLIT) {
1445        QZ_GL_SwapBuffers (this);
1446    }
1447    else if ( [ qz_window isMiniaturized ] ) {
1448
1449        /* Do nothing if miniaturized */
1450    }
1451
1452    else if ( ! QZ_IsWindowObscured (qz_window) ) {
1453
1454        /* Use direct copy to flush contents to the display */
1455        CGrafPtr savePort;
1456        CGrafPtr dstPort, srcPort;
1457        const BitMap  *dstBits, *srcBits;
1458        Rect     dstRect, srcRect;
1459        Point    offset;
1460        int i;
1461
1462        GetPort (&savePort);
1463
1464        dstPort = CreateNewPortForCGDisplayID ((UInt32)display_id);
1465        srcPort = [ window_view qdPort ];
1466
1467        offset.h = 0;
1468        offset.v = 0;
1469        SetPort (srcPort);
1470        LocalToGlobal (&offset);
1471
1472        SetPort (dstPort);
1473
1474        LockPortBits (dstPort);
1475        LockPortBits (srcPort);
1476
1477        dstBits = GetPortBitMapForCopyBits (dstPort);
1478        srcBits = GetPortBitMapForCopyBits (srcPort);
1479
1480        for (i = 0; i < numRects; i++) {
1481
1482            SetRect (&srcRect, rects[i].x, rects[i].y,
1483                     rects[i].x + rects[i].w,
1484                     rects[i].y + rects[i].h);
1485
1486            SetRect (&dstRect,
1487                     rects[i].x + offset.h,
1488                     rects[i].y + offset.v,
1489                     rects[i].x + rects[i].w + offset.h,
1490                     rects[i].y + rects[i].h + offset.v);
1491
1492            CopyBits (srcBits, dstBits,
1493                      &srcRect, &dstRect, srcCopy, NULL);
1494
1495        }
1496
1497        SetPort (savePort);
1498    }
1499    else {
1500        /* Use QDFlushPortBuffer() to flush content to display */
1501        int i;
1502        RgnHandle dirty = NewRgn ();
1503        RgnHandle temp  = NewRgn ();
1504
1505        SetEmptyRgn (dirty);
1506
1507        /* Build the region of dirty rectangles */
1508        for (i = 0; i < numRects; i++) {
1509
1510            MacSetRectRgn (temp, rects[i].x, rects[i].y,
1511                        rects[i].x + rects[i].w, rects[i].y + rects[i].h);
1512            MacUnionRgn (dirty, temp, dirty);
1513        }
1514
1515        QZ_DrawResizeIcon (this, dirty);
1516
1517        /* Flush the dirty region */
1518        QDFlushPortBuffer ( [ window_view qdPort ], dirty );
1519        DisposeRgn (dirty);
1520        DisposeRgn (temp);
1521    }
1522}
1523
1524static void QZ_VideoQuit (_THIS) {
1525
1526    CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
1527
1528    /* Restore gamma settings */
1529    CGDisplayRestoreColorSyncSettings ();
1530
1531    /* Ensure the cursor will be visible and working when we quit */
1532    CGDisplayShowCursor (display_id);
1533    CGAssociateMouseAndMouseCursorPosition (1);
1534
1535    if (mode_flags & SDL_FULLSCREEN) {
1536        /* Fade to black to hide resolution-switching flicker (and garbage
1537           that is displayed by a destroyed OpenGL context, if applicable) */
1538        if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) {
1539            CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
1540        }
1541        QZ_UnsetVideoMode (this, TRUE);
1542        if (fade_token != kCGDisplayFadeReservationInvalidToken) {
1543            CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
1544            CGReleaseDisplayFadeReservation (fade_token);
1545        }
1546    }
1547    else
1548        QZ_UnsetVideoMode (this, TRUE);
1549
1550    CGPaletteRelease (palette);
1551
1552    if (opengl_library) {
1553        SDL_UnloadObject(opengl_library);
1554        opengl_library = NULL;
1555    }
1556    this->gl_config.driver_loaded = 0;
1557
1558    if (field_edit) {
1559        [field_edit release];
1560        field_edit = NULL;
1561    }
1562}
1563
1564#if 0 /* Not used (apparently, it's really slow) */
1565static int  QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color) {
1566
1567    CGSDisplayHWFill (display_id, rect->x, rect->y, rect->w, rect->h, color);
1568
1569    return 0;
1570}
1571#endif
1572
1573static int  QZ_LockHWSurface(_THIS, SDL_Surface *surface) {
1574
1575    return 1;
1576}
1577
1578static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface) {
1579
1580}
1581
1582static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface) {
1583    return(-1); /* unallowed (no HWSURFACE support here). */
1584}
1585
1586static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface) {
1587}
1588
1589/*
1590 int QZ_FlipHWSurface (_THIS, SDL_Surface *surface) {
1591     return 0;
1592 }
1593 */
1594
1595/* Gamma functions */
1596int QZ_SetGamma (_THIS, float red, float green, float blue) {
1597
1598    const CGGammaValue min = 0.0, max = 1.0;
1599
1600    if (red == 0.0)
1601        red = FLT_MAX;
1602    else
1603        red = 1.0 / red;
1604
1605    if (green == 0.0)
1606        green = FLT_MAX;
1607    else
1608        green = 1.0 / green;
1609
1610    if (blue == 0.0)
1611        blue = FLT_MAX;
1612    else
1613        blue  = 1.0 / blue;
1614
1615    if ( CGDisplayNoErr == CGSetDisplayTransferByFormula
1616         (display_id, min, max, red, min, max, green, min, max, blue) ) {
1617
1618        return 0;
1619    }
1620    else {
1621
1622        return -1;
1623    }
1624}
1625
1626int QZ_GetGamma (_THIS, float *red, float *green, float *blue) {
1627
1628    CGGammaValue dummy;
1629    if ( CGDisplayNoErr == CGGetDisplayTransferByFormula
1630         (display_id, &dummy, &dummy, red,
1631          &dummy, &dummy, green, &dummy, &dummy, blue) )
1632
1633        return 0;
1634    else
1635        return -1;
1636}
1637
1638int QZ_SetGammaRamp (_THIS, Uint16 *ramp) {
1639
1640    const CGTableCount tableSize = 255;
1641    CGGammaValue redTable[tableSize];
1642    CGGammaValue greenTable[tableSize];
1643    CGGammaValue blueTable[tableSize];
1644
1645    int i;
1646
1647    /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
1648    for (i = 0; i < 256; i++)
1649        redTable[i % 256] = ramp[i] / 65535.0;
1650
1651    for (i=256; i < 512; i++)
1652        greenTable[i % 256] = ramp[i] / 65535.0;
1653
1654    for (i=512; i < 768; i++)
1655        blueTable[i % 256] = ramp[i] / 65535.0;
1656
1657    if ( CGDisplayNoErr == CGSetDisplayTransferByTable
1658         (display_id, tableSize, redTable, greenTable, blueTable) )
1659        return 0;
1660    else
1661        return -1;
1662}
1663
1664int QZ_GetGammaRamp (_THIS, Uint16 *ramp) {
1665
1666    const CGTableCount tableSize = 255;
1667    CGGammaValue redTable[tableSize];
1668    CGGammaValue greenTable[tableSize];
1669    CGGammaValue blueTable[tableSize];
1670    CGTableCount actual;
1671    int i;
1672
1673    if ( CGDisplayNoErr != CGGetDisplayTransferByTable
1674         (display_id, tableSize, redTable, greenTable, blueTable, &actual) ||
1675         actual != tableSize)
1676
1677        return -1;
1678
1679    /* Pack tables into one array, with values from 0 to 65535 */
1680    for (i = 0; i < 256; i++)
1681        ramp[i] = redTable[i % 256] * 65535.0;
1682
1683    for (i=256; i < 512; i++)
1684        ramp[i] = greenTable[i % 256] * 65535.0;
1685
1686    for (i=512; i < 768; i++)
1687        ramp[i] = blueTable[i % 256] * 65535.0;
1688
1689    return 0;
1690}
1691
1692