/
SDL_QuartzVideo.m
1380 lines (1124 loc) · 45.8 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2009 Sam Lantinga
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
20
slouken@libsdl.org
21
*/
22
#include "SDL_config.h"
23
24
#include "SDL_QuartzVideo.h"
25
#include "SDL_QuartzWindow.h"
26
27
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1060 /* Fixed in Snow Leopard */
28
/*
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Add methods to get at private members of NSScreen.
Since there is a bug in Apple's screen switching code
that does not update this variable when switching
to fullscreen, we'll set it manually (but only for the
main screen).
*/
@interface NSScreen (NSScreenAccess)
- (void) setFrame:(NSRect)frame;
@end
@implementation NSScreen (NSScreenAccess)
- (void) setFrame:(NSRect)frame;
{
_frame = frame;
}
@end
45
46
47
48
49
50
51
52
53
static inline void QZ_SetFrame(NSScreen *nsscreen, NSRect frame)
{
[nsscreen setFrame:frame];
}
#else
static inline void QZ_SetFrame(NSScreen *nsscreen, NSRect frame)
{
}
#endif
54
55
56
57
58
59
60
61
62
63
64
@interface SDLTranslatorResponder : NSTextView
{
}
- (void) doCommandBySelector:(SEL)myselector;
@end
@implementation SDLTranslatorResponder
- (void) doCommandBySelector:(SEL) myselector {}
@end
65
66
/* absent in 10.3.9. */
CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
67
68
69
70
71
72
73
74
75
76
77
/* Bootstrap functions */
static int QZ_Available ();
static SDL_VideoDevice* QZ_CreateDevice (int device_index);
static void QZ_DeleteDevice (SDL_VideoDevice *device);
/* Initialization, Query, Setup, and Redrawing functions */
static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format);
static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format,
Uint32 flags);
78
static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop);
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current,
int width, int height, int bpp,
Uint32 flags);
static int QZ_ToggleFullScreen (_THIS, int on);
static int QZ_SetColors (_THIS, int first_color,
int num_colors, SDL_Color *colors);
static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface);
static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface);
static int QZ_ThreadFlip (_THIS);
static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface);
static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects);
static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects);
static void QZ_UpdateRects (_THIS, int num_rects, SDL_Rect *rects);
static void QZ_VideoQuit (_THIS);
/* Hardware surface functions (for fullscreen mode only) */
#if 0 /* Not used (apparently, it's really slow) */
static int QZ_FillHWRect (_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color);
#endif
static int QZ_LockHWSurface(_THIS, SDL_Surface *surface);
static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface);
103
static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface);
104
105
static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface);
/* static int QZ_FlipHWSurface (_THIS, SDL_Surface *surface); */
106
107
108
/* Bootstrap binding, enables entry point into the driver */
VideoBootStrap QZ_bootstrap = {
109
"Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice
110
111
};
112
113
/* Bootstrap functions */
114
115
static int QZ_Available ()
{
116
117
118
return 1;
}
119
120
static SDL_VideoDevice* QZ_CreateDevice (int device_index)
{
121
#pragma unused (device_index)
122
123
124
125
SDL_VideoDevice *device;
SDL_PrivateVideoData *hidden;
126
127
device = (SDL_VideoDevice*) SDL_malloc (sizeof (*device) );
hidden = (SDL_PrivateVideoData*) SDL_malloc (sizeof (*hidden) );
128
129
130
131
if (device == NULL || hidden == NULL)
SDL_OutOfMemory ();
132
133
SDL_memset (device, 0, sizeof (*device) );
SDL_memset (hidden, 0, sizeof (*hidden) );
134
135
136
137
138
139
140
device->hidden = hidden;
device->VideoInit = QZ_VideoInit;
device->ListModes = QZ_ListModes;
device->SetVideoMode = QZ_SetVideoMode;
device->ToggleFullScreen = QZ_ToggleFullScreen;
141
device->UpdateMouse = QZ_UpdateMouse;
142
143
144
device->SetColors = QZ_SetColors;
/* device->UpdateRects = QZ_UpdateRects; this is determined by SetVideoMode() */
device->VideoQuit = QZ_VideoQuit;
145
146
147
device->LockHWSurface = QZ_LockHWSurface;
device->UnlockHWSurface = QZ_UnlockHWSurface;
148
device->AllocHWSurface = QZ_AllocHWSurface;
149
150
151
152
153
154
155
156
157
158
159
160
161
device->FreeHWSurface = QZ_FreeHWSurface;
/* device->FlipHWSurface = QZ_FlipHWSurface */;
device->SetGamma = QZ_SetGamma;
device->GetGamma = QZ_GetGamma;
device->SetGammaRamp = QZ_SetGammaRamp;
device->GetGammaRamp = QZ_GetGammaRamp;
device->GL_GetProcAddress = QZ_GL_GetProcAddress;
device->GL_GetAttribute = QZ_GL_GetAttribute;
device->GL_MakeCurrent = QZ_GL_MakeCurrent;
device->GL_SwapBuffers = QZ_GL_SwapBuffers;
device->GL_LoadLibrary = QZ_GL_LoadLibrary;
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
device->FreeWMCursor = QZ_FreeWMCursor;
device->CreateWMCursor = QZ_CreateWMCursor;
device->ShowWMCursor = QZ_ShowWMCursor;
device->WarpWMCursor = QZ_WarpWMCursor;
device->MoveWMCursor = QZ_MoveWMCursor;
device->CheckMouseMode = QZ_CheckMouseMode;
device->InitOSKeymap = QZ_InitOSKeymap;
device->PumpEvents = QZ_PumpEvents;
device->SetCaption = QZ_SetCaption;
device->SetIcon = QZ_SetIcon;
device->IconifyWindow = QZ_IconifyWindow;
/*device->GetWMInfo = QZ_GetWMInfo;*/
device->GrabInput = QZ_GrabInput;
177
178
179
180
181
182
183
184
185
/*
* This is a big hassle, needing QuickDraw and QuickTime on older
* systems, and god knows what on 10.6, so we immediately fail here,
* which causes SDL to make an RGB surface and manage the YUV overlay
* in software. Sorry. Use SDL 1.3 if you want YUV rendering in a pixel
* shader. :)
*/
/*device->CreateYUVOverlay = QZ_CreateYUVOverlay;*/
186
187
device->free = QZ_DeleteDevice;
188
189
190
191
return device;
}
192
193
static void QZ_DeleteDevice (SDL_VideoDevice *device)
{
194
195
SDL_free (device->hidden);
SDL_free (device);
196
197
}
198
199
static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format)
{
200
NSRect r = NSMakeRect(0.0, 0.0, 0.0, 0.0);
201
const char *env = NULL;
202
203
204
/* Initialize the video settings; this data persists between mode switches */
display_id = kCGDirectMainDisplay;
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#if 0 /* The mouse event code needs to take this into account... */
env = getenv("SDL_VIDEO_FULLSCREEN_DISPLAY");
if ( env ) {
int monitor = SDL_atoi(env);
CGDirectDisplayID activeDspys [3];
CGDisplayCount dspyCnt;
CGGetActiveDisplayList (3, activeDspys, &dspyCnt);
if ( monitor >= 0 && monitor < dspyCnt ) {
display_id = activeDspys[monitor];
}
}
#endif
219
220
221
save_mode = CGDisplayCurrentMode (display_id);
mode_list = CGDisplayAvailableModes (display_id);
palette = CGPaletteCreateDefaultColorPalette ();
222
223
/* Allow environment override of screensaver disable. */
224
env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER");
225
226
227
228
229
230
231
232
233
if ( env ) {
allow_screensaver = SDL_atoi(env);
} else {
#ifdef SDL_VIDEO_DISABLE_SCREENSAVER
allow_screensaver = 0;
#else
allow_screensaver = 1;
#endif
}
234
235
236
237
/* Gather some information that is useful to know about the display */
CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayBitsPerPixel),
kCFNumberSInt32Type, &device_bpp);
238
239
240
241
242
243
244
CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayWidth),
kCFNumberSInt32Type, &device_width);
CFNumberGetValue (CFDictionaryGetValue (save_mode, kCGDisplayHeight),
kCFNumberSInt32Type, &device_height);
245
246
247
248
249
/* Determine the current screen size */
this->info.current_w = device_width;
this->info.current_h = device_height;
/* Determine the default screen depth */
250
251
video_format->BitsPerPixel = device_bpp;
252
253
/* Set misc globals */
current_grab_mode = SDL_GRAB_OFF;
254
cursor_should_be_visible = YES;
255
cursor_visible = YES;
256
current_mods = 0;
257
field_edit = [[SDLTranslatorResponder alloc] initWithFrame:r];
258
259
260
if ( Gestalt(gestaltSystemVersion, &system_version) != noErr )
system_version = 0;
261
262
263
264
/* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */
QZ_RegisterForSleepNotifications (this);
265
266
267
/* Fill in some window manager capabilities */
this->info.wm_available = 1;
268
return 0;
269
270
}
271
272
static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags)
{
273
CFIndex num_modes;
274
275
276
CFIndex i;
int list_size = 0;
277
278
279
280
/* Any windowed mode is acceptable */
if ( (flags & SDL_FULLSCREEN) == 0 )
return (SDL_Rect**)-1;
281
282
/* Free memory from previous call, if any */
283
if ( client_mode_list != NULL ) {
284
285
int i;
286
287
for (i = 0; client_mode_list[i] != NULL; i++)
288
SDL_free (client_mode_list[i]);
289
290
SDL_free (client_mode_list);
291
client_mode_list = NULL;
292
}
293
294
295
num_modes = CFArrayGetCount (mode_list);
296
/* Build list of modes with the requested bpp */
297
for (i = 0; i < num_modes; i++) {
298
299
300
CFDictionaryRef onemode;
CFNumberRef number;
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
int bpp;
onemode = CFArrayGetValueAtIndex (mode_list, i);
number = CFDictionaryGetValue (onemode, kCGDisplayBitsPerPixel);
CFNumberGetValue (number, kCFNumberSInt32Type, &bpp);
if (bpp == format->BitsPerPixel) {
int intvalue;
int hasMode;
int width, height;
number = CFDictionaryGetValue (onemode, kCGDisplayWidth);
CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
width = (Uint16) intvalue;
number = CFDictionaryGetValue (onemode, kCGDisplayHeight);
CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
height = (Uint16) intvalue;
/* Check if mode is already in the list */
{
int i;
hasMode = SDL_FALSE;
for (i = 0; i < list_size; i++) {
326
327
if (client_mode_list[i]->w == width &&
client_mode_list[i]->h == height) {
328
329
330
hasMode = SDL_TRUE;
break;
}
331
332
}
}
333
334
335
336
337
338
339
340
/* Grow the list and add mode to the list */
if ( ! hasMode ) {
SDL_Rect *rect;
list_size++;
341
342
if (client_mode_list == NULL)
client_mode_list = (SDL_Rect**)
343
SDL_malloc (sizeof(*client_mode_list) * (list_size+1) );
344
else
345
client_mode_list = (SDL_Rect**)
346
SDL_realloc (client_mode_list, sizeof(*client_mode_list) * (list_size+1));
347
348
rect = (SDL_Rect*) SDL_malloc (sizeof(**client_mode_list));
349
350
if (client_mode_list == NULL || rect == NULL) {
351
352
353
354
SDL_OutOfMemory ();
return NULL;
}
355
rect->x = rect->y = 0;
356
357
358
rect->w = width;
rect->h = height;
359
360
client_mode_list[list_size-1] = rect;
client_mode_list[list_size] = NULL;
361
}
362
363
}
}
364
365
366
367
368
369
/* Sort list largest to smallest (by area) */
{
int i, j;
for (i = 0; i < list_size; i++) {
for (j = 0; j < list_size-1; j++) {
370
371
int area1, area2;
372
373
area1 = client_mode_list[j]->w * client_mode_list[j]->h;
area2 = client_mode_list[j+1]->w * client_mode_list[j+1]->h;
374
375
if (area1 < area2) {
376
377
378
SDL_Rect *tmp = client_mode_list[j];
client_mode_list[j] = client_mode_list[j+1];
client_mode_list[j+1] = tmp;
379
380
381
382
}
}
}
}
383
return client_mode_list;
384
385
}
386
387
388
389
390
391
392
393
394
395
396
static SDL_bool QZ_WindowPosition(_THIS, int *x, int *y)
{
const char *window = getenv("SDL_VIDEO_WINDOW_POS");
if ( window ) {
if ( sscanf(window, "%d,%d", x, y) == 2 ) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
397
398
static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop)
{
399
/* Reset values that may change between switches */
400
401
402
403
404
405
this->info.blit_fill = 0;
this->FillHWRect = NULL;
this->UpdateRects = NULL;
this->LockHWSurface = NULL;
this->UnlockHWSurface = NULL;
406
407
408
409
410
411
if (cg_context) {
CGContextFlush (cg_context);
CGContextRelease (cg_context);
cg_context = nil;
}
412
/* Release fullscreen resources */
413
if ( mode_flags & SDL_FULLSCREEN ) {
414
415
416
NSRect screen_rect;
417
/* Release double buffer stuff */
418
if ( mode_flags & SDL_DOUBLEBUF) {
419
420
421
422
423
quit_thread = YES;
SDL_SemPost (sem1);
SDL_WaitThread (thread, NULL);
SDL_DestroySemaphore (sem1);
SDL_DestroySemaphore (sem2);
424
SDL_free (sw_buffers[0]);
425
426
}
427
428
/* If we still have a valid window, close it. */
if ( qz_window ) {
429
430
NSCAssert([ qz_window delegate ] == nil, @"full screen window shouldn't have a delegate"); /* if that should ever change, we'd have to release it here */
[ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */
431
432
433
qz_window = nil;
window_view = nil;
}
434
435
436
437
438
439
/*
Release the OpenGL context
Do this first to avoid trash on the display before fade
*/
if ( mode_flags & SDL_OPENGL ) {
440
QZ_TearDownOpenGL (this);
441
442
CGLSetFullScreen (NULL);
}
443
if (to_desktop) {
444
ShowMenuBar ();
445
446
447
448
449
450
451
452
/* Restore original screen resolution/bpp */
CGDisplaySwitchToMode (display_id, save_mode);
CGReleaseAllDisplays ();
/*
Reset the main screen's rectangle
See comment in QZ_SetVideoFullscreen for why we do this
*/
screen_rect = NSMakeRect(0,0,device_width,device_height);
453
QZ_SetFrame([ NSScreen mainScreen ], screen_rect);
454
}
455
}
456
/* Release window mode resources */
457
else {
458
459
460
id delegate = [ qz_window delegate ];
[ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */
if (delegate != nil) [ delegate release ];
461
qz_window = nil;
462
window_view = nil;
463
464
465
466
/* Release the OpenGL context */
if ( mode_flags & SDL_OPENGL )
QZ_TearDownOpenGL (this);
467
}
468
469
470
/* Signal successful teardown */
video_set = SDL_FALSE;
471
472
473
}
static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int width,
474
475
int height, int bpp, Uint32 flags)
{
476
boolean_t exact_match = 0;
477
NSRect screen_rect;
478
CGError error;
479
NSRect contentRect;
480
CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
481
482
483
contentRect = NSMakeRect (0, 0, width, height);
484
485
486
487
488
/* Fade to black to hide resolution-switching flicker (and garbage
that is displayed by a destroyed OpenGL context, if applicable) */
if ( CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess ) {
CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
}
489
490
491
/* Destroy any previous mode */
if (video_set == SDL_TRUE)
492
QZ_UnsetVideoMode (this, FALSE);
493
494
495
496
497
498
499
/* Sorry, QuickDraw was ripped out. */
if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) {
SDL_SetError ("Embedded QuickDraw windows are no longer supported");
goto ERR_NO_MATCH;
}
500
/* See if requested mode exists */
501
502
503
mode = CGDisplayBestModeForParameters (display_id, bpp, width,
height, &exact_match);
504
505
/* Require an exact match to the requested mode */
if ( ! exact_match ) {
506
SDL_SetError ("Failed to find display resolution: %dx%dx%d", width, height, bpp);
507
508
goto ERR_NO_MATCH;
}
509
510
/* Put up the blanking window (a window above all other windows) */
511
512
513
514
515
516
if (getenv ("SDL_SINGLEDISPLAY"))
error = CGDisplayCapture (display_id);
else
error = CGCaptureAllDisplays ();
if ( CGDisplayNoErr != error ) {
517
518
519
SDL_SetError ("Failed capturing display");
goto ERR_NO_CAPTURE;
}
520
521
522
523
524
525
/* Do the physical switch */
if ( CGDisplayNoErr != CGDisplaySwitchToMode (display_id, mode) ) {
SDL_SetError ("Failed switching display resolution");
goto ERR_NO_SWITCH;
}
526
527
528
529
current->pixels = (Uint32*) CGDisplayBaseAddress (display_id);
current->pitch = CGDisplayBytesPerRow (display_id);
530
current->flags = 0;
531
532
current->w = width;
current->h = height;
533
current->flags |= SDL_FULLSCREEN;
534
current->flags |= SDL_HWSURFACE;
535
current->flags |= SDL_PREALLOC;
536
537
/* current->hwdata = (void *) CGDisplayGetDrawingContext (display_id); */
538
539
540
this->UpdateRects = QZ_DirectUpdate;
this->LockHWSurface = QZ_LockHWSurface;
this->UnlockHWSurface = QZ_UnlockHWSurface;
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
/* Setup double-buffer emulation */
if ( flags & SDL_DOUBLEBUF ) {
/*
Setup a software backing store for reasonable results when
double buffering is requested (since a single-buffered hardware
surface looks hideous).
The actual screen blit occurs in a separate thread to allow
other blitting while waiting on the VBL (and hence results in higher framerates).
*/
this->LockHWSurface = NULL;
this->UnlockHWSurface = NULL;
this->UpdateRects = NULL;
current->flags |= (SDL_HWSURFACE|SDL_DOUBLEBUF);
this->UpdateRects = QZ_DoubleBufferUpdate;
this->LockHWSurface = QZ_LockDoubleBuffer;
this->UnlockHWSurface = QZ_UnlockDoubleBuffer;
this->FlipHWSurface = QZ_FlipDoubleBuffer;
563
current->pixels = SDL_malloc (current->pitch * current->h * 2);
564
565
566
567
568
569
570
571
572
573
574
575
if (current->pixels == NULL) {
SDL_OutOfMemory ();
goto ERR_DOUBLEBUF;
}
sw_buffers[0] = current->pixels;
sw_buffers[1] = (Uint8*)current->pixels + current->pitch * current->h;
quit_thread = NO;
sem1 = SDL_CreateSemaphore (0);
sem2 = SDL_CreateSemaphore (1);
thread = SDL_CreateThread ((int (*)(void *))QZ_ThreadFlip, this);
576
}
577
578
579
if ( CGDisplayCanSetPalette (display_id) )
current->flags |= SDL_HWPALETTE;
580
581
582
583
584
585
/* Check if we should recreate the window */
if (qz_window == nil) {
/* Manually create a window, avoids having a nib file resource */
qz_window = [ [ SDL_QuartzWindow alloc ]
initWithContentRect:contentRect
586
styleMask:0
587
588
589
590
591
592
593
594
595
596
backing:NSBackingStoreBuffered
defer:NO ];
if (qz_window != nil) {
[ qz_window setAcceptsMouseMovedEvents:YES ];
[ qz_window setViewsNeedDisplay:NO ];
}
}
/* We already have a window, just change its size */
else {
597
598
599
[ qz_window setContentSize:contentRect.size ];
current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags;
[ window_view setFrameSize:contentRect.size ];
602
603
604
605
606
/* Setup OpenGL for a fullscreen context */
if (flags & SDL_OPENGL) {
CGLError err;
CGLContextObj ctx;
607
608
if ( ! QZ_SetupOpenGL (this, bpp, flags) ) {
609
goto ERR_NO_GL;
610
}
611
612
613
614
615
616
617
/* Initialize the NSView and add it to our window. The presence of a valid window and
view allow the cursor to be changed whilst in fullscreen.*/
window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
[ [ qz_window contentView ] addSubview:window_view ];
[ window_view release ];
618
ctx = QZ_GetCGLContextObj (gl_context);
619
err = CGLSetFullScreen (ctx);
620
621
if (err) {
622
SDL_SetError ("Error setting OpenGL fullscreen: %s", CGLErrorString(err));
623
624
goto ERR_NO_GL;
}
625
626
[ gl_context makeCurrentContext];
627
628
629
630
glClear (GL_COLOR_BUFFER_BIT);
[ gl_context flushBuffer ];
631
632
633
634
635
636
current->flags |= SDL_OPENGL;
}
/* If we don't hide menu bar, it will get events and interrupt the program */
HideMenuBar ();
637
638
639
640
641
642
/* Fade in again (asynchronously) */
if ( fade_token != kCGDisplayFadeReservationInvalidToken ) {
CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
CGReleaseDisplayFadeReservation(fade_token);
}
643
644
/*
645
646
647
648
649
There is a bug in Cocoa where NSScreen doesn't synchronize
with CGDirectDisplay, so the main screen's frame is wrong.
As a result, coordinate translation produces incorrect results.
We can hack around this bug by setting the screen rect
ourselves. This hack should be removed if/when the bug is fixed.
650
651
*/
screen_rect = NSMakeRect(0,0,width,height);
652
QZ_SetFrame([ NSScreen mainScreen ], screen_rect);
653
654
655
/* Save the flags to ensure correct tear-down */
mode_flags = current->flags;
656
657
658
/* Set app state, hide cursor if necessary, ... */
QZ_DoActivate(this);
659
660
661
return current;
662
/* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
663
664
665
ERR_NO_GL:
ERR_DOUBLEBUF: CGDisplaySwitchToMode (display_id, save_mode);
ERR_NO_SWITCH: CGReleaseAllDisplays ();
666
667
668
669
670
671
ERR_NO_CAPTURE:
ERR_NO_MATCH: if ( fade_token != kCGDisplayFadeReservationInvalidToken ) {
CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
CGReleaseDisplayFadeReservation (fade_token);
}
return NULL;
672
673
674
}
static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width,
675
676
int height, int *bpp, Uint32 flags)
{
677
unsigned int style;
678
NSRect contentRect;
679
680
int center_window = 1;
int origin_x, origin_y;
681
CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
682
683
684
current->flags = 0;
current->w = width;
685
current->h = height;
686
687
contentRect = NSMakeRect (0, 0, width, height);
688
689
690
691
692
693
694
695
/*
Check if we should completely destroy the previous mode
- If it is fullscreen
- If it has different noframe or resizable attribute
- If it is OpenGL (since gl attributes could be different)
- If new mode is OpenGL, but previous mode wasn't
*/
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
if (video_set == SDL_TRUE) {
if (mode_flags & SDL_FULLSCREEN) {
/* Fade to black to hide resolution-switching flicker (and garbage
that is displayed by a destroyed OpenGL context, if applicable) */
if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) {
CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
}
QZ_UnsetVideoMode (this, TRUE);
}
else if ( ((mode_flags ^ flags) & (SDL_NOFRAME|SDL_RESIZABLE)) ||
(mode_flags & SDL_OPENGL) ||
(flags & SDL_OPENGL) ) {
QZ_UnsetVideoMode (this, TRUE);
}
}
711
712
713
714
715
716
717
/* Sorry, QuickDraw was ripped out. */
if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) {
SDL_SetError ("Embedded QuickDraw windows are no longer supported");
if (fade_token != kCGDisplayFadeReservationInvalidToken) {
CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
CGReleaseDisplayFadeReservation (fade_token);
718
}
719
return NULL;
720
}
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
/* Check if we should recreate the window */
if (qz_window == nil) {
/* Set the window style based on input flags */
if ( flags & SDL_NOFRAME ) {
style = NSBorderlessWindowMask;
current->flags |= SDL_NOFRAME;
} else {
style = NSTitledWindowMask;
style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
if ( flags & SDL_RESIZABLE ) {
style |= NSResizableWindowMask;
current->flags |= SDL_RESIZABLE;
}
}
737
738
739
740
741
742
743
744
745
746
/* Manually create a window, avoids having a nib file resource */
qz_window = [ [ SDL_QuartzWindow alloc ]
initWithContentRect:contentRect
styleMask:style
backing:NSBackingStoreBuffered
defer:NO ];
if (qz_window == nil) {
SDL_SetError ("Could not create the Cocoa window");
747
748
749
750
if (fade_token != kCGDisplayFadeReservationInvalidToken) {
CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
CGReleaseDisplayFadeReservation (fade_token);
}
751
752
return NULL;
}
753
754
/*[ qz_window setReleasedWhenClosed:YES ];*/ /* no need to set this as it's the default for NSWindows */
755
756
757
QZ_SetCaption(this, this->wm_title, this->wm_icon);
[ qz_window setAcceptsMouseMovedEvents:YES ];
[ qz_window setViewsNeedDisplay:NO ];
758
759
760
761
762
if ( QZ_WindowPosition(this, &origin_x, &origin_y) ) {
/* have to flip the Y value (NSPoint is lower left corner origin) */
[ qz_window setFrameTopLeftPoint:NSMakePoint((float) origin_x, (float) (this->info.current_h - origin_y))];
center_window = 0;
763
} else if ( center_window ) {
764
765
[ qz_window center ];
}
766
767
[ qz_window setDelegate:
768
[ [ SDL_QuartzWindowDelegate alloc ] init ] ];
769
[ qz_window setContentView: [ [ [ SDL_QuartzView alloc ] init ] autorelease ] ];
770
771
772
}
/* We already have a window, just change its size */
else {
773
774
775
[ qz_window setContentSize:contentRect.size ];
current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags;
[ window_view setFrameSize:contentRect.size ];
776
}
777
778
/* For OpenGL, we bind the context to a subview */
779
if ( flags & SDL_OPENGL ) {
780
781
if ( ! QZ_SetupOpenGL (this, *bpp, flags) ) {
782
783
784
785
if (fade_token != kCGDisplayFadeReservationInvalidToken) {
CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
CGReleaseDisplayFadeReservation (fade_token);
}
786
787
return NULL;
}
788
789
window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
790
[ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
791
792
793
[ [ qz_window contentView ] addSubview:window_view ];
[ gl_context setView: window_view ];
[ window_view release ];
794
[ gl_context makeCurrentContext];
795
[ qz_window makeKeyAndOrderFront:nil ];
796
797
current->flags |= SDL_OPENGL;
}
798
/* For 2D, we build a CGBitmapContext */
799
else {
800
CGColorSpaceRef cgColorspace;
801
802
803
804
/* Only recreate the view if it doesn't already exist */
if (window_view == nil) {
805
window_view = [ [ NSView alloc ] initWithFrame:contentRect ];
806
[ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
807
808
809
810
811
[ [ qz_window contentView ] addSubview:window_view ];
[ window_view release ];
[ qz_window makeKeyAndOrderFront:nil ];
}
812
813
814
815
816
817
818
819
820
cgColorspace = CGColorSpaceCreateDeviceRGB();
current->pitch = 4 * current->w;
current->pixels = SDL_malloc (current->h * current->pitch);
cg_context = CGBitmapContextCreate (current->pixels, current->w, current->h,
8, current->pitch, cgColorspace,
kCGImageAlphaNoneSkipFirst);
CGColorSpaceRelease (cgColorspace);
821
current->flags |= SDL_SWSURFACE;
822
current->flags |= SDL_ASYNCBLIT;
823
current->hwdata = (void *) cg_context;
824
825
this->UpdateRects = QZ_UpdateRects;
826
827
this->LockHWSurface = QZ_LockHWSurface;
this->UnlockHWSurface = QZ_UnlockHWSurface;
828
}
829
830
831
/* Save flags to ensure correct teardown */
mode_flags = current->flags;
832
833
834
835
836
837
838
/* Fade in again (asynchronously) if we came from a fullscreen mode and faded to black */
if (fade_token != kCGDisplayFadeReservationInvalidToken) {
CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
CGReleaseDisplayFadeReservation (fade_token);
}
839
840
841
return current;
}
842
static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, int width,
843
844
int height, int bpp, Uint32 flags)
{
845
current->flags = 0;
846
current->pixels = NULL;
847
848
849
850
851
852
853
854
855
/* Setup full screen video */
if ( flags & SDL_FULLSCREEN ) {
current = QZ_SetVideoFullScreen (this, current, width, height, bpp, flags );
if (current == NULL)
return NULL;
}
/* Setup windowed video */
else {
856
857
/* Force bpp to 32 */
bpp = 32;
858
current = QZ_SetVideoWindowed (this, current, width, height, &bpp, flags);
859
860
861
if (current == NULL)
return NULL;
}
862
863
864
/* Setup the new pixel format */
{
865
866
867
868
869
int amask = 0,
rmask = 0,
gmask = 0,
bmask = 0;
870
871
switch (bpp) {
case 16: /* (1)-5-5-5 RGB */
872
amask = 0;
873
874
875
rmask = 0x7C00;
gmask = 0x03E0;
bmask = 0x001F;
876
877
878
879
880
break;
case 24:
SDL_SetError ("24bpp is not available");
return NULL;
case 32: /* (8)-8-8-8 ARGB */
881
amask = 0x00000000;
882
883
884
885
886
887
888
889
if ( flags & SDL_FULLSCREEN )
{
rmask = 0x00FF0000;
gmask = 0x0000FF00;
bmask = 0x000000FF;
}
else
{
890
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
891
892
893
rmask = 0x0000FF00;
gmask = 0x00FF0000;
bmask = 0xFF000000;
894
#else
895
896
897
rmask = 0x00FF0000;
gmask = 0x0000FF00;
bmask = 0x000000FF;
898
#endif
899
}
900
901
break;
}
902
903
904
if ( ! SDL_ReallocFormat (current, bpp,
rmask, gmask, bmask, amask ) ) {
905
906
SDL_SetError ("Couldn't reallocate pixel format");
return NULL;
907
}
908
}
909
910
/* Signal successful completion (used internally) */
911
video_set = SDL_TRUE;
912
913
914
915
return current;
}
916
917
static int QZ_ToggleFullScreen (_THIS, int on)
{
918
return 0;
919
920
}
921
static int QZ_SetColors (_THIS, int first_color, int num_colors,
922
923
SDL_Color *colors)
{
924
925
CGTableCount index;
CGDeviceColor color;
926
927
for (index = first_color; index < first_color+num_colors; index++) {
928
929
930
931
932
/* Clamp colors between 0.0 and 1.0 */
color.red = colors->r / 255.0;
color.blue = colors->b / 255.0;
color.green = colors->g / 255.0;
933
934
colors++;
935
936
937
CGPaletteSetColorAtIndex (palette, color, index);
}
938
939
940
if ( CGDisplayNoErr != CGDisplaySetPalette (display_id, palette) )
return 0;
941
942
943
944
return 1;
}
945
946
static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface)
{
947
948
949
return 1;
}
950
951
static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface)
{
952
953
}
954
955
956
/* The VBL delay is based on code by Ian R Ollmann's RezLib <iano@cco.caltech.edu> */
static AbsoluteTime QZ_SecondsToAbsolute ( double seconds )
{
957
958
union
{
959
UInt64 i;
960
961
962
963
964
965
966
967
Nanoseconds ns;
} temp;
temp.i = seconds * 1000000000.0;
return NanosecondsToAbsolute ( temp.ns );
}
968
969
static int QZ_ThreadFlip (_THIS)
{
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
Uint8 *src, *dst;
int skip, len, h;
/*
Give this thread the highest scheduling priority possible,
in the hopes that it will immediately run after the VBL delay
*/
{
pthread_t current_thread;
int policy;
struct sched_param param;
current_thread = pthread_self ();
pthread_getschedparam (current_thread, &policy, ¶m);
policy = SCHED_RR;
param.sched_priority = sched_get_priority_max (policy);
pthread_setschedparam (current_thread, policy, ¶m);
}
while (1) {
SDL_SemWait (sem1);
if (quit_thread)
return 0;
995
996
997
998
999
1000
/*
* We have to add SDL_VideoSurface->offset here, since we might be a
* smaller surface in the center of the framebuffer (you asked for
* a fullscreen resolution smaller than the hardware could supply
* so SDL is centering it in a bigger resolution)...
*/